seti $printhelp = $printhelp
seti $help = $help
if $help = 1
  echo 'Western Digital ROYL dump all modules using vendor specific commands.'
  echo 'This will dump the sectors of the modules to the files "modulexxxx.bin".'
  echo 'Note that some USB drives do not support these vendor specific commands.'
  if $printhelp = 1
    exit 0
  endif
  echo 'Hit ENTER to continue...'
  userinput $choicestring
  previousscript
endif

include good_subroutines

# set the filename to be written to
sets $filename = "mod01.bin"
# delete existing file so there is no confusion
deletefile $filename

# get model and serial using identify device command
echo "identify"
buffersize 512
setreadpio
ata28cmd 0 0 0 0 0 0xa0 0xec
# check if command failed
gosub status_check
wordflipbuffer 0 512

# extract model and serial and trim leading and trailing spaces
sets $model = "null"
sets $serial = "null"
seti $count = 0
seti $start_offset = 54
while $count < 40
  seti $byte = buffer $start_offset
  if $byte > 0x20
    break
  endif
  seti $start_offset = $start_offset + 1
  seti $count = $count + 1
done
seti $count = 0
seti $end_offset = 93
while $count < 40
  seti $byte = buffer $end_offset
  if $byte > 0x20
    break
  endif
  seti $end_offset = $end_offset - 1
  seti $count = $count + 1
done
seti $end_offset = $end_offset + 1
seti $size = $end_offset - $start_offset
if $size > 0
  sets $model = buffer $start_offset $size
endif
# find out if it is a WD drive
sets $wdcheck = buffer $start_offset 3
sets $compare = "WDC"
if $wdcheck != $compare
  echo "Model: " $model
  echo "Drive is not WD, exiting
  previousscript
endif

seti $count = 0
seti $start_offset = 20
while $count < 20
  seti $byte = buffer $start_offset
  if $byte > 0x20
    break
  endif
  seti $start_offset = $start_offset + 1
  seti $count = $count + 1
done
seti $count = 0
seti $end_offset = 39
while $count < 20
  seti $byte = buffer $end_offset
  if $byte > 0x20
    break
  endif
  seti $end_offset = $end_offset - 1
  seti $count = $count + 1
done
seti $end_offset = $end_offset + 1
seti $size = $end_offset - $start_offset
if $size > 0
  sets $serial = buffer $start_offset $size
endif

echo "Model: " $model
echo "Serial: " $serial

clearbuffer


# enable vendor specific commands
echo "enable vsc"
buffersize 0
setreadpio
ata28cmd 0x45 0x0b 0x00 0x44 0x57 0xa0 0x80
# check if command failed
gosub status_check

# vsc to prepare (command to set for module 01 access)
echo "prepare access"
buffersize 0x200
clearbuffer
setbuffer 0
  08 00 01 00 01
endbuffer
setwritepio
ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check
# send again
ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check

# vsc to get a sector of the module
echo "read sector"
setreadpio
clearbuffer
ata28cmd 0xd5 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
# check if command failed
gosub status_check

# show how many sectors the module contains
seti $mod_length_sectors = buffer 0xa
seti $tempnum = buffer 0xb
seti $tempnum = $tempnum > 8
seti $mod_length_sectors = $mod_length_sectors + $tempnum
echo "module length in sectors= " $mod_length_sectors
seti $remaining_sectors = $mod_length_sectors - 1

# copy the sector to the scratchpad
seti $scratchpad_size = $mod_length_sectors * 512
scratchpadsize $scratchpad_size
copybuffertoscratchpad 0 0 0x200

# vsc to get the remaining sectors
# this gets them one at a time
# there is a way to get them all at once,
# but there was a case where that did not work
echo "read sectors"
seti $count = 1
while $count < $mod_length_sectors
  buffersize 0x200
  clearbuffer
  ata28cmd 0xd5 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
  # check if command failed
  gosub status_check
  # copy the sectors to the scratchpad
  seti $offset = $count * 0x200
  copybuffertoscratchpad 0 $offset 0x200
  seti $count = $count + 1
done

# write the sectors to the file
writescratchpad $filename 0 0 $scratchpad_size
# show it on the screen
#printscratchpad 0 $scratchpad_size

