2018-04-25 23:51:13 +02:00
|
|
|
/*
|
2022-11-13 16:37:58 -05:00
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
2018-04-25 23:51:13 +02:00
|
|
|
*
|
2022-11-13 16:37:58 -05:00
|
|
|
* This file is part of the 86Box distribution.
|
2018-04-25 23:51:13 +02:00
|
|
|
*
|
2025-01-28 16:26:28 +01:00
|
|
|
* CD-ROM image file handling module.
|
2018-04-25 23:51:13 +02:00
|
|
|
*
|
2025-01-28 16:26:28 +01:00
|
|
|
* Authors: Miran Grca, <mgrca8@gmail.com>
|
|
|
|
|
* RichardG, <richardg867@gmail.com>
|
|
|
|
|
* Cacodemon345
|
2018-04-25 23:51:13 +02:00
|
|
|
*
|
2025-01-28 16:26:28 +01:00
|
|
|
* Copyright 2016-2025 Miran Grca.
|
|
|
|
|
* Copyright 2016-2025 RichardG.
|
|
|
|
|
* Copyright 2024-2025 Cacodemon345.
|
2018-04-25 23:51:13 +02:00
|
|
|
*/
|
2025-01-28 16:26:28 +01:00
|
|
|
#define __STDC_FORMAT_MACROS
|
|
|
|
|
#include <ctype.h>
|
2020-01-17 03:02:12 +01:00
|
|
|
#include <inttypes.h>
|
2025-07-24 12:08:58 +02:00
|
|
|
// #define ENABLE_IMAGE_LOG 1
|
2025-01-28 16:26:28 +01:00
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
2018-01-13 22:56:13 +01:00
|
|
|
#include <stdarg.h>
|
2025-01-28 16:26:28 +01:00
|
|
|
#endif
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <stdint.h>
|
2025-01-28 16:26:28 +01:00
|
|
|
#include <stdio.h>
|
2017-10-12 14:25:17 -04:00
|
|
|
#include <stdlib.h>
|
2025-01-28 16:26:28 +01:00
|
|
|
#include <string.h>
|
2025-07-24 12:08:58 +02:00
|
|
|
#include <wchar.h>
|
2025-01-28 16:26:28 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
# include <libgen.h>
|
|
|
|
|
#endif
|
2020-03-29 14:24:42 +02:00
|
|
|
#include <86box/86box.h>
|
2025-01-28 16:26:28 +01:00
|
|
|
#include <86box/log.h>
|
2022-04-19 23:06:39 +02:00
|
|
|
#include <86box/path.h>
|
2020-03-29 14:24:42 +02:00
|
|
|
#include <86box/plat.h>
|
|
|
|
|
#include <86box/cdrom.h>
|
|
|
|
|
#include <86box/cdrom_image.h>
|
2025-01-28 16:26:28 +01:00
|
|
|
#include <86box/cdrom_image_viso.h>
|
|
|
|
|
|
|
|
|
|
#include <sndfile.h>
|
|
|
|
|
|
|
|
|
|
#define MAX_LINE_LENGTH 512
|
|
|
|
|
#define MAX_FILENAME_LENGTH 256
|
|
|
|
|
#define CROSS_LEN 512
|
|
|
|
|
|
|
|
|
|
static char temp_keyword[1024];
|
|
|
|
|
|
|
|
|
|
#define INDEX_SPECIAL -2 /* Track A0h onwards. */
|
|
|
|
|
#define INDEX_NONE -1 /* Empty block. */
|
|
|
|
|
#define INDEX_ZERO 0 /* Block not in the file, return all 0x00's. */
|
|
|
|
|
#define INDEX_NORMAL 1 /* Block in the file. */
|
|
|
|
|
|
|
|
|
|
typedef struct track_index_t {
|
|
|
|
|
/*
|
|
|
|
|
Is the current block in the file? If not, return all 0x00's. -1 means not
|
|
|
|
|
yet loaded.
|
|
|
|
|
*/
|
|
|
|
|
int32_t type;
|
|
|
|
|
/* The amount of bytes to skip at the beginning of each sector. */
|
|
|
|
|
int32_t skip;
|
|
|
|
|
/*
|
|
|
|
|
Starting and ending sector LBA - negative in order to accomodate LBA -150 to -1
|
|
|
|
|
to read the pregap of track 1.
|
|
|
|
|
*/
|
|
|
|
|
uint64_t start;
|
|
|
|
|
uint64_t length;
|
|
|
|
|
uint64_t file_start;
|
|
|
|
|
uint64_t file_length;
|
|
|
|
|
track_file_t *file;
|
|
|
|
|
} track_index_t;
|
|
|
|
|
|
|
|
|
|
typedef struct track_t {
|
|
|
|
|
uint8_t session;
|
|
|
|
|
uint8_t attr;
|
|
|
|
|
uint8_t tno;
|
|
|
|
|
uint8_t point;
|
|
|
|
|
uint8_t extra[4];
|
|
|
|
|
uint8_t mode;
|
|
|
|
|
uint8_t form;
|
|
|
|
|
uint8_t subch_type;
|
|
|
|
|
uint8_t skip;
|
|
|
|
|
uint32_t sector_size;
|
|
|
|
|
track_index_t idx[3];
|
|
|
|
|
} track_t;
|
|
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
/*
|
|
|
|
|
MDS for DVD has the disc structure table - 4 byte pointer to BCA,
|
|
|
|
|
followed by the copyright, DMI, and layer pages.
|
|
|
|
|
*/
|
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint8_t f1[4];
|
|
|
|
|
uint8_t f4[2048];
|
|
|
|
|
uint8_t f0[2048];
|
|
|
|
|
} layer_t;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
layer_t layers[2];
|
|
|
|
|
} mds_disc_struct_t;
|
|
|
|
|
#pragma pack(pop)
|
|
|
|
|
|
|
|
|
|
#define dstruct_t mds_disc_struct_t
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
typedef struct cd_image_t {
|
|
|
|
|
cdrom_t *dev;
|
|
|
|
|
void *log;
|
|
|
|
|
int is_dvd;
|
|
|
|
|
int has_audio;
|
2025-07-24 12:08:58 +02:00
|
|
|
int has_dstruct;
|
2025-01-28 16:26:28 +01:00
|
|
|
int32_t tracks_num;
|
|
|
|
|
uint32_t bad_sectors_num;
|
|
|
|
|
track_t *tracks;
|
|
|
|
|
uint32_t *bad_sectors;
|
2025-07-24 12:08:58 +02:00
|
|
|
dstruct_t dstruct;
|
2025-01-28 16:26:28 +01:00
|
|
|
} cd_image_t;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
CD = 0x00, /* CD-ROM */
|
|
|
|
|
CD_R = 0x01, /* CD-R */
|
|
|
|
|
CD_RW = 0x02, /* CD-RW */
|
|
|
|
|
DVD = 0x10, /* DVD-ROM */
|
|
|
|
|
DVD_MINUS_R = 0x12 /* DVD-R */
|
|
|
|
|
} mds_medium_type_t;
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
UNKNOWN = 0x00,
|
|
|
|
|
AUDIO = 0xa9, /* sector size = 2352 */
|
|
|
|
|
MODE1 = 0xaa, /* sector size = 2048 */
|
|
|
|
|
MODE2 = 0xab, /* sector size = 2336 */
|
|
|
|
|
MODE2_FORM1 = 0xac, /* sector size = 2048 */
|
|
|
|
|
MODE2_FORM2 = 0xad /* sector size = 2324 (+4) */
|
|
|
|
|
} mds_trk_mode_t;
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
NONE = 0x00, /* no subchannel */
|
|
|
|
|
PW_INTERLEAVED = 0x08 /* 96-byte PW subchannel, interleaved */
|
|
|
|
|
} mds_subch_mode_t;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint8_t file_sig[16];
|
|
|
|
|
uint8_t file_ver[2];
|
|
|
|
|
uint16_t medium_type;
|
|
|
|
|
uint16_t sess_num;
|
|
|
|
|
uint16_t pad[2];
|
|
|
|
|
uint16_t bca_data_len;
|
|
|
|
|
uint32_t pad0[2];
|
|
|
|
|
uint32_t bca_data_offs_offs;
|
|
|
|
|
uint32_t pad1[6];
|
|
|
|
|
uint32_t disc_struct_offs;
|
|
|
|
|
uint32_t pad2[3];
|
|
|
|
|
uint32_t sess_blocks_offs;
|
|
|
|
|
uint32_t dpm_blocks_offs;
|
|
|
|
|
} mds_hdr_t; /* 88 bytes */
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
int32_t sess_start;
|
|
|
|
|
int32_t sess_end;
|
|
|
|
|
uint16_t sess_id;
|
|
|
|
|
uint8_t all_blocks_num;
|
|
|
|
|
uint8_t non_track_blocks_num;
|
|
|
|
|
uint16_t first_trk;
|
|
|
|
|
uint16_t last_trk;
|
|
|
|
|
uint32_t pad;
|
|
|
|
|
uint32_t trk_blocks_offs;
|
|
|
|
|
} mds_sess_block_t; /* 24 bytes */
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint8_t trk_mode;
|
|
|
|
|
/* DiscImageCreator says this is the number of subchannels. */
|
|
|
|
|
uint8_t subch_mode;
|
|
|
|
|
uint8_t adr_ctl;
|
|
|
|
|
uint8_t track_id;
|
|
|
|
|
uint8_t point;
|
|
|
|
|
uint8_t m;
|
|
|
|
|
uint8_t s;
|
|
|
|
|
uint8_t f;
|
|
|
|
|
uint8_t zero;
|
|
|
|
|
uint8_t pm;
|
|
|
|
|
uint8_t ps;
|
|
|
|
|
uint8_t pf;
|
|
|
|
|
/* DiscImageCreator calls this the index offset. */
|
|
|
|
|
uint32_t ex_offs;
|
|
|
|
|
uint16_t sector_len;
|
|
|
|
|
/* DiscImageCreator says unknown1 followed by 17x zero. */
|
|
|
|
|
uint8_t pad0[18];
|
|
|
|
|
uint32_t start_sect;
|
|
|
|
|
uint64_t start_offs;
|
|
|
|
|
uint32_t files_num;
|
|
|
|
|
uint32_t footer_offs;
|
|
|
|
|
uint8_t pad1[24];
|
|
|
|
|
} mds_trk_block_t; /* 80 bytes */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
DiscImageCreator's interpretation here makes sense and essentially
|
|
|
|
|
matches libmirage's - Index 0 sectors followed by Index 1 sectors.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint32_t pregap;
|
|
|
|
|
uint32_t trk_sectors;
|
|
|
|
|
} mds_trk_ex_block_t; /* 8 bytes */
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint32_t fn_offs;
|
|
|
|
|
uint32_t fn_is_wide;
|
|
|
|
|
uint32_t pad;
|
|
|
|
|
uint32_t pad0;
|
|
|
|
|
} mds_footer_t; /* 16 bytes */
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint32_t type;
|
|
|
|
|
uint32_t pad[2];
|
|
|
|
|
uint32_t entries;
|
|
|
|
|
} mds_dpm_block_t;
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
int image_do_log = ENABLE_IMAGE_LOG;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
|
|
|
|
void
|
2025-01-28 16:26:28 +01:00
|
|
|
image_log(void *priv, const char *fmt, ...)
|
2018-10-17 05:29:48 +02:00
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (image_do_log) {
|
2018-10-17 05:29:48 +02:00
|
|
|
va_start(ap, fmt);
|
2025-01-28 16:26:28 +01:00
|
|
|
log_out(priv, fmt, ap);
|
2018-10-17 05:29:48 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
|
|
|
|
|
static char *cit[4] = { "SPECIAL", "NONE", "ZERO", "NORMAL" };
|
2018-10-17 05:29:48 +02:00
|
|
|
#else
|
2025-01-28 16:26:28 +01:00
|
|
|
# define image_log(priv, fmt, ...)
|
2018-10-17 05:29:48 +02:00
|
|
|
#endif
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
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, const uint64_t seek, const size_t count)
|
|
|
|
|
{
|
|
|
|
|
const track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
const audio_file_t *audio = (audio_file_t *) tf->priv;
|
|
|
|
|
const uint64_t samples_seek = seek / 4;
|
|
|
|
|
const uint64_t samples_count = count / 4;
|
|
|
|
|
|
|
|
|
|
if ((seek & 3) || (count & 3)) {
|
|
|
|
|
image_log(tf->log, "CD Audio file: Reading on non-4-aligned boundaries.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const 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)
|
|
|
|
|
{
|
|
|
|
|
const track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
const audio_file_t *audio = (audio_file_t *) tf->priv;
|
|
|
|
|
|
|
|
|
|
/* Assume 16-bit audio, 2 channel. */
|
|
|
|
|
return audio->info.frames * 4ull;
|
|
|
|
|
}
|
2018-04-25 23:51:13 +02:00
|
|
|
|
|
|
|
|
static void
|
2025-01-28 16:26:28 +01:00
|
|
|
audio_close(void *priv)
|
2017-05-18 14:03:43 -04:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
audio_file_t *audio = (audio_file_t *) tf->priv;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
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 uint8_t id, 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;
|
2025-01-02 22:38:10 +01:00
|
|
|
}
|
2018-03-17 20:32:20 +01:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
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
|
2025-01-02 22:38:10 +01:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (audio->file == NULL) {
|
|
|
|
|
image_log(tf->log, "Audio file open error!");
|
|
|
|
|
goto cleanup_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (audio->info.channels != 2 || audio->info.samplerate != 44100 || !audio->info.seekable) {
|
|
|
|
|
image_log(tf->log, "Audio file not seekable or in non-CD format!");
|
|
|
|
|
sf_close(audio->file);
|
|
|
|
|
goto cleanup_error;
|
2025-01-02 22:38:10 +01:00
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
|
|
|
|
|
*error = 0;
|
|
|
|
|
|
|
|
|
|
tf->priv = audio;
|
|
|
|
|
tf->fp = NULL;
|
|
|
|
|
tf->close = audio_close;
|
|
|
|
|
tf->get_length = audio_get_length;
|
|
|
|
|
tf->read = audio_read;
|
|
|
|
|
|
|
|
|
|
char n[1024] = { 0 };
|
|
|
|
|
|
|
|
|
|
sprintf(n, "CD-ROM %i Audio", id + 1);
|
|
|
|
|
tf->log = log_open(n);
|
|
|
|
|
|
|
|
|
|
return tf;
|
|
|
|
|
cleanup_error:
|
|
|
|
|
free(tf);
|
|
|
|
|
free(audio);
|
|
|
|
|
*error = 1;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Binary file functions. */
|
|
|
|
|
static int
|
|
|
|
|
bin_read(void *priv, uint8_t *buffer, const uint64_t seek, const size_t count)
|
|
|
|
|
{
|
|
|
|
|
const track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
|
|
|
|
|
if (tf->fp == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
image_log(tf->log, "binary_read(%08lx, pos=%" PRIu64 " count=%lu)\n",
|
|
|
|
|
tf->fp, seek, count);
|
|
|
|
|
|
|
|
|
|
if (fseeko64(tf->fp, seek, SEEK_SET) == -1) {
|
|
|
|
|
image_log(tf->log, "binary_read failed during seek!\n");
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fread(buffer, count, 1, tf->fp) != 1) {
|
|
|
|
|
image_log(tf->log, "binary_read failed during read!\n");
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (UNLIKELY(tf->motorola)) {
|
|
|
|
|
for (uint64_t i = 0; i < count; i += 2) {
|
|
|
|
|
const uint8_t buffer0 = buffer[i];
|
|
|
|
|
const uint8_t buffer1 = buffer[i + 1];
|
|
|
|
|
buffer[i] = buffer1;
|
|
|
|
|
buffer[i + 1] = buffer0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
|
bin_get_length(void *priv)
|
|
|
|
|
{
|
|
|
|
|
const track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
|
|
|
|
|
if (tf->fp == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
fseeko64(tf->fp, 0, SEEK_END);
|
|
|
|
|
const off64_t len = ftello64(tf->fp);
|
|
|
|
|
image_log(tf->log, "binary_length(%08lx) = %" PRIu64 "\n", tf->fp, len);
|
|
|
|
|
|
|
|
|
|
return len;
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-31 04:37:21 +01:00
|
|
|
static void
|
2025-01-28 16:26:28 +01:00
|
|
|
bin_close(void *priv)
|
2024-12-31 04:37:21 +01:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
track_file_t *tf = (track_file_t *) priv;
|
|
|
|
|
|
|
|
|
|
if (tf == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tf->fp != NULL) {
|
|
|
|
|
fclose(tf->fp);
|
|
|
|
|
tf->fp = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(tf->fn, 0x00, sizeof(tf->fn));
|
|
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
log_close(tf->log);
|
|
|
|
|
tf->log = NULL;
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
free(priv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static track_file_t *
|
|
|
|
|
bin_init(const uint8_t id, const char *filename, int *error)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) calloc(1, sizeof(track_file_t));
|
|
|
|
|
struct stat stats;
|
|
|
|
|
|
|
|
|
|
if (tf == NULL) {
|
|
|
|
|
*error = 1;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
char n[1024] = { 0 };
|
|
|
|
|
|
|
|
|
|
sprintf(n, "CD-ROM %i Bin ", id + 1);
|
|
|
|
|
tf->log = log_open(n);
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
memset(tf->fn, 0x00, sizeof(tf->fn));
|
|
|
|
|
strncpy(tf->fn, filename, sizeof(tf->fn) - 1);
|
|
|
|
|
tf->fp = plat_fopen64(tf->fn, "rb");
|
|
|
|
|
image_log(tf->log, "binary_open(%s) = %08lx\n", tf->fn, tf->fp);
|
|
|
|
|
|
|
|
|
|
if (stat(tf->fn, &stats) != 0) {
|
|
|
|
|
/* Use a blank structure if stat failed. */
|
|
|
|
|
memset(&stats, 0, sizeof(struct stat));
|
|
|
|
|
}
|
|
|
|
|
*error = ((tf->fp == NULL) || ((stats.st_mode & S_IFMT) == S_IFDIR));
|
|
|
|
|
|
|
|
|
|
/* Set the function pointers. */
|
|
|
|
|
if (!*error) {
|
|
|
|
|
tf->read = bin_read;
|
|
|
|
|
tf->get_length = bin_get_length;
|
|
|
|
|
tf->close = bin_close;
|
|
|
|
|
} else {
|
|
|
|
|
/* From the check above, error may still be non-zero if opening a directory.
|
|
|
|
|
* The error is set for viso to try and open the directory following this function.
|
|
|
|
|
* However, we need to make sure the descriptor is closed. */
|
|
|
|
|
if ((tf->fp != NULL) && ((stats.st_mode & S_IFMT) == S_IFDIR)) {
|
|
|
|
|
/* tf is freed by bin_close */
|
|
|
|
|
bin_close(tf);
|
2025-07-24 12:08:58 +02:00
|
|
|
} else {
|
|
|
|
|
log_close(tf->log);
|
|
|
|
|
tf->log = NULL;
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
free(tf);
|
2025-07-24 12:08:58 +02:00
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
tf = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static track_file_t *
|
|
|
|
|
index_file_init(const uint8_t id, const char *filename, int *error, int *is_viso)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = NULL;
|
|
|
|
|
|
|
|
|
|
*is_viso = 0;
|
|
|
|
|
|
|
|
|
|
/* Current we only support .BIN files, either combined or one per
|
|
|
|
|
track. In the future, more is planned. */
|
|
|
|
|
tf = bin_init(id, filename, error);
|
|
|
|
|
|
|
|
|
|
if (*error) {
|
|
|
|
|
if ((tf != NULL) && (tf->close != NULL)) {
|
|
|
|
|
tf->close(tf);
|
|
|
|
|
tf = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tf = viso_init(id, filename, error);
|
|
|
|
|
|
|
|
|
|
if (!*error)
|
|
|
|
|
*is_viso = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-28 23:22:06 +01:00
|
|
|
return tf;
|
2024-12-31 04:37:21 +01:00
|
|
|
}
|
|
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
static void
|
2025-01-28 16:26:28 +01:00
|
|
|
index_file_close(track_index_t *idx)
|
2018-04-25 23:51:13 +02:00
|
|
|
{
|
2025-01-28 23:22:06 +01:00
|
|
|
if ((idx == NULL) || (idx->file == NULL))
|
2025-01-28 16:26:28 +01:00
|
|
|
return;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
image_log(idx->file->log, "Log closed\n");
|
2018-04-25 23:51:13 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (idx->file->log != NULL) {
|
|
|
|
|
log_close(idx->file->log);
|
|
|
|
|
idx->file->log = NULL;
|
|
|
|
|
}
|
2024-05-20 01:57:15 +02:00
|
|
|
|
2025-01-28 23:22:06 +01:00
|
|
|
if (idx->file->close != NULL)
|
|
|
|
|
idx->file->close(idx->file);
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
idx->file = NULL;
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
/* Internal functions. */
|
2018-04-25 23:51:13 +02:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_get_track(const cd_image_t *img, const uint32_t sector)
|
2017-05-18 14:03:43 -04:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
int ret = -1;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
track_t *ct = &(img->tracks[i]);
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
const track_index_t *ci = &(ct->idx[j]);
|
|
|
|
|
if ((ci->type >= INDEX_ZERO) && (ci->length != 0ULL) &&
|
|
|
|
|
((sector + 150) >= ci->start) && ((sector + 150) <= (ci->start + ci->length - 1))) {
|
|
|
|
|
ret = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
image_get_track_and_index(const cd_image_t *img, const uint32_t sector,
|
|
|
|
|
int *track, int *index)
|
|
|
|
|
{
|
|
|
|
|
*track = -1;
|
|
|
|
|
*index = -1;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
track_t *ct = &(img->tracks[i]);
|
2025-02-09 20:06:15 +01:00
|
|
|
if ((ct->point >= 1) && (ct->point <= 99)) for (int j = 0; j < 3; j++) {
|
2025-01-28 16:26:28 +01:00
|
|
|
track_index_t *ci = &(ct->idx[j]);
|
|
|
|
|
if ((ci->type >= INDEX_ZERO) && (ci->length != 0ULL) &&
|
|
|
|
|
((sector + 150) >= ci->start) && ((sector + 150) <= (ci->start + ci->length - 1))) {
|
|
|
|
|
*track = i;
|
|
|
|
|
*index = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
image_is_sector_bad(const cd_image_t *img, const uint32_t sector)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (img->bad_sectors_num > 0) for (int i = 0; i < img->bad_sectors_num; i++)
|
|
|
|
|
if (img->bad_sectors[i] == sector) {
|
|
|
|
|
ret = 1;
|
2025-01-02 22:38:10 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
image_is_track_audio(const cd_image_t *img, const uint32_t pos)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (img->has_audio) {
|
|
|
|
|
const int track = image_get_track(img, pos);
|
|
|
|
|
|
|
|
|
|
if (track >= 0) {
|
|
|
|
|
const track_t *trk = &(img->tracks[track]);
|
|
|
|
|
|
|
|
|
|
ret = (trk->mode == 0);
|
|
|
|
|
}
|
2018-04-25 23:51:13 +02:00
|
|
|
}
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
2025-01-02 22:38:10 +01:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
static int
|
|
|
|
|
image_can_read_pvd(track_file_t *file, const uint64_t start,
|
|
|
|
|
const uint64_t sector_size, const int xa)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buf[2448] = { 0 };
|
|
|
|
|
/* First VD is located at sector 16. */
|
|
|
|
|
uint64_t seek = start + (16ULL * sector_size);
|
|
|
|
|
uint8_t *pvd = (uint8_t *) buf;
|
|
|
|
|
|
|
|
|
|
if (sector_size >= RAW_SECTOR_SIZE) {
|
|
|
|
|
if (xa)
|
|
|
|
|
pvd = &(buf[24]);
|
|
|
|
|
else
|
|
|
|
|
pvd = &(buf[16]);
|
|
|
|
|
} else if (sector_size >= 2332) {
|
|
|
|
|
if (xa)
|
|
|
|
|
pvd = &(buf[8]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file->read(file, buf, seek, sector_size);
|
|
|
|
|
|
|
|
|
|
int ret = (((pvd[0] == 1) &&
|
|
|
|
|
!strncmp((char *) &(pvd[1]), "CD001", 5) &&
|
|
|
|
|
(pvd[6] == 1)) ||
|
|
|
|
|
((pvd[8] == 1) &&
|
|
|
|
|
!strncmp((char *) &(pvd[9]), "CDROM", 5) &&
|
|
|
|
|
(pvd[14] == 1)));
|
|
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
|
if (sector_size >= RAW_SECTOR_SIZE) {
|
|
|
|
|
if (xa)
|
|
|
|
|
/* Mode 2 XA, Form from the sub-header. */
|
|
|
|
|
ret = 0x20 | (((buf[18] & 0x20) >> 5) + 1);
|
|
|
|
|
else
|
|
|
|
|
/* Mode from header. */
|
|
|
|
|
ret = buf[15] << 4;
|
|
|
|
|
} else if (sector_size >= 2332) {
|
|
|
|
|
if (xa)
|
|
|
|
|
/* Mode 2 XA, Form from the sub-header. */
|
|
|
|
|
ret = 0x20 | (((buf[2] & 0x20) >> 5) + 1);
|
|
|
|
|
else
|
|
|
|
|
/* Mode 2 non-XA. */
|
|
|
|
|
ret = 0x20;
|
|
|
|
|
} else if (sector_size >= 2324)
|
|
|
|
|
/* Mode 2 XA Form 2. */
|
|
|
|
|
ret = 0x22;
|
|
|
|
|
else if (!strncmp((char *) &(pvd[0x400]), "CD-XA001", 8))
|
|
|
|
|
/* Mode 2 XA Form 1. */
|
|
|
|
|
ret = 0x21;
|
|
|
|
|
else
|
|
|
|
|
/* Mode 1. */
|
|
|
|
|
ret = 0x10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_cue_get_buffer(char *str, char **line, const int up)
|
2017-05-18 14:03:43 -04:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
char *s = *line;
|
|
|
|
|
char *p = str;
|
|
|
|
|
int quote = 0;
|
|
|
|
|
int done = 0;
|
|
|
|
|
int space = 1;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
/* Copy to local buffer until we have end of string or whitespace. */
|
|
|
|
|
while (!done) {
|
|
|
|
|
switch (*s) {
|
|
|
|
|
case '\0':
|
|
|
|
|
if (quote) {
|
|
|
|
|
/* Ouch, unterminated string.. */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
done = 1;
|
|
|
|
|
break;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
case '\"':
|
|
|
|
|
quote ^= 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ' ':
|
|
|
|
|
case '\t':
|
|
|
|
|
if (space)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!quote) {
|
|
|
|
|
done = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fallthrough;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (up && islower((int) *s))
|
|
|
|
|
*p++ = toupper((int) *s);
|
|
|
|
|
else
|
|
|
|
|
*p++ = *s;
|
|
|
|
|
space = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!done)
|
|
|
|
|
s++;
|
2018-04-25 23:51:13 +02:00
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
|
|
*line = s;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
return 1;
|
2018-04-25 23:51:13 +02:00
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2021-09-02 20:52:30 +02:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_cue_get_keyword(char **dest, char **line)
|
2021-09-02 20:52:30 +02:00
|
|
|
{
|
2025-01-28 23:22:06 +01:00
|
|
|
const int success = image_cue_get_buffer(temp_keyword, line, 1);
|
2025-01-28 16:26:28 +01:00
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
|
*dest = temp_keyword;
|
2021-09-02 20:52:30 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get a string from the input line, handling quotes properly. */
|
|
|
|
|
static uint64_t
|
|
|
|
|
image_cue_get_number(char **line)
|
|
|
|
|
{
|
|
|
|
|
char temp[128];
|
|
|
|
|
uint64_t num;
|
|
|
|
|
|
|
|
|
|
if (!image_cue_get_buffer(temp, line, 0))
|
2025-01-02 22:38:10 +01:00
|
|
|
return 0;
|
2021-09-02 20:52:30 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (sscanf(temp, "%" PRIu64, &num) != 1)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return num;
|
2021-09-02 20:52:30 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-20 02:26:27 -05:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_cue_get_frame(uint64_t *frames, char **line)
|
2018-04-25 23:51:13 +02:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
char temp[128];
|
|
|
|
|
int min = 0;
|
|
|
|
|
int sec = 0;
|
|
|
|
|
int fr = 0;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
int success = image_cue_get_buffer(temp, line, 0);
|
|
|
|
|
if (!success)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
success = sscanf(temp, "%d:%d:%d", &min, &sec, &fr) == 3;
|
|
|
|
|
if (!success)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
*frames = MSF_TO_FRAMES(min, sec, fr);
|
|
|
|
|
|
|
|
|
|
return 1;
|
2018-04-25 23:51:13 +02:00
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_cue_get_flags(track_t *cur, char **line)
|
2018-04-25 23:51:13 +02:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
char temp[128];
|
|
|
|
|
char temp2[128];
|
|
|
|
|
|
|
|
|
|
int success = image_cue_get_buffer(temp, line, 0);
|
|
|
|
|
if (!success)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
memset(temp2, 0x00, sizeof(temp2));
|
|
|
|
|
success = sscanf(temp, "%s", temp2) == 1;
|
|
|
|
|
if (!success)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (strstr(temp2, "PRE") != NULL)
|
|
|
|
|
cur->attr |= 0x01;
|
|
|
|
|
if (strstr(temp2, "DCP") != NULL)
|
|
|
|
|
cur->attr |= 0x02;
|
|
|
|
|
if (strstr(temp2, "4CH") != NULL)
|
|
|
|
|
cur->attr |= 0x08;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static track_t *
|
|
|
|
|
image_insert_track(cd_image_t *img, const uint8_t session, const uint8_t point)
|
|
|
|
|
{
|
|
|
|
|
track_t *ct = NULL;
|
|
|
|
|
|
|
|
|
|
img->tracks_num++;
|
|
|
|
|
if (img->tracks == NULL) {
|
|
|
|
|
img->tracks = calloc(1, sizeof(track_t));
|
|
|
|
|
ct = &(img->tracks[0]);
|
|
|
|
|
} else {
|
|
|
|
|
img->tracks = realloc(img->tracks, img->tracks_num * sizeof(track_t));
|
|
|
|
|
ct = &(img->tracks[img->tracks_num - 1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] Insert %02X: img->tracks[%2i]\n",
|
|
|
|
|
point, img->tracks_num - 1);
|
|
|
|
|
|
|
|
|
|
memset(ct, 0x00, sizeof(track_t));
|
|
|
|
|
|
|
|
|
|
ct->session = session;
|
|
|
|
|
ct->point = point;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
|
ct->idx[i].type = (point > 99) ? INDEX_SPECIAL : INDEX_NONE;
|
|
|
|
|
|
|
|
|
|
if (point >= 0xb0)
|
|
|
|
|
ct->attr = 0x50;
|
|
|
|
|
|
|
|
|
|
return ct;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
image_process(cd_image_t *img)
|
|
|
|
|
{
|
|
|
|
|
track_t *ct = NULL;
|
|
|
|
|
track_t *lt = NULL;
|
|
|
|
|
track_index_t *ci = NULL;
|
|
|
|
|
track_file_t *tf = NULL;
|
|
|
|
|
uint64_t tf_len = 0ULL;
|
|
|
|
|
uint64_t cur_pos = 0ULL;
|
|
|
|
|
int pos = 0;
|
|
|
|
|
int ls = 0;
|
|
|
|
|
int map[256] = { 0 };
|
|
|
|
|
int lead[3] = { 0 };
|
|
|
|
|
uint64_t spg[256] = { 0ULL };
|
|
|
|
|
track_t *lo[256] = { 0 };
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Pass 2 - adjusting pre-gaps of the first track of every session and creating the
|
|
|
|
|
map so we can map from <A0/A1/A2> <01-99> <B0/C0> to <01-99> <A0/A1/A2> <B0/C0>
|
|
|
|
|
so that their times and length can be adjusted correctly in the third and fourth
|
|
|
|
|
passes - especially important for multi-session Cue files.
|
|
|
|
|
|
|
|
|
|
We have to do that because Cue sheets do not explicitly indicate those pre-gaps
|
|
|
|
|
but they are required so we have the correct frames - the first track of each
|
|
|
|
|
session always has a pre-gap of at least 0:02:00. We do not adjust it if it is
|
|
|
|
|
already present.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 2 (adjusting pre-gaps and preparing map)...\n");
|
|
|
|
|
|
|
|
|
|
/* Pre-gap of the first track of the first session. */
|
|
|
|
|
ct = &(img->tracks[3]);
|
|
|
|
|
ci = &(ct->idx[0]);
|
|
|
|
|
|
|
|
|
|
if (ci->type == INDEX_NONE) {
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->start = 0ULL;
|
|
|
|
|
ci->length = 150ULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [PREGAP ] Adjusted pre-gap of track %02X (first in "
|
|
|
|
|
"session %i)\n", ct->point, ct->session);
|
|
|
|
|
|
|
|
|
|
/* The other pre-gaps and the preparation of the map. */
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
ct = &(img->tracks[i]);
|
|
|
|
|
if (((ct->point >= 1) && (ct->point <= 99)) || (ct->point >= 0xb0)) {
|
|
|
|
|
if (ct->point == 0xb0) {
|
|
|
|
|
/* The first track of a session always has a pre-gap of at least 0:02:00. */
|
|
|
|
|
track_t *ft = &(img->tracks[i + (ct->session == 1) + 4]);
|
|
|
|
|
ci = &(ft->idx[0]);
|
|
|
|
|
|
|
|
|
|
if (ci->type == INDEX_NONE) {
|
|
|
|
|
if (ft->idx[1].type == INDEX_NORMAL) {
|
|
|
|
|
ci->type = INDEX_NORMAL;
|
|
|
|
|
ci->file_start = ft->idx[1].file_start - 150ULL;
|
|
|
|
|
} else {
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->start = 0ULL;
|
|
|
|
|
ci->length = 150ULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [PREGAP ] Adjusted pre-gap of track %02X "
|
|
|
|
|
"(first in session %i)\n", ft->point, ct->session);
|
|
|
|
|
|
|
|
|
|
/* Point B0h found, add the previous three lead tracks. */
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
map[pos] = lead[j];
|
|
|
|
|
image_log(img->log, " [REMAP ] Remap %3i to %3i (%02X)\n", pos,
|
|
|
|
|
map[pos], 0xa0 + j);
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the current track. */
|
|
|
|
|
map[pos] = i;
|
|
|
|
|
image_log(img->log, " [NORMAL ] Remap %3i to %3i\n", pos, map[pos]);
|
|
|
|
|
pos++;
|
|
|
|
|
} else if ((ct->point >= 0xa0) && (ct->point <= 0xa2)) {
|
|
|
|
|
/*
|
|
|
|
|
Collect lead track (A0 = first track in session, A1 = last track in session,
|
|
|
|
|
A2 = lead out).
|
|
|
|
|
*/
|
|
|
|
|
lead[ct->point & 0x03] = i;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [LEAD ] Lead %i = %3i (%02X)\n", ct->point & 0x03, i,
|
|
|
|
|
ct->point);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the last three lead tracks. */
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
map[pos] = lead[i];
|
|
|
|
|
image_log(img->log, " [REMAP ] Remap %3i to %3i (%02X)\n", pos, map[pos],
|
|
|
|
|
0xa0 + i);
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
If these two mismatch, it is a fatal condition since it means something
|
|
|
|
|
has gone wrong enough that the Cue sheet processing has been messed up.
|
|
|
|
|
*/
|
|
|
|
|
if (pos != img->tracks_num)
|
|
|
|
|
log_fatal(img->log, "Something has gone wrong and we have remappped %3i tracks "
|
|
|
|
|
"instead of the expected %3i\n", pos, img->tracks_num);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Pass 3 - adjusting the time lengths of each index of track according to the
|
|
|
|
|
files.
|
|
|
|
|
|
|
|
|
|
We have to do that because Cue sheets do not explicitly indicate the lengths
|
|
|
|
|
of track, so we have to deduce them from what the combination of the Cue sheet
|
|
|
|
|
and the various files give us.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 3 (adjusting track file lengths according to the files)...\n");
|
|
|
|
|
for (int i = (img->tracks_num - 1); i >= 0; i--) {
|
|
|
|
|
ct = &(img->tracks[map[i]]);
|
|
|
|
|
if (ct->idx[1].type != INDEX_SPECIAL) {
|
|
|
|
|
for (int j = 2; j >= 0; j--) {
|
|
|
|
|
ci = &(ct->idx[j]);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
If the file is not NULL and is different from the previous file,
|
|
|
|
|
open it and read its length.
|
|
|
|
|
*/
|
|
|
|
|
if ((ci->file != NULL) && (ci->file != tf)) {
|
|
|
|
|
tf = ci->file;
|
|
|
|
|
if (tf != NULL) {
|
|
|
|
|
tf_len = tf->get_length(tf) / ct->sector_size;
|
|
|
|
|
image_log(img->log, " [FILE ] File length: %016"
|
|
|
|
|
PRIX64 " sectors\n", tf_len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((ci->type < INDEX_SPECIAL) || (ci->type > INDEX_NORMAL)) {
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X, INDEX %02X, ATTR %02X,\n",
|
|
|
|
|
ci->type, j,
|
|
|
|
|
ct->attr);
|
|
|
|
|
log_fatal(img->log, " Unrecognized index type during "
|
|
|
|
|
"Pass 3: %2i\n",
|
|
|
|
|
ci->type);
|
|
|
|
|
} else if (ci->type == INDEX_NORMAL) {
|
|
|
|
|
/* Index was in the cue sheet and is present in the file. */
|
|
|
|
|
ci->file_length = tf_len - ci->file_start;
|
|
|
|
|
tf_len -= ci->file_length;
|
|
|
|
|
} else {
|
|
|
|
|
/* Index was not in the cue sheet or is not present in the file,
|
|
|
|
|
keep its length at zero. */
|
|
|
|
|
ci->file_start = tf_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, INDEX %02X, ATTR %02X, "
|
|
|
|
|
"MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point, j,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ci->type + 2]);
|
|
|
|
|
image_log(img->log, " file_start = %016"
|
|
|
|
|
PRIX64 " (%2i:%02i:%02i),\n",
|
|
|
|
|
ci->file_start,
|
|
|
|
|
(int) ((ci->file_start / 75) / 60),
|
|
|
|
|
(int) ((ci->file_start / 75) % 60),
|
|
|
|
|
(int) (ci->file_start % 75));
|
|
|
|
|
image_log(img->log, " file_length = %016"
|
|
|
|
|
PRIX64 " (%2i:%02i:%02i),\n",
|
|
|
|
|
ci->file_length,
|
|
|
|
|
(int) ((ci->file_length / 75) / 60),
|
|
|
|
|
(int) ((ci->file_length / 75) % 60),
|
|
|
|
|
(int) (ci->file_length % 75));
|
|
|
|
|
image_log(img->log, " remaining = %016"
|
|
|
|
|
PRIX64 " (%2i:%02i:%02i)\n",
|
|
|
|
|
tf_len,
|
|
|
|
|
(int) ((tf_len / 75) / 60),
|
|
|
|
|
(int) ((tf_len / 75) % 60),
|
|
|
|
|
(int) (tf_len % 75));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Pass 4 - calculating the actual track starts and lengths for the TOC.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 4 (calculating the actual track starts "
|
|
|
|
|
"and lengths for the TOC)...\n");
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
ct = &(img->tracks[map[i]]);
|
|
|
|
|
if (ct->idx[1].type != INDEX_SPECIAL) {
|
|
|
|
|
int session_changed = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
If the session has changed, store the last session
|
|
|
|
|
and mark that it has changed.
|
|
|
|
|
*/
|
|
|
|
|
if (ct->session != ls) {
|
|
|
|
|
ls = ct->session;
|
|
|
|
|
session_changed = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
ci = &(ct->idx[j]);
|
|
|
|
|
|
|
|
|
|
if ((ci->type < INDEX_SPECIAL) || (ci->type > INDEX_NORMAL)) {
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X, INDEX %02X, ATTR %02X,\n",
|
|
|
|
|
ci->type, j,
|
|
|
|
|
ct->attr);
|
|
|
|
|
log_fatal(img->log, " Unrecognized index type during "
|
|
|
|
|
"Pass 4: %2i\n",
|
|
|
|
|
ci->type);
|
|
|
|
|
} else if (ci->type <= INDEX_NONE)
|
|
|
|
|
/* Index was not in the cue sheet, keep its length at zero. */
|
|
|
|
|
ci->start = cur_pos;
|
|
|
|
|
else if (ci->type == INDEX_ZERO) {
|
|
|
|
|
/* Index was in the cue sheet and is not present in the file. */
|
|
|
|
|
ci->start = cur_pos;
|
|
|
|
|
cur_pos += ci->length;
|
|
|
|
|
} else if (ci->type == INDEX_NORMAL) {
|
|
|
|
|
/* Index was in the cue sheet and is present in the file. */
|
|
|
|
|
ci->start = cur_pos;
|
|
|
|
|
ci->length = ci->file_length;
|
|
|
|
|
cur_pos += ci->file_length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, INDEX %02X, ATTR %02X, "
|
|
|
|
|
"MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point, j,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ci->type + 2]);
|
|
|
|
|
image_log(img->log, " start = %016"
|
|
|
|
|
PRIX64 " (%2i:%02i:%02i),\n",
|
|
|
|
|
ci->start,
|
|
|
|
|
(int) ((ci->start / 75) / 60),
|
|
|
|
|
(int) ((ci->start / 75) % 60),
|
|
|
|
|
(int) (ci->start % 75));
|
|
|
|
|
image_log(img->log, " length = %016"
|
|
|
|
|
PRIX64 " (%2i:%02i:%02i),\n",
|
|
|
|
|
ci->length,
|
|
|
|
|
(int) ((ci->length / 75) / 60),
|
|
|
|
|
(int) ((ci->length / 75) % 60),
|
|
|
|
|
(int) (ci->length % 75));
|
|
|
|
|
image_log(img->log, " cur_pos = %016"
|
|
|
|
|
PRIX64 " (%2i:%02i:%02i)\n",
|
|
|
|
|
cur_pos,
|
|
|
|
|
(int) ((cur_pos / 75) / 60),
|
|
|
|
|
(int) ((cur_pos / 75) % 60),
|
|
|
|
|
(int) (cur_pos % 75));
|
|
|
|
|
|
|
|
|
|
/* Set the pre-gap of the first track of this session. */
|
|
|
|
|
if (session_changed)
|
|
|
|
|
spg[ct->session] = ct->idx[0].start;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Pass 5 - setting the lead out starts for all sessions.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 5 (setting the lead out starts for all sessions)...\n");
|
|
|
|
|
for (int i = 0; i <= ls; i++) {
|
|
|
|
|
lo[i] = NULL;
|
|
|
|
|
for (int j = (img->tracks_num - 1); j >= 0; j--) {
|
|
|
|
|
const track_t *jt = &(img->tracks[j]);
|
|
|
|
|
if ((jt->session == i) && (jt->point >= 1) && (jt->point <= 99)) {
|
|
|
|
|
lo[i] = &(img->tracks[j]);
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, INDEX %02X, ATTR %02X, "
|
|
|
|
|
"MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point, j,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ci->type + 2]);
|
|
|
|
|
image_log(img->log, " using to calculate the start of session "
|
|
|
|
|
"%02X lead out\n",
|
|
|
|
|
ct->session);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Pass 6 - refinining modes and forms, and finalizing all the special tracks.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 6 (refinining modes and forms, and finalizing "
|
|
|
|
|
"all the special tracks)...\n");
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
ct = &(img->tracks[i]);
|
|
|
|
|
lt = NULL;
|
|
|
|
|
switch (ct->point) {
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
case 1 ... 99:
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
|
|
|
|
|
if ((ci->type == INDEX_NORMAL) && (ct->mode >= 1)) {
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/01, INDEX %02X, ATTR %02X, "
|
|
|
|
|
"MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ct->idx[1].type + 2]);
|
|
|
|
|
|
|
|
|
|
/* Override the loaded modes with that we determine here. */
|
|
|
|
|
int can_read_pvd = image_can_read_pvd(ci->file,
|
|
|
|
|
ci->file_start * ct->sector_size,
|
|
|
|
|
ct->sector_size, 0);
|
|
|
|
|
ct->skip = 0;
|
|
|
|
|
if (can_read_pvd) {
|
|
|
|
|
ct->mode = can_read_pvd >> 4;
|
|
|
|
|
ct->form = can_read_pvd & 0xf;
|
|
|
|
|
if (((ct->sector_size == 2332) || (ct->sector_size == 2336)) &&
|
|
|
|
|
(ct->form >= 1))
|
|
|
|
|
ct->skip = 8;
|
|
|
|
|
} else if (ct->sector_size >= 2332) {
|
|
|
|
|
can_read_pvd = image_can_read_pvd(ci->file,
|
|
|
|
|
ci->file_start * ct->sector_size,
|
|
|
|
|
ct->sector_size, 1);
|
|
|
|
|
if (can_read_pvd) {
|
|
|
|
|
ct->mode = can_read_pvd >> 4;
|
|
|
|
|
ct->form = can_read_pvd & 0xf;
|
|
|
|
|
if (((ct->sector_size == 2332) || (ct->sector_size == 2336)) &&
|
|
|
|
|
(ct->form >= 1))
|
|
|
|
|
ct->skip = 8;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " NEW MODE: %02X/%02X\n",
|
|
|
|
|
ct->mode, ct->form);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0xa0:
|
|
|
|
|
for (int j = 0; j < img->tracks_num; j++) {
|
|
|
|
|
track_t *jt = &(img->tracks[j]);
|
|
|
|
|
if ((jt->session == ct->session) &&
|
|
|
|
|
(jt->point >= 1) && (jt->point <= 99)) {
|
|
|
|
|
lt = jt;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (lt != NULL) {
|
|
|
|
|
int disc_type = 0x00;
|
|
|
|
|
|
|
|
|
|
ct->attr = lt->attr;
|
|
|
|
|
|
|
|
|
|
ct->mode = lt->mode;
|
|
|
|
|
ct->form = lt->form;
|
|
|
|
|
|
|
|
|
|
if (lt->mode == 2)
|
|
|
|
|
disc_type = 0x20;
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
ci = &(ct->idx[j]);
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->start = (lt->point * 60 * 75) + (disc_type * 75);
|
|
|
|
|
ci->length = 0;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, INDEX %02X, "
|
|
|
|
|
"ATTR %02X, MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point, j,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ci->type + 2]);
|
|
|
|
|
image_log(img->log, " first track = %02X, "
|
|
|
|
|
"disc type = %02X\n",
|
|
|
|
|
lt->point, disc_type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0xa1:
|
|
|
|
|
for (int j = (img->tracks_num - 1); j >= 0; j--) {
|
|
|
|
|
track_t *jt = &(img->tracks[j]);
|
|
|
|
|
if ((jt->session == ct->session) && (jt->point >= 1) && (jt->point <= 99)) {
|
|
|
|
|
lt = jt;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lt != NULL) {
|
|
|
|
|
ct->attr = lt->attr;
|
|
|
|
|
|
|
|
|
|
ct->mode = lt->mode;
|
|
|
|
|
ct->form = lt->form;
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
ci = &(ct->idx[j]);
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->start = (lt->point * 60 * 75);
|
|
|
|
|
ci->length = 0;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, INDEX %02X, "
|
|
|
|
|
"ATTR %02X, MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point, j,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ci->type + 2]);
|
|
|
|
|
image_log(img->log, " last track = %02X\n",
|
|
|
|
|
lt->point);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0xa2:
|
|
|
|
|
if (lo[ct->session] != NULL) {
|
|
|
|
|
/*
|
|
|
|
|
We have a track to use for the calculation, first adjust the track's
|
|
|
|
|
attribute (ADR/Ctrl), mode, and form to match the last non-special track.
|
|
|
|
|
*/
|
|
|
|
|
lt = lo[ct->session];
|
|
|
|
|
|
|
|
|
|
ct->attr = lt->attr;
|
|
|
|
|
|
|
|
|
|
ct->mode = lt->mode;
|
|
|
|
|
ct->form = lt->form;
|
|
|
|
|
|
|
|
|
|
if (ct->idx[1].type != INDEX_NORMAL) {
|
|
|
|
|
/*
|
|
|
|
|
Index not normal, therefore, this is not a lead out track from a
|
|
|
|
|
second or afterwards session of a multi-session Cue sheet, calculate
|
|
|
|
|
the starting time and update all the indexes accordingly.
|
|
|
|
|
*/
|
|
|
|
|
const track_index_t *li = &(lt->idx[2]);
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, INDEX %02X, "
|
|
|
|
|
"ATTR %02X, MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point, j,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ci->type + 2]);
|
|
|
|
|
|
|
|
|
|
ci = &(ct->idx[j]);
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->start = li->start + li->length;
|
|
|
|
|
ci->length = 0;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " start = %016" PRIX64
|
|
|
|
|
" (%2i:%02i:%02i)\n",
|
|
|
|
|
ci->start,
|
|
|
|
|
(int) ((ci->start / 75) / 60),
|
|
|
|
|
(int) ((ci->start / 75) % 60),
|
|
|
|
|
(int) (ci->start % 75));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
else
|
|
|
|
|
image_log(img->log, " no start calculation done, "
|
|
|
|
|
"already specified\n");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
else
|
|
|
|
|
image_log(img->log, " nothing done, no suitable last track "
|
|
|
|
|
"found\n");
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
case 0xb0:
|
|
|
|
|
/*
|
|
|
|
|
B0: MSF points to the beginning of the pre-gap
|
|
|
|
|
of the following session's first track.
|
|
|
|
|
*/
|
|
|
|
|
ct->extra[0] = (spg[ct->session + 1] / 75) / 60;
|
|
|
|
|
ct->extra[1] = (spg[ct->session + 1] / 75) % 60;
|
|
|
|
|
ct->extra[2] = spg[ct->session + 1] % 75;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, INDEX 01, "
|
|
|
|
|
"ATTR %02X, MODE %02X/%02X, %8s,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form,
|
|
|
|
|
cit[ct->idx[1].type + 2]);
|
|
|
|
|
image_log(img->log, " %02X:%02X:%02X, %02X,\n",
|
|
|
|
|
ct->extra[0], ct->extra[1], ct->extra[2], ct->extra[3]);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
B0 PMSF points to the start of the lead out track
|
|
|
|
|
of the last session.
|
|
|
|
|
*/
|
|
|
|
|
if (lo[ls] != NULL) {
|
|
|
|
|
lt = lo[ls];
|
|
|
|
|
const track_index_t *li = &(lt->idx[2]);
|
|
|
|
|
|
|
|
|
|
ct->idx[1].start = li->start + li->length;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " start = %016" PRIX64
|
|
|
|
|
" (%2i:%02i:%02i)\n",
|
|
|
|
|
ct->idx[1].start,
|
|
|
|
|
(int) ((ct->idx[1].start / 75) / 60),
|
|
|
|
|
(int) ((ct->idx[1].start / 75) % 60),
|
|
|
|
|
(int) (ct->idx[1].start % 75));
|
|
|
|
|
}
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
else
|
|
|
|
|
image_log(img->log, " no start calculation done, "
|
|
|
|
|
"no suitable last track found\n");
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
image_log(img->log, "Final tracks list:\n");
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
ct = &(img->tracks[i]);
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
ci = &(ct->idx[j]);
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X INDEX %02X: [%8s, %016" PRIX64 "]\n",
|
|
|
|
|
ct->point, j,
|
|
|
|
|
cit[ci->type + 2], ci->file_start * ct->sector_size);
|
|
|
|
|
image_log(img->log, " TOC data: %02X %02X %02X "
|
2025-07-24 12:08:58 +02:00
|
|
|
"%02X %02X %02X %02X %02X %02X %02X %02X\n",
|
2025-01-28 16:26:28 +01:00
|
|
|
ct->session, ct->attr, ct->tno, ct->point,
|
|
|
|
|
ct->extra[0], ct->extra[1], ct->extra[2], ct->extra[3],
|
|
|
|
|
(uint32_t) ((ci->start / 75) / 60),
|
|
|
|
|
(uint32_t) ((ci->start / 75) % 60),
|
|
|
|
|
(uint32_t) (ci->start % 75));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
image_set_track_subch_type(track_t *ct)
|
|
|
|
|
{
|
|
|
|
|
if (ct->sector_size == 2448)
|
|
|
|
|
ct->subch_type = 0x08;
|
|
|
|
|
else if (ct->sector_size == 2368)
|
|
|
|
|
ct->subch_type = 0x10;
|
2024-12-31 04:37:21 +01:00
|
|
|
else
|
2025-01-28 16:26:28 +01:00
|
|
|
ct->subch_type = 0x00;
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2018-04-25 23:51:13 +02:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_load_iso(cd_image_t *img, const char *filename)
|
2017-05-18 14:03:43 -04:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
track_t *ct = NULL;
|
|
|
|
|
track_index_t *ci = NULL;
|
|
|
|
|
track_file_t *tf = NULL;
|
|
|
|
|
int success = 1;
|
|
|
|
|
int error = 1;
|
|
|
|
|
int is_viso = 0;
|
|
|
|
|
int sector_sizes[8] = { 2448, 2368, RAW_SECTOR_SIZE, 2336,
|
|
|
|
|
2332, 2328, 2324, COOKED_SECTOR_SIZE };
|
|
|
|
|
|
|
|
|
|
img->tracks = NULL;
|
|
|
|
|
/*
|
|
|
|
|
Pass 1 - loading the ISO image.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 1 (loading the ISO image)...\n");
|
|
|
|
|
img->tracks_num = 0;
|
|
|
|
|
|
|
|
|
|
image_insert_track(img, 1, 0xa0);
|
|
|
|
|
image_insert_track(img, 1, 0xa1);
|
|
|
|
|
image_insert_track(img, 1, 0xa2);
|
|
|
|
|
|
|
|
|
|
/* Data track (shouldn't there be a lead in track?). */
|
|
|
|
|
tf = index_file_init(img->dev->id, filename, &error, &is_viso);
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
if (tf != NULL) {
|
|
|
|
|
tf->close(tf);
|
|
|
|
|
tf = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
success = 0;
|
|
|
|
|
} else if (is_viso)
|
|
|
|
|
success = 3;
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
ct = image_insert_track(img, 1, 1);
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
|
|
|
|
|
ct->form = 0;
|
|
|
|
|
ct->mode = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
|
ct->idx[i].type = INDEX_NONE;
|
|
|
|
|
|
|
|
|
|
ct->attr = DATA_TRACK;
|
|
|
|
|
|
|
|
|
|
/* Try to detect ISO type. */
|
|
|
|
|
ct->mode = 1;
|
|
|
|
|
ct->form = 0;
|
|
|
|
|
|
|
|
|
|
ci->type = INDEX_NORMAL;
|
|
|
|
|
ci->file_start = 0ULL;
|
2018-04-25 23:51:13 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
ci->file = tf;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
|
ct->sector_size = sector_sizes[i];
|
|
|
|
|
int can_read_pvd = image_can_read_pvd(ci->file, 0, ct->sector_size, 0);
|
|
|
|
|
if (can_read_pvd) {
|
|
|
|
|
ct->mode = can_read_pvd >> 4;
|
|
|
|
|
ct->form = can_read_pvd & 0xf;
|
|
|
|
|
if (((ct->sector_size == 2332) || (ct->sector_size == 2336)) &&
|
|
|
|
|
(ct->form >= 1))
|
|
|
|
|
ct->skip = 8;
|
|
|
|
|
break;
|
|
|
|
|
} else if (ct->sector_size >= 2332) {
|
|
|
|
|
can_read_pvd = image_can_read_pvd(ci->file, 0, ct->sector_size, 1);
|
|
|
|
|
if (can_read_pvd) {
|
|
|
|
|
ct->mode = can_read_pvd >> 4;
|
|
|
|
|
ct->form = can_read_pvd & 0xf;
|
|
|
|
|
if (((ct->sector_size == 2332) || (ct->sector_size == 2336)) &&
|
|
|
|
|
(ct->form >= 1))
|
|
|
|
|
ct->skip = 8;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_set_track_subch_type(ct);
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, ATTR %02X, MODE %02X/%02X,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form);
|
|
|
|
|
image_log(img->log, " %02X:%02X:%02X, %02X, %i\n",
|
|
|
|
|
ct->sector_size);
|
2018-04-25 23:51:13 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (success) for (int i = 2; i >= 0; i--) {
|
|
|
|
|
if (ct->idx[i].file == NULL)
|
|
|
|
|
ct->idx[i].file = tf;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tf = NULL;
|
|
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
|
image_process(img);
|
|
|
|
|
else {
|
|
|
|
|
image_log(img->log, " [ISO ] Unable to open image or folder \"%s\"\n",
|
|
|
|
|
filename);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success;
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2024-05-19 21:17:57 +02:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_load_cue(cd_image_t *img, const char *cuefile)
|
2024-05-19 21:17:57 +02:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
track_t *ct = NULL;
|
|
|
|
|
track_index_t *ci = NULL;
|
|
|
|
|
track_file_t *tf = NULL;
|
|
|
|
|
uint64_t frame = 0ULL;
|
|
|
|
|
uint64_t last = 0ULL;
|
|
|
|
|
uint8_t session = 1;
|
|
|
|
|
int last_t = -1;
|
|
|
|
|
int is_viso = 0;
|
|
|
|
|
int lead[3] = { 0 };
|
|
|
|
|
int error;
|
|
|
|
|
char pathname[MAX_FILENAME_LENGTH];
|
|
|
|
|
char buf[MAX_LINE_LENGTH];
|
|
|
|
|
char *line;
|
|
|
|
|
char *command;
|
|
|
|
|
char *type;
|
|
|
|
|
char temp;
|
|
|
|
|
|
|
|
|
|
img->tracks = NULL;
|
|
|
|
|
img->tracks_num = 0;
|
|
|
|
|
|
|
|
|
|
/* Get a copy of the filename into pathname, we need it later. */
|
|
|
|
|
memset(pathname, 0, MAX_FILENAME_LENGTH * sizeof(char));
|
|
|
|
|
path_get_dirname(pathname, cuefile);
|
|
|
|
|
|
|
|
|
|
/* Open the file. */
|
|
|
|
|
FILE *fp = plat_fopen(cuefile, "r");
|
|
|
|
|
if (fp == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
int success = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Pass 1 - loading the Cue sheet.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 1 (loading the Cue sheet)...\n");
|
|
|
|
|
img->tracks_num = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
lead[i] = img->tracks_num;
|
|
|
|
|
(void) image_insert_track(img, session, 0xa0 + i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
line = buf;
|
|
|
|
|
|
|
|
|
|
/* Read a line from the cuesheet file. */
|
|
|
|
|
if (feof(fp) || (fgets(buf, sizeof(buf), fp) == NULL) || ferror(fp))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Do two iterations to make sure to nuke even if it's \r\n or \n\r,
|
|
|
|
|
but do checks to make sure we're not nuking other bytes. */
|
|
|
|
|
for (uint8_t i = 0; i < 2; i++) {
|
|
|
|
|
if (strlen(buf) > 0) {
|
|
|
|
|
if (buf[strlen(buf) - 1] == '\n')
|
|
|
|
|
buf[strlen(buf) - 1] = '\0';
|
|
|
|
|
/* nuke trailing newline */
|
|
|
|
|
else if (buf[strlen(buf) - 1] == '\r')
|
|
|
|
|
buf[strlen(buf) - 1] = '\0';
|
|
|
|
|
/* nuke trailing newline */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
image_log(img->log, " [LINE ] \"%s\"\n", line);
|
|
|
|
|
|
|
|
|
|
(void) image_cue_get_keyword(&command, &line);
|
|
|
|
|
|
|
|
|
|
if (!strcmp(command, "FILE")) {
|
|
|
|
|
/* The file for the track. */
|
|
|
|
|
char filename[MAX_FILENAME_LENGTH];
|
|
|
|
|
char ansi[MAX_FILENAME_LENGTH];
|
|
|
|
|
|
|
|
|
|
tf = NULL;
|
|
|
|
|
|
|
|
|
|
memset(ansi, 0, MAX_FILENAME_LENGTH * sizeof(char));
|
|
|
|
|
memset(filename, 0, MAX_FILENAME_LENGTH * sizeof(char));
|
|
|
|
|
|
|
|
|
|
success = image_cue_get_buffer(ansi, &line, 0);
|
|
|
|
|
if (!success)
|
|
|
|
|
break;
|
|
|
|
|
success = image_cue_get_keyword(&type, &line);
|
|
|
|
|
if (!success)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
error = 1;
|
|
|
|
|
is_viso = 0;
|
|
|
|
|
|
|
|
|
|
if (!strcmp(type, "BINARY") || !strcmp(type, "MOTOROLA")) {
|
|
|
|
|
if (!path_abs(ansi))
|
|
|
|
|
path_append_filename(filename, pathname, ansi);
|
|
|
|
|
else
|
|
|
|
|
strcpy(filename, ansi);
|
|
|
|
|
|
|
|
|
|
tf = index_file_init(img->dev->id, filename, &error, &is_viso);
|
|
|
|
|
|
|
|
|
|
if (tf)
|
|
|
|
|
tf->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);
|
|
|
|
|
tf = audio_init(img->dev->id, filename, &error);
|
|
|
|
|
}
|
|
|
|
|
if (error) {
|
|
|
|
|
if (tf != NULL) {
|
|
|
|
|
tf->close(tf);
|
|
|
|
|
tf = NULL;
|
|
|
|
|
}
|
|
|
|
|
success = 0;
|
|
|
|
|
} else if (is_viso)
|
|
|
|
|
success = 3;
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
if (!success)
|
|
|
|
|
image_log(img->log, " [FILE ] Unable to open file \"%s\" "
|
|
|
|
|
"specified in cue sheet\n", filename);
|
|
|
|
|
#endif
|
|
|
|
|
} else if (!strcmp(command, "TRACK")) {
|
|
|
|
|
int t = image_cue_get_number(&line);
|
|
|
|
|
success = image_cue_get_keyword(&type, &line);
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (last_t != -1) {
|
|
|
|
|
/*
|
|
|
|
|
Important: This has to be done like this because pointers
|
2025-07-24 12:08:58 +02:00
|
|
|
change due to realloc.
|
2025-01-28 16:26:28 +01:00
|
|
|
*/
|
2025-07-24 12:08:58 +02:00
|
|
|
ct = &(img->tracks[img->tracks_num - 1]);
|
2025-01-28 16:26:28 +01:00
|
|
|
|
|
|
|
|
for (int i = 2; i >= 0; i--) {
|
|
|
|
|
if (ct->idx[i].file == NULL)
|
|
|
|
|
ct->idx[i].file = tf;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_t = t;
|
|
|
|
|
ct = image_insert_track(img, session, t);
|
|
|
|
|
|
|
|
|
|
ct->form = 0;
|
|
|
|
|
ct->mode = 0;
|
|
|
|
|
|
|
|
|
|
if (!strcmp(type, "AUDIO")) {
|
|
|
|
|
ct->sector_size = RAW_SECTOR_SIZE;
|
|
|
|
|
ct->attr = AUDIO_TRACK;
|
|
|
|
|
} else if (!memcmp(type, "MODE", 4)) {
|
|
|
|
|
uint32_t mode;
|
|
|
|
|
ct->attr = DATA_TRACK;
|
|
|
|
|
sscanf(type, "MODE%" PRIu32 "/%" PRIu32,
|
|
|
|
|
&mode, &(ct->sector_size));
|
|
|
|
|
ct->mode = mode;
|
|
|
|
|
if (ct->mode == 2) switch(ct->sector_size) {
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
case 2324: case 2328:
|
|
|
|
|
ct->form = 2;
|
|
|
|
|
break;
|
|
|
|
|
case 2048: case 2332: case 2336: case 2352: case 2368: case 2448:
|
|
|
|
|
ct->form = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (((ct->sector_size == 2336) || (ct->sector_size == 2332)) && (ct->mode == 2) && (ct->form == 1))
|
|
|
|
|
ct->skip = 8;
|
|
|
|
|
} else if (!memcmp(type, "CD", 2)) {
|
|
|
|
|
ct->attr = DATA_TRACK;
|
|
|
|
|
ct->mode = 2;
|
|
|
|
|
sscanf(type, "CD%c/%i", &temp, &(ct->sector_size));
|
|
|
|
|
} else
|
|
|
|
|
success = 0;
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
image_set_track_subch_type(ct);
|
|
|
|
|
|
|
|
|
|
last = ct->sector_size;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X/%02X, ATTR %02X, MODE %02X/%02X,\n",
|
|
|
|
|
ct->session,
|
|
|
|
|
ct->point,
|
|
|
|
|
ct->attr,
|
|
|
|
|
ct->mode, ct->form);
|
|
|
|
|
image_log(img->log, " %i\n",
|
|
|
|
|
ct->sector_size);
|
|
|
|
|
}
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
else
|
|
|
|
|
image_log(img->log, " [TRACK ] Unable to initialize track %02X "
|
|
|
|
|
"specified in Cue sheet\n", t);
|
|
|
|
|
#endif
|
|
|
|
|
} else if (!strcmp(command, "INDEX")) {
|
|
|
|
|
int t = image_cue_get_number(&line);
|
|
|
|
|
ci = &(ct->idx[t]);
|
|
|
|
|
|
|
|
|
|
ci->type = INDEX_NORMAL;
|
|
|
|
|
ci->file = tf;
|
|
|
|
|
success = image_cue_get_frame(&frame, &line);
|
|
|
|
|
ci->file_start = frame;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [INDEX ] %02X (%8s): Initialization %s\n",
|
|
|
|
|
t, cit[ci->type + 2], success ? "successful" : "failed");
|
|
|
|
|
} else if (!strcmp(command, "PREGAP")) {
|
|
|
|
|
ci = &(ct->idx[0]);
|
|
|
|
|
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->file = tf;
|
|
|
|
|
success = image_cue_get_frame(&frame, &line);
|
|
|
|
|
ci->length = frame;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [INDEX ] 00 (%8s): Initialization %s\n",
|
|
|
|
|
cit[ci->type + 2], success ? "successful" : "failed");
|
|
|
|
|
} else if (!strcmp(command, "PAUSE") || !strcmp(command, "ZERO")) {
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->file = tf;
|
|
|
|
|
success = image_cue_get_frame(&frame, &line);
|
|
|
|
|
ci->length = frame;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [INDEX ] 01 (%8s): Initialization %s\n",
|
|
|
|
|
cit[ci->type + 2], success ? "successful" : "failed");
|
|
|
|
|
} else if (!strcmp(command, "POSTGAP")) {
|
|
|
|
|
ci = &(ct->idx[2]);
|
|
|
|
|
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->file = tf;
|
|
|
|
|
success = image_cue_get_frame(&frame, &line);
|
|
|
|
|
ci->length = frame;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [INDEX ] 02 (%8s): Initialization %s\n",
|
|
|
|
|
cit[ci->type + 2], success ? "successful" : "failed");
|
|
|
|
|
} else if (!strcmp(command, "FLAGS")) {
|
|
|
|
|
success = image_cue_get_flags(ct, &line);
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [FLAGS ] Initialization %s\n",
|
|
|
|
|
success ? "successful" : "failed");
|
|
|
|
|
} else if (!strcmp(command, "REM")) {
|
|
|
|
|
success = 1;
|
|
|
|
|
char *space = strstr(line, " ");
|
|
|
|
|
if (space != NULL) {
|
|
|
|
|
space++;
|
|
|
|
|
if (space < (line + strlen(line))) {
|
|
|
|
|
(void) image_cue_get_keyword(&command, &space);
|
|
|
|
|
if (!strcmp(command, "LEAD-OUT")) {
|
|
|
|
|
ct = &(img->tracks[lead[2]]);
|
|
|
|
|
/*
|
|
|
|
|
Mark it this way so file pointers on it are not
|
|
|
|
|
going to be adjusted.
|
|
|
|
|
*/
|
|
|
|
|
last_t = -1;
|
|
|
|
|
ct->sector_size = last;
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
ci->type = INDEX_NORMAL;
|
|
|
|
|
ci->file = tf;
|
|
|
|
|
success = image_cue_get_frame(&frame, &space);
|
|
|
|
|
ci->file_start = frame;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [LEAD-OUT] Initialization %s\n",
|
|
|
|
|
success ? "successful" : "failed");
|
|
|
|
|
} else if (!strcmp(command, "SESSION")) {
|
|
|
|
|
session = image_cue_get_number(&space);
|
|
|
|
|
|
|
|
|
|
if (session > 1) {
|
|
|
|
|
ct = image_insert_track(img, session - 1, 0xb0);
|
|
|
|
|
/*
|
|
|
|
|
Mark it this way so file pointers on it are not
|
|
|
|
|
going to be adjusted.
|
|
|
|
|
*/
|
|
|
|
|
last_t = -1;
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
ci->start = (0x40 * 60 * 75) + (0x02 * 75);
|
|
|
|
|
|
|
|
|
|
if (session == 2) {
|
|
|
|
|
ct->extra[3] = 0x02;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
00:00:00 on Wembley, C0:00:00 in the spec.
|
|
|
|
|
And what's in PMSF?
|
|
|
|
|
*/
|
|
|
|
|
ct = image_insert_track(img, session - 1, 0xc0);
|
|
|
|
|
/*
|
|
|
|
|
Mark it this way so file pointers on it are not
|
|
|
|
|
going to be adjusted.
|
|
|
|
|
*/
|
|
|
|
|
last_t = -1;
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
/* Queen - Live at Wembley '86 CD 1. */
|
|
|
|
|
ci->start = (0x5f * 75 * 60);
|
|
|
|
|
/* Optimum recording power. */
|
|
|
|
|
ct->extra[0] = 0x00;
|
|
|
|
|
} else
|
|
|
|
|
ct->extra[3] = 0x01;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
lead[i] = img->tracks_num;
|
|
|
|
|
(void) image_insert_track(img, session, 0xa0 + i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_log(img->log, " [SESSION ] Initialization successful\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcmp(command, "CATALOG") || !strcmp(command, "CDTEXTFILE") ||
|
|
|
|
|
!strcmp(command, "ISRC") || !strcmp(command, "PERFORMER") ||
|
|
|
|
|
!strcmp(command, "SONGWRITER") || !strcmp(command, "TITLE") ||
|
|
|
|
|
!strcmp(command, "")) {
|
|
|
|
|
/* Ignored commands. */
|
|
|
|
|
image_log(img->log, " [CUE ] Ignored command \"%s\" in Cue sheet\n",
|
|
|
|
|
command);
|
|
|
|
|
success = 1;
|
|
|
|
|
} else {
|
|
|
|
|
image_log(img->log, " [CUE ] Unsupported command \"%s\" in Cue sheet\n",
|
|
|
|
|
command);
|
|
|
|
|
success = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (success && (ct != NULL)) for (int i = 2; i >= 0; i--) {
|
|
|
|
|
if (ct->idx[i].file == NULL)
|
|
|
|
|
ct->idx[i].file = tf;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tf = NULL;
|
|
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
|
image_process(img);
|
|
|
|
|
else {
|
|
|
|
|
image_log(img->log, " [CUE ] Unable to open Cue sheet \"%s\"\n", cuefile);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success;
|
2024-05-19 21:17:57 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
static int
|
|
|
|
|
image_load_mds(cd_image_t *img, const char *mdsfile)
|
|
|
|
|
{
|
|
|
|
|
track_t *ct = NULL;
|
|
|
|
|
track_index_t *ci = NULL;
|
|
|
|
|
track_file_t *tf = NULL;
|
|
|
|
|
int is_viso = 0;
|
|
|
|
|
int last_t = -1;
|
|
|
|
|
int error;
|
|
|
|
|
char pathname[MAX_FILENAME_LENGTH];
|
|
|
|
|
|
|
|
|
|
mds_hdr_t mds_hdr = { 0 };
|
|
|
|
|
mds_sess_block_t mds_sess_block = { 0 };
|
|
|
|
|
mds_trk_block_t mds_trk_block = { 0 };
|
|
|
|
|
mds_trk_ex_block_t mds_trk_ex_block = { 0 };
|
|
|
|
|
mds_footer_t mds_footer = { 0 };
|
|
|
|
|
mds_dpm_block_t mds_dpm_block = { 0 };
|
|
|
|
|
uint32_t mds_dpm_blocks_num = 0x00000000;
|
|
|
|
|
uint32_t mds_dpm_block_offs = 0x00000000;
|
|
|
|
|
|
|
|
|
|
img->tracks = NULL;
|
|
|
|
|
img->tracks_num = 0;
|
|
|
|
|
|
|
|
|
|
/* Get a copy of the filename into pathname, we need it later. */
|
|
|
|
|
memset(pathname, 0, MAX_FILENAME_LENGTH * sizeof(char));
|
|
|
|
|
path_get_dirname(pathname, mdsfile);
|
|
|
|
|
|
|
|
|
|
/* Open the file. */
|
|
|
|
|
FILE *fp = plat_fopen(mdsfile, "rb");
|
|
|
|
|
if (fp == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
int success = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Pass 1 - loading the MDS sheet.
|
|
|
|
|
*/
|
|
|
|
|
image_log(img->log, "Pass 1 (loading the Media Descriptor Sheet)...\n");
|
|
|
|
|
img->tracks_num = 0;
|
|
|
|
|
success = 2;
|
|
|
|
|
|
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
|
fread(&mds_hdr, 1, sizeof(mds_hdr_t), fp);
|
|
|
|
|
if (memcmp(mds_hdr.file_sig, "MEDIA DESCRIPTOR", 16)) {
|
|
|
|
|
image_log(img->log, " [MDS ] Not an actual MDF file \"%s\"\n", mdsfile);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mds_hdr.file_ver[0] == 0x02) {
|
|
|
|
|
image_log(img->log, " [MDS ] Daemon tools encrypted MDS is not supported in file \"%s\"\n", mdsfile);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
img->is_dvd = (mds_hdr.medium_type >= 0x10);
|
|
|
|
|
|
|
|
|
|
if (img->is_dvd) {
|
|
|
|
|
if (mds_hdr.disc_struct_offs != 0x00) {
|
|
|
|
|
fseek(fp, mds_hdr.disc_struct_offs, SEEK_SET);
|
|
|
|
|
fread(&(img->dstruct.layers[0]), 1, sizeof(layer_t), fp);
|
|
|
|
|
img->has_dstruct = 1;
|
|
|
|
|
|
|
|
|
|
if (((img->dstruct.layers[0].f0[2] & 0x60) >> 4) == 0x01) {
|
|
|
|
|
fseek(fp, mds_hdr.disc_struct_offs, SEEK_SET);
|
|
|
|
|
fread(&(img->dstruct.layers[1]), 1, sizeof(layer_t), fp);
|
|
|
|
|
img->has_dstruct++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int t = 0; t < 3; t++) {
|
|
|
|
|
ct = image_insert_track(img, 1, 0xa0 + t);
|
|
|
|
|
|
|
|
|
|
ct->attr = DATA_TRACK;
|
|
|
|
|
ct->mode = 0;
|
|
|
|
|
ct->form = 0;
|
|
|
|
|
ct->tno = 0;
|
|
|
|
|
ct->subch_type = 0;
|
|
|
|
|
memset(ct->extra, 0x00, 4);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
ci = &(ct->idx[i]);
|
|
|
|
|
ci->type = INDEX_NONE;
|
|
|
|
|
ci->start = 0;
|
|
|
|
|
ci->length = 0;
|
|
|
|
|
ci->file_start = 0;
|
|
|
|
|
ci->file_length = 0;
|
|
|
|
|
ci->file = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
|
|
|
|
|
if (t < 2)
|
|
|
|
|
ci->start = (0x01 * 60 * 75) + (0 * 75) + 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mds_hdr.dpm_blocks_offs != 0x00) {
|
|
|
|
|
fseek(fp, mds_hdr.dpm_blocks_offs, SEEK_SET);
|
|
|
|
|
fread(&mds_dpm_blocks_num, 1, sizeof(uint32_t), fp);
|
|
|
|
|
|
|
|
|
|
if (mds_dpm_blocks_num > 0) for (int b = 0; b < mds_dpm_blocks_num; b++) {
|
|
|
|
|
fseek(fp, mds_hdr.dpm_blocks_offs + 4 + (b * 4), SEEK_SET);
|
|
|
|
|
fread(&mds_dpm_block_offs, 1, sizeof(uint32_t), fp);
|
|
|
|
|
|
|
|
|
|
fseek(fp, mds_dpm_block_offs, SEEK_SET);
|
|
|
|
|
fread(&mds_dpm_block, 1, 4 * sizeof(mds_dpm_block_t), fp);
|
|
|
|
|
|
|
|
|
|
/* We currently only support the bad sectors block and not (yet) actual DPM. */
|
|
|
|
|
if (mds_dpm_block.type == 0x00000002) {
|
|
|
|
|
/* Bad sectors. */
|
|
|
|
|
img->bad_sectors_num = mds_dpm_block.entries;
|
|
|
|
|
img->bad_sectors = (uint32_t *) malloc(img->bad_sectors_num * sizeof(uint32_t));
|
|
|
|
|
fseek(fp, mds_dpm_block_offs + sizeof(mds_dpm_block_t), SEEK_SET);
|
|
|
|
|
fread(img->bad_sectors, 1, img->bad_sectors_num * sizeof(uint32_t), fp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int s = 0; s < mds_hdr.sess_num; s++) {
|
|
|
|
|
fseek(fp, mds_hdr.sess_blocks_offs + (s * sizeof(mds_sess_block_t)), SEEK_SET);
|
|
|
|
|
fread(&mds_sess_block, 1, sizeof(mds_sess_block_t), fp);
|
|
|
|
|
|
|
|
|
|
for (int t = 0; t < mds_sess_block.all_blocks_num; t++) {
|
|
|
|
|
fseek(fp, mds_sess_block.trk_blocks_offs + (t * sizeof(mds_trk_block_t)), SEEK_SET);
|
|
|
|
|
fread(&mds_trk_block, 1, sizeof(mds_trk_block_t), fp);
|
|
|
|
|
|
|
|
|
|
if (last_t != -1) {
|
|
|
|
|
/*
|
|
|
|
|
Important: This has to be done like this because pointers
|
|
|
|
|
change due to realloc.
|
|
|
|
|
*/
|
|
|
|
|
ct = &(img->tracks[img->tracks_num - 1]);
|
|
|
|
|
|
|
|
|
|
for (int i = 2; i >= 0; i--) {
|
|
|
|
|
if (ct->idx[i].file == NULL)
|
|
|
|
|
ct->idx[i].file = tf;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_t = mds_trk_block.point;
|
|
|
|
|
ct = image_insert_track(img, mds_sess_block.sess_id, mds_trk_block.point);
|
|
|
|
|
|
|
|
|
|
tf = NULL;
|
|
|
|
|
|
|
|
|
|
if (img->is_dvd) {
|
|
|
|
|
/* DVD images have no extra block - the extra block offset is the track length. */
|
|
|
|
|
memset(&mds_trk_ex_block, 0x00, sizeof(mds_trk_ex_block_t));
|
|
|
|
|
mds_trk_ex_block.pregap = 0x00000000;
|
|
|
|
|
mds_trk_ex_block.trk_sectors = mds_trk_block.ex_offs;
|
|
|
|
|
} else if (mds_trk_block.ex_offs != 0ULL) {
|
|
|
|
|
fseek(fp, mds_trk_block.ex_offs, SEEK_SET);
|
|
|
|
|
fread(&mds_trk_ex_block, 1, sizeof(mds_trk_ex_block), fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t astart = mds_trk_block.start_sect - mds_trk_ex_block.pregap;
|
|
|
|
|
uint32_t aend = astart + mds_trk_ex_block.pregap;
|
|
|
|
|
uint32_t aend2 = aend + mds_trk_ex_block.trk_sectors;
|
|
|
|
|
uint32_t astart2 = mds_trk_block.start_sect + mds_trk_ex_block.trk_sectors;
|
|
|
|
|
|
|
|
|
|
if (mds_trk_block.footer_offs != 0ULL) for (uint32_t ff = 0; ff < mds_trk_block.files_num; ff++) {
|
|
|
|
|
fseek(fp, mds_trk_block.footer_offs + (ff * sizeof(mds_footer_t)), SEEK_SET);
|
|
|
|
|
fread(&mds_footer, 1, sizeof(mds_footer_t), fp);
|
|
|
|
|
|
|
|
|
|
wchar_t wfn[2048] = { 0 };
|
|
|
|
|
char fn[2048] = { 0 };
|
|
|
|
|
fseek(fp, mds_footer.fn_offs, SEEK_SET);
|
|
|
|
|
if (mds_footer.fn_is_wide) {
|
|
|
|
|
fgetws(wfn, 256, fp);
|
|
|
|
|
wcstombs(fn, wfn, 256);
|
|
|
|
|
} else
|
|
|
|
|
fgets(fn, 512, fp);
|
|
|
|
|
if (!stricmp(fn, "*.mdf")) {
|
|
|
|
|
strcpy(fn, mdsfile);
|
|
|
|
|
fn[strlen(mdsfile) - 3] = 'm';
|
|
|
|
|
fn[strlen(mdsfile) - 2] = 'd';
|
|
|
|
|
fn[strlen(mdsfile) - 1] = 'f';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char filename[2048] = { 0 };
|
|
|
|
|
if (!path_abs(fn))
|
|
|
|
|
path_append_filename(filename, pathname, fn);
|
|
|
|
|
else
|
|
|
|
|
strcpy(filename, fn);
|
|
|
|
|
|
|
|
|
|
tf = index_file_init(img->dev->id, filename, &error, &is_viso);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ct->sector_size = mds_trk_block.sector_len;
|
|
|
|
|
ct->form = 0;
|
|
|
|
|
ct->tno = mds_trk_block.track_id;
|
|
|
|
|
ct->subch_type = mds_trk_block.subch_mode;
|
|
|
|
|
ct->extra[0] = mds_trk_block.m;
|
|
|
|
|
ct->extra[1] = mds_trk_block.s;
|
|
|
|
|
ct->extra[2] = mds_trk_block.f;
|
|
|
|
|
ct->extra[3] = mds_trk_block.zero;
|
|
|
|
|
/*
|
|
|
|
|
Note from DiscImageCreator:
|
|
|
|
|
|
|
|
|
|
I hexedited the track mode field with various values and fed it to Alchohol;
|
|
|
|
|
it seemed that high part of byte had no effect at all; only the lower one
|
|
|
|
|
affected the mode, in the following manner:
|
|
|
|
|
00: Mode 2, 01: Audio, 02: Mode 1, 03: Mode 2, 04: Mode 2 Form 1,
|
|
|
|
|
05: Mode 2 Form 2, 06: UKNONOWN, 07: Mode 2
|
|
|
|
|
08: Mode 2, 09: Audio, 0A: Mode 1, 0B: Mode 2, 0C: Mode 2 Form 1,
|
|
|
|
|
0D: Mode 2 Form 2, 0E: UKNONOWN, 0F: Mode 2
|
|
|
|
|
*/
|
|
|
|
|
ct->attr = ((mds_trk_block.trk_mode & 0x07) == 0x01) ?
|
|
|
|
|
AUDIO_TRACK : DATA_TRACK;
|
|
|
|
|
ct->mode = 0;
|
|
|
|
|
ct->form = 0;
|
|
|
|
|
if (((mds_trk_block.trk_mode & 0x07) != 0x01) &&
|
|
|
|
|
((mds_trk_block.trk_mode & 0x07) != 0x06))
|
|
|
|
|
ct->mode = ((mds_trk_block.trk_mode & 0x07) != 0x02) + 1;
|
|
|
|
|
if ((mds_trk_block.trk_mode & 0x06) == 0x04)
|
|
|
|
|
ct->form = (mds_trk_block.trk_mode & 0x07) - 0x03;
|
|
|
|
|
if (ct->attr == AUDIO_TRACK)
|
|
|
|
|
success = 1;
|
|
|
|
|
|
|
|
|
|
if (((ct->sector_size == 2336) || (ct->sector_size == 2332)) && (ct->mode == 2) && (ct->form == 1))
|
|
|
|
|
ct->skip = 8;
|
|
|
|
|
|
|
|
|
|
ci = &(ct->idx[0]);
|
|
|
|
|
if (ct->point < 0xa0) {
|
|
|
|
|
ci->start = astart + 150;
|
|
|
|
|
ci->length = mds_trk_ex_block.pregap;
|
|
|
|
|
}
|
|
|
|
|
ci->type = (ci->length > 0) ? INDEX_ZERO : INDEX_NONE;
|
|
|
|
|
ci->file_start = 0;
|
|
|
|
|
ci->file_length = 0;
|
|
|
|
|
ci->file = NULL;
|
|
|
|
|
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
if ((mds_trk_block.point >= 1) && (mds_trk_block.point <= 99)) {
|
|
|
|
|
ci->start = aend + 150;
|
|
|
|
|
ci->length = mds_trk_ex_block.trk_sectors;
|
|
|
|
|
ci->type = INDEX_NORMAL;
|
|
|
|
|
ci->file_start = mds_trk_block.start_offs / ct->sector_size;
|
|
|
|
|
ci->file_length = ci->length;
|
|
|
|
|
ci->file = tf;
|
|
|
|
|
} else {
|
|
|
|
|
ci->start = (mds_trk_block.pm * 60 * 75) + (mds_trk_block.ps * 75) + mds_trk_block.pf;
|
|
|
|
|
ci->type = INDEX_ZERO;
|
|
|
|
|
ci->file_start = 0;
|
|
|
|
|
ci->file_length = 0;
|
|
|
|
|
ci->file = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ci = &(ct->idx[2]);
|
|
|
|
|
if (ct->point < 0xa0) {
|
|
|
|
|
ci->start = aend2 + 150;
|
|
|
|
|
ci->length = astart2 - aend2;
|
|
|
|
|
}
|
|
|
|
|
ci->type = (ci->length > 0) ? INDEX_ZERO : INDEX_NONE;
|
|
|
|
|
ci->file_start = 0;
|
|
|
|
|
ci->file_length = 0;
|
|
|
|
|
ci->file = NULL;
|
|
|
|
|
|
|
|
|
|
if (img->is_dvd) {
|
|
|
|
|
ci = &(ct->idx[1]);
|
|
|
|
|
uint32_t total = ci->start + ci->length;
|
|
|
|
|
|
|
|
|
|
ci = &(img->tracks[2].idx[1]);
|
|
|
|
|
ci->start = total;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 2; i >= 0; i--) {
|
|
|
|
|
if (ct->point >= 0xa0)
|
|
|
|
|
ci->type = INDEX_SPECIAL;
|
|
|
|
|
|
|
|
|
|
if (ct->idx[i].file == NULL)
|
|
|
|
|
ct->idx[i].file = tf;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tf = NULL;
|
|
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
// image_process(img);
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
image_log(img->log, "Final tracks list:\n");
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
ct = &(img->tracks[i]);
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
ci = &(ct->idx[j]);
|
|
|
|
|
image_log(img->log, " [TRACK ] %02X INDEX %02X: [%8s, %016" PRIX64 "]\n",
|
|
|
|
|
ct->point, j,
|
|
|
|
|
cit[ci->type + 2], ci->file_start * ct->sector_size);
|
|
|
|
|
image_log(img->log, " TOC data: %02X %02X %02X "
|
|
|
|
|
"%02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
|
|
|
ct->session, ct->attr, ct->tno, ct->point,
|
|
|
|
|
ct->extra[0], ct->extra[1], ct->extra[2], ct->extra[3],
|
|
|
|
|
(uint32_t) ((ci->start / 75) / 60),
|
|
|
|
|
(uint32_t) ((ci->start / 75) % 60),
|
|
|
|
|
(uint32_t) (ci->start % 75));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
} else {
|
|
|
|
|
image_log(img->log, " [MDS ] Unable to open MDS sheet \"%s\"\n", mdsfile);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
/* Root functions. */
|
2018-10-17 05:29:48 +02:00
|
|
|
static void
|
2025-01-28 16:26:28 +01:00
|
|
|
image_clear_tracks(cd_image_t *img)
|
2017-05-18 14:03:43 -04:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
const track_file_t *last = NULL;
|
|
|
|
|
track_t *cur = NULL;
|
|
|
|
|
track_index_t *idx = NULL;
|
|
|
|
|
|
|
|
|
|
if ((img->tracks != NULL) && (img->tracks_num > 0)) {
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
cur = &img->tracks[i];
|
2018-03-17 20:32:20 +01:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (((cur->point >= 1) && (cur->point <= 99)) ||
|
|
|
|
|
(cur->point == 0xa2)) for (int j = 0; j < 3; j++) {
|
2025-01-28 23:22:06 +01:00
|
|
|
idx = &(cur->idx[j]);
|
|
|
|
|
/* Make sure we do not attempt to close a NULL file. */
|
|
|
|
|
if ((idx->file != NULL) && (idx->type == INDEX_NORMAL)) {
|
|
|
|
|
if (idx->file != last) {
|
|
|
|
|
last = idx->file;
|
|
|
|
|
index_file_close(idx);
|
|
|
|
|
} else
|
|
|
|
|
idx->file = NULL;
|
|
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now free the array. */
|
|
|
|
|
free(img->tracks);
|
|
|
|
|
img->tracks = NULL;
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
/* Mark that there's no tracks. */
|
|
|
|
|
img->tracks_num = 0;
|
2018-04-25 23:51:13 +02:00
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
}
|
2017-05-18 14:03:43 -04:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
/* Shared functions. */
|
|
|
|
|
static int
|
|
|
|
|
image_get_track_info(const void *local, const uint32_t track,
|
|
|
|
|
const int end, track_info_t *ti)
|
|
|
|
|
{
|
|
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
const track_t *ct = NULL;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
ct = &(img->tracks[i]);
|
|
|
|
|
if (ct->point == track)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ct != NULL) {
|
|
|
|
|
const uint32_t pos = end ? ct->idx[1].start :
|
|
|
|
|
(ct->idx[1].start + ct->idx[1].length);
|
|
|
|
|
|
|
|
|
|
ti->number = ct->point;
|
|
|
|
|
ti->attr = ct->attr;
|
|
|
|
|
ti->m = (pos / 75) / 60;
|
|
|
|
|
ti->s = (pos / 75) % 60;
|
|
|
|
|
ti->f = pos % 75;
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
2017-05-18 14:03:43 -04:00
|
|
|
}
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
static void
|
|
|
|
|
image_get_raw_track_info(const void *local, int *num, uint8_t *buffer)
|
|
|
|
|
{
|
|
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, "img->tracks_num = %i\n", img->tracks_num);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
const track_t *ct = &(img->tracks[i]);
|
|
|
|
|
#ifdef ENABLE_IMAGE_LOG
|
|
|
|
|
int old_len = len;
|
|
|
|
|
#endif
|
|
|
|
|
buffer[len++] = ct->session; /* Session number. */
|
|
|
|
|
buffer[len++] = ct->attr; /* Track ADR and Control. */
|
|
|
|
|
buffer[len++] = ct->tno; /* TNO (always 0). */
|
|
|
|
|
buffer[len++] = ct->point; /* Point (track number). */
|
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
|
|
|
buffer[len++] = ct->extra[j];
|
|
|
|
|
buffer[len++] = (ct->idx[1].start / 75) / 60;
|
|
|
|
|
buffer[len++] = (ct->idx[1].start / 75) % 60;
|
|
|
|
|
buffer[len++] = ct->idx[1].start % 75;
|
|
|
|
|
image_log(img->log, "%i: %02X %02X %02X %02X %02X %02X %02X\n", i,
|
|
|
|
|
buffer[old_len], buffer[old_len + 1],
|
|
|
|
|
buffer[old_len + 2], buffer[old_len + 3],
|
|
|
|
|
buffer[old_len + 8], buffer[old_len + 9],
|
|
|
|
|
buffer[old_len + 10]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*num = img->tracks_num;
|
|
|
|
|
}
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2018-10-30 13:32:25 +01:00
|
|
|
static int
|
2025-01-28 16:26:28 +01:00
|
|
|
image_is_track_pre(const void *local, const uint32_t sector)
|
2018-10-30 13:32:25 +01:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (img->has_audio) {
|
|
|
|
|
const int track = image_get_track(img, sector);
|
|
|
|
|
|
|
|
|
|
if (track >= 0) {
|
|
|
|
|
const track_t *trk = &(img->tracks[track]);
|
|
|
|
|
|
|
|
|
|
ret = !!(trk->attr & 0x01);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
2018-10-30 13:32:25 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
static int
|
|
|
|
|
image_read_sector(const void *local, uint8_t *buffer,
|
|
|
|
|
const uint32_t sector)
|
2018-10-17 05:29:48 +02:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
int m = 0;
|
|
|
|
|
int s = 0;
|
|
|
|
|
int f = 0;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
uint32_t lba = sector;
|
|
|
|
|
int track;
|
|
|
|
|
int index;
|
|
|
|
|
uint8_t q[16] = { 0x00 };
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (sector == 0xffffffff)
|
|
|
|
|
lba = img->dev->seek_pos;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
const uint64_t sect = (uint64_t) lba;
|
2018-10-30 13:32:25 +01:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
image_get_track_and_index(img, lba, &track, &index);
|
2018-10-30 13:32:25 +01:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
const track_t *trk = &(img->tracks[track]);
|
|
|
|
|
const track_index_t *idx = &(trk->idx[index]);
|
|
|
|
|
const int track_is_raw = ((trk->sector_size == RAW_SECTOR_SIZE) ||
|
|
|
|
|
(trk->sector_size == 2448));
|
|
|
|
|
const uint64_t seek = ((sect + 150 - idx->start + idx->file_start) *
|
|
|
|
|
trk->sector_size) + trk->skip;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (track >= 0) {
|
|
|
|
|
/* Signal CIRC error to the guest if sector is bad. */
|
|
|
|
|
ret = image_is_sector_bad(img, lba) ? -1 : 1;
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
if (ret > 0) {
|
|
|
|
|
uint64_t offset = 0ULL;
|
|
|
|
|
|
|
|
|
|
image_log(img->log, "cdrom_read_sector(%08X): track %02X, index %02X, %016"
|
|
|
|
|
PRIX64 ", %i, %i, %i, %i\n",
|
|
|
|
|
lba, track, index, idx->start, trk->sector_size, track_is_raw,
|
|
|
|
|
trk->mode, trk->form);
|
|
|
|
|
|
|
|
|
|
memset(buffer, 0x00, 2448);
|
|
|
|
|
|
|
|
|
|
if ((trk->attr & 0x04) && ((idx->type < INDEX_NORMAL) || !track_is_raw)) {
|
|
|
|
|
offset += 16ULL;
|
|
|
|
|
|
|
|
|
|
/* Construct the header. */
|
|
|
|
|
memset(buffer + 1, 0xff, 10);
|
|
|
|
|
buffer += 12;
|
|
|
|
|
FRAMES_TO_MSF(sector + 150, &m, &s, &f);
|
|
|
|
|
/* These have to be BCD. */
|
|
|
|
|
buffer[0] = bin2bcd(m & 0xff);
|
|
|
|
|
buffer[1] = bin2bcd(s & 0xff);
|
|
|
|
|
buffer[2] = bin2bcd(f & 0xff);
|
|
|
|
|
/* Data, should reflect the actual sector type. */
|
|
|
|
|
buffer[3] = trk->mode;
|
|
|
|
|
buffer += 4;
|
|
|
|
|
if (trk->form >= 1) {
|
|
|
|
|
offset += 8ULL;
|
|
|
|
|
|
|
|
|
|
/* Construct the CD-I/XA sub-header. */
|
|
|
|
|
buffer[2] = buffer[6] = (trk->form - 1) << 5;
|
|
|
|
|
buffer += 8;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idx->type >= INDEX_NORMAL) {
|
|
|
|
|
/* Read the data from the file. */
|
|
|
|
|
ret = idx->file->read(idx->file, buffer, seek, trk->sector_size);
|
|
|
|
|
} else
|
|
|
|
|
/* Index is not in the file, no read to fail here. */
|
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
|
|
if ((ret > 0) && ((idx->type < INDEX_NORMAL) || (trk->subch_type != 0x08))) {
|
|
|
|
|
buffer -= offset;
|
|
|
|
|
|
|
|
|
|
if (trk->subch_type == 0x10)
|
|
|
|
|
memcpy(q, &(buffer[2352]), 12);
|
|
|
|
|
else {
|
|
|
|
|
/* Construct Q. */
|
|
|
|
|
q[0] = (trk->attr >> 4) | ((trk->attr & 0xf) << 4);
|
|
|
|
|
q[1] = bin2bcd(trk->point);
|
|
|
|
|
q[2] = index;
|
|
|
|
|
if (index == 0) {
|
|
|
|
|
/*
|
|
|
|
|
Pre-gap sector relative frame addresses count from
|
|
|
|
|
00:01:74 downwards.
|
|
|
|
|
*/
|
|
|
|
|
FRAMES_TO_MSF((int32_t) (149 - (lba + 150 - idx->start)), &m, &s, &f);
|
|
|
|
|
} else {
|
|
|
|
|
FRAMES_TO_MSF((int32_t) (lba + 150 - idx->start), &m, &s, &f);
|
|
|
|
|
}
|
|
|
|
|
q[3] = bin2bcd(m & 0xff);
|
|
|
|
|
q[4] = bin2bcd(s & 0xff);
|
|
|
|
|
q[5] = bin2bcd(f & 0xff);
|
|
|
|
|
FRAMES_TO_MSF(lba + 150, &m, &s, &f);
|
|
|
|
|
q[7] = bin2bcd(m & 0xff);
|
|
|
|
|
q[8] = bin2bcd(s & 0xff);
|
|
|
|
|
q[9] = bin2bcd(f & 0xff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Construct raw subchannel data from Q only. */
|
|
|
|
|
for (int i = 0; i < 12; i++)
|
|
|
|
|
for (int j = 0; j < 8; j++)
|
|
|
|
|
buffer[2352 + (i << 3) + j] = ((q[i] >> (7 - j)) & 0x01) << 6;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
static uint8_t
|
|
|
|
|
image_get_track_type(const void *local, const uint32_t sector)
|
|
|
|
|
{
|
|
|
|
|
const cd_image_t *img = (cd_image_t *) local;
|
|
|
|
|
const int track = image_get_track(img, sector);
|
|
|
|
|
const track_t * trk = &(img->tracks[track]);
|
|
|
|
|
int ret = 0x00;
|
|
|
|
|
|
|
|
|
|
if (image_is_track_audio(img, sector))
|
|
|
|
|
ret = CD_TRACK_AUDIO;
|
|
|
|
|
else if (track >= 0) for (int i = 0; i < img->tracks_num; i++) {
|
|
|
|
|
const track_t *ct = &(img->tracks[i]);
|
|
|
|
|
const track_t *nt = &(img->tracks[i + 1]);
|
|
|
|
|
|
|
|
|
|
if (ct->point == 0xa0) {
|
2025-01-28 23:22:06 +01:00
|
|
|
const uint8_t first = (ct->idx[1].start / 75 / 60);
|
|
|
|
|
const uint8_t last = (nt->idx[1].start / 75 / 60);
|
2025-01-28 16:26:28 +01:00
|
|
|
|
|
|
|
|
if ((trk->point >= first) && (trk->point <= last)) {
|
|
|
|
|
ret = (ct->idx[1].start / 75) % 60;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
image_get_last_block(const void *local)
|
|
|
|
|
{
|
|
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
uint32_t lb = 0x00000000;
|
|
|
|
|
|
|
|
|
|
if (img != NULL) {
|
|
|
|
|
const track_t *lo = NULL;
|
|
|
|
|
|
|
|
|
|
for (int i = (img->tracks_num - 1); i >= 0; i--) {
|
|
|
|
|
if (img->tracks[i].point == 0xa2) {
|
|
|
|
|
lo = &(img->tracks[i]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lo != NULL)
|
|
|
|
|
lb = lo->idx[1].start - 1;
|
|
|
|
|
}
|
2018-10-17 05:29:48 +02:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
return lb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
image_read_dvd_structure(const void *local, const uint8_t layer, const uint8_t format,
|
|
|
|
|
uint8_t *buffer, uint32_t *info)
|
|
|
|
|
{
|
2025-07-24 12:08:58 +02:00
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if ((img->has_dstruct > 0) && ((layer + 1) > img->has_dstruct)) {
|
|
|
|
|
switch (format) {
|
|
|
|
|
case 0x00:
|
|
|
|
|
memcpy(buffer + 4, img->dstruct.layers[layer].f0, 2048);
|
|
|
|
|
ret = 2048 + 2;
|
|
|
|
|
break;
|
|
|
|
|
case 0x01:
|
|
|
|
|
memcpy(buffer + 4, img->dstruct.layers[layer].f1, 4);
|
|
|
|
|
ret = 4 + 2;
|
|
|
|
|
break;
|
|
|
|
|
case 0x04:
|
|
|
|
|
memcpy(buffer + 4, img->dstruct.layers[layer].f4, 2048);
|
|
|
|
|
ret = 2048 + 2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
2018-10-17 05:29:48 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
static int
|
|
|
|
|
image_is_dvd(const void *local)
|
2018-10-17 05:29:48 +02:00
|
|
|
{
|
2025-01-28 16:26:28 +01:00
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
|
|
|
|
|
return img->is_dvd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
image_has_audio(const void *local)
|
|
|
|
|
{
|
|
|
|
|
const cd_image_t *img = (const cd_image_t *) local;
|
|
|
|
|
|
|
|
|
|
return img->has_audio;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
image_close(void *local)
|
|
|
|
|
{
|
|
|
|
|
cd_image_t *img = (cd_image_t *) local;
|
|
|
|
|
|
|
|
|
|
if (img != NULL) {
|
|
|
|
|
image_clear_tracks(img);
|
|
|
|
|
|
|
|
|
|
image_log(img->log, "Log closed\n");
|
|
|
|
|
|
|
|
|
|
log_close(img->log);
|
|
|
|
|
img->log = NULL;
|
|
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
if (img->bad_sectors != NULL)
|
|
|
|
|
free(img->bad_sectors);
|
|
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
free(img);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const cdrom_ops_t image_ops = {
|
|
|
|
|
image_get_track_info,
|
|
|
|
|
image_get_raw_track_info,
|
|
|
|
|
image_is_track_pre,
|
|
|
|
|
image_read_sector,
|
|
|
|
|
image_get_track_type,
|
|
|
|
|
image_get_last_block,
|
|
|
|
|
image_read_dvd_structure,
|
|
|
|
|
image_is_dvd,
|
|
|
|
|
image_has_audio,
|
|
|
|
|
NULL,
|
2025-02-03 10:57:19 +01:00
|
|
|
image_close,
|
|
|
|
|
NULL
|
2025-01-28 16:26:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Public functions. */
|
|
|
|
|
void *
|
|
|
|
|
image_open(cdrom_t *dev, const char *path)
|
|
|
|
|
{
|
|
|
|
|
const uintptr_t ext = path + strlen(path) - strrchr(path, '.');
|
|
|
|
|
cd_image_t *img = (cd_image_t *) calloc(1, sizeof(cd_image_t));
|
|
|
|
|
|
|
|
|
|
if (img != NULL) {
|
|
|
|
|
int ret;
|
2025-07-24 12:08:58 +02:00
|
|
|
const int is_cue = ((ext == 4) && !stricmp(path + strlen(path) - ext + 1, "CUE"));
|
|
|
|
|
const int is_mds = ((ext == 4) && !stricmp(path + strlen(path) - ext + 1, "MDS"));
|
|
|
|
|
char n[1024] = { 0 };
|
|
|
|
|
|
|
|
|
|
sprintf(n, "CD-ROM %i Image", dev->id + 1);
|
|
|
|
|
img->log = log_open(n);
|
2025-01-28 16:26:28 +01:00
|
|
|
|
2025-01-28 18:39:26 +01:00
|
|
|
img->dev = dev;
|
|
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
if (is_mds) {
|
|
|
|
|
ret = image_load_mds(img, path);
|
|
|
|
|
|
|
|
|
|
if (ret >= 2)
|
|
|
|
|
img->has_audio = 0;
|
|
|
|
|
else if (ret)
|
|
|
|
|
img->has_audio = 1;
|
|
|
|
|
else {
|
|
|
|
|
image_close(img);
|
|
|
|
|
img = NULL;
|
|
|
|
|
}
|
|
|
|
|
} else if (is_cue) {
|
2025-01-28 16:26:28 +01:00
|
|
|
ret = image_load_cue(img, path);
|
|
|
|
|
|
|
|
|
|
if (ret >= 2)
|
|
|
|
|
img->has_audio = 0;
|
|
|
|
|
else if (ret)
|
|
|
|
|
img->has_audio = 1;
|
2025-05-06 03:41:33 +02:00
|
|
|
else {
|
|
|
|
|
image_close(img);
|
|
|
|
|
img = NULL;
|
|
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
} else {
|
|
|
|
|
ret = image_load_iso(img, path);
|
|
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
|
image_close(img);
|
|
|
|
|
img = NULL;
|
|
|
|
|
} else
|
|
|
|
|
img->has_audio = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-24 12:08:58 +02:00
|
|
|
if (ret)
|
2025-01-28 16:26:28 +01:00
|
|
|
dev->ops = &image_ops;
|
2025-07-24 12:08:58 +02:00
|
|
|
else {
|
|
|
|
|
log_warning(img->log, "Unable to load CD-ROM image: %s\n", path);
|
|
|
|
|
log_close(img->log);
|
|
|
|
|
}
|
2025-01-28 16:26:28 +01:00
|
|
|
}
|
2018-10-30 13:32:25 +01:00
|
|
|
|
2025-01-28 16:26:28 +01:00
|
|
|
return img;
|
2018-10-17 05:29:48 +02:00
|
|
|
}
|