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}"
delete_file(file_system_image,full_filename) unless file_system_image.files[full_filename].nil?
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
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
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