#!/usr/local/bin/perl

# $B<B83Cf!#%3%"$O40@.!#(Boffset.pl $B$r;29M$K$7$?!#(B

# C $B%=!<%9MQ$NG[Ns(B or $BJQ?tDj5A$+$i!"%P%$%H%*%U%;%C%H$r5a$a!"(B
# CPP $BMQ$K(B #define $B$N7A$G=PNO$9$k!#(B
# Usage: catbin.pl define-file
# define-file $B$ODL>o(B imagedata.inc $B$J$I$N7A$G!"(B C $B%=!<%9$r;H$&!#(B
# (*.c $B$@$H(B Makefile $B$N0MB84X78$,=q$-$K$/$$$N$G!"(B*.c $B$+$i(B imagedata.inc $B$r(B #include $B$9$k$J$I9)IW$9$k(B)
# #define CATBIN_BEGIN tagname
# $B$G!"G[Ns$N=89gBNL>$r=P$9!#(B
# (SIZEOF_tagname $B$r5a$a$k$?$a(B)
# $B%Q!<%9$NJ}?K$O(B
# TYPE_SPECIFIER IDENT[] = {ELEM1, ELEM2, ...};
# TYPE_SPECIFIER IDENT = VALUE; ($BL$BP1~(B)
# TYPE_SPECIFIER IDENT; ($BL$BP1~(B -> BSS $B$KF~$k$N$GF0:n$7$J$$(B!! $B8e=R$N$h$&$KBP=h$9$k(B)
# $B$J$I$N7A$,$"$k!#(B
# TYPE_SPECIFIER IDENT[] = {ELEM1, ELEM2}, IDENT2[]={ELEM3};
# $B$J$I$NJ#?tDj5A7A$K$OL$BP1~$H;W$&!#(Boffset.pl $B$_$?$$$K$9$l$P$$$1$k(B ?
# ; $B$,Mh$k$^$G$r(B $B2~9T%3!<%I$rL5;k$7$F0l$D$N%l%3!<%I$H$9$k!#(B
# $B$=$N%l%3!<%I$N:G=i$NMWAG$O(B TYPE_SPECIFIER $B$J$N$G:o=|!#(B
# (TYPE_SPECIFIER $B$O(B 1 word $B$H$O8B$i$J$$!#(B [, ; or , $B$N(B 
# $BD>A0$N<1JL;R(B $B$NA0$^$G$,(B TYPE_SPECIFIER $B$G$"$k!#(B)

# $B@hF,$O(B 16byte (128bit) alignment $B$7$F$*$/$3$H!#(B
# $BKvHx$O(B $B<+F0E*$K(B 16byte alignment $B$5$l$k!#(B
# ($B05=L!"E83+(B, DMA$B$J$I(B, C object linking $B$G$N(B alignment $B$r9MN8(B)
# $B$9$Y$F$,(B .data $B%;%/%7%g%s$KF~$i$J$1$l$P$d$P$$!#(B
# $B$=$N$?$a!"G[Ns$G$J$/$F$b!"(BC $B$+$iD>@\BeF~$H$+$7$J$$$J$i(B
# TYPE_SPECIFIER IDENT[1] = 0; $B$N$h$&$K(B (0 $B$OI,MW(B)
# $BG[Ns$G@k8@$9$k!#(B
# .bss $B$d(B .rodata $B%;%/%7%g%s$KF~$k$H=EBg$J8mF0:n$H$J$k!#(B

# $B7+$jJV$9$,!"(Bbyte offset $B$G$"$k!#==J,Cm0U$N$3$H!#(B

while (<>) {
# $BG[Ns$N=89gBNL>(B
    if (/^\#define CATBIN_BEGIN/) {
	($gomi, $gomi, $TAGNAME) = split;
    } else {
	$IN .= $_;
    }
}

# $B%3%a%s%H$r:o=|(B
$IN =~ s/\/\*.*?\*\///sg;


# $B9=B$BNDj5A$N;O$^$k$^$G$r:o=|(B ($B%M%9%HIT2D(B)
# $IN =~ s/.*?\{//gs;

# $B9=B$BNDj5A$N=*$o$j(B $B0J9_$r:o=|(B ($B%M%9%HIT2D(B)
# $IN =~ s/\}.*//gs;

# CATBIN_END $B$r:o=|(B
$IN =~ s/^CATBIN_END//m;

# $B$3$N>uBV$r%P%C%/%"%C%W(B
$IN_ORG = $IN;

# $B%W%j%W%m%;%C%5;X<(9T$r:o=|(B
$IN =~ s/^#.*//gm;

