





seti $show_help = $show_help
if $show_help = 1
  echo 'This will read the drive and write the data to an image file.'
  echo 'It uses DMA read commands and adjusts if extended commands are supported.'
  echo 'Please note that this program is in no way optimized for this.'
  echo 'Making an image using a script file will never be as fast as possible.'
  echo 'Reads are done in blocks (clusters) of 256 sectors.'
  echo 'This means that all warnings and errors show the position'
  echo ' of the first sector of the block, NOT the actual problem sector.'
  echo 'This requires the number variable startsector to be set.'
  echo 'This requires the number variable endsector to be set.'
  echo 'If endsector is set to 0, it will default to the last addressable sector.'
  echo 'This uses the identify device command to determine the last sector.'
  echo 'This requires the number variable timeout to be set.'
  echo 'Timeout is the time in seconds to wait before giving up on a command.'
  echo 'If timeout is set to 0, it will default to 5sec.'
  echo 'This requires the string variable logfile to be set.'
  echo 'Logfile will capture all the warnings and errors.'
  echo 'If logfile is blank then no log will be produced.'
  echo 'This requires the string variable imagefile to be set.'
  echo 'Imagefile is the file where the data will be written.'
  echo 'If imagefile is blank then no image file will be produced.'

  echo 'Example:'
  echo 'hddsupertool -t /dev/sda -f ata_image_device timeout=0 startsector=0 endsector=0 logfile=="image.log" imagefile=="image.bin"'
  echo 'Hit ENTER to continue...'
  userinput $choicestring
  previousscript
endif

include good_subroutines

sets $null = ""
seti $using_menu = $using_menu
seti $devicefault = 0
seti $currentspeed = 0
sets $pass1_datafile = "pass1data"
sets $pass2_datafile = "pass2data"

sets $resume_file = $pass1_datafile
seti $resume = 0
seti $resume_position = 0
seti $errorcount = 0
scratchpadsize 33554432
clearscratchpad

if $resume = 0
  getfilesize $pass2_datafile
  if $error_level > 255
    sets $resume_file = $pass2_datafile
    readscratchpad $resume_file 0 0 $error_level
    seti $resume_position = scratchpad 0 qw
    seti $errorcount = scratchpad 8 qw
    seti $resume = 1
    seti $pass = 2
  endif
endif

if $resume = 0
  getfilesize $pass1_datafile
  if $error_level > 255
    sets $resume_file = $pass1_datafile
    readscratchpad $resume_file 0 0 $error_level
    seti $resume_position = scratchpad 0 qw
    seti $errorcount = scratchpad 8 qw
    seti $resume = 1
    seti $pass = 1
  endif
endif



if $resume = 0
  seti $startsector = 0
else
  echo "resume=" $resume
  seti $startsector = $resume_position
  echo "Resuming start sector from position " $startsector
endif


seti $endsector = 0


seti $ask = 0
while 1 = 1
  variablecheck $timeout
  if $error_level < 16
    seti $ask = 1
  elseif $using_menu = 1
    seti $ask = 1
  endif
  if $ask = 1
    echo "Enter timeout in ms, or leave blank for default(10000ms):"
    userinput $choicestring
    if $choicestring != $null
      seti $timeout = $choicestring 0
      break
    else
      seti $timeout = 10000
      break
    endif
  else
    break
  endif
done
if $timeout = 0
  seti $timeout = 10000
endif
seti $timeout = $timeout * 1000
softtimeout $timeout


sets $logfile = "errorlog"

sets $imagefile = "image"


# get the size of the drive using identify device command
gosub reset_device
buffersize 512
setreadpio
ata28cmd 0 0 0 0 0 0xa0 0xec
# check if command failed
seti $command_failed = 0
gosub check_command_status
if $command_failed = 1
  echo "Identify device command failed!"
  gosub show_sense_data
  gosub show_ata_return_status
  previousscript
endif
# check if drive supports extended commands
seti $tempnum = buffer 167
seti $extended = $tempnum & 4
# total addressable sectors
if $extended > 0
  seti $addressable = buffer 200 qw
else
  seti $addressable = buffer 120 dw
endif

seti $endsector = $addressable

if $startsector >= $endsector
  echo "Reached end sector, nothing more to read."
  previousscript
endif


seti $blocksize = 256
seti $buffersize = $blocksize * 512
buffersize $buffersize
setreaddma
seti $currentposition = $startsector
seti $previousposition = $currentposition
seti $blockposition = $currentposition
seti $processedsectors = 0
seti $totalsectors = $endsector - $startsector

if $logfile != $null
  #deletefile $logfile
  gettime
  sets $line = "# error log " $date
  writelog $logfile $line
  sets $line = "# cluster size is " $blocksize
  writelog $logfile $line
endif

gettime
seti $displaytime = $time
seti $redodisplay = 1
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
gettime
seti $programstart = $time


gosub reset_device
while $currentposition < $endsector
  seti $sector = $currentposition

  if $extended > 0
    seti $features = 0
    seti $count = $blocksize
    seti $LBAlow = $sector & 0xffff
    seti $temp = $sector > 16
    seti $LBAmid = $temp & 0xffff
    seti $temp = $sector > 32
    seti $LBAhigh = $temp & 0xffff
    seti $device = 0xe0
    seti $command = 0x25
    gettime
    seti $startreadtime = $time
    ata48cmd $features $count $LBAhigh $LBAmid $LBAlow $device $command
    gettime
    seti $endreadtime = $time

  else
    seti $features = 0
    seti $count = $blocksize & 0xff
    seti $LBAlow = $sector & 0xff
    seti $temp = $sector > 8
    seti $LBAmid = $temp & 0xff
    seti $temp = $sector > 16
    seti $LBAhigh = $temp & 0xff
    seti $temp = $sector > 24
    seti $LBAdevice = $temp & 0x0f
    seti $device = 0xe0
    seti $device = $device | $LBAdevice
    seti $command = 0xc8
    gettime
    seti $startreadtime = $time
    ata28cmd $features $count $LBAlow $LBAmid $LBAhigh $device $command
    gettime
    seti $endreadtime = $time
  endif

  # check if command failed
  seti $command_failed = 0
  gosub check_command_status
  if $command_failed = 1
    seti $devicefault = $ata_return_status & 0x20
  endif

  # write the data to the file
  if $imagefile != $null
    if $command_failed = 0
      seti $writeoffset = $currentposition * 512
      seti $writesize = $blocksize * 512
      writebuffer $imagefile 0 $writeoffset $writesize
    endif
  endif

  seti $readtime = $endreadtime - $startreadtime

  if $command_failed = 1
    seti $errorcount = $errorcount + 1
  endif

  seti $showerror = 0
  if $command_failed = 1
    seti $showerror = 1
  endif

  if $showerror > 0
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    seti $adjustedtime = $readtime / 1000
    hex
    sets $errorstring = "e=0x" $ata_return_error " s=0x" $ata_return_status
    decimal
    if $direct_mode = 1
      if $command_status = 2
        sets $status = "BUSY"
      elseif $command_status = 3
        sets $status = "TIMEOUT/RESET"
      elseif $command_status = 4
        sets $status = "TIMEOUT/RESET/BUSY"
      elseif $command_status = 5
        sets $status = "TIMEOUT"
      elseif $command_status = 6
        sets $status = "NO DRQ"
      elseif $command_status = 7
        sets $status = "WRONG DEVICE"
      else
        sets $status = ""
      endif
    else
      sets $status = ""
    endif
    sets $outputline = "ERROR " $status " " $currentposition "  " $adjustedtime "ms " $errorstring
    echo $outputline
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    seti $redodisplay = 1
    if $logfile != $null
      writelog $logfile $outputline
    endif

    seti $error_offset = $errorcount * 8
    setscratchpad $error_offset
      f8: $currentposition
    endscratchpad
  endif


  seti $elapsedtime = $endreadtime - $displaytime
  if $elapsedtime > 1000000
    seti $redodisplay = 1
    seti $sectorstransferred = $currentposition - $previousposition
    seti $previousposition = $currentposition
    seti $bytestransferred = $sectorstransferred * 512
    seti $adjustedbytes = $bytestransferred * 1000000
    seti $currentspeed = $adjustedbytes / $elapsedtime
    seti $currentspeed = $currentspeed / 1000
  endif
  if $redodisplay = 1
    gosub displayoutput
    seti $displaytime = $endreadtime
  endif
  seti $redodisplay = 0


  if $devicefault > 0
    echo "Device fault detected!"
    echo "Current position= " $currentposition
    gosub show_ata_return_status
    break
  endif

  seti $currentposition = $currentposition + $blocksize
  seti $processedsectors = $processedsectors + $blocksize
  # check if last read is less than blocksize
  seti $partial = $currentposition + $blocksize
  if $partial > $endsector
    if $currentposition < $endsector
      seti $blocksize = $endsector - $currentposition
      seti $buffersize = $blocksize * 512
      buffersize $buffersize
    endif
  endif
done
if $devicefault > 0
  previousscript
endif



gosub displayoutput



previousscript
end



subroutine displayoutput
  upline
  upline
  upline
  upline
  upline
  upline
  echo "                                                  "
  gettime
  seti $totaltime = $time - $programstart
  seti $totalseconds = $totaltime / 1000000
  seti $remainder = $totaltime % 1000000
  seti $totalminutes = $totalseconds / 60
  seti $seconds = $totalseconds % 60
  seti $hours = $totalminutes / 60
  seti $minutes = $totalminutes % 60
  echo "elapsed time h:m:s = " $hours ":" $minutes ":" $seconds "." $remainder
  seti $positionpercent = $processedsectors * 100
  seti $percentcomplete = $positionpercent / $totalsectors
  seti $remainder = $positionpercent % $totalsectors
  seti $remainder = $remainder * 100
  seti $percentremainder = $remainder / $totalsectors
  if $percentremainder < 10
    echo $percentcomplete ".0" $percentremainder "%  complete    "
  else
    echo $percentcomplete "." $percentremainder "%  complete    "
  endif
  echo "position= " $currentposition " of " $endsector "                          "
  echo "speed= " $currentspeed " kB/s                           "

  echo "errors=" $errorcount "                           "

  setscratchpad 0
    0: $currentposition
    8: $errorcount
  endscratchpad
  seti $data_size = $errorcount * 8
  seti $data_size = $data_size + 256
  writescratchpad $resume_file 0 0 $data_size
endsubroutine






