#!/usr/bin/env ruby
# $Id: cd-read.rb,v 1.10 2006/12/21 11:55:23 rocky Exp $
#
# A program to read CD blocks. See read-cd from the libcdio distribution
# more complete program.

#
#    Copyright (C) 2006 Rocky Bernstein <rocky@gnu.org>
#
#    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 2 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, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
mypath = File.dirname(__FILE__)
if(File::exists?(mypath + "/../lib/cdio.rb"))
  $: << File.dirname(__FILE__) + '/../lib'
  $: << File.dirname(__FILE__) + '/../ext/cdio'
else
  require 'rubygems'
  require_gem "rbcdio"
end
require "cdio"
require 'getoptlong'

# Print short help - the command form and a one-line description.
def usage()
  puts "%s --mode *mode* [options] [device]
    Read blocks of a CD" % $0
end

# Print help (includes usage), option descriptions, and then exit.
def help
  usage
  puts
  puts "Options are ..."
  puts
  OPTIONS.sort.each do |long, short, mode, desc|
    if mode == GetoptLong::REQUIRED_ARGUMENT
      if desc =~ /\b([A-Z]{2,})\b/
        long = long + "=#{$1}"
      end
    end
    printf "  %-20s (%s)\n", long, short
    printf "      %s\n", desc
  end
  exit 100
end

read_modes = {
    'audio' => Rubycdio::READ_MODE_AUDIO,
    'm1f1'  => Rubycdio::READ_MODE_M1F1,
    'm1f2'  => Rubycdio::READ_MODE_M1F2,
    'm2f1'  => Rubycdio::READ_MODE_M2F1,
    'm2f2'  => Rubycdio::READ_MODE_M2F2,
    'data'  => nil
    }

# Standard C library's isprint() in Ruby
def isprint(c)
  /[[:print:]]/ === c.chr
end

# Print a hex dump and string interpretation of buffer.
# If just_hex is true, then show only the hex dump.
def hexdump (buffer, just_hex=false)
  for i in 0 .. buffer.length - 1
    if i % 16 == 0
      printf "0x%04x: ", i
    end
    printf "%02x", buffer[i]
    printf " " if i % 2 == 1
    if i % 16 == 15
      if !just_hex
        printf "  "
        for j in i-15 .. i
          printf "%s", isprint(buffer[j]) ?  buffer[j].chr : '.'
        end
      end
      printf "\n"
    end
  end
  print "\n"
end

# Return a list of the command line options supported by the
# program.
def command_line_options
  OPTIONS.collect { |lst| lst[0..-2] }
end

OPTIONS = [
           ['--help',        '-h', GetoptLong::NO_ARGUMENT,
            "Display this help message."],
           ["--mode", "-m", GetoptLong::REQUIRED_ARGUMENT,
           "CD Reading mode: audio, m1f1, m1f2 m2f1 or m2f2"],
           ["--start", "-s", GetoptLong::REQUIRED_ARGUMENT,
             "Starting block"],
           ["--number", "-n", GetoptLong::REQUIRED_ARGUMENT,
           "Number of blocks"]
           ]

options={}
options['--start'] = 1
options['--number'] = 1
options['--mode'] = nil
opts = GetoptLong.new(*command_line_options)

begin
  opts.each { |opt, value| options[opt]=value }
rescue GetoptLong::InvalidOption
  exit 3
end
help if options["--help"]

if not options['--mode']
  puts "Mode option must given (and one of audio, m1f1, m1f2, m1f2 or m1f2)."
  exit(1)
end
begin
  read_mode = read_modes[options['--mode']]
rescue KeyError
  puts "Need to use the --mode option with one of audio, m1f1, m1f2, m1f2 or m1f2"
  exit 1
end


# While sys.argv[0] is a program name and sys.argv[1] the first
# option, argv[0] is the first unprocessed option -- roughly
# the equivalent of sys.argv[1].
if ARGV.length() > 1
    begin
        d = Cdio::Device.new(ARGV.first)
    rescue IOError
        puts "Problem opening CD-ROM: %s" % argv[0]
        exit(1)
    end
else
    begin
        d = Cdio::Device.new(nil, Rubycdio::DRIVER_UNKNOWN)
    rescue IOError
        puts "Problem finding a CD-ROM"
        exit(1)
    end
end

## All this setup just to issue this one of these commands.
if read_mode == nil
    blocks, data=d.read_data_blocks(options['--start'].to_i, 
                                    options['--number'].to_i)
else
    blocks, data=d.read_sectors(options['--start'].to_i, read_mode, 
                                options['--number'].to_i)
end
if data
  hexdump data
end