mirror of
https://github.com/libretro/opera-libretro.git
synced 2026-11-07 04:25:58 -10:00
cuefile: parse multi-file track metadata
This commit is contained in:
1 parent
8f5d03b100
commit
079b8bcf1f
2 files changed
+237
-122
No files matched your search
@@ -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")));
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in new issue
Block a user