A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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 * AI code (c) 2017 Moshe Piekarski
17 *
18 * ToDo convert all score to pinky location
19 *
20 * This program is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU General Public License
22 * as published by the Free Software Foundation; either version 2
23 * of the License, or (at your option) any later version.
24 *
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
26 * KIND, either express or implied.
27 *
28 ****************************************************************************/
29
30#include "plugin.h"
31#include "arcade.h"
32#include "pacbox.h"
33#include "pacbox_lcd.h"
34#include "wsg3.h"
35#include "lib/configfile.h"
36#include "lib/playback_control.h"
37#include "lib/helper.h"
38static fb_data *lcd_fb;
39
40/*Allows split screen jump and makes pacman invincible if you start at 18 credits (for testing purposes)*/
41//#define CHEATS 1
42
43/* Enable AI on all targets */
44#define AI 1
45
46struct pacman_settings {
47 int difficulty;
48 int numlives;
49 int bonus;
50 int ghostnames;
51 int showfps;
52 int sound;
53 int ai;
54};
55
56static struct pacman_settings settings;
57static struct pacman_settings old_settings;
58static bool sound_playing = false;
59
60#define SETTINGS_VERSION 2
61#define SETTINGS_MIN_VERSION 2
62#define SETTINGS_FILENAME "pacbox.cfg"
63
64static char* difficulty_options[] = { "Normal", "Hard" };
65static char* numlives_options[] = { "1", "2", "3", "5" };
66static char* bonus_options[] = {"10000", "15000", "20000", "No Bonus"};
67static char* ghostnames_options[] = {"Normal", "Alternate"};
68static char* yesno_options[] = {"No", "Yes"};
69
70#ifdef AI
71static unsigned char ai_direction[15][205] = /* level turn directions */
72{
73 {2,1,3,1,2,0,3,0,3,1,2,1,3,1,3,0,3,0,2,0,3,0,2,1,3,1,2,0,2,1,3,1,3,0,3,1,3,1,2,0,3,0,3,0,3,0,2,0,3,1,2,0,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,1,3,1,2,1,2,1,3,0,2,0,2,0,2,1,2,0,3,0,2,1,3,0,3,2,1,3,0,3,1,2,1,2,0,2,3,1,3,0,3,0,2,4}, /* first level */
74 {2,0,3,1,3,1,2,0,2,0,2,0,3,1,3,1,2,1,2,0,3,0,3,0,3,1,3,1,2,1,2,0,2,0,3,0,2,0,3,1,2,0,3,1,2,0,2,1,2,1,2,0,2,0,3,1,0,3,0,3,2,0,2,0,2,0,3,1,0,3,2,0,2,1,2,1,3,0,3,1,3,1,3,0,3,0,1,2,1,3,1,3,1,2},/* second level*/
75 {2,1,3,1,2,0,2,0,3,0,2,1,3,1,2,1,2,0,3,0,3,0,3,1,3,1,2,1,2,0,2,0,2,3,0,3,0,3,0,2,0,3,1,2,0,2,0,3,1,2,1,2,1,3,1,3,1,2,0,2,1,3,1,3,0,3,0,2,0,3,1,2,1,2,0,3,1,3,1,2,1,2,0,2,0,2,0,3,1,2,0,2,1,3,0,1,2,1,2,0},/*third level*/
76 {2,1,3,1,2,0,2,0,3,0,2,1,3,1,2,1,2,0,3,0,3,0,3,1,3,1,2,1,2,0,2,0,2,3,0,3,0,3,0,2,0,3,1,2,0,2,0,3,1,2,1,2,1,3,1,3,1,2,0,2,0,3,1,3,1,2,1,2,0,2,0,3,0,2,0,3,0,2,1,2,1,3,0,3,0,2,1,0,3,1,3,1,2,1,2,1,3,1,3,0,3,0,2,0,2,1,2,1,3,1,3,0,3,1,3,2,0,2,0,2,0,2},/*level four*/
77 {2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,2,1,3,1,3,0,3,0,2,0,2,1,3,1,3,0,2,1,2,0,3,1,3,1,3,1,2,1,2,0,2,1,2,0,2,0,3,1,3,0,2,0,2},/*levels 5,7,8,11*/
78 {2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,2,0,3,0,2,1,2,0,3,0,2,1,3,0,2,1,3,1,3,0,3,1,2,1,2,1,2,0,2,0,3,1,3,0,1,0,3,1},/*level six*/
79 {2,1,3,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,0,2,1,3,0,3,1,3,0,2,1,2,0,3,1,3,1,2,1,3,1,3,0,2,0,3,1,2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,3,0,3,1,2,1,3,1,3,1,2,0,3,0,2,1,3,1,2,0,3,0,2,1,3,1,3,2,1,3,0,2,0,2,0,2,1,3,1,2,1,3,1,2,0,3,0,2,0,3,1,3,1,2,0,2,0,3,1,2,0,3,0,2,1,3,0,3,1,2,1,3,1,2,1,2,1,2,0,2,1,3,0,2,1,2,1,2,0,2,1,2,0,2,1,2,0,2,0,3,0,2,0,3,1,2,0,2,1,3,1,2,1,3,1,3,0,2},/*level nine*/
80 {2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,3,0,2,1,2,1,2,0,3,0,2,0,3,1,2,0,2,1,2,1,2,1,2,0,2,0,3,0,2,1,3,0,3,0,1,2,1,3,1,2,1,0,3,0,2,0,2,1,2,0,2,0,3,1,3,0,2,1,2,1,2,0,3,0,2,0,3,1,2,0,2,1,2,1,2,1,2,0,2,0,3,0,2,1,3,0,3,0,1,2,1,3,1,2,1,0,3,0,2,0,2,1,2,0,2,0,3,1,3,0,2,1,2,1,2,},/*level ten*/
81 {2,1,3,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,0,2,1,3,0,3,1,3,0,2,1,2,0,3,1,3,1,2,1,3,1,3,0,2,0,3,1,2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,3,0,3,1,2,1,3,1,3,1,2,0,3,0,2,1,3,1,2,0,3,0,2,1,3,1,3,2,1,3,0,2,0,2,0,2,1,3,1,2,1,3,1,2,0,3,0,2,0,3,1,3,1,2,0,2,0,3,1,2,0,3,0,2,1,3,0,1,3,0,3,1,3,0,2,1,3,1,3,1,2,1,2,1,3,0,2,1,2,1,2,0,2,0,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,1,3,1,2,0,2},/*level twelve*/
82 {2,2,1,2,1,3,0,3,0,2,0,2,0,3,0,2,0,2,0,3,1,2,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,2,1,2,0,2,0,3,0,2,0,3,1,2,0,3,1,2,0,2,3,1,2,1,2,1,3,1,2,0,2,0,2,0,3,1,2,1,2,1,2,0,2,1,2,1,3,1,3,0,3,0,3,0,3,0,2,1,3,1,3,1,3,0,3,0,2,0,3,0,2,1,3,0,3,1,3,2,1},/*level fourteen*/
83 {2,1,3,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,0,2,1,3,0,3,1,3,0,2,1,2,0,3,1,3,1,2,1,3,1,3,0,2,0,3,1,2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,1,2,0,2,0,2,0,3,0,3,1,3,1,3,0,3,1,2,1,3,1,3,1,2,0,3,0,2,1,3,1,2,0,3,0,2,1,3,1,3,2,1,3,0,2,0,2,0,2,1,3,1,2,1,3,1,2,0,3,0,2,0,3,1,3,1,2,0,3,0,3,1,2,1,3,1,3,0,2,0,3,0,2,0,3,0,2,1,3,1,3,0,3,0,3,0,2,1,3,1,2,1,3,0,3,1,2,0,3,0,2,0,2,0,3,1,0,1,2,1,3,0,2,0,2,1,2,0,2,1,3,2},
84 {2,2,1,2,1,3,0,3,0,2,0,3,0,2,0,2,0,3,0,2,1,3,1,2,1,3,1,3,1,3,1,2,0,2,0,2,0,3,1,2,0,2,0,2,0,3,0,3,1,3,0,3,1,2,1,2,0,2,0,3,0,2,1,3,1,2,0,2,0,2,1,3,1,2,1,3,1,2,1,3,0,2,0,3,0,2},
85 {2,2,1,2,1,3,0,3,0,2,0,3,0,2,0,2,0,3,0,2,1,3,1,2,1,3,1,3,1,3,1,2,0,2,0,2,0,3,1,2,0,2,0,2,0,3,0,3,1,3,1,2,1,3,1,2,1,2,1,3,1,3,0,2,0,3,1,3,0,2,1,3,1,2,0,2,1,3,1,2,1,3,1,3,1,3,1,2,1},
86 {2,1,3,1,2,0,3,0,3,1,2,1,3,1,3,0,3,0,3,0,2,1,3,1,3,0,3,0,2,0,2,1,2,1,3,1,2,1,2,0,2,0,3,0,2,0,3,0,3,1,2,0,2,1,3,1,3,1,3,0,3,0,2,1,3,1,2,1,2,0,2,0,3,0,3,1,2,1,3,0},
87 {2,1,3,1,2,0,3,0,2,0,3,1,2,0,3,1,2,1,2,0,2,0,2,0,3,0,3,0,2,1,3,0,2,1,3,1,2,1,2,1,2,0,2,1,2,1,2,1,3,0,1,3,0,1,2,1,2,1,2,0,3,1,2}
88};
89static unsigned char ai_location[15][205] = /* level turn locations */
90{
91 {0,52,58,57,61,34,60,37,54,43,55,42,58,43,61,46,60,49,57,48,54,49,51,42,52,43,55,39,54,34,55,34,58,37,26,52,58,57,61,48,60,49,57,52,42,57,39,48,35,57,40,54,39,48,35,52,37,51,40,48,43,45,42,42,39,39,35,43,37,49,40,48,43,42,49,49,45,45,42,42,39,39,40,34,39,43,35,34,40,37,39,3,39,55,52,54,57,55,57,58,54,57,3,52,58,55,57,57,54,53}, /* first level */
92 {0,47,54,52,58,57,61,45,60,42,57,39,54,43,55,49,58,48,61,34,60,37,54,40,51,49,52,57,55,57,58,54,54,51,48,52,39,48,35,57,40,54,39,57,40,54,35,48,37,40,56,37,57,33,54,37,2,54,40,51,4,42,48,39,39,34,35,37,1,39,4,45,35,39,37,34,40,37,39,40,40,43,43,46,42,49,16,40,48,42,48,45,52,55}, /*second level */
93 {0,52,58,57,61,45,60,42,57,43,54,39,55,49,58,48,61,34,60,37,54,40,51,49,52,57,55,57,58,54,54,51,51,4,49,48,52,42,57,39,48,35,57,40,54,39,48,35,52,37,51,40,48,43,49,46,52,55,39,54,34,55,34,58,37,57,43,54,42,51,49,52,48,55,39,35,43,37,49,40,48,43,45,42,42,39,39,35,43,37,39,35,34,37,43,3,37,39,40,34}, /*third level */
94 {0,52,58,57,61,45,60,42,57,43,54,39,55,49,58,48,61,34,60,37,54,40,51,49,52,57,55,57,58,54,54,51,51,4,49,48,52,42,57,39,48,35,57,40,54,39,48,35,52,37,51,40,48,43,49,46,52,55,45,54,39,48,40,49,49,52,48,55,45,54,42,45,43,42,42,39,43,35,39,37,34,40,37,39,43,35,34,1,35,37,37,49,40,48,43,42,52,43,55,46,54,49,51,42,48,39,52,34,55,34,58,37,54,43,55,4,45,54,42,48,39,1}, /*fourth level */
95 {0,14,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,57,35,54,46,51,49,42,48,39,42,34,39,37,35,43,37,49,40,48,43,49,46,52,42,57,39,39,35,34,37,40,3,43,35,39,40,34,39,40,40,43,43,49,52,48,55,45,54,39,58,36,57,34,54,37,55,43,54,39,51}, /*fifth level */
96 {0,14,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,57,35,54,46,51,49,42,45,43,42,42,39,52,40,57,39,51,40,48,43,45,42,42,39,43,35,39,40,34,39,37,35,34,37,37,35,34,3,37,46,40,45,49,49,42,52,39,58,36,57,34,54,37,55,52,4,54,48,37,},/*sixth level*/
97 {0,52,58,57,61,45,60,42,57,43,54,42,51,49,48,52,55,45,54,42,48,39,52,40,45,49,52,52,48,51,49,42,45,49,52,52,55,42,58,43,61,57,60,54,54,57,55,57,58,57,61,48,60,49,57,48,54,52,55,48,54,52,55,51,58,48,61,45,60,42,57,39,48,40,45,49,46,40,49,49,48,52,52,48,55,52,58,57,61,48,60,49,57,42,58,43,61,34,60,37,54,34,55,34,58,1,34,61,43,60,42,57,39,48,54,52,57,55,57,58,57,61,48,60,49,57,39,48,40,52,43,55,39,42,34,39,37,40,34,39,37,35,34,37,37,35,43,37,42,40,43,43,42,46,51,49,42,48,39,55,52,48,51,49,42,52,39,48,54,52,51,51,42,52,39,48,54,42,57,39,54,35,57,37,4,35,48,37,49,40,48,43,49,46,52,39},/*ninth level*/
98 {0,14,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,57,35,54,46,51,49,42,45,43,42,42,39,57,40,54,39,51,40,48,43,42,46,39,42,34,39,37,35,34,40,37,39,43,3,37,42,40,43,43,42,4,45,43,42,42,39,39,58,36,57,34,54,37,55,52,48,51,49,42,52,45,60,},/*tenth level*/
99 {0,52,58,57,61,45,60,42,57,43,54,42,51,49,48,52,55,45,54,42,48,39,52,40,45,49,52,52,48,51,49,42,45,49,52,52,55,42,58,43,61,57,60,54,54,57,55,57,58,57,61,48,60,49,57,48,54,52,55,48,54,52,55,51,58,48,61,45,60,42,57,39,48,40,45,49,46,40,49,49,48,52,52,48,55,52,58,57,61,48,60,49,57,42,58,43,61,34,60,37,54,34,55,34,58,1,34,61,43,60,42,57,39,48,54,52,57,55,57,58,57,61,48,60,49,57,39,48,40,52,43,55,39,42,34,39,37,40,34,39,37,35,34,37,37,4,46,40,45,49,46,52,39,42,40,43,43,49,49,42,52,39,55,52,48,51,49,42,52,39,48,54,39,48,35,57,37,51,40,48,43,45,42,42,39,39,35,43,37,57,40,54,35},/*twelfth level*/
100 {0,14,39,58,34,61,46,60,49,57,45,54,42,45,43,42,42,39,39,35,43,37,42,40,43,43,42,49,49,52,48,55,42,58,43,61,57,60,54,54,57,55,57,58,54,57,48,54,52,39,48,35,52,37,48,35,57,37,54,35,4,52,37,51,40,48,43,49,49,42,48,39,42,34,39,57,40,54,46,51,49,42,48,39,52,34,55,34,58,37,54,40,51,49,48,52,39,42,40,43,43,49,52,52,48,37,42,34,39,37,35,34,37,37,35,43,37,4,39},/*fourteenth level*/
101 {0,52,58,57,61,45,60,42,57,43,54,42,51,49,48,52,55,45,54,42,48,39,52,40,45,45,52,52,48,51,49,42,45,49,52,52,55,42,58,43,61,57,60,54,54,57,55,57,58,57,61,48,60,49,57,48,54,52,55,48,54,52,55,51,58,48,61,45,60,42,57,39,48,40,45,49,46,40,49,49,48,52,52,48,55,52,58,57,61,48,60,49,57,42,58,43,61,34,60,37,54,34,55,34,58,1,34,61,43,60,42,57,39,48,54,52,57,55,57,58,57,61,48,60,49,57,39,48,40,52,43,55,39,54,40,45,49,49,42,52,43,55,52,54,51,48,37,41,34,39,37,35,34,37,37,46,40,45,46,42,49,39,42,40,43,43,42,49,49,48,52,55,39,54,40,48,54,39,48,35,57,2,3,37,54,40,57,39,54,35,48,37,45,35,39,37,40},
102 {0,14,39,58,34,61,46,60,49,57,48,54,52,48,51,45,48,42,49,39,42,40,43,43,42,49,49,52,52,58,57,61,45,60,42,57,39,39,52,55,45,54,39,42,34,39,37,35,43,37,46,35,52,46,51,49,42,48,54,42,57,35,54,37,57,40,54,39,45,35,34,40,37,52,34,55,34,58,34,61,57,60,57,57,57,53},
103 {0,14,39,58,34,61,46,60,49,57,48,54,52,48,51,45,48,42,49,39,42,40,43,43,42,49,49,52,52,58,57,61,45,60,42,57,39,39,52,55,45,54,39,42,34,39,37,35,43,37,49,40,48,43,49,49,42,52,34,55,34,58,37,42,34,35,43,37,57,35,54,37,57,40,54,35,48,37,49,40,48,43,49,46,52,52,57,55,57},
104 {0,52,58,57,61,34,60,37,54,43,55,42,58,43,61,46,60,49,57,52,54,48,55,52,58,55,57,57,54,54,48,51,49,42,52,43,55,39,58,36,57,34,54,37,42,34,39,37,35,43,37,39,35,34,37,37,46,40,49,49,48,52,35,48,37,49,40,48,43,45,42,42,39,52,35,57,37,54,40,57},
105 {0,52,58,57,61,48,60,49,57,48,54,52,55,48,54,57,55,57,58,54,48,51,45,48,42,49,39,52,35,48,37,52,34,48,37,52,46,51,49,45,37,39,33,38,35,35,49,34,60,37,53,60,42,55,60,38,35,58,40,54,35,57,37}
106};
107#endif
108
109static struct configdata config[] =
110{
111 {TYPE_ENUM, 0, 2, { .int_p = &settings.difficulty }, "Difficulty",
112 difficulty_options},
113 {TYPE_ENUM, 0, 4, { .int_p = &settings.numlives }, "Pacmen Per Game",
114 numlives_options},
115 {TYPE_ENUM, 0, 4, { .int_p = &settings.bonus }, "Bonus", bonus_options},
116 {TYPE_ENUM, 0, 2, { .int_p = &settings.ghostnames }, "Ghost Names",
117 ghostnames_options},
118 {TYPE_ENUM, 0, 2, { .int_p = &settings.showfps }, "Show FPS",
119 yesno_options},
120 {TYPE_ENUM, 0, 2, { .int_p = &settings.sound }, "Sound",
121 yesno_options},
122#ifdef AI
123 {TYPE_ENUM, 0, 2, { .int_p = &settings.ai }, "AI",
124 yesno_options},
125#endif
126};
127
128static bool loadFile( const char * name, unsigned char * buf, int len )
129{
130 char filename[MAX_PATH];
131
132 rb->snprintf(filename,sizeof(filename), ROCKBOX_DIR "/pacman/%s",name);
133
134 int fd = rb->open( filename, O_RDONLY);
135
136 if( fd < 0 ) {
137 return false;
138 }
139
140 int n = rb->read( fd, buf, len);
141
142 rb->close( fd );
143
144 if( n != len ) {
145 return false;
146 }
147
148 return true;
149}
150
151static bool loadROMS( void )
152{
153 bool romsLoaded = false;
154
155 romsLoaded = loadFile( "pacman.6e", ram_, 0x1000) &&
156 loadFile( "pacman.6f", ram_+0x1000, 0x1000) &&
157 loadFile( "pacman.6h", ram_+0x2000, 0x1000) &&
158 loadFile( "pacman.6j", ram_+0x3000, 0x1000) &&
159 loadFile( "pacman.5e", charset_rom_, 0x1000) &&
160 loadFile( "pacman.5f", spriteset_rom_, 0x1000);
161
162 if( romsLoaded ) {
163 decodeROMs();
164 reset_PacmanMachine();
165 }
166
167 return romsLoaded;
168}
169
170/* A buffer to render Pacman's 244x288 screen into */
171static unsigned char video_buffer[ScreenWidth*ScreenHeight] __attribute__ ((aligned (16)));
172
173static long start_time;
174static long video_frames = 0;
175
176static int dipDifficulty[] = { DipDifficulty_Normal, DipDifficulty_Hard };
177static int dipLives[] = { DipLives_1, DipLives_2, DipLives_3, DipLives_5 };
178static int dipBonus[] = { DipBonus_10000, DipBonus_15000, DipBonus_20000,
179 DipBonus_None };
180static int dipGhostNames[] = { DipGhostNames_Normal, DipGhostNames_Alternate };
181
182static int settings_to_dip(struct pacman_settings settings)
183{
184 return ( DipPlay_OneCoinOneGame |
185 DipCabinet_Upright |
186 DipMode_Play |
187 DipRackAdvance_Off |
188
189 dipDifficulty[settings.difficulty] |
190 dipLives[settings.numlives] |
191 dipBonus[settings.bonus] |
192 dipGhostNames[settings.ghostnames]
193 );
194}
195
196static bool pacbox_menu(void)
197{
198 int selected=0;
199 int result;
200 int menu_quit=0;
201 int new_setting;
202 bool need_restart = false;
203
204 static const struct opt_items noyes[2] = {
205 { "No", -1 },
206 { "Yes", -1 },
207 };
208
209 static const struct opt_items difficulty_options[2] = {
210 { "Normal", -1 },
211 { "Harder", -1 },
212 };
213
214 static const struct opt_items numlives_options[4] = {
215 { "1", -1 },
216 { "2", -1 },
217 { "3", -1 },
218 { "5", -1 },
219 };
220
221 static const struct opt_items bonus_options[4] = {
222 { "10000 points", -1 },
223 { "15000 points", -1 },
224 { "20000 points", -1 },
225 { "No bonus", -1 },
226 };
227
228 static const struct opt_items ghostname_options[2] = {
229 { "Normal", -1 },
230 { "Alternate", -1 },
231 };
232
233 enum
234 {
235 PBMI_DIFFICULTY = 0,
236 PBMI_PACMEN_PER_GAME,
237 PBMI_BONUS_LIFE,
238 PBMI_GHOST_NAMES,
239 PBMI_DISPLAY_FPS,
240 PBMI_SOUND,
241#ifdef AI
242 PBMI_AI,
243#endif
244 PBMI_RESTART,
245 PBMI_QUIT,
246 };
247
248 MENUITEM_STRINGLIST(menu, "Pacbox Menu", NULL,
249 "Difficulty", "Pacmen Per Game", "Bonus Life",
250 "Ghost Names", "Display FPS", "Sound",
251#ifdef AI
252 "AI",
253#endif
254 "Restart", "Quit");
255
256 rb->button_clear_queue();
257
258#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
259 rb->lcd_set_mode(LCD_MODE_RGB565);
260#endif
261
262 while (!menu_quit) {
263 result=rb->do_menu(&menu, &selected, NULL, false);
264
265 switch(result)
266 {
267 case PBMI_DIFFICULTY:
268 new_setting=settings.difficulty;
269 rb->set_option("Difficulty", &new_setting, RB_INT,
270 difficulty_options , 2, NULL);
271 if (new_setting != settings.difficulty) {
272 settings.difficulty=new_setting;
273 need_restart=true;
274 }
275 break;
276 case PBMI_PACMEN_PER_GAME:
277 new_setting=settings.numlives;
278 rb->set_option("Pacmen Per Game", &new_setting, RB_INT,
279 numlives_options , 4, NULL);
280 if (new_setting != settings.numlives) {
281 settings.numlives=new_setting;
282 need_restart=true;
283 }
284 break;
285 case PBMI_BONUS_LIFE:
286 new_setting=settings.bonus;
287 rb->set_option("Bonus Life", &new_setting, RB_INT,
288 bonus_options , 4, NULL);
289 if (new_setting != settings.bonus) {
290 settings.bonus=new_setting;
291 need_restart=true;
292 }
293 break;
294 case PBMI_GHOST_NAMES:
295 new_setting=settings.ghostnames;
296 rb->set_option("Ghost Names", &new_setting, RB_INT,
297 ghostname_options , 2, NULL);
298 if (new_setting != settings.ghostnames) {
299 settings.ghostnames=new_setting;
300 need_restart=true;
301 }
302 break;
303 case PBMI_DISPLAY_FPS:
304 rb->set_option("Display FPS",&settings.showfps, RB_INT,
305 noyes, 2, NULL);
306 break;
307 case PBMI_SOUND:
308 rb->set_option("Sound",&settings.sound, RB_INT,
309 noyes, 2, NULL);
310 break;
311#ifdef AI
312 case PBMI_AI:
313 rb->set_option("AI",&settings.ai, RB_INT,
314 noyes, 2, NULL);
315 break;
316#endif
317 case PBMI_RESTART:
318 need_restart=true;
319 menu_quit=1;
320 break;
321 default:
322 menu_quit=1;
323 break;
324 }
325 }
326
327#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
328 rb->lcd_set_mode(LCD_MODE_PAL256);
329#endif
330
331 if (need_restart) {
332 init_PacmanMachine(settings_to_dip(settings));
333 }
334
335 /* Possible results:
336 exit game
337 restart game
338 usb connected
339 */
340 return (result==PBMI_QUIT);
341}
342
343/* Sound is emulated in ISR context, so not much is done per sound frame */
344#define NBSAMPLES 128
345static uint32_t sound_buf[NBSAMPLES];
346#if CONFIG_CPU == MCF5249
347/* Not enough to put this in IRAM */
348static int16_t raw_buf[NBSAMPLES];
349#else
350static int16_t raw_buf[NBSAMPLES] IBSS_ATTR;
351#endif
352
353/*
354 Audio callback
355 */
356static void get_more(const void **start, size_t *size)
357{
358 int32_t *out, *outend;
359 int16_t *raw;
360
361 /* Emulate the audio for the current register settings */
362 playSound(raw_buf, NBSAMPLES);
363
364 out = sound_buf;
365 outend = out + NBSAMPLES;
366 raw = raw_buf;
367
368 /* Convert to stereo */
369 do
370 {
371 uint32_t sample = (uint16_t)*raw++;
372 *out++ = sample | (sample << 16);
373 }
374 while (out < outend);
375
376 *start = sound_buf;
377 *size = NBSAMPLES*sizeof(sound_buf[0]);
378}
379
380/*
381 Start the sound emulation
382*/
383static void start_sound(void)
384{
385 int sr_index;
386
387 if (sound_playing)
388 return;
389
390#ifndef PLUGIN_USE_IRAM
391 /* Ensure control of PCM - stopping music itn't obligatory */
392 rb->plugin_get_audio_buffer(NULL);
393#endif
394
395 /* Get the closest rate >= to what is preferred */
396 sr_index = rb->round_value_to_list32(PREFERRED_SAMPLING_RATE,
397 rb->hw_freq_sampr, HW_NUM_FREQ, false);
398
399 if (rb->hw_freq_sampr[sr_index] < PREFERRED_SAMPLING_RATE
400 && sr_index > 0)
401 {
402 /* Round up */
403 sr_index--;
404 }
405
406 wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]);
407
408 rb->pcm_set_frequency(rb->hw_freq_sampr[sr_index]);
409 rb->pcm_play_data(get_more, NULL, NULL, 0);
410
411 sound_playing = true;
412}
413
414/*
415 Stop the sound emulation
416*/
417static void stop_sound(void)
418{
419 if (!sound_playing)
420 return;
421
422 rb->pcm_play_stop();
423 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
424
425 sound_playing = false;
426}
427
428/* use buttons for joystick */
429void joystick(void)
430{
431 int status;
432 /* Check the button status */
433 status = rb->button_status();
434 rb->button_clear_queue();
435 /*handle buttons if AI is off */
436#ifdef PACMAN_HAS_REMOTE
437 setDeviceMode( Joy1_Left, (status & PACMAN_LEFT || status == PACMAN_RC_LEFT) ? DeviceOn : DeviceOff);
438 setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT || status == PACMAN_RC_RIGHT) ? DeviceOn : DeviceOff);
439 setDeviceMode( Joy1_Up, (status & PACMAN_UP || status == PACMAN_RC_UP) ? DeviceOn : DeviceOff);
440 setDeviceMode( Joy1_Down, (status & PACMAN_DOWN || status == PACMAN_RC_DOWN) ? DeviceOn : DeviceOff);
441 setDeviceMode( CoinSlot_1, (status & PACMAN_COIN || status == PACMAN_RC_COIN) ? DeviceOn : DeviceOff);
442 setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP || status == PACMAN_RC_1UP) ? DeviceOn : DeviceOff);
443 setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP || status == PACMAN_RC_2UP) ? DeviceOn : DeviceOff);
444#else
445 setDeviceMode( Joy1_Left, (status & PACMAN_LEFT) ? DeviceOn : DeviceOff);
446 setDeviceMode( Joy1_Right, (status & PACMAN_RIGHT) ? DeviceOn : DeviceOff);
447 setDeviceMode( Joy1_Up, (status & PACMAN_UP) ? DeviceOn : DeviceOff);
448 setDeviceMode( Joy1_Down, (status & PACMAN_DOWN) ? DeviceOn : DeviceOff);
449 setDeviceMode( CoinSlot_1, (status & PACMAN_COIN) ? DeviceOn : DeviceOff);
450 setDeviceMode( Key_OnePlayer, (status & PACMAN_1UP) ? DeviceOn : DeviceOff);
451#ifdef PACMAN_2UP
452 setDeviceMode( Key_TwoPlayers, (status & PACMAN_2UP) ? DeviceOn : DeviceOff);
453#endif
454#endif
455#ifdef CHEATS
456// skip level for testing purposes
457 if(status == SKIP_LEVEL)
458 {
459//dots
460 ram_[0x4E0E] = 242;
461//level
462 ram_[0x4E13] = 254;
463 }
464#endif
465}
466#ifdef AI
467/* blank controls */
468void clear_joystick(void)
469{
470 setDeviceMode( Joy1_Left, DeviceOff);
471 setDeviceMode( Joy1_Right, DeviceOff);
472 setDeviceMode( Joy1_Up, DeviceOff);
473 setDeviceMode( Joy1_Down, DeviceOff);
474
475}
476
477/* Make turns */
478void ai_turn( unsigned char level, unsigned char turn)
479{
480 switch(ai_direction[level][turn])
481 {
482 case 0:
483 clear_joystick();
484 setDeviceMode( Joy1_Up, DeviceOn);
485 break;
486 case 1:
487 clear_joystick();
488 setDeviceMode( Joy1_Down, DeviceOn);
489 break;
490 case 2:
491 clear_joystick();
492 setDeviceMode( Joy1_Right, DeviceOn);
493 break;
494 case 3:
495 clear_joystick();
496 setDeviceMode( Joy1_Left, DeviceOn);
497 break;
498 case 4:
499 clear_joystick();
500 break;
501 }
502}
503/*
504 Decide turns automatically
505*/
506unsigned char ai( unsigned char turn )
507{
508 unsigned char position; /* pac-mans current position */
509 unsigned char level; /* current game level */
510 unsigned char map[20] = {0,1,2,3,4,5,4,4,6,7,4,8,8,9,10,10,11,10,12,12};
511
512 /*Select level map*/
513 if(ram_[0x4E13] < 20)
514 level=map[ram_[0x4E13]];
515 else if(ram_[0x4E13] != 255)
516 level=13;
517 else
518 level=14;
519
520 /* AI can't start in middle of a level */
521 if( turn > 210)
522 {
523 rb->splash(HZ/2, "AI will engage at next level start");
524 return 0;
525 }
526
527 /* reset joystick direction on level start */
528 if(!(ram_[0x4E0E] || turn))
529 {
530 /*levels that start facing right */
531 if((level != 4) && (level != 11) && (level != 7) && (level != 5) && (level != 9) && (level !=12))
532 clear_joystick();
533 else
534 {
535 clear_joystick();
536 setDeviceMode( Joy1_Right, DeviceOn);
537 }
538 return 1;
539 }
540 if( turn == 0)
541 {
542 if( (ram_[0x4E0E] == 1) && (ram_[0x4D3A] == 47))
543 {
544 turn=1;
545 }
546 joystick();
547 return turn;
548 }
549
550 if((turn != 0) && (turn !=209))
551 {
552 /* set which axis to look for pac-man along */
553 position = ram_[0x4D3A];
554 if( ai_direction[level][turn-1] < 2)
555 {
556 position = ram_[0x4D39];
557 }
558
559
560 /*move joystick if necessary */
561 if(ai_location[level][turn] < 30)
562 {
563 if((ai_location[level][turn] < 10) && (ai_location[level][turn] > 0)) /* handle turns using ghosts eaten as basis for turn timing */
564 {
565 if( ram_[0x4DD0] == ai_location[level][turn])
566 {
567 ai_turn(level,turn);
568 turn++;
569 }
570 }
571
572 if( ram_[0x4D31] == (ai_location[level][turn] + 30)) /* handle turns using pinky's location as basis for turn timing */
573 {
574 ai_turn(level,turn);
575 turn++;
576 }
577 }else if( position == ai_location[level][turn] ) /* handle turns using pacman's location as basis for turn timing */
578 {
579 ai_turn(level,turn);
580 turn++;
581 }
582 }
583
584 /* reset turn counter and joystick direction on level start */
585 if(ram_[0x4E0E] == 0 )
586 {
587 /*levels that start facing right */
588 if((level != 4) && (level != 11) && (level != 7) && (level != 5) && (level != 9) && (level !=12))
589 clear_joystick();
590 else
591 {
592 clear_joystick();
593 setDeviceMode( Joy1_Right, DeviceOn);
594 }
595 return 1;
596 }
597 return turn;
598}
599#endif
600
601/*
602 Runs the game engine for one frame.
603*/
604static int gameProc( void )
605{
606 int fps;
607 int status;
608 long end_time;
609 int frame_counter = 0;
610 int yield_counter = 0;
611#ifdef AI
612 unsigned char turn = 250;
613#endif
614
615 if (settings.sound)
616 start_sound();
617
618 while (1)
619 {
620 /* Run the machine for one frame (1/60th second) */
621 run();
622
623/*Make Pac-man invincible*/
624#ifdef CHEATS
625 if(ram_[0x4E6E]== 23)
626 ram_[0x4DA5]=00;
627#endif
628
629
630
631 frame_counter++;
632
633 /* Check the button status */
634 status = rb->button_status();
635 rb->button_clear_queue();
636
637#ifdef HAS_BUTTON_HOLD
638 if (rb->button_hold())
639 status = PACMAN_MENU;
640#endif
641
642 if ((status & PACMAN_MENU) == PACMAN_MENU
643#ifdef PACMAN_RC_MENU
644 || status == PACMAN_RC_MENU
645#endif
646 ) {
647 bool menu_res;
648
649 end_time = *rb->current_tick;
650
651 stop_sound();
652
653 menu_res = pacbox_menu();
654
655 rb->lcd_clear_display();
656#ifdef HAVE_REMOTE_LCD
657 rb->lcd_remote_clear_display();
658 rb->lcd_remote_update();
659#endif
660 if (menu_res)
661 return 1;
662
663 if (settings.sound)
664 start_sound();
665
666 start_time += *rb->current_tick-end_time;
667 }
668#ifdef AI
669 if(!settings.ai)
670 {
671 joystick();
672 turn = 250;
673 }
674 /* run ai */
675 if (settings.ai && !ram_[0x4E02])
676 turn = ai(turn);
677 else
678 joystick();
679#else
680 joystick();
681#endif
682 /* We only update the screen every third frame - Pacman's native
683 framerate is 60fps, so we are attempting to display 20fps */
684 if (frame_counter == 60 / FPS) {
685
686 frame_counter = 0;
687 video_frames++;
688
689 yield_counter ++;
690
691 if (yield_counter == FPS) {
692 yield_counter = 0;
693 rb->yield ();
694 }
695
696 /* The following functions render the Pacman screen from the
697 contents of the video and color ram. We first update the
698 background, and then draw the Sprites on top.
699 */
700
701 renderBackground( video_buffer );
702 renderSprites( video_buffer );
703
704#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
705 rb->lcd_blit_pal256( video_buffer, 0, 0, XOFS, YOFS,
706 ScreenWidth, ScreenHeight);
707#else
708 blit_display(lcd_fb ,video_buffer);
709#endif
710
711 if (settings.showfps) {
712 fps = (video_frames*HZ*100) / (*rb->current_tick-start_time);
713 rb->lcd_putsxyf(0,0,"%d.%02d / %d fps ",fps/100,fps%100,FPS);
714 }
715
716#if !defined(HAVE_LCD_MODES) || \
717 defined(HAVE_LCD_MODES) && !(HAVE_LCD_MODES & LCD_MODE_PAL256)
718 rb->lcd_update();
719#endif
720
721 /* Keep the framerate at Pacman's 60fps */
722 end_time = start_time + (video_frames*HZ)/FPS;
723 while (TIME_BEFORE(*rb->current_tick,end_time)) {
724 rb->sleep(1);
725 }
726 }
727 }
728
729 stop_sound();
730
731 return 0;
732}
733
734enum plugin_status plugin_start(const void* parameter)
735{
736 (void)parameter;
737
738#ifdef HAVE_ADJUSTABLE_CPU_FREQ
739 rb->cpu_boost(true);
740#endif
741 rb->lcd_set_backdrop(NULL);
742 rb->lcd_set_foreground(LCD_WHITE);
743 rb->lcd_set_background(LCD_BLACK);
744 rb->lcd_clear_display();
745 rb->lcd_update();
746
747 struct viewport *vp_main = rb->lcd_set_viewport(NULL);
748 lcd_fb = vp_main->buffer->fb_ptr;
749
750 /* Set the default settings */
751 settings.difficulty = 0; /* Normal */
752 settings.numlives = 2; /* 3 lives */
753 settings.bonus = 0; /* 10000 points */
754 settings.ghostnames = 0; /* Normal names */
755 settings.showfps = 0; /* Do not show FPS */
756 settings.sound = 0; /* Sound off by default */
757 settings.ai = 0; /* AI off by default */
758
759 if (configfile_load(SETTINGS_FILENAME, config,
760 sizeof(config)/sizeof(*config),
761 SETTINGS_MIN_VERSION
762 ) < 0)
763 {
764 /* If the loading failed, save a new config file (as the disk is
765 already spinning) */
766 configfile_save(SETTINGS_FILENAME, config,
767 sizeof(config)/sizeof(*config),
768 SETTINGS_VERSION);
769 }
770
771 /* Keep a copy of the saved version of the settings - so we can check if
772 the settings have changed when we quit */
773 old_settings = settings;
774
775#ifdef HAVE_BACKLIGHT
776 /*Turn off backlight for ai*/
777 if(settings.ai)
778 backlight_ignore_timeout();
779#endif
780
781 /* Initialise the hardware */
782 init_PacmanMachine(settings_to_dip(settings));
783
784#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
785 rb->lcd_set_mode(LCD_MODE_PAL256);
786#endif
787
788 /* Load the romset */
789 if (loadROMS()) {
790 start_time = *rb->current_tick-1;
791
792 gameProc();
793
794 /* Save the user settings if they have changed */
795 if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) {
796 rb->splash(0, "Saving settings...");
797 configfile_save(SETTINGS_FILENAME, config,
798 sizeof(config)/sizeof(*config),
799 SETTINGS_VERSION);
800 }
801 } else {
802 rb->splashf(HZ*2, "No ROMs in %s/pacman/", ROCKBOX_DIR);
803 }
804
805#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
806 rb->lcd_set_mode(LCD_MODE_RGB565);
807#endif
808
809#ifdef HAVE_ADJUSTABLE_CPU_FREQ
810 rb->cpu_boost(false);
811#endif
812
813 backlight_use_settings();
814
815 return PLUGIN_OK;
816}