Search
lxdream.org :: lxdream/src/drivers/cdrom/cd_nrg.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/cdrom/cd_nrg.c
changeset 1178:e55ec927d55d
prev1109:700c5ab26a63
next1298:d0eb2307b847
author nkeynes
date Sat Jan 26 14:00:48 2013 +1000 (11 years ago)
permissions -rw-r--r--
last change Change glib includes to #include <glib.h> rather than the individual
headers, as recent glib versions are breaking on this
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * Nero (NRG) CD file format. File information stolen shamelessly from
     5  * libcdio.
     6  *
     7  * Copyright (c) 2005 Nathan Keynes.
     8  *
     9  * This program is free software; you can redistribute it and/or modify
    10  * it under the terms of the GNU General Public License as published by
    11  * the Free Software Foundation; either version 2 of the License, or
    12  * (at your option) any later version.
    13  *
    14  * This program is distributed in the hope that it will be useful,
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17  * GNU General Public License for more details.
    18  */
    20 #include <assert.h>
    21 #include <stdio.h>
    22 #include <errno.h>
    23 #include "drivers/cdrom/cdimpl.h"
    24 #include "dream.h"
    26 static gboolean nrg_image_is_valid( FILE *f );
    27 static gboolean nrg_image_read_toc( cdrom_disc_t disc, ERROR *err );
    29 struct cdrom_disc_factory nrg_disc_factory = { "Nero", "nrg",
    30         nrg_image_is_valid, NULL, nrg_image_read_toc };
    32 #define NERO_V55_ID  0x4e455235 
    33 #define NERO_V50_ID  0x4e45524f 
    35 /* Courtesy of libcdio */
    36 /* 5.0 or earlier */
    37 #define NERO_ID  0x4e45524f  /* Nero pre 5.5.x */
    38 #define CUES_ID  0x43554553  /* Nero pre version 5.5.x-6.x */
    39 #define DAOI_ID  0x44414f49
    40 #define ETNF_ID  0x45544e46
    41 #define SINF_ID  0x53494e46  /* Session information */
    42 #define END_ID  0x454e4421
    43 /* 5.5+ only */
    44 #define NER5_ID  0x4e455235  /* Nero version 5.5.x */
    45 #define CDTX_ID  0x43445458  /* CD TEXT */
    46 #define CUEX_ID  0x43554558  /* Nero version 5.5.x-6.x */
    47 #define DAOX_ID  0x44414f58  /* Nero version 5.5.x-6.x */
    48 #define ETN2_ID  0x45544e32
    49 #define MTYP_ID  0x4d545950  /* Disc Media type? */
    52 union nrg_footer {
    53     struct nrg_footer_v50 {
    54         uint32_t dummy;
    55         uint32_t id;
    56         uint32_t offset;
    57     } v50;
    58     struct nrg_footer_v55 {
    59         uint32_t id;
    60         uint64_t offset;
    61     } __attribute__((packed)) v55;
    62 };
    64 struct nrg_chunk {
    65     uint32_t id;
    66     uint32_t length;
    67 };
    69 struct nrg_etnf {
    70     uint32_t offset;
    71     uint32_t length;
    72     uint32_t mode;
    73     uint32_t lba;
    74     uint32_t padding;
    75 };
    77 struct nrg_etn2 {
    78     uint64_t offset;
    79     uint64_t length;
    80     uint32_t mode;
    81     uint32_t lba;
    82     uint64_t padding;
    83 };
    85 struct nrg_cues {
    86     uint8_t type;
    87     uint8_t track;
    88     uint8_t control;
    89     uint8_t pad;
    90     uint32_t addr;
    91 };
    93 struct nrg_daoi {
    94     uint32_t length;
    95     char mcn[14];
    96     uint8_t disc_mode;
    97     uint8_t unknown[2]; /* always 01 01? */
    98     uint8_t track_count;
    99     struct nrg_daoi_track {
   100         char unknown[10];
   101         uint32_t sector_size __attribute__((packed)); /* Always 0? */
   102         uint8_t mode;
   103         uint8_t unknown2[3]; /* Always 00 00 01? */
   104         uint32_t pregap __attribute__((packed));
   105         uint32_t offset __attribute__((packed));
   106         uint32_t end __attribute__((packed));
   107     } track[0];
   108 } __attribute__((packed));
   110 struct nrg_daox {
   111     uint32_t length;
   112     char mcn[14];
   113     uint8_t disc_mode;
   114     uint8_t unknown[2]; /* always 01 01? */
   115     uint8_t track_count;
   116     struct nrg_daox_track {
   117         char unknown[10];
   118         uint32_t sector_size __attribute__((packed)); /* Always 0? */
   119         uint8_t mode;
   120         uint8_t unknown2[3]; /* Always 00 00 01? */
   121         uint64_t pregap __attribute__((packed));
   122         uint64_t offset __attribute__((packed));
   123         uint64_t end __attribute__((packed));
   124     } track[0];
   125 } __attribute__((packed));
   128 sector_mode_t static nrg_track_mode( uint8_t mode )
   129 {
   130     switch( mode ) {
   131     case 0: return SECTOR_MODE1;
   132     case 2: return SECTOR_MODE2_FORM1;
   133     case 3: return SECTOR_SEMIRAW_MODE2;
   134     case 7: return SECTOR_CDDA;
   135     case 16: return SECTOR_CDDA_SUBCHANNEL;
   136     default: return -1;
   137     }
   138 }
   140 static gboolean nrg_image_is_valid( FILE *f )
   141 {
   142     union nrg_footer footer;
   144     fseek( f, -12, SEEK_END );
   145     fread( &footer, sizeof(footer), 1, f );
   146     if( GUINT32_FROM_BE(footer.v50.id) == NERO_V50_ID ||
   147             GUINT32_FROM_BE(footer.v55.id) == NERO_V55_ID ) {
   148         return TRUE;
   149     } else {
   150         return FALSE;
   151     }
   152 }
   154 #define RETURN_PARSE_ERROR( ... ) do { SET_ERROR(err, LX_ERR_FILE_INVALID, __VA_ARGS__); return FALSE; } while(0)
   156 static gboolean nrg_image_read_toc( cdrom_disc_t disc, ERROR *err )
   157 {
   158     union nrg_footer footer;
   159     struct nrg_chunk chunk;
   160     struct nrg_daoi *dao;
   161     struct nrg_daox *daox;
   162     struct nrg_etnf *etnf;
   163     struct nrg_etn2 *etn2;
   164     gboolean end = FALSE;
   165     uint32_t chunk_id;
   166     int session_id = 1;
   167     int session_track_id = 0;
   168     int track_id = 0;
   169     int cue_track_id = 0, cue_track_count = 0;
   170     int i, count;
   172     FILE *f = cdrom_disc_get_base_file(disc);
   174     fseek( f, -12, SEEK_END );
   175     fread( &footer, sizeof(footer), 1, f );
   176     uint32_t start = 0;
   177     if( GUINT32_FROM_BE(footer.v50.id) == NERO_V50_ID ) {
   178         start = GUINT32_FROM_BE(footer.v50.offset);
   179     } else if( GUINT32_FROM_BE(footer.v55.id) == NERO_V55_ID ) {
   180         start = (uint32_t)GUINT64_FROM_BE(footer.v55.offset);
   181     } else {
   182         /* Not a (recognized) Nero image (should never happen) */
   183         RETURN_PARSE_ERROR("File is not an NRG image" );
   184     }
   185     if( fseek( f, start, SEEK_SET) != 0 ) {
   186         RETURN_PARSE_ERROR("File is not a valid NRG image" );
   187     }
   189     do {
   190         fread( &chunk, sizeof(chunk), 1, f );
   191         chunk.length = GUINT32_FROM_BE(chunk.length);
   192         char data[chunk.length];
   193         fread( data, chunk.length, 1, f );
   194         chunk_id = GUINT32_FROM_BE(chunk.id);
   195         switch( chunk_id ) {
   196         case CUES_ID:
   197         case CUEX_ID:
   198             cue_track_id = track_id;
   199             cue_track_count = ((chunk.length / sizeof(struct nrg_cues)) >> 1) - 1;
   200             track_id += cue_track_count;
   201             for( i=0; i<chunk.length; i+= sizeof(struct nrg_cues) ) {
   202                 struct nrg_cues *cue = (struct nrg_cues *)(data+i);
   203                 int track = 0;
   204                 uint32_t lba;
   205                 if( chunk_id == CUEX_ID ) {
   206                     lba = GUINT32_FROM_BE( cue->addr );
   207                 } else {
   208                     lba = BCD_MSFTOLBA( cue->addr );
   209                 }
   210                 if( cue->track == 0 )
   211                     continue; /* Track 0. Leadin? always 0? */
   212                 if( cue->track == 0xAA ) { /* end of disc */
   213                     disc->leadout = lba;
   214                 } else {
   215                     track = BCDTOU8(cue->track) - 1;
   216                     if( (cue->control & 0x01) != 0 ) {
   217                         /* Track-start address */
   218                         disc->track[track].lba = lba;
   219                         disc->track[track].flags = cue->type;
   220                     }
   221                 }
   222             }
   223             break;
   224         case DAOI_ID:
   225             dao = (struct nrg_daoi *)data;
   226             count = dao->track_count - cue_track_id;
   227             memcpy( disc->mcn, dao->mcn, 13 );
   228             disc->mcn[13] = '\0';
   229             if( dao->track_count != track_id ||
   230                 count * 30 + 22 != chunk.length ) {
   231                 RETURN_PARSE_ERROR( "Invalid NRG image file (bad DAOI block)" );
   232             }
   233             for( i=0; i<count; i++ ) {
   234                 uint32_t offset = GUINT32_FROM_BE(dao->track[i].offset);
   235                 sector_mode_t mode = nrg_track_mode( dao->track[i].mode );
   236                 if( mode == -1 ) {
   237                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", dao->track[i].mode);
   238                 }
   239                 if( CDROM_SECTOR_SIZE(mode) != GUINT32_FROM_BE(dao->track[i].sector_size) ) {
   240                     /* Sector size mismatch */
   241                     RETURN_PARSE_ERROR("Invalid NRG image file (Bad sector size in DAOI block)");
   242                 }
   243                 cdrom_count_t sector_count =
   244                     (GUINT32_FROM_BE(dao->track[i].end) - GUINT32_FROM_BE(dao->track[i].offset))/
   245                     CDROM_SECTOR_SIZE(mode);
   246                 disc->track[cue_track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   247                 cue_track_id++;
   248             }
   249             break;
   250         case DAOX_ID:
   251             daox = (struct nrg_daox *)data;
   252             count = daox->track_count - cue_track_id;
   253             memcpy( disc->mcn, daox->mcn, 13 );
   254             disc->mcn[13] = '\0';
   255             if( daox->track_count != track_id ||
   256                 count * 42 + 22 != chunk.length ) {
   257                 RETURN_PARSE_ERROR( "Invalid NRG image file (bad DAOX block)" );
   258             }
   259             for( i=0; i<count; i++ ) {
   260                 uint32_t offset = (uint32_t)GUINT64_FROM_BE(daox->track[i].offset);
   261                 sector_mode_t mode = nrg_track_mode( daox->track[i].mode );
   262                 if( mode == -1 ) {
   263                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", daox->track[i].mode);
   264                 }
   265                 if( CDROM_SECTOR_SIZE(mode) != GUINT32_FROM_BE(daox->track[i].sector_size) ) {
   266                     /* Sector size mismatch */
   267                     RETURN_PARSE_ERROR("Invalid NRG image file (Bad sector size in DAOX block)");
   268                 }
   269                 cdrom_count_t sector_count = (cdrom_count_t)
   270                     ((GUINT64_FROM_BE(daox->track[i].end) - GUINT64_FROM_BE(daox->track[i].offset))/
   271                     CDROM_SECTOR_SIZE(mode));
   272                 disc->track[cue_track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   273                 cue_track_id++;
   274             }
   275             break;
   277         case SINF_ID: 
   278             /* Data is a single 32-bit number representing number of tracks in session */
   279             i = GUINT32_FROM_BE( *(uint32_t *)data );
   280             while( i-- > 0 )
   281                 disc->track[session_track_id++].sessionno = session_id;
   282             session_id++;
   283             break;
   284         case ETNF_ID:
   285             etnf = (struct nrg_etnf *)data;
   286             count = chunk.length / sizeof(struct nrg_etnf);
   287             for( i=0; i < count; i++, etnf++ ) {
   288                 uint32_t offset = GUINT32_FROM_BE(etnf->offset);
   289                 sector_mode_t mode = nrg_track_mode( GUINT32_FROM_BE(etnf->mode) );
   290                 if( mode == -1 ) {
   291                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", etnf->mode);
   292                 }
   293                 cdrom_count_t sector_count = GUINT32_FROM_BE(etnf->length) /
   294                         CDROM_SECTOR_SIZE(mode);
   296                 disc->track[track_id].lba = GUINT32_FROM_BE(etnf->lba) + i*CDROM_PREGAP;
   297                 if( mode == SECTOR_CDDA )
   298                     disc->track[track_id].flags = 0x01;
   299                 else
   300                     disc->track[track_id].flags = 0x01 | TRACK_FLAG_DATA;
   301                 disc->track[track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   302                 track_id++;
   303             }
   304             break;
   305         case ETN2_ID:
   306             etn2 = (struct nrg_etn2 *)data;
   307             count = chunk.length / sizeof(struct nrg_etn2);
   308             for( i=0; i < count; i++, etn2++ ) {
   309                 uint32_t offset = (uint32_t)GUINT64_FROM_BE(etn2->offset);
   310                 sector_mode_t mode = nrg_track_mode( GUINT32_FROM_BE(etn2->mode) );
   311                 if( mode == -1 ) {
   312                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", etn2->mode);
   313                 }
   314                 cdrom_count_t sector_count = (uint32_t)(GUINT64_FROM_BE(etn2->length) /
   315                         CDROM_SECTOR_SIZE(mode));
   317                 disc->track[track_id].lba = GUINT32_FROM_BE(etn2->lba) + i*CDROM_PREGAP;
   318                 if( mode == SECTOR_CDDA )
   319                     disc->track[track_id].flags = 0x01;
   320                 else
   321                     disc->track[track_id].flags = 0x01 | TRACK_FLAG_DATA;
   322                 disc->track[track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   323                 track_id++;
   324             }
   325             break;
   327         case END_ID:
   328             end = TRUE;
   329             break;
   330         }
   331     } while( !end );
   333     disc->track_count = track_id;
   334     disc->session_count = session_id-1;
   335     return TRUE;
   336 }
.