# $BG[NsE:;z$N:o=|(B
# $IN =~ s/\[.+?\]//gs;

# $B%]%$%s%?;XDj;R$N:o=|(B
# $IN =~ s/\*//gs;

# $B6u9T!"9TF,$N6uGr$r:o=|(B
$IN =~ s/^\s+//gm;

# $B9TKv$N6uGr$r:o=|(B
$IN =~ s/;\s+/;/gm;

@list = split(/;/, $IN);

$OUTNAME = "/tmp/offset-tmp$$.c";
open OUT, ">" . $OUTNAME;

print OUT <<EOF;
#include <ultra64.h>
#include <stdio.h>
#ifndef _64
#define _64
#endif
#ifndef _32
#define _32
#endif
#ifndef _16
#define _16
#endif
#ifndef STATIC
#define STATIC
#endif
unsigned long long int fORCe_1_sTRUCTURe_aLIGNMENt[1] = {0};
$IN_ORG
unsigned long long int fORCe_2_sTRUCTURe_aLIGNMENt[1] = {0};

void alignerr(char *member, int i, int ofs)
{
  fprintf(stderr, "Error: member \'%s\': must be %dbit aligned (current offset is 0x%x)\\n", member, i << 3, ofs);
  exit(-1);
}
void alignwarn(char *member, int ofs, int lastofs)
{
  fprintf(stderr, "Warning: member \'%s\' is aligned (current/last offset 0x%x/0x%x)\\n", member, ofs, lastofs);
}
int main() {
  int k;
  int i, j;
  j = 0;
  k = (int)&fORCe_1_sTRUCTURe_aLIGNMENt + sizeof(fORCe_1_sTRUCTURe_aLIGNMENt);
EOF

foreach $line (@list) {
# $B%"%i%$%s%a%s%H;XDj;R(B (_64, _32 $B$J$I(B) $B$rH4$-=P$9!#(B
    $align = $line;
    $align =~ s/^\s*\_(\d+).+/\1/;
    if ($align != 0) {
	if ($align & 0x07) {
	    print STDERR "$line\n", "Error: alignment specifier ($align) is invalid\n";
	    die;
	}
	$align >>= 3;
    }
# $B<1JL;R$N$_H4$-=P$9!#(B
# $B$^$:!"7?;XDj;R$r:o=|(B
    $line =~ s/(\w|\s)+\s+(\w+\s*[;,]{0,1}.*)/\2/;
# $BG[NsE:;z$N:o=|(B
    $line =~ s/\[.*?\]//gs;
# $B%]%$%s%?;XDj;R$N:o=|(B
    $line =~ s/\*//gs;
# $B=i4|2=;R$N:o=|(B
    $line =~ s/\=//gs;
# $B=i4|CMDj5A$N:o=|(B
    $line =~ s/\{.*?\}//gs;

    $word = $line;
    print OUT "  i = (int)&$word - k;";
    if ($align > 1) {
	print OUT "  if (i & ", $align - 1, ") alignerr(\"$word\", $align, i);\n";
    }
    print OUT "  if (i != j) alignwarn(\"$word\", i, j);\n";
    print OUT '  printf("#define OFFSET_', uc($word), ' 0x%x\n", i);', "\n";
    print OUT "  j = i + sizeof($word);\n";
}
# $B$3$3$^$G$KEP>l$7$?G[Ns$NBg$-$5$NAm7W(B: RSP DMA $B$K$bBP1~$G$-$k$h$&$K!#(B
# $B$7$+$b!"(BC object $B$H$7$F%3%s%Q%$%k$5$l$k$?$a(B 16byte alignment $B$7$h$&!#(B
print OUT '  printf("#define SIZEOF_', uc($TAGNAME), ' 0x%x\n", ',
"(j + 15) & 0xfffffff0);\n";

print OUT '  if (j & 0x0f)
    fprintf(stderr, "Warning: sizeof(the structure)=0x%x, not 16byte-aligned\\n",
            j);
';

# #define $B$N$_(B $BCj=P(B
@define_list = split /\n/, $IN_ORG;

print OUT '  printf("#ifndef __CATBIN_PL__\n");', "\n";
foreach (@define_list) {
    chomp;
    if (/^\#define/) {
	print OUT '  printf("', $_, '\n");', "\n";
    }
}
print OUT '  printf("#endif\n");', "\n";
print OUT '  return 0;
}
';

close OUT;

$i = system("cc -o /tmp/offset-tmp$$ $OUTNAME");
if ($i) { die; }

$i = system("/tmp/offset-tmp$$");
unlink("/tmp/offset-tmp$$");
unlink($OUTNAME);
if ($i) { die; }
