1#!/usr/bin/perl -w
2# Copyright 2002 by Heiko Ei�feldt (Eissfeldt)
3use strict;
4use integer;
5
6# read all .inf files and generate the binary cdtext block
7# for cdrecord.
8
9my @results;
10
11sub fill_packet
12{
13	my $ID = shift;
14	my $track = shift;
15	my $seq_nr = shift;
16	my $charpos = shift;
17	my $text = shift;
18	my $todo = shift;
19
20	return if (!defined($$text));
21
22	my @packet = ();
23	push @packet, chr($ID);	# track title, performer, ...
24	push @packet, chr($$track);
25	push @packet, chr($$seq_nr);
26	$$charpos = 15 if ($$charpos > 15);
27	push @packet, chr($$charpos);
28
29	my $cp = 0;
30	my $tracks_inp = 0;
31	while (length($$text) + 1 < 12 - $cp) {
32		push @packet, split(//, $$text);
33		push @packet, chr(0);
34		$cp += length($$text) + 1;
35		$$charpos = 0;
36		$tracks_inp++;
37
38		$$text = shift @$todo;
39		if ($#$todo < 1 && (!defined($$text) || $$text eq "") ) {
40			push @packet, (chr(0)) x (12 - $cp);
41			$$seq_nr++;
42			print_packet(@packet);
43			return;
44		}
45		$$text = "" if (!defined($$text));
46		$$track++;
47	}
48
49	# packet gets full
50	my $left = 12 - $cp;
51	if ($left > length($$text)) {
52		# title fits into packet
53		push @packet, split(//, $$text);
54		push @packet, chr(0);
55		$tracks_inp++;
56		print_packet(@packet);
57
58		$$charpos = 0;
59		$$text = shift @$todo;
60		unless ((!defined($$text) || $$text eq "") && $#$todo < 1) { $$track++; }
61		$$seq_nr++;
62	} else {
63		# print current packet and more if more entries are present
64		push @packet, split(//, substr($$text, 0, $left));
65		print_packet(@packet);
66
67		$$text = substr($$text, $left);
68		$$charpos += $left;
69		$$seq_nr++;
70	}
71}
72
73my @crctab =(
74    0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
75    0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
76    0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
77    0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
78    0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
79    0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
80    0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
81    0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
82    0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
83    0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
84    0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
85    0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
86    0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
87    0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
88    0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
89    0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
90    0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
91    0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
92    0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
93    0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
94    0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
95    0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
96    0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
97    0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
98    0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
99    0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
100    0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
101    0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
102    0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
103    0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
104    0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
105    0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0,
106);
107
108sub add_crc
109{
110	# crc with polynomial: x^16 + x^12 + x^5 + 1
111	# 1,0001,0000,0010,0001
112	my $packref = shift;
113	my $crc = 0;
114
115	foreach (@$packref) {
116		$crc = ($crc << 8) ^ $crctab[
117					( ($crc >> (16-8)) ^ ord($_) ) & 0xff
118					];
119		$crc &= 0xffff;
120	}
121	$$packref[16] = chr((($crc >> 8) & 0xff) ^ 0xff);
122	$$packref[17] = chr(($crc & 0xff) ^ 0xff);
123}
124
125sub print_packet
126{
127	return if ($#_ < 1);
128	my @packet = (@_);
129	add_crc(\@packet);
130	if ($packet[0] ne chr(0x8f)) {
131		printf STDERR ("%02x "x4 ." "."%c "x12 ." "."%02x "x2), map( defined($_) ? ord($_) : "___undef", @packet );
132	} else {
133		printf STDERR ("%02x "x4 ." "."%02x "x12 ." "."%02x "x2), map( defined($_) ? ord($_) : "___undef", @packet );
134	}
135	printf STDERR "\n";
136	push @results, @packet;
137}
138
139my $defaultperformer = $ARGV[0] || die "usage: ", $^X, " defaultperformer_name\n";
140my $prefix = $ARGV[1] || "audio";
141@ARGV = glob("${prefix}_??.inf");
142my @albumtitles;
143my @tracktitles;
144my @performers;
145my $ISRC;
146my @ISRCs;
147my $MCN;
148
149my $performer;
150while (<>) {
151	if (/^Performer=\s+'(.*?)'$/) {
152		$performer = $1;
153	}
154	if (/^Albumtitle=\s+'(.*?)'$/) {
155		push @albumtitles, $1;
156	}
157	if (/^Tracktitle=\s+'(.*?)'$/) {
158		push @tracktitles, $1;
159	}
160	if (/^ISRC=\s+(\S+?)$/) {
161		$ISRC = $1;
162	}
163	if (/^MCN=\s+(\S+?)$/) {
164		$MCN = $1;
165	}
166	if (eof) {
167		close ARGV;
168		$performer = $defaultperformer if (!defined($performer));
169		push @performers, $performer;
170		$performer = undef;
171		push @ISRCs, $ISRC;
172		$ISRC = undef;
173	}
174}
175
176#
177my $seq_nr = 0;
178
179my @todo;
180my $text;
181my $track;
182my $charpos;
183
184# build cdtext packets
185
186# build track titles
187@todo = ($albumtitles[0], @tracktitles);
188$text = shift @todo;
189$track = 0;
190$charpos = 0;
191
192while ($#todo > 1 || defined($text)) {
193	fill_packet(0x80, \$track, \$seq_nr, \$charpos, \$text, \@todo);
194}
195
196my $trackpacks = $seq_nr;	# store for later reference
197my $last_track = $track;
198
199# build performer entries
200#@todo = ($performer) x (1 + scalar(@tracktitles));
201@todo = ($defaultperformer, @performers);
202$text = shift @todo;
203$track = 0;
204$charpos = 0;
205
206while ($#todo > 1 || defined($text)) {
207	fill_packet(0x81, \$track, \$seq_nr, \$charpos, \$text, \@todo);
208}
209
210my $perfpacks = $seq_nr - $trackpacks;	# store for later reference
211
212# build ISRC entries
213@todo = ($MCN, @ISRCs);
214$text = shift @todo;
215$track = 0;
216$charpos = 0;
217
218while ($#todo > 1 || defined($text)) {
219	$text = "" if (!defined($text));
220	fill_packet(0x8e, \$track, \$seq_nr, \$charpos, \$text, \@todo);
221}
222
223my $isrcpacks = $seq_nr - $trackpacks - $perfpacks;	# store for later reference
224
225# build size information blocks
226my $size1 = chr(0) . chr(1) . chr($last_track) . chr(0)
227	 . chr($trackpacks) . chr($perfpacks) . chr(0) x 5;
228my $size2 = chr(0) x 6 . chr($isrcpacks) . chr(3) . chr($seq_nr+2) . chr(0) x 2;
229my $size3 = chr(0) x 4 . chr(9);	# hard coded language 09 = english
230@todo = ($size1, $size2, $size3);
231$text = shift @todo;
232$track = 0;
233$charpos = 0;
234
235while ((defined($text) && $text ne "") ) {
236	fill_packet(0x8f, \$track, \$seq_nr, \$charpos, \$text, \@todo);
237}
238
239# write out the results
240my $size = @results + 2;
241print chr($size >> 8), chr($size & 0xff), chr(0), chr(0), @results;
242