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/env perl
2############################################################################
3# __________ __ ___.
4# Open \______ \ ____ ____ | | _\_ |__ _______ ___
5# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8# \/ \/ \/ \/ \/
9# $Id$
10#
11# Copyright (C) 2018 William Wilgus
12#
13# All files in this archive are subject to the GNU General Public License.
14# See the file COPYING in the source tree root for full license agreement.
15#
16# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17# KIND, either express or implied.
18#
19############################################################################
20use strict;
21use warnings;
22
23# Extracts members from c struct
24# places them in a lua array 'member = {offset,size,"type"}'
25# requires two passes
26# first pass outputs a c file that shall be compiled with the -S option
27# second pass extracts the member, offset, size, type from the assembly
28
29my $svnrev = '$Revision$';
30my $helper_name = 'LUA_RB_SETTINGS_H_HELPER';
31
32############# configuration #############
33my @sections = (
34 '',
35 'struct system_status',
36 'struct user_settings',
37 'struct replaygain_settings',
38 'struct eq_band_setting',
39 'struct compressor_settings',
40 'struct mp3_enc_config',
41 '', # nothing to be captured just placeholder for lua section
42 'struct mp3entry',
43 'struct mp3_albumart',
44 'struct embedded_cuesheet',
45 #'struct cuesheet',
46 #'struct cue_track_info',
47);
48
49my @sections_lua = (
50 'rb.system',
51 'rb.system.global_status',
52 'rb.system.global_settings',
53 'rb.system.replaygain_settings',
54 'rb.system.eq_band_setting',
55 'rb.system.compressor_settings',
56 'rb.system.mp3_enc_config',
57 'rb.metadata',
58 'rb.metadata.mp3_entry',
59 'rb.metadata.mp3_albumart',
60 'rb.metadata.embedded_cuesheet',
61 #'rb.metadata.cuesheet',
62 #'rb.metadata.cue_track_info',
63);
64
65# structs will have their dependencies included automagically
66my @includes = ();
67
68my $section_lua_suffix = '';
69
70my %replace_type_prefix = (
71 unsigned => 'u_',
72 signed => '',
73 struct => 's_',
74 const => 'const_',
75 enum => 'e_',
76 '#pointer#' => 'ptr_',
77);
78
79my %replace_type = (
80 int => 'i',
81 uint => 'u_i',
82 long => 'l',
83 char => 'c',
84 bool => 'b',
85 double => 'd',
86 '#string#' => 'str',
87);
88
89############# internal #############
90my @section_count = (); #variables found
91my @section_found = (); #{} bracket matches
92my @section_lists = (); #variable declarations
93my @section_asm_regex = ();
94my @section_begin_regex = ();
95
96my $header = '';
97my $array_marker = '_typeisarray_';
98my $pointer_marker = '_typeisptr_';
99my $current_include;
100
101############# precompile regex for speed #############
102my $array_mark_regex = qr/^.*${array_marker}$/;
103my $pointer_mark_regex = qr/^(.*)${pointer_marker}.*$/;
104
105my $incl_regex = qr/^.*\".+\/(.+\.h)\".*$/;
106my $extern_regex = qr/^extern\b.*$/;
107
108#type var ????;
109my $decl_regex = qr/^(.+?\s.+;)/;
110my $typevar_regex = qr/\W*(?<type>.*?)\W*(?<var>[^\s\[]+)(?<arr>)\W*;/;
111my$typevar_array_regex = qr/\W*(?<type>.*?)\W*(?<var>[^\s\[]+)\W*(?<arr>\[.+\]).*;/;
112
113# struct or union defined within another structure
114my $embed_structunion_regex = qr/\W*(struct|union\s[^;}]+)/;
115
116#.."section",.."member"..=..offset,..size,..type,..arrayct
117my $asm_regex = qr/.*?,.*?(\".+?\".*?=.*?,.+?,.+?\".+\".*?,.*);/;
118my $asmMOST_regex = qr/\"(?<member>.+)\"=(?<offset>\d+),(?<size>\d+),\"(?<type>.+)\",(?<arr>\d+).*/;
119
120for(my $i = 0; $i < @sections; $i++)
121{
122 $section_asm_regex[$i] = qr/\"$sections[$i]\"${asm_regex}/;
123 $section_begin_regex[$i] = qr/^$sections[$i]\b\s*+[^\*;]*$/;
124 $section_count[$i] = 0;
125 $section_found[$i] = 0;
126 $section_lists[$i] = '';
127}
128
129my $section_end_regex = qr/}\s*;.*$/;
130
131#######################################################
132#extract all the variables within the structs(sections)
133#######################################################
134while(my $line = <STDIN>)
135{
136 next if($line =~ /^\s+$/);
137
138 chomp($line);
139
140 if($header) # [PASS 2]
141 {
142 for(my $i = 0; $i < @sections; $i++)
143 {
144 next if(!$sections[$i]);
145
146 if($line =~ $section_asm_regex[$i])
147 {
148 $section_lists[$i] .= $1.'@';
149 $section_count[$i]++;
150 last;
151 }
152 }
153 }
154 elsif($line =~ s/$helper_name\W*//) #is this the second pass?
155 {
156 $header = $line;
157 #warn $header."\n";
158 for(my $i = 0; $i < @sections; $i++)
159 {
160 @section_lists[$i] = '';
161 @section_count[$i] = 0;
162 }
163 }
164 else # [PASS 1]
165 {
166 if($line =~ $incl_regex){$current_include = $1; next;}
167 elsif($line =~ $extern_regex){next;}
168
169 for(my $i = 0; $i < @sections; $i++)
170 {
171 next if(!$sections[$i]);
172
173 if($section_found[$i] > 0)
174 {
175 # variable declaration?
176 if($line =~ $decl_regex)
177 {
178 $section_lists[$i] .= $1.'@';
179 $section_count[$i]++;
180 }
181 # struct or union defined within a structure skip if single line
182 elsif($line =~ $embed_structunion_regex && (index($line, "}") < 0))
183 {
184 my $embedtype = $line;
185 $embedtype =~ s{[^\w\d_]+}{}gx; #strip non conforming chars
186 while($line = <STDIN>)
187 {
188 #skip over the contents user will have to determine how to parse
189 if($line =~ /^\s*}.+$/)
190 {
191 if($line =~ $decl_regex)
192 {
193 $section_lists[$i] .= $embedtype." ".$1.'@';
194 $section_count[$i]++;
195 }
196 last
197 }
198 }
199 }
200
201 # struct end?
202 if($line =~ $section_end_regex)
203 {
204 $section_found[$i]--;
205 }
206 last;
207 }
208 elsif($line =~ $section_begin_regex[$i]) # struct begin?
209 {
210 if($current_include)
211 {
212 push (@includes, $current_include);
213 $current_include = '';
214 }
215 $section_found[$i]++;
216 $section_lists[$i] = '';
217 $section_count[$i] = 0;
218 last;
219 }
220 }
221 }#else
222}
223
224for(my $i = 0; $i < @sections; $i++)
225{
226 if($section_found[$i])
227 {
228 warn "$0 Warning formatting error in: $sections[$i]\n";
229 }
230}
231
232sub Extract_Variable {
233 #[PASS 1] extracts the member, offset, size, and type from the include file
234 my $sinput = $_[0];
235 my ($type, $var, $arr);
236
237 $sinput =~ s{\s*\*\s*}{${pointer_marker}}gx;
238 if($sinput =~ $typevar_array_regex) #arrays
239 {
240 $type = $+{type};
241 $var = $+{var};
242 $arr = $+{var};
243 if($type eq ''){$type = "<unknown>";}
244 if($sinput =~ s/\bchar\b//){$type = $replace_type{'#string#'};}
245 else{$type .= ${array_marker};} #for identification of array .. stripped later
246 }
247 elsif($sinput =~ $typevar_regex)
248 {
249 $type = $+{type};
250 $var = $+{var};
251 $arr = $+{var};
252 }
253 else {
254 warn "Skipping ".$sinput."\n";
255 return ('', '', '');
256 }
257
258 # Type substitutions
259 $type =~ s/^(unsigned|signed|struct)/$replace_type_prefix{$1}/x;
260 $type =~ s/\b(const|enum)\b/$replace_type_prefix{$1}/gx;
261 $type =~ s/^(?:.?+)(bool)\b/$replace_type{lc $1}/ix;
262 $type =~ s/^(uint|int)(?:\d\d_t)\b/$replace_type{lc $1}/ix; # ...intNN_t...
263 if($type =~ $array_mark_regex)
264 {
265 $type =~ s/\b(int|long|char|double)(${array_marker}.*)\b/$replace_type{$1}$2/;
266 }
267 else {$type =~ s/\b(int|long|char|double)\b/$replace_type{$1}/;}
268 $type =~ s{\s+}{}gx;
269
270 $var =~ s{[^\w\d_]+}{}gx; # strip non conforming chars
271
272 $arr =~ s{[^\[\d\]]+}{}gx; # get element count
273
274 return ($type, $var, $arr);
275}
276
277sub Print_Variable {
278 #[PASS 2] prints the member, offset, size, and type from the assembly file
279 my $sinput = $_[0];
280 my ($member, $offset, $size, $type, $arr);
281
282 $sinput =~ s{[^\w\d_,=\"\*]+}{}gx;
283 if($sinput =~ $asmMOST_regex)
284 {
285 $member = $+{member};
286 $offset = $+{offset};
287 $size = $+{size};
288 $type = $+{type};
289 $arr = $+{arr};
290
291 if($type =~ /^(.*)${array_marker}$/) # strip array marker add [n]
292 {
293 $type = sprintf('%s[%d]', $1, $arr);
294 }
295
296 printf "\t%s = \"0x%x, %d, %s\",\n", $member, $offset, $size, $type;
297 return 1;
298 }
299 return 0;
300}
301
302if($header) #output sections to lua file [PASS 2]
303{
304 print "-- Don't change this file!\n";
305 printf "-- It is automatically generated %s\n", $svnrev;
306 print "-- member = \"offset, size, type\"\n\n";
307
308 print "--";
309 foreach my $key (sort(keys %replace_type_prefix)) {
310 print $key, '= \'', $replace_type_prefix{$key}, '\', ';
311 }
312 print "\n--";
313 foreach my $key (sort(keys %replace_type)) {
314 print $key, '= \'', $replace_type{$key}, '\', ';
315 }
316 print "\n\n";
317
318 for(my $i = 0; $i < @sections_lua; $i++)
319 {
320 if($sections_lua[$i])
321 {
322 print "$sections_lua[$i]$section_lua_suffix = {\n";
323
324 my @members=split('@', $section_lists[$i]);
325 $section_lists[$i] = '';
326
327 foreach my $memb(@members)
328 {
329 $section_count[$i] -= Print_Variable($memb);
330 }
331
332 print "}\n\n";
333
334 if($sections[$i] && $section_count[$i] ne 0)
335 {
336 warn "$0 Warning: Failed to extract '$sections[$i]'\n";
337 }
338 }
339 }
340 print "\nreturn false\n";
341 #my ($user,$system,$cuser,$csystem) = times;
342 #warn "Pass2 ".$user." ".$system." ".$cuser." ".$csystem."\n";
343 exit;
344}
345
346#else output sections to .c file [PASS 1]
347my $c_header = join(", ", $helper_name, @sections);
348my $print_variable = 'PRINT_M_O_S_T';
349my $print_array = 'PRINT_ARRAY_M_O_S_T';
350my $emit_asm = 'ASM_EMIT_M_O_S_T';
351
352foreach my $incl(@includes)
353{
354 printf "#include \"%s\"\n", $incl;
355}
356
357print <<EOF
358
359#include <stddef.h> /* offsetof */
360#include <stdbool.h>
361
362/* (ab)uses the compiler to emit member offset and size to asm comments */
363/* GAS supports C-style comments in asm files other compilers may not */
364/* "NAME", "MEMBER" = ?OFFSET, ?SIZE, "TYPE, ?ARRAYCT";
365 NOTE: ? may differ between machines */
366
367#undef ${emit_asm}
368#define ${emit_asm}(name, member, type, offset, size, elems) asm volatile\\
369("/* "#name ", " #member " = %0, %1, " #type ", %2; */\\n" : : \\
370"n"(offset), "n"(size), "n"(elems))
371
372/* constraint 'n' - An immediate integer operand with a known numeric value is allowed */
373
374#undef ${print_variable}
375#define ${print_variable}(name, member, value, type) ${emit_asm}(#name, \\
376#member, #type, offsetof(name, member), sizeof(value), 0)
377
378#undef ${print_array}
379#define ${print_array}(name, member, value, type) ${emit_asm}(#name, \\
380#member, #type, offsetof(name, member), sizeof(value), sizeof(value)/sizeof(value[0]))
381
382int main(void)
383{
384
385/* GAS supports C-style comments in asm files other compilers may not */
386/* This header identifies assembler output for second pass */
387asm volatile("/* $c_header; */");
388
389EOF
390;
391my $section_prefix = "section_";
392my $format_asm = " %s(%s, %s, ${section_prefix}%d.%s, %s);\n";
393
394
395for(my $i = 0; $i < @sections; $i++)
396{
397 if($sections[$i] && $section_lists[$i])
398 {
399 printf "%s %s%d;\n", $sections[$i], $section_prefix, $i; #create variable for each section
400 my @members=split('@', $section_lists[$i]);
401 $section_lists[$i] = '';
402
403 foreach my $memb(@members)
404 {
405 my ($type, $var, $arr) = Extract_Variable($memb);
406 my $call = ${print_variable};
407
408 if($var =~ $pointer_mark_regex) #strip pointer marker
409 {
410 $type = $type.$1;
411 $var =~ s{.*${pointer_marker}}{}gx;
412 $type = $replace_type_prefix{'#pointer#'}.$type;
413 }
414
415 if($type && $var)
416 {
417 if($type =~ $array_mark_regex){$call = ${print_array};}
418 printf $format_asm, $call, $sections[$i], $var, $i, $var, $type;
419 }
420 }
421 }
422}
423print <<EOF
424
425 return 0;
426}
427
428EOF
429;
430
431#my ($user,$system,$cuser,$csystem) = times;
432#warn "Pass1 ".$user." ".$system." ".$cuser." ".$csystem."\n";