A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 816 lines 32 kB view raw
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}