mirror of
https://github.com/mpv-player/mpv
synced 2024-12-18 04:45:33 +00:00
b74edd4069
Make TOOLS/matroska.pl output structs with fields sorted by name in ebml_types.h to make the order of fields deterministic. Fix warnings in demux_mkv.c caused by the first struct fields switching between scalar and struct types due to non-deterministic ebml_types.h field order. Since it's deterministic now, this shouldn't change anymore. The warnings produced by the compilers are bogus, but we want to silence them anyway, since this could make developers overlook legitimate warnings. What commits7b52ba8
,6dd97cc
,4aae1ff
were supposed to fix. An earlier attempt sorted fields in the generated C source file, not the header file. Hopefully this is the last commit concerning this issue...
170 lines
5.3 KiB
Perl
Executable File
170 lines
5.3 KiB
Perl
Executable File
#! /usr/bin/env perl
|
|
|
|
# Generate C definitions for parsing Matroska files.
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use FindBin;
|
|
use lib "$FindBin::Bin/lib";
|
|
use Parse::Matroska::Definitions;
|
|
use Parse::Matroska::Reader;
|
|
|
|
use Getopt::Long;
|
|
use List::Util qw{max};
|
|
|
|
my @global_elem_list = @Parse::Matroska::Definitions::global_elem_list;
|
|
|
|
Getopt::Long::Configure(qw{auto_version auto_help});
|
|
my %opt;
|
|
GetOptions(\%opt,
|
|
"generate-header",
|
|
"generate-definitions",
|
|
"full",
|
|
);
|
|
|
|
if ($opt{"generate-header"}) {
|
|
generate_c_header();
|
|
} elsif ($opt{"generate-definitions"}) {
|
|
generate_c_definitions();
|
|
} else {
|
|
for (@ARGV) {
|
|
my $reader = Parse::Matroska::Reader->new($_ eq '-' ? \*STDIN : $_) or die $!;
|
|
while (my $elem = $reader->read_element($_ eq '-')) {
|
|
process_elem($elem, $_ eq '-');
|
|
}
|
|
}
|
|
}
|
|
|
|
# Generate declarations for libmpdemux/ebml_types.h
|
|
sub generate_c_header {
|
|
print "/* Generated by TOOLS/matroska.pl, do not edit manually */\n\n";
|
|
|
|
# Write a #define for the ElementID of each known element
|
|
for my $el (@global_elem_list) {
|
|
printf "#define %-40s 0x%s\n", $el->{definename}, $el->{elid};
|
|
}
|
|
print "\n";
|
|
|
|
# Define a struct for each ElementID that has child elements
|
|
for my $el (@global_elem_list) {
|
|
next unless $el->{subelements};
|
|
print "\nstruct $el->{structname} {\n";
|
|
|
|
# Figure out the length of the longest variable name
|
|
# Used for pretty-printing in the next step
|
|
my $l = max(map { length $_->{valname} } values %{$el->{subelements}});
|
|
|
|
# Output each variable, with pointers for array (multiple) elements
|
|
for my $subel (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
|
|
printf " %-${l}s %s%s;\n",
|
|
$subel->{valname}, $subel->{multiple}?'*':' ', $subel->{fieldname};
|
|
}
|
|
print "\n";
|
|
|
|
# Output a counter variable for each element
|
|
# (presence/absence for scalars, item count for arrays)
|
|
for my $subel (values %{$el->{subelements}}) {
|
|
print " int n_$subel->{fieldname};\n"
|
|
}
|
|
print "};\n";
|
|
}
|
|
print "\n";
|
|
|
|
# Output extern references for ebml_elem_desc structs for each of the elements
|
|
# These are defined by generate_c_definitions
|
|
for my $el (@global_elem_list) {
|
|
next unless $el->{subelements};
|
|
print "extern const struct ebml_elem_desc $el->{structname}_desc;\n";
|
|
}
|
|
print "\n";
|
|
|
|
# Output the max number of sub-elements a known element might have
|
|
printf "#define MAX_EBML_SUBELEMENTS %d\n",
|
|
max(map { scalar keys %{$_->{subelements}} }
|
|
grep { $_->{subelements} } @global_elem_list);
|
|
}
|
|
|
|
# Generate definitions for libmpdemux/ebml_defs.c
|
|
sub generate_c_definitions {
|
|
print "/* Generated by TOOLS/matroska.pl, do not edit manually */\n\n";
|
|
# ebml_defs.c uses macros declared in ebml.c
|
|
for my $el (@global_elem_list) {
|
|
print "\n";
|
|
if ($el->{subelements}) {
|
|
# set N for the next macros
|
|
print "#define N $el->{fieldname}\n";
|
|
|
|
# define a struct ebml_$N_desc and gets ready to define fields
|
|
# this secretly opens two scopes; hence the }}; at the end
|
|
print "E_S(\"$el->{name}\", ".scalar(keys %{$el->{subelements}}).")\n";
|
|
|
|
# define a field for each subelement
|
|
# also does lots of macro magic, but doesn't open a scope
|
|
for my $subel (sort { $a->{definename} cmp $b->{definename} } values %{$el->{subelements}}) {
|
|
print "F($subel->{definename}, $subel->{fieldname}, ".
|
|
($subel->{multiple}?'1':'0').")\n";
|
|
}
|
|
# close the struct
|
|
print "}};\n";
|
|
|
|
# unset N since we've used it
|
|
print "#undef N\n";
|
|
} else {
|
|
print "E(\"$el->{name}\", $el->{fieldname}, $el->{ebmltype})\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
sub repr {
|
|
my @ret;
|
|
foreach (@_) {
|
|
if (/'/) {
|
|
s/"/\\"/g;
|
|
push @ret, "\"$_\"";
|
|
} else {
|
|
push @ret, "'$_'";
|
|
}
|
|
}
|
|
return @ret if wantarray;
|
|
return pop @ret if defined wantarray;
|
|
return;
|
|
}
|
|
|
|
sub process_elem {
|
|
my ($elem, $read_bin) = @_;
|
|
unless ($opt{full}) {
|
|
if ($elem->{name} eq 'Cluster' || $elem->{name} eq 'Cues') {
|
|
$elem->skip;
|
|
return;
|
|
}
|
|
}
|
|
die unless $elem;
|
|
|
|
if ($elem->{type} ne 'skip') {
|
|
print "$elem->{depth} $elem->{elid} $elem->{name} size: $elem->{content_len} value: ";
|
|
}
|
|
|
|
if ($elem->{type} eq 'sub') {
|
|
print "subelements:\n";
|
|
while (my $chld = $elem->next_child($read_bin)) {
|
|
process_elem($chld);
|
|
}
|
|
} elsif ($elem->{type} eq 'binary') {
|
|
my $t = "<skipped $elem->{content_len} bytes>";
|
|
if ($elem->{content_len} < 20) {
|
|
$t = unpack "H*", $elem->get_value;
|
|
}
|
|
print "binary $t\n";
|
|
delete $elem->{value};
|
|
} elsif ($elem->{type} eq 'ebml_id') {
|
|
print "binary $elem->{value}->{elid} (".($elem->{value}->{name}||"UNKNOWN").")\n";
|
|
} elsif ($elem->{type} eq 'skip') {
|
|
# skip
|
|
} elsif ($elem->{type} eq 'str') {
|
|
print "string ". repr($elem->get_value) . "\n";
|
|
} else {
|
|
print "$elem->{type} ". $elem->get_value ."\n";
|
|
}
|
|
}
|