shithub: psxe

Download patch

ref: b259b105d7df69edf839bef1100904fbe6a688a2
parent: 16d73aa74e7c26ec2e4e900b452e7bb0c6d778fe
author: allkern <lisandroaalarcon@gmail.com>
date: Sat Aug 26 15:53:42 EDT 2023

Implement CUE parser

--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,5 @@
 *.exe
 *.out
 *.toml
-*.zip
\ No newline at end of file
+*.zip
+*.cue
\ No newline at end of file
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@
 SOURCES := $(wildcard psx/*.c)
 SOURCES += $(wildcard psx/dev/*.c)
 SOURCES += $(wildcard psx/input/*.c)
+SOURCES += $(wildcard psx/disc/*.c)
 SOURCES += $(wildcard frontend/*.c)
 
 bin/psxe frontend/main.c:
--- a/build-win32.ps1
+++ b/build-win32.ps1
@@ -15,6 +15,7 @@
     "psx\*.c" `
     "psx\dev\*.c" `
     "psx\input\*.c" `
+    "psx\disc\*.c" `
     "frontend\*.c" `
     -o "bin\psxe.exe" `
     -DREP_VERSION="`"$($VERSION_TAG)`"" `
--- a/build-win64.ps1
+++ b/build-win64.ps1
@@ -15,6 +15,7 @@
     "psx\*.c" `
     "psx\dev\*.c" `
     "psx\input\*.c" `
+    "psx\disc\*.c" `
     "frontend\*.c" `
     -o "bin\psxe.exe" `
     -DREP_VERSION="`"$($VERSION_TAG)`"" `
--- a/psx/disc/cue.c
+++ b/psx/disc/cue.c
@@ -10,17 +10,73 @@
 #include <ctype.h>
 
 #include "cue.h"
+#include "../disc.h"
+#include "../log.h"
 
-int cue_get_keyword(char* buf) {
+static const char* g_psxd_cue_tokens[] = {
+    "4CH",
+    "AIFF",
+    "AUDIO",
+    "BINARY",
+    "CATALOG",
+    "CDG",
+    "CDI_2336",
+    "CDI_2352",
+    "CDTEXTFILE",
+    "DCP",
+    "FILE",
+    "FLAGS",
+    "INDEX",
+    "ISRC",
+    "MODE1_2048",
+    "MODE1_2352",
+    "MODE2_2336",
+    "MODE2_2352",
+    "MOTOROLA",
+    "MP3",
+    "PERFORMER",
+    "POSTGAP",
+    "PRE",
+    "PREGAP",
+    "REM",
+    "SCMS",
+    "SONGWRITER",
+    "TITLE",
+    "TRACK",
+    "WAVE",
+    0
+};
+
+#define EXPECT_KEYWORD(kw) \
+    if (cue_parse_keyword(cue)) \
+        return cue->error; \
+    if (cue_get_keyword(cue) != kw) \
+        ERROR_OUT(PE_UNEXPECTED_TOKEN);
+
+void cue_add_track(psxd_cue_t* cue) {
+    ++cue->numtracks;
+
+    cue->track = realloc(cue->track, cue->numtracks * sizeof(cue_track_t*));
+    cue->track[cue->numtracks - 1] = malloc(sizeof(cue_track_t));
+
+    memset(cue->track[cue->numtracks - 1], 0, sizeof(cue_track_t));
+}
+
+void cue_ignore_whitespace(psxd_cue_t* cue) {
+    while (isspace(cue->c))
+        cue->c = fgetc(cue->file);
+}
+
+int cue_get_keyword(psxd_cue_t* cue) {
     int i = 0;
 
     const char* token = g_psxd_cue_tokens[i];
 
     while (token) {
-        if (strcmp(token, buf) == 1) {
+        if (strcmp(token, cue->buf) == 1) {
             return i;
         } else {
-            ++i;
+            token = g_psxd_cue_tokens[++i];
         }
     }
 
@@ -27,50 +83,197 @@
     return -1;
 }
 
-int cue_parse_keyword(FILE* file, char* c, char* buf) {
-    char* ptr = buf;
+#define ERROR_OUT(err) \
+    { cue->error = err; return err; }
 
-    if (!isalpha(*c))
-        return PE_EXPECTED_KEYWORD;
+int cue_parse_keyword(psxd_cue_t* cue) {
+    if (!isalpha(cue->c))
+        ERROR_OUT(PE_EXPECTED_KEYWORD);
     
-    while (isalpha(*c)) {
-        fread(c, 1, 1, file);
+    while (isalnum(cue->c) || (cue->c == '/')) {
+        *cue->ptr++ = cue->c;
 
-        *ptr++ = *c;
+        cue->c = fgetc(cue->file);
     }
 
-    *ptr = 0;
+    *cue->ptr = 0;
 
+    cue->ptr = cue->buf;
+
+    cue_ignore_whitespace(cue);
+
     return 0;
 }
 
+int cue_parse_string(psxd_cue_t* cue) {
+    if (cue->c != '\"')
+        ERROR_OUT(PE_EXPECTED_STRING);
+
+    cue->c = fgetc(cue->file);
+
+    while (cue->c != '\"') {
+        *cue->ptr++ = cue->c;
+
+        cue->c = fgetc(cue->file);
+    }
+
+    *cue->ptr = 0;
+
+    cue->c = fgetc(cue->file);
+
+    cue->ptr = cue->buf;
+
+    cue_ignore_whitespace(cue);
+
+    return 0;
+}
+
+int cue_parse_number(psxd_cue_t* cue) {
+    if (!isdigit(cue->c))
+        ERROR_OUT(PE_EXPECTED_NUMBER);
+    
+    while (isdigit(cue->c)) {
+        *cue->ptr++ = cue->c;
+
+        cue->c = fgetc(cue->file);
+    }
+
+    *cue->ptr = 0;
+
+    cue->ptr = cue->buf;
+
+    cue_ignore_whitespace(cue);
+
+    return 0;
+}
+
+int cue_parse_msf(psxd_cue_t* cue, msf_t* msf) {
+    if (cue_parse_number(cue))
+        return cue->error;
+    
+    if (cue->c != ':')
+        ERROR_OUT(PE_EXPECTED_COLON);
+    
+    cue->c = fgetc(cue->file);
+
+    msf->m = atoi(cue->buf);
+
+    if (cue_parse_number(cue))
+        return cue->error;
+
+    if (cue->c != ':')
+        ERROR_OUT(PE_EXPECTED_COLON);
+    
+    cue->c = fgetc(cue->file);
+    
+    msf->s = atoi(cue->buf);
+
+    if (cue_parse_number(cue))
+        return cue->error;
+    
+    msf->f = atoi(cue->buf);
+
+    return 0;
+}
+
 int cue_parse(psxd_cue_t* cue, FILE* file) {
-    int state = CUE_NONE;
+    cue->file = file;
+    cue->c = fgetc(file);
 
-    char c, buf[128];
-    char* ptr = buf;
-    int s;
+    void* filebuf;
+    size_t filesz;
+    msf_t msf;
+
+    EXPECT_KEYWORD(CUE_FILE);
+
+    parse_file:
+
+    if (cue_parse_string(cue))
+        return cue->error;
+
+    // Open file, get size and seek to 0
+    FILE* trackfile = fopen(cue->buf, "rb");
+
+    fseek(trackfile, 0, SEEK_END);
+
+    filesz = ftell(trackfile);
+
+    fseek(trackfile, 0, SEEK_SET);
+
+    // If we have to preload the disc image
+    // then copy data to our filebuf and close
+    // the file. Otherwise, our filebuf contains
+    // a pointer to the open FILE.
+    if (cue->preload) {
+        filebuf = malloc(filesz);
+
+        fread(filebuf, 1, filesz, trackfile);
+
+        fclose(trackfile);
+    } else {
+        filebuf = trackfile;
+    }
+
+    strcpy(cue->current_file, cue->buf);
+
+    EXPECT_KEYWORD(CUE_BINARY);
+    EXPECT_KEYWORD(CUE_TRACK);
+
+    if (cue_parse_number(cue))
+        return cue->error;
     
-    c = fgetc(file);
+    int track = atoi(cue->buf) - 1;
+    
+    if (track != cue->numtracks)
+        ERROR_OUT(PE_NON_SEQUENTIAL_TRACKS);
+    
+    cue_add_track(cue);
 
-    while (!feof(file)) {
-        s = cue_parse_keyword(file, &c, buf);
+    cue->track[track]->buf = filebuf;
+    cue->track[track]->size = filesz;
+    cue->track[track]->filename = malloc(strlen(cue->current_file));
 
-        if (s)
-            return s;
-        
-        switch (cue_get_keyword(buf)) {
-            case CUE_FILE: {
-                
-            } break;
+    strcpy(cue->track[track]->filename, cue->current_file);
+
+    if (cue_parse_keyword(cue))
+        return cue->error;
 
-            default: {
-                return PE_UNEXPECTED_TOKEN;
-            } break;
-        }
+    cue->track[track]->type = cue_get_keyword(cue);
+
+    // Expecting at least 1 index
+    EXPECT_KEYWORD(CUE_INDEX);
+
+    parse_index:
+
+    if (cue_parse_number(cue))
+        return cue->error;
+    
+    int index = atoi(cue->buf);
+
+    if (cue_parse_msf(cue, &msf))
+        return cue->error;
+
+    cue->track[track]->index[index] = msf;
+
+    if (feof(cue->file)) {
+        fclose(file);
+
+        return 0;
     }
 
-    fclose(file);
+    if (cue_parse_keyword(cue))
+        return cue->error;
+    
+    switch (cue_get_keyword(cue)) {
+        case CUE_INDEX:
+            goto parse_index;
+
+        case CUE_FILE:
+            goto parse_file;
+
+        default:
+            ERROR_OUT(PE_UNEXPECTED_TOKEN);
+    }
 }
 
 psxd_cue_t* psxd_cue_create() {
@@ -77,12 +280,23 @@
     return (psxd_cue_t*)malloc(sizeof(psxd_cue_t));
 }
 
-void psxd_cue_init(psxd_cue_t* cue) {
+void psxd_cue_init(psxd_cue_t* cue, int preload) {
     memset(cue, 0, sizeof(psxd_cue_t));
+
+    cue->buf = malloc(CUE_BUF_SIZE);
+    cue->ptr = cue->buf;
+    cue->preload = preload;
+    cue->current_file = malloc(CUE_BUF_SIZE);
 }
 
 void psxd_cue_load(psxd_cue_t* cue, const char* path) {
-    
+    FILE* file = fopen(path, "rb");
+
+    cue_parse(cue, file);
+
+    if (cue->error) {
+        log_fatal("CUE error %u", cue->error);
+    }
 }
 
 void psxd_cue_init_disc(psxd_cue_t* cue, psx_disc_t* disc) {
@@ -90,5 +304,7 @@
 }
 
 void psxd_cue_destroy(psxd_cue_t* cue) {
+    free(cue->current_file);
+    free(cue->buf);
     free(cue);
 }
\ No newline at end of file
--- a/psx/disc/cue.h
+++ b/psx/disc/cue.h
@@ -7,10 +7,20 @@
 #ifndef CUE_H
 #define CUE_H
 
+#define CUE_BUF_SIZE 256
+
 #include "../disc.h"
 
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+
 enum {
     PE_EXPECTED_KEYWORD = 1,
+    PE_EXPECTED_STRING,
+    PE_EXPECTED_NUMBER,
+    PE_EXPECTED_COLON,
+    PE_NON_SEQUENTIAL_TRACKS,
     PE_UNEXPECTED_TOKEN
 };
 
@@ -48,45 +58,28 @@
     CUE_WAVE
 };
 
-static const char* g_psxd_cue_tokens[] = {
-    "4CH",
-    "AIFF",
-    "AUDIO",
-    "BINARY",
-    "CATALOG",
-    "CDG",
-    "CDI_2336",
-    "CDI_2352",
-    "CDTEXTFILE",
-    "DCP",
-    "FILE",
-    "FLAGS",
-    "INDEX",
-    "ISRC",
-    "MODE1_2048",
-    "MODE1_2352",
-    "MODE2_2336",
-    "MODE2_2352",
-    "MOTOROLA",
-    "MP3",
-    "PERFORMER",
-    "POSTGAP",
-    "PRE",
-    "PREGAP",
-    "REM",
-    "SCMS",
-    "SONGWRITER",
-    "TITLE",
-    "TRACK",
-    "WAVE"
-};
-
 typedef struct {
+    char* filename;
+    int type;
+    void* buf;
+    msf_t index[2];
+    size_t size;
+} cue_track_t;
 
+typedef struct {
+    int preload;
+    char* buf;
+    char* ptr;
+    char c;
+    int error;
+    FILE* file;
+    int numtracks;
+    cue_track_t** track;
+    char* current_file;
 } psxd_cue_t;
 
 psxd_cue_t* psxd_cue_create();
-void psxd_cue_init(psxd_cue_t*);
+void psxd_cue_init(psxd_cue_t*, int);
 void psxd_cue_load(psxd_cue_t*, const char*);
 void psxd_cue_init_disc(psxd_cue_t*, psx_disc_t*);
 void psxd_cue_destroy(psxd_cue_t*);
--