mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-23 12:56:51 +03:00
Ticket #2935: update uc1541 extfs plug-in up to version 2.5.
Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
parent
30dbb79655
commit
8a1971026d
@ -6,7 +6,58 @@ This extfs provides an access to disk image files for the Commodore
|
|||||||
VIC20/C64/C128. It requires the utility c1541 that comes bundled with Vice,
|
VIC20/C64/C128. It requires the utility c1541 that comes bundled with Vice,
|
||||||
the emulator for the VIC20, C64, C128 and other computers made by Commodore.
|
the emulator for the VIC20, C64, C128 and other computers made by Commodore.
|
||||||
|
|
||||||
|
Remarks
|
||||||
|
-------
|
||||||
|
|
||||||
|
Due to different way of representing file entries on regular D64 disk images,
|
||||||
|
there could be issues with filenames that are transfered from/to the image.
|
||||||
|
Following rules was applied to represent a single file entry:
|
||||||
|
|
||||||
|
1. An extension is attached to the end of a filename depending on a file type.
|
||||||
|
Possible extensions are: prg, del, seq, usr and rel.
|
||||||
|
2. Every non-ASCII character (which could be some of characters specific to
|
||||||
|
PET-ASCII, or be a control character) will be replaced by dot (.), since
|
||||||
|
c1541 program will list them that way.
|
||||||
|
3. Every slash character (/) will be replaced by pipe character (|).
|
||||||
|
4. Leading space will be replaced by tilda (~).
|
||||||
|
|
||||||
|
While copying from D64 image to filesystem, filenames will be stored as they
|
||||||
|
are seen on a listing.
|
||||||
|
|
||||||
|
While copying from filesystem to D64 image, filename conversion will be done:
|
||||||
|
1. Every $ and * characters will be replaced by question mark (?)
|
||||||
|
2. Every pipe (|) and backslash (\) characters will be replaced by slash (/)
|
||||||
|
3. Every tilda (~) will be replaced by a space
|
||||||
|
4. 'prg' extension will be truncated
|
||||||
|
|
||||||
|
Representation of a directory can be sometimes confusing - in case when one
|
||||||
|
copied file without extension it stays there in such form, till next access
|
||||||
|
(after flushing VFS). Also file sizes are not accurate, since D64 directory
|
||||||
|
entries have sizes stored as 256 bytes blocks.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Here are specific for this script variable, which while set, can influence
|
||||||
|
script behaviour:
|
||||||
|
|
||||||
|
UC1541_DEBUG - if set, uc1541 will produce log in /tmp/uc1541.log file
|
||||||
|
|
||||||
|
UC1541_VERBOSE - of set, script will be more verbose, i.e. error messages form
|
||||||
|
c1541 program will be passed to Midnight Commander, so that user will be aware
|
||||||
|
of error cause if any.
|
||||||
|
|
||||||
|
UC1541_HIDE_DEL - if set, no DEL entries will be shown
|
||||||
|
|
||||||
Changelog:
|
Changelog:
|
||||||
|
2.5 Fixed bug with filenames started with a '-' sign.
|
||||||
|
2.4 Fixed endless loop bug for reading directory in Python implemented
|
||||||
|
directory reader.
|
||||||
|
2.3 Re added and missing method _correct_fname used for writing files
|
||||||
|
into d64 image.
|
||||||
|
2.2 Fixed bug(?) with unusual sector end (marked as sector 0, not 255),
|
||||||
|
causing endless directory reading on random locations.
|
||||||
|
2.1 Fixed bug with filenames containing slash.
|
||||||
2.0 Added reading raw D64 image, and mapping for jokers. Now it is
|
2.0 Added reading raw D64 image, and mapping for jokers. Now it is
|
||||||
possible to read files with PET-ASCII/control sequences in filenames.
|
possible to read files with PET-ASCII/control sequences in filenames.
|
||||||
Working with d64 images only. Added workaround for space at the
|
Working with d64 images only. Added workaround for space at the
|
||||||
@ -20,8 +71,8 @@ Changelog:
|
|||||||
1.0 Initial release
|
1.0 Initial release
|
||||||
|
|
||||||
Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
|
Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
|
||||||
Date: 2012-09-02
|
Date: 2012-10-15
|
||||||
Version: 2.0
|
Version: 2.5
|
||||||
Licence: BSD
|
Licence: BSD
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -70,15 +121,15 @@ class D64(object):
|
|||||||
"""
|
"""
|
||||||
Implement d64 directory reader
|
Implement d64 directory reader
|
||||||
"""
|
"""
|
||||||
CHAR_MAP = {32: ' ', 33: '!', 34: '"', 35: '#', 36: '$', 37: '%', 38: '&',
|
CHAR_MAP = {32: ' ', 33: '!', 34: '"', 35: '#', 37: '%', 38: '&', 39: "'",
|
||||||
39: "'", 40: '(', 41: ')', 42: '*', 43: '+', 44: ',', 45: '-',
|
40: '(', 41: ')', 42: '*', 43: '+', 44: ',', 45: '-', 46: '.',
|
||||||
46: '.', 47: '/', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4',
|
47: '/', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5',
|
||||||
53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 60: '<',
|
54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 60: '<', 61: '=',
|
||||||
61: '=', 62: '>', 63: '?', 64: '@', 65: 'a', 66: 'b', 67: 'c',
|
62: '>', 63: '?', 64: '@', 65: 'a', 66: 'b', 67: 'c', 68: 'd',
|
||||||
68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j',
|
69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k',
|
||||||
75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q',
|
76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r',
|
||||||
82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x',
|
83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y',
|
||||||
89: 'y', 90: 'z', 91: '[', 93: ']', 97: 'A', 98: 'B', 99: 'C',
|
90: 'z', 91: '[', 93: ']', 97: 'A', 98: 'B', 99: 'C',
|
||||||
100: 'D', 101: 'E', 102: 'F', 103: 'G', 104: 'H', 105: 'I',
|
100: 'D', 101: 'E', 102: 'F', 103: 'G', 104: 'H', 105: 'I',
|
||||||
106: 'J', 107: 'K', 108: 'L', 109: 'M', 110: 'N', 111: 'O',
|
106: 'J', 107: 'K', 108: 'L', 109: 'M', 110: 'N', 111: 'O',
|
||||||
112: 'P', 113: 'Q', 114: 'R', 115: 'S', 116: 'T', 117: 'U',
|
112: 'P', 113: 'Q', 114: 'R', 115: 'S', 116: 'T', 117: 'U',
|
||||||
@ -105,10 +156,10 @@ class D64(object):
|
|||||||
dimage.close()
|
dimage.close()
|
||||||
|
|
||||||
self.current_sector_data = None
|
self.current_sector_data = None
|
||||||
self._sector_shift = 256
|
|
||||||
self.next_sector = 0
|
self.next_sector = 0
|
||||||
self.next_track = None
|
self.next_track = None
|
||||||
self._directory_contents = []
|
self._dir_contents = []
|
||||||
|
self._already_done = []
|
||||||
|
|
||||||
def _map_filename(self, string):
|
def _map_filename(self, string):
|
||||||
"""
|
"""
|
||||||
@ -117,16 +168,17 @@ class D64(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
filename = list()
|
filename = list()
|
||||||
in_fname = True
|
|
||||||
|
|
||||||
for chr_ in string:
|
for chr_ in string:
|
||||||
character = D64.CHAR_MAP.get(ord(chr_), '?')
|
if ord(chr_) == 160: # shift+space character; $a0
|
||||||
|
break
|
||||||
|
|
||||||
if in_fname:
|
character = D64.CHAR_MAP.get(ord(chr_), '?')
|
||||||
if ord(chr_) == 160:
|
filename.append(character)
|
||||||
in_fname = False
|
|
||||||
else:
|
# special cases
|
||||||
filename.append(character)
|
if filename[0] == "-":
|
||||||
|
filename[0] = "?"
|
||||||
|
|
||||||
LOG.debug("string: ``%s'' mapped to: ``%s''", string,
|
LOG.debug("string: ``%s'' mapped to: ``%s''", string,
|
||||||
"".join(filename))
|
"".join(filename))
|
||||||
@ -138,7 +190,11 @@ class D64(object):
|
|||||||
Return False if the chain ends, True otherwise
|
Return False if the chain ends, True otherwise
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.next_track == 0 and self.next_sector == 255:
|
# Well, self.next_sector _should_ have value $FF, but apparently there
|
||||||
|
# are the cases where it is not, therefore checking for that will not
|
||||||
|
# be performed and value of $00 on the next track will end the
|
||||||
|
# directory
|
||||||
|
if self.next_track == 0:
|
||||||
LOG.debug("End of directory")
|
LOG.debug("End of directory")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -150,10 +206,19 @@ class D64(object):
|
|||||||
LOG.debug("Going to the track: %s,%s", self.next_track,
|
LOG.debug("Going to the track: %s,%s", self.next_track,
|
||||||
self.next_sector)
|
self.next_sector)
|
||||||
|
|
||||||
self.current_sector_data = self.raw[offset:offset + self._sector_shift]
|
self.current_sector_data = self.raw[offset:offset + 256]
|
||||||
|
|
||||||
self.next_track = ord(self.current_sector_data[0])
|
self.next_track = ord(self.current_sector_data[0])
|
||||||
self.next_sector = ord(self.current_sector_data[1])
|
self.next_sector = ord(self.current_sector_data[1])
|
||||||
|
|
||||||
|
if (self.next_track, self.next_sector) in self._already_done:
|
||||||
|
# Just a failsafe. Endless loop is not what is expected.
|
||||||
|
LOG.debug("Loop in track/sector pointer at %d,%d",
|
||||||
|
self.next_track, self.next_sector)
|
||||||
|
self._already_done = []
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._already_done.append((self.next_track, self.next_sector))
|
||||||
LOG.debug("Next track: %s,%s", self.next_track, self.next_sector)
|
LOG.debug("Next track: %s,%s", self.next_track, self.next_sector)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -192,7 +257,7 @@ class D64(object):
|
|||||||
|
|
||||||
def _harvest_entries(self):
|
def _harvest_entries(self):
|
||||||
"""
|
"""
|
||||||
Traverse through sectors and store entries in _directory_contents
|
Traverse through sectors and store entries in _dir_contents
|
||||||
"""
|
"""
|
||||||
sector = self.current_sector_data
|
sector = self.current_sector_data
|
||||||
for x in range(8):
|
for x in range(8):
|
||||||
@ -212,10 +277,10 @@ class D64(object):
|
|||||||
else:
|
else:
|
||||||
size = ord(entry[30]) + ord(entry[31]) * 226
|
size = ord(entry[30]) + ord(entry[31]) * 226
|
||||||
|
|
||||||
self._directory_contents.append({'fname': self._map_filename(fname),
|
self._dir_contents.append({'fname': self._map_filename(fname),
|
||||||
'ftype': type_verbose,
|
'ftype': type_verbose,
|
||||||
'size': size,
|
'size': size,
|
||||||
'protect': protect})
|
'protect': protect})
|
||||||
sector = sector[32:]
|
sector = sector[32:]
|
||||||
|
|
||||||
def list_dir(self):
|
def list_dir(self):
|
||||||
@ -226,7 +291,7 @@ class D64(object):
|
|||||||
while self._go_to_next_sector():
|
while self._go_to_next_sector():
|
||||||
self._harvest_entries()
|
self._harvest_entries()
|
||||||
|
|
||||||
return self._directory_contents
|
return self._dir_contents
|
||||||
|
|
||||||
|
|
||||||
class Uc1541(object):
|
class Uc1541(object):
|
||||||
@ -249,7 +314,7 @@ class Uc1541(object):
|
|||||||
def list(self):
|
def list(self):
|
||||||
"""
|
"""
|
||||||
Output list contents of D64 image.
|
Output list contents of D64 image.
|
||||||
Convert filenames to be unix filesystem friendly
|
Convert filenames to be Unix filesystem friendly
|
||||||
Add suffix to show user what kind of file do he dealing with.
|
Add suffix to show user what kind of file do he dealing with.
|
||||||
"""
|
"""
|
||||||
LOG.info("List contents of %s", self.arch)
|
LOG.info("List contents of %s", self.arch)
|
||||||
@ -295,11 +360,11 @@ class Uc1541(object):
|
|||||||
def copyout(self, src, dst):
|
def copyout(self, src, dst):
|
||||||
"""
|
"""
|
||||||
Copy file form the D64 image. Source filename has to be corrected,
|
Copy file form the D64 image. Source filename has to be corrected,
|
||||||
since it's representaion differ from the real one inside D64 image.
|
since it's representation differ from the real one inside D64 image.
|
||||||
"""
|
"""
|
||||||
LOG.info("Copy form D64 %s as %s", src, dst)
|
LOG.info("Copy form D64 %s as %s", src, dst)
|
||||||
if not src.endswith(".prg"):
|
if not src.endswith(".prg"):
|
||||||
return "canot read"
|
return "cannot read"
|
||||||
|
|
||||||
src = self._get_masked_fname(src)
|
src = self._get_masked_fname(src)
|
||||||
|
|
||||||
@ -308,15 +373,38 @@ class Uc1541(object):
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def _correct_fname(self, fname):
|
||||||
|
"""
|
||||||
|
Return filename with mapped characters, without .prg extension.
|
||||||
|
Characters like $, *, + in filenames are perfectly legal, but c1541
|
||||||
|
program seem to have issues with it while writing, so it will also be
|
||||||
|
replaced.
|
||||||
|
"""
|
||||||
|
char_map = {'|': "/",
|
||||||
|
"\\": "/",
|
||||||
|
"~": " ",
|
||||||
|
"$": "?",
|
||||||
|
"*": "?"}
|
||||||
|
|
||||||
|
if fname.lower().endswith(".prg"):
|
||||||
|
fname = fname[:-4]
|
||||||
|
|
||||||
|
new_fname = []
|
||||||
|
for char in fname:
|
||||||
|
trans = char_map.get(char)
|
||||||
|
new_fname.append(trans if trans else char)
|
||||||
|
|
||||||
|
return "".join(new_fname)
|
||||||
|
|
||||||
def _get_masked_fname(self, fname):
|
def _get_masked_fname(self, fname):
|
||||||
"""
|
"""
|
||||||
Return masked filename with '?' jokers instead of non ASCII
|
Return masked filename with '?' jokers instead of non ASCII
|
||||||
characters, usefull for copying or deleting files with c1541. In case
|
characters, useful for copying or deleting files with c1541. In case
|
||||||
of several files with same name exists in directory, only first one
|
of several files with same name exists in directory, only first one
|
||||||
will be operative (first as appeared in directory).
|
will be operative (first as appeared in directory).
|
||||||
|
|
||||||
Warning! If there are two different names but the only differenc is in
|
Warning! If there are two different names but the only difference is in
|
||||||
non-ASCII characters (some PET ASCII or controll characters) there is
|
non-ASCII characters (some PET ASCII or control characters) there is
|
||||||
a risk that one can remove both files.
|
a risk that one can remove both files.
|
||||||
"""
|
"""
|
||||||
directory = self._get_dir()
|
directory = self._get_dir()
|
||||||
@ -348,12 +436,16 @@ class Uc1541(object):
|
|||||||
display_name = ".".join([fname, ext])
|
display_name = ".".join([fname, ext])
|
||||||
pattern_name = self.pyd64[idx]['fname']
|
pattern_name = self.pyd64[idx]['fname']
|
||||||
|
|
||||||
if '/' in fname:
|
if '/' in display_name:
|
||||||
display_name = fname.replace('/', '|')
|
display_name = display_name.replace('/', '|')
|
||||||
|
|
||||||
# workaround for space at the beggining of the filename
|
# workaround for space and dash at the beggining of the
|
||||||
if fname[0] == ' ':
|
# filename
|
||||||
display_name = '~' + display_name[1:]
|
char_map = {' ': '~',
|
||||||
|
'-': '_'}
|
||||||
|
display_name = "".join([char_map.get(display_name[0],
|
||||||
|
display_name[0]),
|
||||||
|
display_name[1:]])
|
||||||
|
|
||||||
if ext == 'del':
|
if ext == 'del':
|
||||||
perms = "----------"
|
perms = "----------"
|
||||||
|
Loading…
Reference in New Issue
Block a user