1
mirror of https://github.com/mpv-player/mpv synced 2024-11-14 22:48:35 +01:00
mpv/TOOLS/matroska.pl
wm4 b74edd4069 demux_mkv: fix compiler warnings
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 commits 7b52ba8, 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...
2013-11-04 23:49:22 +01:00

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";
}
}