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 *
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}