mirror of
https://github.com/mist-devel/mist-firmware.git
synced 2026-01-11 23:43:04 +00:00
353 lines
11 KiB
C
353 lines
11 KiB
C
// cue_parser.c
|
|
// 2021, Gyorgy Szombathelyi
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include "cue_parser.h"
|
|
#ifdef CUE_PARSER_TEST
|
|
#define cue_parser_debugf(a, ...) printf(a"\n", ## __VA_ARGS__)
|
|
#else
|
|
#include "debug.h"
|
|
#include "idxfile.h"
|
|
#endif
|
|
|
|
//// defines ////
|
|
#define TOKEN_FILE "FILE"
|
|
#define TOKEN_BINARY "BINARY"
|
|
#define TOKEN_TRACK "TRACK"
|
|
#define TOKEN_AUDIO "AUDIO"
|
|
#define TOKEN_MODE1_2048 "MODE1/2048"
|
|
#define TOKEN_MODE1_2352 "MODE1/2352"
|
|
#define TOKEN_MODE2_2352 "MODE2/2352"
|
|
#define TOKEN_MODE2_2336 "MODE2/2336"
|
|
#define TOKEN_PREGAP "PREGAP"
|
|
#define TOKEN_INDEX "INDEX"
|
|
|
|
#define MODE_NONE 0
|
|
#define MODE_FILE 1
|
|
#define MODE_TRACK 2
|
|
#define MODE_PREGAP 3
|
|
#define MODE_INDEX 4
|
|
|
|
#define CUE_WORD_SIZE 128
|
|
|
|
#define CUE_EOT 4
|
|
#define CUE_NOWORD 3
|
|
//// macros ////
|
|
#define CHAR_IS_NUM(c) (((c) >= '0') && ((c) <= '9'))
|
|
#define CHAR_IS_ALPHA_LOWER(c) (((c) >= 'a') && ((c) <= 'z'))
|
|
#define CHAR_IS_ALPHA_UPPER(c) (((c) >= 'A') && ((c) <= 'Z'))
|
|
#define CHAR_IS_ALPHA(c) (CHAR_IS_ALPHA_LOWER(c) || CHAR_IS_ALPHA_UPPER(c))
|
|
#define CHAR_IS_ALPHANUM(c) (CHAR_IS_ALPHA_LOWER(c) || CHAR_IS_ALPHA_UPPER(c) || CHAR_IS_NUM(c))
|
|
#define CHAR_IS_SPECIAL(c) (((c) == '[') || ((c) == ']') || ((c) == '-') || ((c) == '_') || ((c) == ',') || ((c) == '=') || ((c) == '~') || ((c) == ':') || ((c) == '/'))
|
|
#define CHAR_IS_VALID(c) (CHAR_IS_ALPHANUM(c) || CHAR_IS_SPECIAL(c))
|
|
#define CHAR_IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\r') || ((c) == '\n'))
|
|
#define CHAR_IS_SPACE(c) (((c) == ' ') || ((c) == '\t'))
|
|
#define CHAR_IS_LINEEND(c) (((c) == '\n'))
|
|
#define CHAR_IS_COMMENT(c) (((c) == ';'))
|
|
#define CHAR_IS_QUOTE(c) (((c) == '"'))
|
|
#define CHAR_TO_UPPERCASE(c) ({ char _c = (c); if (CHAR_IS_ALPHA_LOWER(_c)) _c = _c - 'a' + 'A'; _c;})
|
|
#define CHAR_TO_LOWERCASE(c) ({ char _c = (c); if (CHAR_IS_ALPHA_UPPER(_c)) _c = _c - 'A' + 'a'; _c;})
|
|
|
|
#ifdef CUE_PARSER_TEST
|
|
FILE* cue_fp = NULL;
|
|
char sector_buffer[512] = {0};
|
|
int cue_size = 0;
|
|
#else
|
|
static FIL cue_file;
|
|
#endif
|
|
|
|
static int cue_pt = 0;
|
|
|
|
toc_t toc;
|
|
|
|
const char *cue_error_msg[] = {
|
|
"\n Cannot open CUE file.\n",
|
|
"\n Invalid CUE file.\n",
|
|
"\n Unsupported CUE file.\n",
|
|
"\n Cannot open the image.\n"
|
|
};
|
|
|
|
void LBA2MSF(int lba, msf_t* msf) {
|
|
msf->m = (lba / 75) / 60;
|
|
msf->s = (lba / 75) % 60;
|
|
msf->f = (lba % 75);
|
|
}
|
|
|
|
int MSF2LBA(unsigned char m, unsigned char s, unsigned char f) {
|
|
return (f + s * 75 + m * 60 * 75 - 150);
|
|
}
|
|
|
|
static char ParseMSF(const char *s, msf_t *msf) {
|
|
char c;
|
|
|
|
if (*s && CHAR_IS_NUM(*s)) msf->m = 10*(*s++ - '0'); else return 0;
|
|
if (*s && CHAR_IS_NUM(*s)) msf->m+= (*s++ - '0'); else return 0;
|
|
if (*s && *s == ':') s++; else return 0;
|
|
if (*s && CHAR_IS_NUM(*s)) msf->s = 10*(*s++ - '0'); else return 0;
|
|
if (*s && CHAR_IS_NUM(*s)) msf->s+= (*s++ - '0'); else return 0;
|
|
if (*s && *s == ':') s++; else return 0;
|
|
if (*s && CHAR_IS_NUM(*s)) msf->f = 10*(*s++ - '0'); else return 0;
|
|
if (*s && CHAR_IS_NUM(*s)) msf->f+= (*s++ - '0'); else return 0;
|
|
if (*s) return 0;
|
|
return 1;
|
|
}
|
|
|
|
static char cue_getch()
|
|
{
|
|
#ifndef CUE_PARSER_TEST
|
|
UINT br;
|
|
#endif
|
|
if (!(cue_pt&0x1ff)) {
|
|
// reload buffer
|
|
#ifdef CUE_PARSER_TEST
|
|
fread(sector_buffer, sizeof(char), sizeof(sector_buffer), cue_fp);
|
|
#else
|
|
f_read(&cue_file, sector_buffer, 512, &br);
|
|
#endif
|
|
}
|
|
|
|
#ifdef CUE_PARSER_TEST
|
|
if (cue_pt >= cue_size) return 0;
|
|
#else
|
|
if (cue_pt >= f_size(&cue_file)) return 0;
|
|
#endif
|
|
else return sector_buffer[(cue_pt++)&0x1ff];
|
|
}
|
|
|
|
static int cue_getword(char* word)
|
|
{
|
|
char c;
|
|
char literal=0;
|
|
int i=0;
|
|
|
|
while(1) {
|
|
c = cue_getch();
|
|
if ((!c) || CHAR_IS_LINEEND(c) || (CHAR_IS_WHITESPACE(c) && !literal)) break;
|
|
else if (CHAR_IS_QUOTE(c)) literal ^= 1;
|
|
else if ((literal || (CHAR_IS_VALID(c))) && i<(CUE_WORD_SIZE-1)) word[i++] = c;
|
|
}
|
|
word[i] = '\0';
|
|
return c==0 ? CUE_EOT : i == 0 ? CUE_NOWORD : literal ? 1 : 0;
|
|
}
|
|
|
|
//// cue_parse() ////
|
|
#ifdef CUE_PARSER_TEST
|
|
char cue_parse(const char *filename)
|
|
#else
|
|
char cue_parse(const char *filename, IDXFile *image)
|
|
#endif
|
|
{
|
|
char word[CUE_WORD_SIZE] = {0};
|
|
int word_status;
|
|
char mode = 0, submode = 0, error = CUE_RES_OK, index = 0, bin_valid = 0;
|
|
int track = 0, x, pregap = 0, tracklen;
|
|
msf_t msf;
|
|
int lba, lastindex1 = 0;
|
|
char e[3];
|
|
|
|
memset(&toc, 0, sizeof(toc));
|
|
|
|
const char *ext = GetExtension(filename);
|
|
e[0] = e[1] = e[2] = ' ';
|
|
if (ext) {
|
|
if (ext[0]) e[0] = toupper(ext[0]);
|
|
if (ext[1]) e[1] = toupper(ext[1]);
|
|
if (ext[2]) e[2] = toupper(ext[2]);
|
|
}
|
|
if (!memcmp(e, "ISO", 3)) {
|
|
// open iso file
|
|
#ifdef CUE_PARSER_TEST
|
|
bin_valid = 1;
|
|
#else
|
|
toc.file = image;
|
|
if (IDXOpen(toc.file, filename, FA_READ) == FR_OK) {
|
|
bin_valid = 1;
|
|
track = 1;
|
|
toc.tracks[0].sector_size = 2048;
|
|
toc.tracks[0].type = SECTOR_DATA_MODE1;
|
|
toc.tracks[0].offset = 0;
|
|
toc.tracks[0].start = 0;
|
|
} else {
|
|
error = CUE_RES_BINERR;
|
|
}
|
|
#endif
|
|
} else {
|
|
// open cue file
|
|
#ifdef CUE_PARSER_TEST
|
|
if ((cue_fp = fopen(filename, "rb")) == NULL) {
|
|
#else
|
|
if (f_open(&cue_file, filename, FA_READ) != FR_OK) {
|
|
#endif
|
|
cue_parser_debugf("Can't open file %s !", filename);
|
|
return CUE_RES_NOTFOUND;
|
|
}
|
|
|
|
#ifdef CUE_PARSER_TEST
|
|
fseek(cue_fp, 0L, SEEK_END);
|
|
cue_size = ftell(cue_fp);
|
|
fseek(cue_fp, 0L, SEEK_SET);
|
|
#else
|
|
cue_parser_debugf("Opened file %s with size %llu bytes.", filename, f_size(&cue_file));
|
|
#endif
|
|
cue_pt = 0;
|
|
|
|
// parse cue
|
|
while (1) {
|
|
// get line
|
|
word_status = cue_getword(word);
|
|
if (word_status != CUE_NOWORD) {
|
|
//cue_parser_debugf("next word(%d): \"%s\".", word_status, word);
|
|
switch (mode) {
|
|
case MODE_NONE:
|
|
submode = 0;
|
|
if (!strcmp(word, TOKEN_FILE)) mode = MODE_FILE; else
|
|
if (!strcmp(word, TOKEN_TRACK)) mode = MODE_TRACK; else
|
|
if (!strcmp(word, TOKEN_PREGAP)) mode = MODE_PREGAP; else
|
|
if (!strcmp(word, TOKEN_INDEX)) mode = MODE_INDEX;
|
|
break;
|
|
case MODE_FILE:
|
|
if (submode == 0) {
|
|
pregap = 0;
|
|
cue_parser_debugf("Filename: %s", word);
|
|
if (bin_valid) {
|
|
// only one .bin supported
|
|
error = CUE_RES_UNS;
|
|
} else {
|
|
#ifdef CUE_PARSER_TEST
|
|
bin_valid = 1;
|
|
}
|
|
#else
|
|
toc.file = image;
|
|
if (IDXOpen(toc.file, word, FA_READ) == FR_OK)
|
|
bin_valid = 1;
|
|
else
|
|
error = CUE_RES_BINERR;
|
|
}
|
|
#endif
|
|
} else if (submode == 1) {
|
|
cue_parser_debugf("Filemode: %s", word);
|
|
mode = 0;
|
|
}
|
|
submode++;
|
|
break;
|
|
case MODE_TRACK:
|
|
if (submode == 0) {
|
|
x = strtol(word, 0, 10);
|
|
cue_parser_debugf("Trackno: %d -> %d (%s)", track, x, word);
|
|
if (!x || x > 99 || x != (track + 1)) error = CUE_RES_INVALID; else track = x;
|
|
} else if (submode == 1) {
|
|
cue_parser_debugf("Trackmode: %s", word);
|
|
if (!strcmp(word, TOKEN_AUDIO)) {
|
|
toc.tracks[track-1].sector_size = 2352;
|
|
toc.tracks[track-1].type = SECTOR_AUDIO;
|
|
} else if (!strcmp(word, TOKEN_MODE1_2352)) {
|
|
toc.tracks[track-1].sector_size = 2352;
|
|
toc.tracks[track-1].type = SECTOR_DATA_MODE1;
|
|
} else if (!strcmp(word, TOKEN_MODE2_2352)) {
|
|
toc.tracks[track-1].sector_size = 2352;
|
|
toc.tracks[track-1].type = SECTOR_DATA_MODE2;
|
|
} else if (!strcmp(word, TOKEN_MODE2_2336)) {
|
|
toc.tracks[track-1].sector_size = 2336;
|
|
toc.tracks[track-1].type = SECTOR_DATA_MODE2;
|
|
} else if (!strcmp(word, TOKEN_MODE1_2048)) {
|
|
toc.tracks[track-1].sector_size = 2048;
|
|
toc.tracks[track-1].type = SECTOR_DATA_MODE1;
|
|
} else {
|
|
error = CUE_RES_INVALID;
|
|
}
|
|
mode = 0;
|
|
}
|
|
submode++;
|
|
break;
|
|
case MODE_PREGAP:
|
|
cue_parser_debugf("Pregap size: %s", word);
|
|
if (!ParseMSF(word, &msf)) {
|
|
error = CUE_RES_INVALID;
|
|
} else {
|
|
pregap += MSF2LBA(msf.m, msf.s, msf.f) + 150;
|
|
}
|
|
mode = 0;
|
|
break;
|
|
case MODE_INDEX:
|
|
if (submode == 0) {
|
|
cue_parser_debugf("Index: %s", word);
|
|
index = strtol(word, 0, 10);
|
|
} else if (submode == 1) {
|
|
if (!ParseMSF(word, &msf)) {
|
|
error = CUE_RES_INVALID;
|
|
} else {
|
|
lba = MSF2LBA(msf.m, msf.s, msf.f);
|
|
if (index == 0) {
|
|
if (track > 1 && !toc.tracks[track - 2].end) {
|
|
toc.tracks[track - 2].end = lba + 150 + pregap;
|
|
}
|
|
} else if (index == 1) {
|
|
toc.tracks[track - 1].start = lba + 150 + pregap;
|
|
if (track > 1) {
|
|
tracklen = lba - lastindex1;
|
|
toc.tracks[track - 1].offset = toc.tracks[track - 2].offset + (tracklen * toc.tracks[track - 2].sector_size);
|
|
if (!toc.tracks[track-2].end) toc.tracks[track - 2].end = toc.tracks[track - 1].start - 1;
|
|
} else {
|
|
toc.tracks[0].offset = (lba + 150) * toc.tracks[0].sector_size;
|
|
}
|
|
lastindex1 = lba;
|
|
}
|
|
}
|
|
cue_parser_debugf("Pos: (%s) = %d:%d:%d (%d)", word, msf.m, msf.s, msf.f, error);
|
|
mode = 0;
|
|
}
|
|
submode++;
|
|
break;
|
|
}
|
|
}
|
|
// if end of file or error, stop
|
|
if (word_status == CUE_EOT || error) break;
|
|
}
|
|
|
|
#ifdef CUE_PARSER_TEST
|
|
// close file
|
|
fclose(cue_fp);
|
|
#else
|
|
f_close(&cue_file);
|
|
#endif
|
|
}
|
|
|
|
#ifndef CUE_PARSET_TEST
|
|
if (!bin_valid)
|
|
error = CUE_RES_BINERR;
|
|
else if (error)
|
|
f_close(&toc.file->file);
|
|
else {
|
|
IDXIndex(toc.file);
|
|
if (track > 0) {
|
|
tracklen = (f_size(&toc.file->file) - toc.tracks[track - 1].offset) / toc.tracks[track - 1].sector_size;
|
|
toc.tracks[track - 1].end = toc.tracks[track - 1].start + tracklen;
|
|
}
|
|
}
|
|
#endif
|
|
if (error) {
|
|
toc.last = 0;
|
|
} else {
|
|
toc.last = track;
|
|
toc.end = toc.tracks[track-1].end;
|
|
toc.valid = 1;
|
|
}
|
|
|
|
iprintf("Tracks in the CUE file %s : %d\n", filename, toc.last);
|
|
for (int i = 0; i < toc.last ; i++) {
|
|
LBA2MSF(toc.tracks[i].start, &msf);
|
|
iprintf("Track %i, start: %d - %02d:%02d:%02d (%d) end: %d sector size:%d\n",
|
|
i+1, toc.tracks[i].start, msf.m, msf.s, msf.f, toc.tracks[i].offset, toc.tracks[i].end, toc.tracks[i].sector_size);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
int cue_gettrackbylba(int lba) {
|
|
int index = 0;
|
|
while ((toc.tracks[index].end <= lba) && (index < toc.last)) index++;
|
|
return index;
|
|
}
|