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 * Copyright (C) 2006 Eli Sherer
11 * 2007 Antoine Cellerier
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23#include "plugin.h"
24#include "lib/helper.h"
25#include "lib/playback_control.h"
26#include "lib/highscore.h"
27
28
29
30#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
31
32#define QUIT BUTTON_OFF
33#define LEFT BUTTON_LEFT
34#define RIGHT BUTTON_RIGHT
35#define PAUSE BUTTON_MODE
36#define UP BUTTON_UP
37#define DOWN BUTTON_DOWN
38
39#define RC_QUIT BUTTON_RC_STOP
40
41#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
42 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
43 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
44
45#define QUIT (BUTTON_SELECT | BUTTON_REPEAT)
46#define LEFT BUTTON_LEFT
47#define RIGHT BUTTON_RIGHT
48#define PAUSE (BUTTON_SELECT | BUTTON_REL)
49#define MENU_UP BUTTON_SCROLL_FWD
50#define MENU_DOWN BUTTON_SCROLL_BACK
51#define UP BUTTON_MENU
52#define DOWN BUTTON_PLAY
53
54#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
55
56#define QUIT BUTTON_POWER
57#define LEFT BUTTON_LEFT
58#define RIGHT BUTTON_RIGHT
59#define UP BUTTON_UP
60#define DOWN BUTTON_DOWN
61#define PAUSE BUTTON_PLAY
62
63#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
64
65#define QUIT BUTTON_POWER
66#define LEFT BUTTON_LEFT
67#define RIGHT BUTTON_RIGHT
68#define UP BUTTON_UP
69#define DOWN BUTTON_DOWN
70#define PAUSE BUTTON_A
71
72#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
73(CONFIG_KEYPAD == SANSA_C200_PAD)
74
75#define QUIT BUTTON_POWER
76#define LEFT BUTTON_LEFT
77#define RIGHT BUTTON_RIGHT
78#define UP BUTTON_UP
79#define DOWN BUTTON_DOWN
80#define PAUSE BUTTON_REC
81
82#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
83
84#define QUIT BUTTON_POWER
85#define LEFT BUTTON_LEFT
86#define RIGHT BUTTON_RIGHT
87#define UP BUTTON_UP
88#define DOWN BUTTON_DOWN
89#define PAUSE BUTTON_HOME
90
91#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
92
93#define QUIT (BUTTON_HOME|BUTTON_REPEAT)
94#define LEFT BUTTON_LEFT
95#define RIGHT BUTTON_RIGHT
96#define UP BUTTON_UP
97#define DOWN BUTTON_DOWN
98#define PAUSE BUTTON_SELECT
99
100#elif (CONFIG_KEYPAD == SANSA_M200_PAD)
101
102#define QUIT BUTTON_POWER
103#define LEFT BUTTON_LEFT
104#define RIGHT BUTTON_RIGHT
105#define UP BUTTON_UP
106#define DOWN BUTTON_DOWN
107#define PAUSE BUTTON_SELECT
108
109#elif CONFIG_KEYPAD == IRIVER_H10_PAD
110
111#define QUIT BUTTON_POWER
112#define LEFT BUTTON_LEFT
113#define RIGHT BUTTON_RIGHT
114#define UP BUTTON_SCROLL_UP
115#define DOWN BUTTON_SCROLL_DOWN
116#define PAUSE BUTTON_PLAY
117
118#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
119
120#define QUIT BUTTON_BACK
121#define LEFT BUTTON_LEFT
122#define RIGHT BUTTON_RIGHT
123#define UP BUTTON_UP
124#define DOWN BUTTON_DOWN
125#define PAUSE BUTTON_PLAY
126
127#elif (CONFIG_KEYPAD == MROBE100_PAD)
128
129#define QUIT BUTTON_POWER
130#define LEFT BUTTON_LEFT
131#define RIGHT BUTTON_RIGHT
132#define UP BUTTON_UP
133#define DOWN BUTTON_DOWN
134#define PAUSE BUTTON_DISPLAY
135
136#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
137
138#define QUIT BUTTON_RC_REC
139#define LEFT BUTTON_RC_REW
140#define RIGHT BUTTON_RC_FF
141#define UP BUTTON_RC_VOL_UP
142#define DOWN BUTTON_RC_VOL_DOWN
143#define PAUSE BUTTON_RC_PLAY
144
145#elif CONFIG_KEYPAD == COWON_D2_PAD
146
147#define QUIT BUTTON_POWER
148
149#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
150
151#define QUIT BUTTON_BACK
152#define LEFT BUTTON_LEFT
153#define RIGHT BUTTON_RIGHT
154#define UP BUTTON_UP
155#define DOWN BUTTON_DOWN
156#define PAUSE BUTTON_PLAY
157
158#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
159#define QUIT BUTTON_POWER
160#define LEFT BUTTON_BACK
161#define RIGHT BUTTON_MENU
162#define UP BUTTON_UP
163#define DOWN BUTTON_DOWN
164#define PAUSE BUTTON_PLAY
165
166#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
167
168#define QUIT BUTTON_POWER
169#define LEFT BUTTON_LEFT
170#define RIGHT BUTTON_RIGHT
171#define UP BUTTON_UP
172#define DOWN BUTTON_DOWN
173#define PAUSE BUTTON_VIEW
174
175#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
176
177#define QUIT BUTTON_POWER
178#define LEFT BUTTON_PREV
179#define RIGHT BUTTON_NEXT
180#define UP BUTTON_UP
181#define DOWN BUTTON_DOWN
182#define PAUSE BUTTON_MENU
183
184#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
185
186#define QUIT BUTTON_POWER
187#define LEFT BUTTON_PREV
188#define RIGHT BUTTON_NEXT
189#define UP BUTTON_UP
190#define DOWN BUTTON_DOWN
191#define PAUSE BUTTON_MENU
192
193#elif CONFIG_KEYPAD == ONDAVX747_PAD || \
194CONFIG_KEYPAD == ONDAVX777_PAD || \
195CONFIG_KEYPAD == MROBE500_PAD
196
197#define QUIT BUTTON_POWER
198
199#elif CONFIG_KEYPAD == SAMSUNG_YH820_PAD || \
200 CONFIG_KEYPAD == SAMSUNG_YH92X_PAD
201
202#define QUIT BUTTON_REW
203#define LEFT BUTTON_LEFT
204#define RIGHT BUTTON_RIGHT
205#define UP BUTTON_UP
206#define DOWN BUTTON_DOWN
207#define PAUSE BUTTON_PLAY
208
209#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
210
211#define QUIT BUTTON_REC
212#define LEFT BUTTON_PREV
213#define RIGHT BUTTON_NEXT
214#define UP BUTTON_UP
215#define DOWN BUTTON_DOWN
216#define PAUSE BUTTON_PLAY
217
218#elif CONFIG_KEYPAD == MPIO_HD200_PAD
219
220#define QUIT (BUTTON_REC|BUTTON_PLAY)
221#define LEFT BUTTON_VOL_DOWN
222#define RIGHT BUTTON_VOL_UP
223#define UP BUTTON_REW
224#define DOWN BUTTON_FF
225#define PAUSE BUTTON_PLAY
226
227#elif CONFIG_KEYPAD == MPIO_HD300_PAD
228
229#define QUIT (BUTTON_MENU | BUTTON_REPEAT)
230#define LEFT BUTTON_REW
231#define RIGHT BUTTON_FF
232#define UP BUTTON_UP
233#define DOWN BUTTON_DOWN
234#define PAUSE BUTTON_PLAY
235
236#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
237
238#define QUIT BUTTON_POWER
239#define LEFT BUTTON_LEFT
240#define RIGHT BUTTON_RIGHT
241#define UP BUTTON_UP
242#define DOWN BUTTON_DOWN
243#define PAUSE BUTTON_PLAYPAUSE
244
245#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
246
247#define QUIT BUTTON_POWER
248#define LEFT BUTTON_LEFT
249#define RIGHT BUTTON_RIGHT
250#define UP BUTTON_UP
251#define DOWN BUTTON_DOWN
252#define PAUSE BUTTON_SELECT
253
254#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
255
256#define QUIT BUTTON_BACK
257#define LEFT BUTTON_LEFT
258#define RIGHT BUTTON_RIGHT
259#define UP BUTTON_UP
260#define DOWN BUTTON_DOWN
261#define PAUSE BUTTON_SELECT
262
263#elif (CONFIG_KEYPAD == HM60X_PAD) || \
264 (CONFIG_KEYPAD == HM801_PAD)
265
266#define QUIT BUTTON_POWER
267#define LEFT BUTTON_LEFT
268#define RIGHT BUTTON_RIGHT
269#define UP BUTTON_UP
270#define DOWN BUTTON_DOWN
271#define PAUSE BUTTON_SELECT
272
273#elif CONFIG_KEYPAD == SONY_NWZ_PAD
274#define QUIT BUTTON_BACK
275#define LEFT BUTTON_LEFT
276#define RIGHT BUTTON_RIGHT
277#define UP BUTTON_UP
278#define DOWN BUTTON_DOWN
279#define PAUSE BUTTON_PLAY
280
281#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
282#define QUIT BUTTON_BACK
283#define LEFT BUTTON_LEFT
284#define RIGHT BUTTON_RIGHT
285#define UP BUTTON_UP
286#define DOWN BUTTON_DOWN
287#define PAUSE BUTTON_PLAYPAUSE
288
289#elif CONFIG_KEYPAD == DX50_PAD
290#define QUIT BUTTON_POWER
291#define LEFT BUTTON_LEFT
292#define RIGHT BUTTON_RIGHT
293#define UP BUTTON_VOL_UP
294#define DOWN BUTTON_VOL_DOWN
295#define PAUSE BUTTON_PLAY
296
297#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
298#define QUIT BUTTON_POWER
299#define PAUSE BUTTON_MENU
300
301#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
302#define QUIT BUTTON_POWER
303#define LEFT BUTTON_LEFT
304#define RIGHT BUTTON_RIGHT
305#define UP BUTTON_UP
306#define DOWN BUTTON_DOWN
307#define PAUSE BUTTON_SELECT
308
309#elif CONFIG_KEYPAD == XDUOO_X3_PAD || CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
310
311#define QUIT BUTTON_POWER
312#define LEFT BUTTON_PREV
313#define RIGHT BUTTON_NEXT
314#define UP BUTTON_HOME
315#define DOWN BUTTON_OPTION
316#define PAUSE BUTTON_PLAY
317
318#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
319
320#define QUIT BUTTON_POWER
321#define LEFT BUTTON_PREV
322#define RIGHT BUTTON_NEXT
323#define UP BUTTON_HOME
324#define DOWN BUTTON_OPTION
325#define PAUSE BUTTON_PLAY
326
327#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
328
329#define QUIT BUTTON_POWER
330#define LEFT BUTTON_HOME
331#define RIGHT BUTTON_VOL_DOWN
332#define UP BUTTON_PREV
333#define DOWN BUTTON_NEXT
334#define PAUSE BUTTON_PLAY
335
336#elif CONFIG_KEYPAD == EROSQ_PAD
337
338#define QUIT BUTTON_POWER
339#define LEFT BUTTON_SCROLL_BACK
340#define RIGHT BUTTON_SCROLL_FWD
341#define UP BUTTON_PREV
342#define DOWN BUTTON_NEXT
343#define PAUSE BUTTON_PLAY
344
345#elif CONFIG_KEYPAD == FIIO_M3K_PAD
346
347#define QUIT BUTTON_POWER
348#define LEFT BUTTON_LEFT
349#define RIGHT BUTTON_RIGHT
350#define UP BUTTON_UP
351#define DOWN BUTTON_DOWN
352#define PAUSE BUTTON_PLAY
353
354#elif CONFIG_KEYPAD == SDL_PAD
355#define QUIT BUTTON_TOPLEFT
356#define LEFT BUTTON_MIDLEFT
357#define RIGHT BUTTON_MIDRIGHT
358#define UP BUTTON_TOPMIDDLE
359#define DOWN BUTTON_BOTTOMMIDDLE
360#define PAUSE BUTTON_CENTER
361#elif CONFIG_KEYPAD == MA_PAD
362
363#define QUIT BUTTON_BACK
364#define LEFT BUTTON_LEFT
365#define RIGHT BUTTON_RIGHT
366#define UP BUTTON_UP
367#define DOWN BUTTON_DOWN
368#define PAUSE BUTTON_PLAY
369
370#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
371/* use touchscreen */
372
373#elif CONFIG_KEYPAD == RG_NANO_PAD
374
375#define QUIT BUTTON_START
376#define LEFT BUTTON_LEFT
377#define RIGHT BUTTON_RIGHT
378#define UP BUTTON_UP
379#define DOWN BUTTON_DOWN
380#define PAUSE BUTTON_A
381
382#else
383#error "No keymap defined!"
384#endif
385
386#if defined(HAVE_TOUCHSCREEN)
387#ifndef QUIT
388#define QUIT BUTTON_TOPLEFT
389#endif
390#ifndef LEFT
391#define LEFT BUTTON_MIDLEFT
392#endif
393#ifndef RIGHT
394#define RIGHT BUTTON_MIDRIGHT
395#endif
396#ifndef UP
397#define UP BUTTON_TOPMIDDLE
398#endif
399#ifndef DOWN
400#define DOWN BUTTON_BOTTOMMIDDLE
401#endif
402#ifndef PAUSE
403#define PAUSE BUTTON_CENTER
404#endif
405#endif
406
407#define MOVE_NO 0 /* player movement */
408#define MOVE_UP 1 /* 1 */
409#define MOVE_DN 2 /* 3 0 4 */
410#define MOVE_LT 3 /* 2 */
411#define MOVE_RT 4
412
413/* ball movement (12 ways) */
414/* UUL UR */
415/* UL UR */
416/* ULL . URR */
417/* DLL DRR */
418/* DL DR */
419/* DDL DDR */
420
421#define DIR_UU (1<<7)
422#define DIR_U (1<<6)
423#define DIR_RR (1<<5)
424#define DIR_R (1<<4)
425#define DIR_DD (1<<3)
426#define DIR_D (1<<2)
427#define DIR_LL (1<<1)
428#define DIR_L (1<<0)
429
430#define MOVE_UUR ( DIR_UU | DIR_R )
431#define MOVE_UR ( DIR_U | DIR_R )
432#define MOVE_URR ( DIR_U | DIR_RR )
433#define MOVE_DRR ( DIR_D | DIR_RR )
434#define MOVE_DR ( DIR_D | DIR_R )
435#define MOVE_DDR ( DIR_DD | DIR_R )
436#define MOVE_DDL ( DIR_DD | DIR_L )
437#define MOVE_DL ( DIR_D | DIR_L )
438#define MOVE_DLL ( DIR_D | DIR_LL )
439#define MOVE_ULL ( DIR_U | DIR_LL )
440#define MOVE_UL ( DIR_U | DIR_L )
441#define MOVE_UUL ( DIR_UU | DIR_L )
442
443#if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
444# define CUBE_SIZE 8 /* 8x22=176 */
445# define pos(a) ((a)>>3)
446#else
447# define CUBE_SIZE 4
448# define pos(a) ((a)>>2)
449#endif
450
451#define STARTING_QIXES 2
452#define MAX_LEVEL 10
453#define MAX_QIXES MAX_LEVEL+STARTING_QIXES
454#define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
455#define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
456#define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
457#define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
458
459#ifdef HAVE_LCD_COLOR
460#define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
461#define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
462#define PLR_COL LCD_WHITE /* color used for the player */
463#elif LCD_DEPTH>=2
464#define CLR_RED LCD_DARKGRAY /* used to imply danger */
465#define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
466#define PLR_COL LCD_BLACK /* color used for the player */
467#endif
468
469#if LCD_DEPTH>=2
470#define EMPTIED LCD_BLACK /* empty spot */
471#define FILLED CLR_LTBLUE /* filled spot */
472#define TRAIL CLR_RED /* the red trail of the player */
473#define QIX LCD_WHITE
474#else
475#define EMPTIED 0
476#define FILLED 1
477#define TRAIL 2
478#define QIX 3
479#endif
480#define UNCHECKED 0
481#define CHECKED 1
482#define PAINTED -1
483#define PIC_QIX 0
484#define PIC_PLAYER 1
485
486/* The time (in ms) for one iteration through the game loop - decrease this
487 to speed up the game - note that current_tick is (currently) only accurate
488 to 10ms.
489*/
490static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
491static int difficulty = 75; /* Percentage of screen that needs to be filled
492 * in order to win the game */
493
494static bool quit = false;
495static bool _ingame = false;
496
497#define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/xobox.resume"
498#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/xobox.score"
499#define NUM_SCORES 5
500static struct highscore highscores[NUM_SCORES];
501
502static unsigned int board[BOARD_H][BOARD_W];
503static int testboard[BOARD_H][BOARD_W];
504
505#if CUBE_SIZE == 8
506/*
507 00011000 0x18 - 11100111 0xe7
508 00111100 0x3c - 11100111 0xe7
509 01111110 0x7e - 11000011 0xc3
510 11111111 0xff - 00000000 0x00
511 11111111 0xff - 00000000 0x00
512 01111110 0x7e - 11000011 0xc3
513 00111100 0x3c - 11100111 0xe7
514 00011000 0x18 - 11100111 0xe7
515 */
516const unsigned char pics[2][8] = {
517 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
518 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
519};
520#elif CUBE_SIZE == 4
521/*
522 0110 0x6 - 1001 0x9
523 1111 0xf - 0110 0x6
524 1111 0xf - 0110 0x6
525 0110 0x6 - 1001 0x9
526 */
527const unsigned char pics[2][4] = {
528 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
529 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
530};
531#else
532#error Incorrect CUBE_SIZE value.
533#endif
534
535static struct qix
536{
537 int velocity; /* velocity */
538 int x, y; /* position on screen */
539 int angle; /* angle */
540} qixes[MAX_QIXES]; /* black_qix */
541
542static struct splayer
543{
544 int i, j; /* position on board */
545 int move, score, level, lives;
546 bool drawing;
547 bool gameover;
548} player;
549
550static int percentage_cache;
551
552/*************************** STACK STUFF **********************/
553
554/* the stack */
555#define STACK_SIZE (2*BOARD_W*BOARD_H)
556static struct pos
557{
558 int x, y; /* position on board */
559} stack[STACK_SIZE];
560static int stackPointer;
561
562static inline bool pop (struct pos *p)
563{
564 if (stackPointer > 0) {
565 p->x = stack[stackPointer].x;
566 p->y = stack[stackPointer].y;
567 stackPointer--;
568 return true;
569 } else
570 return false; /* SE */
571}
572
573static inline bool push (struct pos *p)
574{
575 if (stackPointer < STACK_SIZE - 1) {
576 stackPointer++;
577 stack[stackPointer].x = p->x;
578 stack[stackPointer].y = p->y;
579 return true;
580 } else
581 return false; /* SOF */
582}
583
584static inline void emptyStack (void)
585{
586 stackPointer = 0;
587}
588
589/*********************** END OF STACK STUFF *********************/
590
591/* calculate the new x coordinate of the ball according to angle and speed */
592static inline int get_newx (int x, int len, int deg)
593{
594 if (deg & DIR_R)
595 return x + len;
596 else if (deg & DIR_L)
597 return x - len;
598 else if (deg & DIR_RR)
599 return x + len * 2;
600 else /* (def & DIR_LL) */
601 return x - len * 2;
602}
603
604/* calculate the new y coordinate of the ball according to angle and speed */
605static inline int get_newy (int y, int len, int deg)
606{
607 if (deg & DIR_D)
608 return y + len;
609 else if (deg & DIR_U)
610 return y - len;
611 else if (deg & DIR_DD)
612 return y + len * 2;
613 else /* (deg & DIR_UU) */
614 return y - len * 2;
615}
616
617/* make random function get it's value from the device ticker */
618static inline void randomize (void)
619{
620 rb->srand (*rb->current_tick);
621}
622
623/* get a random number between 0 and range-1 */
624static int t_rand (int range)
625{
626 return rb->rand () % range;
627}
628
629/* initializes the test help board */
630static void init_testboard (void)
631{
632 int j; /* testboard */
633 for (j = 0; j < BOARD_H; j++)
634 /* UNCHEKED == (int)0 */
635 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
636}
637
638/* initializes the game board on with the player,qix's and black qix */
639static void init_board (void)
640{
641 int i, j;
642 for (j = 0; j < BOARD_H; j++)
643 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
644 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
645 || (j >= BOARD_H - 2))
646 board[j][i] = FILLED;
647 else
648 board[j][i] = EMPTIED;
649 }
650
651 /* (level+2) is the number of qixes */
652 for (j = 0; j < player.level + STARTING_QIXES; j++) {
653 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
654
655 /* not on frame */
656 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
657 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
658
659 const int angle_table[] = {
660 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
661 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
662 qixes[j].angle = angle_table[t_rand (12)];
663#if CUBE_SIZE == 4
664 /* Work arround a nasty bug. FIXME */
665 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
666 qixes[j].velocity = 1;
667#endif
668 }
669 /*black_qix.velocity=1;
670 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
671 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
672 black_qix.angle=MOVE_UR; */
673 player.move = MOVE_NO;
674 player.drawing = false;
675 player.i = BOARD_W / 2;
676 player.j = 1;
677
678 percentage_cache = 0;
679}
680
681/* calculates the percentage of the screen filling */
682static int percentage (void)
683{
684 int i, j, filled = 0;
685 for (j = 2; j < BOARD_H - 2; j++)
686 for (i = 1; i < BOARD_W - 1; i++)
687 if (board[j][i] == FILLED)
688 filled++;
689 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
690}
691
692/* draw the board on with all the game figures */
693static void refresh_board (void)
694{
695 int i, j;
696 int x;
697
698#if LCD_DEPTH>=2
699 rb->lcd_set_background (LCD_BLACK);
700#endif
701 rb->lcd_clear_display ();
702 for (j = 0; j < BOARD_H; j++)
703 {
704 unsigned last_color = board[j][0];
705 int last_i = 0;
706 for (i = 1; i < BOARD_W; i++) {
707 if( last_color != board[j][i] )
708 {
709#if LCD_DEPTH>=2
710 rb->lcd_set_foreground (last_color);
711#else
712 if (last_color != EMPTIED)
713#endif
714 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
715 BOARD_Y + CUBE_SIZE * j,
716 CUBE_SIZE * (i - last_i), CUBE_SIZE );
717 last_color = board[j][i];
718 last_i = i;
719 }
720 }
721#if LCD_DEPTH>=2
722 rb->lcd_set_foreground (last_color);
723#else
724 if (last_color != EMPTIED)
725#endif
726 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
727 BOARD_Y + CUBE_SIZE * j,
728 CUBE_SIZE * (i - last_i), CUBE_SIZE);
729 }
730
731#if LCD_DEPTH>=2
732 rb->lcd_set_foreground (LCD_BLACK);
733 rb->lcd_set_background (CLR_LTBLUE);
734#else
735 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
736#endif
737 rb->lcd_putsxyf (BOARD_X, BOARD_Y, "Level %d", player.level + 1);
738 rb->lcd_putsxyf (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, "%d%%",
739 percentage_cache);
740 rb->lcd_putsxyf (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, "Score: %d",
741 player.score);
742#if LCD_DEPTH>=2
743 x = BOARD_X + CUBE_SIZE * BOARD_W - 60;
744#else
745 x = BOARD_X + CUBE_SIZE * BOARD_W - 40;
746#endif
747 rb->lcd_putsxyf (x, BOARD_Y + CUBE_SIZE * BOARD_H - 8,
748 (player.lives != 1) ? "%d Lives" : "%d Life", player.lives);
749
750#if LCD_DEPTH>=2
751 rb->lcd_set_foreground (PLR_COL);
752 rb->lcd_set_background (board[player.j][player.i]);
753#endif
754 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
755 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
756
757#if LCD_DEPTH>=2
758 rb->lcd_set_background (EMPTIED);
759 rb->lcd_set_foreground (LCD_WHITE);
760 rb->lcd_set_drawmode (DRMODE_FG);
761#else
762 rb->lcd_set_drawmode (DRMODE_FG);
763#endif
764 for (j = 0; j < player.level + STARTING_QIXES; j++)
765 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
766 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
767#if LCD_DEPTH>=2
768 rb->lcd_set_foreground (LCD_BLACK);
769#endif
770 rb->lcd_set_drawmode (DRMODE_SOLID);
771
772 rb->lcd_update ();
773}
774
775static inline int infested_area (int i, int j, int v)
776{
777 struct pos p;
778 p.x = i;
779 p.y = j;
780 emptyStack ();
781 if (!push (&p))
782 return -1;
783 while (pop (&p)) {
784 if (testboard[p.y][p.x] == v) continue;
785 if (testboard[p.y][p.x] > UNCHECKED)
786 return 1; /* This area was previously flagged as infested */
787 testboard[p.y][p.x] = v;
788 if (board[p.y][p.x] == QIX)
789 return 1; /* Infested area */
790 {
791 struct pos p1 = { p.x+1, p.y };
792 if ((p1.x < BOARD_W)
793 && (board[p1.y][p1.x] != FILLED)
794 && (!push (&p1)))
795 return -1;
796 }
797 {
798 struct pos p1 = { p.x-1, p.y };
799 if ((p1.x >= 0)
800 && (board[p1.y][p1.x] != FILLED)
801 && (!push (&p1)))
802 return -1;
803 }
804 {
805 struct pos p1 = { p.x, p.y+1 };
806 if ((p1.y < BOARD_H)
807 && (board[p1.y][p1.x] != FILLED)
808 && (!push (&p1)))
809 return -1;
810 }
811 {
812 struct pos p1 = { p.x, p.y-1 };
813 if ((p1.y >= 0)
814 && (board[p1.y][p1.x] != FILLED)
815 && (!push (&p1)))
816 return -1;
817 }
818 }
819 return 0;
820}
821
822static inline int fill_area (int i, int j)
823{
824 struct pos p;
825 p.x = i;
826 p.y = j;
827 int v = testboard[p.y][p.x];
828 emptyStack ();
829 if (!push (&p))
830 return -1;
831 while (pop (&p)) {
832 board[p.y][p.x] = FILLED;
833 testboard[p.y][p.x] = PAINTED;
834 {
835 struct pos p1 = { p.x+1, p.y };
836 if ((p1.x < BOARD_W)
837 && (testboard[p1.y][p1.x] == v)
838 && (!push (&p1)))
839 return -1;
840 }
841 {
842 struct pos p1 = { p.x-1, p.y };
843 if ((p1.x >= 0)
844 && (testboard[p1.y][p1.x] == v)
845 && (!push (&p1)))
846 return -1;
847 }
848 {
849 struct pos p1 = { p.x, p.y+1 };
850 if ((p1.y < BOARD_H)
851 && (testboard[p1.y][p1.x] == v)
852 && (!push (&p1)))
853 return -1;
854 }
855 {
856 struct pos p1 = { p.x, p.y-1 };
857 if ((p1.y >= 0)
858 && (testboard[p1.y][p1.x] == v)
859 && (!push (&p1)))
860 return -1;
861 }
862 }
863 return 0;
864}
865
866
867/* take care of stuff after xonix has landed on a filled spot */
868static void complete_trail (int fill)
869{
870 int i, j, ret;
871 for (j = 0; j < BOARD_H; j++) {
872 for (i = 0; i < BOARD_W; i++) {
873 if (board[j][i] == TRAIL) {
874 if (fill)
875 board[j][i] = FILLED;
876 else
877 board[j][i] = EMPTIED;
878 }
879 }
880 }
881
882 if (fill) {
883 int v = CHECKED;
884 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
885 board[pos(qixes[i].y - BOARD_Y)]
886 [pos(qixes[i].x - BOARD_X)] = QIX;
887
888 init_testboard();
889 for (j = 1; j < BOARD_H - 1; j++) {
890 for (i = 0; i < BOARD_W - 0; i++) {
891 if (board[j][i] != FILLED) {
892 ret = infested_area (i, j, v);
893 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
894 quit = true;
895 v++;
896 }
897 }
898 }
899
900 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
901 board[pos(qixes[i].y - BOARD_Y)]
902 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
903 percentage_cache = percentage();
904 }
905
906 rb->button_clear_queue();
907}
908
909/* returns the color the real pixel(x,y) on the lcd is pointing at */
910static inline unsigned int getpixel (int x, int y)
911{
912 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
913 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
914 return board[b][a];
915 else
916 return FILLED;
917}
918
919/* returns the color the ball on (newx,newy) is heading at *----*
920 checks the four edge points of the square if 1st of all | |
921 are a trail (cause it's a lose life situation) and 2nd | |
922 if it's filled so it needs to bounce. *____*
923 */
924static inline unsigned int next_hit (int newx, int newy)
925{
926 if ((getpixel (newx, newy) == TRAIL)
927 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
928 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
929 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
930 return TRAIL;
931 else if ((getpixel (newx, newy) == FILLED)
932 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
933 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
934 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
935 FILLED))
936 return FILLED;
937 else
938 return EMPTIED;
939}
940
941static void die (void)
942{
943 player.lives--;
944 if (player.lives == 0)
945 player.gameover = true;
946 else {
947 refresh_board ();
948 rb->splash (HZ, "Crash!");
949 complete_trail (false);
950 player.move = MOVE_NO;
951 player.drawing = false;
952 player.i = BOARD_W / 2;
953 player.j = 1;
954 }
955}
956
957/* returns true if the (side) of the block -***-
958 starting from (newx,newy) has any filled pixels * *
959 -***-
960 */
961static inline bool line_check_lt (int newx, int newy)
962{
963 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
964 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
965}
966static inline bool line_check_rt (int newx, int newy)
967{
968 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
969 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
970}
971static inline bool line_check_up (int newx, int newy)
972{
973 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
974 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
975}
976static inline bool line_check_dn (int newx, int newy)
977{
978 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
979 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
980}
981
982static inline void move_qix (struct qix *q)
983{
984 int newx, newy;
985 newx = get_newx (q->x, q->velocity, q->angle);
986 newy = get_newy (q->y, q->velocity, q->angle);
987 switch (next_hit (newx, newy))
988 {
989 case EMPTIED:
990 q->x = newx;
991 q->y = newy;
992 break;
993 case FILLED:
994 {
995 const int a = q->angle;
996 q->angle =
997 ((a&(DIR_UU|DIR_U))
998 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
999 : (a&(DIR_UU|DIR_U)))
1000 : 0)
1001 |
1002 ((a&(DIR_RR|DIR_R))
1003 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
1004 : (a&(DIR_RR|DIR_R)))
1005 : 0)
1006 |
1007 ((a&(DIR_DD|DIR_D))
1008 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
1009 : (a&(DIR_DD|DIR_D)))
1010 : 0)
1011 |
1012 ((a&(DIR_LL|DIR_L))
1013 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
1014 : (a&(DIR_LL|DIR_L)))
1015 : 0);
1016 q->x = get_newx (q->x, q->velocity, q->angle);
1017 q->y = get_newy (q->y, q->velocity, q->angle);
1018 break;
1019 }
1020 case TRAIL:
1021 die();
1022 break;
1023 }
1024}
1025
1026/* move the board forward timewise */
1027static inline void move_board (void)
1028{
1029 int j, newi, newj;
1030
1031 for (j = 0; j < player.level + STARTING_QIXES; j++)
1032 move_qix (&qixes[j]);
1033 /* move_qix(&black_qix,true); */
1034 if (player.move) {
1035 newi = player.i;
1036 newj = player.j;
1037 switch (player.move) {
1038 case MOVE_UP:
1039 if (player.j > 1)
1040 newj--;
1041 break;
1042 case MOVE_DN:
1043 if (player.j < BOARD_H - 2)
1044 newj++;
1045 break;
1046 case MOVE_LT:
1047 if (player.i > 0)
1048 newi--;
1049 break;
1050 case MOVE_RT:
1051 if (player.i < BOARD_W - 1)
1052 newi++;
1053 break;
1054 default:
1055 break;
1056 }
1057
1058 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
1059 board[newj][newi] = TRAIL;
1060 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
1061 player.move = MOVE_NO; /* stop moving */
1062 player.drawing = false;
1063 complete_trail (true);
1064 } else if ((board[player.j][player.i] == FILLED)
1065 && (board[newj][newi] == EMPTIED)) {
1066 /* start drawing */
1067 player.drawing = true;
1068 board[newj][newi] = TRAIL;
1069 /* if the block after next is empty and we're moving onto filled, stop */
1070 } else if ((board[newj][newi] == FILLED)
1071 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
1072 player.move = MOVE_NO;
1073 }
1074 player.i = newi;
1075 player.j = newj;
1076 }
1077 if (percentage_cache >= difficulty) { /* finished level */
1078 refresh_board ();
1079 rb->splashf (HZ * 2, "Level %d finished", player.level+1);
1080 player.score += percentage_cache;
1081 if (player.level < MAX_LEVEL)
1082 player.level++;
1083 init_board ();
1084 refresh_board ();
1085 rb->button_clear_queue();
1086 rb->splash (HZ * 2, "Ready?");
1087 }
1088}
1089
1090/* init game's variables */
1091static void init_game (void)
1092{
1093 player.level = 0;
1094 player.score = 0;
1095 player.lives = 3;
1096 player.gameover = false;
1097 player.drawing = false;
1098 init_board ();
1099 refresh_board ();
1100 rb->splash (HZ * 2, "Ready?");
1101}
1102
1103static bool load_game(void)
1104{
1105 int fd = rb->open(RESUME_FILE, O_RDONLY);
1106
1107 if (fd < 0) {
1108 return true;
1109 }
1110
1111 bool load_success =
1112 rb->read(fd, &player, sizeof(player)) == sizeof(player) &&
1113 rb->read(fd, &qixes, sizeof(qixes)) == sizeof(qixes) &&
1114 rb->read(fd, &stack, sizeof(stack)) == sizeof(stack) &&
1115 rb->read(fd, &board, sizeof(board)) == sizeof(board) &&
1116 rb->read(fd, &testboard, sizeof(testboard)) == sizeof(testboard) &&
1117 rb->read(fd, &speed, sizeof(speed)) == sizeof(speed) &&
1118 rb->read(fd, &difficulty, sizeof(difficulty)) == sizeof(difficulty) &&
1119 rb->read(fd, &stackPointer, sizeof(stackPointer)) == sizeof(stackPointer) &&
1120 rb->read(fd, &percentage_cache,
1121 sizeof(percentage_cache)) == sizeof(percentage_cache);
1122
1123 rb->close(fd);
1124 _ingame = load_success;
1125
1126 return load_success;
1127}
1128
1129static bool save_game(void)
1130{
1131 int fd = rb->open(RESUME_FILE, O_WRONLY|O_CREAT, 0666);
1132
1133 if (fd < 0) {
1134 return false;
1135 }
1136
1137 bool save_success =
1138 rb->write(fd, &player, sizeof(player)) > 0 &&
1139 rb->write(fd, &qixes, sizeof(qixes)) > 0 &&
1140 rb->write(fd, &stack, sizeof(stack)) > 0 &&
1141 rb->write(fd, &board, sizeof(board)) > 0 &&
1142 rb->write(fd, &testboard, sizeof(testboard)) > 0 &&
1143 rb->write(fd, &speed, sizeof(speed)) > 0 &&
1144 rb->write(fd, &difficulty, sizeof(difficulty)) > 0 &&
1145 rb->write(fd, &stackPointer, sizeof(stackPointer)) > 0 &&
1146 rb->write(fd, &percentage_cache, sizeof(percentage_cache)) > 0;
1147
1148 rb->close(fd);
1149
1150 if (!save_success) {
1151 rb->remove(RESUME_FILE);
1152 }
1153
1154 return save_success;
1155}
1156
1157/* the main menu */
1158static int xobox_menu_cb(int action,
1159 const struct menu_item_ex *this_item,
1160 struct gui_synclist *this_list)
1161{
1162 (void)this_list;
1163 intptr_t item = (intptr_t)this_item;
1164 if(action == ACTION_REQUEST_MENUITEM
1165 && !_ingame && (item == 0 || item == 6))
1166 return ACTION_EXIT_MENUITEM;
1167 return action;
1168}
1169
1170static int xobox_menu(bool ingame)
1171{
1172 rb->button_clear_queue();
1173
1174 int selection = 0;
1175 MENUITEM_STRINGLIST(main_menu, "Xobox Menu", xobox_menu_cb,
1176 "Resume Game", "Start New Game",
1177 "Speed", "Difficulty",
1178 "High Scores", "Playback Control",
1179 "Quit Without Saving", "Quit");
1180 _ingame = ingame;
1181
1182 while (true) {
1183 switch (rb->do_menu(&main_menu, &selection, NULL, false)) {
1184 case 0:
1185 rb->remove(RESUME_FILE);
1186 refresh_board();
1187 rb->splash (HZ*2, "Ready?");
1188 return 0;
1189 case 1:
1190 init_game ();
1191 return 0;
1192 case 2:
1193 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
1194 break;
1195 case 3:
1196 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
1197 5, 50, 95, NULL);
1198 break;
1199 case 4:
1200 highscore_show(-1, highscores, NUM_SCORES, true);
1201 break;
1202 case 5:
1203 playback_control(NULL);
1204 break;
1205 case 6:
1206 return 1;
1207 case 7:
1208 if (_ingame) {
1209 rb->splash(HZ, "Saving game...");
1210
1211 if (!save_game()) {
1212 rb->splash(HZ, "Failed to save game");
1213 }
1214 }
1215
1216 return 1;
1217 case MENU_ATTACHED_USB:
1218 return 1;
1219 default:
1220 break;
1221 }
1222 }
1223}
1224
1225/* general keypad handler loop */
1226static int xobox_loop (void)
1227{
1228 int button = 0;
1229 bool pause = false;
1230 int end;
1231
1232 if (xobox_menu(_ingame)) {
1233 return PLUGIN_OK;
1234 }
1235
1236 while (!quit) {
1237 end = *rb->current_tick + ((11-speed)*HZ)/100;
1238
1239#ifdef HAS_BUTTON_HOLD
1240 if (rb->button_hold()) {
1241 pause = true;
1242 rb->splash (HZ, "Paused");
1243 }
1244#endif
1245
1246 button = rb->button_get_w_tmo (1);
1247 switch (button) {
1248 case UP:
1249 case UP|BUTTON_REPEAT:
1250 player.move = MOVE_UP;
1251 break;
1252 case DOWN:
1253 case DOWN|BUTTON_REPEAT:
1254 player.move = MOVE_DN;
1255 break;
1256 case LEFT:
1257 case LEFT|BUTTON_REPEAT:
1258 player.move = MOVE_LT;
1259 break;
1260 case RIGHT:
1261 case RIGHT|BUTTON_REPEAT:
1262 player.move = MOVE_RT;
1263 break;
1264 case PAUSE:
1265 pause = !pause;
1266 if (pause)
1267 rb->splash (HZ, "Paused");
1268 break;
1269 case QUIT:
1270 if (!pause) {
1271 if (xobox_menu(true)) {
1272 quit = true;
1273 }
1274 }
1275 break;
1276 default:
1277 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1278 return PLUGIN_USB_CONNECTED;
1279 break;
1280 }
1281 if (!pause) {
1282 move_board ();
1283 refresh_board ();
1284 }
1285 if (player.gameover) {
1286 rb->splash (HZ, "Game Over!");
1287
1288 int pos = highscore_update(player.score, player.level, "",
1289 highscores, NUM_SCORES);
1290
1291 if (pos != -1) {
1292 if (pos == 0) {
1293 rb->splashf(HZ, "New High Score: %d", player.score);
1294 }
1295
1296 highscore_show(pos, highscores, NUM_SCORES, true);
1297 }
1298
1299 if (xobox_menu(false)) {
1300 quit = true;
1301 }
1302 }
1303
1304 if (TIME_BEFORE(*rb->current_tick, end))
1305 rb->sleep (end - *rb->current_tick);
1306 else
1307 rb->yield ();
1308
1309 } /* end while */
1310 return PLUGIN_OK; /* for no warnings on compiling */
1311}
1312
1313/* plugin main procedure */
1314enum plugin_status plugin_start (const void *parameter)
1315{
1316 int ret = PLUGIN_OK;
1317
1318 (void) parameter;
1319
1320 rb->lcd_setfont (FONT_SYSFIXED);
1321#if LCD_DEPTH>=2
1322 rb->lcd_set_backdrop(NULL);
1323#endif
1324
1325 /* Turn off backlight timeout */
1326 backlight_ignore_timeout();
1327
1328 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1329
1330 if (!load_game()) {
1331 rb->splash(HZ, "Failed to load saved game");
1332 }
1333
1334 randomize ();
1335 ret = xobox_loop ();
1336
1337
1338 /* Turn on backlight timeout (revert to settings) */
1339 backlight_use_settings();
1340
1341 rb->lcd_setfont (FONT_UI);
1342
1343 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1344
1345 return ret;
1346}