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