1714 lines
40 KiB
C
1714 lines
40 KiB
C
/* @(#)cue.c 1.57 13/12/21 Copyright 2001-2013 J. Schilling */
|
||
#include <schily/mconfig.h>
|
||
#ifndef lint
|
||
static UConst char sccsid[] =
|
||
"@(#)cue.c 1.57 13/12/21 Copyright 2001-2013 J. Schilling";
|
||
#endif
|
||
/*
|
||
* Cue sheet parser
|
||
*
|
||
* Copyright (c) 2001-2013 J. Schilling
|
||
*/
|
||
/*
|
||
* The contents of this file are subject to the terms of the
|
||
* Common Development and Distribution License, Version 1.0 only
|
||
* (the "License"). You may not use this file except in compliance
|
||
* with the License.
|
||
*
|
||
* See the file CDDL.Schily.txt in this distribution for details.
|
||
* A copy of the CDDL is also available via the Internet at
|
||
* http://www.opensource.org/licenses/cddl1.txt
|
||
*
|
||
* When distributing Covered Code, include this CDDL HEADER in each
|
||
* file and include the License file CDDL.Schily.txt from this distribution.
|
||
*/
|
||
|
||
#include <schily/mconfig.h>
|
||
#include <schily/stdio.h>
|
||
#include <schily/stdlib.h>
|
||
#include <schily/unistd.h>
|
||
#include <schily/standard.h>
|
||
#include <schily/fcntl.h>
|
||
#include <schily/stat.h>
|
||
#include <schily/varargs.h>
|
||
#include <schily/schily.h>
|
||
#include <schily/nlsdefs.h>
|
||
#include <schily/string.h>
|
||
#include <schily/utypes.h>
|
||
#include <schily/ctype.h>
|
||
#include <schily/errno.h>
|
||
|
||
#include "xio.h"
|
||
#include "cdtext.h"
|
||
#include "cdrecord.h"
|
||
#include "auheader.h"
|
||
#include "schily/libport.h"
|
||
|
||
/*#define PARSE_DEBUG*/
|
||
|
||
typedef struct state {
|
||
char *filename; /* Name of file to open */
|
||
void *xfp; /* Open file */
|
||
Llong trackoff; /* Current offset in open file */
|
||
Llong filesize; /* Size of current open file */
|
||
int filetype; /* File type (e.g. K_WAVE) */
|
||
int tracktype; /* Track type (e.g. TOC_DA) */
|
||
int sectype; /* Sector type (e.g. SECT_AUDIO)*/
|
||
int dbtype; /* Data block type (e.g. DB_RAW)*/
|
||
int secsize; /* Sector size from TRACK type */
|
||
int dataoff; /* Data offset from Track type */
|
||
int state; /* Current state of the parser */
|
||
int prevstate; /* Previous state of the parser */
|
||
int track; /* Relative Track index */
|
||
int trackno; /* Absolute Track number on disk*/
|
||
int index; /* Last INDEX xx number parsed */
|
||
long index0; /* Current INDEX 00 if found */
|
||
long index1; /* Current INDEX 01 if found */
|
||
long secoff; /* Last INDEX 01 value in file */
|
||
long pregapsize; /* Pregap size from PREGAP */
|
||
long postgapsize; /* Postgap size from POSTGAP */
|
||
int flags; /* Track flags (e.g. TI_COPY) */
|
||
int pflags; /* Parser flags */
|
||
} state_t;
|
||
|
||
/*
|
||
* Values for "state" and "prevstate".
|
||
*/
|
||
#define STATE_NONE 0 /* Initial state of parser */
|
||
#define STATE_POSTGAP 1 /* Past INDEX before FILE/TRACK */
|
||
#define STATE_FILE 2 /* FILE keyword found */
|
||
#define STATE_TRACK 3 /* TRACK keyword found */
|
||
#define STATE_FLAGS 4 /* FLAGS past TRACK before INDEX*/
|
||
#define STATE_INDEX0 5 /* INDEX 00 found */
|
||
#define STATE_INDEX1 6 /* INDEX 01 found */
|
||
#define STATE_MAX 6 /* # of entries in states[] */
|
||
|
||
/*
|
||
* Flag bits used in "pflags".
|
||
*/
|
||
#define PF_CDRTOOLS_EXT 0x01 /* Cdrtools extensions allowed */
|
||
#define PF_INDEX0_PREV 0x02 /* INDEX 00 belongs to prev FILE*/
|
||
#define PF_FILE_FOUND 0x04 /* FILE command found for TRACK */
|
||
|
||
LOCAL char *states[] = {
|
||
"NONE",
|
||
"POSTGAP",
|
||
"FILE",
|
||
"TRACK",
|
||
"FLAGS",
|
||
"INDEX 00",
|
||
"INDEX 01"
|
||
};
|
||
|
||
typedef struct keyw {
|
||
char *k_name;
|
||
int k_type;
|
||
} keyw_t;
|
||
|
||
/*
|
||
* Keywords (first word on line):
|
||
* CATALOG - global CATALOG <MCN>
|
||
* CDTEXTFILE - global CDTEXTFILE <fname>
|
||
* FILE - track local FILE <fame> <type>
|
||
* FLAGS - track local FLAGS <flag> ...
|
||
* INDEX - track local INDEX <#> <mm:ss:ff>
|
||
* ISRC - track local ISRC <ISRC>
|
||
* PERFORMER - global/local PERFORMER <string>
|
||
* POSTGAP - track locak POSTGAP <mm:ss:ff>
|
||
* PREGAP - track local PREGAP <mm:ss:ff>
|
||
* REM - anywhere REM <comment>
|
||
* SONGWRITER - global/local SONGWRITER <string>
|
||
* TITLE - global/local TITLE <string>
|
||
* TRACK - track local TRACK <#> <datatype>
|
||
*
|
||
* Order of keywords:
|
||
* CATALOG
|
||
* CDTEXTFILE
|
||
* PERFORMER | SONGWRITER | TITLE Doc says past FILE...
|
||
* FILE Must be past CATALOG
|
||
* ------- Repeat the following: mehrere FILE Commands?
|
||
* TRACK
|
||
* FLAGS | ISRC | PERFORMER | PREGAP | SONGWRITER | TITLE
|
||
* INDEX
|
||
* POSTGAP
|
||
*
|
||
* Additional keyword rules:
|
||
* CATALOG once
|
||
* CDTEXTFILE once
|
||
* FILE before "other command"
|
||
* FLAGS one per TRACK, after TRACK before INDEX
|
||
* INDEX >= 0, <= 99, first 0 or 1, sequential,
|
||
* first index of a FILE at 00:00:00
|
||
* ISRC after TRACK before INDEX
|
||
* PERFORMER
|
||
* POSTGAP one per TRACK, after all INDEX for current TRACK
|
||
* PREGAP one per TRACK, after TRACK before INDEX
|
||
* REM
|
||
* SONGWRITER
|
||
* TITLE
|
||
* TRACK >= 1, <= 99, sequential, >= 1 TRACK per FILE
|
||
*/
|
||
|
||
#define K_G 0x10000 /* Global */
|
||
#define K_T 0x20000 /* Track local */
|
||
#define K_A (K_T | K_G) /* Global & Track local */
|
||
|
||
#define K_ARRANGER (0 | K_A) /* CD-Text Arranger */
|
||
#define K_MCN (1 | K_G) /* Media catalog number */
|
||
#define K_TEXTFILE (2 | K_G) /* CD-Text binary file */
|
||
#define K_COMPOSER (3 | K_A) /* CD-Text Composer */
|
||
#define K_FILE (4 | K_T) /* Input data file */
|
||
#define K_FLAGS (5 | K_T) /* Flags for ctrl nibble */
|
||
#define K_INDEX (6 | K_T) /* Index marker for track */
|
||
#define K_ISRC (7 | K_T) /* ISRC string for track */
|
||
#define K_MESSAGE (8 | K_A) /* CD-Text Message */
|
||
#define K_PERFORMER (9 | K_A) /* CD-Text Performer */
|
||
#define K_POSTGAP (10 | K_T) /* Post gap for track (autogen) */
|
||
#define K_PREGAP (11 | K_T) /* Pre gap for track (autogen) */
|
||
#define K_REM (12 | K_A) /* Remark (Comment) */
|
||
#define K_SONGWRITER (13| K_A) /* CD-Text Songwriter */
|
||
#define K_TITLE (14| K_A) /* CD-Text Title */
|
||
#define K_TRACK (15| K_T) /* Track marker */
|
||
|
||
|
||
LOCAL keyw_t keywords[] = {
|
||
{ "ARRANGER", K_ARRANGER }, /* Not supported by CDR-WIN */
|
||
{ "CATALOG", K_MCN },
|
||
{ "CDTEXTFILE", K_TEXTFILE },
|
||
{ "COMPOSER", K_COMPOSER }, /* Not supported by CDR-WIN */
|
||
{ "FILE", K_FILE },
|
||
{ "FLAGS", K_FLAGS },
|
||
{ "INDEX", K_INDEX },
|
||
{ "ISRC", K_ISRC },
|
||
{ "MESSAGE", K_MESSAGE }, /* Not supported by CDR-WIN */
|
||
{ "PERFORMER", K_PERFORMER },
|
||
{ "POSTGAP", K_POSTGAP },
|
||
{ "PREGAP", K_PREGAP },
|
||
{ "REM", K_REM },
|
||
{ "SONGWRITER", K_SONGWRITER },
|
||
{ "TITLE", K_TITLE },
|
||
{ "TRACK", K_TRACK },
|
||
{ NULL, 0 },
|
||
};
|
||
|
||
|
||
/*
|
||
* Filetypes - argument to FILE Keyword (one only):
|
||
*
|
||
* BINARY - Intel binary file (least significant byte first)
|
||
* MOTOTOLA - Motorola binary file (most significant byte first)
|
||
* AIFF - Audio AIFF file
|
||
* WAVE - Audio WAVE file
|
||
* MP3 - Audio MP3 file
|
||
* AU - Sun Audio file
|
||
* OGG - Audio OGG file
|
||
*/
|
||
#define K_BINARY 100
|
||
#define K_MOTOROLA 101
|
||
#define K_AIFF 102
|
||
#define K_WAVE 103
|
||
#define K_MP3 104
|
||
#define K_FT_CDRWIN_MAX 104
|
||
#define K_AU 105
|
||
#define K_OGG 106
|
||
|
||
LOCAL keyw_t filetypes[] = {
|
||
{ "BINARY", K_BINARY },
|
||
{ "MOTOROLA", K_MOTOROLA },
|
||
{ "AIFF", K_AIFF },
|
||
{ "WAVE", K_WAVE },
|
||
{ "MP3", K_MP3 },
|
||
{ "AU", K_AU }, /* Not supported by CDR-WIN */
|
||
{ "OGG", K_OGG }, /* Not supported by CDR-WIN */
|
||
{ NULL, 0 },
|
||
};
|
||
|
||
/*
|
||
* Flags - argument to FLAGS Keyword (more than one allowed):
|
||
* DCP - Digital copy permitted
|
||
* 4CH - Four channel audio
|
||
* PRE - Pre-emphasis enabled (audio tracks only)
|
||
* SCMS - Serial copy management system (not supported by all recorders)
|
||
*/
|
||
#define K_DCP 1000
|
||
#define K_4CH 1001
|
||
#define K_PRE 1002
|
||
#define K_SCMS 1003
|
||
#define K_FL_CDRWIN_MAX 1003
|
||
|
||
LOCAL keyw_t flags[] = {
|
||
{ "DCP", K_DCP },
|
||
{ "4CH", K_4CH },
|
||
{ "PRE", K_PRE },
|
||
{ "SCMS", K_SCMS },
|
||
{ NULL, 0 },
|
||
};
|
||
|
||
/*
|
||
* Datatypes - argument to TRACK Keyword (one only):
|
||
* AUDIO - Audio/Music (2352)
|
||
* CDG - Karaoke CD+G (2448)
|
||
* MODE1/2048 - CDROM Mode1 Data (cooked)
|
||
* MODE1/2352 - CDROM Mode1 Data (raw)
|
||
* MODE2/2336 - CDROM-XA Mode2 Data
|
||
* MODE2/2352 - CDROM-XA Mode2 Data
|
||
* CDI/2336 - CDI Mode2 Data
|
||
* CDI/2352 - CDI Mode2 Data
|
||
*/
|
||
#define K_AUDIO 10000
|
||
#define K_CDG 10001
|
||
#define K_MODE1 10002
|
||
#define K_MODE2 10003
|
||
#define K_CDI 10004
|
||
#define K_DT_CDRWIN_MAX 10004
|
||
|
||
LOCAL keyw_t dtypes[] = {
|
||
{ "AUDIO", K_AUDIO },
|
||
{ "CDG", K_CDG },
|
||
{ "MODE1", K_MODE1 },
|
||
{ "MODE2", K_MODE2 },
|
||
{ "CDI", K_CDI },
|
||
{ NULL, 0 },
|
||
};
|
||
|
||
|
||
#ifdef CUE_MAIN
|
||
EXPORT int main __PR((int ac, char **av));
|
||
#endif
|
||
EXPORT int parsecue __PR((char *cuefname, track_t trackp[]));
|
||
EXPORT void fparsecue __PR((FILE *f, track_t trackp[]));
|
||
LOCAL void parse_arranger __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_mcn __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_textfile __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_composer __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_file __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_flags __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_index __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_isrc __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_message __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_performer __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_postgap __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_pregap __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_rem __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_songwriter __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_title __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_track __PR((track_t trackp[], state_t *sp));
|
||
LOCAL void parse_offset __PR((long *lp));
|
||
LOCAL void newtrack __PR((track_t trackp[], state_t *sp));
|
||
|
||
LOCAL keyw_t *lookup __PR((char *word, keyw_t table[]));
|
||
LOCAL char *state_name __PR((int st));
|
||
#ifdef DEBUG
|
||
LOCAL void wdebug __PR((void));
|
||
#endif
|
||
LOCAL FILE *cueopen __PR((char *name));
|
||
LOCAL char *cuename __PR((void));
|
||
LOCAL char *nextline __PR((FILE *f));
|
||
#ifdef __needed__
|
||
LOCAL void ungetline __PR((void));
|
||
#endif
|
||
LOCAL char *skipwhite __PR((const char *s));
|
||
LOCAL char *peekword __PR((void));
|
||
LOCAL char *lineend __PR((void));
|
||
LOCAL char *markword __PR((char *delim));
|
||
LOCAL char getworddelim __PR((void));
|
||
LOCAL char *getnextitem __PR((char *delim));
|
||
LOCAL char *neednextitem __PR((char *delim, char *type));
|
||
#ifdef __needed__
|
||
LOCAL char *nextword __PR((void));
|
||
#endif
|
||
LOCAL char *needword __PR((char *type));
|
||
LOCAL char *curword __PR((void));
|
||
LOCAL char *nextitem __PR((void));
|
||
LOCAL char *needitem __PR((char *type));
|
||
LOCAL void checkextra __PR((void));
|
||
LOCAL void statewarn __PR((state_t *sp, const char *fmt, ...));
|
||
#ifdef __needed__
|
||
LOCAL void cuewarn __PR((const char *fmt, ...));
|
||
#endif
|
||
LOCAL void cueabort __PR((const char *fmt, ...));
|
||
LOCAL void extabort __PR((const char *fmt, ...));
|
||
|
||
#ifdef CUE_MAIN
|
||
int debug;
|
||
int xdebug = 1;
|
||
|
||
int write_secs __PR((void));
|
||
int write_secs() { return (-1); }
|
||
|
||
EXPORT int
|
||
main(ac, av)
|
||
int ac;
|
||
char *av[];
|
||
{
|
||
int i;
|
||
track_t track[MAX_TRACK+2]; /* Max tracks + track 0 + track AA */
|
||
|
||
save_args(ac, av);
|
||
|
||
fillbytes(track, sizeof (track), '\0');
|
||
for (i = 0; i < MAX_TRACK+2; i++)
|
||
track[i].track = track[i].trackno = i;
|
||
track[0].tracktype = TOC_MASK;
|
||
|
||
|
||
parsecue(av[1], track);
|
||
return (0);
|
||
}
|
||
#else
|
||
extern int xdebug;
|
||
#endif
|
||
|
||
EXPORT int
|
||
parsecue(cuefname, trackp)
|
||
char *cuefname;
|
||
track_t trackp[];
|
||
{
|
||
FILE *f = cueopen(cuefname);
|
||
|
||
fparsecue(f, trackp);
|
||
return (0);
|
||
}
|
||
|
||
EXPORT void
|
||
fparsecue(f, trackp)
|
||
FILE *f;
|
||
track_t trackp[];
|
||
{
|
||
char *word;
|
||
struct keyw *kp;
|
||
BOOL isglobal = TRUE;
|
||
state_t state;
|
||
|
||
state.filename = NULL;
|
||
state.xfp = NULL;
|
||
state.trackoff = 0;
|
||
state.filesize = 0;
|
||
state.filetype = 0;
|
||
state.tracktype = 0;
|
||
state.sectype = 0;
|
||
state.dbtype = 0;
|
||
state.secsize = 0;
|
||
state.dataoff = 0;
|
||
state.state = STATE_NONE;
|
||
state.prevstate = STATE_NONE;
|
||
state.track = 0;
|
||
state.trackno = 0;
|
||
state.index = -1;
|
||
state.index0 = -1;
|
||
state.index1 = -1;
|
||
state.secoff = 0;
|
||
state.pregapsize = -1;
|
||
state.postgapsize = -1;
|
||
state.flags = 0;
|
||
state.pflags = 0;
|
||
|
||
if (xdebug > 1)
|
||
printf(_("---> Entering CUE Parser...\n"));
|
||
do {
|
||
if (nextline(f) == NULL) {
|
||
/*
|
||
* EOF on CUE File
|
||
* Do post processing here
|
||
*/
|
||
if (state.state < STATE_INDEX1 && state.state != STATE_POSTGAP) {
|
||
statewarn(&state, _("INDEX 01 missing"));
|
||
cueabort(_("Incomplete CUE file"));
|
||
}
|
||
if (state.xfp) {
|
||
xclose(state.xfp);
|
||
state.xfp = NULL;
|
||
}
|
||
if (xdebug > 1) {
|
||
printf(_("---> CUE Parser got EOF, found %2.2d tracks.\n"),
|
||
state.track);
|
||
}
|
||
return;
|
||
}
|
||
word = nextitem();
|
||
if (*word == '\0') /* empty line */
|
||
continue;
|
||
|
||
if (xdebug > 1)
|
||
printf(_("\nKEY: '%s' %s\n"), word, peekword());
|
||
kp = lookup(word, keywords);
|
||
if (kp == NULL)
|
||
cueabort(_("Unknown CUE keyword '%s'"), word);
|
||
|
||
if ((kp->k_type & K_G) == 0) {
|
||
if (isglobal)
|
||
isglobal = FALSE;
|
||
}
|
||
if ((kp->k_type & K_T) == 0) {
|
||
if (!isglobal) {
|
||
statewarn(&state,
|
||
_("%s keyword must be before first TRACK"),
|
||
word);
|
||
cueabort(_("Badly placed CUE keyword '%s'"), word);
|
||
}
|
||
}
|
||
#ifdef DEBUG
|
||
printf("%s-", isglobal ? "G" : "T");
|
||
wdebug();
|
||
#endif
|
||
|
||
switch (kp->k_type) {
|
||
|
||
case K_ARRANGER: parse_arranger(trackp, &state); break;
|
||
case K_MCN: parse_mcn(trackp, &state); break;
|
||
case K_TEXTFILE: parse_textfile(trackp, &state); break;
|
||
case K_COMPOSER: parse_composer(trackp, &state); break;
|
||
case K_FILE: parse_file(trackp, &state); break;
|
||
case K_FLAGS: parse_flags(trackp, &state); break;
|
||
case K_INDEX: parse_index(trackp, &state); break;
|
||
case K_ISRC: parse_isrc(trackp, &state); break;
|
||
case K_MESSAGE: parse_message(trackp, &state); break;
|
||
case K_PERFORMER: parse_performer(trackp, &state); break;
|
||
case K_POSTGAP: parse_postgap(trackp, &state); break;
|
||
case K_PREGAP: parse_pregap(trackp, &state); break;
|
||
case K_REM: parse_rem(trackp, &state); break;
|
||
case K_SONGWRITER: parse_songwriter(trackp, &state); break;
|
||
case K_TITLE: parse_title(trackp, &state); break;
|
||
case K_TRACK: parse_track(trackp, &state); break;
|
||
|
||
default:
|
||
cueabort(_("Panic: unknown CUE command '%s'"), word);
|
||
}
|
||
} while (1);
|
||
}
|
||
|
||
LOCAL void
|
||
parse_arranger(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0)
|
||
extabort("ARRANGER");
|
||
|
||
if (sp->track > 0 && sp->state > STATE_INDEX0) {
|
||
statewarn(sp, _("ARRANGER keyword cannot be after INDEX keyword"));
|
||
cueabort(_("Badly placed ARRANGER keyword"));
|
||
}
|
||
|
||
word = needitem("arranger");
|
||
txp = gettextptr(sp->track, trackp);
|
||
txp->tc_arranger = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_mcn(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
|
||
if (sp->track != 0)
|
||
cueabort(_("CATALOG keyword must be before first TRACK"));
|
||
|
||
word = needitem("MCN");
|
||
setmcn(word, &trackp[0]);
|
||
txp = gettextptr(0, trackp); /* MCN is isrc for trk 0 */
|
||
txp->tc_isrc = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_textfile(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
|
||
if (sp->track != 0)
|
||
cueabort(_("CDTEXTFILE keyword must be before first TRACK"));
|
||
|
||
word = needitem("cdtextfile");
|
||
|
||
if (trackp[MAX_TRACK+1].flags & TI_TEXT) {
|
||
if (!checktextfile(word)) {
|
||
comerrno(EX_BAD,
|
||
_("Cannot use '%s' as CD-Text file.\n"),
|
||
word);
|
||
}
|
||
trackp[0].flags |= TI_TEXT;
|
||
} else {
|
||
errmsgno(EX_BAD, _("Ignoring CDTEXTFILE '%s'.\n"), word);
|
||
errmsgno(EX_BAD, _("If you like to write CD-Text, call cdrecord -text.\n"));
|
||
}
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_composer(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0)
|
||
extabort("COMPOSER");
|
||
|
||
if (sp->track > 0 && sp->state > STATE_INDEX0) {
|
||
statewarn(sp, _("COMPOSER keyword cannot be after INDEX keyword"));
|
||
cueabort(_("Badly placed COMPOSER keyword"));
|
||
}
|
||
|
||
word = needitem("composer");
|
||
txp = gettextptr(sp->track, trackp);
|
||
txp->tc_composer = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_file(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char cname[1024];
|
||
char newname[1024];
|
||
struct keyw *kp;
|
||
char *word;
|
||
char *filetype;
|
||
struct stat st;
|
||
#ifdef hint
|
||
Llong lsize;
|
||
#endif
|
||
|
||
if ((sp->state <= STATE_TRACK && sp->state > STATE_POSTGAP) ||
|
||
(sp->state >= STATE_TRACK && sp->state < STATE_INDEX1)) {
|
||
if (sp->state >= STATE_INDEX0 && sp->state < STATE_INDEX1) {
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0)
|
||
extabort(_("FILE keyword after INDEX 00 and before INDEX 01"));
|
||
sp->prevstate = sp->state;
|
||
goto file_ok;
|
||
}
|
||
if (sp->state <= STATE_TRACK && sp->state > STATE_POSTGAP)
|
||
statewarn(sp, _("FILE keyword only allowed once before TRACK keyword"));
|
||
if (sp->state >= STATE_TRACK && sp->state < STATE_INDEX1)
|
||
statewarn(sp, _("FILE keyword not allowed after TRACK and before INDEX 01"));
|
||
cueabort(_("Badly placed FILE keyword"));
|
||
}
|
||
file_ok:
|
||
if (sp->state < STATE_INDEX1 && sp->pflags & PF_FILE_FOUND)
|
||
cueabort(_("Only one FILE keyword allowed per TRACK"));
|
||
|
||
sp->pflags |= PF_FILE_FOUND;
|
||
sp->state = STATE_FILE;
|
||
|
||
word = needitem("filename");
|
||
if (sp->xfp) {
|
||
xclose(sp->xfp);
|
||
sp->xfp = NULL;
|
||
}
|
||
sp->xfp = xopen(word, O_RDONLY|O_BINARY, 0, X_NOREWIND);
|
||
if (sp->xfp == NULL && geterrno() == ENOENT) {
|
||
char *p;
|
||
|
||
if (strchr(word, '/') == 0 &&
|
||
strchr(cuename(), '/') != 0) {
|
||
js_snprintf(cname, sizeof (cname),
|
||
"%s", cuename());
|
||
p = strrchr(cname, '/');
|
||
if (p)
|
||
*p = '\0';
|
||
js_snprintf(newname, sizeof (newname),
|
||
"%s/%s", cname, word);
|
||
word = newname;
|
||
sp->xfp = xopen(word, O_RDONLY|O_BINARY, 0, X_NOREWIND);
|
||
}
|
||
}
|
||
if (sp->xfp == NULL) {
|
||
#ifdef PARSE_DEBUG
|
||
errmsg(_("Cannot open FILE '%s'.\n"), word);
|
||
#else
|
||
comerr(_("Cannot open FILE '%s'.\n"), word);
|
||
#endif
|
||
}
|
||
|
||
sp->filename = strdup(word);
|
||
sp->trackoff = 0;
|
||
sp->secoff = 0;
|
||
sp->filesize = 0;
|
||
sp->flags &= ~TI_SWAB; /* Reset what we might set for FILE */
|
||
|
||
filetype = needitem("filetype");
|
||
kp = lookup(filetype, filetypes);
|
||
if (kp == NULL)
|
||
cueabort(_("Unknown filetype '%s'"), filetype);
|
||
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0 &&
|
||
kp->k_type > K_FT_CDRWIN_MAX)
|
||
extabort(_("Filetype '%s'"), kp->k_name);
|
||
|
||
switch (kp->k_type) {
|
||
|
||
case K_BINARY:
|
||
case K_MOTOROLA:
|
||
if (fstat(xfileno(sp->xfp), &st) >= 0 &&
|
||
S_ISREG(st.st_mode)) {
|
||
sp->filesize = st.st_size;
|
||
if (kp->k_type == K_BINARY)
|
||
sp->flags |= TI_SWAB;
|
||
} else {
|
||
cueabort(_("Unknown file size for FILE '%s'"),
|
||
sp->filename);
|
||
}
|
||
break;
|
||
case K_AIFF:
|
||
cueabort(_("Unsupported filetype '%s'"), kp->k_name);
|
||
break;
|
||
case K_AU:
|
||
sp->filesize = ausize(xfileno(sp->xfp));
|
||
break;
|
||
case K_WAVE:
|
||
#ifdef PARSE_DEBUG
|
||
sp->filesize = 1000000000;
|
||
#else
|
||
sp->filesize = wavsize(xfileno(sp->xfp));
|
||
#endif
|
||
sp->flags |= TI_SWAB;
|
||
break;
|
||
case K_MP3:
|
||
case K_OGG:
|
||
cueabort(_("Unsupported filetype '%s'"), kp->k_name);
|
||
break;
|
||
|
||
default: cueabort(_("Panic: unknown filetype '%s'"), filetype);
|
||
}
|
||
|
||
if (sp->filesize == AU_BAD_CODING) {
|
||
cueabort(_("Inappropriate audio coding in '%s'"),
|
||
sp->filename);
|
||
}
|
||
if (xdebug > 0)
|
||
printf(_("Track[%2.2d] %2.2d File '%s' Filesize %lld\n"),
|
||
sp->track, sp->trackno, sp->filename, sp->filesize);
|
||
|
||
sp->filetype = kp->k_type;
|
||
|
||
checkextra();
|
||
|
||
|
||
#ifdef hint
|
||
trackp->itracksize = lsize;
|
||
if (trackp->itracksize != lsize)
|
||
comerrno(EX_BAD, _("This OS cannot handle large audio images.\n"));
|
||
#endif
|
||
}
|
||
|
||
LOCAL void
|
||
parse_flags(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
struct keyw *kp;
|
||
char *word;
|
||
|
||
if ((sp->state < STATE_TRACK) ||
|
||
(sp->state >= STATE_INDEX0)) {
|
||
statewarn(sp, _("FLAGS keyword must be after TRACK and before INDEX keyword"));
|
||
cueabort(_("Badly placed FLAGS keyword"));
|
||
}
|
||
sp->state = STATE_FLAGS;
|
||
|
||
do {
|
||
word = needitem("flag");
|
||
kp = lookup(word, flags);
|
||
if (kp == NULL)
|
||
cueabort(_("Unknown flag '%s'"), word);
|
||
|
||
switch (kp->k_type) {
|
||
|
||
case K_DCP: sp->flags |= TI_COPY; break;
|
||
case K_4CH: sp->flags |= TI_QUADRO; break;
|
||
case K_PRE: sp->flags |= TI_PREEMP; break;
|
||
case K_SCMS: sp->flags |= TI_SCMS; break;
|
||
default: cueabort(_("Panic: unknown FLAG '%s'"), word);
|
||
}
|
||
|
||
} while (peekword() < lineend());
|
||
|
||
if (xdebug > 0)
|
||
printf(_("Track[%2.2d] %2.2d flags 0x%08X\n"), sp->track, sp->trackno, sp->flags);
|
||
}
|
||
|
||
LOCAL void
|
||
parse_index(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
long l;
|
||
int track = sp->track;
|
||
|
||
if (sp->state < STATE_TRACK) {
|
||
if (sp->state == STATE_FILE &&
|
||
sp->prevstate >= STATE_TRACK &&
|
||
sp->prevstate <= STATE_INDEX1) {
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0)
|
||
extabort(_("INDEX keyword after FILE keyword"));
|
||
goto index_ok;
|
||
}
|
||
statewarn(sp, _("INDEX keyword must be after TRACK keyword"));
|
||
cueabort(_("Badly placed INDEX keyword"));
|
||
}
|
||
index_ok:
|
||
|
||
word = needitem("index");
|
||
if (*astolb(word, &l, 10) != '\0')
|
||
cueabort(_("Not a number '%s'"), word);
|
||
if (l < 0 || l > 99)
|
||
cueabort(_("Illegal index '%s'"), word);
|
||
|
||
if ((sp->index < l) &&
|
||
(((sp->index + 1) == l) || l == 1))
|
||
sp->index = l;
|
||
else
|
||
cueabort(_("Badly placed INDEX %2.2ld number"), l);
|
||
|
||
if (sp->state == STATE_FILE) {
|
||
if (track == 1 || l > 1)
|
||
cueabort(_("INDEX %2.2d not allowed after FILE"), l);
|
||
if (l == 1)
|
||
sp->pflags |= PF_INDEX0_PREV;
|
||
}
|
||
|
||
if (l > 0)
|
||
sp->state = STATE_INDEX1;
|
||
else
|
||
sp->state = STATE_INDEX0;
|
||
sp->prevstate = sp->state;
|
||
|
||
parse_offset(&l);
|
||
|
||
if (xdebug > 1)
|
||
printf(_("Track[%2.2d] %2.2d Index %2.2d %ld\n"), sp->track, sp->trackno, sp->index, l);
|
||
|
||
if (track == 1 ||
|
||
!streql(sp->filename, trackp[track-1].filename)) {
|
||
/*
|
||
* Check for offset 0 when a new file begins.
|
||
*/
|
||
if (sp->index == 0 && l > 0)
|
||
cueabort(_("Bad INDEX 00 offset in CUE file (must be 00:00:00 for new FILE)"));
|
||
if (sp->index == 1 && sp->index0 < 0 && l > 0)
|
||
cueabort(_("Bad INDEX 01 offset in CUE file (must be 00:00:00 for new FILE)"));
|
||
}
|
||
|
||
if (sp->index == 0) {
|
||
sp->index0 = l;
|
||
} else if (sp->index == 1) {
|
||
sp->index1 = l;
|
||
trackp[track].nindex = 1;
|
||
newtrack(trackp, sp);
|
||
|
||
if (xdebug > 1) {
|
||
printf(_("Track[%2.2d] %2.2d pregapsize %ld\n"),
|
||
sp->track, sp->trackno, trackp[track].pregapsize);
|
||
}
|
||
} else if (sp->index == 2) {
|
||
trackp[track].tindex = malloc(100*sizeof (long));
|
||
trackp[track].tindex[1] = 0;
|
||
trackp[track].tindex[2] = l - sp->index1;
|
||
trackp[track].nindex = 2;
|
||
} else if (sp->index > 2) {
|
||
trackp[track].tindex[sp->index] = l - sp->index1;
|
||
trackp[track].nindex = sp->index;
|
||
}
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_isrc(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
int track = sp->track;
|
||
|
||
if (track == 0)
|
||
cueabort(_("ISRC keyword must be past first TRACK"));
|
||
|
||
if ((sp->state < STATE_TRACK) ||
|
||
(sp->state >= STATE_INDEX0)) {
|
||
statewarn(sp, _("ISRC keyword must be after TRACK and before INDEX keyword"));
|
||
cueabort(_("Badly placed ISRC keyword"));
|
||
}
|
||
sp->state = STATE_FLAGS;
|
||
|
||
word = needitem("ISRC");
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0 &&
|
||
strchr(word, '-')) {
|
||
extabort(_("'-' in ISRC arg"));
|
||
}
|
||
setisrc(word, &trackp[track]);
|
||
txp = gettextptr(track, trackp);
|
||
txp->tc_isrc = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_message(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0)
|
||
extabort("MESSAGE");
|
||
|
||
if (sp->track > 0 && sp->state > STATE_INDEX0) {
|
||
statewarn(sp, _("MESSAGE keyword cannot be after INDEX keyword"));
|
||
cueabort(_("Badly placed MESSAGE keyword"));
|
||
}
|
||
|
||
word = needitem("message");
|
||
txp = gettextptr(sp->track, trackp);
|
||
txp->tc_message = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_performer(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
|
||
if (sp->track > 0 && sp->state > STATE_INDEX0) {
|
||
statewarn(sp, _("PERFORMER keyword cannot be after INDEX keyword"));
|
||
cueabort(_("Badly placed PERFORMER keyword"));
|
||
}
|
||
|
||
word = needitem("performer");
|
||
txp = gettextptr(sp->track, trackp);
|
||
txp->tc_performer = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_postgap(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
long l;
|
||
|
||
if (sp->state < STATE_INDEX1) {
|
||
statewarn(sp, _("POSTGAP keyword must be after INDEX 01"));
|
||
cueabort(_("Badly placed POSTGAP keyword"));
|
||
}
|
||
sp->state = STATE_POSTGAP;
|
||
|
||
parse_offset(&l);
|
||
sp->postgapsize = l;
|
||
trackp[sp->track].padsecs = l;
|
||
/*
|
||
* Add to size of track.
|
||
* In non-CUE mode, this is done in opentracks().
|
||
*/
|
||
if (l > 0)
|
||
trackp[sp->track].tracksecs += l;
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_pregap(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
long l;
|
||
|
||
if ((sp->state < STATE_TRACK) ||
|
||
(sp->state >= STATE_INDEX0)) {
|
||
statewarn(sp, _("PREGAP keyword must be after TRACK and before INDEX keyword"));
|
||
cueabort(_("Badly placed PREGAP keyword"));
|
||
}
|
||
sp->state = STATE_FLAGS;
|
||
|
||
parse_offset(&l);
|
||
sp->pregapsize = l;
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_rem(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *oword = curword();
|
||
char *word;
|
||
|
||
word = nextitem();
|
||
if ((oword == word) || (*word == '\0'))
|
||
return;
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0 &&
|
||
streql(word, "CDRTOOLS")) {
|
||
sp->pflags |= PF_CDRTOOLS_EXT;
|
||
errmsgno(EX_BAD,
|
||
_("Warning: Enabling cdrecord specific CUE extensions.\n"));
|
||
}
|
||
if ((sp->pflags & PF_CDRTOOLS_EXT) == 0 &&
|
||
streql(word, "COMMENT")) {
|
||
oword = word;
|
||
word = nextitem();
|
||
if ((oword == word) || (*word == '\0'))
|
||
return;
|
||
if (strncmp(word, "ExactAudioCopy ", 15) == 0) {
|
||
sp->pflags |= PF_CDRTOOLS_EXT;
|
||
errmsgno(EX_BAD,
|
||
_("Warning: Found ExactAudioCopy, enabling CUE extensions.\n"));
|
||
}
|
||
}
|
||
}
|
||
|
||
LOCAL void
|
||
parse_songwriter(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
|
||
if (sp->track > 0 && sp->state > STATE_INDEX0) {
|
||
statewarn(sp, _("SONGWRITER keyword cannot be after INDEX keyword"));
|
||
cueabort(_("Badly placed SONGWRITER keyword"));
|
||
}
|
||
word = needitem("songwriter");
|
||
txp = gettextptr(sp->track, trackp);
|
||
txp->tc_songwriter = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_title(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
char *word;
|
||
textptr_t *txp;
|
||
|
||
if (sp->track > 0 && sp->state > STATE_INDEX0) {
|
||
statewarn(sp, _("TITLE keyword cannot be after INDEX keyword"));
|
||
cueabort(_("Badly placed TITLE keyword"));
|
||
}
|
||
word = needitem("title");
|
||
txp = gettextptr(sp->track, trackp);
|
||
txp->tc_title = strdup(word);
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_track(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
struct keyw *kp;
|
||
char *word;
|
||
long l;
|
||
long secsize = -1;
|
||
|
||
if ((sp->state >= STATE_TRACK) &&
|
||
(sp->state < STATE_INDEX1)) {
|
||
statewarn(sp, _("TRACK keyword must be after INDEX 01"));
|
||
cueabort(_("Badly placed TRACK keyword"));
|
||
}
|
||
sp->pflags &= ~(PF_INDEX0_PREV|PF_FILE_FOUND);
|
||
if (sp->state == STATE_FILE)
|
||
sp->pflags |= PF_FILE_FOUND;
|
||
sp->state = STATE_TRACK;
|
||
sp->prevstate = STATE_TRACK;
|
||
sp->track++;
|
||
sp->index0 = -1;
|
||
sp->index = -1;
|
||
sp->pregapsize = -1;
|
||
sp->postgapsize = -1;
|
||
|
||
word = needitem("track number");
|
||
if (*astolb(word, &l, 10) != '\0')
|
||
cueabort(_("Not a number '%s'"), word);
|
||
if (l <= 0 || l > 99)
|
||
cueabort(_("Illegal TRACK number '%s'"), word);
|
||
|
||
if ((sp->trackno < l) &&
|
||
(((sp->trackno + 1) == l) || sp->trackno == 0))
|
||
sp->trackno = l;
|
||
else
|
||
cueabort(_("Badly placed TRACK %ld number"), l);
|
||
|
||
word = needword("data type");
|
||
kp = lookup(word, dtypes);
|
||
if (kp == NULL)
|
||
cueabort(_("Unknown data type '%s'"), word);
|
||
|
||
if (getworddelim() == '/') {
|
||
word = needitem("sector size");
|
||
if (*astol(++word, &secsize) != '\0')
|
||
cueabort(_("Not a number '%s'"), word);
|
||
}
|
||
|
||
/*
|
||
* Reset all flags that may be set in TRACK & FLAGS lines
|
||
*/
|
||
sp->flags &= ~(TI_AUDIO|TI_COPY|TI_QUADRO|TI_PREEMP|TI_SCMS);
|
||
|
||
if (kp->k_type == K_AUDIO)
|
||
sp->flags |= TI_AUDIO;
|
||
|
||
switch (kp->k_type) {
|
||
|
||
case K_CDG:
|
||
if (secsize < 0)
|
||
secsize = 2448;
|
||
case K_AUDIO:
|
||
if (secsize < 0)
|
||
secsize = 2352;
|
||
|
||
sp->tracktype = TOC_DA;
|
||
sp->sectype = SECT_AUDIO;
|
||
sp->dbtype = DB_RAW;
|
||
sp->secsize = secsize;
|
||
sp->dataoff = 0;
|
||
if (secsize != 2352)
|
||
cueabort(_("Unsupported sector size %ld for audio"), secsize);
|
||
break;
|
||
|
||
case K_MODE1:
|
||
if (secsize < 0)
|
||
secsize = 2048;
|
||
|
||
sp->tracktype = TOC_ROM;
|
||
sp->sectype = SECT_ROM;
|
||
sp->dbtype = DB_ROM_MODE1;
|
||
sp->secsize = secsize;
|
||
sp->dataoff = 16;
|
||
/*
|
||
* XXX Sector Size == 2352 ???
|
||
* XXX It seems that there exist bin/cue pairs with this value
|
||
*/
|
||
if (secsize != 2048)
|
||
cueabort(_("Unsupported sector size %ld for data"), secsize);
|
||
break;
|
||
|
||
case K_MODE2:
|
||
case K_CDI:
|
||
sp->tracktype = TOC_ROM;
|
||
sp->sectype = SECT_MODE_2;
|
||
sp->dbtype = DB_ROM_MODE2;
|
||
sp->secsize = secsize;
|
||
sp->dataoff = 16;
|
||
if (secsize == 2352) {
|
||
sp->tracktype = TOC_XA2;
|
||
sp->sectype = SECT_MODE_2_MIX;
|
||
sp->sectype |= ST_MODE_RAW;
|
||
sp->dbtype = DB_RAW;
|
||
sp->dataoff = 0;
|
||
} else if (secsize != 2336)
|
||
cueabort(_("Unsupported sector size %ld for mode2"), secsize);
|
||
if (kp->k_type == K_CDI)
|
||
sp->tracktype = TOC_CDI;
|
||
break;
|
||
|
||
default: cueabort(_("Panic: unknown datatype '%s'"), word);
|
||
}
|
||
|
||
if (sp->flags & TI_PREEMP)
|
||
sp->sectype |= ST_PREEMPMASK;
|
||
sp->secsize = secsize;
|
||
|
||
if (xdebug > 1) {
|
||
printf(_("Track[%2.2d] %2.2d Tracktype %s/%d\n"),
|
||
sp->track, sp->trackno, kp->k_name, sp->secsize);
|
||
}
|
||
|
||
checkextra();
|
||
}
|
||
|
||
LOCAL void
|
||
parse_offset(lp)
|
||
long *lp;
|
||
{
|
||
char *word;
|
||
char *p;
|
||
long m = -1;
|
||
long s = -1;
|
||
long f = -1;
|
||
|
||
word = needitem("time offset/length");
|
||
|
||
if (strchr(word, ':') == NULL) {
|
||
if (*astol(word, lp) != '\0')
|
||
cueabort(_("Not a number '%s'"), word);
|
||
return;
|
||
}
|
||
if (*(p = astolb(word, &m, 10)) != ':')
|
||
cueabort(_("Not a number '%s'"), word);
|
||
if (m < 0 || m >= 160)
|
||
cueabort(_("Illegal minute value in '%s'"), word);
|
||
p++;
|
||
if (*(p = astolb(p, &s, 10)) != ':')
|
||
cueabort(_("Not a number '%s'"), p);
|
||
if (s < 0 || s >= 60)
|
||
cueabort(_("Illegal second value in '%s'"), word);
|
||
p++;
|
||
if (*(p = astolb(p, &f, 10)) != '\0')
|
||
cueabort(_("Not a number '%s'"), p);
|
||
if (f < 0 || f >= 75)
|
||
cueabort(_("Illegal frame value in '%s'"), word);
|
||
|
||
m = m * 60 + s;
|
||
m = m * 75 + f;
|
||
*lp = m;
|
||
}
|
||
|
||
/*--------------------------------------------------------------------------*/
|
||
LOCAL void
|
||
newtrack(trackp, sp)
|
||
track_t trackp[];
|
||
state_t *sp;
|
||
{
|
||
register int i;
|
||
register int track = sp->track;
|
||
Llong tracksize;
|
||
|
||
if (xdebug > 1)
|
||
printf(_("-->Newtrack %2.2d Trackno %2.2d\n"), track, sp->trackno);
|
||
if (track > 1 && streql(sp->filename, trackp[track-1].filename)) {
|
||
tracksize = (sp->index1 - sp->secoff) * trackp[track-1].isecsize;
|
||
|
||
if (xdebug > 1)
|
||
printf(" trackoff %lld filesize %lld index1 %ld size %ld/%lld secsize/isecsize %d/%d\n",
|
||
sp->trackoff, sp->filesize, sp->index1,
|
||
sp->index1 - sp->secoff,
|
||
tracksize,
|
||
trackp[track-1].secsize,
|
||
trackp[track-1].isecsize);
|
||
|
||
trackp[track-1].itracksize = tracksize;
|
||
trackp[track-1].tracksize = tracksize;
|
||
if (trackp[track-1].secsize != trackp[track-1].isecsize) {
|
||
/*
|
||
* In RAW mode, we need to recompute the track size.
|
||
*/
|
||
trackp[track-1].tracksize =
|
||
(trackp[track-1].itracksize /
|
||
trackp[track-1].isecsize) *
|
||
trackp[track-1].secsize
|
||
+ trackp[track-1].itracksize %
|
||
trackp[track-1].isecsize;
|
||
}
|
||
trackp[track-1].tracksecs = sp->index1 - sp->secoff;
|
||
/*
|
||
* Add to size of track.
|
||
* In non-CUE mode, this is done in opentracks().
|
||
*/
|
||
if (trackp[track-1].padsecs > 0)
|
||
trackp[track-1].tracksecs += trackp[track-1].padsecs;
|
||
|
||
sp->trackoff += tracksize;
|
||
sp->secoff = sp->index1;
|
||
}
|
||
/*
|
||
* Make 'tracks' immediately usable in track structure.
|
||
*/
|
||
for (i = 0; i < MAX_TRACK+2; i++)
|
||
trackp[i].tracks = track;
|
||
|
||
trackp[track].filename = sp->filename;
|
||
trackp[track].xfp = xopen(sp->filename, O_RDONLY|O_BINARY, 0, X_NOREWIND);
|
||
trackp[track].trackstart = 0L;
|
||
/*
|
||
* SEtzen wenn tracksecs bekannt sind
|
||
* d.h. mit Index0 oder Index 1 vom n<>chsten track
|
||
*
|
||
* trackp[track].itracksize = tracksize;
|
||
* trackp[track].tracksize = tracksize;
|
||
* trackp[track].tracksecs = -1L;
|
||
*/
|
||
tracksize = sp->filesize - sp->trackoff;
|
||
|
||
trackp[track].itracksize = tracksize;
|
||
trackp[track].tracksize = tracksize;
|
||
trackp[track].tracksecs = (tracksize + sp->secsize - 1) / sp->secsize;
|
||
|
||
if (xdebug > 1)
|
||
printf(_(" Remaining Filesize %lld (%lld secs)\n"),
|
||
(sp->filesize-sp->trackoff),
|
||
(sp->filesize-sp->trackoff +sp->secsize - 1) / sp->secsize);
|
||
|
||
if (sp->pregapsize >= 0) {
|
||
/* trackp[track].flags &= ~TI_PREGAP;*/
|
||
sp->flags &= ~TI_PREGAP;
|
||
trackp[track].pregapsize = sp->pregapsize;
|
||
} else {
|
||
/* trackp[track].flags |= TI_PREGAP;*/
|
||
if (track > 1)
|
||
sp->flags |= TI_PREGAP;
|
||
if (track == 1)
|
||
trackp[track].pregapsize = sp->index1 + 150;
|
||
else if (sp->index0 < 0)
|
||
trackp[track].pregapsize = 0;
|
||
else if (sp->pflags & PF_INDEX0_PREV) /* INDEX0 in prev FILE */
|
||
trackp[track].pregapsize = trackp[track-1].tracksecs - sp->index0;
|
||
else
|
||
trackp[track].pregapsize = sp->index1 - sp->index0;
|
||
}
|
||
|
||
trackp[track].isecsize = sp->secsize;
|
||
trackp[track].secsize = sp->secsize;
|
||
trackp[track].flags = sp->flags |
|
||
(trackp[0].flags & ~(TI_HIDDEN|TI_SWAB|TI_AUDIO|TI_COPY|TI_QUADRO|TI_PREEMP|TI_SCMS));
|
||
if (trackp[0].flags & TI_RAW) {
|
||
if (is_raw16(&trackp[track]))
|
||
trackp[track].secsize = RAW16_SEC_SIZE;
|
||
else
|
||
trackp[track].secsize = RAW96_SEC_SIZE;
|
||
#ifndef HAVE_LIB_EDC_ECC
|
||
if ((sp->sectype & ST_MODE_MASK) != ST_MODE_AUDIO) {
|
||
errmsgno(EX_BAD,
|
||
_("EDC/ECC library not compiled in.\n"));
|
||
comerrno(EX_BAD,
|
||
_("Data sectors are not supported in RAW mode.\n"));
|
||
}
|
||
#endif
|
||
}
|
||
/*
|
||
* In RAW mode, we need to recompute the track size.
|
||
*/
|
||
trackp[track].tracksize =
|
||
(trackp[track].itracksize / trackp[track].isecsize) *
|
||
trackp[track].secsize
|
||
+ trackp[track].itracksize % trackp[track].isecsize;
|
||
|
||
trackp[track].secspt = 0; /* transfer size is set up in set_trsizes() */
|
||
/* trackp[track].pktsize = pktsize; */
|
||
trackp[track].pktsize = 0;
|
||
trackp[track].trackno = sp->trackno;
|
||
trackp[track].sectype = sp->sectype;
|
||
|
||
trackp[track].dataoff = sp->dataoff;
|
||
trackp[track].tracktype = sp->tracktype;
|
||
trackp[track].dbtype = sp->dbtype;
|
||
|
||
if (track == 1) {
|
||
track_t *tp0 = &trackp[0];
|
||
track_t *tp1 = &trackp[1];
|
||
|
||
tp0->tracktype &= ~TOC_MASK;
|
||
tp0->tracktype |= sp->tracktype;
|
||
|
||
/*
|
||
* setleadinout() also sets: sectype dbtype dataoff
|
||
*/
|
||
tp0->sectype = tp1->sectype;
|
||
tp0->dbtype = tp1->dbtype;
|
||
tp0->dataoff = tp1->dataoff;
|
||
tp0->isecsize = tp1->isecsize;
|
||
tp0->secsize = tp1->secsize;
|
||
|
||
if (sp->index0 == 0 && sp->index1 > 0) {
|
||
|
||
tp0->filename = tp1->filename;
|
||
tp0->xfp = xopen(sp->filename, O_RDONLY|O_BINARY, 0, X_NOREWIND);
|
||
tp0->trackstart = tp1->trackstart;
|
||
tp0->itracksize = sp->index1 * tp1->isecsize;
|
||
tp0->tracksize = sp->index1 * tp1->secsize;
|
||
tp0->tracksecs = sp->index1;
|
||
tp1->tracksecs -= sp->index1;
|
||
sp->secoff += sp->index1;
|
||
sp->trackoff += sp->index1 * tp1->isecsize;
|
||
tp1->flags &= ~TI_PREGAP;
|
||
tp0->flags |= tp1->flags &
|
||
(TI_SWAB|TI_AUDIO|TI_COPY|TI_QUADRO|TI_PREEMP|TI_SCMS);
|
||
tp0->flags |= TI_HIDDEN;
|
||
tp1->flags |= TI_HIDDEN;
|
||
}
|
||
|
||
if (xdebug > 1) {
|
||
printf(_("Track[%2.2d] %2.2d Tracktype %X\n"),
|
||
0, 0, trackp[0].tracktype);
|
||
}
|
||
}
|
||
if (xdebug > 1) {
|
||
printf(_("Track[%2.2d] %2.2d Tracktype %X\n"),
|
||
track, sp->trackno, trackp[track].tracktype);
|
||
}
|
||
trackp[track].nindex = 1;
|
||
trackp[track].tindex = 0;
|
||
|
||
if (xdebug > 1) {
|
||
printf(_("Track[%2.2d] %2.2d flags 0x%08X\n"), 0, 0, trackp[0].flags);
|
||
printf(_("Track[%2.2d] %2.2d flags 0x%08X\n"), track, sp->trackno, trackp[track].flags);
|
||
}
|
||
}
|
||
|
||
/*--------------------------------------------------------------------------*/
|
||
LOCAL keyw_t *
|
||
lookup(word, table)
|
||
char *word;
|
||
keyw_t table[];
|
||
{
|
||
register keyw_t *kp = table;
|
||
|
||
while (kp->k_name) {
|
||
if (streql(kp->k_name, word))
|
||
return (kp);
|
||
kp++;
|
||
}
|
||
return (NULL);
|
||
}
|
||
|
||
LOCAL char *
|
||
state_name(st)
|
||
int st;
|
||
{
|
||
if (st < STATE_NONE || st > STATE_MAX)
|
||
return ("UNKNOWN");
|
||
return (states[st]);
|
||
}
|
||
|
||
/*--------------------------------------------------------------------------*/
|
||
/*
|
||
* Parser low level functions start here...
|
||
*/
|
||
|
||
LOCAL char linebuf[4096];
|
||
LOCAL char *fname;
|
||
LOCAL char *linep;
|
||
LOCAL char *wordendp;
|
||
LOCAL char wordendc;
|
||
LOCAL int olinelen;
|
||
LOCAL int linelen;
|
||
LOCAL int lineno;
|
||
|
||
LOCAL char worddelim[] = "=:,/";
|
||
LOCAL char nulldelim[] = "";
|
||
|
||
#ifdef DEBUG
|
||
LOCAL void
|
||
wdebug()
|
||
{
|
||
printf("WORD: '%s' rest '%s'\n", linep, peekword());
|
||
printf("linep %lX peekword %lX end %lX\n",
|
||
(long)linep, (long)peekword(), (long)&linebuf[linelen]);
|
||
}
|
||
#endif
|
||
|
||
LOCAL FILE *
|
||
cueopen(name)
|
||
char *name;
|
||
{
|
||
FILE *f;
|
||
|
||
f = fileopen(name, "r");
|
||
if (f == NULL)
|
||
comerr(_("Cannot open '%s'.\n"), name);
|
||
|
||
fname = name;
|
||
return (f);
|
||
}
|
||
|
||
LOCAL char *
|
||
cuename()
|
||
{
|
||
return (fname);
|
||
}
|
||
|
||
LOCAL char *
|
||
nextline(f)
|
||
FILE *f;
|
||
{
|
||
register int len;
|
||
|
||
do {
|
||
fillbytes(linebuf, sizeof (linebuf), '\0');
|
||
len = fgetline(f, linebuf, sizeof (linebuf));
|
||
if (len < 0)
|
||
return (NULL);
|
||
if (len > 0 && linebuf[len-1] == '\r') {
|
||
linebuf[len-1] = '\0';
|
||
len--;
|
||
}
|
||
linelen = len;
|
||
lineno++;
|
||
} while (linebuf[0] == '#');
|
||
|
||
olinelen = linelen;
|
||
linep = linebuf;
|
||
wordendp = linep;
|
||
wordendc = *linep;
|
||
|
||
return (linep);
|
||
}
|
||
|
||
#ifdef __needed__
|
||
LOCAL void
|
||
ungetline()
|
||
{
|
||
linelen = olinelen;
|
||
linep = linebuf;
|
||
*wordendp = wordendc;
|
||
wordendp = linep;
|
||
wordendc = *linep;
|
||
}
|
||
#endif
|
||
|
||
LOCAL char *
|
||
skipwhite(s)
|
||
const char *s;
|
||
{
|
||
register const Uchar *p = (const Uchar *)s;
|
||
|
||
while (*p) {
|
||
if (!isspace(*p))
|
||
break;
|
||
p++;
|
||
}
|
||
return ((char *)p);
|
||
}
|
||
|
||
LOCAL char *
|
||
peekword()
|
||
{
|
||
return (&wordendp[1]);
|
||
}
|
||
|
||
LOCAL char *
|
||
lineend()
|
||
{
|
||
return (&linebuf[linelen]);
|
||
}
|
||
|
||
LOCAL char *
|
||
markword(delim)
|
||
char *delim;
|
||
{
|
||
register BOOL quoted = FALSE;
|
||
register Uchar c;
|
||
register Uchar *s;
|
||
register Uchar *from;
|
||
register Uchar *to;
|
||
|
||
for (s = (Uchar *)linep; (c = *s) != '\0'; s++) {
|
||
if (c == '"') {
|
||
quoted = !quoted;
|
||
for (to = s, from = &s[1]; *from; ) {
|
||
c = *from++;
|
||
if (c == '\\' && quoted && (*from == '\\' || *from == '"'))
|
||
c = *from++;
|
||
*to++ = c;
|
||
}
|
||
*to = '\0';
|
||
c = *s;
|
||
linelen--;
|
||
}
|
||
if (!quoted && isspace(c))
|
||
break;
|
||
if (!quoted && strchr(delim, c) && s > (Uchar *)linep)
|
||
break;
|
||
}
|
||
wordendp = (char *)s;
|
||
wordendc = (char)*s;
|
||
*s = '\0';
|
||
|
||
return (linep);
|
||
}
|
||
|
||
LOCAL char
|
||
getworddelim()
|
||
{
|
||
return (wordendc);
|
||
}
|
||
|
||
LOCAL char *
|
||
getnextitem(delim)
|
||
char *delim;
|
||
{
|
||
*wordendp = wordendc;
|
||
|
||
linep = skipwhite(wordendp);
|
||
return (markword(delim));
|
||
}
|
||
|
||
LOCAL char *
|
||
neednextitem(delim, type)
|
||
char *delim;
|
||
char *type;
|
||
{
|
||
char *olinep = linep;
|
||
char *nlinep;
|
||
|
||
nlinep = getnextitem(delim);
|
||
|
||
if ((olinep == nlinep) || (*nlinep == '\0')) {
|
||
if (type == NULL)
|
||
cueabort(_("Missing text"));
|
||
else
|
||
cueabort(_("Missing '%s'"), type);
|
||
}
|
||
|
||
return (nlinep);
|
||
}
|
||
|
||
#ifdef __needed__
|
||
LOCAL char *
|
||
nextword()
|
||
{
|
||
return (getnextitem(worddelim));
|
||
}
|
||
#endif
|
||
|
||
LOCAL char *
|
||
needword(type)
|
||
char *type;
|
||
{
|
||
return (neednextitem(worddelim, type));
|
||
}
|
||
|
||
LOCAL char *
|
||
curword()
|
||
{
|
||
return (linep);
|
||
}
|
||
|
||
LOCAL char *
|
||
nextitem()
|
||
{
|
||
return (getnextitem(nulldelim));
|
||
}
|
||
|
||
LOCAL char *
|
||
needitem(type)
|
||
char *type;
|
||
{
|
||
return (neednextitem(nulldelim, type));
|
||
}
|
||
|
||
LOCAL void
|
||
checkextra()
|
||
{
|
||
if (peekword() < lineend())
|
||
cueabort(_("Extra text '%s'"), peekword());
|
||
}
|
||
|
||
/* VARARGS2 */
|
||
#ifdef PROTOTYPES
|
||
LOCAL void
|
||
statewarn(state_t *sp, const char *fmt, ...)
|
||
#else
|
||
LOCAL void
|
||
statewarn(sp, fmt, va_alist)
|
||
state_t *sp;
|
||
char *fmt;
|
||
va_dcl
|
||
#endif
|
||
{
|
||
va_list args;
|
||
|
||
#ifdef PROTOTYPES
|
||
va_start(args, fmt);
|
||
#else
|
||
va_start(args);
|
||
#endif
|
||
errmsgno(EX_BAD, _("%r. Current state is '%s'.\n"),
|
||
fmt, args, state_name(sp->state));
|
||
va_end(args);
|
||
}
|
||
|
||
#ifdef __needed__
|
||
/* VARARGS1 */
|
||
#ifdef PROTOTYPES
|
||
LOCAL void
|
||
cuewarn(const char *fmt, ...)
|
||
#else
|
||
LOCAL void
|
||
cuewarn(fmt, va_alist)
|
||
char *fmt;
|
||
va_dcl
|
||
#endif
|
||
{
|
||
va_list args;
|
||
|
||
#ifdef PROTOTYPES
|
||
va_start(args, fmt);
|
||
#else
|
||
va_start(args);
|
||
#endif
|
||
errmsgno(EX_BAD, _("%r on line %d col %d in '%s'.\n"),
|
||
fmt, args, lineno, linep - linebuf, fname);
|
||
va_end(args);
|
||
}
|
||
#endif
|
||
|
||
/* VARARGS1 */
|
||
#ifdef PROTOTYPES
|
||
LOCAL void
|
||
cueabort(const char *fmt, ...)
|
||
#else
|
||
LOCAL void
|
||
cueabort(fmt, va_alist)
|
||
char *fmt;
|
||
va_dcl
|
||
#endif
|
||
{
|
||
va_list args;
|
||
|
||
#ifdef PROTOTYPES
|
||
va_start(args, fmt);
|
||
#else
|
||
va_start(args);
|
||
#endif
|
||
#ifdef PARSE_DEBUG
|
||
errmsgno(EX_BAD, _("%r on line %d col %d in '%s'.\n"),
|
||
#else
|
||
comerrno(EX_BAD, _("%r on line %d col %d in '%s'.\n"),
|
||
#endif
|
||
fmt, args, lineno, linep - linebuf, fname);
|
||
va_end(args);
|
||
}
|
||
|
||
/* VARARGS1 */
|
||
#ifdef PROTOTYPES
|
||
LOCAL void
|
||
extabort(const char *fmt, ...)
|
||
#else
|
||
LOCAL void
|
||
extabort(fmt, va_alist)
|
||
char *fmt;
|
||
va_dcl
|
||
#endif
|
||
{
|
||
va_list args;
|
||
|
||
#ifdef PROTOTYPES
|
||
va_start(args, fmt);
|
||
#else
|
||
va_start(args);
|
||
#endif
|
||
errmsgno(EX_BAD, _("Unsupported by CDRWIN: %r on line %d col %d in '%s'.\n"),
|
||
fmt, args, lineno, linep - linebuf, fname);
|
||
va_end(args);
|
||
errmsgno(EX_BAD, _("Add 'REM CDRTOOLS' to enable cdrtools specific CUE extensions.\n"));
|
||
comexit(EX_BAD);
|
||
}
|