lib/NADOLDisk.rb in dsktool-0.2.1 vs lib/NADOLDisk.rb in dsktool-0.4.1

- old
+ new

@@ -7,70 +7,227 @@ #each entry consists of $10 bytes, which are: # 00-0B - filename - if file is deleted, first byte will be FF # 0C-0D - filesize (low byte first) # 0E - track of track sector list sector # 0F - sector of track sector list sector - +# #TRACK/SECTOR LIST FORMAT # pairs of track/sectors in order. up to 128 entries [assumes that no file can be > 128 sectors] +# +#SECTOR USAGE BITMAP +#is at Track $00, Sector $2, from bytes $20 .. $65 +#bitmap is of form: +# +# ----hi bit-- +# | | +# 01234567 89ABCDEF +# ^^^^^^ ^^^^^^^ +# low byte hi byte require 'NADOLFile' class NADOLDisk < DSK - def dump_catalog - files.each_value { |file| - puts "#{sprintf('% 6d',file.contents.length)} #{file.filename}" - } + def dump_catalog + s="" + files.keys.sort.each { |file_name| + file=files[file_name] + s<< "#{sprintf('% 6d',file.contents.length)} #{file.filename}\n" + } + s end - def initialize(file_bytes) - super(file_bytes) + def initialize(file_bytes,sector_order) + super(file_bytes,sector_order) self.read_catalog + end + + def file_system + :nadol end - + + +def set_sector(track,sector,contents) + super(track,sector,contents) + + #now mark sector as used in sector usage list + #don't bother marking the 'system' sectors used + if ((track>0) || (sector>9)) then + sector_usage_bitmap_sector=get_sector(0,2) + offset_of_byte_containing_this_sector=0x20+(track*2)+(sector/8) + byte_containing_this_sector=sector_usage_bitmap_sector[offset_of_byte_containing_this_sector] + byte_containing_this_sector=byte_containing_this_sector|(2**(7-(sector%8))) + sector_usage_bitmap_sector[offset_of_byte_containing_this_sector]=byte_containing_this_sector + set_sector(0,2,sector_usage_bitmap_sector) + end +end + + +#iterate through the CATALOG to find either the named file or (if nil is passed in) an empty slot +def find_catalog_slot(filename) + track=0 + sector=3 + catalog_filename=NADOLFile.catalog_filename(filename.upcase) unless filename.nil? + while (sector<=9) do + sector_data=get_sector(track,sector) + (0..15).each do |slot_no| + slot_start=slot_no*0x10 + if (filename.nil? && (sector_data[slot_start]==0x00)|| (sector_data[slot_start]==0xFF)) then + return DSKTrackSector.new(track,sector,slot_start) + end + if (!filename.nil?) && (sector_data[slot_start..slot_start+0x0B]==catalog_filename) then + return DSKTrackSector.new(track,sector,slot_start) + end + end + sector+=1 + end + nil +end + +#iterate through the sector usage bitmap, return a list of [track,sector] for sectors marked available +def free_sector_list + sector_usage_bitmap=get_sector(0,2)[0x20..0x65] + free_sectors=[] + (0..(sector_usage_bitmap.length/2)-1).each do |track| + track_bitmap_lo=sector_usage_bitmap[track*2] + track_bitmap_hi=sector_usage_bitmap[1+track*2] + (0..7).each do |sector| + if ((track_bitmap_lo & (2**(7-sector)))==0) then + free_sectors<<DSKTrackSector.new(track,sector) + end + if ((track_bitmap_hi & (2**(7-sector)))==0) then + free_sectors<<DSKTrackSector.new(track,sector+8) + end + end + end + free_sectors.sort +end + +def make_file(filename,contents,file_options={}) + if (file_options[:tokenise]) then + return NADOLTokenisedFile.new(filename,NADOLTokenisedFile.tokenise(contents)) + else + return NADOLFile.new(filename,contents) + end +end + + def delete_file(filename) + this_files_catalog_slot=find_catalog_slot(filename) + #if file not in catalog, do nothing + return if this_files_catalog_slot.nil? + file_descriptive_entry=get_sector(this_files_catalog_slot.track_no,this_files_catalog_slot.sector_no)[this_files_catalog_slot.offset..this_files_catalog_slot.offset+0x0f] + + #mark sector as free in sector usage list + sector_usage_bitmap_sector=get_sector(0,2) + sectors_to_mark_available=get_track_sector_list(file_descriptive_entry[0x0E],file_descriptive_entry[0x0F]) + sectors_to_mark_available<<DSKTrackSector.new(file_descriptive_entry[0x0E],file_descriptive_entry[0x0F]) + sectors_to_mark_available.each do |ts| + offset_of_byte_containing_this_sector=0x20+(ts.track_no*2)+(ts.sector_no/8) + byte_containing_this_sector=sector_usage_bitmap_sector[offset_of_byte_containing_this_sector] + byte_containing_this_sector=byte_containing_this_sector&(0xff-(2**(7-(ts.sector_no%8)))) + sector_usage_bitmap_sector[offset_of_byte_containing_this_sector]=byte_containing_this_sector + end + set_sector(0,2,sector_usage_bitmap_sector) + + #mark slot as available in catalog + catalog_sector=get_sector(this_files_catalog_slot.track_no,this_files_catalog_slot.sector_no) + catalog_sector[this_files_catalog_slot.offset]=0xFF + set_sector(this_files_catalog_slot.track_no,this_files_catalog_slot.sector_no,catalog_sector) + end + + + #given a track and sector, treat it as a track/sector list and return an array containing track/sector pairs + def get_track_sector_list(ts_list_track_no,ts_list_sector_no) + ts_list_sector=get_sector(ts_list_track_no,ts_list_sector_no) + ts_list=[] + for entry_number in 0..0x7f + data_track_no=ts_list_sector[entry_number*2] + data_sector_no=ts_list_sector[entry_number*2+1] + if( (data_track_no!=0 || data_sector_no!=0) && data_track_no<track_count && data_sector_no<=0x0f) then + ts_list<<DSKTrackSector.new(data_track_no,data_sector_no) + end + end + ts_list + end + +#add a file to the in-memory image of this DSK + def add_file(file) + raise "only NADOLFiles may be added to NADOL format disks!" unless file.kind_of?(NADOLFile) + + delete_file(file.filename) unless files[file.filename].nil? + catalog_slot=find_catalog_slot(nil) + raise "CATALOG IS FULL!" if catalog_slot.nil? + + free_sectors=free_sector_list + + sectors_needed=1+file.length_in_sectors + raise "not enough free space - #{sectors_needed} sectors needed, #{free_sector_list.length} available " unless sectors_needed<=free_sectors.length + + #for each sector in the file, copy it to disk and then record it in the track/sector list + track_sector_list="\0"*256 + track_sector_list_sector=free_sectors[0] + (0..sectors_needed-2).each do |sector_in_file| + sector_to_use=free_sectors[sector_in_file+1] + track_sector_list[sector_in_file*2]=sector_to_use.track_no + track_sector_list[(sector_in_file*2)+1]=sector_to_use.sector_no + sector_contents=file.contents[(sector_in_file*256)..(sector_in_file*256)+255] || "" + set_sector(sector_to_use.track_no,sector_to_use.sector_no,sector_contents) + end + #write the track/sector list + set_sector(track_sector_list_sector.track_no,track_sector_list_sector.sector_no,track_sector_list) + + #update the catalog + catalog_sector=get_sector(catalog_slot.track_no,catalog_slot.sector_no) + catalog_sector[catalog_slot.offset..catalog_slot.offset+0x0B]=file.catalog_filename + catalog_sector[catalog_slot.offset+0x0C]=file.contents.length % 0x100 + catalog_sector[catalog_slot.offset+0x0D]=file.contents.length / 0x100 + catalog_sector[catalog_slot.offset+0x0E]=track_sector_list_sector.track_no + catalog_sector[catalog_slot.offset+0x0F]=track_sector_list_sector.sector_no + set_sector(catalog_slot.track_no,catalog_slot.sector_no,catalog_sector) + + raise "catalog not updated correctly!" if find_catalog_slot(file.filename).nil? + #reread the catalog to populate the files list + read_catalog + end + #reads the catalog, and populate the "files" array with files #CATALOG will be at track $00, sector $03 to track $00, sector $09 #each entry consists of $10 bytes, which are: # 00-0B - filename - if file is deleted, first byte will be FF # 0C-0D - filesize (low byte first) # 0E - track of track sector list sector # 0F - sector of track sector list sector def read_catalog - 0x300.step(0x9FF,0x10) do |file_descriptive_entry_start| - file_descriptive_entry=@file_bytes[file_descriptive_entry_start..file_descriptive_entry_start+0x0F] - break if (file_descriptive_entry[0]==0xFF) # skip deleted files - filename="" - file_descriptive_entry[0..11].to_s.each_byte{|b| filename+=(b.%128).chr} #strip off high bit - filename.sub!(/ *$/,"") #strip off trailing spaces - full_sectors=file_descriptive_entry[0x0D] - bytes_in_last_sector=file_descriptive_entry[0x0C] - file_size=full_sectors*256+bytes_in_last_sector - if (file_size>0) then - contents="" - ts_list_track_no=file_descriptive_entry[0x0E] - ts_list_sector_no=file_descriptive_entry[0x0F] - ts_list=get_sector(ts_list_track_no,ts_list_sector_no) - entry_number=0 - while entry_number<full_sectors do - data_track_no=ts_list[entry_number*2] - data_sector_no=ts_list[entry_number*2+1] - contents+=get_sector(data_track_no,data_sector_no) - entry_number+=1 - end - if (bytes_in_last_sector>0) then - data_track_no=ts_list[entry_number*2] - data_sector_no=ts_list[entry_number*2+1] - contents+=get_sector(data_track_no,data_sector_no)[0..bytes_in_last_sector-1] - end - if (NADOLTokenisedFile.can_be_nadol_tokenised_file?(contents)) then - @files[filename]= NADOLTokenisedFile.new(filename,contents) - else - @files[filename]= NADOLFile.new(filename,contents) - end - end - end - end + @files={} + track=0 + sector=3 + while (sector<=9) do + sector_data=get_sector(track,sector) + (0..15).each do |slot_no| + slot_offset=slot_no*0x10 + file_descriptive_entry=sector_data[slot_offset..slot_offset+0x10] + if (file_descriptive_entry[0]!=0xFF && file_descriptive_entry[0]!=0x00) then # skip deleted /empty files + filename="" + file_descriptive_entry[0..11].to_s.each_byte{|b| filename+=(b.%128).chr} #strip off high bit + filename.sub!(/ *$/,"") #strip off trailing spaces + file_size=file_descriptive_entry[0x0D]*256+file_descriptive_entry[0x0C] + if (file_size>0) then + contents="" + get_track_sector_list(file_descriptive_entry[0x0E],file_descriptive_entry[0x0F]).each do |ts| + contents<<get_sector(ts.track_no,ts.sector_no) + end + contents=contents[0..file_size-1] + if (NADOLTokenisedFile.can_be_nadol_tokenised_file?(contents)) then + @files[filename]= NADOLTokenisedFile.new(filename,contents) + else + @files[filename]= NADOLBinaryFile.new(filename,contents) + end + end + end + end + sector+=1 + end + end end # == Author # Jonno Downes (jonno@jamtronix.com) # # == Copyright