# disable vendor specific commands
echo "disable vsc"
buffersize 0
setreadpio
ata28cmd 0x44 0x0b 0x00 0x44 0x57 0xa0 0x80
# check if command failed
gosub status_check


# process the module
echo "Header:"
printscratchpad 0 4
seti $mod_id = scratchpad 8 w
hex
echo "Module ID = 0x" $mod_id
echo "Size in sectors = 0x" $mod_length_sectors
seti $checksum = scratchpad 0xc dw
echo "32 bit checksum = 0x" $checksum
seti $total_records = scratchpad 0x30 w
echo "Total records = 0x" $total_records
decimal
sets $header = scratchpad 0 4
sets $header_check = "ROYL"
if $header != $header_check
  echo "Header is not 'ROYL', exiting"
  previousscript
endif

# calculate checksum
seti $calculated_checksum = 0
seti $count = 0
while $count < $scratchpad_size
  seti $dword = scratchpad $count dw
  seti $calculated_checksum = $calculated_checksum + $dword
  seti $count = $count + 4
  # skip the actual checksum bytes
  if $count = 0xc
    seti $count = $count + 4
  endif
done
seti $calculated_checksum = $calculated_checksum & 0xffffffff
seti $calculated_checksum = 0xffffffff - $calculated_checksum
seti $calculated_checksum = $calculated_checksum + 1
if $checksum = $calculated_checksum
  sets $message = " (good)"
else
  sets $message = " (BAD)"
endif
hex
echo "Calculated Checksum = 0x" $calculated_checksum $message
decimal
if $checksum != $calculated_checksum
  echo "Calculated checksum does not match actual, exiting"
  previousscript
endif

# process the data records
seti $record_count = 0
seti $offset = 0x32
echo ""
while $record_count < $total_records
  seti $record_count = $record_count + 1
  seti $record_length = scratchpad $offset b
  seti $next_record_offset = $offset + $record_length
  seti $offset = $offset + 2
  seti $module_id = scratchpad $offset w
  seti $offset = $offset + 2
  seti $module_size = scratchpad $offset w
  hex
  echo "Module ID = 0x" $module_id "  Size in sectors = 0x" $module_size
  decimal
  seti $offset = $next_record_offset
  gosub read_module
done




previousscript
end



subroutine status_check
  seti $command_failed = 0
  gosub check_command_status
  if $command_failed = 1
    echo "Command failed!"
    gosub show_sense_data
    gosub show_ata_return_status
    previousscript
  endif
endsubroutine



subroutine status_check_no_stop
  seti $command_failed = 0
  gosub check_command_status
  if $command_failed = 1
    echo "Command failed!"
    gosub show_sense_data
    gosub show_ata_return_status
  endif
endsubroutine




subroutine read_module
  # set the filename to be written to
  hex
  sets $filename = "module" $module_id ".bin"
  decimal
  # delete existing file so there is no confusion
  deletefile $filename

  # enable vendor specific commands
  echo "enable vsc"
  buffersize 0
  setreadpio
  ata28cmd 0x45 0x0b 0x00 0x44 0x57 0xa0 0x80
  # check if command failed
  gosub status_check

  # vsc to prepare to read module
  echo "prepare to read"
  buffersize 0x200
  clearbuffer
  seti $idlow = $module_id & 0xff
  seti $idhigh = $module_id > 8
  setbuffer 0
    08 00 01 00 $idlow $idhigh
  endbuffer
  setwritepio
  ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
  # check if command failed
  gosub status_check
  # send again
  ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
  # check if command failed
  gosub status_check

  # vsc to get the sectors of the module
  # this gets them one at a time
  # there is a way to get them all at once,
  # but there was a case where that did not work
  echo "read sectors"
  seti $count = 0
  setreadpio
  while $count < $module_size
    buffersize 0x200
    clearbuffer
    ata28cmd 0xd5 0x01 0xbf 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check_no_stop
    if $command_failed != 0
      break
    endif
    # write the sectors to the file
    seti $write_offset = $count * 0x200
    writebuffer $filename 0 $write_offset 0x200
    seti $count = $count + 1
  done

  # disable vendor specific commands
  echo "disable vsc"
  buffersize 0
  setreadpio
  ata28cmd 0x44 0x0b 0x00 0x44 0x57 0xa0 0x80
  # check if command failed
  gosub status_check

endsubroutine









