Преглед изворни кода

gw write: Respect write-splice extended info in SCP image files.
Refs #60

Keir Fraser пре 4 година
родитељ
комит
e2ef469634
2 измењених фајлова са 79 додато и 19 уклоњено
  1. 31 11
      scripts/greaseweazle/flux.py
  2. 48 8
      scripts/greaseweazle/image/scp.py

+ 31 - 11
scripts/greaseweazle/flux.py

@@ -7,6 +7,7 @@
 
 from greaseweazle import error
 
+
 class Flux:
 
     def __init__(self, index_list, flux_list, sample_freq):
@@ -15,6 +16,7 @@ class Flux:
         self.sample_freq = sample_freq
         self.splice = 0
 
+
     def __str__(self):
         s = "\nFlux: %.2f MHz" % (self.sample_freq*1e-6)
         s += ("\n Total: %u samples, %.2fms\n"
@@ -25,38 +27,54 @@ class Flux:
             rev += 1
         return s[:-1]
 
+
     def summary_string(self):
         return ("Raw Flux (%u flux in %.2fms)"
                 % (len(self.list), sum(self.list)*1000/self.sample_freq))
 
+
     def flux_for_writeout(self):
-        error.check(self.splice == 0,
-                    "Cannot write non-index-aligned raw flux")
-        # Copy the first revolution only to a fresh flux list.
+
+        error.check(self.splice == 0 or len(self.index_list) > 1,
+                    "Cannot write single-revolution unaligned raw flux")
+        splice_at_index = (self.splice == 0)
+
+        # Copy the required amount of flux to a fresh list.
         flux_list = []
-        to_index = remain = self.index_list[0]
+        to_index = self.index_list[0]
+        remain = to_index + self.splice
         for f in self.list:
             if f > remain:
                 break
             flux_list.append(f)
             remain -= f
-        # Extend with "safe" 4us sample values, to avoid unformatted area
-        # at end of track if drive motor is a little slow.
-        four_us = max(self.sample_freq * 4e-6, 1)
-        if remain > four_us:
+
+        if splice_at_index:
+            # Extend with "safe" 4us sample values, to avoid unformatted area
+            # at end of track if drive motor is a little slow.
+            four_us = max(self.sample_freq * 4e-6, 1)
+            if remain > four_us:
+                flux_list.append(remain)
+            for i in range(round(to_index/(10*four_us))):
+                flux_list.append(four_us)
+        elif remain > 0:
+            # End the write exactly where specified.
             flux_list.append(remain)
-        for i in range(round(to_index/(10*four_us))):
-            flux_list.append(four_us)
+
         return WriteoutFlux(to_index, flux_list, self.sample_freq,
-                            terminate_at_index = True)
+                            terminate_at_index = (self.splice == 0))
+
+
 
     def flux(self):
         return self
 
+
     def scale(self, factor):
         """Scale up all flux and index timings by specified factor."""
         self.sample_freq /= factor
 
+
     @property
     def mean_index_time(self):
         """Mean time between index pulses, in seconds (float)"""
@@ -70,6 +88,7 @@ class WriteoutFlux(Flux):
         super().__init__([ticks_to_index], flux_list, sample_freq)
         self.terminate_at_index = terminate_at_index
 
+
     def __str__(self):
         s = ("\nWriteoutFlux: %.2f MHz, %.2fms to index, %s\n"
              " Total: %u samples, %.2fms"
@@ -79,6 +98,7 @@ class WriteoutFlux(Flux):
                 len(self.list), sum(self.list)*1000/self.sample_freq))
         return s
 
+
     def flux_for_writeout(self):
         return self
  

+ 48 - 8
scripts/greaseweazle/image/scp.py

@@ -11,32 +11,48 @@ from greaseweazle import error
 from greaseweazle.flux import Flux
 from .image import Image
 
+
 class SCPOpts:
     """legacy_ss: Set to True to generate (incorrect) legacy single-sided
     SCP image.
     """
+
     def __init__(self):
         self.legacy_ss = False
 
+
+class SCPTrack:
+
+    def __init__(self, tdh, dat, splice=None):
+        self.tdh = tdh
+        self.dat = dat
+        self.splice = splice
+
+
 class SCP(Image):
 
     # 40MHz
     sample_freq = 40000000
 
+
     def __init__(self):
         self.opts = SCPOpts()
         self.nr_revs = None
         self.to_track = dict()
 
