123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- # greaseweazle/image/ipf.py
- #
- # Written & released by Keir Fraser <keir.xen@gmail.com>
- #
- # This is free and unencumbered software released into the public domain.
- # See the file COPYING for more details, or visit <http://unlicense.org>.
- import os, sys
- import platform
- import ctypes as ct
- from bitarray import bitarray
- from greaseweazle.track import MasterTrack
- from greaseweazle import error
- from .image import Image
- class CapsDateTimeExt(ct.Structure):
- _pack_ = 1
- _fields_ = [
- ('year', ct.c_uint),
- ('month', ct.c_uint),
- ('day', ct.c_uint),
- ('hour', ct.c_uint),
- ('min', ct.c_uint),
- ('sec', ct.c_uint),
- ('tick', ct.c_uint)]
- class CapsImageInfo(ct.Structure):
- platform_name = [
- "N/A", "Amiga", "Atari ST", "IBM PC", "Amstrad CPC",
- "Spectrum", "Sam Coupe", "Archimedes", "C64", "Atari (8-bit)" ]
- _pack_ = 1
- _fields_ = [
- ('type', ct.c_uint), # image type
- ('release', ct.c_uint), # release ID
- ('revision', ct.c_uint), # release revision ID
- ('mincylinder', ct.c_uint), # lowest cylinder number
- ('maxcylinder', ct.c_uint), # highest cylinder number
- ('minhead', ct.c_uint), # lowest head number
- ('maxhead', ct.c_uint), # highest head number
- ('crdt', CapsDateTimeExt), # image creation date.time
- ('platform', ct.c_uint * 4)] # intended platform(s)
- class CapsTrackInfoT2(ct.Structure):
- _pack_ = 1
- _fields_ = [
- ('type', ct.c_uint), # track type
- ('cylinder', ct.c_uint), # cylinder#
- ('head', ct.c_uint), # head#
- ('sectorcnt', ct.c_uint), # available sectors
- ('sectorsize', ct.c_uint), # sector size, unused
- ('trackbuf', ct.POINTER(ct.c_ubyte)), # track buffer memory
- ('tracklen', ct.c_uint), # track buffer memory length
- ('timelen', ct.c_uint), # timing buffer length
- ('timebuf', ct.POINTER(ct.c_uint)), # timing buffer
- ('overlap', ct.c_int), # overlap position
- ('startbit', ct.c_uint), # start position of the decoding
- ('wseed', ct.c_uint), # weak bit generator data
- ('weakcnt', ct.c_uint)] # number of weak data areas
- class CapsSectorInfo(ct.Structure):
- _pack_ = 1
- _fields_ = [
- ('descdatasize', ct.c_uint), # data size in bits from IPF descriptor
- ('descgapsize', ct.c_uint), # gap size in bits from IPF descriptor
- ('datasize', ct.c_uint), # data size in bits from decoder
- ('gapsize', ct.c_uint), # gap size in bits from decoder
- ('datastart', ct.c_uint), # data start pos in bits from decoder
- ('gapstart', ct.c_uint), # gap start pos in bits from decoder
- ('gapsizews0', ct.c_uint), # gap size before write splice
- ('gapsizews1', ct.c_uint), # gap size after write splice
- ('gapws0mode', ct.c_uint), # gap size mode before write splice
- ('gapws1mode', ct.c_uint), # gap size mode after write splice
- ('celltype', ct.c_uint), # bitcell type
- ('enctype', ct.c_uint)] # encoder type
- class CapsDataInfo(ct.Structure):
- _pack_ = 1
- _fields_ = [
- ('type', ct.c_uint), # data type
- ('start', ct.c_uint), # start position
- ('size', ct.c_uint)] # size in bits
- class DI_LOCK:
- INDEX = 1<<0
- ALIGN = 1<<1
- DENVAR = 1<<2
- DENAUTO = 1<<3
- DENNOISE = 1<<4
- NOISE = 1<<5
- NOISEREV = 1<<6
- MEMREF = 1<<7
- UPDATEFD = 1<<8
- TYPE = 1<<9
- DENALT = 1<<10
- OVLBIT = 1<<11
- TRKBIT = 1<<12
- NOUPDATE = 1<<13
- SETWSEED = 1<<14
- def_flags = (DENVAR | UPDATEFD | TYPE | OVLBIT | TRKBIT)
- class IPF(Image):
- read_only = True
- def __init__(self):
- self.lib = get_libcaps()
- def __del__(self):
- try:
- self.lib.CAPSUnlockAllTracks(self.iid)
- self.lib.CAPSUnlockImage(self.iid)
- self.lib.CAPSRemImage(self.iid)
- del(self.iid)
- except AttributeError:
- pass
- def __str__(self):
- pi = self.pi
- s = "IPF Image File:"
- s += "\n SPS ID: %04d (rev %d)" % (pi.release, pi.revision)
- s += "\n Platform: "
- nr_platforms = 0
- for p in pi.platform:
- if p == 0 and nr_platforms != 0:
- break
- if nr_platforms > 0:
- s += ", "
- s += pi.platform_name[p]
- nr_platforms += 1
- s += ("\n Created: %d/%d/%d %02d:%02d:%02d"
- % (pi.crdt.year, pi.crdt.month, pi.crdt.day,
- pi.crdt.hour, pi.crdt.min, pi.crdt.sec))
- s += ("\n Cyls: %d-%d Heads: %d-%d"
- % (pi.mincylinder, pi.maxcylinder, pi.minhead, pi.maxhead))
- return s
- @classmethod
- def from_file(cls, name):
- ipf = cls()
- ipf.iid = ipf.lib.CAPSAddImage()
- error.check(ipf.iid >= 0, "Could not create IPF image container")
- cname = ct.c_char_p(name.encode())
- res = ipf.lib.CAPSLockImage(ipf.iid, cname)
- error.check(res == 0, "Could not open IPF image '%s'" % name)
- res = ipf.lib.CAPSLoadImage(ipf.iid, DI_LOCK.def_flags)
- error.check(res == 0, "Could not load IPF image '%s'" % name)
- ipf.pi = CapsImageInfo()
- res = ipf.lib.CAPSGetImageInfo(ct.byref(ipf.pi), ipf.iid)
- error.check(res == 0, "Could not get info for IPF '%s'" % name)
- print(ipf)
- return ipf
- def get_track(self, cyl, head):
- pi = self.pi
- if head < pi.minhead or head > pi.maxhead:
- return None
- if cyl < pi.mincylinder or cyl > pi.maxcylinder:
- return None
- ti = CapsTrackInfoT2(2)
- res = self.lib.CAPSLockTrack(ct.byref(ti), self.iid,
- cyl, head, DI_LOCK.def_flags)
- error.check(res == 0, "Could not lock IPF track %d.%d" % (cyl, head))
- if not ti.trackbuf:
- return None # unformatted/empty
- carray_type = ct.c_ubyte * ((ti.tracklen+7)//8)
- carray = carray_type.from_address(
- ct.addressof(ti.trackbuf.contents))
- trackbuf = bitarray(endian='big')
- trackbuf.frombytes(bytes(carray))
- trackbuf = trackbuf[:ti.tracklen]
- data = []
- for i in range(ti.sectorcnt):
- si = CapsSectorInfo()
- res = self.lib.CAPSGetInfo(ct.byref(si), self.iid,
- cyl, head, 1, i)
- error.check(res == 0, "Couldn't get sector info")
- # Adjust the range start to be splice- rather than index-relative
- data.append(((si.datastart - ti.overlap) % ti.tracklen,
- si.datasize))
- weak = []
- for i in range(ti.weakcnt):
- wi = CapsDataInfo()
- res = self.lib.CAPSGetInfo(ct.byref(wi), self.iid,
- cyl, head, 2, i)
- error.check(res == 0, "Couldn't get weak data info")
- # Adjust the range start to be splice- rather than index-relative
- weak.append(((wi.start - ti.overlap) % ti.tracklen, wi.size))
- timebuf = None
- if ti.timebuf:
- carray_type = ct.c_uint * ti.timelen
- carray = carray_type.from_address(
- ct.addressof(ti.timebuf.contents))
- # Unpack the per-byte timing info into per-bitcell
- timebuf = []
- for i in carray:
- for j in range(8):
- timebuf.append(i)
- # Pad the timing info with normal cell lengths as necessary
- for j in range(len(carray)*8, ti.tracklen):
- timebuf.append(1000)
- # Clip the timing info, if necessary.
- timebuf = timebuf[:ti.tracklen]
- # Rotate the track to start at the splice rather than the index.
- if ti.overlap:
- trackbuf = trackbuf[ti.overlap:] + trackbuf[:ti.overlap]
- if timebuf:
- timebuf = timebuf[ti.overlap:] + timebuf[:ti.overlap]
-
- # We don't really have access to the bitrate. It depends on RPM.
- # So we assume a rotation rate of 300 RPM (5 rev/sec).
- rpm = 300
-
- track = MasterTrack(
- bits = trackbuf,
- time_per_rev = 60/rpm,
- bit_ticks = timebuf,
- splice = ti.overlap,
- weak = weak)
- return track
- # Open and initialise the CAPS library.
- def open_libcaps():
- # Get the OS-dependent list of valid CAPS library names.
- _names = []
- if platform.system() == "Linux":
- _names = [ "libcapsimage.so.5", "libcapsimage.so.5.1",
- "libcapsimage.so.4", "libcapsimage.so.4.2",
- "libcapsimage.so" ]
- elif platform.system() == "Darwin":
- _names = [ "CAPSImage.framework/CAPSImage" ]
- elif platform.system() == "Windows":
- _names = [ "CAPSImg_x64.dll", "CAPSImg.dll" ]
- # Get the absolute path to the root Greaseweazle folder.
- path = os.path.dirname(os.path.abspath(__file__))
- for _ in range(3):
- path = os.path.join(path, os.pardir)
- path = os.path.normpath(path)
- # Create a search list of both relative and absolute library names.
- names = []
- for name in _names:
- names.append(name)
- names.append(os.path.join(path, name))
- # Walk the search list, trying to open the CAPS library.
- for name in names:
- try:
- lib = ct.cdll.LoadLibrary(name)
- break
- except:
- pass
-
- error.check("lib" in locals(), """\
- Could not find SPS/CAPS IPF decode library
- For installation instructions please read the wiki:
- <https://github.com/keirf/Greaseweazle/wiki/IPF-Images>""")
-
- # We have opened the library. Now initialise it.
- res = lib.CAPSInit()
- error.check(res == 0, "Failure initialising IPF library '%s'" % name)
- return lib
- # Get a reference to the CAPS library. Open it if necessary.
- def get_libcaps():
- global libcaps
- if not 'libcaps' in globals():
- libcaps = open_libcaps()
- return libcaps
- # Local variables:
- # python-indent: 4
- # End:
|