2017-02-25 06:05:41 +00:00
__version__ = " 4.0.0 "
2016-04-13 00:13:33 +00:00
import logging
2016-02-13 04:36:33 +00:00
try :
import numpy as np
except ImportError :
raise RuntimeError ( " atrcopy %s requires numpy " % __version__ )
from errors import *
2017-02-23 23:34:56 +00:00
from ataridos import AtrHeader , AtariDosDiskImage , BootDiskImage , AtariDosFile , get_xex , add_atr_header
2017-02-22 20:11:28 +00:00
from dos33 import Dos33DiskImage
2016-05-06 00:44:20 +00:00
from kboot import KBootImage , add_xexboot_header
2017-02-17 08:06:27 +00:00
from segments import SegmentData , SegmentSaver , DefaultSegment , EmptySegment , ObjSegment , RawSectorsSegment , user_bit_mask , match_bit_mask , comment_bit_mask , data_style , selected_bit_mask , diff_bit_mask , not_user_bit_mask , interleave_segments
2016-02-13 04:36:33 +00:00
from spartados import SpartaDosDiskImage
2016-06-03 20:05:16 +00:00
from cartridge import A8CartHeader , AtariCartImage
2016-06-03 19:40:48 +00:00
from parsers import SegmentParser , DefaultSegmentParser , guess_parser_for_mime , guess_parser_for_system , iter_parsers , iter_known_segment_parsers , mime_parse_order
2016-02-13 04:36:33 +00:00
from utils import to_numpy
2016-02-13 05:36:08 +00:00
2016-02-13 04:36:33 +00:00
def process ( image , dirent , options ) :
skip = False
action = " copying to "
filename = dirent . get_filename ( )
outfilename = filename
if options . no_sys :
if dirent . ext == " SYS " :
skip = True
action = " skipping system file "
if not skip :
if options . xex :
outfilename = " %s %s .XEX " % ( dirent . filename , dirent . ext )
if options . lower :
outfilename = outfilename . lower ( )
if options . dry_run :
action = " DRY_RUN: %s " % action
skip = True
if options . extract :
print " %s : %s %s " % ( dirent , action , outfilename )
if not skip :
bytes = image . get_file ( dirent )
with open ( outfilename , " wb " ) as fh :
fh . write ( bytes )
else :
print dirent
2017-02-24 19:57:51 +00:00
def find_diskimage ( filename ) :
with open ( filename , " rb " ) as fh :
if options . verbose :
print " Loading file %s " % filename
rawdata = SegmentData ( fh . read ( ) )
parser = None
for mime in mime_parse_order :
if options . verbose :
print " Trying MIME type %s " % mime
parser = guess_parser_for_mime ( mime , rawdata , options . verbose )
if parser is None :
continue
if options . verbose :
print " Found parser %s " % parser . menu_name
print " %s : %s " % ( filename , parser . image )
break
if parser is None :
print " %s : Unknown disk image type " % filename
parser . image . filename = filename
parser . image . ext = " "
return parser
def extract_all ( image ) :
if image . files or options . force :
for dirent in image . files :
try :
process ( image , dirent )
except FileNumberMismatchError164 :
print " Error 164: %s " % str ( dirent )
except ByteNotInFile166 :
print " Invalid sector for: %s " % str ( dirent )
def extract_files ( image , files ) :
for name in files :
try :
dirent = image . find_dirent ( name )
except FileNotFound :
print " %s not in %s " % ( name , image )
continue
print " extracting %s " % name
if not options . dry_run :
data = image . get_file ( dirent )
with open ( name , " wb " ) as fh :
fh . write ( data )
def add_files ( image , files ) :
filetype = options . filetype
if not filetype :
filetype = image . default_filetype
changed = False
for name in files :
try :
dirent = image . find_dirent ( name )
if options . force :
image . delete_file ( name )
else :
print " skipping %s , use -f to overwrite " % ( name )
continue
except FileNotFound :
pass
print " copying %s to %s " % ( name , image )
if not options . dry_run :
with open ( name , " rb " ) as fh :
data = fh . read ( )
image . write_file ( name , filetype , data )
changed = True
if changed :
image . save ( )
def remove_files ( image , files ) :
changed = False
for name in files :
try :
dirent = image . find_dirent ( name )
except FileNotFound :
print " %s not in %s " % ( name , image )
continue
print " removing %s from %s " % ( name , image )
if not options . dry_run :
image . delete_file ( name )
changed = True
if changed :
image . save ( )
def list_files ( image , files ) :
files = set ( files )
for dirent in image . files :
if not files or dirent . filename in files :
print dirent
2016-02-13 04:36:33 +00:00
def run ( ) :
import sys
import argparse
2017-02-24 19:57:51 +00:00
global options
2016-02-13 04:36:33 +00:00
2017-02-24 19:57:51 +00:00
parser = argparse . ArgumentParser ( description = " Manipulate files on several types of 8-bit computer disk images " )
2016-02-13 04:36:33 +00:00
parser . add_argument ( " -v " , " --verbose " , default = 0 , action = " count " )
parser . add_argument ( " -d " , " --debug " , action = " store_true " , default = False , help = " debug the currently under-development parser " )
parser . add_argument ( " -l " , " --lower " , action = " store_true " , default = False , help = " convert filenames to lower case " )
parser . add_argument ( " --dry-run " , action = " store_true " , default = False , help = " don ' t extract, just show what would have been extracted " )
parser . add_argument ( " -n " , " --no-sys " , action = " store_true " , default = False , help = " only extract things that look like games (no DOS or .SYS files) " )
2017-02-24 19:57:51 +00:00
parser . add_argument ( " --all " , action = " store_true " , default = False , help = " extract all files " )
2016-02-13 04:36:33 +00:00
parser . add_argument ( " --xex " , action = " store_true " , default = False , help = " add .xex extension " )
2017-02-24 19:57:51 +00:00
parser . add_argument ( " -f " , " --force " , action = " store_true " , default = False , help = " force operation, allowing file overwrites and operation on non-standard disk images " )
parser . add_argument ( " files " , metavar = " IMAGE " , nargs = " + " , help = " a disk image file [or a list of them] " )
2016-02-13 04:36:33 +00:00
parser . add_argument ( " -s " , " --segments " , action = " store_true " , default = False , help = " display segments " )
2017-02-24 19:57:51 +00:00
parser . add_argument ( " -x " , " -e " , " --extract " , action = " store_true " , default = False , help = " extract named files " )
parser . add_argument ( " -a " , " --add " , action = " store_true " , default = False , help = " add files to image " )
parser . add_argument ( " -r " , " --remove " , action = " store_true " , default = False , help = " remove named files from image " )
parser . add_argument ( " -t " , " --filetype " , action = " store " , default = " " , help = " file type metadata for writing to disk images that require it " )
2016-02-13 04:36:33 +00:00
options , extra_args = parser . parse_known_args ( )
2016-04-13 00:13:33 +00:00
# Turn off debug messages by default
log = logging . getLogger ( " atrcopy " )
if options . verbose :
log . setLevel ( logging . DEBUG )
else :
log . setLevel ( logging . INFO )
2017-02-24 19:57:51 +00:00
file_list = [ ]
if options . add or options . extract or options . remove :
image = options . files . pop ( )
file_list = options . files
options . files = [ image ]
2016-02-13 04:36:33 +00:00
for filename in options . files :
2017-02-24 19:57:51 +00:00
parser = find_diskimage ( filename )
if parser and parser . image :
if options . all :
extract_all ( parser . image )
elif options . segments :
print " \n " . join ( [ str ( a ) for a in parser . segments ] )
elif options . add :
add_files ( parser . image , file_list )
elif options . extract :
extract_files ( parser . image , file_list )
elif options . remove :
remove_files ( parser . image , file_list )
else :
list_files ( parser . image , file_list )