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

# Read a file (from STDIN) created via (e.g.)
#   readcd2 -fulltoc dev=0,1,0 -f=audio_cd
# , produce a leader of a cddb file on STDOUT
# (processable with `fdquery --i file' from Net::FreeDB2, or cddb2cddb)

use strict;
use MP3::Tag;

my $round_offset = 0;	# Rounding down gives better match to initial size...
binmode STDIN;
my $in = do { local $/;  <STDIN> };
my $s = 2 + unpack 'n', $in;
my $s1 = length $in;
die "TOC size mismatch: header=$s, actual=$s1" unless $s == $s1;
my %chunks = unpack 'x4 (x3 C x4 a3)*', $in;

sub msf2_sector {
  my ($m,$s,$f) = unpack 'CCC', shift;
  $f + 75*($s + 60*$m)		# Apparently, already shifted by 150
}

my @tracks = sort {$a <=> $b} grep $_ <= 99, keys %chunks;
die "Gaps in tracks (@tracks)" unless @tracks == $tracks[-1] and $tracks[0] == 1;
my @sect = map msf2_sector($_), @chunks{@tracks, 0xa2};	# 0xa2 is leadout
#print "$_\n" for @sect;
#exit;

my @l = map $sect[$_] - $sect[$_ - 1], 1..$#sect;

print <<EOP;
# xmcd
#
# Track frame offsets:
EOP

for my $start (@sect[0..$#sect - 1]) {
  print "# $start\n";
}

my $length = int ($sect[-1]/75);

my $diskid = compute_discid_my (@sect);
$diskid =~ s/^0x//i;

print <<EOP;
#
# Disc length: $length seconds
#
# Revision: 5
# Submitted via: not submitted yet
DISCID=$diskid
EOP

sub cddb_sum {
  # a number like 2344 becomes 2+3+4+4 (13).
  my ($n) = @_;
  my $ret = 0;
  while ($n > 0) {
    $ret += ($n % 10);
    $n /= 10;
  }
  return $ret;
}

sub compute_discid_my {
  my @sect = @_;
  my @secs = map int($_/75), @sect;	# cdda2wav rounds down
  my $n = 0;
  $n += cddb_sum($_) for @secs[0 .. $#secs - 1];	# Skip leadout
  my $t = $secs[-1] - $secs[0];
  return sprintf '%08x', (($n % 0xFF) << 24) | ($t << 8) | (@secs - 1);
}