A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1#!/usr/bin/perl
2# __________ __ ___.
3# Open \______ \ ____ ____ | | _\_ |__ _______ ___
4# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7# \/ \/ \/ \/ \/
8# $Id$
9#
10
11use strict;
12use Getopt::Long qw(:config pass_through); # pass_through so not confused by -DTYPE_STUFF
13use IPC::Open2;
14
15my $ROOT="..";
16my $wpsdir;
17my $verbose;
18my $rbdir=".rockbox";
19my $tempdir=".rockbox";
20my $wpslist;
21my $target;
22my $modelname;
23
24# Get options
25GetOptions ( 'r|root=s' => \$ROOT,
26 'm|modelname=s' => \$modelname,
27 'v|verbose' => \$verbose,
28 'rbdir=s' => \$rbdir, # If we want to put in a different directory
29 'tempdir=s' => \$tempdir, # override .rockbox temporary dir
30 );
31
32($wpslist, $target) = @ARGV;
33
34my $firmdir="$ROOT/firmware";
35my $cppdef = $target;
36
37# These parameters are filled in as we parse wpslist
38my $req_t;
39my $theme;
40my $has_wps;
41my $wps;
42my $has_rwps;
43my $rwps;
44my $has_sbs;
45my $sbs;
46my $has_rsbs;
47my $rsbs;
48my $has_fms;
49my $fms;
50my $has_rfms;
51my $rfms;
52my $width;
53my $height;
54my $font;
55my $remotefont;
56my $fgcolor;
57my $bgcolor;
58my $sepcolor;
59my $sep;
60my $statusbar;
61my $remotestatusbar;
62my $author;
63my $backdrop;
64my $lineselectstart;
65my $lineselectend;
66my $selecttype;
67my $iconset;
68my $remoteiconset;
69my $viewericon;
70my $showicons;
71my $remoteviewericon;
72my $lineselecttextcolor;
73my $filetylecolor;
74my $listviewport;
75my $remotelistviewport;
76
77# LCD sizes
78my ($main_height, $main_width, $main_depth);
79my ($remote_height, $remote_width, $remote_depth);
80my $has_remote;
81
82
83if(!$wpslist) {
84 print "Usage: wpsbuilds.pl <WPSLIST> <target>\n",
85 "Run this script in the root of the target build, and it will put all the\n",
86 "stuff in $tempdir/wps/\n and build the cfg according to $rbdir";
87 exit;
88}
89
90sub getlcdsizes
91{
92 my ($remote) = @_;
93 my $str;
94
95 if($remote) {
96 # Get the remote LCD screen size
97 $str = <<STOP
98\#include "config.h"
99#ifdef HAVE_REMOTE_LCD
100Height: LCD_REMOTE_HEIGHT
101Width: LCD_REMOTE_WIDTH
102Depth: LCD_REMOTE_DEPTH
103#endif
104STOP
105;
106 }
107 else {
108 $str = <<STOP
109\#include "config.h"
110Height: LCD_HEIGHT
111Width: LCD_WIDTH
112Depth: LCD_DEPTH
113STOP
114;
115 }
116 close(GCC);
117
118 my $cmd = "gcc $cppdef -I. -I$firmdir/export -E -P -";
119 my $pid = open2(*COUT, *CIN, $cmd) or die "Could not spawn child: $!\n";
120
121 print CIN $str;
122 close(CIN);
123 waitpid($pid, 0);
124
125 my ($height, $width, $depth, $touch);
126 while(<COUT>) {
127 if($_ =~ /^Height: (\d*)/) {
128 $height = $1;
129 }
130 elsif($_ =~ /^Width: (\d*)/) {
131 $width = $1;
132 }
133 elsif($_ =~ /^Depth: (\d*)/) {
134 $depth = $1;
135 }
136 if($_ =~ /^Touchscreen/) {
137 $touch = 1;
138 }
139 }
140 close(COUT);
141
142 return ($height, $width, $depth);
143}
144
145sub mkdirs
146{
147 mkdir "$tempdir/wps", 0777;
148 mkdir "$tempdir/themes", 0777;
149 mkdir "$tempdir/icons", 0777;
150
151 if( -d "$tempdir/wps/$theme") {
152 #print STDERR "wpsbuild warning: directory wps/$theme already exists!\n";
153 }
154 else
155 {
156 mkdir "$tempdir/wps/$theme", 0777;
157 }
158}
159
160sub normalize
161{
162 my $in = $_[0];
163 # strip resolution
164 $in =~ s/(\.[0-9]*x[0-9]*x[0-9]*)//;
165 return $in;
166}
167
168sub copybackdrop
169{
170 #copy the backdrop file into the build dir
171 if ($backdrop ne '') {
172 my $dst = normalize($backdrop);
173 system("cp $ROOT/$backdrop $tempdir/$dst");
174 }
175}
176
177sub copythemefont
178{
179 #copy the font specified by the theme
180 my $o = $_[0];
181
182 $o =~ s/\.fnt/\.bdf/;
183 mkdir "$tempdir/fonts";
184 system("$ROOT/tools/convbdf -f -o \"$tempdir/fonts/$_[0]\" \"$ROOT/fonts/$o\" ");
185}
186
187sub copythemeicon
188{
189 my $i = $_[0];
190 #copy the icon specified by the theme
191 if ($i ne "-") {
192 my $tempicon = $tempdir . "/" . $i;
193 $tempicon =~ /\/.*icons\/(.*)/i;
194 system("cp $ROOT/icons/$1 $tempicon");
195 }
196}
197
198sub uniq {
199 my %seen = ();
200 my @r = ();
201 foreach my $a (@_) {
202 unless ($seen{$a}) {
203 push @r, $a;
204 $seen{$a} = 1;
205 }
206 }
207 return @r;
208}
209
210sub copywps
211{
212 # we assume that we copy the WPS files from the same dir the WPSLIST
213 # file is located in
214 my $dir;
215 my %skinfiles = ("wps", $wps,
216 "sbs", $sbs,
217 "fms", $fms,
218 "rwps", $rwps,
219 "rsbs", $rsbs,
220 "rfms", $rfms);
221 my @filelist;
222 my $file;
223
224 if($wpslist =~ /(.*)\/WPSLIST/) {
225 $dir = $1;
226
227 # copy fully-fledged wps, sbs, etc. including graphics
228 foreach my $ext (keys %skinfiles) {
229 next unless ($skinfiles{$ext});
230 $file = $skinfiles{$ext};
231 system("cp $dir/$file $tempdir/wps/$theme.$ext");
232 open(SKIN, "$dir/$file");
233 while (<SKIN>) {
234 $filelist[$#filelist + 1] = $1 if (/[\(,]([^,]*?.bmp)[\),]/);
235 }
236 close(SKIN);
237 }
238
239 if ($#filelist >= 0) {
240 if (-e "$dir/$theme") {
241 foreach $file (uniq(@filelist)) {
242 system("cp $dir/$theme/$file $tempdir/wps/$theme/");
243 }
244 }
245 else {
246 print STDERR "beep, no dir to copy WPS from!\n";
247 }
248 }
249 } else {
250 print STDERR "No source directory!\n";
251 }
252}
253
254sub buildcfg {
255 my $cfg = $theme . ".cfg";
256 my @out;
257
258 push @out, <<MOO
259\#
260\# $cfg generated by wpsbuild.pl
261\# $wps is made by $author
262\#
263MOO
264;
265
266 my %skinfiles = ("wps" => $wps,
267 "sbs" => $sbs,
268 "fms" => $fms,
269 "rwps" => $rwps,
270 "rsbs" => $rsbs,
271 "rfms" => $rfms);
272 for my $skin (keys %skinfiles) {
273 my $val = $skinfiles{$skin};
274 if (!defined($val)) {
275 # dont put value if not defined (e.g. rwps when there's no remote)
276 next;
277 } elsif ($val eq '') {
278 # empty resets to built-in
279 push @out, "$skin: -\n";
280 } else {
281 # file name is always <theme>.{wps,rwps,...} (see copywps())
282 push @out, "$skin: $rbdir/wps/$theme.$skin\n";
283 }
284 }
285
286 push @out, "selector type: $selecttype\n" if (defined($selecttype));
287 push @out, "backdrop: $backdrop\n" if (defined($backdrop));
288 push @out, "filetype colours: $filetylecolor\n" if (defined($filetylecolor));
289
290 if ($main_depth > 2) {
291 push @out, "foreground color: $fgcolor\n" if($fgcolor);
292 push @out, "background color: $bgcolor\n" if($bgcolor);
293 push @out, "line selector start color: $lineselectstart\n" if($lineselectstart);
294 push @out, "line selector end color: $lineselectend\n" if($lineselectend);;
295 push @out, "line selector text color: $lineselecttextcolor\n" if($lineselecttextcolor);
296 # list separator actually depends on HAVE_TOUCSCREEN
297 push @out, "list separator height: $sep\n" if($sep);
298 push @out, "list separator color: $sepcolor\n" if($sepcolor);
299 }
300
301 push @out, "font: $font\n" if (defined($font));
302 push @out, "statusbar: $statusbar\n" if (defined($statusbar));
303 push @out, "iconset: $iconset\n" if (defined($iconset));
304 push @out, "viewers iconset: $viewericon\n" if (defined($viewericon));
305 push @out, "show icons: $showicons\n" if (defined($viewericon) && defined($showicons));
306 push @out, "ui viewport: $listviewport\n" if (defined($listviewport));
307
308 if ($has_remote) {
309 push @out, "remote font: $remotefont\n" if (defined($remotefont));
310 push @out, "remote statusbar: $remotestatusbar\n" if (defined($remotestatusbar));
311 push @out, "remote iconset: $remoteiconset\n" if (defined($remoteiconset));
312 push @out, "remote viewers iconset: $remoteviewericon\n" if (defined($remoteviewericon));
313 push @out, "remote ui viewport: $remotelistviewport\n" if (defined($remotelistviewport));
314 }
315
316 if(-f "$tempdir/wps/$cfg") {
317 print STDERR "wpsbuild warning: wps/$cfg already exists!\n";
318 }
319 else {
320 open(CFG, ">$tempdir/themes/$cfg");
321 print CFG @out;
322 close(CFG);
323 }
324}
325
326# Get the LCD sizes first
327($main_height, $main_width, $main_depth) = getlcdsizes();
328($remote_height, $remote_width, $remote_depth) = getlcdsizes(1);
329
330#print "LCD: ${main_width}x${main_height}x${main_depth}\n";
331$has_remote = 1 if ($remote_height && $remote_width && $remote_depth);
332
333
334# check if line matches the setting string or if it contains a regex
335# that contains the targets resolution
336#
337# the line can be of the from
338# 1) setting: value (handled above)
339# 2) setting.128x96x2: value
340# 3) setting.128x96x2&touchscreen: value
341# The resolution pattern can be a perl-comatible regex
342sub check_res_feature {
343 my ($line, $string, $remote) = @_;
344 if ($line =~ /^$string: *(.*)/i) {
345 return $1;
346 }
347 elsif($line =~ /^$string\.(.*): *(.*)/i) {
348 # $1 is a resolution/feature regex, $2 the filename incl. resolution
349 my $substr = $1;
350 my $fn = $2;
351 my $feature;
352 my $size_str = "${main_width}x${main_height}x${main_depth}";
353 if ($remote) {
354 $size_str = "${remote_width}x${remote_height}x${remote_depth}";
355 }
356
357 # extract feature constraint, if any
358 if ($substr =~ /&([a-z0-9_]+)/) {
359 $feature = $1;
360 $substr =~ s/&$feature//;
361 }
362
363 if ($size_str =~ /$substr$/) {
364 if ($feature) {
365 # check feature constrait
366 open(FEAT, "<apps/features");
367 chomp(my @lines = <FEAT>);
368 close(FEAT);
369 my $matches = (grep { /$feature/ } @lines);
370 return $fn if $matches > 0;
371 }
372 else {
373 # no feature constraint
374 return $fn;
375 }
376 }
377 }
378 return "";
379}
380
381# check if <theme>.<model>.<ext> exists. If not, check if <theme>.<ext> exists
382sub check_skinfile {
383 my $ext = $_[0];
384 my $req_skin = $theme . "." . $modelname . ".$ext";
385 if (-e "$wpsdir/$req_skin") {
386 return $req_skin;
387 } else {
388 $req_skin = $theme . ".$ext";
389 if (-e "$wpsdir/$req_skin") {
390 return $req_skin;
391 }
392 }
393 return '';
394}
395
396
397# Infer WPS (etc.) filename from the the if it wasnt given
398$wpslist =~ /(.*)WPSLIST/;
399$wpsdir = $1;
400
401open(WPS, "<$wpslist");
402while(<WPS>) {
403 my $l = $_;
404
405 # remove CR
406 $l =~ s/\r//g;
407 if($l =~ /^ *\#/) {
408 # skip comment
409 next;
410 }
411
412 # prefix $rbdir with / if needed (needed for the theme.cfg)
413 unless ($rbdir =~ m/^\/.*/) {
414 $rbdir = "/" . $rbdir;
415 }
416
417 if($l =~ /^ *<theme>/i) {
418 # undef is a unary operator (!)
419 undef $theme;
420 undef $has_wps;
421 undef $has_rwps;
422 undef $has_sbs;
423 undef $has_rsbs;
424 undef $has_fms;
425 undef $has_rfms;
426 undef $wps;
427 undef $rwps;
428 undef $sbs;
429 undef $rsbs;
430 undef $fms;
431 undef $rfms;
432 undef $width;
433 undef $height;
434 undef $font;
435 undef $remotefont;
436 undef $fgcolor;
437 undef $bgcolor;
438 undef $sepcolor;
439 undef $sep;
440 undef $statusbar;
441 undef $remotestatusbar;
442 undef $author;
443 undef $backdrop;
444 undef $lineselectstart;
445 undef $lineselectend;
446 undef $selecttype;
447 undef $iconset;
448 undef $remoteiconset;
449 undef $viewericon;
450 undef $showicons;
451 undef $remoteviewericon;
452 undef $lineselecttextcolor;
453 undef $filetylecolor;
454 undef $listviewport;
455 undef $remotelistviewport;
456 }
457 elsif($l =~ /^Name: *(.*)/i) {
458 $theme = $1;
459 }
460 elsif($l =~ /^Authors: *(.*)/i) {
461 $author = $1;
462 }
463 elsif ($l =~ /^WPS: *(yes|no)/i) {
464 $has_wps = $1;
465 }
466 elsif ($l =~ /^RWPS: *(yes|no)/i) {
467 $has_rwps = $1;
468 }
469 elsif ($l =~ /^SBS: *(yes|no)/i) {
470 $has_sbs = $1;
471 }
472 elsif ($l =~ /^RSBS: *(yes|no)/i) {
473 $has_rsbs = $1;
474 }
475 elsif ($l =~ /^FMS: *(yes|no)/i) {
476 $has_fms = $1;
477 }
478 elsif ($l =~ /^RFMS: *(yes|no)/i) {
479 $has_rfms = $1;
480 }
481 elsif($l =~ /^ *<main>/i) {
482 # parse main unit settings
483 while(<WPS>) {
484 my $l = $_;
485 if ($l =~ /^ *<\/main>/i) {
486 last;
487 }
488 elsif($_ = check_res_feature($l, "wps")) {
489 $wps = $_;
490 }
491 elsif($_ = check_res_feature($l, "sbs")) {
492 $sbs = $_;
493 }
494 elsif($_ = check_res_feature($l, "fms")) {
495 $fms = $_;
496 }
497 elsif($_ = check_res_feature($l, "Font")) {
498 $font = $_;
499 }
500 elsif($_ = check_res_feature($l, "Statusbar")) {
501 $statusbar = $_;
502 }
503 elsif($_ = check_res_feature($l, "Backdrop")) {
504 $backdrop = $_;
505 }
506 elsif($l =~ /^Foreground Color: *(.*)/i) {
507 $fgcolor = $1;
508 }
509 elsif($l =~ /^Background Color: *(.*)/i) {
510 $bgcolor = $1;
511 }
512 elsif($_ = check_res_feature($l, "list separator color")) {
513 $sepcolor = $_;
514 }
515 elsif($_ = check_res_feature($l, "list separator height")) {
516 $sep = $_;
517 }
518 elsif($l =~ /^line selector start color: *(.*)/i) {
519 $lineselectstart = $1;
520 }
521 elsif($l =~ /^line selector end color: *(.*)/i) {
522 $lineselectend = $1;
523 }
524 elsif($_ = check_res_feature($l, "selector type")) {
525 $selecttype = $_;
526 }
527 elsif($_ = check_res_feature($l, "iconset")) {
528 $iconset = $_;
529 }
530 elsif($_ = check_res_feature($l, "viewers iconset")) {
531 $viewericon = $_;
532 }
533 elsif($l =~ /^show icons: *(.*)/i) {
534 $showicons = $1;
535 }
536 elsif($l =~ /^line selector text color: *(.*)/i) {
537 $lineselecttextcolor = $1;
538 }
539 elsif($l =~ /^filetype colours: *(.*)/i) {
540 $filetylecolor = $1;
541 }
542 elsif($_ = check_res_feature($l, "ui viewport")) {
543 $listviewport = $_;
544 }
545 }
546 }
547 elsif($l =~ /^ *<remote>/i) {
548 while(<WPS>) {
549 # parse remote settings
550 my $l = $_;
551 if ($l =~ /^ *<\/remote>/i) {
552 last;
553 }
554 elsif(!$has_remote) {
555 next; # dont parse <remote> section
556 }
557 elsif($_ = check_res_feature($l, "rwps", 1)) {
558 $rwps = $_;
559 }
560 elsif($_ = check_res_feature($l, "rsbs", 1)) {
561 $rsbs = $_;
562 }
563 elsif($_ = check_res_feature($l, "rfms", 1)) {
564 $rfms = $_;
565 }
566 elsif($_ = check_res_feature($l, "Font", 1)) {
567 $remotefont = $_;
568 }
569 elsif($_ = check_res_feature($l, "iconset", 1)) {
570 $remoteiconset = $_;
571 }
572 elsif($_ = check_res_feature($l, "viewers iconset", 1)) {
573 $remoteviewericon = $_;
574 }
575 elsif($_ = check_res_feature($l, "statusbar", 1)) {
576 $remotestatusbar = $_;
577 }
578 elsif($_ = check_res_feature($l, "ui viewport", 1)) {
579 $remotelistviewport = $_;
580 }
581 }
582 }
583 elsif($l =~ /^ *<\/theme>/i) {
584 # for each wps,sbs,fms (+ remote variants) check if <theme>[.<model>].wps
585 # exists if no filename was specified in WPSLIST
586 my $req_skin;
587
588 if ($has_wps eq "yes" && !$wps) {
589 $wps = check_skinfile("wps");
590 } elsif ($has_wps eq "no") {
591 $wps = '';
592 }
593
594 if ($has_sbs eq "yes" && !$sbs) {
595 $sbs = check_skinfile("sbs");
596 } elsif ($has_sbs eq "no") {
597 $sbs = '';
598 }
599
600 if ($has_fms eq "yes" && !$fms) {
601 $fms = check_skinfile("fms");
602 } elsif ($has_fms eq "no") {
603 $fms = '';
604 }
605
606 # now check for remote skin files (use main screen's extension)
607 if ($has_remote) {
608 if ($has_rwps eq "yes" && !$rwps) {
609 $rwps = check_skinfile("wps");
610 } elsif ($has_rwps eq "no") {
611 $rwps = '';
612 }
613
614 if ($has_rsbs eq "yes" && !$rsbs) {
615 $rsbs = check_skinfile("sbs");
616 } elsif ($has_rsbs eq "no") {
617 $rsbs = '';
618 }
619
620 if ($has_rfms eq "yes" && !$rfms) {
621 $rfms = check_skinfile("fms");
622 } elsif ($has_rfms eq "no") {
623 $rfms = '';
624 }
625 }
626 #print "LCD: $wps wants $width x $height\n";
627
628 #
629 # The target model has an LCD that is suitable for this
630 # WPS
631 #
632 #print "Size requirement is fine!\n";
633 mkdirs() if (-e "$wpsdir/$theme");
634 # Do the copying before building the .cfg - buildcfg()
635 # mangles some filenames
636 if (defined($backdrop) && $backdrop ne "-") {
637 copybackdrop();
638 $backdrop = "$rbdir/" . normalize($backdrop);
639 }
640 foreach my $i ($iconset, $viewericon, $remoteiconset, $remoteviewericon) {
641 if (defined($i) && $i ne "-") {
642 copythemeicon($i);
643 $i = "$rbdir/$i";
644 }
645 }
646 foreach my $i ($font, $remotefont) {
647 if (defined($i) && $i ne "-") {
648 copythemefont($i);
649 $i = "$rbdir/fonts/$i";
650 }
651 }
652 buildcfg();
653 copywps();
654 }
655 else{
656 #print "Unknown line: $l!\n";
657 }
658}
659
660close(WPS);