This repository has been archived on 2025-05-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
libcdio-osx/lib/driver/image/nrg.c
Natalia Portillo c0d32b3e8c Ignore Release and Debug folders.
Windows SDK does have fcntl.h, snprintf, sys/stat.h, vsnprintf, windows.h and lseek64. Does not have inttypes.h. Modify config.h to reflect this.
Windows 8 WDK requires us to specify ddk headers will be used usermode.
__PRETTY_FUNCTION__ is defined in MingW but not in VS. Equal to __FUNCSIG__
DDK headers are not longer in a folder (as of Win8 WDK)
Windows SDK defines _snprintf, same as snprintf.
VS didn't like bool declarations to be inline to function return. Also, it's BOOL in Windows SDK.
Specific VS version.h should be used when using VS.

DOES COMPILE
2013-10-19 22:07:37 +01:00

1406 lines
40 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2011, 2012
Rocky Bernstein <rocky@gnu.org>
Copyright (C) 2001, 2003 Herbert Valerio Riedel <hvr@gnu.org>
Copyright (C) 2013 Natalia Portillo <claunia@claunia.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*! This code implements low-level access functions for the Nero native
CD-image format residing inside a disk file (*.nrg).
*/
#include "image.h"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <cdio/bytesex.h>
#include <cdio/ds.h>
#include <cdio/logging.h>
#include <cdio/util.h>
#if defined (_MSC_VER) || defined (_XBOX)
#include <cdio/msvc/version.h>
#else
#include <cdio/version.h>
#endif
#include "cdio_assert.h"
#include "_cdio_stdio.h"
#include "nrg.h"
#include "cdtext_private.h"
nero_id_t nero_id;
nero_dtype_t nero_dtype;
/* reader */
#define DEFAULT_CDIO_DEVICE "image.nrg"
/*
Link element of track structure as a linked list.
Possibly redundant with above track_info_t */
typedef struct {
uint32_t start_lsn;
uint32_t sec_count; /* Number of sectors in track. Does not
include pregap before next entry. */
uint64_t img_offset; /* Bytes offset from beginning of disk image file.*/
uint32_t blocksize; /* Number of bytes in a block */
} _mapping_t;
#define NEED_NERO_STRUCT
#include "image_common.h"
static bool parse_nrg (_img_private_t *env, const char *psz_cue_name,
const cdio_log_level_t log_level);
static lsn_t get_disc_last_lsn_nrg (void *p_user_data);
/* Updates internal track TOC, so we can later
simulate ioctl(CDROMREADTOCENTRY).
*/
static void
_register_mapping (_img_private_t *env, lsn_t start_lsn, uint32_t sec_count,
uint64_t img_offset, uint32_t blocksize,
track_format_t track_format, bool track_green)
{
const int track_num=env->gen.i_tracks;
track_info_t *this_track=&(env->tocent[env->gen.i_tracks]);
_mapping_t *_map = calloc(1, sizeof (_mapping_t));
_map->start_lsn = start_lsn;
_map->sec_count = sec_count;
_map->img_offset = img_offset;
_map->blocksize = blocksize;
if (!env->mapping) env->mapping = _cdio_list_new ();
_cdio_list_append (env->mapping, _map);
env->size = MAX (env->size, (start_lsn + sec_count));
/* Update *this_track and track_num. These structures are
in a sense redundant witht the obj->mapping list. Perhaps one
or the other can be eliminated.
*/
cdio_lba_to_msf (cdio_lsn_to_lba(start_lsn), &(this_track->start_msf));
this_track->start_lba = cdio_msf_to_lba(&this_track->start_msf);
this_track->track_num = track_num+1;
this_track->blocksize = blocksize;
if (env->is_cues)
this_track->datastart = img_offset;
else
this_track->datastart = 0;
if (track_green)
this_track->datastart += CDIO_CD_SUBHEADER_SIZE;
this_track->sec_count = sec_count;
this_track->track_format= track_format;
this_track->track_green = track_green;
switch (this_track->track_format) {
case TRACK_FORMAT_AUDIO:
this_track->blocksize = CDIO_CD_FRAMESIZE_RAW;
this_track->datasize = CDIO_CD_FRAMESIZE_RAW;
/*this_track->datastart = 0;*/
this_track->endsize = 0;
break;
case TRACK_FORMAT_CDI:
this_track->datasize=CDIO_CD_FRAMESIZE;
break;
case TRACK_FORMAT_XA:
if (track_green) {
this_track->blocksize = CDIO_CD_FRAMESIZE;
/*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE;*/
this_track->datasize = M2RAW_SECTOR_SIZE;
this_track->endsize = 0;
} else {
/*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE +
CDIO_CD_SUBHEADER_SIZE;*/
this_track->datasize = CDIO_CD_FRAMESIZE;
this_track->endsize = CDIO_CD_SYNC_SIZE + CDIO_CD_ECC_SIZE;
}
break;
case TRACK_FORMAT_DATA:
if (track_green) {
/*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE;*/
this_track->datasize = CDIO_CD_FRAMESIZE;
this_track->endsize = CDIO_CD_EDC_SIZE + CDIO_CD_M1F1_ZERO_SIZE
+ CDIO_CD_ECC_SIZE;
} else {
/* Is the below correct? */
/*this_track->datastart = 0;*/
this_track->datasize = CDIO_CD_FRAMESIZE;
this_track->endsize = 0;
}
break;
default:
/*this_track->datasize=CDIO_CD_FRAMESIZE_RAW;*/
cdio_warn ("track %d has unknown format %d",
env->gen.i_tracks, this_track->track_format);
}
env->gen.i_tracks++;
cdio_debug ("start lsn: %lu sector count: %0lu -> %8ld (%08lx)",
(long unsigned int) start_lsn,
(long unsigned int) sec_count,
(long unsigned int) img_offset,
(long unsigned int) img_offset);
}
/*
Disk and track information for a Nero file are located at the end
of the file. This routine extracts that information.
FIXME: right now psz_nrg_name is not used. It will be in the future.
*/
static bool
parse_nrg (_img_private_t *p_env, const char *psz_nrg_name,
const cdio_log_level_t log_level)
{
off_t footer_start;
off_t size;
char *footer_buf = NULL;
if (!p_env) return false;
size = cdio_stream_stat (p_env->gen.data_source);
if (-1 == size) return false;
{
_footer_t buf;
cdio_assert (sizeof (buf) == 12);
cdio_stream_seek (p_env->gen.data_source, size - sizeof (buf), SEEK_SET);
cdio_stream_read (p_env->gen.data_source, (void *) &buf, sizeof (buf), 1);
if (buf.v50.ID == UINT32_TO_BE (NERO_ID)) {
cdio_debug ("detected Nero version 5.0 (32-bit offsets) NRG magic");
footer_start = uint32_to_be (buf.v50.footer_ofs);
} else if (buf.v55.ID == UINT32_TO_BE (NER5_ID)) {
cdio_debug ("detected Nero version 5.5.x (64-bit offsets) NRG magic");
footer_start = uint64_from_be (buf.v55.footer_ofs);
} else {
cdio_log (log_level, "Image not recognized as either version 5.0 or "
"version 5.5.x-6.x type NRG");
return false;
}
cdio_debug (".NRG footer start = %ld, length = %ld",
(long) footer_start, (long) (size - footer_start));
cdio_assert ((size - footer_start) <= 4096);
footer_buf = calloc(1, (size_t)(size - footer_start));
cdio_stream_seek (p_env->gen.data_source, footer_start, SEEK_SET);
cdio_stream_read (p_env->gen.data_source, footer_buf,
(size_t)(size - footer_start), 1);
}
{
int pos = 0;
while (pos < size - footer_start) {
_chunk_t *chunk = (void *) (footer_buf + pos);
uint32_t opcode = UINT32_FROM_BE (chunk->id);
bool break_out = false;
switch (opcode) {
case CUES_ID: /* "CUES" Seems to have sector size 2336 and 150 sector
pregap seems to be included at beginning of image.
*/
case CUEX_ID: /* "CUEX" */
{
unsigned entries = UINT32_FROM_BE (chunk->len);
_cuex_array_t *_entries = (void *) chunk->data;
cdio_assert (p_env->mapping == NULL);
cdio_assert ( sizeof (_cuex_array_t) == 8 );
cdio_assert ( UINT32_FROM_BE (chunk->len) % sizeof(_cuex_array_t)
== 0 );
entries /= sizeof (_cuex_array_t);
if (CUES_ID == opcode) {
lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn);
unsigned int idx;
unsigned int i = 0;
cdio_debug ("CUES type image detected" );
/* CUES LSN has 150 pregap include at beginning? -/
cdio_assert (lsn == 0?);
*/
p_env->is_cues = true; /* HACK alert. */
p_env->gen.i_tracks = 0;
p_env->gen.i_first_track = 1;
for (idx = 1; idx < entries-1; idx += 2, i++) {
lsn_t sec_count;
int cdte_format = _entries[idx].addr_ctrl / 16;
int cdte_ctrl = _entries[idx].type >> 4;
if ( COPY_PERMITTED & cdte_ctrl ) {
if (p_env) p_env->tocent[i].flags |= COPY_PERMITTED;
} else {
if (p_env) p_env->tocent[i].flags &= ~COPY_PERMITTED;
}
if ( PRE_EMPHASIS & cdte_ctrl ) {
if (p_env) p_env->tocent[i].flags |= PRE_EMPHASIS;
} else {
if (p_env) p_env->tocent[i].flags &= ~PRE_EMPHASIS;
}
if ( FOUR_CHANNEL_AUDIO & cdte_ctrl ) {
if (p_env) p_env->tocent[i].flags |= FOUR_CHANNEL_AUDIO;
} else {
if (p_env) p_env->tocent[i].flags &= ~FOUR_CHANNEL_AUDIO;
}
cdio_assert (_entries[idx].track == _entries[idx + 1].track);
/* lsn and sec_count*2 aren't correct, but it comes closer on the
single example I have: svcdgs.nrg
We are picking up the wrong fields and/or not interpreting
them correctly.
*/
switch (cdte_format) {
case 0:
lsn = UINT32_FROM_BE (_entries[idx].lsn);
break;
case 1:
{
#if 0
msf_t msf = (msf_t) _entries[idx].lsn;
lsn = cdio_msf_to_lsn(&msf);
#else
lsn = CDIO_INVALID_LSN;
#endif
cdio_log (log_level,
"untested (i.e. probably wrong) CUE MSF code");
break;
}
default:
lsn = CDIO_INVALID_LSN;
cdio_log(log_level,
"unknown cdte_format %d", cdte_format);
}
sec_count = UINT32_FROM_BE (_entries[idx + 1].lsn);
_register_mapping (p_env, lsn, sec_count*2,
(lsn+CDIO_PREGAP_SECTORS) * M2RAW_SECTOR_SIZE,
M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true);
}
} else {
lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn);
unsigned int idx;
unsigned int i = 0;
cdio_debug ("CUEX type image detected");
/* LSN must start at -150 (LBA 0)? */
cdio_assert (lsn == -150);
for (idx = 2; idx < entries; idx += 2, i++) {
lsn_t sec_count;
int cdte_format = _entries[idx].addr_ctrl >> 4;
int cdte_ctrl = _entries[idx].type >> 4;
if ( COPY_PERMITTED & cdte_ctrl ) {
if (p_env) p_env->tocent[i].flags |= COPY_PERMITTED;
} else {
if (p_env) p_env->tocent[i].flags &= ~COPY_PERMITTED;
}
if ( PRE_EMPHASIS & cdte_ctrl ) {
if (p_env) p_env->tocent[i].flags |= PRE_EMPHASIS;
} else {
if (p_env) p_env->tocent[i].flags &= ~PRE_EMPHASIS;
}
if ( FOUR_CHANNEL_AUDIO & cdte_ctrl ) {
if (p_env) p_env->tocent[i].flags |= FOUR_CHANNEL_AUDIO;
} else {
if (p_env) p_env->tocent[i].flags &= ~FOUR_CHANNEL_AUDIO;
}
/* extractnrg.pl has cdte_format for LBA's 0, and
for MSF 1. ???
FIXME: Should decode as appropriate for cdte_format.
*/
cdio_assert ( cdte_format == 0 || cdte_format == 1 );
cdio_assert (_entries[idx].track != _entries[idx + 1].track);
lsn = UINT32_FROM_BE (_entries[idx].lsn);
sec_count = UINT32_FROM_BE (_entries[idx + 1].lsn);
_register_mapping (p_env, lsn, sec_count - lsn,
(lsn + CDIO_PREGAP_SECTORS)*M2RAW_SECTOR_SIZE,
M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true);
}
}
break;
}
case DAOX_ID: /* "DAOX" */
case DAOI_ID: /* "DAOI" */
{
_daox_t *_xentries = NULL;
_daoi_t *_ientries = NULL;
_dao_array_common_t *_dao_array_common = NULL;
_dao_common_t *_dao_common = (void *) chunk->data;
int disc_mode = _dao_common->unknown[1];
track_format_t track_format;
int i;
/* We include an extra 0 byte so these can be used as C strings.*/
p_env->psz_mcn = calloc(1, CDIO_MCN_SIZE+1);
memcpy(p_env->psz_mcn, &(_dao_common->psz_mcn), CDIO_MCN_SIZE);
p_env->psz_mcn[CDIO_MCN_SIZE] = '\0';
if (DAOX_ID == opcode) {
_xentries = (void *) chunk->data;
p_env->dtyp = _xentries->track_info[0].common.unknown[2];
} else {
_ientries = (void *) chunk->data;
p_env->dtyp = _ientries->track_info[0].common.unknown[2];
}
p_env->is_dao = true;
cdio_debug ("DAO%c tag detected, track format %d, mode %x\n",
opcode==DAOX_ID ? 'X': 'I', p_env->dtyp, disc_mode);
switch (p_env->dtyp) {
case 0:
/* Mode 1 */
track_format = TRACK_FORMAT_DATA;
p_env->disc_mode = CDIO_DISC_MODE_CD_DATA;
break;
case 2:
/* Mode 2 form 1 */
disc_mode = 0;
track_format = TRACK_FORMAT_XA;
p_env->disc_mode = CDIO_DISC_MODE_CD_XA;
break;
case 3:
/* Mode 2 */
track_format = TRACK_FORMAT_XA;
p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */
break;
case 0x6:
/* Mode2 form mix */
track_format = TRACK_FORMAT_XA;
p_env->disc_mode = CDIO_DISC_MODE_CD_MIXED;
break;
case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */
track_format = TRACK_FORMAT_XA;
p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */
break;
case 0x7:
track_format = TRACK_FORMAT_AUDIO;
p_env->disc_mode = CDIO_DISC_MODE_CD_DA;
break;
default:
cdio_log (log_level, "Unknown track format %x\n",
p_env->dtyp);
track_format = TRACK_FORMAT_AUDIO;
}
if (0 == disc_mode) {
for (i=0; i<p_env->gen.i_tracks; i++) {
p_env->tocent[i].track_format= track_format;
p_env->tocent[i].datastart = 0;
p_env->tocent[i].track_green = false;
if (TRACK_FORMAT_AUDIO == track_format) {
p_env->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW;
p_env->tocent[i].datasize = CDIO_CD_FRAMESIZE_RAW;
p_env->tocent[i].endsize = 0;
} else {
p_env->tocent[i].datasize = CDIO_CD_FRAMESIZE;
p_env->tocent[i].datastart = 0;
}
}
} else if (2 == disc_mode) {
for (i=0; i<p_env->gen.i_tracks; i++) {
p_env->tocent[i].track_green = true;
p_env->tocent[i].track_format= track_format;
p_env->tocent[i].datasize = CDIO_CD_FRAMESIZE;
if (TRACK_FORMAT_XA == track_format) {
p_env->tocent[i].datastart = CDIO_CD_SYNC_SIZE
+ CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE;
p_env->tocent[i].endsize = CDIO_CD_SYNC_SIZE
+ CDIO_CD_ECC_SIZE;
} else {
p_env->tocent[i].datastart = CDIO_CD_SYNC_SIZE
+ CDIO_CD_HEADER_SIZE;
p_env->tocent[i].endsize = CDIO_CD_EDC_SIZE
+ CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE;
}
}
} else if (0x20 == disc_mode) {
cdio_debug ("Mixed mode CD?\n");
} else {
/* Mixed mode CD */
cdio_log (log_level,
"Don't know if mode 1, mode 2 or mixed: %x\n",
disc_mode);
}
for (i=0; i<p_env->gen.i_tracks; i++) {
if (DAOX_ID == opcode) {
_dao_array_common = &_xentries->track_info[i].common;
} else {
_dao_array_common = &_ientries->track_info[i].common;
}
p_env->tocent[i].isrc = calloc(1, CDIO_ISRC_SIZE+1);
memcpy(p_env->tocent[i].isrc, _dao_array_common->psz_isrc, CDIO_ISRC_SIZE);
p_env->tocent[i].isrc[CDIO_ISRC_SIZE] = '\0';
if (p_env->tocent[i].isrc[0]) {
cdio_info("nrg isrc has value \"%s\"", p_env->tocent[i].isrc);
}
if (!p_env->tocent[i].datasize) {
continue;
}
if (DAOX_ID == opcode) {
p_env->tocent[i].pregap = (uint64_from_be
(_xentries->track_info[i].index0)) / (p_env->tocent[i].datasize);
} else {
p_env->tocent[i].pregap = (uint32_from_be
(_ientries->track_info[i].index0)) / (p_env->tocent[i].datasize);
}
}
break;
}
case NERO_ID:
case NER5_ID:
cdio_error ("unexpected nrg magic ID NER%c detected",
opcode==NERO_ID ? 'O': '5');
free(footer_buf);
return false;
case END1_ID: /* "END!" */
cdio_debug ("nrg end tag detected");
break_out = true;
break;
case ETNF_ID: /* "ETNF" */ {
unsigned entries = UINT32_FROM_BE (chunk->len);
_etnf_array_t *_entries = (void *) chunk->data;
cdio_assert (p_env->mapping == NULL);
cdio_assert ( sizeof (_etnf_array_t) == 20 );
cdio_assert ( UINT32_FROM_BE(chunk->len) % sizeof(_etnf_array_t)
== 0 );
entries /= sizeof (_etnf_array_t);
cdio_debug ("SAO type image (ETNF) detected");
{
int idx;
for (idx = 0; idx < entries; idx++) {
uint32_t _len = UINT32_FROM_BE (_entries[idx].length);
uint32_t _start = UINT32_FROM_BE (_entries[idx].start_lsn);
uint32_t _start2 = UINT32_FROM_BE (_entries[idx].start);
uint32_t track_mode= uint32_from_be (_entries[idx].type);
bool track_green = true;
track_format_t track_format = TRACK_FORMAT_XA;
uint16_t blocksize;
switch (track_mode) {
case 0:
/* Mode 1 */
track_format = TRACK_FORMAT_DATA;
track_green = false; /* ?? */
blocksize = CDIO_CD_FRAMESIZE;
p_env->disc_mode = CDIO_DISC_MODE_CD_DATA;
cdio_debug ("Format DATA, blocksize %u", CDIO_CD_FRAMESIZE);
break;
case 2:
/* Mode 2 form 1 */
track_format = TRACK_FORMAT_XA;
track_green = false; /* ?? */
blocksize = CDIO_CD_FRAMESIZE;
p_env->disc_mode = CDIO_DISC_MODE_CD_XA;
cdio_debug ("Format XA, blocksize %u", CDIO_CD_FRAMESIZE);
break;
case 3:
/* Mode 2 */
track_format = TRACK_FORMAT_XA;
track_green = true;
blocksize = M2RAW_SECTOR_SIZE;
p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */
cdio_debug ("Format XA, blocksize %u", M2RAW_SECTOR_SIZE);
break;
case 06:
/* Mode2 form mix */
track_format = TRACK_FORMAT_XA;
track_green = true;
blocksize = M2RAW_SECTOR_SIZE;
p_env->disc_mode = CDIO_DISC_MODE_CD_MIXED;
cdio_debug ("Format MIXED CD, blocksize %u", M2RAW_SECTOR_SIZE);
break;
case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */
track_format = TRACK_FORMAT_XA;
track_green = true;
blocksize = M2RAW_SECTOR_SIZE;
p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */
cdio_debug ("Format MIXED CD, blocksize %u", M2RAW_SECTOR_SIZE);
break;
case 7:
track_format = TRACK_FORMAT_AUDIO;
track_green = false;
blocksize = CDIO_CD_FRAMESIZE_RAW;
p_env->disc_mode = CDIO_DISC_MODE_CD_DA;
cdio_debug ("Format CD_DA, blocksize %u", CDIO_CD_FRAMESIZE_RAW);
break;
default:
cdio_log (log_level,
"Don't know how to handle track mode (%lu)?",
(long unsigned int) track_mode);
free(footer_buf);
return false;
}
cdio_assert (_len % blocksize == 0);
_len /= blocksize;
cdio_assert (_start * blocksize == _start2);
_start += idx * CDIO_PREGAP_SECTORS;
_register_mapping (p_env, _start, _len, _start2, blocksize,
track_format, track_green);
}
}
break;
}
case ETN2_ID: { /* "ETN2", same as above, but with 64bit stuff instead */
unsigned entries = uint32_from_be (chunk->len);
_etn2_array_t *_entries = (void *) chunk->data;
cdio_assert (p_env->mapping == NULL);
cdio_assert (sizeof (_etn2_array_t) == 32);
cdio_assert (uint32_from_be (chunk->len) % sizeof (_etn2_array_t) == 0);
entries /= sizeof (_etn2_array_t);
cdio_debug ("SAO type image (ETN2) detected");
{
int idx;
for (idx = 0; idx < entries; idx++) {
uint32_t _len = uint64_from_be (_entries[idx].length);
uint32_t _start = uint32_from_be (_entries[idx].start_lsn);
uint32_t _start2 = uint64_from_be (_entries[idx].start);
uint32_t track_mode= uint32_from_be (_entries[idx].type);
bool track_green = true;
track_format_t track_format = TRACK_FORMAT_XA;
uint16_t blocksize;
switch (track_mode) {
case 0:
track_format = TRACK_FORMAT_DATA;
track_green = false; /* ?? */
blocksize = CDIO_CD_FRAMESIZE;
break;
case 2:
track_format = TRACK_FORMAT_XA;
track_green = false; /* ?? */
blocksize = CDIO_CD_FRAMESIZE;
break;
case 3:
track_format = TRACK_FORMAT_XA;
track_green = true;
blocksize = M2RAW_SECTOR_SIZE;
break;
case 7:
track_format = TRACK_FORMAT_AUDIO;
track_green = false;
blocksize = CDIO_CD_FRAMESIZE_RAW;
break;
default:
cdio_log (log_level,
"Don't know how to handle track mode (%lu)?",
(long unsigned int) track_mode);
free(footer_buf);
return false;
}
if (_len % blocksize != 0) {
cdio_log (log_level,
"length is not a multiple of blocksize "
"len %lu, size %d, rem %lu",
(long unsigned int) _len, blocksize,
(long unsigned int) _len % blocksize);
if (0 == _len % CDIO_CD_FRAMESIZE) {
cdio_log(log_level, "Adjusting blocksize to %d",
CDIO_CD_FRAMESIZE);
blocksize = CDIO_CD_FRAMESIZE;
} else if (0 == _len % M2RAW_SECTOR_SIZE) {
cdio_log(log_level,
"Adjusting blocksize to %d", M2RAW_SECTOR_SIZE);
blocksize = M2RAW_SECTOR_SIZE;
} else if (0 == _len % CDIO_CD_FRAMESIZE_RAW) {
cdio_log(log_level,
"Adjusting blocksize to %d", CDIO_CD_FRAMESIZE_RAW);
blocksize = CDIO_CD_FRAMESIZE_RAW;
}
}
_len /= blocksize;
if (_start * blocksize != _start2) {
cdio_log (log_level,
"%lu * %d != %lu",
(long unsigned int) _start, blocksize,
(long unsigned int) _start2);
if (_start * CDIO_CD_FRAMESIZE == _start2) {
cdio_log(log_level,
"Adjusting blocksize to %d", CDIO_CD_FRAMESIZE);
blocksize = CDIO_CD_FRAMESIZE;
} else if (_start * M2RAW_SECTOR_SIZE == _start2) {
cdio_log(log_level,
"Adjusting blocksize to %d", M2RAW_SECTOR_SIZE);
blocksize = M2RAW_SECTOR_SIZE;
} else if (_start * CDIO_CD_FRAMESIZE_RAW == _start2) {
cdio_log(log_level,
"Adjusting blocksize to %d", CDIO_CD_FRAMESIZE_RAW);
blocksize = CDIO_CD_FRAMESIZE_RAW;
}
}
_start += idx * CDIO_PREGAP_SECTORS;
_register_mapping (p_env, _start, _len, _start2, blocksize,
track_format, track_green);
}
}
break;
}
case SINF_ID: { /* "SINF" */
uint32_t _sessions;
cdio_assert (UINT32_FROM_BE (chunk->len) == 4);
memcpy(&_sessions, chunk->data, 4);
cdio_debug ("SINF: %lu sessions",
(long unsigned int) UINT32_FROM_BE (_sessions));
}
break;
case MTYP_ID: { /* "MTYP" */
uint32_t mtyp_be;
uint32_t mtyp;
cdio_assert (UINT32_FROM_BE (chunk->len) == 4);
memcpy(&mtyp_be, chunk->data, 4);
mtyp = UINT32_FROM_BE (mtyp_be);
cdio_debug ("MTYP: %lu",
(long unsigned int) mtyp);
if (mtyp != MTYP_AUDIO_CD) {
cdio_log (log_level,
"Unknown MTYP value: %u", (unsigned int) mtyp);
}
p_env->mtyp = mtyp;
}
break;
case CDTX_ID: { /* "CD TEXT" */
uint8_t *wdata = (uint8_t *) chunk->data;
int len = UINT32_FROM_BE (chunk->len);
cdio_assert (len % CDTEXT_LEN_PACK == 0);
p_env->gen.cdtext = cdtext_init ();
if(0 !=cdtext_data_init (p_env->gen.cdtext, wdata, len))
{
cdtext_destroy(p_env->gen.cdtext);
free(p_env->gen.cdtext);
p_env->gen.cdtext = NULL;
}
break;
}
default:
cdio_log (log_level,
"unknown tag %8.8x seen",
(unsigned int) UINT32_FROM_BE (chunk->id));
break;
}
if (break_out)
break;
pos += 8;
pos += UINT32_FROM_BE (chunk->len);
}
}
/* Fake out leadout track. */
/* Don't use get_disc_last_lsn_nrg since that will lead to recursion since
we haven't fully initialized things yet.
*/
cdio_lsn_to_msf (p_env->size, &p_env->tocent[p_env->gen.i_tracks].start_msf);
p_env->tocent[p_env->gen.i_tracks].start_lba = cdio_lsn_to_lba(p_env->size);
p_env->tocent[p_env->gen.i_tracks-1].sec_count =
cdio_lsn_to_lba(p_env->size - p_env->tocent[p_env->gen.i_tracks-1].start_lba);
p_env->gen.b_cdtext_error = false;
p_env->gen.toc_init = true;
free(footer_buf);
return true;
}
/*!
Initialize image structures.
*/
static bool
_init_nrg (_img_private_t *p_env)
{
if (p_env->gen.init) {
cdio_error ("init called more than once");
return false;
}
if (!(p_env->gen.data_source = cdio_stdio_new (p_env->gen.source_name))) {
cdio_warn ("can't open nrg image file %s for reading",
p_env->gen.source_name);
return false;
}
p_env->psz_mcn = NULL;
p_env->disc_mode = CDIO_DISC_MODE_NO_INFO;
if ( !parse_nrg (p_env, p_env->gen.source_name, CDIO_LOG_WARN) ) {
cdio_warn ("image file %s is not a Nero image",
p_env->gen.source_name);
return false;
}
p_env->gen.init = true;
return true;
}
/*!
Reads into buf the next size bytes.
Returns -1 on error.
Would be libc's seek() but we have to adjust for the extra track header
information in each sector.
*/
static off_t
_lseek_nrg (void *p_user_data, off_t offset, int whence)
{
_img_private_t *p_env = p_user_data;
/* real_offset is the real byte offset inside the disk image
The number below was determined empirically.
*/
off_t real_offset= p_env->is_dao ? 0x4b000 : 0;
unsigned int i;
p_env->pos.lba = 0;
for (i=0; i<p_env->gen.i_tracks; i++) {
track_info_t *this_track=&(p_env->tocent[i]);
p_env->pos.index = i;
if ( (this_track->sec_count*this_track->datasize) >= offset) {
int blocks = (int) (offset / this_track->datasize);
int rem = (int) (offset % this_track->datasize);
off_t block_offset = blocks * this_track->blocksize;
real_offset += block_offset + rem;
p_env->pos.buff_offset = rem;
p_env->pos.lba += blocks;
break;
}
real_offset += this_track->sec_count*this_track->blocksize;
offset -= this_track->sec_count*this_track->datasize;
p_env->pos.lba += this_track->sec_count;
}
if (i==p_env->gen.i_tracks) {
cdio_warn ("seeking outside range of disk image");
return -1;
} else
real_offset += p_env->tocent[i].datastart;
return cdio_stream_seek(p_env->gen.data_source, real_offset, whence);
}
/*!
Reads into buf the next size bytes.
Returns -1 on error.
FIXME:
At present we assume a read doesn't cross sector or track
boundaries.
*/
static ssize_t
_read_nrg (void *p_user_data, void *buf, size_t size)
{
_img_private_t *p_env = p_user_data;
return cdio_stream_read(p_env->gen.data_source, buf, size, 1);
}
/*!
Get the size of the CD in logical block address (LBA) units.
@param p_cdio the CD object queried
@return the lsn. On error 0 or CDIO_INVALD_LSN.
*/
static lsn_t
get_disc_last_lsn_nrg (void *p_user_data)
{
_img_private_t *p_env = p_user_data;
return p_env->size;
}
/*!
Reads a single audio sector from CD device into data starting
from LSN.
*/
static driver_return_code_t
_read_audio_sectors_nrg (void *p_user_data, void *data, lsn_t lsn,
unsigned int nblocks)
{
_img_private_t *p_env = p_user_data;
CdioListNode_t *node;
if (lsn >= p_env->size)
{
cdio_warn ("trying to read beyond image size (%lu >= %lu)",
(long unsigned int) lsn, (long unsigned int) p_env->size);
return -1;
}
if (p_env->is_dao) {
int ret;
ret = cdio_stream_seek (p_env->gen.data_source,
(lsn + CDIO_PREGAP_SECTORS) * CDIO_CD_FRAMESIZE_RAW, SEEK_SET);
if (ret!=0) return ret;
ret = cdio_stream_read (p_env->gen.data_source, data,
CDIO_CD_FRAMESIZE_RAW, nblocks);
/* ret is number of bytes if okay, but we need to return 0 okay. */
return ret == 0;
}
_CDIO_LIST_FOREACH (node, p_env->mapping) {
_mapping_t *_map = _cdio_list_node_data (node);
if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) {
int ret;
long int img_offset = _map->img_offset;
img_offset += (lsn - _map->start_lsn) * CDIO_CD_FRAMESIZE_RAW;
ret = cdio_stream_seek (p_env->gen.data_source, img_offset,
SEEK_SET);
if (ret!=0) return ret;
ret = cdio_stream_read (p_env->gen.data_source, data,
CDIO_CD_FRAMESIZE_RAW, nblocks);
if (ret==0) return ret;
break;
}
}
if (!node) cdio_warn ("reading into pre gap (lsn %lu)",
(long unsigned int) lsn);
return 0;
}
static driver_return_code_t
_read_mode1_sector_nrg (void *p_user_data, void *data, lsn_t lsn,
bool b_form2)
{
_img_private_t *p_env = p_user_data;
char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
CdioListNode_t *node;
if (lsn >= p_env->size)
{
cdio_warn ("trying to read beyond image size (%lu >= %lu)",
(long unsigned int) lsn, (long unsigned int) p_env->size);
return -1;
}
_CDIO_LIST_FOREACH (node, p_env->mapping) {
_mapping_t *_map = _cdio_list_node_data (node);
if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) {
int ret;
long int img_offset = _map->img_offset;
img_offset += (lsn - _map->start_lsn) * _map->blocksize;
ret = cdio_stream_seek (p_env->gen.data_source, img_offset,
SEEK_SET);
if (ret!=0) return ret;
/* FIXME: Not completely sure the below is correct. */
ret = cdio_stream_read (p_env->gen.data_source,
(M2RAW_SECTOR_SIZE == _map->blocksize)
? (buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE)
: buf,
_map->blocksize, 1);
if (ret==0) return ret;
break;
}
}
if (!node)
cdio_warn ("reading into pre gap (lsn %lu)", (long unsigned int) lsn);
memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE,
b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
return 0;
}
/*!
Reads nblocks of mode2 sectors from cd device into data starting
from lsn.
*/
static driver_return_code_t
_read_mode1_sectors_nrg (void *p_user_data, void *data, lsn_t lsn,
bool b_form2, unsigned nblocks)
{
_img_private_t *p_env = p_user_data;
int i;
int retval;
unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
for (i = 0; i < nblocks; i++) {
if ( (retval = _read_mode1_sector_nrg (p_env,
((char *)data) + (blocksize * i),
lsn + i, b_form2)) )
return retval;
}
return 0;
}
static driver_return_code_t
_read_mode2_sector_nrg (void *p_user_data, void *data, lsn_t lsn,
bool b_form2)
{
_img_private_t *p_env = p_user_data;
char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
CdioListNode_t *node;
if (lsn >= p_env->size)
{
cdio_warn ("trying to read beyond image size (%lu >= %lu)",
(long unsigned int) lsn, (long unsigned int) p_env->size);
return -1;
}
_CDIO_LIST_FOREACH (node, p_env->mapping) {
_mapping_t *_map = _cdio_list_node_data (node);
if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) {
int ret;
long int img_offset = _map->img_offset;
img_offset += (lsn - _map->start_lsn) * _map->blocksize;
ret = cdio_stream_seek (p_env->gen.data_source, img_offset,
SEEK_SET);
if (ret!=0) return ret;
ret = cdio_stream_read (p_env->gen.data_source,
(M2RAW_SECTOR_SIZE == _map->blocksize)
? (buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE)
: buf,
_map->blocksize, 1);
if (ret==0) return ret;
break;
}
}
if (!node)
cdio_warn ("reading into pre gap (lsn %lu)", (long unsigned int) lsn);
if (b_form2)
memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE,
M2RAW_SECTOR_SIZE);
else
memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE);
return 0;
}
/*!
Reads nblocks of mode2 sectors from cd device into data starting
from lsn.
Returns 0 if no error.
*/
static driver_return_code_t
_read_mode2_sectors_nrg (void *p_user_data, void *data, lsn_t lsn,
bool b_form2, unsigned nblocks)
{
_img_private_t *p_env = p_user_data;
int i;
int retval;
unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
for (i = 0; i < nblocks; i++) {
if ( (retval = _read_mode2_sector_nrg (p_env,
((char *)data) + (blocksize * i),
lsn + i, b_form2)) )
return retval;
}
return 0;
}
/*
Free memory resources associated with NRG object.
*/
static void
_free_nrg (void *p_user_data)
{
_img_private_t *p_env = p_user_data;
if (NULL == p_env) return;
if (NULL != p_env->mapping)
_cdio_list_free (p_env->mapping, true);
/* The remaining part of the image is like the other image drivers,
so free that in the same way. */
_free_image(p_user_data);
}
/*!
Eject media -- there's nothing to do here except free resources.
We always return 2.
*/
static driver_return_code_t
_eject_media_nrg(void *obj)
{
_free_nrg (obj);
return DRIVER_OP_UNSUPPORTED;
}
#if !defined(HAVE_GLOB_H) && defined(_WIN32)
static inline void Win32Glob(const char* pattern, const char* szCurPath, char ***drives, unsigned int *num_files)
{
char szPath[MAX_PATH];
WIN32_FIND_DATAA ffd;
HANDLE hFind;
BOOL bFound;
SetCurrentDirectoryA(szCurPath);
hFind = FindFirstFileA(pattern, &ffd);
bFound = (hFind != INVALID_HANDLE_VALUE);
while (bFound) {
cdio_add_device_list(drives, ffd.cFileName, num_files);
bFound = FindNextFileA(hFind, &ffd);
}
if (hFind != INVALID_HANDLE_VALUE)
FindClose(hFind);
hFind = FindFirstFileA("*", &ffd);
bFound = (hFind != INVALID_HANDLE_VALUE);
while (bFound) {
if ( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(strcmp(ffd.cFileName, ".") != 0) && (strcmp(ffd.cFileName, "..") != 0) ) {
GetFullPathNameA(ffd.cFileName, sizeof(szPath), szPath, NULL);
Win32Glob(pattern, szPath, drives, num_files);
SetCurrentDirectoryA(szCurPath);
}
bFound = FindNextFileA(hFind, &ffd);
}
if (hFind != INVALID_HANDLE_VALUE)
FindClose(hFind);
}
#endif
/*!
Return an array of strings giving possible NRG disk images.
*/
char **
cdio_get_devices_nrg (void)
{
char **drives = NULL;
unsigned int num_files=0;
#ifdef HAVE_GLOB_H
unsigned int i;
glob_t globbuf;
globbuf.gl_offs = 0;
glob("*.nrg", GLOB_DOOFFS, NULL, &globbuf);
for (i=0; i<globbuf.gl_pathc; i++) {
cdio_add_device_list(&drives, globbuf.gl_pathv[i], &num_files);
}
globfree(&globbuf);
#elif defined(_WIN32)
char szStartDir[MAX_PATH];
GetCurrentDirectoryA(sizeof(szStartDir), szStartDir);
Win32Glob("*.nrg", szStartDir, &drives, &num_files);
#else
cdio_add_device_list(&drives, DEFAULT_CDIO_DEVICE, &num_files);
#endif /*HAVE_GLOB_H*/
cdio_add_device_list(&drives, NULL, &num_files);
return drives;
}
/*!
Return a string containing the default CD device.
*/
char *
cdio_get_default_device_nrg(void)
{
char **drives = cdio_get_devices_nrg();
char *drive = (drives[0] == NULL) ? NULL : strdup(drives[0]);
cdio_free_device_list(drives);
return drive;
}
static bool
get_hwinfo_nrg ( const CdIo *p_cdio, /*out*/ cdio_hwinfo_t *hw_info)
{
strncpy(hw_info->psz_vendor, "libcdio",
sizeof(hw_info->psz_vendor)-1);
hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0';
strncpy(hw_info->psz_model, "Nero",
sizeof(hw_info->psz_model)-1);
hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0';
strncpy(hw_info->psz_revision, CDIO_VERSION,
sizeof(hw_info->psz_revision)-1);
hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0';
return true;
}
/*!
Return the number of tracks in the current medium.
CDIO_INVALID_TRACK is returned on error.
*/
static track_format_t
get_track_format_nrg(void *p_user_data, track_t track_num)
{
_img_private_t *p_env = p_user_data;
if (track_num > p_env->gen.i_tracks || track_num == 0)
return TRACK_FORMAT_ERROR;
if ( p_env->dtyp != DTYP_INVALID) {
switch (p_env->dtyp) {
case DTYP_MODE2_XA:
return TRACK_FORMAT_XA;
case DTYP_MODE1:
return TRACK_FORMAT_DATA;
default: ;
}
}
/*if ( MTYP_AUDIO_CD == p_env->mtyp) return TRACK_FORMAT_AUDIO; */
return p_env->tocent[track_num-1].track_format;
}
/*!
Return true if we have XA data (green, mode2 form1) or
XA data (green, mode2 form2). That is track begins:
sync - header - subheader
12 4 - 8
FIXME: there's gotta be a better design for this and get_track_format?
*/
static bool
_get_track_green_nrg(void *p_user_data, track_t track_num)
{
_img_private_t *p_env = p_user_data;
if (track_num > p_env->gen.i_tracks || track_num == 0)
return false;
if ( MTYP_AUDIO_CD == p_env->mtyp) return false;
return p_env->tocent[track_num-1].track_green;
}
/*!
Check that a NRG file is valid.
*/
bool
cdio_is_nrg(const char *psz_nrg)
{
_img_private_t env;
bool is_nrg = false;
if (psz_nrg == NULL) return false;
memset(&env, 0, sizeof(env));
if (!(env.gen.data_source = cdio_stdio_new (psz_nrg))) {
cdio_warn ("can't open nrg image file %s for reading", psz_nrg);
return false;
}
if (parse_nrg(&env, psz_nrg, CDIO_LOG_INFO)) {
is_nrg = true;
#ifdef ALSO_TEST_NAME
size_t psz_len;
psz_len = strlen(psz_nrg);
/* At least 4 characters needed for .nrg extension */
if ( psz_len < 4 ) return false;
is_nrg = strncasecmp( psz_nrg+(psz_len-3), "nrg", 3 ) == 0;
#endif
}
cdio_stdio_destroy(env.gen.data_source);
return is_nrg;
}
/*!
Initialization routine. This is the only thing that doesn't
get called via a function pointer. In fact *we* are the
ones to set that up.
*/
CdIo *
cdio_open_am_nrg (const char *psz_source_name, const char *psz_access_mode)
{
if (psz_access_mode != NULL && strcmp(psz_access_mode, "image"))
cdio_warn ("there is only one access mode for nrg. Arg %s ignored",
psz_access_mode);
return cdio_open_nrg(psz_source_name);
}
CdIo *
cdio_open_nrg (const char *psz_source)
{
CdIo *ret;
_img_private_t *_data;
cdio_funcs_t _funcs;
memset( &_funcs, 0, sizeof(_funcs) );
_funcs.eject_media = _eject_media_nrg;
_funcs.free = _free_nrg;
_funcs.get_arg = _get_arg_image;
_funcs.get_cdtext = _get_cdtext_image;
_funcs.get_cdtext_raw = NULL;
_funcs.get_devices = cdio_get_devices_nrg;
_funcs.get_default_device = cdio_get_default_device_nrg;
_funcs.get_disc_last_lsn = get_disc_last_lsn_nrg;
_funcs.get_discmode = _get_discmode_image;
_funcs.get_drive_cap = _get_drive_cap_image;
_funcs.get_first_track_num = _get_first_track_num_image;
_funcs.get_hwinfo = get_hwinfo_nrg;
_funcs.get_media_changed = get_media_changed_image;
_funcs.get_mcn = _get_mcn_image;
_funcs.get_num_tracks = _get_num_tracks_image;
_funcs.get_track_channels = get_track_channels_generic;
_funcs.get_track_copy_permit = get_track_copy_permit_image;
_funcs.get_track_format = get_track_format_nrg;
_funcs.get_track_green = _get_track_green_nrg;
_funcs.get_track_lba = NULL; /* Will use generic routine via msf */
_funcs.get_track_msf = _get_track_msf_image;
_funcs.get_track_preemphasis = get_track_preemphasis_generic;
_funcs.get_track_pregap_lba = get_track_pregap_lba_image;
_funcs.get_track_isrc = get_track_isrc_image;
_funcs.lseek = _lseek_nrg;
_funcs.read = _read_nrg;
_funcs.read_audio_sectors = _read_audio_sectors_nrg;
_funcs.read_data_sectors = read_data_sectors_image;
_funcs.read_mode1_sector = _read_mode1_sector_nrg;
_funcs.read_mode1_sectors = _read_mode1_sectors_nrg;
_funcs.read_mode2_sector = _read_mode2_sector_nrg;
_funcs.read_mode2_sectors = _read_mode2_sectors_nrg;
_funcs.run_mmc_cmd = NULL;
_funcs.set_arg = _set_arg_image;
_data = calloc(1, sizeof (_img_private_t));
_data->gen.init = false;
_data->gen.i_tracks = 0;
_data->mtyp = 0;
_data->dtyp = DTYP_INVALID;
_data->gen.i_first_track= 1;
_data->is_dao = false;
_data->is_cues = false; /* FIXME: remove is_cues. */
ret = cdio_new ((void *)_data, &_funcs);
if (ret == NULL) {
free(_data);
return NULL;
}
ret->driver_id = DRIVER_NRG;
_set_arg_image(_data, "source", (NULL == psz_source)
? DEFAULT_CDIO_DEVICE: psz_source);
_set_arg_image (_data, "access-mode", "Nero");
_data->psz_cue_name = strdup(_get_arg_image(_data, "source"));
if (!cdio_is_nrg(_data->psz_cue_name)) {
cdio_debug ("source name %s is not recognized as a NRG image",
_data->psz_cue_name);
_free_nrg(_data);
free(ret);
return NULL;
}
if (_init_nrg(_data))
return ret;
else {
_free_nrg(_data);
free(ret);
return NULL;
}
}
bool
cdio_have_nrg (void)
{
return true;
}