The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use FileHandle;
use Data::Dumper;

my %XAUDIO_MISDEFINED_SYMBOL = (
	XA_NOTIFY_PRIVATE_DATA => 1,
	XA_VERSION_ID => 1,
	XA_VERSION_ID_REVISION => 1,
	XA_VERSION_ID_MAJOR => 1,
	XA_VERSION_ID_MINOR => 1,
);

main: {
	my $sdk_dir = shift @ARGV;

	if ( $sdk_dir eq '' ) {
		print "usage: gen_constant X-Audio-SDK-Directory\n";
		exit;
	}
	$sdk_dir =~ s!/$!!;

	my $control_h  = "$sdk_dir/include/control.h";
	my $player_h   = "$sdk_dir/include/player.h";
	my $xaudio_h   = "$sdk_dir/include/xaudio.h";
	
	if ( not -f $control_h ) {
		print "can't find $control_h\n";
		print "aborting!\n";
		exit;
	}
	
	if ( not -f $player_h ) {
		print "can't find $player_h\n";
		print "aborting!\n";
		exit;
	}

	if ( not -f $xaudio_h ) {
		print "can't find $xaudio_h\n";
		print "aborting!\n";
		exit;
	}
	

	gen_constant ($control_h, $player_h, $xaudio_h);
}

sub gen_constant {
	my @files = @_;
	
	# open output file
	my $out_fh = new FileHandle;
	open ($out_fh, "> constants.h") or die "can't write constants.h";

	gen_start_block ($out_fh);

	my @enums;
	my @defines;
	foreach my $file (@files) {
		print "open header file: '$file'\n";
		open (H, $file) or die "can't read $file";
		my $in_enum;
		while (<H>) {
			if ( /typedef enum/ ) {
				$in_enum = 1;
				next;
			}
			if ( $in_enum ) {
				if ( /}/ ) {
					$in_enum = 0;
					next;
				}
				/^\s*(XA_\w+)/;
				push @enums, $1 if $1;
			} else {
				/^#define\s+(XA_\w+)/;
				push @defines, $1 if $1;
			}
		}
		close H;
	}

	gen_code_block ($out_fh, \@enums, \@defines);

	gen_end_block ($out_fh);
	close $out_fh;

	print "\t", join ("\n\t",@enums,@defines),"\n";
}

sub gen_start_block {
	my ($fh) = @_;
	
	print $fh <<__EOC;
/*
 * ATTENTION: This file was genenerated by gen_constant, part
 *            of the MPEG::MP3Play distribution.
 *            Do not edit this file, edit gen_constant instead.
 *
 *            ANY CHANGES MADE HERE WILL BE LOST!
 */

static int
not_here(char *s)
{
    croak("%s not implemented on this architecture", s);
    return -1;
}

static double
constant(char *name, int arg)
{
    errno = 0;
    switch (name[strlen(name)-1]) {
__EOC
}

sub gen_end_block {
	my ($fh) = @_;
	
	print $fh <<__EOC;
	default:
		break;
    }
    errno = EINVAL;
    return 0;

not_there:
    errno = ENOENT;
    return 0;
}
__EOC
}

sub gen_code_block {
	my ($fh, $enum_lref, $define_lref) = @_;

	my %switch;
	
	foreach my $sym (@$enum_lref) {
		next if $XAUDIO_MISDEFINED_SYMBOL{$sym};
		$switch{substr($sym,-1,1)} .= 
			qq{\t\tif (strEQ(name, "$sym"))\n\t\t\treturn $sym;\n}
	}

	foreach my $sym (@$define_lref) {
		next if $sym =~ /^(XA_NOTIFY_DEBUG|XA_NOTIFY_ERROR|XA_NOTIFY_PROGRESS)/;
		next if $XAUDIO_MISDEFINED_SYMBOL{$sym};
		$switch{substr($sym,-1,1)} .= 
			qq{\t\tif (strEQ(name, "$sym"))\n}.
			qq{#ifdef $sym\n}.
			qq{\t\t\treturn $sym;\n}.
			qq{#else\n}.
			qq{\t\t\tgoto not_there;\n}.
			qq{#endif\n}
	}
	
	foreach my $char ( sort keys %switch ) {
		print $fh qq{\n\tcase '$char':\n}, $switch{$char},
			  qq{\n\t\tbreak;\n};
	}

}