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) 2014 Franklin Wei, Benjamin Brown
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22/* TODO: */
23/* vertical stride support (as of Dec. 2014, only the M:Robe 500 has a color,
24 vertical stride LCD) */
25
26/* monochrome/grayscale support (many grayscale targets have vertical strides,
27 so get that working first!) */
28
29#include "plugin.h"
30
31#include "lib/display_text.h"
32#include "lib/helper.h"
33#include "lib/pluginlib_actions.h"
34#include "lib/pluginlib_bmp.h"
35#include "lib/pluginlib_exit.h"
36#include "lib/keymaps.h"
37
38#include "sys.h"
39#include "parts.h"
40#include "engine.h"
41
42static struct System* save_sys;
43static fb_data *lcd_fb = NULL;
44
45static bool sys_save_settings(struct System* sys)
46{
47 File f;
48 file_create(&f, false);
49 if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "wb"))
50 {
51 return false;
52 }
53 file_write(&f, &sys->settings, sizeof(sys->settings));
54 file_close(&f);
55 return true;
56}
57
58static void sys_rotate_keymap(struct System* sys)
59{
60 switch(sys->settings.rotation_option)
61 {
62 case 0:
63 sys->keymap.up = BTN_UP;
64 sys->keymap.down = BTN_DOWN;
65 sys->keymap.left = BTN_LEFT;
66 sys->keymap.right = BTN_RIGHT;
67#ifdef BTN_HAVE_DIAGONAL
68 sys->keymap.upleft = BTN_UP_LEFT;
69 sys->keymap.upright = BTN_UP_RIGHT;
70 sys->keymap.downleft = BTN_DOWN_RIGHT;
71 sys->keymap.downright = BTN_DOWN_LEFT;
72#endif
73 break;
74 case 1:
75 sys->keymap.up = BTN_RIGHT;
76 sys->keymap.down = BTN_LEFT;
77 sys->keymap.left = BTN_UP;
78 sys->keymap.right = BTN_DOWN;
79#ifdef BTN_HAVE_DIAGONAL
80 sys->keymap.upleft = BTN_UP_RIGHT;
81 sys->keymap.upright = BTN_DOWN_RIGHT;
82 sys->keymap.downleft = BTN_UP_LEFT;
83 sys->keymap.downright = BTN_DOWN_LEFT;
84#endif
85 break;
86 case 2:
87 sys->keymap.up = BTN_LEFT;
88 sys->keymap.down = BTN_RIGHT;
89 sys->keymap.left = BTN_DOWN;
90 sys->keymap.right = BTN_UP;
91#ifdef BTN_HAVE_DIAGONAL
92 sys->keymap.upleft = BTN_DOWN_LEFT;
93 sys->keymap.upright = BTN_UP_LEFT;
94 sys->keymap.downleft = BTN_DOWN_RIGHT;
95 sys->keymap.downright = BTN_DOWN_LEFT;
96#endif
97 break;
98 default:
99 error("sys_rotate_keymap: fall-through!");
100 }
101}
102
103static bool sys_load_settings(struct System* sys)
104{
105 File f;
106 file_create(&f, false);
107 if(!file_open(&f, SETTINGS_FILE, sys->e->_saveDir, "rb"))
108 {
109 return false;
110 }
111 file_read(&f, &sys->settings, sizeof(sys->settings));
112 file_close(&f);
113 sys_rotate_keymap(sys);
114 return true;
115}
116
117void exit_handler(void)
118{
119 sys_save_settings(save_sys);
120 sys_stopAudio(save_sys);
121 rb->timer_unregister();
122#ifdef HAVE_ADJUSTABLE_CPU_FREQ
123 rb->cpu_boost(false);
124#endif
125
126 backlight_use_settings();
127}
128
129static bool sys_do_help(void)
130{
131#ifdef HAVE_LCD_COLOR
132 rb->lcd_set_foreground(LCD_WHITE);
133 rb->lcd_set_background(LCD_BLACK);
134#endif
135 rb->lcd_setfont(FONT_UI);
136 char *help_text[] = {
137 "XWorld", "",
138 "XWorld", "is", "a", "port", "of", "a", "bytecode", "interpreter", "for", "`Another", "World',", "a", "cinematic", "adventure", "game", "by", "Eric", "Chahi.",
139 "",
140 "",
141 "Level", "Codes:", "",
142 "Level", "1:", "LDKD", "",
143 "Level", "2:", "HTDC", "",
144 "Level", "3:", "CLLD", "",
145 "Level", "4:", "LBKG", "",
146 "Level", "5:", "XDDJ", "",
147 "Level", "6:", "FXLC", "",
148 "Level", "7:", "KRFK", "",
149 "Level", "8:", "KFLB", "",
150 "Level", "9:", "DDRX", "",
151 "Level", "10:", "BFLX", "",
152 "Level", "11:", "BRTD", "",
153 "Level", "12:", "TFBB", "",
154 "Level", "13:", "TXHF", "",
155 "Level", "14:", "CKJL", "",
156 "Level", "15:", "LFCK", "",
157 };
158 struct style_text style[] = {
159 { 0, TEXT_CENTER | TEXT_UNDERLINE },
160 };
161
162 return display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
163}
164
165static const struct opt_items scaling_settings[3] = {
166 { "Disabled", -1 },
167 { "Fast" , -1 },
168#ifdef HAVE_LCD_COLOR
169 { "Good" , -1 }
170#endif
171};
172
173static const struct opt_items rotation_settings[3] = {
174 { "Disabled" , -1 },
175 { "Clockwise" , -1 },
176 { "Counterclockwise", -1 }
177};
178
179static void do_video_settings(struct System* sys)
180{
181 MENUITEM_STRINGLIST(menu, "Video Settings", NULL,
182 "Negative",
183#ifdef SYS_MOTION_BLUR
184 "Motion Blur",
185#endif
186 "Scaling",
187 "Rotation",
188 "Show FPS",
189 "Zoom on Code",
190 "Back");
191 int sel = 0;
192 while(1)
193 {
194 switch(rb->do_menu(&menu, &sel, NULL, false))
195 {
196 case 0:
197 rb->set_bool("Negative", &sys->settings.negative_enabled);
198 break;
199#ifdef SYS_MOTION_BLUR
200 case 1:
201 rb->set_bool("Motion Blur", &sys->settings.blur);
202 break;
203#endif
204#ifndef SYS_MOTION_BLUR
205 case 1:
206#else
207 case 2:
208#endif
209 rb->set_option("Scaling", &sys->settings.scaling_quality, RB_INT, scaling_settings,
210#ifdef HAVE_LCD_COLOR
211 3
212#else
213 2
214#endif
215 , NULL);
216 if(sys->settings.scaling_quality &&
217 sys->settings.zoom)
218 {
219 rb->splash(HZ*2, "Zoom automatically disabled.");
220 sys->settings.zoom = false;
221 }
222 break;
223#ifndef SYS_MOTION_BLUR
224 case 2:
225#else
226 case 3:
227#endif
228 rb->set_option("Rotation", &sys->settings.rotation_option, RB_INT, rotation_settings, 3, NULL);
229 if(sys->settings.rotation_option &&
230 sys->settings.zoom)
231 {
232 rb->splash(HZ*2, "Zoom automatically disabled.");
233 sys->settings.zoom = false;
234 }
235 sys_rotate_keymap(sys);
236 break;
237#ifndef SYS_MOTION_BLUR
238 case 3:
239#else
240 case 4:
241#endif
242 rb->set_bool("Show FPS", &sys->settings.showfps);
243 break;
244#ifndef SYS_MOTION_BLUR
245 case 4:
246#else
247 case 5:
248#endif
249 rb->set_bool("Zoom on Code", &sys->settings.zoom);
250 /* zoom only works with scaling and rotation disabled */
251 if(sys->settings.zoom && (sys->settings.scaling_quality || sys->settings.rotation_option))
252 {
253 rb->splash(HZ*2, "Scaling and rotation automatically disabled.");
254 sys->settings.scaling_quality = 0;
255 sys->settings.rotation_option = 0;
256 }
257 break;
258 default:
259 sys_save_settings(sys);
260 return;
261 }
262 }
263}
264
265#define MAX_SOUNDBUF_SIZE 256
266
267static void do_sound_settings(struct System* sys)
268{
269 MENUITEM_STRINGLIST(menu, "Sound Settings", NULL,
270 "Enabled",
271 "Buffer Level",
272 "Volume",
273 "Back",
274 );
275 int sel = 0;
276 while(1)
277 {
278 switch(rb->do_menu(&menu, &sel, NULL, false))
279 {
280 case 0:
281 rb->set_bool("Enabled", &sys->settings.sound_enabled);
282 break;
283 case 1:
284 rb->set_int("Buffer Level", "samples", UNIT_INT, &sys->settings.sound_bufsize, NULL, 16, 16, MAX_SOUNDBUF_SIZE, NULL);
285 break;
286 case 2:
287 {
288 const struct settings_list* vol =
289 rb->find_setting(&rb->global_status->volume);
290 rb->option_screen((struct settings_list*)vol, NULL, false, "Volume");
291 break;
292 }
293 case 3:
294 default:
295 sys_save_settings(sys);
296 return;
297 }
298 }
299}
300
301static void sys_reset_settings(struct System* sys)
302{
303 sys->settings.negative_enabled = false;
304 sys->settings.rotation_option = 0; // off
305 sys->settings.scaling_quality = 1; // fast
306 sys->settings.sound_enabled = true;
307 sys->settings.sound_bufsize = 32; /* keep this low */
308 sys->settings.showfps = false;
309 sys->settings.zoom = false;
310 sys->settings.blur = false;
311 sys_rotate_keymap(sys);
312}
313
314static struct System* mainmenu_sysptr;
315
316static int mainmenu_cb(int action,
317 const struct menu_item_ex *this_item,
318 struct gui_synclist *this_list)
319{
320 (void)this_list;
321 int idx = ((intptr_t)this_item);
322 if(action == ACTION_REQUEST_MENUITEM && !mainmenu_sysptr->loaded && (idx == 0 || idx == 8 || idx == 10))
323 return ACTION_EXIT_MENUITEM;
324 return action;
325}
326
327static AudioCallback audio_callback = NULL;
328static void get_more(const void** start, size_t* size);
329static void* audio_param;
330static struct System* audio_sys;
331
332/************************************** MAIN MENU ***************************************/
333/* called after game init */
334
335void sys_menu(struct System* sys)
336{
337 sys_stopAudio(sys);
338
339#ifdef HAVE_ADJUSTABLE_CPU_FREQ
340 /* boost for load */
341 rb->cpu_boost(true);
342#endif
343
344 rb->splash(0, "Loading...");
345 sys->loaded = engine_loadGameState(sys->e, 0);
346
347#ifdef HAVE_ADJUSTABLE_CPU_FREQ
348 rb->cpu_boost(false);
349#endif
350
351 rb->lcd_update();
352
353 mainmenu_sysptr = sys;
354 MENUITEM_STRINGLIST(menu, "XWorld Menu", mainmenu_cb,
355 "Resume Game", /* 0 */
356 "Start New Game", /* 1 */
357 "Video Settings", /* 2 */
358 "Sound Settings", /* 3 */
359 "Fast Mode", /* 4 */
360 "Help", /* 5 */
361 "Reset Settings", /* 6 */
362 "Load", /* 7 */
363 "Save", /* 8 */
364 "Quit without Saving", /* 9 */
365 "Save and Quit"); /* 10 */
366 bool quit = false;
367 while(!quit)
368 {
369 switch(rb->do_menu(&menu, NULL, NULL, false))
370 {
371 case 0:
372 quit = true;
373 break;
374 case 1:
375 vm_initForPart(&sys->e->vm, GAME_PART_FIRST); // This game part is the protection screen
376 quit = true;
377 break;
378 case 2:
379 do_video_settings(sys);
380 break;
381 case 3:
382 do_sound_settings(sys);
383 break;
384 case 4:
385 rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
386 sys_save_settings(sys);
387 break;
388 case 5:
389 sys_do_help();
390 break;
391 case 6:
392 sys_reset_settings(sys);
393 sys_save_settings(sys);
394 break;
395 case 7:
396 rb->splash(0, "Loading...");
397 sys->loaded = engine_loadGameState(sys->e, 0);
398 rb->lcd_update();
399 break;
400 case 8:
401 sys->e->_stateSlot = 0;
402 rb->splash(0, "Saving...");
403 engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
404 rb->lcd_update();
405 break;
406 case 9:
407 engine_deleteGameState(sys->e, 0);
408 exit(PLUGIN_OK);
409 break;
410 case 10:
411 /* saves are NOT deleted on loading */
412 exit(PLUGIN_OK);
413 break;
414 default:
415 break;
416 }
417 }
418
419#ifdef HAVE_ADJUSTABLE_CPU_FREQ
420 /* boost for game */
421 rb->cpu_boost(true);
422#endif
423
424 sys_startAudio(sys, audio_callback, audio_param);
425}
426
427void sys_init(struct System* sys, const char* title)
428{
429 (void) title;
430
431 backlight_ignore_timeout();
432 rb_atexit(exit_handler);
433 save_sys = sys;
434 rb->memset(&sys->input, 0, sizeof(sys->input));
435 sys->mutex_bitfield = ~0;
436 if(!sys_load_settings(sys))
437 {
438 sys_reset_settings(sys);
439 }
440 struct viewport *vp_main = rb->lcd_set_viewport(NULL);
441 lcd_fb = vp_main->buffer->fb_ptr;
442}
443
444void sys_destroy(struct System* sys)
445{
446 (void) sys;
447 rb->splash(HZ, "Exiting...");
448}
449
450void sys_setPalette(struct System* sys, uint8_t start, uint8_t n, const uint8_t *buf)
451{
452 for(int i = start; i < start + n; ++i)
453 {
454 uint8_t c[3];
455 for (int j = 0; j < 3; j++) {
456 uint8_t col = buf[i * 3 + j];
457 c[j] = (col << 2) | (col & 3);
458 }
459#if (LCD_DEPTH > 24)
460 sys->palette[i] = (fb_data) {
461 c[2], c[1], c[0], 255
462 };
463#elif (LCD_DEPTH > 16) && (LCD_DEPTH <= 24)
464 sys->palette[i] = (fb_data) {
465 c[2], c[1], c[0]
466 };
467#elif defined(HAVE_LCD_COLOR)
468 sys->palette[i] = FB_RGBPACK(c[0], c[1], c[2]);
469#elif LCD_DEPTH > 1
470 sys->palette[i] = LCD_BRIGHTNESS((c[0] + c[1] + c[2]) / 3);
471#endif
472 }
473}
474
475/****************************** THE MAIN DRAWING METHOD ********************************/
476
477void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t *buf, uint32_t pitch)
478{
479 static int last_timestamp = 1;
480
481 /* get the address of the temporary framebuffer that has been allocated in the audiobuf */
482 fb_data* framebuffer = (fb_data*) sys->e->res._memPtrStart + MEM_BLOCK_SIZE + (4 * VID_PAGE_SIZE);
483 buf += y * pitch + x;
484
485 /************************ BLIT THE TEMPORARY FRAMEBUFFER ***********************/
486
487 if(sys->settings.rotation_option)
488 {
489 /* clockwise */
490 if(sys->settings.rotation_option == 1)
491 {
492 while (h--) {
493 /* one byte gives two pixels */
494 for (int i = 0; i < w / 2; ++i) {
495 uint8_t pix1 = *(buf + i) >> 4;
496 uint8_t pix2 = *(buf + i) & 0xF;
497#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
498 framebuffer[( (h * 2) ) * 320 + i] = sys->palette[pix1];
499 framebuffer[( (h * 2) + 1) * 320 + i] = sys->palette[pix2];
500#else
501 framebuffer[( (i * 2) ) * 200 + h] = sys->palette[pix1];
502 framebuffer[( (i * 2) + 1) * 200 + h] = sys->palette[pix2];
503#endif
504 }
505 buf += pitch;
506 }
507 }
508 /* counterclockwise */
509 else
510 {
511 while (h--) {
512 /* one byte gives two pixels */
513 for (int i = 0; i < w / 2; ++i) {
514 uint8_t pix1 = *(buf + i) >> 4;
515 uint8_t pix2 = *(buf + i) & 0xF;
516#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
517 framebuffer[(200 - h * 2 ) * 320 + ( 320 - i )] = sys->palette[pix1];
518 framebuffer[(200 - h * 2 - 1) * 320 + ( 320 - i )] = sys->palette[pix2];
519#else
520 framebuffer[(320 - i * 2 ) * 200 + ( 200 - h )] = sys->palette[pix1];
521 framebuffer[(320 - i * 2 - 1) * 200 + ( 200 - h )] = sys->palette[pix2];
522#endif
523 }
524 buf += pitch;
525 }
526 }
527 }
528 /* no rotation */
529 else
530 {
531 int next = 0;
532#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
533 for(int x = 0; x < w / 2; ++x)
534 {
535 for(int y = 0; y < h; ++y)
536 {
537 uint8_t pix1 = buf[ y * w + x ] >> 4;
538 uint8_t pix2 = buf[ y * w + x ] & 0xF;
539 framebuffer[(x + 0)*h + y] = sys->palette[pix1];
540 framebuffer[(x + 1)*h + y] = sys->palette[pix2];
541 }
542 }
543#else
544 while (h--) {
545 /* one byte gives two pixels */
546 for (int i = 0; i < w / 2; ++i) {
547 uint8_t pix1 = *(buf + i) >> 4;
548 uint8_t pix2 = *(buf + i) & 0xF;
549 framebuffer[next] = sys->palette[pix1];
550 ++next;
551 framebuffer[next] = sys->palette[pix2];
552 ++next;
553 }
554 buf += pitch;
555 }
556#endif
557 }
558
559 /*************************** NOW SCALE IT! ***************************/
560
561 if(sys->settings.scaling_quality)
562 {
563 struct bitmap in_bmp;
564 if(sys->settings.rotation_option)
565 {
566#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
567 in_bmp.width = 320;
568 in_bmp.height = 200;
569#else
570 in_bmp.width = 200;
571 in_bmp.height = 320;
572#endif
573 }
574 else
575 {
576#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
577 in_bmp.width = 200;
578 in_bmp.height = 320;
579#else
580 in_bmp.width = 320;
581 in_bmp.height = 200;
582#endif
583 }
584 in_bmp.data = (unsigned char*) framebuffer;
585 struct bitmap out_bmp;
586 out_bmp.width = LCD_WIDTH;
587 out_bmp.height = LCD_HEIGHT;
588 out_bmp.data = (unsigned char*) lcd_fb;
589
590#ifdef HAVE_LCD_COLOR
591 if(sys->settings.scaling_quality == 1)
592#endif
593 simple_resize_bitmap(&in_bmp, &out_bmp);
594#ifdef HAVE_LCD_COLOR
595 else
596 smooth_resize_bitmap(&in_bmp, &out_bmp);
597#endif
598 }
599 else
600 {
601#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
602 for(int x = 0; x < 320; ++x)
603 {
604 for(int y = 0; y < 200; ++y)
605 {
606 rb->lcd_set_foreground(framebuffer[x * 200 + y]);
607 rb->lcd_drawpixel(x, y);
608 }
609 }
610#else
611 if(sys->settings.zoom)
612 {
613 rb->lcd_bitmap_part(framebuffer, CODE_X, CODE_Y, STRIDE(SCREEN_MAIN, 320, 200), 0, 0, 320 - CODE_X, 200 - CODE_Y);
614 }
615 else
616 {
617 if(sys->settings.rotation_option)
618 rb->lcd_bitmap(framebuffer, 0, 0, 200, 320);
619 else
620 rb->lcd_bitmap(framebuffer, 0, 0, 320, 200);
621 }
622#endif
623 }
624
625 /************************************* APPLY FILTERS ******************************************/
626
627 if(sys->settings.negative_enabled)
628 {
629 for(int y = 0; y < LCD_HEIGHT; ++y)
630 {
631 for(int x = 0; x < LCD_WIDTH; ++x)
632 {
633#ifdef HAVE_LCD_COLOR
634 int r, g, b;
635 fb_data pix = lcd_fb[y * LCD_WIDTH + x];
636#if (LCD_DEPTH > 24)
637 r = 0xff - pix.r;
638 g = 0xff - pix.g;
639 b = 0xff - pix.b;
640 lcd_fb[y * LCD_WIDTH + x] = (fb_data) { b, g, r, 255 };
641#elif (LCD_DEPTH == 24)
642 r = 0xff - pix.r;
643 g = 0xff - pix.g;
644 b = 0xff - pix.b;
645 lcd_fb[y * LCD_WIDTH + x] = (fb_data) { b, g, r };
646#else
647 r = RGB_UNPACK_RED (pix);
648 g = RGB_UNPACK_GREEN(pix);
649 b = RGB_UNPACK_BLUE (pix);
650 lcd_fb[y * LCD_WIDTH + x] = LCD_RGBPACK(0xff - r, 0xff - g, 0xff - b);
651#endif
652#else
653 lcd_fb[y * LCD_WIDTH + x] = LCD_BRIGHTNESS(0xff - lcd_fb[y * LCD_WIDTH + x]);
654#endif
655 }
656 }
657 }
658
659#ifdef SYS_MOTION_BLUR
660 if(sys->settings.blur)
661 {
662 static fb_data *prev_frames = NULL;
663 static fb_data *orig_fb = NULL;
664 static int prev_baseidx = 0; /* circular buffer */
665 if(!prev_frames)
666 {
667 prev_frames = sys_get_buffer(sys, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT * BLUR_FRAMES);
668 orig_fb = sys_get_buffer(sys, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
669
670 rb->memset(prev_frames, 0, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT * BLUR_FRAMES);
671 }
672 if(prev_frames && orig_fb)
673 {
674
675 rb->memcpy(orig_fb, lcd_fb, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
676 /* fancy useless slow motion blur */
677 for(int y = 0; y < LCD_HEIGHT; ++y)
678 {
679 for(int x = 0; x < LCD_WIDTH; ++x)
680 {
681 int r, g, b;
682 fb_data pix = lcd_fb[y * LCD_WIDTH + x];
683 r = RGB_UNPACK_RED (pix);
684 g = RGB_UNPACK_GREEN(pix);
685 b = RGB_UNPACK_BLUE (pix);
686 r *= BLUR_FRAMES + 1;
687 g *= BLUR_FRAMES + 1;
688 b *= BLUR_FRAMES + 1;
689 for(int i = 0; i < BLUR_FRAMES; ++i)
690 {
691 fb_data prev_pix = prev_frames[ (prev_baseidx + i * (LCD_WIDTH * LCD_HEIGHT)) % (LCD_WIDTH * LCD_HEIGHT * BLUR_FRAMES) + y * LCD_WIDTH + x];
692 r += (BLUR_FRAMES-i) * RGB_UNPACK_RED(prev_pix);
693 g += (BLUR_FRAMES-i) * RGB_UNPACK_GREEN(prev_pix);
694 b += (BLUR_FRAMES-i) * RGB_UNPACK_BLUE(prev_pix);
695 }
696 r /= (BLUR_FRAMES + 1) / 2 * (1 + BLUR_FRAMES + 1);
697 g /= (BLUR_FRAMES + 1) / 2 * (1 + BLUR_FRAMES + 1);
698 b /= (BLUR_FRAMES + 1) / 2 * (1 + BLUR_FRAMES + 1);
699 lcd_fb[y * LCD_WIDTH + x] = LCD_RGBPACK(r, g, b);
700 }
701 }
702 prev_baseidx -= LCD_WIDTH * LCD_HEIGHT;
703 if(prev_baseidx < 0)
704 prev_baseidx += BLUR_FRAMES * LCD_WIDTH * LCD_HEIGHT;
705 rb->memcpy(prev_frames + prev_baseidx, orig_fb, sizeof(fb_data) * LCD_WIDTH * LCD_HEIGHT);
706 }
707 }
708#endif
709
710 /*********************** SHOW FPS *************************/
711
712 int current_time = sys_getTimeStamp(sys);
713 if(sys->settings.showfps)
714 {
715 rb->lcd_set_foreground(LCD_BLACK);
716 rb->lcd_set_background(LCD_WHITE);
717 /* use 1000 and not HZ here because getTimeStamp is in milliseconds */
718 rb->lcd_putsf(0, 0, "FPS: %d", 1000 / ((current_time - last_timestamp == 0) ? 1 : current_time - last_timestamp));
719 }
720 rb->lcd_update();
721 last_timestamp = sys_getTimeStamp(sys);
722}
723
724static void do_pause_menu(struct System* sys)
725{
726 sys_stopAudio(sys);
727
728#ifdef HAVE_ADJUSTABLE_CPU_FREQ
729 rb->cpu_boost(false);
730#endif
731
732 int sel = 0;
733 MENUITEM_STRINGLIST(menu, "XWorld Menu", NULL,
734 "Resume Game", /* 0 */
735 "Start New Game", /* 1 */
736 "Video Settings", /* 2 */
737 "Sound Settings", /* 3 */
738 "Fast Mode", /* 4 */
739 "Enter Code", /* 5 */
740 "Help", /* 6 */
741 "Reset Settings", /* 7 */
742 "Load", /* 8 */
743 "Save", /* 9 */
744 "Quit"); /* 10 */
745
746 bool quit = false;
747 while(!quit)
748 {
749 switch(rb->do_menu(&menu, &sel, NULL, false))
750 {
751 case 0:
752 quit = true;
753 break;
754 case 1:
755 vm_initForPart(&sys->e->vm, GAME_PART_FIRST);
756 quit = true;
757 break;
758 case 2:
759 do_video_settings(sys);
760 break;
761 case 3:
762 do_sound_settings(sys);
763 break;
764 case 4:
765 rb->set_bool("Fast Mode", &sys->e->vm._fastMode);
766 sys_save_settings(sys);
767 break;
768 case 5:
769 sys->input.code = true;
770 quit = true;
771 break;
772 case 6:
773 sys_do_help();
774 break;
775 case 7:
776 sys_reset_settings(sys);
777 sys_save_settings(sys);
778 break;
779 case 8:
780 rb->splash(0, "Loading...");
781 sys->loaded = engine_loadGameState(sys->e, 0);
782 rb->lcd_update();
783 quit = true;
784 break;
785 case 9:
786 sys->e->_stateSlot = 0;
787 rb->splash(0, "Saving...");
788 engine_saveGameState(sys->e, sys->e->_stateSlot, "quicksave");
789 rb->lcd_update();
790 break;
791 case 10:
792 exit(PLUGIN_OK);
793 break;
794 }
795 }
796
797#ifdef HAVE_ADJUSTABLE_CPU_FREQ
798 rb->cpu_boost(true);
799#endif
800
801 sys_startAudio(sys, audio_callback, audio_param);
802}
803
804void sys_processEvents(struct System* sys)
805{
806 int btn = rb->button_status();
807 rb->button_clear_queue();
808
809 static int oldbuttonstate = 0;
810
811 debug(DBG_SYS, "button is 0x%08x", btn);
812
813 /* exit early if we can */
814 if(btn == oldbuttonstate)
815 {
816 return;
817 }
818
819 /* handle special keys first */
820 switch(btn)
821 {
822 case BTN_PAUSE:
823 do_pause_menu(sys);
824 sys->input.dirMask = 0;
825 sys->input.button = false;
826 /* exit early to avoid unwanted button presses being detected */
827 return;
828 default:
829 exit_on_usb(btn);
830 break;
831 }
832
833 /* Ignore some buttons that cause errant input */
834
835 btn &= ~BUTTON_REDRAW;
836
837#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
838 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
839 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
840 if(btn & 0x80000000)
841 return;
842#endif
843
844#if (CONFIG_KEYPAD == SANSA_E200_PAD)
845 if(btn == (BUTTON_SCROLL_FWD || BUTTON_SCROLL_BACK))
846 return;
847#endif
848
849#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
850 if(btn == (BUTTON_SELECT))
851 return;
852#endif
853
854 /* copied from doom which was copied from rockboy... */
855 unsigned released = ~btn & oldbuttonstate;
856 unsigned pressed = btn & ~oldbuttonstate;
857 oldbuttonstate = btn;
858
859 if(released)
860 {
861 if(released & BTN_FIRE)
862 sys->input.button = false;
863 if(released & sys->keymap.up)
864 sys->input.dirMask &= ~DIR_UP;
865 if(released & sys->keymap.down)
866 sys->input.dirMask &= ~DIR_DOWN;
867 if(released & sys->keymap.left)
868 sys->input.dirMask &= ~DIR_LEFT;
869 if(released & sys->keymap.right)
870 sys->input.dirMask &= ~DIR_RIGHT;
871#ifdef BTN_DOWN_LEFT
872 if(released & sys->keymap.downleft)
873 sys->input.dirMask &= ~(DIR_DOWN | DIR_LEFT);
874#endif
875#ifdef BTN_DOWN_RIGHT
876 if(released & sys->keymap.downright)
877 sys->input.dirMask &= ~(DIR_DOWN | DIR_RIGHT);
878#endif
879#ifdef BTN_UP_LEFT
880 if(released & sys->keymap.upleft)
881 sys->input.dirMask &= ~(DIR_UP | DIR_LEFT);
882#endif
883#ifdef BTN_UP_RIGHT
884 if(released & sys->keymap.upright)
885 sys->input.dirMask &= ~(DIR_UP | DIR_RIGHT);
886#endif
887 }
888
889 if(pressed)
890 {
891 if(pressed & BTN_FIRE)
892 sys->input.button = true;
893 if(pressed & sys->keymap.up)
894 sys->input.dirMask |= DIR_UP;
895 if(pressed & sys->keymap.down)
896 sys->input.dirMask |= DIR_DOWN;
897 if(pressed & sys->keymap.left)
898 sys->input.dirMask |= DIR_LEFT;
899 if(pressed & sys->keymap.right)
900 sys->input.dirMask |= DIR_RIGHT;
901#ifdef BTN_DOWN_LEFT
902 if(pressed & sys->keymap.downleft)
903 sys->input.dirMask |= (DIR_DOWN | DIR_LEFT);
904#endif
905#ifdef BTN_DOWN_RIGHT
906 if(pressed & sys->keymap.downright)
907 sys->input.dirMask |= (DIR_DOWN | DIR_RIGHT);
908#endif
909#ifdef BTN_UP_LEFT
910 if(pressed & sys->keymap.upleft)
911 sys->input.dirMask |= (DIR_UP | DIR_LEFT);
912#endif
913#ifdef BTN_UP_RIGHT
914 if(pressed & sys->keymap.upright)
915 sys->input.dirMask |= (DIR_UP | DIR_RIGHT);
916#endif
917 }
918
919#if 0
920 /* handle releases */
921 if(btn & BUTTON_REL)
922 {
923 debug(DBG_SYS, "button_rel");
924 btn &= ~BUTTON_REL;
925
926 if(btn & BTN_FIRE)
927 sys->input.button = false;
928 if(btn & sys->keymap.up)
929 sys->input.dirMask &= ~DIR_UP;
930 if(btn & sys->keymap.down)
931 sys->input.dirMask &= ~DIR_DOWN;
932 if(btn & sys->keymap.left)
933 sys->input.dirMask &= ~DIR_LEFT;
934 if(btn & sys->keymap.right)
935 sys->input.dirMask &= ~DIR_RIGHT;
936#ifdef BTN_DOWN_LEFT
937 if(btn & sys->keymap.downleft)
938 sys->input.dirMask &= ~(DIR_DOWN | DIR_LEFT);
939#endif
940#ifdef BTN_DOWN_RIGHT
941 if(btn & sys->keymap.downright)
942 sys->input.dirMask &= ~(DIR_DOWN | DIR_RIGHT);
943#endif
944#ifdef BTN_UP_LEFT
945 if(btn & sys->keymap.upleft)
946 sys->input.dirMask &= ~(DIR_UP | DIR_LEFT);
947#endif
948#ifdef BTN_UP_RIGHT
949 if(btn & sys->keymap.upright)
950 sys->input.dirMask &= ~(DIR_UP | DIR_RIGHT);
951#endif
952 }
953 /* then handle presses */
954 else
955 {
956 if(btn & BTN_FIRE)
957 sys->input.button = true;
958 if(btn & sys->keymap.up)
959 sys->input.dirMask |= DIR_UP;
960 if(btn & sys->keymap.down)
961 sys->input.dirMask |= DIR_DOWN;
962 if(btn & sys->keymap.left)
963 sys->input.dirMask |= DIR_LEFT;
964 if(btn & sys->keymap.right)
965 sys->input.dirMask |= DIR_RIGHT;
966#ifdef BTN_DOWN_LEFT
967 if(btn & sys->keymap.downleft)
968 sys->input.dirMask |= (DIR_DOWN | DIR_LEFT);
969#endif
970#ifdef BTN_DOWN_RIGHT
971 if(btn & sys->keymap.downright)
972 sys->input.dirMask |= (DIR_DOWN | DIR_RIGHT);
973#endif
974#ifdef BTN_UP_LEFT
975 if(btn & sys->keymap.upleft)
976 sys->input.dirMask |= (DIR_UP | DIR_LEFT);
977#endif
978#ifdef BTN_UP_RIGHT
979 if(btn & sys->keymap.upright)
980 sys->input.dirMask |= (DIR_UP | DIR_RIGHT);
981#endif
982 }
983 debug(DBG_SYS, "dirMask is 0x%02x", sys->input.dirMask);
984 debug(DBG_SYS, "button is %s", sys->input.button == true ? "true" : "false");
985#endif
986}
987
988void sys_sleep(struct System* sys, uint32_t duration)
989{
990 (void) sys;
991 /* duration is in ms */
992 rb->sleep(duration / (1000/HZ));
993}
994
995uint32_t sys_getTimeStamp(struct System* sys)
996{
997 (void) sys;
998 return (uint32_t) (*rb->current_tick * (1000/HZ));
999}
1000
1001/* game provides us mono samples, we need stereo */
1002static int16_t rb_soundbuf[MAX_SOUNDBUF_SIZE * 2];
1003static int8_t temp_soundbuf[MAX_SOUNDBUF_SIZE];
1004
1005static void get_more(const void** start, size_t* size)
1006{
1007 if(audio_sys->settings.sound_enabled && audio_callback)
1008 {
1009 audio_callback(audio_param, temp_soundbuf, audio_sys->settings.sound_bufsize);
1010
1011 /* convert xworld format (signed 8-bit) to rockbox format (stereo signed 16-bit) */
1012 for(int i = 0; i < audio_sys->settings.sound_bufsize; ++i)
1013 {
1014 rb_soundbuf[2*i] = rb_soundbuf[2*i+1] = temp_soundbuf[i] * 256;
1015 }
1016 }
1017 else
1018 {
1019 rb->memset(rb_soundbuf, 0, audio_sys->settings.sound_bufsize * 2 * sizeof(int16_t));
1020 }
1021 *start = rb_soundbuf;
1022 *size = audio_sys->settings.sound_bufsize * 2 * sizeof(int16_t);
1023}
1024
1025void sys_startAudio(struct System* sys, AudioCallback callback, void *param)
1026{
1027 (void) sys;
1028 audio_callback = callback;
1029 audio_param = param;
1030 audio_sys = sys;
1031
1032 rb->pcm_play_data(get_more, NULL, NULL, 0);
1033}
1034
1035void sys_stopAudio(struct System* sys)
1036{
1037 (void) sys;
1038 rb->pcm_play_stop();
1039}
1040
1041uint32_t sys_getOutputSampleRate(struct System* sys)
1042{
1043 (void) sys;
1044 return rb->mixer_get_frequency();
1045}
1046
1047/* pretty non-reentrant here, but who cares? it's not like someone
1048 would want to run two instances of the game on Rockbox! :D */
1049
1050static uint32_t cur_delay;
1051static void* cur_param;
1052static TimerCallback user_callback;
1053static void timer_callback(void)
1054{
1055 debug(DBG_SYS, "timer callback with delay of %d ms callback 0x%08x param 0x%08x", cur_delay, timer_callback, cur_param);
1056 uint32_t ret = user_callback(cur_delay, cur_param);
1057 if(ret != cur_delay)
1058 {
1059 cur_delay = ret;
1060 rb->timer_register(1, NULL, TIMER_FREQ / 1000 * ret, timer_callback IF_COP(, CPU));
1061 }
1062}
1063
1064void *sys_addTimer(struct System* sys, uint32_t delay, TimerCallback callback, void *param)
1065{
1066 (void) sys;
1067 debug(DBG_SYS, "registering timer with delay of %d ms callback 0x%08x param 0x%08x", delay, callback, param);
1068 user_callback = callback;
1069 cur_delay = delay;
1070 cur_param = param;
1071 rb->timer_register(1, NULL, TIMER_FREQ / 1000 * delay, timer_callback IF_COP(, CPU));
1072 return NULL;
1073}
1074
1075void sys_removeTimer(struct System* sys, void *timerId)
1076{
1077 (void) sys;
1078 (void) timerId;
1079 /* there's only one timer per game instance, so ignore both parameters */
1080 rb->timer_unregister();
1081}
1082
1083void *sys_createMutex(struct System* sys)
1084{
1085 if(!sys)
1086 error("sys is NULL!");
1087
1088 debug(DBG_SYS, "allocating mutex");
1089
1090 /* this bitfield works as follows: bit set = free, unset = in use */
1091 for(int i = 0; i < MAX_MUTEXES; ++i)
1092 {
1093 /* check that the corresponding bit is 1 (free) */
1094 if(sys->mutex_bitfield & (1 << i))
1095 {
1096 rb->mutex_init(sys->mutex_memory + i);
1097 sys->mutex_bitfield &= ~(1 << i);
1098 return sys->mutex_memory + i;
1099 }
1100 }
1101 warning("Out of mutexes!");
1102 return NULL;
1103}
1104
1105void sys_destroyMutex(struct System* sys, void *mutex)
1106{
1107 int mutex_number = ((char*)mutex - (char*)sys->mutex_memory) / sizeof(struct mutex); /* pointer arithmetic! check for bugs! */
1108 sys->mutex_bitfield |= 1 << mutex_number;
1109}
1110
1111void sys_lockMutex(struct System* sys, void *mutex)
1112{
1113 (void) sys;
1114 rb->mutex_lock((struct mutex*) mutex);
1115}
1116
1117void sys_unlockMutex(struct System* sys, void *mutex)
1118{
1119 (void) sys;
1120 rb->mutex_unlock((struct mutex*) mutex);
1121}
1122
1123uint8_t* getOffScreenFramebuffer(struct System* sys)
1124{
1125 (void) sys;
1126 return NULL;
1127}
1128
1129void *sys_get_buffer(struct System* sys, size_t sz)
1130{
1131 if((signed int)sys->bytes_left - (signed int)sz >= 0)
1132 {
1133 void* ret = sys->membuf;
1134 rb->memset(ret, 0, sz);
1135 sys->membuf = (char*)(sys->membuf) + sz;
1136 sys->bytes_left -= sz;
1137 return ret;
1138 }
1139 else
1140 {
1141 error("sys_get_buffer: out of memory!");
1142 return NULL;
1143 }
1144}
1145
1146void MutexStack(struct MutexStack_t* s, struct System *stub, void *mutex)
1147{
1148 s->sys = stub;
1149 s->_mutex = mutex;
1150 /* FW 2017-2-12: disabled; no blocking ops in IRQ context! */
1151 /*sys_lockMutex(s->sys, s->_mutex);*/
1152}
1153
1154void MutexStack_destroy(struct MutexStack_t* s)
1155{
1156 (void) s;
1157 /*sys_unlockMutex(s->sys, s->_mutex);*/
1158}