A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 765 lines 18 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * 9 * Copyright (C) 2005 Karl Kurbjun 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation; either version 2 14 * of the License, or (at your option) any later version. 15 * 16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 17 * KIND, either express or implied. 18 * 19 * H300 Port by Karl Kurbjun 20 * IPod port by Dave Chapman and Paul Louden 21 * Additional code contributed by Thom Johansen 22 * Based off work by: Digita Doom, IDoom, Prboom, lSDLDoom, LxDoom, 23 * MBF, Boom, DosDoom, 24 * and of course Original Doom by ID Software 25 * See: http://prboom.sourceforge.net/about.html for the history 26 * 27 * 28 ****************************************************************************/ 29 30#include "d_main.h" 31#include "doomdef.h" 32#include "settings.h" 33#include "m_fixed.h" 34#include "m_argv.h" 35#include "m_misc.h" 36#include "g_game.h" 37#include "rockmacros.h" 38#include "doomstat.h" 39#include "i_system.h" 40#include "hu_stuff.h" 41#include "st_stuff.h" 42#include "lib/helper.h" 43 44extern boolean timingdemo, singledemo, demoplayback, fastdemo; // killough 45 46int filearray[9]; 47int fpoint=1; // save 0 for closing 48 49int fileexists(const char * fname) 50{ 51 int fd; 52 fd = open(fname,O_RDONLY); 53 54 if (fd>=0) 55 { 56 close(fd); 57 return 0; 58 } 59 return -1; 60} 61 62#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 63int my_open(const char *file, int flags, ...) 64{ 65 if(fpoint==8) 66 return -1; 67#undef open 68 if (flags & O_CREAT) 69 { 70 va_list ap; 71 va_start(ap, flags); 72 filearray[fpoint]=rb->open(file, flags, va_arg(ap, unsigned int)); 73 va_end(ap); 74 } 75 else 76 filearray[fpoint]=rb->open(file, flags); 77 if(filearray[fpoint]<0) 78 return filearray[fpoint]; 79 80 fpoint++; 81 return filearray[fpoint-1]; 82} 83 84int my_close(int id) 85{ 86 int i=0; 87 if(id<0) 88 return id; 89 while(filearray[i]!=id && i<8) 90 i++; 91 92 if(i==8) 93 { 94 printf("A requested FID did not exist!!!!"); 95 return -9; 96 } 97#undef close 98 rb->close(id); 99 100 for(; i<fpoint-1; i++) 101 filearray[i]=filearray[i+1]; 102 103 fpoint--; 104 return 0; 105} 106#endif 107#define MAXARGVS 100 108 109bool noprintf=0; // Variable disables printf lcd updates to protect grayscale lib/direct lcd updates 110 111#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 112// Here is a hacked up printf command to get the output from the game. 113int printf(const char *fmt, ...) 114{ 115 static int p_xtpt; 116 char p_buf[50]; 117 rb->yield(); 118 va_list ap; 119 120 va_start(ap, fmt); 121 vsnprintf(p_buf,sizeof(p_buf), fmt, ap); 122 va_end(ap); 123 124 rb->lcd_putsxy(1,p_xtpt, (unsigned char *)p_buf); 125 if (!noprintf) 126 rb->lcd_update(); 127 128 p_xtpt+=8; 129 if(p_xtpt>LCD_HEIGHT-8) 130 { 131 p_xtpt=0; 132 if (!noprintf) 133 rb->lcd_clear_display(); 134 } 135 return 1; 136} 137#endif 138 139char *my_strtok( char * s, const char * delim ) 140{ 141 register char *spanp; 142 register int c, sc; 143 char *tok; 144 static char *lasts; 145 146 147 if (s == NULL && (s = lasts) == NULL) 148 return (NULL); 149 150 /* 151 * Skip (span) leading delimiters (s += strspn(s, delim), sort of). 152 */ 153cont: 154 c = *s++; 155 for (spanp = (char *)delim; (sc = *spanp++) != 0;) { 156 if (c == sc) 157 goto cont; 158 } 159 160 if (c == 0) { /* no non-delimiter characters */ 161 lasts = NULL; 162 return (NULL); 163 } 164 tok = s - 1; 165 166 /* 167 * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). 168 * Note that delim must have one NUL; we stop if we see that, too. 169 */ 170 for (;;) { 171 c = *s++; 172 spanp = (char *)delim; 173 do { 174 if ((sc = *spanp++) == c) { 175 if (c == 0) 176 s = NULL; 177 else 178 s[-1] = 0; 179 lasts = s; 180 return (tok); 181 } 182 } while (sc != 0); 183 } 184 /* NOTREACHED */ 185} 186 187struct argvlist 188{ 189 int timedemo; // 1 says there's a timedemo 190 int demonum; 191 int addonnum; 192} argvlist; 193 194const unsigned char versions_builtin[7][20] = 195{ 196 "Doom Shareware", 197 "Doom Registered", 198 "Ultimate Doom", 199 "Doom 2", 200 "Freedoom", 201 "Plutonia", 202 "TNT" 203}; 204 205const unsigned char wads_builtin[7][35] = 206{ 207 GAMEBASE"doom1.wad", 208 GAMEBASE"doom.wad", 209 GAMEBASE"doomu.wad", 210 GAMEBASE"doom2.wad", 211 GAMEBASE"doomf.wad", 212 GAMEBASE"plutonia.wad", 213 GAMEBASE"tnt.wad" 214}; 215 216int namemap[7]; 217static char **addons; 218static char **demolmp; 219char addon[200]; 220// This sets up the base game and builds up myargv/c 221bool Dhandle_ver (int dver) 222{ 223 switch (dver) { 224 case 0: /* Doom Shareware */ 225 gamemode = shareware; 226 gamemission = doom; 227 D_AddFile(wads_builtin[0],source_iwad); 228 break; 229 case 1: /* Doom registered */ 230 gamemode = registered; 231 gamemission = doom; 232 D_AddFile(wads_builtin[1],source_iwad); 233 break; 234 case 2: /* Ultimate Doom */ 235 gamemode = retail; 236 gamemission = doom; 237 D_AddFile(wads_builtin[2],source_iwad); 238 break; 239 case 3: /* Doom2 */ 240 gamemode = commercial; 241 gamemission = doom2; 242 D_AddFile(wads_builtin[3],source_iwad); 243 break; 244 case 4: /* Doom2f */ 245 gamemode = commercial; 246 gamemission = doom2; 247 D_AddFile(wads_builtin[4],source_iwad); 248 break; 249 case 5: /* Plutonia */ 250 gamemode = commercial; 251 gamemission = pack_plut; 252 D_AddFile(wads_builtin[5],source_iwad); 253 break; 254 case 6: /* TNT */ 255 gamemode = commercial; 256 gamemission = pack_tnt; 257 D_AddFile(wads_builtin[6],source_iwad); 258 break; 259 default: 260 gamemission = none; 261 return 0; 262 } 263 // Start adding to myargv 264 if(argvlist.timedemo && (gamemode == shareware)) 265 { 266 singletics = true; 267 timingdemo = true; // show stats after quit 268 G_DeferedPlayDemo("demo3"); 269 singledemo = true; // quit after one demo 270 } 271 272 if(argvlist.addonnum) 273 { 274 snprintf(addon,sizeof(addon),"%s%s", GAMEBASE"addons/", addons[argvlist.addonnum]); 275 D_AddFile(addon,source_pwad); 276 modifiedgame = true; 277 } 278 279 if(argvlist.demonum) 280 { 281 snprintf(addon, sizeof(addon),"%s%s", GAMEBASE"demos/", demolmp[argvlist.demonum]); 282 D_AddFile(addon, source_lmp); 283 G_DeferedPlayDemo(addon); 284 singledemo = true; // quit after one demo 285 } 286 return 1; 287} 288 289// This function builds up the basegame list for use in the options selection 290// it also sets the defaults for the argvlist 291// Now checking for rcokdoom.wad based on prboom.wad 292int Dbuild_base (struct opt_items *names) 293{ 294 if ( fileexists(GAMEBASE"rockdoom.wad") ) 295 return 0; 296 297 D_AddFile (GAMEBASE"rockdoom.wad", source_pwad); 298 299 int i=0, j; 300 /* Doom Shareware */ 301 /* Doom registered */ 302 /* Ultimate Doom */ 303 /* Doom2 */ 304 /* Doom2f */ 305 /* Plutonia */ 306 /* TNT */ 307 for(j=0;j<7;j++) 308 if ( !fileexists (wads_builtin[j]) ) 309 { 310 names[i].string=versions_builtin[j]; 311 names[i].voice_id=-1; 312 namemap[i]=j; 313 i++; 314 } 315 // Set argvlist defaults 316 argvlist.timedemo=0; 317 318 return i; 319} 320 321// This is a general function that takes in a menu_item structure and makes a list 322// of files within it based on matching the string stringmatch to the files. 323int Dbuild_filelistm(char ***names, char *firstentry, char *directory, char *stringmatch) 324{ 325 int i=0; 326 DIR *filedir; 327 struct dirent *dptr; 328 char *startpt; 329 char **temp; 330 331 filedir=rb->opendir(directory); 332 333 if(filedir==NULL) 334 { 335 temp=malloc(sizeof(char *)); 336 temp[0]=firstentry; 337 *names=temp; 338 return 1; 339 } 340 341 // Get the total number of entries 342 while((dptr=rb->readdir(filedir))) 343 i++; 344 345 // Reset the directory 346 rb->closedir(filedir); 347 filedir=rb->opendir(directory); 348 349 i++; 350 temp=malloc(i*sizeof(char *)); 351 temp[0]=firstentry; 352 i=1; 353 354 while((dptr=rb->readdir(filedir))) 355 { 356 if(rb->strcasestr(dptr->d_name, stringmatch)) 357 { 358 startpt=malloc(strlen(dptr->d_name)*sizeof(char)); 359 strcpy(startpt,dptr->d_name); 360 temp[i]=startpt; 361 i++; 362 } 363 } 364 rb->closedir(filedir); 365 *names=temp; 366 return i; 367} 368 369static int translatekey(int key) __attribute__ ((noinline)); 370 371// This key configuration code is not the cleanest or the most efficient, but it works 372static int translatekey(int key) 373{ 374 if (key<31) 375 { 376 switch(key) 377 { 378 case 0: 379 return 0; 380 case 1: 381 return KEY_RIGHTARROW; 382 case 2: 383 return KEY_LEFTARROW; 384 case 3: 385 return KEY_UPARROW; 386 case 4: 387 return KEY_DOWNARROW; 388 case 5: 389 return KEY_ENTER; 390 case 6: 391 return KEY_RCTRL; 392 case 7: 393 return ' '; 394 case 8: 395 return KEY_ESCAPE; 396 case 9: 397 return 'w'; 398 case 10: 399 return KEY_TAB; 400 default: 401 return 0; 402 } 403 } 404 else 405 { 406 switch(key) 407 { 408 case 0: 409 return 0; 410 case KEY_RIGHTARROW: 411 return 1; 412 case KEY_LEFTARROW: 413 return 2; 414 case KEY_UPARROW: 415 return 3; 416 case KEY_DOWNARROW: 417 return 4; 418 case KEY_ENTER: 419 return 5; 420 case KEY_RCTRL: 421 return 6; 422 case ' ': 423 return 7; 424 case KEY_ESCAPE: 425 return 8; 426 case 'w': 427 return 9; 428 case KEY_TAB: 429 return 10; 430 default: 431 return 0; 432 } 433 } 434} 435 436// I havn't added configurable keys for enter or escape because this requires some modification to 437// m_menu.c which hasn't been done yet. 438 439int Oset_keys() 440{ 441 int selected=0, result; 442 int menuquit=0; 443 444 445 static const struct opt_items doomkeys[] = { 446 { "Unmapped", -1 }, 447 { "Key Right", -1 }, 448 { "Key Left", -1 }, 449 { "Key Up", -1 }, 450 { "Key Down", -1 }, 451 { "Key Select", -1 }, 452#if defined(TOSHIBA_GIGABEAT_F) 453 { "Key A", -1 }, 454 { "Key Menu", -1 }, 455 { "Key Power", -1 }, 456 { "Key Volume Down", -1 }, 457 { "Key Volume Up", -1 }, 458#else 459 { "Key Record", -1 }, 460 { "Key Mode", -1 }, 461 { "Key Off", -1 }, 462 { "Key On", -1 }, 463#endif 464 }; 465 466 int *keys[]={ 467 &key_right, 468 &key_left, 469 &key_up, 470 &key_down, 471 &key_fire, 472 &key_use, 473 &key_strafe, 474 &key_weapon, 475 &key_map 476 }; 477 478 int numdoomkeys=sizeof(doomkeys) / sizeof(*doomkeys); 479 480 MENUITEM_STRINGLIST(menu, "Set Keys", NULL, 481 "Game Right", "Game Left", "Game Up", "Game Down", 482 "Game Shoot", "Game Open", "Game Strafe", 483 "Game Weapon", "Game Automap"); 484 485 while(!menuquit) 486 { 487 result = rb->do_menu(&menu, &selected, NULL, false); 488 if(result<0) 489 menuquit=1; 490 else 491 { 492 *keys[result]=translatekey(*keys[result]); 493 rb->set_option(menu_[result], keys[result], RB_INT, doomkeys, numdoomkeys, NULL ); 494 *keys[result]=translatekey(*keys[result]); 495 } 496 } 497 498 return (1); 499} 500 501extern int fake_contrast; 502 503static bool Doptions() 504{ 505 static const struct opt_items onoff[2] = { 506 { "Off", -1 }, 507 { "On", -1 }, 508 }; 509 510 int selected=0, result; 511 int menuquit=0; 512 513 MENUITEM_STRINGLIST(menu, "Options", NULL, 514 "Set Keys", "Sound", "Timedemo", "Player Bobbing", 515 "Weapon Recoil", "Translucency", "Fake Contrast", 516 "Always Run", "Headsup Display", "Statusbar Always Red", 517#if(LCD_HEIGHT>LCD_WIDTH) 518 "Rotate Screen 90 deg", 519#endif 520 ); 521 522 void *options[]={ 523 &enable_sound, 524 &argvlist.timedemo, 525 &default_player_bobbing, 526 &default_weapon_recoil, 527 &default_translucency, 528 &fake_contrast, 529 &autorun, 530 &hud_displayed, 531 &sts_always_red, 532#if(LCD_HEIGHT>LCD_WIDTH) 533 &rotate_screen, 534#endif 535 }; 536 537 while(!menuquit) 538 { 539 result = rb->do_menu(&menu, &selected, NULL, false); 540 if(result==0) 541 Oset_keys(); 542 else if (result > 0) 543 rb->set_option(menu_[result], options[result-1], RB_INT, onoff, 2, NULL ); 544 else 545 menuquit=1; 546 } 547 548 return (1); 549} 550 551static const char* choice_get_name(int selected_item, void * data, 552 char * buffer, size_t buffer_len) 553{ 554 const char **names = (const char **) data; 555 (void) buffer; 556 (void) buffer_len; 557 return names[selected_item]; 558} 559 560int list_action_callback(int action, struct gui_synclist *lists) 561{ 562 (void) lists; 563 if (action == ACTION_STD_OK) 564 return ACTION_STD_CANCEL; 565 return action; 566} 567 568bool menuchoice(char **names, int count, int *selected) 569{ 570 struct simplelist_info info; 571 rb->simplelist_info_init(&info, NULL, count, (void*)names); 572 info.selection = *selected; 573 info.get_name = choice_get_name; 574 info.action_callback = list_action_callback; 575 if(rb->simplelist_show_list(&info)) 576 return true; 577 578 if(info.selection<count && info.selection>=0) 579 *selected = info.selection; 580 return false; 581} 582 583// 584// Doom Menu 585// 586int doom_menu() 587{ 588 int selected=0, result; 589 int status; 590 int gamever; 591 bool menuquit=0; 592 593 static struct opt_items names[7]; 594 595 MENUITEM_STRINGLIST(menu, "Doom Menu", NULL, 596 "Game", "Addons", "Demos", 597 "Options", "Play Game", "Quit"); 598 599 if( (status=Dbuild_base(names)) == 0 ) // Build up the base wad files (select last added file) 600 { 601 rb->splash(HZ*2, "Missing Base WAD!"); 602 return -2; 603 } 604 605 int numadd=Dbuild_filelistm(&addons, "No Addon", GAMEBASE"addons/", ".WAD" ); 606 607 int numdemos=Dbuild_filelistm(&demolmp, "No Demo", GAMEBASE"demos/", ".LMP" ); 608 609 argvlist.demonum=0; 610 argvlist.addonnum=0; 611 612 gamever=status-1; 613 614 /* Clean out the button Queue */ 615 while (rb->button_get(false) != BUTTON_NONE) 616 rb->yield(); 617 618 while(!menuquit) 619 { 620 result = rb->do_menu(&menu, &selected, NULL, false); 621 switch (result) { 622 case 0: /* Game picker */ 623 rb->set_option("Game WAD", &gamever, RB_INT, names, status, NULL ); 624 break; 625 626 case 1: /* Addon picker */ 627 menuchoice(addons,numadd,&argvlist.addonnum); 628 break; 629 630 case 2: /* Demos */ 631 menuchoice(demolmp,numdemos,&argvlist.demonum); 632 break; 633 634 case 3: /* Options */ 635 Doptions(); 636 break; 637 638 case 4: /* Play Game */ 639 menuquit=1; 640 break; 641 642 case 5: /* Quit */ 643 menuquit=1; 644 gamever=-1; 645 break; 646 647 default: 648 break; 649 } 650 } 651 652 return (gamever); 653} 654 655extern int systemvol; 656/* this is the plugin entry point */ 657enum plugin_status plugin_start(const void* parameter) 658{ 659 /* Disable all talking before initializing IRAM */ 660 rb->talk_disable(true); 661 662 (void)parameter; 663 664 doomexit=0; 665 666#ifdef HAVE_ADJUSTABLE_CPU_FREQ 667 rb->cpu_boost(true); 668#endif 669 670 rb->lcd_setfont(FONT_SYSFIXED); 671 672 // We're using doom's memory management since it implements a proper free (and re-uses the memory) 673 // and now with prboom's code: realloc and calloc 674 printf ("Z_Init: Init zone memory allocation daemon.\n"); 675 Z_Init (); 676 677 printf ("M_LoadDefaults: Load system defaults.\n"); 678 M_LoadDefaults (); // load before initing other systems 679 680 rb->splash(HZ*2, "Welcome to RockDoom"); 681 682 myargv =0; 683 myargc=0; 684 685 rb->lcd_clear_display(); 686 687 int result = doom_menu(); 688 if (result < 0) 689 { 690#ifdef HAVE_ADJUSTABLE_CPU_FREQ 691 rb->cpu_boost(false); 692#endif 693 rb->talk_disable(false); 694 if( result == -1 ) 695 return PLUGIN_OK; // Quit was selected 696 else 697 return PLUGIN_ERROR; // Missing base wads 698 } 699 700#if(LCD_HEIGHT>LCD_WIDTH) 701 if(rotate_screen) 702 { 703 SCREENHEIGHT=LCD_WIDTH; 704 SCREENWIDTH=LCD_HEIGHT; 705 } 706 else 707 { 708 SCREENHEIGHT=LCD_HEIGHT; 709 SCREENWIDTH=LCD_WIDTH; 710 } 711#endif 712 713 Dhandle_ver( namemap[ result ] ); 714 715 rb->lcd_setfont(FONT_SYSFIXED); 716 717 rb->lcd_clear_display(); 718 719 int mod = (rb->sound_max(SOUND_VOLUME)-rb->sound_min(SOUND_VOLUME))/15; 720 if(mod == 0) 721 mod = rb->global_status->volume; 722 systemvol= rb->global_status->volume-rb->global_status->volume%mod; 723 general_translucency = default_translucency; // phares 724 725 726 backlight_ignore_timeout(); 727 728#ifdef RB_PROFILE 729 rb->profile_thread(); 730#endif 731 732#if LCD_DEPTH>1 733 rb->lcd_set_backdrop(NULL); 734#endif 735 736 D_DoomMain (); 737 738#ifdef RB_PROFILE 739 rb->profstop(); 740#endif 741 742 backlight_use_settings(); 743 744 M_SaveDefaults (); 745 746 I_Quit(); // Make SURE everything was closed out right 747 748 printf("There were still: %d files open\n", fpoint); 749 while(fpoint>0) 750 { 751#if (CONFIG_PLATFORM & PLATFORM_HOSTED) 752 close(filearray[fpoint]); 753#else 754 rb->close(filearray[fpoint]); 755#endif 756 fpoint--; 757 } 758 759#ifdef HAVE_ADJUSTABLE_CPU_FREQ 760 rb->cpu_boost(false); 761#endif 762 763 rb->talk_disable(false); 764 return PLUGIN_OK; 765}