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 1109:700c5ab26a63
prev1097:d4807997e450
next1178:e55ec927d55d
author nkeynes
date Thu Jun 10 22:13:16 2010 +1000 (13 years ago)
permissions -rw-r--r--
last change Integrate executable wrapping into the user interface
- command-line now loads wrapped by default, -e <bin> to run binary
- add support for .bin executables
- Add useful (internal) error codes
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     } 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     default: return -1;
   136     }
   137 }
   139 static gboolean nrg_image_is_valid( FILE *f )
   140 {
   141     union nrg_footer footer;
   143     fseek( f, -12, SEEK_END );
   144     fread( &footer, sizeof(footer), 1, f );
   145     if( GUINT32_FROM_BE(footer.v50.id) == NERO_V50_ID ||
   146             GUINT32_FROM_BE(footer.v55.id) == NERO_V55_ID ) {
   147         return TRUE;
   148     } else {
   149         return FALSE;
   150     }
   151 }
   153 #define RETURN_PARSE_ERROR( ... ) do { SET_ERROR(err, LX_ERR_FILE_INVALID, __VA_ARGS__); return FALSE; } while(0)
   155 static gboolean nrg_image_read_toc( cdrom_disc_t disc, ERROR *err )
   156 {
   157     union nrg_footer footer;
   158     struct nrg_chunk chunk;
   159     struct nrg_daoi *dao;
   160     struct nrg_daox *daox;
   161     struct nrg_etnf *etnf;
   162     struct nrg_etn2 *etn2;
   163     gboolean end = FALSE;
   164     uint32_t chunk_id;
   165     int session_id = 1;
   166     int session_track_id = 0;
   167     int track_id = 0;
   168     int cue_track_id = 0, cue_track_count = 0;
   169     int i, count;
   171     FILE *f = cdrom_disc_get_base_file(disc);
   173     fseek( f, -12, SEEK_END );
   174     fread( &footer, sizeof(footer), 1, f );
   175     if( GUINT32_FROM_BE(footer.v50.id) == NERO_V50_ID ) {
   176         fseek( f, GUINT32_FROM_BE(footer.v50.offset), SEEK_SET );
   177     } else if( GUINT32_FROM_BE(footer.v55.id) == NERO_V55_ID ) {
   178         fseek( f, (uint32_t)GUINT64_FROM_BE(footer.v55.offset), SEEK_SET );
   179     } else {
   180         /* Not a (recognized) Nero image (should never happen) */
   181         RETURN_PARSE_ERROR("File is not an NRG image" );
   182     }
   184     do {
   185         fread( &chunk, sizeof(chunk), 1, f );
   186         chunk.length = GUINT32_FROM_BE(chunk.length);
   187         char data[chunk.length];
   188         fread( data, chunk.length, 1, f );
   189         chunk_id = GUINT32_FROM_BE(chunk.id);
   190         switch( chunk_id ) {
   191         case CUES_ID:
   192         case CUEX_ID:
   193             cue_track_id = track_id;
   194             cue_track_count = ((chunk.length / sizeof(struct nrg_cues)) >> 1) - 1;
   195             track_id += cue_track_count;
   196             for( i=0; i<chunk.length; i+= sizeof(struct nrg_cues) ) {
   197                 struct nrg_cues *cue = (struct nrg_cues *)(data+i);
   198                 int track = 0;
   199                 uint32_t lba;
   200                 if( chunk_id == CUEX_ID ) {
   201                     lba = GUINT32_FROM_BE( cue->addr );
   202                 } else {
   203                     lba = BCD_MSFTOLBA( cue->addr );
   204                 }
   205                 if( cue->track == 0 )
   206                     continue; /* Track 0. Leadin? always 0? */
   207                 if( cue->track == 0xAA ) { /* end of disc */
   208                     disc->leadout = lba;
   209                 } else {
   210                     track = BCDTOU8(cue->track) - 1;
   211                     if( (cue->control & 0x01) != 0 ) {
   212                         /* Track-start address */
   213                         disc->track[track].lba = lba;
   214                         disc->track[track].flags = cue->type;
   215                     }
   216                 }
   217             }
   218             break;
   219         case DAOI_ID:
   220             dao = (struct nrg_daoi *)data;
   221             count = dao->track_count - cue_track_id;
   222             memcpy( disc->mcn, dao->mcn, 13 );
   223             disc->mcn[13] = '\0';
   224             if( dao->track_count != track_id ||
   225                 count * 30 + 22 != chunk.length ) {
   226                 RETURN_PARSE_ERROR( "Invalid NRG image file (bad DAOI block)" );
   227             }
   228             for( i=0; i<count; i++ ) {
   229                 uint32_t offset = GUINT32_FROM_BE(dao->track[i].offset);
   230                 sector_mode_t mode = nrg_track_mode( dao->track[i].mode );
   231                 if( mode == -1 ) {
   232                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", dao->track[i].mode);
   233                 }
   234                 if( CDROM_SECTOR_SIZE(mode) != GUINT32_FROM_BE(dao->track[i].sector_size) ) {
   235                     /* Sector size mismatch */
   236                     RETURN_PARSE_ERROR("Invalid NRG image file (Bad sector size in DAOI block)");
   237                 }
   238                 cdrom_count_t sector_count =
   239                     (GUINT32_FROM_BE(dao->track[i].end) - GUINT32_FROM_BE(dao->track[i].offset))/
   240                     CDROM_SECTOR_SIZE(mode);
   241                 disc->track[cue_track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   242                 cue_track_id++;
   243             }
   244             break;
   245         case DAOX_ID:
   246             daox = (struct nrg_daox *)data;
   247             count = daox->track_count - cue_track_id;
   248             memcpy( disc->mcn, daox->mcn, 13 );
   249             disc->mcn[13] = '\0';
   250             if( daox->track_count != track_id ||
   251                 count * 42 + 22 != chunk.length ) {
   252                 RETURN_PARSE_ERROR( "Invalid NRG image file (bad DAOX block)" );
   253             }
   254             for( i=0; i<count; i++ ) {
   255                 uint32_t offset = (uint32_t)GUINT64_FROM_BE(daox->track[i].offset);
   256                 sector_mode_t mode = nrg_track_mode( daox->track[i].mode );
   257                 if( mode == -1 ) {
   258                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", daox->track[i].mode);
   259                 }
   260                 if( CDROM_SECTOR_SIZE(mode) != GUINT32_FROM_BE(daox->track[i].sector_size) ) {
   261                     /* Sector size mismatch */
   262                     RETURN_PARSE_ERROR("Invalid NRG image file (Bad sector size in DAOX block)");
   263                 }
   264                 cdrom_count_t sector_count = (cdrom_count_t)
   265                     ((GUINT64_FROM_BE(daox->track[i].end) - GUINT64_FROM_BE(daox->track[i].offset))/
   266                     CDROM_SECTOR_SIZE(mode));
   267                 disc->track[cue_track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   268                 cue_track_id++;
   269             }
   270             break;
   272         case SINF_ID: 
   273             /* Data is a single 32-bit number representing number of tracks in session */
   274             i = GUINT32_FROM_BE( *(uint32_t *)data );
   275             while( i-- > 0 )
   276                 disc->track[session_track_id++].sessionno = session_id;
   277             session_id++;
   278             break;
   279         case ETNF_ID:
   280             etnf = (struct nrg_etnf *)data;
   281             count = chunk.length / sizeof(struct nrg_etnf);
   282             for( i=0; i < count; i++, etnf++ ) {
   283                 uint32_t offset = GUINT32_FROM_BE(etnf->offset);
   284                 sector_mode_t mode = nrg_track_mode( GUINT32_FROM_BE(etnf->mode) );
   285                 if( mode == -1 ) {
   286                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", etnf->mode);
   287                 }
   288                 cdrom_count_t sector_count = GUINT32_FROM_BE(etnf->length) /
   289                         CDROM_SECTOR_SIZE(mode);
   291                 disc->track[track_id].lba = GUINT32_FROM_BE(etnf->lba) + i*CDROM_PREGAP;
   292                 if( mode == SECTOR_CDDA )
   293                     disc->track[track_id].flags = 0x01;
   294                 else
   295                     disc->track[track_id].flags = 0x01 | TRACK_FLAG_DATA;
   296                 disc->track[track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   297                 track_id++;
   298             }
   299             break;
   300         case ETN2_ID:
   301             etn2 = (struct nrg_etn2 *)data;
   302             count = chunk.length / sizeof(struct nrg_etn2);
   303             for( i=0; i < count; i++, etn2++ ) {
   304                 uint32_t offset = (uint32_t)GUINT64_FROM_BE(etn2->offset);
   305                 sector_mode_t mode = nrg_track_mode( GUINT32_FROM_BE(etn2->mode) );
   306                 if( mode == -1 ) {
   307                     RETURN_PARSE_ERROR("Unknown track mode in NRG image file (%d)", etn2->mode);
   308                 }
   309                 cdrom_count_t sector_count = (uint32_t)(GUINT64_FROM_BE(etn2->length) /
   310                         CDROM_SECTOR_SIZE(mode));
   312                 disc->track[track_id].lba = GUINT32_FROM_BE(etn2->lba) + i*CDROM_PREGAP;
   313                 if( mode == SECTOR_CDDA )
   314                     disc->track[track_id].flags = 0x01;
   315                 else
   316                     disc->track[track_id].flags = 0x01 | TRACK_FLAG_DATA;
   317                 disc->track[track_id].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count );
   318                 track_id++;
   319             }
   320             break;
   322         case END_ID:
   323             end = TRUE;
   324             break;
   325         }
   326     } while( !end );
   328     disc->track_count = track_id;
   329     disc->session_count = session_id-1;
   330     return TRUE;
   331 }
.