



seti $printhelp = $printhelp
seti $help = $help
if $help = 1
  echo 'Western Digital ROYL check ROM file.'
  echo 'This script performs some checks on a ROM file.'
  echo 'This requires the text variable "file" to be set.'
  echo '"file" is the name of the file containing the ROM.'
  echo 'This scipt does not perform any disk IO,'
  echo 'so it can be ran with any target, even /dev/zero.'

  if $printhelp = 1
    exit 0
  endif
  echo 'Hit ENTER to continue...'
  userinput $choicestring
  previousscript
endif

include good_subroutines

# set a blank string variable to compare with user input
# make sure not to set this anyplace else
sets $null = ""

# set this to itself for use with the menu system
# make sure not to set this anyplace else
# if it was not previously set it will = 0
seti $using_menu = $using_menu

# set the file name to be written
while 1 = 1
  variablecheck $file
  if $error_level < 16
    seti $ask = 1
  elseif $using_menu = 1
    seti $ask = 1
  endif
  if $ask = 1
    echo "Enter file name:"
    userinput $choicestring
    if $choicestring != $null
      sets $file = $choicestring
      break
    else
      echo "Choice cannot be blank!"
    endif
  else
    break
  endif
done

getfilesize $file
seti $file_size = $error_level
if $file_size < 0
  echo "ERROR! File " $file " not found!"
  previousscript
endif
echo "Reading the rom file"
seti $rom_size = $file_size
scratchpadsize $rom_size
clearscratchpad
readscratchpad $file 0 0 $rom_size

gosub process_rom
gosub process_rom

gosub save_patched_rom


previousscript
end




subroutine process_rom
  echo "processing rom"
  #printscratchpad 0 512
  seti $pmcblock_number = 0
  seti $error = 0
  seti $first_block = $rom_size
  while 1 = 1
    seti $table_offset = $pmcblock_number * 32
    seti $working_offset = $table_offset
    seti $block_number = scratchpad $table_offset
    if $pmcblock_number = 0
      if $block_number != 0x5a
        hex
        echo "ERROR: expecting first byte of 0x5a but found 0x" $block_number
        decimal
        seti $error = $error + 1
        break
      endif
    elseif $pmcblock_number != $block_number
      if $table_offset < $first_block
        echo "Warning, there is a gap between table end and first block start."
        echo "This could mean a bad table entry, or it could be normal."
      endif
      echo "finished processing blocks"
      break
    endif

    seti $working_offset = $table_offset + 4
    seti $checksum_location = scratchpad $working_offset dw
    seti $working_offset = $table_offset + 8
    seti $block_size = scratchpad $working_offset dw
    seti $checksum_length = $checksum_location - $block_size
    seti $working_offset = $table_offset + 0x0c
    seti $block_start = scratchpad $working_offset dw
    seti $block_end = $block_start + $checksum_location
    seti $working_offset = $table_offset + 0x1f
    seti $table_checksum = scratchpad $working_offset

    if $block_start < $first_block
      seti $first_block = $block_start
    endif

    seti $calulated_tablechk = 0
    seti $count = 0
    while $count < 31
      seti $position = $table_offset + $count
      seti $byte = scratchpad $position
      seti $calulated_tablechk = $calulated_tablechk + $byte
      seti $count = $count + 1
    done
    seti $calulated_tablechk = $calulated_tablechk & 0xff
    if $table_checksum != $calulated_tablechk
      echo "ERROR: bad table checksum"
    endif

    if $checksum_length = 1
      seti $position = $block_start + $block_size
      seti $checksum = scratchpad $position
      seti $calculated_checksum = 0
      seti $count = 0
      while $count < $block_size
        seti $position = $block_start + $count
        seti $byte = scratchpad $position
        seti $calculated_checksum = $calculated_checksum + $byte
        seti $count = $count + 1
      done
      seti $calculated_checksum = $calculated_checksum & 0xff
    elseif $checksum_length = 2
      seti $position = $block_start + $block_size
      seti $checksum = scratchpad $position w
      seti $calculated_checksum = 0
      seti $count = 0
      while $count < $block_size
        seti $position = $block_start + $count
        seti $word = scratchpad $position w
        seti $calculated_checksum = $calculated_checksum + $word
        seti $count = $count + 2
      done
      seti $calculated_checksum = $calculated_checksum & 0xffff
      #seti $calculated_checksum = 0xffff - $calculated_checksum
      #seti $calculated_checksum = $calculated_checksum + 1
    else
      echo "ERROR: bad checksum length of " $checksum_length
      seti $error = $error + 1
    endif

    hex
    if $checksum = $calculated_checksum
      sets $checkstring = " calulated=0x" $calculated_checksum " (good)"
    else
      sets $checkstring = " calulated=0x" $calculated_checksum " (BAD)"
      seti $error = $error + 1
    endif
    echo "block=0x" $pmcblock_number " start=0x" $block_start " size=0x" $block_size " checksum=0x" $checksum $checkstring
    decimal

    seti $pmcblock_number = $pmcblock_number + 1
  done
  if $error = 0
    echo "ROM blocks appear to be good."
  else
    echo "WARNING: There were errors with the ROM blocks."
  endif

  # search for modules and check them, starting from the end of the last rom entry
  echo "Searching for modules."
  sets $header_check = "ROYL"
  seti $position = $block_end
  seti $search_end = $rom_size - 4
  while $position < $search_end
    sets $header = scratchpad $position 4
    if $header = $header_check
      seti $working_offset = $position + 8
      seti $mod_id = scratchpad $working_offset w
      hex
      #echo "Module ID = 0x" $mod_id
      if $mod_id = 0xb
        gosub process_directory
      elseif $mod_id = 0x20b
        gosub process_directory
      endif
    endif
    seti $position = $position + 1
  done
