#!/usr/bin/perl

use Getopt::Std;

#
# Script that will extract the first El Torito bootimage from a
# bootable CD image
# R. Krienke 08/2001
# krienke@uni-koblenz.de
# License: This script may be freely distributed
#
# Get latest version from:
# http://www.uni-koblenz.de/~krienke/ftp/noarch/geteltorito
#
# Version 0.1
#    Initial release
# Version 0.2
#    Several patches included from Nathan Stratton Treadway(nathant@ontko.com)
#    to adjust the platform output as well as fixes for other minor bugs
#
# For information on El Torito see 
# http://www.cdpage.com/Compact_Disc_Variations/eltoritoi.html

$secSize=2048;
$ret=undef;$version=undef;$opt_h=undef;$loadSegment=undef;$systemType=undef;

#
# Read a particular sector from a file
# sector counting starts at 0, not 1
#
sub getSector{
   my ($secNum, $secCount, $file)=@_;
   my ($sec, $count);

   open(FILE, $file) || die "Cannot open \"$file\" \n";

   seek(FILE, $secNum*$secSize, 0);
   $count=read(FILE, $sec, $secSize*$secCount, 0) ;
   if( $count != $secSize*$secCount ){
   	warn "Error reading $secSize bytes from file \"$file\"\n";
   }
   close(FILE);

   return($sec);
}


#
# Usage
#
sub usage{
	die "\n$0 [-h] cd-image \n",
	    "	Script will try to extract an El Torito image from a \n",
	    "	bootable CD (or cd-image)  and write the data extracted to STDOUT\n\n";
}


# ---------------------------------------------------------------------
$ret=getopts('h');
 
if( defined($opt_h) || $#ARGV <0 ){
         usage(0);
}	 
	 
$imageFile=$ARGV[0];

if( ! -r $imageFile ){
	die "Cannot read image/device \"$imageFile\". Aborting\n";
}

#
# Read Sector 17 from CD which should contain a Boot Record Volume
# descriptor. This descriptor contains at its start the text ($isoIdent)
# CD001     and ($toritoSpec)
# EL TORITO SPECIFICATION
# see http://www.cdpage.com/Compact_Disc_Variations/eltoritoi.html
# for details
#

$sector=getSector(17, 1, $imageFile );
($boot, $isoIdent, $version, $toritoSpec,
 	$unUsed, $bootP)= unpack( "Ca5CA32A32L", $sector );

if( $isoIdent ne "CD001" || $toritoSpec ne "EL TORITO SPECIFICATION" ){
	die "This data image does not seem to be a bootable CD-image\n";
}	

#
# Now fetch the sector of the booting catalog 
#
$sector=getSector($bootP, 1, $imageFile );

print STDERR "Booting catalog starts at sector: $bootP \n";

# The first 32 bytes of this sector contains the validation entry for a
# boot. The first byte has to be 01, the next byte determines the
# architecture the image is designed for, where 00 is i86, 01 is PowerPC
# and 02 is Mac. More data give info about manufacturer, etc.  The 
# final two bytes must contain 0x55 and 0xAA respectively (as 
# defined by the El Torito standard).

$validateEntry=substr($sector, 0, 32);

($header, $platform, $unUsed, $manufact, $unUsed, $five, $aa)=
		unpack( "CCSA24SCC", $validateEntry);

if( $header != 1 || $five != 0x55 || $aa != 0xaa ){
	die "Invalid Validation Entry on image \n";
}

print STDERR "Manufacturer of CD: $manufact\n";
print STDERR "Image architecture: ";
print STDERR "x86" if( $platform == 0 );
print STDERR "PowerPC" if( $platform == 1 );
print STDERR "Mac" if( $platform == 2 );
print STDERR "unknown ($platform)" if( $platform > 2 );
print STDERR "\n";

#
# Now we examine the initial/defaultentry which follows the validate
# entry and has a size of 32 bytes. 

$initialEntry=substr($sector, 32, 32);

($boot, $media, $loadSegment, $systemType, $unUsed, 
	$sCount, $imgStart, $unUsed)=unpack( "CCSCCnLC", $initialEntry);

if( $boot != 0x88 ){
	die "Boot indicator in Initial/Default-Entry is not 0x88. CD is not bootable. \n";
}    

print STDERR "Boot media type is: ";
if( $media == 0 ){
	print STDERR "no emulation";
	$count=0;
}
if( $media == 1 ){
	print STDERR "1.2meg floppy";
	$count=1200*1024/$secSize;  
}
if( $media == 2 ){
	print STDERR "1.44meg floppy";
	$count=1440*1024/$secSize;  
}
if( $media == 3 ){
	print STDERR "2.88meg floppy";
	$count=2880*1024/$secSize;  
}
if( $media == 4 ){
	print STDERR "harddisk";
	$count=0;
}
print STDERR "\n";

# Only use the internal sector counter if the real size is unknown
# ($count==0)
$cnt=$count==0?$sCount:$count;

print STDERR "El Torito image starts at sector $imgStart and has $cnt sector(s) of $secSize Bytes\n";

# We are there:
# Now read the bootimage to stdout
$image=getSector($imgStart, $cnt, $imageFile);

print "$image";
print STDERR "Image has been written to stdout ....\n"; 
