mirror of
https://github.com/gryf/uc1541.git
synced 2026-02-02 19:55:46 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 711695be2b | |||
| 197ba7a707 | |||
| 58d3fce340 | |||
| d4e3794c1c | |||
| 7c2e6f19a9 | |||
| fa81fc0b57 | |||
| 3822ad2531 |
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) 2015, Roman Dobosz
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the organization nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ROMAN DOBOSZ BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
123
README.rst
Normal file
123
README.rst
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
===================================================
|
||||||
|
Midnight Commander virtual filesystem for d64 files
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
This extfs provides an access to disk image files for the Commodore
|
||||||
|
VIC20/C64/C128 for Midnight Commander. It requires the utility c1541 that comes
|
||||||
|
bundled with Vice, the emulator for the VIC20, C64, C128 and other computers
|
||||||
|
made by Commodore.
|
||||||
|
|
||||||
|
Features
|
||||||
|
========
|
||||||
|
|
||||||
|
* view, remove, copy files in and out of the image
|
||||||
|
* support for read from and write to d64 images
|
||||||
|
* gzipped disk images support
|
||||||
|
* onfly characters remapping (see below)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Screens below shows how it looks like on real C64 machine and its representation
|
||||||
|
on Midnight Commander listing.
|
||||||
|
|
||||||
|
|image1| |image2|
|
||||||
|
|
||||||
|
In ``media`` directory you can find the ``test.d6z`` gzip compressed D64 image
|
||||||
|
file.
|
||||||
|
|
||||||
|
Rquirements
|
||||||
|
===========
|
||||||
|
|
||||||
|
* Python 2.7
|
||||||
|
* Vice installation (c1541 program in path)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
* copy ``uc1541`` to ``~/.local/share/mc/extfs.d/``
|
||||||
|
* add or change entry for files handle in ``~/.config/mc/mc.ext``::
|
||||||
|
|
||||||
|
# Disk images for Commodore computers (VIC20, C64, C128)
|
||||||
|
regex/\.(d64|D64|d6z|D6z|d6Z|D6Z)$
|
||||||
|
Open=%cd %p/uc1541://
|
||||||
|
View=%view{ascii} c1541 %f -list
|
||||||
|
Extract=c1541 %f -extract
|
||||||
|
|
||||||
|
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`` - if 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
|
||||||
|
=========
|
||||||
|
|
||||||
|
* **3.0** Added beta quality Python3 support
|
||||||
|
* **2.8** Treat non standard discs a bit better
|
||||||
|
* **2.7** Added support for gzipped disk images
|
||||||
|
* **2.6** Added mkdir and run handling (or rather lack of handling :). Minor
|
||||||
|
refactoring.
|
||||||
|
* **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
|
||||||
|
possible to read files with PET-ASCII/control sequences in filenames. Working
|
||||||
|
with d64 images only. Added workaround for space at the beginning of the
|
||||||
|
filename.
|
||||||
|
* **1.2** Added configuration env variables: ``UC1541_VERBOSE`` and
|
||||||
|
``UC1541_HIDE_DEL``. First one, if set to any value, will cause that error
|
||||||
|
messages from ``c1541`` program will be redirected as a failure messages
|
||||||
|
visible in MC.
|
||||||
|
The other variable, when set to any value, cause *del* entries to be not
|
||||||
|
shown in the lister.
|
||||||
|
* **1.1** Added protect bits, added failsafe for ``argparse`` module
|
||||||
|
* **1.0** Initial release
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
This software is licensed under 3-clause BSD license. See LICENSE file for
|
||||||
|
details.
|
||||||
|
|
||||||
|
.. |image1| image:: /media/vice_dir.png?raw=true
|
||||||
|
.. |image2| image:: /media/mc.png?raw=true
|
||||||
BIN
media/mc.png
Normal file
BIN
media/mc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
media/test.d6z
Normal file
BIN
media/test.d6z
Normal file
Binary file not shown.
BIN
media/vice_dir.png
Normal file
BIN
media/vice_dir.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
137
uc1541
137
uc1541
@@ -2,81 +2,12 @@
|
|||||||
"""
|
"""
|
||||||
UC1541 Virtual filesystem
|
UC1541 Virtual filesystem
|
||||||
|
|
||||||
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,
|
|
||||||
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 - if 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:
|
|
||||||
2.7 Added support for gzipped disk images
|
|
||||||
2.6 Added mkdir and run handling (or rather lack of handling :). Minor
|
|
||||||
refactoring.
|
|
||||||
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
|
|
||||||
possible to read files with PET-ASCII/control sequences in filenames.
|
|
||||||
Working with d64 images only. Added workaround for space at the
|
|
||||||
beggining of the filename.
|
|
||||||
1.2 Added configuration env variables: UC1541_VERBOSE and UC1541_HIDE_DEL.
|
|
||||||
First one, if set to any value, will cause that error messages from
|
|
||||||
c1541 program will be redirected as a failure messages visible in MC.
|
|
||||||
The other variable, when set to any value, cause "del" entries to be
|
|
||||||
not shown in the lister.
|
|
||||||
1.1 Added protect bits, added failsafe for argparse module
|
|
||||||
1.0 Initial release
|
|
||||||
|
|
||||||
Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
|
Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
|
||||||
Date: 2013-11-12
|
Date: 2018-02-13
|
||||||
Version: 2.7
|
Version: 3.0
|
||||||
Licence: BSD
|
Licence: BSD
|
||||||
|
source: https://bitbucket.org/gryf/uc1541
|
||||||
|
mirror: https://github.com/gryf/uc1541
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -121,6 +52,18 @@ else:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _ord(string_or_int):
|
||||||
|
"""
|
||||||
|
Return an int value for the (possible) string passed in argument. This
|
||||||
|
function is for compatibility between python2 and python3, where single
|
||||||
|
element in byte string array is a string or an int respectively.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return ord(string_or_int)
|
||||||
|
except TypeError:
|
||||||
|
return string_or_int
|
||||||
|
|
||||||
|
|
||||||
class D64(object):
|
class D64(object):
|
||||||
"""
|
"""
|
||||||
Implement d64 directory reader
|
Implement d64 directory reader
|
||||||
@@ -167,16 +110,16 @@ class D64(object):
|
|||||||
def _get_raw(self, dimage):
|
def _get_raw(self, dimage):
|
||||||
"""Try to get contents of the D64 image either it's gzip compressed or
|
"""Try to get contents of the D64 image either it's gzip compressed or
|
||||||
not."""
|
not."""
|
||||||
fobj = gzip.open(dimage)
|
fobj = gzip.open(dimage, 'rb')
|
||||||
# Although the common approach with gzipped files is to check the
|
# Although the common approach with gzipped files is to check the
|
||||||
# magic number, in this case there is no guarantee that first track
|
# magic number, in this case there is no guarantee that first track
|
||||||
# does not contain exactly the same byte sequence as the magic number.
|
# does not contain exactly the same byte sequence as the magic number.
|
||||||
# So the only way left is to actually try to uncompress the file.
|
# So the only way left is to actually try to uncompress the file.
|
||||||
try:
|
try:
|
||||||
self.raw = fobj.read()
|
self.raw = fobj.read()
|
||||||
except IOError:
|
except (IOError, OSError):
|
||||||
fobj.close()
|
fobj.close()
|
||||||
fobj = open(dimage)
|
fobj = open(dimage, 'rb')
|
||||||
self.raw = fobj.read()
|
self.raw = fobj.read()
|
||||||
fobj.close()
|
fobj.close()
|
||||||
|
|
||||||
@@ -189,10 +132,10 @@ class D64(object):
|
|||||||
filename = list()
|
filename = list()
|
||||||
|
|
||||||
for chr_ in string:
|
for chr_ in string:
|
||||||
if ord(chr_) == 160: # shift+space character; $a0
|
if _ord(chr_) == 160: # shift+space character; $a0
|
||||||
break
|
break
|
||||||
|
|
||||||
character = D64.CHAR_MAP.get(ord(chr_), '?')
|
character = D64.CHAR_MAP.get(_ord(chr_), '?')
|
||||||
filename.append(character)
|
filename.append(character)
|
||||||
|
|
||||||
# special cases
|
# special cases
|
||||||
@@ -227,8 +170,13 @@ class D64(object):
|
|||||||
|
|
||||||
self.current_sector_data = self.raw[offset:offset + 256]
|
self.current_sector_data = self.raw[offset:offset + 256]
|
||||||
|
|
||||||
self.next_track = ord(self.current_sector_data[0])
|
# Guard for reading data out of bound - that happened for discs which
|
||||||
self.next_sector = ord(self.current_sector_data[1])
|
# store only raw data, even on 18 track
|
||||||
|
if not self.current_sector_data:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.next_track = _ord(self.current_sector_data[0])
|
||||||
|
self.next_sector = _ord(self.current_sector_data[1])
|
||||||
|
|
||||||
if (self.next_track, self.next_sector) in self._already_done:
|
if (self.next_track, self.next_sector) in self._already_done:
|
||||||
# Just a failsafe. Endless loop is not what is expected.
|
# Just a failsafe. Endless loop is not what is expected.
|
||||||
@@ -281,7 +229,7 @@ class D64(object):
|
|||||||
sector = self.current_sector_data
|
sector = self.current_sector_data
|
||||||
for dummy in range(8):
|
for dummy in range(8):
|
||||||
entry = sector[:32]
|
entry = sector[:32]
|
||||||
ftype = ord(entry[2])
|
ftype = _ord(entry[2])
|
||||||
|
|
||||||
if ftype == 0: # deleted
|
if ftype == 0: # deleted
|
||||||
sector = sector[32:]
|
sector = sector[32:]
|
||||||
@@ -289,12 +237,12 @@ class D64(object):
|
|||||||
|
|
||||||
type_verbose = self._get_ftype(ftype)
|
type_verbose = self._get_ftype(ftype)
|
||||||
|
|
||||||
protect = ord(entry[2]) & 64 and "<" or " "
|
protect = _ord(entry[2]) & 64 and "<" or " "
|
||||||
fname = entry[5:21]
|
fname = entry[5:21]
|
||||||
if ftype == 'rel':
|
if ftype == 'rel':
|
||||||
size = ord(entry[23])
|
size = _ord(entry[23])
|
||||||
else:
|
else:
|
||||||
size = ord(entry[30]) + ord(entry[31]) * 226
|
size = _ord(entry[30]) + _ord(entry[31]) * 226
|
||||||
|
|
||||||
self._dir_contents.append({'fname': self._map_filename(fname),
|
self._dir_contents.append({'fname': self._map_filename(fname),
|
||||||
'ftype': type_verbose,
|
'ftype': type_verbose,
|
||||||
@@ -339,6 +287,11 @@ class Uc1541(object):
|
|||||||
LOG.info("List contents of %s", self.arch)
|
LOG.info("List contents of %s", self.arch)
|
||||||
directory = self._get_dir()
|
directory = self._get_dir()
|
||||||
|
|
||||||
|
# If there is an error reading directory, show the reason to the user
|
||||||
|
if self.out.startswith("Error"):
|
||||||
|
sys.stderr.write(self.out.split("\n")[0] + "\n")
|
||||||
|
return 2
|
||||||
|
|
||||||
for entry in directory:
|
for entry in directory:
|
||||||
sys.stdout.write("%(perms)s 1 %(uid)-8d %(gid)-8d %(size)8d "
|
sys.stdout.write("%(perms)s 1 %(uid)-8d %(gid)-8d %(size)8d "
|
||||||
"Jan 01 1980 %(display_name)s\n" % entry)
|
"Jan 01 1980 %(display_name)s\n" % entry)
|
||||||
@@ -508,14 +461,20 @@ class Uc1541(object):
|
|||||||
dir/list
|
dir/list
|
||||||
"""
|
"""
|
||||||
command = ['c1541', '-attach', self.arch, '-%s' % cmd]
|
command = ['c1541', '-attach', self.arch, '-%s' % cmd]
|
||||||
if src and dst:
|
if src:
|
||||||
command.append(src)
|
command.append(src)
|
||||||
|
if dst:
|
||||||
command.append(dst)
|
command.append(dst)
|
||||||
elif src or dst:
|
|
||||||
command.append(src and src or dst)
|
|
||||||
|
|
||||||
self.out, self.err = Popen(command, stdout=PIPE,
|
LOG.debug('executing command: %s', ' '.join(command))
|
||||||
stderr=PIPE).communicate()
|
# For some reason using write command and reading output confuses
|
||||||
|
# Python3 beneath MC and as a consequence MC report an error...
|
||||||
|
# therefore for write command let's not use universal_newlines...
|
||||||
|
self.out, self.err = Popen(command,
|
||||||
|
universal_newlines=(cmd != 'write'),
|
||||||
|
stdout=PIPE, stderr=PIPE).communicate()
|
||||||
|
|
||||||
|
LOG.debug('an err: %s', self.err)
|
||||||
return not self.err
|
return not self.err
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user