endsubroutine





subroutine process_directory
  echo "Directory module 0x" $mod_id " found, processing"
  #printscratchpad $position 512
  seti $directory_id = $mod_id
  seti $directory_size = 0
  seti $directory_offset = $position + 0x1e
  seti $directory_total_count = scratchpad $directory_offset b
  seti $directory_count = 0
  seti $directory_offset = $position + 0x1f
  while $directory_count < $directory_total_count
    seti $entry_size = scratchpad $directory_offset b
    seti $working_offset = $directory_offset + 0x1
    seti $mod_copies = scratchpad $working_offset b
    seti $working_offset = $directory_offset + 0x2
    seti $mod_id = scratchpad $working_offset w
    seti $working_offset = $directory_offset + 0x4
    seti $mod_size = scratchpad $working_offset w
    if $entry_size = 0
      seti $entry_size = $last_entry_size
    else
      seti $last_entry_size = $entry_size
    endif
    if $mod_id = 1
      seti $mod_size = $mod_size * 512
    endif
    #printscratchpad $directory_offset $entry_size
    if $directory_id = $mod_id
      seti $directory_size = $mod_size
    endif
    seti $working_offset = $directory_offset + 0xa
    seti $mod_location = scratchpad $working_offset dw
    seti $working_offset = $working_offset + 4
    seti $mod_location1 = scratchpad $working_offset dw
    seti $working_offset = $working_offset + 4
    seti $mod_location2 = scratchpad $working_offset dw
    seti $working_offset = $working_offset + 4
    seti $mod_location3 = scratchpad $working_offset dw
    if $mod_id = 0
      hex
      echo "ModID=0x" $mod_id " size=0x" $mod_size " location=0x" $mod_location
      decimal
    elseif $mod_id = 1
      hex
      echo "ModID=0x" $mod_id " size=0x" $mod_size " copies=0x" $mod_copies " locations=0x" $mod_location " 0x" $mod_location1 " 0x" $mod_location2 " 0x" $mod_location3
      decimal
    else
      gosub process_module
    endif
    seti $directory_offset = $directory_offset + $entry_size
    seti $directory_count = $directory_count + 1
  done
  seti $check_byte = scratchpad $directory_offset b
  if $check_byte = 0x46
    seti $working_offset = $directory_offset + 1
    seti $total_regions = scratchpad $working_offset b
    seti $working_offset = $directory_offset + 4
    seti $region_bitmap = scratchpad $working_offset w
    if $directory_id = 0xb
      seti $modb_region_directory_location = $directory_offset
      hex
      echo "setting modb=0x" $working_offset
      decimal
      gosub patch_rom
    elseif $directory_id = 0x20b
      seti $mod20b_region_directory_location = $directory_offset
      hex
      echo "setting mod20b=0x" $working_offset
      decimal
      gosub patch_rom
    endif
    hex
    echo "SA regions=0x" $total_regions "  region bitmap=0x" $region_bitmap
    decimal
    seti $region_count = 0
    while $region_count < $total_regions
      seti $offset_adustment = $region_count * 8
      seti $working_offset = $directory_offset + $offset_adustment
      seti $working_offset = $working_offset + 6
      seti $region_size = scratchpad $working_offset dw
      seti $working_offset = $working_offset + 4
      seti $region_location = scratchpad $working_offset dw
      hex
      echo "Region=0x" $region_count " Location=0x" $region_location " Size=0x" $region_size
      decimal
      seti $region_count = $region_count + 1
    done
  else
    echo "SA regions not found"
    seti $remainder = $directory_offset - $position
    printscratchpad $directory_offset $remainder
  endif
endsubroutine





