| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 | # greaseweazle/image/kryoflux.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 struct, re, math, osimport itertools as itfrom greaseweazle import errorfrom greaseweazle.flux import Fluxfrom .image import Imagemck = 18432000 * 73 / 14 / 2sck = mck / 2class Op:    Nop1  =  8    Nop2  =  9    Nop3  = 10    Ovl16 = 11    Flux3 = 12    OOB   = 13class OOB:    StreamInfo =  1    Index      =  2    StreamEnd  =  3    KFInfo     =  4    EOF        = 13class KryoFlux(Image):    def __init__(self, name):        if os.path.isdir(name):            self.basename = os.path.join(name, '')        else:            m = re.search("(\d{2}.[01])?.raw$", name)            self.basename = name[:m.start()]    @classmethod    def to_file(cls, name):        return cls(name)    @classmethod    def from_file(cls, name):        return cls(name)    def get_track(self, cyl, side):        name = self.basename + '%02d.%d.raw' % (cyl, side)        try:            with open(name, 'rb') as f:                dat = f.read()        except FileNotFoundError:            return None        # Parse the index-pulse stream positions.        index = []        idx = 0        while idx < len(dat):            op = dat[idx]            if op == Op.OOB:                oob_op, oob_sz = struct.unpack('<BH', dat[idx+1:idx+4])                idx += 4                if oob_op == OOB.Index:                    pos, = struct.unpack('<I', dat[idx:idx+4])                    index.append(pos)                elif oob_op == OOB.EOF:                    break                idx += oob_sz            elif op == Op.Nop3 or op == Op.Flux3:                idx += 3            elif op <= 7 or op == Op.Nop2:                idx += 2            else:                idx += 1        # Build the flux and index lists for the Flux object.        flux, flux_list, index_list = [], [], []        val, index_idx, stream_idx, idx = 0, 0, 0, 0        while idx < len(dat):            if index_idx < len(index) and stream_idx >= index[index_idx]:                # We've passed an index marker.                index_list.append(sum(flux))                flux_list += flux                flux = []                index_idx += 1            op = dat[idx]            if op <= 7:                # Flux2                val += (op << 8) + dat[idx+1]                flux.append(val)                val = 0                stream_idx += 2                idx += 2            elif op <= 10:                # Nop1, Nop2, Nop3                nr = op-7                stream_idx += nr                idx += nr            elif op == Op.Ovl16:                # Ovl16                val += 0x10000                stream_idx += 1                idx += 1            elif op == Op.Flux3:                # Flux3                val += (dat[idx+1] << 8) + dat[idx+2]                flux.append(val)                val = 0                stream_idx += 3                idx += 3            elif op == Op.OOB:                # OOB                oob_op, oob_sz = struct.unpack('<BH', dat[idx+1:idx+4])                idx += 4                if oob_op == OOB.StreamInfo or oob_op == OOB.StreamEnd:                    pos, = struct.unpack('<I', dat[idx:idx+4])                    error.check(pos == stream_idx,                                "Out-of-sync during KryoFlux stream read")                elif oob_op == OOB.EOF:                    break                idx += oob_sz            else:                # Flux1                val += op                flux.append(val)                val = 0                stream_idx += 1                idx += 1        flux_list += flux        # Crop partial first revolution.        if len(index_list) > 1:            short_index, index_list = index_list[0], index_list[1:]            flux = 0            for i in range(len(flux_list)):                if flux >= short_index:                    break                flux += flux_list[i]            flux_list = flux_list[i:]        return Flux(index_list, flux_list, sck)    def emit_track(self, cyl, side, track):        """Converts @track into a KryoFlux stream file."""        # Check if we should insert an OOB record for the next index mark.        def check_index(prev_flux):            nonlocal index_idx, dat            if index_idx < len(index) and total >= index[index_idx]:                dat += struct.pack('<2BH3I', Op.OOB, OOB.Index, 12,                                   stream_idx,                                   round(index[index_idx] - total + prev_flux),                                   round(index[index_idx]/8))                index_idx += 1        # Emit a resampled flux value to the KryoFlux data stream.        def emit(f):            nonlocal stream_idx, dat, total            while f >= 0x10000:                stream_idx += 1                dat.append(Op.Ovl16)                f -= 0x10000                total += 0x10000                check_index(0x10000)            if f >= 0x800:                stream_idx += 3                dat += struct.pack('>BH', Op.Flux3, f)            elif Op.OOB < f < 0x100:                stream_idx += 1                dat.append(f)            else:                stream_idx += 2                dat += struct.pack('>H', f)            total += f            check_index(f)        flux = track.flux()        # HxC crashes or fails to load non-index-cued stream files.        # So let's give it what it wants.        flux.cue_at_index()        factor = sck / flux.sample_freq        dat = bytearray()        # Start the data stream with a dummy index if our Flux is index cued.        if flux.index_cued:            dat += struct.pack('<2BH3I', Op.OOB, OOB.Index, 12, 0, 0, 0)        # Prefix-sum list of resampled index timings.        index = list(it.accumulate(map(lambda x: x*factor, flux.index_list)))        index_idx = 0                stream_idx, total, rem = 0, 0, 0.0        for x in flux.list:            y = x * factor + rem            f = round(y)            rem = y - f            emit(f)        # We may not have enough flux to get to the final index value.        # Generate a dummy flux just enough to get us there.        if index_idx < len(index):            emit(math.ceil(index[index_idx] - total) + 1)        # A dummy cell so that we definitely have *something* after the        # final OOB.Index, so that all parsers should register the Index.        emit(round(sck*12e-6)) # 12us        # Emit StreamEnd and EOF blocks to terminate the stream.        dat += struct.pack('<2BH2I', Op.OOB, OOB.StreamEnd, 8, stream_idx, 0)        dat += struct.pack('<2BH', Op.OOB, OOB.EOF, 0x0d0d)        name = self.basename + '%02d.%d.raw' % (cyl, side)        with open(name, 'wb') as f:                f.write(dat)    def __enter__(self):        return self    def __exit__(self, type, value, tb):        pass# Local variables:# python-indent: 4# End:
 |