|
|
|
@@ -14,10 +14,12 @@
|
|
|
|
|
* Authors: Miran Grca, <mgrca8@gmail.com>
|
|
|
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
|
|
|
|
* The DOSBox Team, <unknown>
|
|
|
|
|
* Cacodemon345
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2016-2020 Miran Grca.
|
|
|
|
|
* Copyright 2017-2020 Fred N. van Kempen.
|
|
|
|
|
* Copyright 2002-2020 The DOSBox Team.
|
|
|
|
|
* Copyright 2024 Cacodemon345.
|
|
|
|
|
*/
|
|
|
|
|
#define __STDC_FORMAT_MACROS
|
|
|
|
|
#include <ctype.h>
|
|
|
|
@@ -40,6 +42,8 @@
|
|
|
|
|
#include <86box/plat.h>
|
|
|
|
|
#include <86box/cdrom_image_backend.h>
|
|
|
|
|
|
|
|
|
|
#include <sndfile.h>
|
|
|
|
|
|
|
|
|
|
#define CDROM_BCD(x) (((x) % 10) | (((x) / 10) << 4))
|
|
|
|
|
|
|
|
|
|
#define MAX_LINE_LENGTH 512
|
|
|
|
@@ -66,13 +70,109 @@ cdrom_image_backend_log(const char *fmt, ...)
|
|
|
|
|
# define cdrom_image_backend_log(fmt, ...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
typedef struct audio_file_t {
|
|
|
|
|
SNDFILE *file;
|
|
|
|
|
SF_INFO info;
|
|
|
|
|
} audio_file_t;
|
|
|
|
|
|
|
|
|
|
/* Audio file functions */
|
|
|
|
|
static int
|
|
|
|
|
audio_read(void *priv, uint8_t *buffer, uint64_t seek, size_t count)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
audio_file_t *audio = (audio_file_t *) tf->priv;
|
|
|
|
|
uint64_t samples_seek = seek / 4;
|
|
|
|
|
uint64_t samples_count = count / 4;
|
|
|
|
|
|
|
|
|
|
if ((seek & 3) || (count & 3)) {
|
|
|
|
|
cdrom_image_backend_log("CD Audio file: Reading on non-4-aligned boundaries.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sf_count_t res = sf_seek(audio->file, samples_seek, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
if (res == -1)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return !!sf_readf_short(audio->file, (short *) buffer, samples_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
|
audio_get_length(void *priv)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
audio_file_t *audio = (audio_file_t *) tf->priv;
|
|
|
|
|
|
|
|
|
|
/* Assume 16-bit audio, 2 channel. */
|
|
|
|
|
return audio->info.frames * 4ull;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
audio_close(void *priv)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
audio_file_t *audio = (audio_file_t *) tf->priv;
|
|
|
|
|
|
|
|
|
|
memset(tf->fn, 0x00, sizeof(tf->fn));
|
|
|
|
|
if (audio && audio->file)
|
|
|
|
|
sf_close(audio->file);
|
|
|
|
|
free(audio);
|
|
|
|
|
free(tf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static track_file_t *
|
|
|
|
|
audio_init(const char *filename, int *error)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) calloc(sizeof(track_file_t), 1);
|
|
|
|
|
audio_file_t *audio = (audio_file_t *) calloc(sizeof(audio_file_t), 1);
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
wchar_t filename_w[4096];
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (tf == NULL || audio == NULL) {
|
|
|
|
|
goto cleanup_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(tf->fn, 0x00, sizeof(tf->fn));
|
|
|
|
|
strncpy(tf->fn, filename, sizeof(tf->fn) - 1);
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
mbstowcs(filename_w, filename, 4096);
|
|
|
|
|
audio->file = sf_wchar_open(filename_w, SFM_READ, &audio->info);
|
|
|
|
|
#else
|
|
|
|
|
audio->file = sf_open(filename, SFM_READ, &audio->info);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (!audio->file) {
|
|
|
|
|
cdrom_image_backend_log("Audio file open error!");
|
|
|
|
|
goto cleanup_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (audio->info.channels != 2 || audio->info.samplerate != 44100 || !audio->info.seekable) {
|
|
|
|
|
cdrom_image_backend_log("Audio file not seekable or in non-CD format!");
|
|
|
|
|
sf_close(audio->file);
|
|
|
|
|
goto cleanup_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*error = 0;
|
|
|
|
|
tf->priv = audio;
|
|
|
|
|
tf->fp = NULL;
|
|
|
|
|
tf->close = audio_close;
|
|
|
|
|
tf->get_length = audio_get_length;
|
|
|
|
|
tf->read = audio_read;
|
|
|
|
|
return tf;
|
|
|
|
|
cleanup_error:
|
|
|
|
|
free(tf);
|
|
|
|
|
free(audio);
|
|
|
|
|
*error = 1;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Binary file functions. */
|
|
|
|
|
static int
|
|
|
|
|
bin_read(void *priv, uint8_t *buffer, uint64_t seek, size_t count)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf;
|
|
|
|
|
|
|
|
|
|
cdrom_image_backend_log("CDROM: binary_read(%08lx, pos=%" PRIu64 " count=%lu\n",
|
|
|
|
|
cdrom_image_backend_log("CDROM: binary_read(%08lx, pos=%" PRIu64 " count=%lu)\n",
|
|
|
|
|
tf->fp, seek, count);
|
|
|
|
|
|
|
|
|
|
if ((tf = (track_file_t *) priv)->fp == NULL)
|
|
|
|
@@ -92,6 +192,15 @@ bin_read(void *priv, uint8_t *buffer, uint64_t seek, size_t count)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (UNLIKELY(tf->motorola)) {
|
|
|
|
|
for (uint64_t i = 0; i < count; i += 2) {
|
|
|
|
|
uint8_t buffer0 = buffer[i];
|
|
|
|
|
uint8_t buffer1 = buffer[i + 1];
|
|
|
|
|
buffer[i] = buffer1;
|
|
|
|
|
buffer[i + 1] = buffer0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -279,12 +388,11 @@ int
|
|
|
|
|
cdi_get_audio_track_info(cd_img_t *cdi, UNUSED(int end), int track, int *track_num, TMSF *start, uint8_t *attr)
|
|
|
|
|
{
|
|
|
|
|
const track_t *trk = &cdi->tracks[track - 1];
|
|
|
|
|
const int pos = trk->start + 150;
|
|
|
|
|
|
|
|
|
|
if ((track < 1) || (track > cdi->tracks_num))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
const int pos = trk->start + 150;
|
|
|
|
|
|
|
|
|
|
FRAMES_TO_MSF(pos, &start->min, &start->sec, &start->fr);
|
|
|
|
|
|
|
|
|
|
*track_num = trk->track_number;
|
|
|
|
@@ -434,9 +542,7 @@ cdi_read_sectors(cd_img_t *cdi, uint8_t *buffer, int raw, uint32_t sector, uint3
|
|
|
|
|
break;
|
|
|
|
|
/* Based on the DOSBox patch, but check all 8 bytes and makes sure it's not an
|
|
|
|
|
audio track. */
|
|
|
|
|
if (raw && (sector < cdi->tracks[0].length) &&
|
|
|
|
|
!cdi->tracks[0].mode2 && (cdi->tracks[0].attr != AUDIO_TRACK) &&
|
|
|
|
|
*(uint64_t *) &(buf[(i * sector_size) + 2068]))
|
|
|
|
|
if (raw && (sector < cdi->tracks[0].length) && !cdi->tracks[0].mode2 && (cdi->tracks[0].attr != AUDIO_TRACK) && *(uint64_t *) &(buf[(i * sector_size) + 2068]))
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -538,70 +644,82 @@ cdi_track_push_back(cd_img_t *cdi, track_t *trk)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cdi_load_iso(cd_img_t *cdi, const char *filename)
|
|
|
|
|
cdi_get_iso_track(cd_img_t *cdi, track_t *trk, const char *filename)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
|
int ret = 2;
|
|
|
|
|
memset(trk, 0, sizeof(track_t));
|
|
|
|
|
|
|
|
|
|
/* Data track (shouldn't there be a lead in track?). */
|
|
|
|
|
trk->file = bin_init(filename, &error);
|
|
|
|
|
if (error) {
|
|
|
|
|
if ((trk->file != NULL) && (trk->file->close != NULL))
|
|
|
|
|
trk->file->close(trk->file);
|
|
|
|
|
ret = 3;
|
|
|
|
|
trk->file = viso_init(filename, &error);
|
|
|
|
|
if (error) {
|
|
|
|
|
if ((trk->file != NULL) && (trk->file->close != NULL))
|
|
|
|
|
trk->file->close(trk->file);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
trk->number = 1;
|
|
|
|
|
trk->track_number = 1;
|
|
|
|
|
trk->attr = DATA_TRACK;
|
|
|
|
|
|
|
|
|
|
/* Try to detect ISO type. */
|
|
|
|
|
trk->form = 0;
|
|
|
|
|
trk->mode2 = 0;
|
|
|
|
|
|
|
|
|
|
if (cdi_can_read_pvd(trk->file, RAW_SECTOR_SIZE, 0, 0))
|
|
|
|
|
trk->sector_size = RAW_SECTOR_SIZE;
|
|
|
|
|
else if (cdi_can_read_pvd(trk->file, 2336, 1, 0)) {
|
|
|
|
|
trk->sector_size = 2336;
|
|
|
|
|
trk->mode2 = 1;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk->file, 2324, 1, 2)) {
|
|
|
|
|
trk->sector_size = 2324;
|
|
|
|
|
trk->mode2 = 1;
|
|
|
|
|
trk->form = 2;
|
|
|
|
|
trk->noskip = 1;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk->file, 2328, 1, 2)) {
|
|
|
|
|
trk->sector_size = 2328;
|
|
|
|
|
trk->mode2 = 1;
|
|
|
|
|
trk->form = 2;
|
|
|
|
|
trk->noskip = 1;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk->file, 2336, 1, 1)) {
|
|
|
|
|
trk->sector_size = 2336;
|
|
|
|
|
trk->mode2 = 1;
|
|
|
|
|
trk->form = 1;
|
|
|
|
|
trk->skip = 8;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk->file, RAW_SECTOR_SIZE, 1, 0)) {
|
|
|
|
|
trk->sector_size = RAW_SECTOR_SIZE;
|
|
|
|
|
trk->mode2 = 1;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk->file, RAW_SECTOR_SIZE, 1, 1)) {
|
|
|
|
|
trk->sector_size = RAW_SECTOR_SIZE;
|
|
|
|
|
trk->mode2 = 1;
|
|
|
|
|
trk->form = 1;
|
|
|
|
|
} else {
|
|
|
|
|
/* We use 2048 mode 1 as the default. */
|
|
|
|
|
trk->sector_size = COOKED_SECTOR_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trk->length = trk->file->get_length(trk->file) / trk->sector_size;
|
|
|
|
|
cdrom_image_backend_log("ISO: Data track: length = %" PRIu64 ", sector_size = %i\n", trk->length, trk->sector_size);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cdi_load_iso(cd_img_t *cdi, const char *filename)
|
|
|
|
|
{
|
|
|
|
|
int ret = 2;
|
|
|
|
|
track_t trk;
|
|
|
|
|
|
|
|
|
|
cdi->tracks = NULL;
|
|
|
|
|
cdi->tracks_num = 0;
|
|
|
|
|
|
|
|
|
|
memset(&trk, 0, sizeof(track_t));
|
|
|
|
|
ret = cdi_get_iso_track(cdi, &trk, filename);
|
|
|
|
|
|
|
|
|
|
/* Data track (shouldn't there be a lead in track?). */
|
|
|
|
|
trk.file = bin_init(filename, &error);
|
|
|
|
|
if (error) {
|
|
|
|
|
if ((trk.file != NULL) && (trk.file->close != NULL))
|
|
|
|
|
trk.file->close(trk.file);
|
|
|
|
|
ret = 3;
|
|
|
|
|
trk.file = viso_init(filename, &error);
|
|
|
|
|
if (error) {
|
|
|
|
|
if ((trk.file != NULL) && (trk.file->close != NULL))
|
|
|
|
|
trk.file->close(trk.file);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
trk.number = 1;
|
|
|
|
|
trk.track_number = 1;
|
|
|
|
|
trk.attr = DATA_TRACK;
|
|
|
|
|
|
|
|
|
|
/* Try to detect ISO type. */
|
|
|
|
|
trk.form = 0;
|
|
|
|
|
trk.mode2 = 0;
|
|
|
|
|
|
|
|
|
|
if (cdi_can_read_pvd(trk.file, RAW_SECTOR_SIZE, 0, 0))
|
|
|
|
|
trk.sector_size = RAW_SECTOR_SIZE;
|
|
|
|
|
else if (cdi_can_read_pvd(trk.file, 2336, 1, 0)) {
|
|
|
|
|
trk.sector_size = 2336;
|
|
|
|
|
trk.mode2 = 1;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk.file, 2324, 1, 2)) {
|
|
|
|
|
trk.sector_size = 2324;
|
|
|
|
|
trk.mode2 = 1;
|
|
|
|
|
trk.form = 2;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk.file, 2328, 1, 2)) {
|
|
|
|
|
trk.sector_size = 2328;
|
|
|
|
|
trk.mode2 = 1;
|
|
|
|
|
trk.form = 2;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk.file, 2336, 1, 1)) {
|
|
|
|
|
trk.sector_size = 2336;
|
|
|
|
|
trk.mode2 = 1;
|
|
|
|
|
trk.form = 1;
|
|
|
|
|
trk.skip = 8;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk.file, RAW_SECTOR_SIZE, 1, 0)) {
|
|
|
|
|
trk.sector_size = RAW_SECTOR_SIZE;
|
|
|
|
|
trk.mode2 = 1;
|
|
|
|
|
} else if (cdi_can_read_pvd(trk.file, RAW_SECTOR_SIZE, 1, 1)) {
|
|
|
|
|
trk.sector_size = RAW_SECTOR_SIZE;
|
|
|
|
|
trk.mode2 = 1;
|
|
|
|
|
trk.form = 1;
|
|
|
|
|
} else {
|
|
|
|
|
/* We use 2048 mode 1 as the default. */
|
|
|
|
|
trk.sector_size = COOKED_SECTOR_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trk.length = trk.file->get_length(trk.file) / trk.sector_size;
|
|
|
|
|
cdrom_image_backend_log("ISO: Data track: length = %" PRIu64 ", sector_size = %i\n", trk.length, trk.sector_size);
|
|
|
|
|
if (ret >= 1) {
|
|
|
|
|
cdi_track_push_back(cdi, &trk);
|
|
|
|
|
|
|
|
|
|
/* Lead out track. */
|
|
|
|
@@ -612,6 +730,7 @@ cdi_load_iso(cd_img_t *cdi, const char *filename)
|
|
|
|
|
trk.length = 0;
|
|
|
|
|
trk.file = NULL;
|
|
|
|
|
cdi_track_push_back(cdi, &trk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
@@ -765,7 +884,7 @@ cdi_add_track(cd_img_t *cdi, track_t *cur, uint64_t *shift, uint64_t prestart, u
|
|
|
|
|
if (cur->number != 1)
|
|
|
|
|
return 0;
|
|
|
|
|
cur->skip = skip * cur->sector_size;
|
|
|
|
|
if ((cur->sector_size != RAW_SECTOR_SIZE) && (cur->form > 0))
|
|
|
|
|
if ((cur->sector_size != RAW_SECTOR_SIZE) && (cur->form > 0) && !cur->noskip)
|
|
|
|
|
cur->skip += 8;
|
|
|
|
|
cur->start += cur_pregap;
|
|
|
|
|
*total_pregap = cur_pregap;
|
|
|
|
@@ -789,7 +908,7 @@ cdi_add_track(cd_img_t *cdi, track_t *cur, uint64_t *shift, uint64_t prestart, u
|
|
|
|
|
|
|
|
|
|
cur->start += prev->start + prev->length + cur_pregap;
|
|
|
|
|
cur->skip = skip * cur->sector_size;
|
|
|
|
|
if ((cur->sector_size != RAW_SECTOR_SIZE) && (cur->form > 0))
|
|
|
|
|
if ((cur->sector_size != RAW_SECTOR_SIZE) && (cur->form > 0) && !cur->noskip)
|
|
|
|
|
cur->skip += 8;
|
|
|
|
|
*shift += prev->start + prev->length;
|
|
|
|
|
*total_pregap = cur_pregap;
|
|
|
|
@@ -819,6 +938,7 @@ cdi_load_cue(cd_img_t *cdi, const char *cuefile)
|
|
|
|
|
uint64_t total_pregap = 0ULL;
|
|
|
|
|
uint64_t frame = 0ULL;
|
|
|
|
|
uint64_t index;
|
|
|
|
|
int iso_file_used = 0;
|
|
|
|
|
int success;
|
|
|
|
|
int error;
|
|
|
|
|
int can_add_track = 0;
|
|
|
|
@@ -874,6 +994,20 @@ cdi_load_cue(cd_img_t *cdi, const char *cuefile)
|
|
|
|
|
if (!success)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (iso_file_used) {
|
|
|
|
|
/* We don't alter anything of the detected track type with the one specified in the CUE file, except its numbers. */
|
|
|
|
|
cur_pregap = 0;
|
|
|
|
|
prestart = 0;
|
|
|
|
|
|
|
|
|
|
trk.number = cdi_cue_get_number(&line);
|
|
|
|
|
trk.track_number = trk.number;
|
|
|
|
|
success = cdi_cue_get_keyword(&type, &line);
|
|
|
|
|
if (!success)
|
|
|
|
|
break;
|
|
|
|
|
can_add_track = 1;
|
|
|
|
|
|
|
|
|
|
iso_file_used = 0;
|
|
|
|
|
} else {
|
|
|
|
|
trk.start = 0;
|
|
|
|
|
trk.skip = 0;
|
|
|
|
|
cur_pregap = 0;
|
|
|
|
@@ -950,6 +1084,7 @@ cdi_load_cue(cd_img_t *cdi, const char *cuefile)
|
|
|
|
|
success = 0;
|
|
|
|
|
|
|
|
|
|
can_add_track = 1;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(command, "INDEX")) {
|
|
|
|
|
index = cdi_cue_get_number(&line);
|
|
|
|
|
success = cdi_cue_get_frame(&frame, &line);
|
|
|
|
@@ -992,13 +1127,40 @@ cdi_load_cue(cd_img_t *cdi, const char *cuefile)
|
|
|
|
|
trk.file = NULL;
|
|
|
|
|
error = 1;
|
|
|
|
|
|
|
|
|
|
if (!strcmp(type, "BINARY")) {
|
|
|
|
|
if (!strcmp(type, "BINARY") || !strcmp(type, "MOTOROLA")) {
|
|
|
|
|
int fn_len = 0;
|
|
|
|
|
if (!path_abs(ansi)) {
|
|
|
|
|
path_append_filename(filename, pathname, ansi);
|
|
|
|
|
} else {
|
|
|
|
|
strcpy(filename, ansi);
|
|
|
|
|
}
|
|
|
|
|
fn_len = strlen(filename);
|
|
|
|
|
if ((tolower((int) filename[fn_len - 1]) == 'o'
|
|
|
|
|
&& tolower((int) filename[fn_len - 2]) == 's'
|
|
|
|
|
&& tolower((int) filename[fn_len - 3]) == 'i'
|
|
|
|
|
&& filename[fn_len - 4] == '.')
|
|
|
|
|
|| plat_dir_check(filename)) {
|
|
|
|
|
error = !cdi_get_iso_track(cdi, &trk, filename);
|
|
|
|
|
if (!error) {
|
|
|
|
|
iso_file_used = 1;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
trk.file = track_file_init(filename, &error);
|
|
|
|
|
|
|
|
|
|
if (trk.file) {
|
|
|
|
|
trk.file->motorola = !strcmp(type, "MOTOROLA");
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(type, "WAVE") || !strcmp(type, "AIFF") || !strcmp(type, "MP3")) {
|
|
|
|
|
if (!path_abs(ansi)) {
|
|
|
|
|
path_append_filename(filename, pathname, ansi);
|
|
|
|
|
} else {
|
|
|
|
|
strcpy(filename, ansi);
|
|
|
|
|
}
|
|
|
|
|
trk.file = audio_init(filename, &error);
|
|
|
|
|
}
|
|
|
|
|
if (error) {
|
|
|
|
|
#ifdef ENABLE_CDROM_IMAGE_BACKEND_LOG
|
|
|
|
|
cdrom_image_backend_log("CUE: cannot open fille '%s' in cue sheet!\n",
|
|
|
|
|
cdrom_image_backend_log("CUE: cannot open file '%s' in cue sheet!\n",
|
|
|
|
|
filename);
|
|
|
|
|
#endif
|
|
|
|
|
if (trk.file != NULL) {
|
|
|
|
@@ -1067,7 +1229,7 @@ cdi_has_audio_track(cd_img_t *cdi)
|
|
|
|
|
if ((cdi == NULL) || (cdi->tracks == NULL))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Audio track has attribute 0x14. */
|
|
|
|
|
/* Audio track has attribute 0x10. */
|
|
|
|
|
for (int i = 0; i < cdi->tracks_num; i++) {
|
|
|
|
|
if (cdi->tracks[i].attr == AUDIO_TRACK)
|
|
|
|
|
return 1;
|
|
|
|
|