subroutine process_module
  # process the module
  seti $working_offset = $mod_location
  #echo "Header:"
  #printscratchpad $working_offset 4
  seti $working_offset = $mod_location + 8
  seti $mod_id = scratchpad $working_offset w
  hex
  #echo "Module ID = 0x" $mod_id

  # find how many sectors the module contains
  seti $working_offset = $mod_location + 0xa
  seti $mod_length_sectors = scratchpad $working_offset
  seti $working_offset = $mod_location + 0xb
  seti $tempnum = scratchpad $working_offset
  seti $tempnum = $tempnum > 8
  seti $mod_length_sectors = $mod_length_sectors + $tempnum

  #echo "Size in sectors = 0x" $mod_length_sectors
  seti $working_offset = $mod_location + 0xc
  seti $checksum = scratchpad $working_offset dw
  #echo "32 bit checksum = 0x" $checksum
  seti $working_offset = $mod_location + 0x10
  #echo "Mod version:"
  #printscratchpad $working_offset 8
  decimal

  seti $module_size = $mod_size
  seti $calculated_checksum = 0
  seti $count = 0
  while $count < $module_size
    seti $working_offset = $mod_location + $count
    if $working_offset > $search_end
      #echo "end of module is past end of rom"
      break
    endif
    seti $dword = scratchpad $working_offset dw
    seti $remainder = $module_size - $count
    if $remainder < 4
      if $remainder = 3
        seti $dword = $dword & 0xffffff
      elseif $remainder = 2
        seti $dword = $dword & 0xffff
      elseif $remainder = 1
        seti $dword = $dword & 0xff
      endif
    endif
    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

  hex
  if $checksum = $calculated_checksum
    sets $checkstring = " calulated=0x" $calculated_checksum " (good)"
  else
    sets $checkstring = " calulated=0x" $calculated_checksum " (bad)"
  endif
  echo "0x" $mod_location " ModID=0x" $mod_id " size=0x" $mod_size " checksum=0x" $checksum $checkstring
  decimal

endsubroutine





subroutine patch_rom
  echo "Patching rom"
  if $mod_id = 0xb
    hex
    echo "modb=0x" $modb_region_directory_location
    decimal
    seti $pdirectory_offset = $modb_region_directory_location
  elseif $mod_id = 0x20b
    hex
    echo "mod20b=0x" $mod20b_region_directory_location
    decimal
    seti $pdirectory_offset = $mod20b_region_directory_location
  endif


  seti $working_offset = $pdirectory_offset + 1
  seti $total_regions = scratchpad $working_offset b
  seti $working_offset = $pdirectory_offset + 4
  seti $region_bitmap = scratchpad $working_offset w
  hex
  echo "SA regions=0x" $total_regions "  region bitmap=0x" $region_bitmap
  decimal
  seti $region_count = 0
  while $region_count < $total_regions
    seti $offset_adustment = $region_count * 8
    seti $working_offset = $pdirectory_offset + $offset_adustment
    seti $working_offset = $working_offset + 6
    seti $region_size = scratchpad $working_offset dw
    seti $working_offset = $working_offset + 4
    seti $region_location = scratchpad $working_offset dw
    hex
    echo "Unpatched Region=0x" $region_count " Location=0x" $region_location " Size=0x" $region_size
    decimal


    seti $region_location = $region_location + 1
    seti $value_data_size = 4
    while $value_data_size > 0
      seti $value_data_size = $value_data_size - 1
      seti $tempmultiplyer = $value_data_size * 8
      seti $tempvalue = $region_location > $tempmultiplyer
      seti $tempvalue = $tempvalue & 0xff
      seti $tempoffset = $working_offset + $value_data_size
      setscratchpad $tempoffset
        $tempvalue
      endscratchpad
    done
    seti $region_location = scratchpad $working_offset dw
    hex
    echo "Patched Location=0x" $region_location
    decimal


    seti $region_count = $region_count + 1
  done


  # calculate new checksum and insert it
  seti $calculated_checksum = 0
  seti $count = 0
  while $count < $module_size
    seti $working_offset = $mod_location + $count
    if $working_offset > $search_end
      #echo "end of module is past end of rom"
      break
    endif
    seti $dword = scratchpad $working_offset dw
    seti $remainder = $module_size - $count
    if $remainder < 4
      if $remainder = 3
        seti $dword = $dword & 0xffffff
      elseif $remainder = 2
        seti $dword = $dword & 0xffff
      elseif $remainder = 1
        seti $dword = $dword & 0xff
      endif
    endif
    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
  hex
  echo "New Calculated Checksum = 0x" $calculated_checksum
  decimal
  seti $b1 = $calculated_checksum & 0xff
  seti $calculated_checksum = $calculated_checksum > 8
  seti $b2 = $calculated_checksum & 0xff
  seti $calculated_checksum = $calculated_checksum > 8
  seti $b3 = $calculated_checksum & 0xff
  seti $calculated_checksum = $calculated_checksum > 8
  seti $b4 = $calculated_checksum & 0xff
  seti $working_offset = $mod_location + 0x0c
  setscratchpad $working_offset
    $b1 $b2 $b3 $b4
  endscratchpad
endsubroutine





subroutine save_patched_rom
  sets $new_file = "patched_" $file
  writescratchpad $new_file 0 0 $rom_size
endsubroutine