+    
     def side_count(self):
         s = [0,0] # non-empty tracks on each side
         for tnr in self.to_track:
             s[tnr&1] += 1
         return s
 
+
     @classmethod
     def from_file(cls, name):
 
+        splices = None
+
         with open(name, "rb") as f:
             dat = f.read()
 
@@ -47,7 +63,7 @@ class SCP(Image):
         index_cued = flags & 1 or nr_revs == 1
         if not index_cued:
             nr_revs -= 1
-        
+
         # Some tools generate a short TLUT. We handle this by truncating the
         # TLUT at the first Track Data Header.
         trk_offs = struct.unpack("<168I", dat[16:0x2b0])
@@ -62,6 +78,23 @@ class SCP(Image):
             error.check(off >= 0, "SCP: Bad Track Table")
             trk_offs = trk_offs[:off]
 
+        # Parse the extension block introduced by github:markusC64/g64conv.
+        # b'EXTS', length, <length byte Extension Area>
+        # Extension Area contains consecutive chunks of the form:
+        # ID, length, <length bytes of ID-specific dat>
+        ext_sig, ext_len = struct.unpack('<4sI', dat[0x2b0:0x2b8])
+        min_tdh = min(filter(lambda x: x != 0, trk_offs), default=0)
+        if ext_sig == b'EXTS' and 0x2b8 + ext_len <= min_tdh:
+            pos, end = 0x2b8, 0x2b8 + ext_len
+            while end - pos >= 8:
+                chk_sig, chk_len = struct.unpack('<4sI', dat[pos:pos+8])
+                pos += 8
+                if chk_sig == b'WRSP' and chk_len >= 169*4:
+                    # Write-splice positions for writing out SCP tracks
+                    # correctly to disk.
+                    splices = struct.unpack('<168I', dat[pos+4:pos+169*4])
+                pos += chk_len
+
         scp = cls()
         scp.nr_revs = nr_revs
 
@@ -87,7 +120,10 @@ class SCP(Image):
                 continue
 
             tdat = dat[trk_off+s_off:trk_off+e_off]
-            scp.to_track[trknr] = (thdr[4:], tdat)
+            track = SCPTrack(thdr[4:], tdat)
+            if splices is not None:
+                track.splice = splices[trknr]
+            scp.to_track[trknr] = track
 
 
         # Some tools produce (or used to produce) single-sided images using
@@ -107,7 +143,8 @@ class SCP(Image):
         tracknr = cyl * 2 + side
         if not tracknr in self.to_track:
             return None
-        tdh, dat = self.to_track[tracknr]
+        track = self.to_track[tracknr]
+        tdh, dat = track.tdh, track.dat
 
         index_list = []
         while tdh:
@@ -126,7 +163,9 @@ class SCP(Image):
             flux_list.append(val + x)
             val = 0
 
-        return Flux(index_list, flux_list, SCP.sample_freq)
+        flux = Flux(index_list, flux_list, SCP.sample_freq)
+        flux.splice = track.splice if track.splice is not None else 0
+        return flux
 
 
     def emit_track(self, cyl, side, track):
@@ -163,7 +202,7 @@ class SCP(Image):
                 rev += 1
                 if rev >= nr_revs:
                     # We're done: We simply discard any surplus flux samples
-                    self.to_track[cyl*2+side] = (tdh, dat)
+                    self.to_track[cyl*2+side] = SCPTrack(tdh, dat)
                     return
                 to_index += flux.index_list[rev]
 
@@ -190,7 +229,7 @@ class SCP(Image):
             len_at_index = len(dat)
             rev += 1
 
-        self.to_track[cyl*2+side] = (tdh, dat)
+        self.to_track[cyl*2+side] = SCPTrack(tdh, dat)
 
 
     def get_image(self):
@@ -218,9 +257,10 @@ class SCP(Image):
         trk_dat = bytearray()
         for tnr in range(ntracks):
             if tnr in to_track:
-                tdh, dat = to_track[tnr]
+                track = to_track[tnr]
                 trk_offs += struct.pack("<I", 0x2b0 + len(trk_dat))
-                trk_dat += struct.pack("<3sB", b"TRK", tnr) + tdh + dat
+                trk_dat += struct.pack("<3sB", b"TRK", tnr)
+                trk_dat += track.tdh + track.dat
             else:
                 trk_offs += struct.pack("<I", 0)
         error.check(len(trk_offs) <= 0x2a0, "SCP: Too many tracks")