Search
lxdream.org :: lxdream/src/gdrom/nrg.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/gdrom/nrg.c
changeset 138:afabd7e6d26d
next142:2f631c3a3946
author nkeynes
date Sun Apr 30 01:51:08 2006 +0000 (14 years ago)
permissions -rw-r--r--
last change Add Nero file format handler and general gdrom disc hooks
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/gdrom/nrg.c Sun Apr 30 01:51:08 2006 +0000
1.3 @@ -0,0 +1,258 @@
1.4 +/**
1.5 + * $Id: nrg.c,v 1.1 2006-04-30 01:51:08 nkeynes Exp $
1.6 + *
1.7 + * Nero (NRG) CD file format. File information stolen shamelessly from
1.8 + * libcdio.
1.9 + *
1.10 + * Copyright (c) 2005 Nathan Keynes.
1.11 + *
1.12 + * This program is free software; you can redistribute it and/or modify
1.13 + * it under the terms of the GNU General Public License as published by
1.14 + * the Free Software Foundation; either version 2 of the License, or
1.15 + * (at your option) any later version.
1.16 + *
1.17 + * This program is distributed in the hope that it will be useful,
1.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.20 + * GNU General Public License for more details.
1.21 + */
1.22 +
1.23 +#include <assert.h>
1.24 +#include <stdio.h>
1.25 +#include <errno.h>
1.26 +#include "gdrom/gdrom.h"
1.27 +#include "dream.h"
1.28 +
1.29 +#define NERO_V55_ID 0x4e455235
1.30 +#define NERO_V50_ID 0x4e45524f
1.31 +
1.32 +/* Courtesy of libcdio */
1.33 +/* 5.0 or earlier */
1.34 +#define NERO_ID 0x4e45524f /* Nero pre 5.5.x */
1.35 +#define CUES_ID 0x43554553 /* Nero pre version 5.5.x-6.x */
1.36 +#define DAOI_ID 0x44414f49
1.37 +#define ETNF_ID 0x45544e46
1.38 +#define SINF_ID 0x53494e46 /* Session information */
1.39 +#define END_ID 0x454e4421
1.40 +/* 5.5+ only */
1.41 +#define NER5_ID 0x4e455235 /* Nero version 5.5.x */
1.42 +#define CDTX_ID 0x43445458 /* CD TEXT */
1.43 +#define CUEX_ID 0x43554558 /* Nero version 5.5.x-6.x */
1.44 +#define DAOX_ID 0x44414f58 /* Nero version 5.5.x-6.x */
1.45 +#define ETN2_ID 0x45544e32
1.46 +#define MTYP_ID 0x4d545950 /* Disc Media type? */
1.47 +
1.48 +
1.49 +union nrg_footer {
1.50 + struct nrg_footer_v50 {
1.51 + uint32_t dummy;
1.52 + uint32_t id;
1.53 + uint32_t offset;
1.54 + } v50;
1.55 + struct nrg_footer_v55 {
1.56 + uint32_t id;
1.57 + uint64_t offset;
1.58 + } v55;
1.59 +};
1.60 +
1.61 +struct nrg_chunk {
1.62 + uint32_t id;
1.63 + uint32_t length;
1.64 +};
1.65 +
1.66 +struct nrg_etnf {
1.67 + uint32_t offset;
1.68 + uint32_t length;
1.69 + uint32_t mode;
1.70 + uint32_t lba;
1.71 + uint32_t padding;
1.72 +};
1.73 +
1.74 +struct nrg_cues {
1.75 + uint8_t type;
1.76 + uint8_t track;
1.77 + uint8_t control;
1.78 + uint8_t pad;
1.79 + uint32_t addr;
1.80 +};
1.81 +
1.82 +struct nrg_daoi {
1.83 + uint32_t length;
1.84 + char mcn[14];
1.85 + uint8_t disc_mode;
1.86 + uint8_t unknown[2]; /* always 01 01? */
1.87 + uint8_t track_count;
1.88 + struct nrg_daoi_track {
1.89 + char unknown[10];
1.90 + uint32_t sector_size __attribute__((packed)); /* Always 0? */
1.91 + uint8_t mode;
1.92 + uint8_t unknown2[3]; /* Always 00 00 01? */
1.93 + uint32_t pregap __attribute__((packed));
1.94 + uint32_t offset __attribute__((packed));
1.95 + uint32_t end __attribute__((packed));
1.96 + } track[0];
1.97 +} __attribute__((packed));
1.98 +
1.99 +/**
1.100 + * Convert an 8-bit BCD number to normal integer form.
1.101 + * Eg, 0x79 => 79
1.102 + */
1.103 +uint8_t static bcd_to_uint8( uint8_t bcd )
1.104 +{
1.105 + return (bcd & 0x0F) + (((bcd & 0xF0)>>4)*10);
1.106 +}
1.107 +
1.108 +
1.109 +/**
1.110 + * Convert a 32 bit MSF address (BCD coded) to the
1.111 + * equivalent LBA form.
1.112 + * Eg, 0x
1.113 + */
1.114 +uint32_t static msf_to_lba( uint32_t msf )
1.115 +{
1.116 + msf = ntohl(msf);
1.117 + int f = bcd_to_uint8(msf);
1.118 + int s = bcd_to_uint8(msf>>8);
1.119 + int m = bcd_to_uint8(msf>>16);
1.120 + return (m * 60 + s) * 75 + f;
1.121 +
1.122 +}
1.123 +
1.124 +uint32_t static nrg_track_mode( uint8_t mode )
1.125 +{
1.126 + switch( mode ) {
1.127 + case 0: return GDROM_MODE1;
1.128 + case 2: return GDROM_MODE2_XA1;
1.129 + case 3: return GDROM_MODE2;
1.130 + case 7: return GDROM_CDDA;
1.131 + default:
1.132 + ERROR( "Unrecognized track mode %d in Nero image", mode );
1.133 + return -1;
1.134 + }
1.135 +}
1.136 +
1.137 +gdrom_disc_t nrg_image_open( const gchar *filename )
1.138 +{
1.139 + FILE *f = fopen( filename, "ro" );
1.140 + union nrg_footer footer;
1.141 + struct nrg_chunk chunk;
1.142 + struct nrg_daoi *dao;
1.143 + gdrom_disc_t disc;
1.144 + gboolean end = FALSE;
1.145 + int session_id = 0;
1.146 + int session_track_id = 0;
1.147 + int track_id = 0;
1.148 + int cue_track_id = 0, cue_track_count = 0;
1.149 + int i;
1.150 +
1.151 + if( f == NULL ) {
1.152 + ERROR( "Unable to open file '%s': %s", filename, strerror(errno) );
1.153 + return NULL;
1.154 + }
1.155 +
1.156 + fseek( f, -12, SEEK_END );
1.157 + fread( &footer, sizeof(footer), 1, f );
1.158 + if( ntohl(footer.v50.id) == NERO_V50_ID ) {
1.159 + INFO( "Loading Nero 5.0 image" );
1.160 + fseek( f, ntohl(footer.v50.offset), SEEK_SET );
1.161 + } else if( ntohl(footer.v55.id) == NERO_V55_ID ) {
1.162 + INFO( "Loading Nero 5.5+ image" );
1.163 + fseek( f, ntohl(footer.v55.offset), SEEK_SET );
1.164 + } else {
1.165 + ERROR("Unable to understand file '%s' as a Nero image", filename );
1.166 + return NULL;
1.167 + }
1.168 +
1.169 + disc = gdrom_image_new(f);
1.170 + if( disc == NULL ) {
1.171 + ERROR("Unable to allocate memory!");
1.172 + fclose(f);
1.173 + return NULL;
1.174 + }
1.175 +
1.176 + do {
1.177 + fread( &chunk, sizeof(chunk), 1, f );
1.178 + chunk.length = ntohl(chunk.length);
1.179 + char data[chunk.length];
1.180 + fread( data, chunk.length, 1, f );
1.181 + switch( ntohl(chunk.id) ) {
1.182 + case CUES_ID:
1.183 + cue_track_id = track_id;
1.184 + cue_track_count = ((chunk.length / sizeof(struct nrg_cues)) >> 1) - 1;
1.185 + track_id += cue_track_count;
1.186 + for( i=0; i<chunk.length; i+= sizeof(struct nrg_cues) ) {
1.187 + struct nrg_cues *cue = (struct nrg_cues *)(data+i);
1.188 + int track = 0;
1.189 + if( cue->track == 0 )
1.190 + continue; /* Track 0. Leadin? always 0? */
1.191 + if( cue->track == 0xAA ) { /* end of disc */
1.192 + disc->track[track_id-1].sector_count =
1.193 + msf_to_lba( cue->addr ) - disc->track[track_id-1].lba;
1.194 + } else {
1.195 + track = cue_track_id + bcd_to_uint8(cue->track) - 1;
1.196 + if( (cue->control & 0x01) == 0 ) {
1.197 + /* Pre-gap address. */
1.198 + if( track != 0 ) {
1.199 + disc->track[track-1].sector_count =
1.200 + msf_to_lba( cue->addr ) - disc->track[track-1].lba;
1.201 + }
1.202 + } else { /* Track-start address */
1.203 + disc->track[track].lba = msf_to_lba( cue->addr );
1.204 +
1.205 + }
1.206 + }
1.207 + }
1.208 + break;
1.209 + case DAOI_ID:
1.210 + dao = (struct nrg_daoi *)data;
1.211 + memcpy( disc->mcn, dao->mcn, 13 );
1.212 + disc->mcn[13] = '\0';
1.213 + assert( dao->track_count * 30 + 22 == chunk.length );
1.214 + assert( dao->track_count == cue_track_count );
1.215 + for( i=0; i<dao->track_count; i++ ) {
1.216 + disc->track[cue_track_id].sector_size = ntohl(dao->track[i].sector_size);
1.217 + disc->track[cue_track_id].offset = ntohl(dao->track[i].offset);
1.218 + disc->track[cue_track_id].mode = nrg_track_mode( dao->track[i].mode );
1.219 + assert( disc->track[cue_track_id].sector_count ==
1.220 + (ntohl(dao->track[i].end) - ntohl(dao->track[i].offset))/
1.221 + ntohl(dao->track[i].sector_size) );
1.222 + cue_track_id++;
1.223 + }
1.224 + break;
1.225 + case SINF_ID:
1.226 + /* Data is a single 32-bit number representing number of tracks in session */
1.227 + i = ntohl( *(uint32_t *)data );
1.228 + while( i-- > 0 )
1.229 + disc->track[session_track_id++].session = session_id;
1.230 + session_id++;
1.231 + break;
1.232 + case ETNF_ID:
1.233 + for( i=0; i < chunk.length; i+= 0x14 ) {
1.234 + struct nrg_etnf *etnf = (struct nrg_etnf *)(data+i);
1.235 + disc->track[track_id].offset = ntohl(etnf->offset);
1.236 + disc->track[track_id].lba = ntohl(etnf->lba) + (i+1)*GDROM_PREGAP;
1.237 + disc->track[track_id].mode = nrg_track_mode( ntohl(etnf->mode) );
1.238 + if( disc->track[track_id].mode == -1 ) {
1.239 + disc->close(disc);
1.240 + return NULL;
1.241 + }
1.242 + disc->track[track_id].sector_size = GDROM_SECTOR_SIZE(disc->track[track_id].mode);
1.243 + disc->track[track_id].sector_count = ntohl(etnf->length) /
1.244 + disc->track[track_id].sector_size;
1.245 + track_id++;
1.246 + }
1.247 + break;
1.248 + case END_ID:
1.249 + end = TRUE;
1.250 + break;
1.251 + }
1.252 + } while( !end );
1.253 + disc->track_count = track_id;
1.254 + return disc;
1.255 +}
1.256 +
1.257 +gboolean nrg_read_sectors( struct gdrom_disc *disc, uint32_t lba, uint32_t count, char *buf )
1.258 +{
1.259 + return FALSE;
1.260 +}
1.261 +
.