Last active
June 18, 2021 03:52
-
-
Save bbbradsmith/a0fb690f366c902e96f25a8351a5c46c to your computer and use it in GitHub Desktop.
Pasti STX disk image python info dumper (Atari ST)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Pasti .STX Atari ST disk image info dumper | |
# Prints out a human-readable version of all data found in the file | |
# | |
# Usage: | |
# stx_dump("a.stx") | |
# | |
# Based on Pasti reverse engineering: | |
# http://info-coach.fr/atari/documents/_mydoc/Pasti-documentation.pdf | |
# | |
# See also: stx_merge.py | |
# https://gist.github.com/bbbradsmith/e74766f1fb36d0f62262f4187fbe7de4 | |
# | |
# Brad Smith, 2019 | |
# http://rainwarrior.ca | |
# | |
import struct | |
def dump(d,offset=0,columns=32): | |
s = "" | |
for i in range(len(d)): | |
if (i % columns) == 0: | |
s += "$%06X:" % (offset+i) | |
s += " %02X" % d[i] | |
if (i % columns) == (columns - 1) and i != (len(d)-1): | |
s += "\n" | |
return s | |
def stx_dump(filename, dump_data=True, dump_sector_descs=True): | |
print("Pasti: " + filename) | |
d = open(filename,"rb").read() | |
print("\nFile Descriptor") | |
file_desc = struct.unpack("<BBBBHHHBBL",d[0:16]) | |
print("id: $%02X $%02X $%02X $%02X \"%c%c%c\"+$%02X" % (file_desc[0:4]+file_desc[0:4])) | |
print("version: %d" % file_desc[4]) | |
print("tool: $%04X" % file_desc[5]) | |
print("reserved: $%04X" % file_desc[6]) | |
print("tracks: %d" % file_desc[7]) | |
print("revision: %d" % file_desc[8]) | |
print("reserved: $%08X" % file_desc[9]) | |
p = 16 | |
for t in range(0,file_desc[7]): | |
print("\nTrack Record %d at $%08X" % (t,p)) | |
track_desc = struct.unpack("<LLHHHBB",d[p:p+16]) | |
print("record size: %d" % track_desc[0]) | |
print("fuzzy count: %d" % track_desc[1]) | |
print("sectors: %d" % track_desc[2]) | |
print("flags: $%04X" % track_desc[3]) | |
print("length: %d" % track_desc[4]) | |
print("side: %d, number: %d" % (track_desc[5]>>7,track_desc[5]&0x7F)) | |
print("type: $%02X" % track_desc[6]) | |
pnext = p + track_desc[0] | |
p += 16 | |
sector_descs = [] | |
timing_needed = False | |
if (track_desc[3] & 1): | |
for s in range(0,track_desc[2]): | |
if dump_sector_descs: | |
print("\nSector Descriptor %d:%d at $%08X" % (t,s,p)) | |
sector_desc = struct.unpack("<LHHBBBBHBB",d[p:p+16]) | |
sector_descs.append(sector_desc) | |
if dump_sector_descs: | |
print("offset: $%08X" % sector_desc[0]) | |
print("bit position: %d" % sector_desc[1]) | |
print("read time: %d" % sector_desc[2]) | |
print("address track: %d" % sector_desc[3]) | |
print("address head: %d" % sector_desc[4]) | |
print("address number: %d" % sector_desc[5]) | |
print("address size: %d (%d)" % (sector_desc[6],128<<sector_desc[6])) | |
print("CRC: $%04X" % sector_desc[7]) | |
print("flags: $%02X" % sector_desc[8]) | |
print("reserved: $%02X" % sector_desc[9]) | |
if sector_desc[8] & 1: | |
timing_needed = True | |
p+=16 | |
if (track_desc[1] > 0): | |
print ("\nFuzzy Mask (%d bytes)" % track_desc[1]) | |
if dump_data: | |
print(dump(d[p:p+track_desc[1]],offset=p)) | |
p += track_desc[1] | |
pimage = p | |
pimage_size = 0 | |
if (track_desc[3] & 0x40): # track image | |
print ("\nTrack Image %d" % t) | |
if track_desc[3] & 0x80: | |
first_sync = struct.unpack("<H",d[p:p+2])[0] | |
print("first sync: $%04X" % first_sync) | |
p += 2 | |
image_size = struct.unpack("<H",d[p:p+2])[0] | |
print("image size: %d" % image_size) | |
p += 2 | |
pimage_size = image_size | |
if dump_data: | |
print(dump(d[p:p+image_size],offset=p)) | |
p += image_size | |
if (track_desc[3] & 1): # sector descriptors = maybe sector images | |
sector_images = [] | |
if not (track_desc[3] & 0x40): # no track image = all sectors | |
sector_images = range(0,track_desc[2]) | |
else: | |
for s in range(0,track_desc[2]): | |
if sector_descs[s][0] >= image_size: # offset outside track image | |
sector_images.append(s) | |
for s in sector_images: | |
print ("\nSector Image %d" % s) | |
image_offset = sector_descs[s][0] | |
image_size = 128 << sector_descs[s][6] | |
print ("offset: $%08X" % image_offset) | |
print ("size: %d" % image_size) | |
if dump_data: | |
print(dump(d[pimage+image_offset:pimage+image_offset+image_size],pimage+image_offset)) | |
p = pimage+image_offset+image_size # seek to end of this block? | |
if file_desc[8] == 2 and timing_needed and p < pnext: # timing record | |
print ("\nTiming Data") | |
timing_desc = struct.unpack("<HH",d[p:p+4]) | |
print ("flags: %d" % timing_desc[0]) | |
print ("size: %d (%d)" % (timing_desc[1],(timing_desc[1]-4)/2)) | |
for i in range(4,timing_desc[1],2): | |
block_time = struct.unpack("<H",d[p+i:p+i+2])[0] | |
print ("%3d: %d" % ((i-4)/2,block_time)) | |
p += timing_desc[1] | |
if p != pnext: | |
# track structures seem to be padded to even bytes ($FF for fill) | |
print ("\nExtra Data? %d bytes" % (pnext-p)) | |
if dump_data: | |
print(dump(d[p:pnext],p)) | |
p = pnext | |
if p != len(d): | |
print("\nExtra Suffix Data? %d bytes" % (len(d)-p)) | |
if dump_data: | |
print(dump(d[p:pnext],p)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment