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 'opensuperclone --tool -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.osc 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