summaryrefslogtreecommitdiff
path: root/apps/plugins/pacbox/pacbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pacbox/pacbox.c')
-rw-r--r--apps/plugins/pacbox/pacbox.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/apps/plugins/pacbox/pacbox.c b/apps/plugins/pacbox/pacbox.c
new file mode 100644
index 0000000000..b8625eead6
--- /dev/null
+++ b/apps/plugins/pacbox/pacbox.c
@@ -0,0 +1,534 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Pacbox - a Pacman Emulator for Rockbox
11 *
12 * Based on PIE - Pacman Instructional Emulator
13 *
14 * Copyright (c) 1997-2003,2004 Alessandro Scotti
15 * http://www.ascotti.org/
16 *
17 * All files in this archive are subject to the GNU General Public License.
18 * See the file COPYING in the source tree root for full license agreement.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "plugin.h"
26#include "arcade.h"
27
28PLUGIN_HEADER
29
30#ifdef USE_IRAM
31extern char iramcopy[];
32extern char iramstart[];
33extern char iramend[];
34extern char iedata[];
35extern char iend[];
36#endif
37
38/* How many video frames (out of a possible 60) we display each second */
39#define FPS 20
40
41#if CONFIG_KEYPAD == IPOD_4G_PAD
42
43#define PACMAN_UP BUTTON_RIGHT
44#define PACMAN_DOWN BUTTON_LEFT
45#define PACMAN_LEFT BUTTON_MENU
46#define PACMAN_RIGHT BUTTON_PLAY
47#define PACMAN_1UP BUTTON_SELECT
48#define PACMAN_COIN BUTTON_SELECT
49
50#elif CONFIG_KEYPAD == IRIVER_H100_PAD || CONFIG_KEYPAD == IRIVER_H300_PAD
51
52#define PACMAN_UP BUTTON_RIGHT
53#define PACMAN_DOWN BUTTON_LEFT
54#define PACMAN_LEFT BUTTON_UP
55#define PACMAN_RIGHT BUTTON_DOWN
56#define PACMAN_1UP BUTTON_SELECT
57#define PACMAN_2UP BUTTON_ON
58#define PACMAN_COIN BUTTON_REC
59#define PACMAN_MENU BUTTON_MODE
60
61#elif CONFIG_KEYPAD == GIGABEAT_PAD
62
63#define PACMAN_UP BUTTON_UP
64#define PACMAN_DOWN BUTTON_DOWN
65#define PACMAN_LEFT BUTTON_LEFT
66#define PACMAN_RIGHT BUTTON_RIGHT
67#define PACMAN_1UP BUTTON_SELECT
68#define PACMAN_2UP BUTTON_POWER
69#define PACMAN_COIN BUTTON_A
70#define PACMAN_MENU BUTTON_MENU
71
72#elif CONFIG_KEYPAD == IAUDIO_X5_PAD
73
74#define PACMAN_UP BUTTON_RIGHT
75#define PACMAN_DOWN BUTTON_LEFT
76#define PACMAN_LEFT BUTTON_UP
77#define PACMAN_RIGHT BUTTON_DOWN
78#define PACMAN_1UP BUTTON_SELECT
79#define PACMAN_2UP BUTTON_POWER
80#define PACMAN_COIN BUTTON_REC
81#define PACMAN_MENU BUTTON_PLAY
82
83#endif
84
85#if (LCD_HEIGHT >= 288)
86#define XOFS ((LCD_WIDTH-224)/2)
87#define YOFS ((LCD_HEIGHT-288)/2)
88#elif (LCD_WIDTH >= 288)
89#define XOFS ((LCD_WIDTH-288)/2)
90#define YOFS ((LCD_HEIGHT-224)/2)
91#elif (LCD_WIDTH >= 220)
92#define XOFS ((LCD_WIDTH-(288*3/4))/2)
93#define YOFS ((LCD_HEIGHT-(224*3/4))/2)
94#elif (LCD_WIDTH >= 144)
95#define XOFS ((LCD_WIDTH-288/2)/2)
96#define YOFS ((LCD_HEIGHT-224/2)/2)
97#endif
98
99struct plugin_api* rb;
100
101unsigned framesPerSecond = VideoFrequency;
102unsigned long frame_counter = 0;
103
104struct pacman_settings {
105 int difficulty;
106 int numlives;
107 int bonus;
108 int ghostnames;
109 int showfps;
110};
111
112struct pacman_settings settings;
113
114bool loadFile( const char * name, unsigned char * buf, int len )
115{
116 char filename[MAX_PATH];
117
118 rb->snprintf(filename,sizeof(filename),"/.rockbox/pacman/%s",name);
119
120 int fd = rb->open( filename, O_RDONLY);
121
122 if( fd < 0 ) {
123 return false;
124 }
125
126 int n = rb->read( fd, buf, len);
127
128 rb->close( fd );
129
130 if( n != len ) {
131 return false;
132 }
133
134 return true;
135}
136
137bool loadROMS( void )
138{
139 bool romsLoaded = false;
140
141 romsLoaded = loadFile( "pacman.6e", ram_, 0x1000) &&
142 loadFile( "pacman.6f", ram_+0x1000, 0x1000) &&
143 loadFile( "pacman.6h", ram_+0x2000, 0x1000) &&
144 loadFile( "pacman.6j", ram_+0x3000, 0x1000) &&
145 loadFile( "pacman.5e", charset_rom_, 0x1000) &&
146 loadFile( "pacman.5f", spriteset_rom_, 0x1000);
147
148 if( romsLoaded ) {
149 decodeROMs();
150 reset_PacmanMachine();
151 }
152
153 return romsLoaded;
154}
155
156/* A buffer to render Pacman's 244x288 screen into */
157unsigned char background[ScreenWidth*ScreenHeight] __attribute__ ((aligned (4)));
158unsigned char video_buffer[ScreenWidth*ScreenHeight] __attribute__ ((aligned (4)));
159
160long start_time;
161long sleep_counter = 0;
162long video_frames = 0;
163
164int dipDifficulty[] = { DipDifficulty_Normal, DipDifficulty_Hard };
165int dipLives[] = { DipLives_1, DipLives_2, DipLives_3, DipLives_5 };
166int dipBonus[] = { DipBonus_10000, DipBonus_15000, DipBonus_20000, DipBonus_None };
167int dipGhostNames[] = { DipGhostNames_Normal, DipGhostNames_Alternate };
168
169int settings_to_dip(struct pacman_settings settings)
170{
171 return ( DipPlay_OneCoinOneGame |
172 DipCabinet_Upright |
173 DipMode_Play |
174 DipRackAdvance_Off |
175
176 dipDifficulty[settings.difficulty] |
177 dipLives[settings.numlives] |
178 dipBonus[settings.bonus] |
179 dipGhostNames[settings.ghostnames]
180 );
181}
182
183
184
185int pacbox_menu_cb(int key, int m)
186{
187 (void)m;
188 switch(key)
189 {
190#ifdef MENU_ENTER2
191 case MENU_ENTER2:
192#endif
193 case MENU_ENTER:
194 key = BUTTON_NONE; /* eat the downpress, next menu reacts on release */
195 break;
196
197#ifdef MENU_ENTER2
198 case MENU_ENTER2 | BUTTON_REL:
199#endif
200 case MENU_ENTER | BUTTON_REL:
201 key = MENU_ENTER; /* fake downpress, next menu doesn't like release */
202 break;
203 }
204
205 return key;
206}
207
208bool pacbox_menu(void)
209{
210 int m;
211 int result;
212 int menu_quit=0;
213 int new_setting;
214 bool need_restart = false;
215
216 static const struct opt_items noyes[2] = {
217 { "No", NULL },
218 { "Yes", NULL },
219 };
220
221 static const struct opt_items difficulty_options[2] = {
222 { "Normal", NULL },
223 { "Harder", NULL },
224 };
225
226 static const struct opt_items numlives_options[4] = {
227 { "1", NULL },
228 { "2", NULL },
229 { "3", NULL },
230 { "5", NULL },
231 };
232
233 static const struct opt_items bonus_options[4] = {
234 { "10000 points", NULL },
235 { "15000 points", NULL },
236 { "20000 points", NULL },
237 { "No bonus", NULL },
238 };
239
240 static const struct opt_items ghostname_options[2] = {
241 { "Normal", NULL },
242 { "Alternate", NULL },
243 };
244
245 static const struct menu_item items[] = {
246 { "Difficulty", NULL },
247 { "Pacmen Per Game", NULL },
248 { "Bonus Life", NULL },
249 { "Ghost Names", NULL },
250 { "Display FPS", NULL },
251 { "Restart", NULL },
252 { "Quit", NULL },
253 };
254
255 m = rb->menu_init(items, sizeof(items) / sizeof(*items),
256 pacbox_menu_cb, NULL, NULL, NULL);
257
258 rb->button_clear_queue();
259
260 while (!menu_quit) {
261 result=rb->menu_show(m);
262
263 switch(result)
264 {
265 case 0:
266 new_setting=settings.difficulty;
267 rb->set_option("Difficulty", &new_setting, INT,
268 difficulty_options , 2, NULL);
269 if (new_setting != settings.difficulty) {
270 settings.difficulty=new_setting;
271 need_restart=true;
272 }
273 break;
274 case 1:
275 new_setting=settings.numlives;
276 rb->set_option("Pacmen Per Game", &new_setting, INT,
277 numlives_options , 4, NULL);
278 if (new_setting != settings.numlives) {
279 settings.numlives=new_setting;
280 need_restart=true;
281 }
282 break;
283 case 2:
284 new_setting=settings.bonus;
285 rb->set_option("Bonus Life", &new_setting, INT,
286 bonus_options , 4, NULL);
287 if (new_setting != settings.bonus) {
288 settings.bonus=new_setting;
289 need_restart=true;
290 }
291 break;
292 case 3:
293 new_setting=settings.ghostnames;
294 rb->set_option("Ghost Names", &new_setting, INT,
295 ghostname_options , 2, NULL);
296 if (new_setting != settings.ghostnames) {
297 settings.ghostnames=new_setting;
298 need_restart=true;
299 }
300 break;
301 case 4: /* Show FPS */
302 rb->set_option("Display FPS",&settings.showfps,INT, noyes, 2, NULL);
303 break;
304 case 5: /* Restart */
305 need_restart=true;
306 menu_quit=1;
307 break;
308 default:
309 menu_quit=1;
310 break;
311 }
312 }
313
314 rb->menu_exit(m);
315
316 if (need_restart) {
317 init_PacmanMachine(settings_to_dip(settings));
318 }
319
320 /* Possible results:
321 exit game
322 restart game
323 usb connected
324 */
325 return (result==6);
326}
327
328
329/*
330 Runs the game engine for one frame.
331*/
332int gameProc( void )
333{
334 int x,y;
335 int fps;
336 char str[80];
337 int status;
338 long end_time;
339 unsigned char* vbuf = video_buffer;
340 fb_data* dst;
341 fb_data* next_dst;
342
343 /* Run the machine for one frame (1/60th second) */
344 run();
345
346 frame_counter++;
347
348 /* Check the button status */
349 status = rb->button_status();
350
351#ifdef PACMAN_MENU
352 if (status & PACMAN_MENU) {
353#else
354 if (rb->button_hold()) {
355#endif
356 end_time = *rb->current_tick;
357 x = pacbox_menu();
358 rb->lcd_clear_display();
359#ifdef HAVE_REMOTE_LCD
360 rb->lcd_remote_clear_display();
361 rb->lcd_remote_update();
362#endif
363 if (x == 1) { return 1; }
364 start_time += *rb->current_tick-end_time;
365 }
366
367 setDeviceMode( Joy1_Left, (status & PACMAN_LEFT) ? DeviceOn : DeviceOff);
368 setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT) ? DeviceOn : DeviceOff);
369 setDeviceMode( Joy1_Up, (status & PACMAN_UP) ? DeviceOn : DeviceOff);
370 setDeviceMode( Joy1_Down, (status & PACMAN_DOWN) ? DeviceOn : DeviceOff);
371 setDeviceMode( CoinSlot_1, (status & PACMAN_COIN) ? DeviceOn : DeviceOff);
372 setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP) ? DeviceOn : DeviceOff);
373#ifdef PACMAN_2UP
374 setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP) ? DeviceOn : DeviceOff);
375#endif
376
377 /* We only update the screen every third frame - Pacman's native
378 framerate is 60fps, so we are attempting to display 20fps */
379 if( (frame_counter % (60/FPS)) == 0) {
380
381 video_frames++;
382
383 /* The following functions render the Pacman screen from the contents
384 of the video and color ram. We first update the background, and
385 then draw the Sprites on top.
386
387 Note that we only redraw the parts of the background that have
388 changed, which is why we need to keep a copy of the background without
389 the sprites on top. Even with the memcpy, this is faster than redrawing
390 the whole background.
391 */
392 renderBackground( background );
393 rb->memcpy(video_buffer,background,sizeof(video_buffer));
394 renderSprites( video_buffer );
395
396#ifdef HAVE_LCD_COLOR
397#if (LCD_WIDTH >= 224) && (LCD_HEIGHT >= 288)
398 /* Native resolution = 224x288 */
399 (void)next_dst;
400 dst=&rb->lcd_framebuffer[YOFS*LCD_WIDTH+XOFS];
401 for (y=0;y<ScreenHeight;y++) {
402 for (x=0;x<ScreenWidth;x++) {
403 *(dst++) = palette[*(vbuf++)];
404 }
405 dst += XOFS*2;
406 }
407#elif (LCD_WIDTH >= 288) && (LCD_HEIGHT >= 224)
408 /* Native resolution - rotated 90 degrees = 288x224 */
409 next_dst=&rb->lcd_framebuffer[YOFS*LCD_WIDTH+XOFS+ScreenHeight-1];
410 for( y=ScreenHeight-1; y>=0; y-- ) {
411 dst = (next_dst--);
412 for( x=0; x<ScreenWidth; x++ ) {
413 *dst = palette[*(vbuf++)];
414 dst+=LCD_WIDTH;
415 }
416 }
417#elif (LCD_WIDTH >= 216) && (LCD_HEIGHT >= 168)
418 /* 0.75 scaling - display 3 out of 4 pixels = 216x168
419 Skipping pixel #2 out of 4 seems to give the most legible display
420 */
421 next_dst=&rb->lcd_framebuffer[YOFS*LCD_WIDTH+XOFS+((ScreenHeight*3)/4)-1];
422 for (y=ScreenHeight-1;y >= 0; y--) {
423 if ((y & 3) != 1) {
424 dst = (next_dst--);
425 for (x=0;x<ScreenWidth;x++) {
426 if ((x & 3) == 1) { vbuf++; }
427 else {
428 *dst = palette[*(vbuf++)];
429 dst+=LCD_WIDTH;
430 }
431 }
432 } else {
433 vbuf+=ScreenWidth;
434 }
435 }
436#elif (LCD_WIDTH >= 144) && (LCD_HEIGHT >= 112)
437 /* 0.5 scaling - display every other pixel = 144x112 */
438 next_dst=&rb->lcd_framebuffer[YOFS*LCD_WIDTH+XOFS+ScreenHeight/2-1];
439 for (y=(ScreenHeight/2)-1;y >= 0; y--) {
440 dst = (next_dst--);
441 for (x=0;x<ScreenWidth/2;x++) {
442 *dst = palette[*(vbuf)];
443 vbuf+=2;
444 dst+=LCD_WIDTH;
445 }
446 vbuf+=ScreenWidth;
447 }
448#endif
449#else /* Greyscale LCDs */
450#if (LCD_WIDTH >= 144) && (LCD_HEIGHT >= 112)
451#if LCD_PIXELFORMAT == VERTICAL_PACKING
452 /* 0.5 scaling - display every other pixel = 144x112 */
453 next_dst=&rb->lcd_framebuffer[YOFS/4*LCD_WIDTH+XOFS+ScreenHeight/2-1];
454 for (y=(ScreenHeight/2)-1;y >= 0; y--) {
455 dst = (next_dst--);
456 for (x=0;x<ScreenWidth/8;x++) {
457 *dst = (palette[*(vbuf+6)]<<6) | (palette[*(vbuf+4)] << 4) | (palette[*(vbuf+2)] << 2) | palette[*(vbuf)];
458 vbuf+=8;
459 dst+=LCD_WIDTH;
460 }
461 vbuf+=ScreenWidth;
462 }
463#endif /* Vertical Packing */
464#endif /* Size >= 144x112 */
465#endif /* Not Colour */
466
467 if (settings.showfps) {
468 fps = (video_frames*HZ*100) / (*rb->current_tick-start_time);
469 rb->snprintf(str,sizeof(str),"%d.%02d / %d fps ",fps/100,fps%100,
470 FPS);
471 rb->lcd_putsxy(0,0,str);
472 }
473
474 rb->lcd_update();
475
476#ifdef SIMULATOR
477 /* Keep the framerate at Pacman's 60fps */
478 end_time = start_time + (video_frames*HZ)/FPS;
479 while (TIME_BEFORE(*rb->current_tick,end_time)) {
480 rb->sleep(1);
481 }
482#endif
483 }
484
485 return 0;
486}
487
488enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
489{
490 (void)parameter;
491 int status;
492
493 rb = api;
494
495#ifdef USE_IRAM
496 rb->memcpy(iramstart, iramcopy, iramend-iramstart);
497 rb->memset(iedata, 0, iend - iedata);
498#endif
499
500#ifdef HAVE_ADJUSTABLE_CPU_FREQ
501 rb->cpu_boost(true);
502#endif
503
504 rb->lcd_set_foreground(LCD_WHITE);
505 rb->lcd_set_background(LCD_BLACK);
506 rb->lcd_clear_display();
507 rb->lcd_update();
508
509 /* Set the default settings (we should load these from a file) */
510 settings.difficulty = 0; /* Normal */
511 settings.numlives = 2; /* 3 lives */
512 settings.bonus = 0; /* 10000 points */
513 settings.ghostnames = 0; /* Normal names */
514 settings.showfps = 0; /* Do not show FPS */
515
516 /* Initialise the hardware */
517 init_PacmanMachine(settings_to_dip(settings));
518
519 /* Load the romset */
520 if (loadROMS()) {
521 start_time = *rb->current_tick-1;
522 do {
523 status = gameProc();
524 } while (!status);
525 } else {
526 rb->splash(HZ*2,true,"No ROMs in /.rockbox/pacman/");
527 }
528
529#ifdef HAVE_ADJUSTABLE_CPU_FREQ
530 rb->cpu_boost(false);
531#endif
532
533 return PLUGIN_OK;
534}