cuefile: parse multi-file track metadata

This commit is contained in:
Antonio SJ Musumeci authored and trapexit committed 2026-06-02 15:11:40 -05:00
1 parent 8f5d03b100
commit 079b8bcf1f
2 files changed
+237 -122

No files matched your search

+212 -119
View File
@@ -11,164 +11,257 @@
#define STRING_MAX 4096
static RFILE *cue_get_file_for_image(const char *path)
static
RFILE*
cue_get_file_for_image(const char *path)
{
int i;
char cue_path_base[STRING_MAX];
char cue_path[8192];
char *exts[] = {".cue", ".CUE"};
char *last_dot = NULL;
int i;
char cue_path_base[STRING_MAX];
char cue_path[8192];
char *exts[] = {".cue", ".CUE"};
char *last_dot = NULL;
strcpy(cue_path_base, path);
strcpy(cue_path_base, path);
last_dot = strrchr(cue_path_base, '.');
if (last_dot == NULL)
return NULL;
last_dot = strrchr(cue_path_base, '.');
if(last_dot == NULL)
return NULL;
/* cut original extension */
*(last_dot) = '\0';
/* cut original extension */
*(last_dot) = '\0';
for(i=0; i<2; i++)
{
for(i=0; i<2; i++)
{
RFILE *cue_file = NULL;
strcpy(cue_path, cue_path_base);
strcat(cue_path, exts[i]);
cue_file = filestream_open(cue_path, RETRO_VFS_FILE_ACCESS_READ, 0);
if (cue_file)
return cue_file;
}
if(cue_file)
return cue_file;
}
return NULL;
return NULL;
}
static void str_to_upper(char *s)
static
void
str_to_upper(char *s)
{
for ( ; *s; ++s) *s = toupper(*s);
for( ; *s; ++s)
*s = toupper((unsigned char)*s);
}
static char *extract_file_name(const char *path, char *line)
static
char*
extract_file_name(const char *path,
char *line)
{
char file[STRING_MAX];
char base_path[STRING_MAX];
char cd_image[STRING_MAX];
char *last_separator = NULL;
char file[STRING_MAX];
char base_path[STRING_MAX];
char cd_image[STRING_MAX];
char *last_separator = NULL;
#ifdef _WIN32
char slash = '\\';
char slash = '\\';
#else
char slash = '/';
char slash = '/';
#endif
char *file_name_end = NULL;
char *file_name_start = strstr(line, "\"");
if (!file_name_start)
{
if (retro_log_printf_cb)
retro_log_printf_cb(RETRO_LOG_INFO, "[Opera]: Missing quotes in : %s\n", line);
char *file_name_end = NULL;
char *file_name_start = strstr(line, "\"");
if(!file_name_start)
{
if(retro_log_printf_cb)
retro_log_printf_cb(RETRO_LOG_INFO, "[Opera]: Missing quotes in : %s\n", line);
return NULL;
}
}
strcpy(file, ++file_name_start);
file_name_end = strstr(file, "\"");
strcpy(file, ++file_name_start);
file_name_end = strstr(file, "\"");
if (!file_name_end)
{
if (retro_log_printf_cb)
retro_log_printf_cb(RETRO_LOG_INFO, "[Opera]: Missing end quote in : %s\n", line);
if(!file_name_end)
{
if(retro_log_printf_cb)
retro_log_printf_cb(RETRO_LOG_INFO, "[Opera]: Missing end quote in : %s\n", line);
return NULL;
}
}
*file_name_end = '\0';
*file_name_end = '\0';
strcpy(base_path, path);
strcpy(base_path, path);
last_separator = strrchr(base_path, slash);
*last_separator = '\0';
last_separator = strrchr(base_path, slash);
if(last_separator)
{
*last_separator = '\0';
sprintf(cd_image, "%s%c%s", base_path, slash, file);
}
else
strcpy(cd_image, file);
sprintf(cd_image, "%s%c%s", base_path, slash, file);
return strdup(cd_image);
return strdup(cd_image);
}
cueFile *cue_get(const char *path)
static
uint32_t
cue_msf_to_lba(const char *msf_)
{
char line[STRING_MAX];
int files_found = 0;
cueFile *cue = NULL;
RFILE *cue_file =
cue_is_cue_path(path) ?
filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, 0) :
cue_get_file_for_image(path);
uint32_t min;
uint32_t sec;
uint32_t frame;
if (!cue_file)
return NULL;
if(sscanf(msf_,"%u:%u:%u",&min,&sec,&frame) != 3)
return 0;
cue = (cueFile *)malloc(sizeof(cueFile));
cue->cd_format = CUE_MODE_UNKNOWN;
while ((filestream_gets(cue_file, line, STRING_MAX)))
{
if (strstr(line, "FILE") && files_found == 0)
{
char *cd_image = extract_file_name(path, line);
if (cd_image)
{
files_found++;
cue->cd_image = cd_image;
}
}
str_to_upper(line);
if (strstr(line, "TRACK 01"))
{
if (strstr(line,"TRACK 01 MODE1/2048"))
cue->cd_format = MODE1_2048;
else if (strstr(line, "TRACK 01 MODE1/2352"))
cue->cd_format = MODE1_2352;
else if (strstr(line, "TRACK 01 MODE2/2352"))
cue->cd_format = MODE2_2352;
else
{
if (retro_log_printf_cb)
retro_log_printf_cb(RETRO_LOG_INFO, "[Opera]: Unknown file format in CUE file: %s -> %s", path, line);
}
break;
}
}
filestream_close(cue_file);
if (retro_log_printf_cb)
{
retro_log_printf_cb(RETRO_LOG_INFO, "[Opera]: CD image file in CUE: %s",
cue->cd_image ? cue->cd_image : "Not found");
}
if (cue->cd_format != CUE_MODE_UNKNOWN)
return cue;
free(cue);
return NULL;
return ((min * 60 * 75) + (sec * 75) + frame);
}
const char *cue_get_cd_format_name(CD_format cd_format)
cueFile*
cue_get(const char *path)
{
switch (cd_format)
{
case MODE1_2048:
return "MODE1/2048";
case MODE1_2352:
return "MODE1/2352";
case MODE2_2352:
return "MODE2/2352";
default:
break;
}
char line[STRING_MAX];
char line_upper[STRING_MAX];
int current_file = -1;
int current_track = -1;
cueFile *cue = NULL;
RFILE *cue_file =
cue_is_cue_path(path) ?
filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, 0) :
cue_get_file_for_image(path);
return "UNKNOWN";
if(!cue_file)
return NULL;
cue = (cueFile *)calloc(1,sizeof(cueFile));
cue->cd_format = CUE_MODE_UNKNOWN;
while ((filestream_gets(cue_file, line, STRING_MAX)))
{
char *track_ptr;
char *index_ptr;
strcpy(line_upper,line);
str_to_upper(line_upper);
if(strstr(line_upper, "FILE "))
{
char *cd_image = extract_file_name(path, line);
if(cd_image)
{
if(cue->num_files < CUE_MAX_FILES)
{
cue->files[cue->num_files].path = cd_image;
current_file = cue->num_files;
if(cue->cd_image == NULL)
cue->cd_image = cd_image;
cue->num_files++;
}
else
free(cd_image);
}
}
track_ptr = strstr(line_upper, "TRACK ");
if(track_ptr)
{
uint32_t track_num;
char *mode_ptr;
track_num = (uint32_t)strtoul(track_ptr + 6, NULL, 10);
if(track_num > 0 && track_num <= CUE_MAX_TRACKS && cue->num_tracks < CUE_MAX_TRACKS)
{
current_track = cue->num_tracks;
memset(&cue->tracks[current_track],0,sizeof(cue->tracks[current_track]));
cue->tracks[current_track].track_num = (uint8_t)track_num;
cue->tracks[current_track].file_index = (current_file >= 0) ?
(uint8_t)current_file : 0;
cue->tracks[current_track].format = CUE_MODE_UNKNOWN;
mode_ptr = track_ptr + 8;
while(*mode_ptr && isspace((unsigned char)*mode_ptr))
mode_ptr++;
if(strstr(mode_ptr,"MODE1/2048"))
cue->tracks[current_track].format = MODE1_2048;
else if(strstr(mode_ptr,"MODE1/2352"))
cue->tracks[current_track].format = MODE1_2352;
else if(strstr(mode_ptr,"MODE2/2352"))
cue->tracks[current_track].format = MODE2_2352;
else if(strstr(mode_ptr,"AUDIO"))
cue->tracks[current_track].format = CUE_MODE_AUDIO;
}
}
index_ptr = strstr(line_upper, "INDEX 01");
if(index_ptr && current_track == cue->num_tracks && cue->num_tracks < CUE_MAX_TRACKS)
{
char *msf_ptr = index_ptr + 8;
while(*msf_ptr && !isdigit((unsigned char)*msf_ptr))
msf_ptr++;
cue->tracks[cue->num_tracks].index_lba = cue_msf_to_lba(msf_ptr);
cue->num_tracks++;
current_track = -1;
}
}
filestream_close(cue_file);
if(cue->num_tracks > 0 && cue->cd_format == CUE_MODE_UNKNOWN)
cue->cd_format = cue->tracks[0].format;
if(retro_log_printf_cb)
{
retro_log_printf_cb(RETRO_LOG_INFO, "[Opera]: CD image files in CUE: %d first=%s\n",
cue->num_files,
cue->cd_image ? cue->cd_image : "Not found");
}
if((cue->cd_format != CUE_MODE_UNKNOWN || cue->num_tracks > 0) &&
cue->num_files > 0)
return cue;
cue_free(cue);
return NULL;
}
int cue_is_cue_path(const char *path)
void
cue_free(cueFile *cue)
{
char *dot = strrchr(path, '.');
return (dot && (!strcmp(dot, ".cue") || !strcmp(dot, ".CUE")));
int i;
if(!cue)
return;
for(i = 0; i < cue->num_files; i++)
free(cue->files[i].path);
free(cue);
}
const
char*
cue_get_cd_format_name(CD_format cd_format)
{
switch (cd_format)
{
case MODE1_2048:
return "MODE1/2048";
case MODE1_2352:
return "MODE1/2352";
case MODE2_2352:
return "MODE2/2352";
case CUE_MODE_AUDIO:
return "AUDIO";
default:
break;
}
return "UNKNOWN";
}
int
cue_is_cue_path(const char *path)
{
const char *dot = strrchr(path, '.');
return (dot && (!strcmp(dot, ".cue") ||
!strcmp(dot, ".CUE")));
}
+25 -3
View File
@@ -1,6 +1,8 @@
#ifndef CUEFILE_H__
#define CUEFILE_H__
#include <stdint.h>
#define SECTOR_SIZE_2048 2048
#define SECTOR_SIZE_2352 2352
@@ -8,14 +10,34 @@
#define SECTOR_OFFSET_MODE1_2352 16
#define SECTOR_OFFSET_MODE2_2352 24
typedef enum {MODE1_2048, MODE1_2352, MODE2_2352, CUE_MODE_UNKNOWN} CD_format;
#define CUE_MAX_TRACKS 100
#define CUE_MAX_FILES CUE_MAX_TRACKS
typedef enum {MODE1_2048, MODE1_2352, MODE2_2352, CUE_MODE_AUDIO, CUE_MODE_UNKNOWN} CD_format;
typedef struct {
CD_format cd_format;
char * cd_image;
uint8_t track_num;
uint8_t file_index;
CD_format format;
uint32_t index_lba;
uint32_t frames;
} cue_track_t;
typedef struct {
char *path;
} cue_file_t;
typedef struct {
CD_format cd_format;
char * cd_image;
int num_files;
cue_file_t files[CUE_MAX_FILES];
int num_tracks;
cue_track_t tracks[CUE_MAX_TRACKS];
} cueFile;
cueFile *cue_get(const char *path);
void cue_free(cueFile *cue);
const char *cue_get_cd_format_name(CD_format cd_format);
int cue_is_cue_path(const char *path);