lib/file_systems/AppleCPM.rb in ripxplore-0.5.2 vs lib/file_systems/AppleCPM.rb in ripxplore-0.7.0

- old
+ new

@@ -34,11 +34,20 @@ s="" SECTORS_IN_BLOCK[block_no%4].each {|sector_no| s+=file_system_image.get_sector(track_no,sector_no)} s end + def self.set_block(file_system_image,block_no,contents) + raise "invalid block #{block_no} - length was #{contents.length}" unless contents.length==BLOCK_SIZE + track_no=(block_no/4)+3 + 0.upto(3) do |i| + sector_no=SECTORS_IN_BLOCK[block_no%4][i] + file_system_image.set_sector(track_no,sector_no,contents[(i*256),256]) + end + end + #look for a valid looking CPM directory on track 3. #go through each sector in order, for each sector, look at every 32nd byte, and see if it is a valid 'user number' (i.e. a number from 00..0F). Stop looking when you see an 'E5'. #if an invalid user number is found before an E5, then the disk is NOT a CPM disk is_valid_file_system_if lambda { is_valid=true @@ -92,9 +101,91 @@ files[full_filename].contents+=s[0,(records_allocated*128)] end end files end + +def self.find_free_space(file_system_image) + free_blocks=[] + free_directory_entries=[] + #first two blocks are where the catalog lives + (2..((file_system_image.track_count-3)*4)-1).each {|block| free_blocks<<block} + catalog=get_block(file_system_image,0)+get_block(file_system_image,1) + 0.upto(63) do |dir_entry_no| + dir_entry_start=dir_entry_no*0x20 + dir_entry=catalog[dir_entry_start..dir_entry_start+0x1F] + if (dir_entry[0]<0x10) then + 0x10.upto(0x1f) do |i| + block=dir_entry[i] + free_blocks.delete(block) + end + else + free_directory_entries<<dir_entry_no + end + end + [free_blocks,free_directory_entries] +end + + + def self.add_file(file_system_image,native_filetype_class,full_filename,file_contents,file_type=nil,aux_code=nil) + raise "{native_filetype_class} not supported on Apple CPM file system" if native_filetype_class.file_system_file_types[self].nil? + partial_filename,file_type=CPMFile.split_filename(full_filename) + full_filename ="#{partial_filename}.#{file_type}" +# puts "#{full_filename},#{partial_filename},#{file_type}" + delete_file(file_system_image,full_filename) unless file_system_image.files[full_filename].nil? #so we can overwrite the file if it already exists + free_blocks,free_directory_entries=find_free_space(file_system_image) + total_record_count=(file_contents.length/RECORD_SIZE.to_f).ceil + total_blocks_needed=(total_record_count/RECORDS_PER_BLOCK.to_f).ceil + total_extents_needed=(total_blocks_needed/BLOCKS_PER_EXTENT.to_f).ceil +# puts "#{total_extents_needed} extents needed" + raise "#{total_blocks_needed} free blocks required, only #{free_blocks.length} available" unless total_blocks_needed<=free_blocks.length + raise "#{total_extents_needed} free directory entries required, only #{free_directory_entries.length} available" unless total_extents_needed<=free_directory_entries.length + catalog=get_block(file_system_image,0)+get_block(file_system_image,1) + padded_file_contents=file_contents+(0x1A.chr*BLOCK_SIZE) + + total_extents_needed.times do |extent_no| + records_this_extent=(extent_no==(total_extents_needed-1) ? (total_record_count % RECORDS_PER_EXTENT):RECORDS_PER_EXTENT) + blocks_this_extent=(records_this_extent/RECORDS_PER_BLOCK.to_f).ceil + blocks_used=[0]*BLOCKS_PER_EXTENT + first_record_this_extent=extent_no*RECORDS_PER_EXTENT + contents_this_extent=padded_file_contents[(first_record_this_extent*RECORD_SIZE),blocks_this_extent*BLOCK_SIZE] + + blocks_this_extent.times do |block_no| + this_block=free_blocks[block_no+(extent_no*BLOCKS_PER_EXTENT)] + set_block(file_system_image,this_block,contents_this_extent[block_no*BLOCK_SIZE,BLOCK_SIZE]) + blocks_used[block_no]=this_block + end + dir_entry_no=free_directory_entries[extent_no] + dir_entry_start=dir_entry_no*0x20 + dir_entry=[0,partial_filename,file_type,extent_no,0,0,records_this_extent,blocks_used].flatten.pack("CA8A3C4C16") + catalog[dir_entry_start,0x20]=dir_entry +# puts "dir entry # #{dir_entry_no}" + end + + set_block(file_system_image,0,catalog[0,BLOCK_SIZE]) + set_block(file_system_image,1,catalog[BLOCK_SIZE,BLOCK_SIZE]) + + native_file=files(file_system_image)[full_filename] + raise "error: file should now be in catalog\n #{file_system_image.catalog}" if native_file.nil? + return native_file + end + + def self.delete_file(file_system_image,full_filename) + catalog=get_block(file_system_image,0)+get_block(file_system_image,1) + (partial_filename,file_ext)=CPMFile.split_filename(full_filename) + 0.upto(63) do |dir_entry_no| + dir_entry_start=dir_entry_no*0x20 + dir_entry=catalog[dir_entry_start..dir_entry_start+0x1F] + if (partial_filename==dir_entry[0x01..0x08].gsub(' ','')) && (file_ext==dir_entry[0x09..0x0B].gsub(' ','')) + #we found a matching filename, so set the 'user number' field to a 'blank' entry + catalog[dir_entry_start]=0xE5 + end + end + + set_block(file_system_image,0,catalog[0,BLOCK_SIZE]) + set_block(file_system_image,1,catalog[BLOCK_SIZE,BLOCK_SIZE]) + end + end # == Author # Jonno Downes (jonno@jamtronix.com)