From ffc40da878317cf1f3df2ac8356dcca65017b49a Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 22 Oct 2025 14:27:18 -0500 Subject: [PATCH] scripts: Reworked tagrepr -> Tag.repr to rely more on self-parsing This should make tag editing less tedious/error-prone. We already used self-parsing to generate -l/--list in dbgtag.py, but this extends the idea to tagrepr (now Tag.repr), which is used in quite a few more scripts. To make this work the little tag encoding spec had to become a bit more rigorous, fortunately the only real change was the addition of '+' characters to mark reserved-but-expected-zero bits. Example: TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq ^--^----^----^--^-^-- valid bit, unmatched '----|----|--|-|-- matches 1 '----|--|-|-- matches 0 '--|-|-- reserved 0, unmatched '-|-- perturb bit, unmatched '-- phase bits, unmatched dbgtag.py 0x3000 => cksumq0 dbgtag.py 0x3007 => cksumq3p dbgtag.py 0x3017 => cksumq3p 0x10 dbgtag.py 0x3417 => 0x3417 Though Tag.repr still does a bit of manual formatting for the differences between shrub/normal/null/alt tags. Still, this should reduce the number of things that need to be changed from 2 -> 1 when adding/editing most new tags. --- scripts/dbgbmap.py | 448 ++++++++++++++++++++++-------------------- scripts/dbgbmapsvg.py | 448 ++++++++++++++++++++++-------------------- scripts/dbgbtree.py | 394 ++++++++++++++++++++----------------- scripts/dbgerr.py | 42 ++-- scripts/dbgflags.py | 118 +++++------ scripts/dbglfs3.py | 410 ++++++++++++++++++++------------------ scripts/dbgmtree.py | 394 ++++++++++++++++++++----------------- scripts/dbgrbyd.py | 396 ++++++++++++++++++++----------------- scripts/dbgtag.py | 433 +++++++++++++++++++++------------------- 9 files changed, 1657 insertions(+), 1426 deletions(-) diff --git a/scripts/dbgbmap.py b/scripts/dbgbmap.py index 54bf0712..e22a050b 100755 --- a/scripts/dbgbmap.py +++ b/scripts/dbgbmap.py @@ -29,76 +29,6 @@ except ModuleNotFoundError: crc32c_lib = None -TAG_NULL = 0x0000 ## 0x0000 v--- ---- ---- ---- -TAG_CONFIG = 0x0000 ## 0x00tt v--- ---- -ttt tttt -TAG_MAGIC = 0x0031 # 0x003r v--- ---- --11 --rr -TAG_VERSION = 0x0034 # 0x0034 v--- ---- --11 -1-- -TAG_RCOMPAT = 0x0035 # 0x0035 v--- ---- --11 -1-1 -TAG_WCOMPAT = 0x0036 # 0x0036 v--- ---- --11 -11- -TAG_OCOMPAT = 0x0037 # 0x0037 v--- ---- --11 -111 -TAG_GEOMETRY = 0x0038 # 0x0038 v--- ---- --11 1--- -TAG_NAMELIMIT = 0x0039 # 0x0039 v--- ---- --11 1--1 -TAG_FILELIMIT = 0x003a # 0x003a v--- ---- --11 1-1- -TAG_GDELTA = 0x0100 ## 0x01tt v--- ---1 -ttt ttrr -TAG_GRMDELTA = 0x0100 # 0x0100 v--- ---1 ---- ---- -TAG_GBMAPDELTA = 0x0104 # 0x0104 v--- ---1 ---- -1rr -TAG_NAME = 0x0200 ## 0x02tt v--- --1- -ttt tttt -TAG_BNAME = 0x0200 # 0x0200 v--- --1- ---- ---- -TAG_REG = 0x0201 # 0x0201 v--- --1- ---- ---1 -TAG_DIR = 0x0202 # 0x0202 v--- --1- ---- --1- -TAG_STICKYNOTE = 0x0203 # 0x0203 v--- --1- ---- --11 -TAG_BOOKMARK = 0x0204 # 0x0204 v--- --1- ---- -1-- -TAG_MNAME = 0x0220 # 0x0220 v--- --1- --1- ---- -TAG_STRUCT = 0x0300 ## 0x03tt v--- --11 -ttt ttrr -TAG_BRANCH = 0x0300 # 0x030r v--- --11 ---- --rr -TAG_DATA = 0x0304 # 0x0304 v--- --11 ---- -1-- -TAG_BLOCK = 0x0308 # 0x0308 v--- --11 ---- 1err -TAG_DID = 0x0314 # 0x0314 v--- --11 ---1 -1-- -TAG_BSHRUB = 0x0318 # 0x0318 v--- --11 ---1 1--- -TAG_BTREE = 0x031c # 0x031c v--- --11 ---1 11rr -TAG_MROOT = 0x0321 # 0x032r v--- --11 --1- --rr -TAG_MDIR = 0x0325 # 0x0324 v--- --11 --1- -1rr -TAG_MTREE = 0x032c # 0x032c v--- --11 --1- 11rr -TAG_BMRANGE = 0x0330 # 0x033u v--- --11 --11 uuuu -TAG_BMFREE = 0x0330 # 0x0330 v--- --11 --11 ---- -TAG_BMINUSE = 0x0331 # 0x0331 v--- --11 --11 ---1 -TAG_BMERASED = 0x0332 # 0x0332 v--- --11 --11 --1- -TAG_BMBAD = 0x0333 # 0x0333 v--- --11 --11 --11 -TAG_ATTR = 0x0400 ## 0x04aa v--- -1-a -aaa aaaa -TAG_UATTR = 0x0400 # 0x04aa v--- -1-- -aaa aaaa -TAG_SATTR = 0x0500 # 0x05aa v--- -1-1 -aaa aaaa -TAG_SHRUB = 0x1000 ## 0x1kkk v--1 kkkk -kkk kkkk -TAG_ALT = 0x4000 ## 0x4kkk v1cd kkkk -kkk kkkk -TAG_B = 0x0000 -TAG_R = 0x2000 -TAG_LE = 0x0000 -TAG_GT = 0x1000 -TAG_CKSUM = 0x3000 ## 0x300p v-11 ---- ---- -pqq -TAG_PHASE = 0x0003 -TAG_PERTURB = 0x0004 -TAG_NOTE = 0x3100 ## 0x3100 v-11 ---1 ---- ---- -TAG_ECKSUM = 0x3200 ## 0x3200 v-11 --1- ---- ---- -TAG_GCKSUMDELTA = 0x3300 ## 0x3300 v-11 --11 ---- ---- - -RCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format -RCOMPAT_WRONLY = 0x00000002 # Reading is disallowed -RCOMPAT_BMOSS = 0x00000010 # Files may use inlined data -RCOMPAT_BSPROUT = 0x00000020 # Files may use block pointers -RCOMPAT_BSHRUB = 0x00000040 # Files may use inlined btrees -RCOMPAT_BTREE = 0x00000080 # Files may use btrees -RCOMPAT_MMOSS = 0x00000100 # May use an inlined mdir -RCOMPAT_MSPROUT = 0x00000200 # May use an mdir pointer -RCOMPAT_MSHRUB = 0x00000400 # May use an inlined mtree -RCOMPAT_MTREE = 0x00000800 # May use an mdir btree -RCOMPAT_GRM = 0x00001000 # Global-remove in use - -WCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format -WCOMPAT_RDONLY = 0x00000002 # Writing is disallowed -WCOMPAT_DIR = 0x00000010 # Directory file types in use -WCOMPAT_GCKSUM = 0x00001000 # Global-checksum in use -WCOMPAT_GBMAP = 0x00002000 # Global on-disk block-map in use - - # assign chars/colors to specific filesystem objects CHARS = { 'mdir': 'm', @@ -164,6 +94,243 @@ SI2_PREFIXES = { } +RCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format +RCOMPAT_WRONLY = 0x00000002 # Reading is disallowed +RCOMPAT_BMOSS = 0x00000010 # Files may use inlined data +RCOMPAT_BSPROUT = 0x00000020 # Files may use block pointers +RCOMPAT_BSHRUB = 0x00000040 # Files may use inlined btrees +RCOMPAT_BTREE = 0x00000080 # Files may use btrees +RCOMPAT_MMOSS = 0x00000100 # May use an inlined mdir +RCOMPAT_MSPROUT = 0x00000200 # May use an mdir pointer +RCOMPAT_MSHRUB = 0x00000400 # May use an inlined mtree +RCOMPAT_MTREE = 0x00000800 # May use an mdir btree +RCOMPAT_GRM = 0x00001000 # Global-remove in use + +WCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format +WCOMPAT_RDONLY = 0x00000002 # Writing is disallowed +WCOMPAT_DIR = 0x00000010 # Directory file types in use +WCOMPAT_GCKSUM = 0x00001000 # Global-checksum in use +WCOMPAT_GBMAP = 0x00002000 # Global on-disk block-map in use + +TAG_NULL = 0x0000 ## v--- ---- +--- ---- +TAG_CONFIG = 0x0000 ## v--- ---- +ttt tttt +TAG_MAGIC = 0x0031 # v--- ---- +-11 --rr +TAG_VERSION = 0x0034 # v--- ---- +-11 -1-- +TAG_RCOMPAT = 0x0035 # v--- ---- +-11 -1-1 +TAG_WCOMPAT = 0x0036 # v--- ---- +-11 -11- +TAG_OCOMPAT = 0x0037 # v--- ---- +-11 -111 +TAG_GEOMETRY = 0x0038 # v--- ---- +-11 1--- +TAG_NAMELIMIT = 0x0039 # v--- ---- +-11 1--1 +TAG_FILELIMIT = 0x003a # v--- ---- +-11 1-1- +TAG_GDELTA = 0x0100 ## v--- ---1 +ttt ttrr +TAG_GRMDELTA = 0x0100 # v--- ---1 +--- ---- +TAG_GBMAPDELTA = 0x0104 # v--- ---1 +--- -1rr +TAG_NAME = 0x0200 ## v--- --1- +ttt tttt +TAG_BNAME = 0x0200 # v--- --1- +--- ---- +TAG_REG = 0x0201 # v--- --1- +--- ---1 +TAG_DIR = 0x0202 # v--- --1- +--- --1- +TAG_STICKYNOTE = 0x0203 # v--- --1- +--- --11 +TAG_BOOKMARK = 0x0204 # v--- --1- +--- -1-- +TAG_MNAME = 0x0220 # v--- --1- +-1- ---- +TAG_STRUCT = 0x0300 ## v--- --11 +ttt ttrr +TAG_BRANCH = 0x0300 # v--- --11 +--- --rr +TAG_DATA = 0x0304 # v--- --11 +--- -1-- +TAG_BLOCK = 0x0308 # v--- --11 +--- 1err +TAG_DID = 0x0314 # v--- --11 +--1 -1-- +TAG_BSHRUB = 0x0318 # v--- --11 +--1 1--- +TAG_BTREE = 0x031c # v--- --11 +--1 11rr +TAG_MROOT = 0x0321 # v--- --11 +-1- --rr +TAG_MDIR = 0x0325 # v--- --11 +-1- -1rr +TAG_MTREE = 0x032c # v--- --11 +-1- 11rr +TAG_BMRANGE = 0x0330 # v--- --11 +-11 ++uu +TAG_BMFREE = 0x0330 # v--- --11 +-11 ---- +TAG_BMINUSE = 0x0331 # v--- --11 +-11 ---1 +TAG_BMERASED = 0x0332 # v--- --11 +-11 --1- +TAG_BMBAD = 0x0333 # v--- --11 +-11 --11 +TAG_ATTR = 0x0400 ## v--- -1-a +aaa aaaa +TAG_UATTR = 0x0400 # v--- -1-- +aaa aaaa +TAG_SATTR = 0x0500 # v--- -1-1 +aaa aaaa +TAG_SHRUB = 0x1000 ## v--1 kkkk +kkk kkkk +TAG_ALT = 0x4000 ## v1cd kkkk +kkk kkkk +TAG_B = 0x0000 +TAG_R = 0x2000 +TAG_LE = 0x0000 +TAG_GT = 0x1000 +TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq +TAG_PHASE = 0x0003 +TAG_PERTURB = 0x0004 +TAG_NOTE = 0x3100 ## v-11 ---1 ++++ ++++ +TAG_ECKSUM = 0x3200 ## v-11 --1- ++++ ++++ +TAG_GCKSUMDELTA = 0x3300 ## v-11 --11 ++++ ++++ + + +# self-parsing tag repr +class Tag: + def __init__(self, name, tag, encoding, help): + self.name = name + self.tag = tag + self.encoding = encoding + self.help = help + # derive mask from encoding + self.mask = sum( + (1 if x in 'v-01' else 0) << len(self.encoding)-1-i + for i, x in enumerate(self.encoding)) + + def __repr__(self): + return 'Tag(%r, %r, %r)' % ( + self.name, + self.tag, + self.encoding) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + # substitute mask chars when zero + tag = '0x%s' % ''.join( + n if n != '0' else next( + (x for x in self.encoding[i*4:i*4+4] + if x not in 'v-01+'), + '0') + for i, n in enumerate('%04x' % self.tag)) + # group into nibbles + encoding = ' '.join(self.encoding[i*4:i*4+4] + for i in range(len(self.encoding)//4)) + return ('LFS3_%s' % self.name, tag, encoding) + + def specificity(self): + return sum(1 for x in self.encoding if x in 'v-01') + + def matches(self, tag): + return (tag & self.mask) == (self.tag & self.mask) + + def get(self, chars, tag): + return sum( + tag & ((1 if x in chars else 0) << len(self.encoding)-1-i) + for i, x in enumerate(self.encoding)) + + def max(self, chars): + return max(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def min(self, chars): + return min(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def width(self, chars): + return self.max(chars) - self.min(chars) + + def __contains__(self, chars): + return any(x in self.encoding for x in chars) + + @staticmethod + @ft.cache + def tags(): + # parse our script's source to figure out tags + import inspect + import re + tags = [] + tag_pattern = re.compile( + '^(?PTAG_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P(?:[^ ] *?){16}) *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = tag_pattern.match(line) + if m: + tags.append(Tag( + m.group('name'), + globals()[m.group('name')], + m.group('encoding').replace(' ', ''), + m.group('help'))) + return tags + + # find best matching tag + @staticmethod + def find(tag): + # find tags, note this is cached + tags__ = Tag.tags() + + # find the most specific matching tag, ignoring valid bits + return max((t for t in tags__ if t.matches(tag & 0x7fff)), + key=lambda t: t.specificity(), + default=None) + + # human readable tag repr + @staticmethod + def repr(tag, weight=None, size=None, *, + global_=False, + toff=None): + # find the most specific matching tag, ignoring the shrub bit + t = Tag.find(tag & ~(TAG_SHRUB if tag & 0x7000 == TAG_SHRUB else 0)) + + # build repr + r = [] + # normal tag? + if not tag & TAG_ALT: + if t is not None: + # prefix shrub tags with shrub + if tag & 0x7000 == TAG_SHRUB: + r.append('shrub') + # lowercase name + r.append(t.name.split('_', 1)[1].lower()) + # gstate tag? + if global_: + if r[-1] == 'gdelta': + r[-1] = 'gstate' + elif r[-1].endswith('delta'): + r[-1] = r[-1][:-len('delta')] + # include perturb/phase bits + if 'q' in t: + r.append('q%d' % t.get('q', tag)) + if 'p' in t and tag & TAG_PERTURB: + r.append('p') + + # include unmatched fields, but not just redund, and + # only reserved bits if non-zero + if 'tua' in t or ('+' in t and t.get('+', tag) != 0): + r.append(' 0x%0*x' % ( + (t.width('tuar+')+4-1)//4, + t.get('tuar+', tag))) + # unknown tag? + else: + r.append('0x%04x' % tag) + + # weight? + if weight: + r.append(' w%d' % weight) + # size? don't include if null + if size is not None and (size or tag & 0x7fff): + r.append(' %d' % size) + + # alt pointer? + else: + r.append('alt') + r.append('r' if tag & TAG_R else 'b') + r.append('gt' if tag & TAG_GT else 'le') + r.append(' 0x%0*x' % ( + (t.width('k')+4-1)//4, + t.get('k', tag))) + + # weight? + if weight is not None: + r.append(' w%d' % weight) + # jump? + if size and toff is not None: + r.append(' 0x%x' % (0xffffffff & (toff-size))) + elif size: + r.append(' -%d' % size) + + return ''.join(r) + + # some ways of block geometry representations # 512 -> 512 # 512x16 -> (512, 16) @@ -328,141 +495,6 @@ def xxd(data, width=16): b if b >= ' ' and b <= '~' else '.' for b in map(chr, data[i:i+width]))) -# human readable tag repr -def tagrepr(tag, weight=None, size=None, *, - global_=False, - toff=None): - # null tags - if (tag & 0x6fff) == TAG_NULL: - return '%snull%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - ' w%d' % weight if weight else '', - ' %d' % size if size else '') - # config tags - elif (tag & 0x6f00) == TAG_CONFIG: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'magic' if (tag & 0xfff) == TAG_MAGIC - else 'version' if (tag & 0xfff) == TAG_VERSION - else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT - else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT - else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY - else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT - else 'filelimit' if (tag & 0xfff) == TAG_FILELIMIT - else 'config 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-state delta tags - elif (tag & 0x6f00) == TAG_GDELTA: - if global_: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grm' if (tag & 0xfff) == TAG_GRMDELTA - else 'gstate 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grmdelta' if (tag & 0xfff) == TAG_GRMDELTA - else 'gbmapdelta' if (tag & 0xfff) == TAG_GBMAPDELTA - else 'gdelta 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # name tags, includes file types - elif (tag & 0x6f00) == TAG_NAME: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'bname' if (tag & 0xfff) == TAG_BNAME - else 'reg' if (tag & 0xfff) == TAG_REG - else 'dir' if (tag & 0xfff) == TAG_DIR - else 'stickynote' if (tag & 0xfff) == TAG_STICKYNOTE - else 'bookmark' if (tag & 0xfff) == TAG_BOOKMARK - else 'mname' if (tag & 0xfff) == TAG_MNAME - else 'name 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # structure tags - elif (tag & 0x6f00) == TAG_STRUCT: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'branch' if (tag & 0xfff) == TAG_BRANCH - else 'data' if (tag & 0xfff) == TAG_DATA - else 'block' if (tag & 0xfff) == TAG_BLOCK - else 'did' if (tag & 0xfff) == TAG_DID - else 'bshrub' if (tag & 0xfff) == TAG_BSHRUB - else 'btree' if (tag & 0xfff) == TAG_BTREE - else 'mroot' if (tag & 0xfff) == TAG_MROOT - else 'mdir' if (tag & 0xfff) == TAG_MDIR - else 'mtree' if (tag & 0xfff) == TAG_MTREE - else 'bmfree' if (tag & 0xfff) == TAG_BMFREE - else 'bminuse' if (tag & 0xfff) == TAG_BMINUSE - else 'bmerased' if (tag & 0xfff) == TAG_BMERASED - else 'bmbad' if (tag & 0xfff) == TAG_BMBAD - else 'bmrange 0x%x' % (tag & 0xf) - if (tag & 0xff0) == TAG_BMRANGE - else 'struct 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # custom attributes - elif (tag & 0x6e00) == TAG_ATTR: - return '%s%sattr 0x%02x%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 's' if tag & 0x100 else 'u', - ((tag & 0x100) >> 1) + (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # alt pointers - elif tag & TAG_ALT: - return 'alt%s%s 0x%03x%s%s' % ( - 'r' if tag & TAG_R else 'b', - 'gt' if tag & TAG_GT else 'le', - tag & 0x0fff, - ' w%d' % weight if weight is not None else '', - ' 0x%x' % (0xffffffff & (toff-size)) - if size and toff is not None - else ' -%d' % size if size - else '') - # checksum tags - elif (tag & 0x7f00) == TAG_CKSUM: - return 'cksum%s%s%s%s%s' % ( - 'q%d' % (tag & 0x3), - 'p' if tag & TAG_PERTURB else '', - ' 0x%02x' % (tag & 0xff) if tag & 0xf8 else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # note tags - elif (tag & 0x7f00) == TAG_NOTE: - return 'note%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # erased-state checksum tags - elif (tag & 0x7f00) == TAG_ECKSUM: - return 'ecksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-checksum delta tags - elif (tag & 0x7f00) == TAG_GCKSUMDELTA: - if global_: - return 'gcksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return 'gcksumdelta%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # unknown tags - else: - return '0x%04x%s%s' % ( - tag, - ' w%d' % weight if weight is not None else '', - ' %d' % size if size is not None else '') - # compute the difference between two paths, returning everything # in a after the paths diverge, as well as the relevant index def pathdelta(a, b): @@ -541,7 +573,7 @@ class Rattr: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.size) + return Tag.repr(self.tag, self.weight, self.size) def __iter__(self): return iter((self.tag, self.weight, self.data)) @@ -620,7 +652,7 @@ class Ralt: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.jump, toff=self.toff) + return Tag.repr(self.tag, self.weight, self.jump, toff=self.toff) def __iter__(self): return iter((self.tag, self.weight, self.jump)) @@ -2801,7 +2833,7 @@ class Gstate: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, 0, self.size, global_=True) + return Tag.repr(self.tag, 0, self.size, global_=True) def __iter__(self): return iter((self.tag, self.data)) diff --git a/scripts/dbgbmapsvg.py b/scripts/dbgbmapsvg.py index fc8ae042..074527bf 100755 --- a/scripts/dbgbmapsvg.py +++ b/scripts/dbgbmapsvg.py @@ -27,76 +27,6 @@ except ModuleNotFoundError: crc32c_lib = None -TAG_NULL = 0x0000 ## 0x0000 v--- ---- ---- ---- -TAG_CONFIG = 0x0000 ## 0x00tt v--- ---- -ttt tttt -TAG_MAGIC = 0x0031 # 0x003r v--- ---- --11 --rr -TAG_VERSION = 0x0034 # 0x0034 v--- ---- --11 -1-- -TAG_RCOMPAT = 0x0035 # 0x0035 v--- ---- --11 -1-1 -TAG_WCOMPAT = 0x0036 # 0x0036 v--- ---- --11 -11- -TAG_OCOMPAT = 0x0037 # 0x0037 v--- ---- --11 -111 -TAG_GEOMETRY = 0x0038 # 0x0038 v--- ---- --11 1--- -TAG_NAMELIMIT = 0x0039 # 0x0039 v--- ---- --11 1--1 -TAG_FILELIMIT = 0x003a # 0x003a v--- ---- --11 1-1- -TAG_GDELTA = 0x0100 ## 0x01tt v--- ---1 -ttt ttrr -TAG_GRMDELTA = 0x0100 # 0x0100 v--- ---1 ---- ---- -TAG_GBMAPDELTA = 0x0104 # 0x0104 v--- ---1 ---- -1rr -TAG_NAME = 0x0200 ## 0x02tt v--- --1- -ttt tttt -TAG_BNAME = 0x0200 # 0x0200 v--- --1- ---- ---- -TAG_REG = 0x0201 # 0x0201 v--- --1- ---- ---1 -TAG_DIR = 0x0202 # 0x0202 v--- --1- ---- --1- -TAG_STICKYNOTE = 0x0203 # 0x0203 v--- --1- ---- --11 -TAG_BOOKMARK = 0x0204 # 0x0204 v--- --1- ---- -1-- -TAG_MNAME = 0x0220 # 0x0220 v--- --1- --1- ---- -TAG_STRUCT = 0x0300 ## 0x03tt v--- --11 -ttt ttrr -TAG_BRANCH = 0x0300 # 0x030r v--- --11 ---- --rr -TAG_DATA = 0x0304 # 0x0304 v--- --11 ---- -1-- -TAG_BLOCK = 0x0308 # 0x0308 v--- --11 ---- 1err -TAG_DID = 0x0314 # 0x0314 v--- --11 ---1 -1-- -TAG_BSHRUB = 0x0318 # 0x0318 v--- --11 ---1 1--- -TAG_BTREE = 0x031c # 0x031c v--- --11 ---1 11rr -TAG_MROOT = 0x0321 # 0x032r v--- --11 --1- --rr -TAG_MDIR = 0x0325 # 0x0324 v--- --11 --1- -1rr -TAG_MTREE = 0x032c # 0x032c v--- --11 --1- 11rr -TAG_BMRANGE = 0x0330 # 0x033u v--- --11 --11 uuuu -TAG_BMFREE = 0x0330 # 0x0330 v--- --11 --11 ---- -TAG_BMINUSE = 0x0331 # 0x0331 v--- --11 --11 ---1 -TAG_BMERASED = 0x0332 # 0x0332 v--- --11 --11 --1- -TAG_BMBAD = 0x0333 # 0x0333 v--- --11 --11 --11 -TAG_ATTR = 0x0400 ## 0x04aa v--- -1-a -aaa aaaa -TAG_UATTR = 0x0400 # 0x04aa v--- -1-- -aaa aaaa -TAG_SATTR = 0x0500 # 0x05aa v--- -1-1 -aaa aaaa -TAG_SHRUB = 0x1000 ## 0x1kkk v--1 kkkk -kkk kkkk -TAG_ALT = 0x4000 ## 0x4kkk v1cd kkkk -kkk kkkk -TAG_B = 0x0000 -TAG_R = 0x2000 -TAG_LE = 0x0000 -TAG_GT = 0x1000 -TAG_CKSUM = 0x3000 ## 0x300p v-11 ---- ---- -pqq -TAG_PHASE = 0x0003 -TAG_PERTURB = 0x0004 -TAG_NOTE = 0x3100 ## 0x3100 v-11 ---1 ---- ---- -TAG_ECKSUM = 0x3200 ## 0x3200 v-11 --1- ---- ---- -TAG_GCKSUMDELTA = 0x3300 ## 0x3300 v-11 --11 ---- ---- - -RCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format -RCOMPAT_WRONLY = 0x00000002 # Reading is disallowed -RCOMPAT_BMOSS = 0x00000010 # Files may use inlined data -RCOMPAT_BSPROUT = 0x00000020 # Files may use block pointers -RCOMPAT_BSHRUB = 0x00000040 # Files may use inlined btrees -RCOMPAT_BTREE = 0x00000080 # Files may use btrees -RCOMPAT_MMOSS = 0x00000100 # May use an inlined mdir -RCOMPAT_MSPROUT = 0x00000200 # May use an mdir pointer -RCOMPAT_MSHRUB = 0x00000400 # May use an inlined mtree -RCOMPAT_MTREE = 0x00000800 # May use an mdir btree -RCOMPAT_GRM = 0x00001000 # Global-remove in use - -WCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format -WCOMPAT_RDONLY = 0x00000002 # Writing is disallowed -WCOMPAT_DIR = 0x00000010 # Directory file types in use -WCOMPAT_GCKSUM = 0x00001000 # Global-checksum in use -WCOMPAT_GBMAP = 0x00002000 # Global on-disk block-map in use - - # assign colors to specific filesystem objects # @@ -183,6 +113,243 @@ SI2_PREFIXES = { } +RCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format +RCOMPAT_WRONLY = 0x00000002 # Reading is disallowed +RCOMPAT_BMOSS = 0x00000010 # Files may use inlined data +RCOMPAT_BSPROUT = 0x00000020 # Files may use block pointers +RCOMPAT_BSHRUB = 0x00000040 # Files may use inlined btrees +RCOMPAT_BTREE = 0x00000080 # Files may use btrees +RCOMPAT_MMOSS = 0x00000100 # May use an inlined mdir +RCOMPAT_MSPROUT = 0x00000200 # May use an mdir pointer +RCOMPAT_MSHRUB = 0x00000400 # May use an inlined mtree +RCOMPAT_MTREE = 0x00000800 # May use an mdir btree +RCOMPAT_GRM = 0x00001000 # Global-remove in use + +WCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format +WCOMPAT_RDONLY = 0x00000002 # Writing is disallowed +WCOMPAT_DIR = 0x00000010 # Directory file types in use +WCOMPAT_GCKSUM = 0x00001000 # Global-checksum in use +WCOMPAT_GBMAP = 0x00002000 # Global on-disk block-map in use + +TAG_NULL = 0x0000 ## v--- ---- +--- ---- +TAG_CONFIG = 0x0000 ## v--- ---- +ttt tttt +TAG_MAGIC = 0x0031 # v--- ---- +-11 --rr +TAG_VERSION = 0x0034 # v--- ---- +-11 -1-- +TAG_RCOMPAT = 0x0035 # v--- ---- +-11 -1-1 +TAG_WCOMPAT = 0x0036 # v--- ---- +-11 -11- +TAG_OCOMPAT = 0x0037 # v--- ---- +-11 -111 +TAG_GEOMETRY = 0x0038 # v--- ---- +-11 1--- +TAG_NAMELIMIT = 0x0039 # v--- ---- +-11 1--1 +TAG_FILELIMIT = 0x003a # v--- ---- +-11 1-1- +TAG_GDELTA = 0x0100 ## v--- ---1 +ttt ttrr +TAG_GRMDELTA = 0x0100 # v--- ---1 +--- ---- +TAG_GBMAPDELTA = 0x0104 # v--- ---1 +--- -1rr +TAG_NAME = 0x0200 ## v--- --1- +ttt tttt +TAG_BNAME = 0x0200 # v--- --1- +--- ---- +TAG_REG = 0x0201 # v--- --1- +--- ---1 +TAG_DIR = 0x0202 # v--- --1- +--- --1- +TAG_STICKYNOTE = 0x0203 # v--- --1- +--- --11 +TAG_BOOKMARK = 0x0204 # v--- --1- +--- -1-- +TAG_MNAME = 0x0220 # v--- --1- +-1- ---- +TAG_STRUCT = 0x0300 ## v--- --11 +ttt ttrr +TAG_BRANCH = 0x0300 # v--- --11 +--- --rr +TAG_DATA = 0x0304 # v--- --11 +--- -1-- +TAG_BLOCK = 0x0308 # v--- --11 +--- 1err +TAG_DID = 0x0314 # v--- --11 +--1 -1-- +TAG_BSHRUB = 0x0318 # v--- --11 +--1 1--- +TAG_BTREE = 0x031c # v--- --11 +--1 11rr +TAG_MROOT = 0x0321 # v--- --11 +-1- --rr +TAG_MDIR = 0x0325 # v--- --11 +-1- -1rr +TAG_MTREE = 0x032c # v--- --11 +-1- 11rr +TAG_BMRANGE = 0x0330 # v--- --11 +-11 ++uu +TAG_BMFREE = 0x0330 # v--- --11 +-11 ---- +TAG_BMINUSE = 0x0331 # v--- --11 +-11 ---1 +TAG_BMERASED = 0x0332 # v--- --11 +-11 --1- +TAG_BMBAD = 0x0333 # v--- --11 +-11 --11 +TAG_ATTR = 0x0400 ## v--- -1-a +aaa aaaa +TAG_UATTR = 0x0400 # v--- -1-- +aaa aaaa +TAG_SATTR = 0x0500 # v--- -1-1 +aaa aaaa +TAG_SHRUB = 0x1000 ## v--1 kkkk +kkk kkkk +TAG_ALT = 0x4000 ## v1cd kkkk +kkk kkkk +TAG_B = 0x0000 +TAG_R = 0x2000 +TAG_LE = 0x0000 +TAG_GT = 0x1000 +TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq +TAG_PHASE = 0x0003 +TAG_PERTURB = 0x0004 +TAG_NOTE = 0x3100 ## v-11 ---1 ++++ ++++ +TAG_ECKSUM = 0x3200 ## v-11 --1- ++++ ++++ +TAG_GCKSUMDELTA = 0x3300 ## v-11 --11 ++++ ++++ + + +# self-parsing tag repr +class Tag: + def __init__(self, name, tag, encoding, help): + self.name = name + self.tag = tag + self.encoding = encoding + self.help = help + # derive mask from encoding + self.mask = sum( + (1 if x in 'v-01' else 0) << len(self.encoding)-1-i + for i, x in enumerate(self.encoding)) + + def __repr__(self): + return 'Tag(%r, %r, %r)' % ( + self.name, + self.tag, + self.encoding) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + # substitute mask chars when zero + tag = '0x%s' % ''.join( + n if n != '0' else next( + (x for x in self.encoding[i*4:i*4+4] + if x not in 'v-01+'), + '0') + for i, n in enumerate('%04x' % self.tag)) + # group into nibbles + encoding = ' '.join(self.encoding[i*4:i*4+4] + for i in range(len(self.encoding)//4)) + return ('LFS3_%s' % self.name, tag, encoding) + + def specificity(self): + return sum(1 for x in self.encoding if x in 'v-01') + + def matches(self, tag): + return (tag & self.mask) == (self.tag & self.mask) + + def get(self, chars, tag): + return sum( + tag & ((1 if x in chars else 0) << len(self.encoding)-1-i) + for i, x in enumerate(self.encoding)) + + def max(self, chars): + return max(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def min(self, chars): + return min(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def width(self, chars): + return self.max(chars) - self.min(chars) + + def __contains__(self, chars): + return any(x in self.encoding for x in chars) + + @staticmethod + @ft.cache + def tags(): + # parse our script's source to figure out tags + import inspect + import re + tags = [] + tag_pattern = re.compile( + '^(?PTAG_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P(?:[^ ] *?){16}) *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = tag_pattern.match(line) + if m: + tags.append(Tag( + m.group('name'), + globals()[m.group('name')], + m.group('encoding').replace(' ', ''), + m.group('help'))) + return tags + + # find best matching tag + @staticmethod + def find(tag): + # find tags, note this is cached + tags__ = Tag.tags() + + # find the most specific matching tag, ignoring valid bits + return max((t for t in tags__ if t.matches(tag & 0x7fff)), + key=lambda t: t.specificity(), + default=None) + + # human readable tag repr + @staticmethod + def repr(tag, weight=None, size=None, *, + global_=False, + toff=None): + # find the most specific matching tag, ignoring the shrub bit + t = Tag.find(tag & ~(TAG_SHRUB if tag & 0x7000 == TAG_SHRUB else 0)) + + # build repr + r = [] + # normal tag? + if not tag & TAG_ALT: + if t is not None: + # prefix shrub tags with shrub + if tag & 0x7000 == TAG_SHRUB: + r.append('shrub') + # lowercase name + r.append(t.name.split('_', 1)[1].lower()) + # gstate tag? + if global_: + if r[-1] == 'gdelta': + r[-1] = 'gstate' + elif r[-1].endswith('delta'): + r[-1] = r[-1][:-len('delta')] + # include perturb/phase bits + if 'q' in t: + r.append('q%d' % t.get('q', tag)) + if 'p' in t and tag & TAG_PERTURB: + r.append('p') + + # include unmatched fields, but not just redund, and + # only reserved bits if non-zero + if 'tua' in t or ('+' in t and t.get('+', tag) != 0): + r.append(' 0x%0*x' % ( + (t.width('tuar+')+4-1)//4, + t.get('tuar+', tag))) + # unknown tag? + else: + r.append('0x%04x' % tag) + + # weight? + if weight: + r.append(' w%d' % weight) + # size? don't include if null + if size is not None and (size or tag & 0x7fff): + r.append(' %d' % size) + + # alt pointer? + else: + r.append('alt') + r.append('r' if tag & TAG_R else 'b') + r.append('gt' if tag & TAG_GT else 'le') + r.append(' 0x%0*x' % ( + (t.width('k')+4-1)//4, + t.get('k', tag))) + + # weight? + if weight is not None: + r.append(' w%d' % weight) + # jump? + if size and toff is not None: + r.append(' 0x%x' % (0xffffffff & (toff-size))) + elif size: + r.append(' -%d' % size) + + return ''.join(r) + + # open with '-' for stdin/stdout def openio(path, mode='r', buffering=-1): import os @@ -358,141 +525,6 @@ def xxd(data, width=16): b if b >= ' ' and b <= '~' else '.' for b in map(chr, data[i:i+width]))) -# human readable tag repr -def tagrepr(tag, weight=None, size=None, *, - global_=False, - toff=None): - # null tags - if (tag & 0x6fff) == TAG_NULL: - return '%snull%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - ' w%d' % weight if weight else '', - ' %d' % size if size else '') - # config tags - elif (tag & 0x6f00) == TAG_CONFIG: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'magic' if (tag & 0xfff) == TAG_MAGIC - else 'version' if (tag & 0xfff) == TAG_VERSION - else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT - else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT - else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY - else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT - else 'filelimit' if (tag & 0xfff) == TAG_FILELIMIT - else 'config 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-state delta tags - elif (tag & 0x6f00) == TAG_GDELTA: - if global_: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grm' if (tag & 0xfff) == TAG_GRMDELTA - else 'gstate 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grmdelta' if (tag & 0xfff) == TAG_GRMDELTA - else 'gbmapdelta' if (tag & 0xfff) == TAG_GBMAPDELTA - else 'gdelta 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # name tags, includes file types - elif (tag & 0x6f00) == TAG_NAME: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'bname' if (tag & 0xfff) == TAG_BNAME - else 'reg' if (tag & 0xfff) == TAG_REG - else 'dir' if (tag & 0xfff) == TAG_DIR - else 'stickynote' if (tag & 0xfff) == TAG_STICKYNOTE - else 'bookmark' if (tag & 0xfff) == TAG_BOOKMARK - else 'mname' if (tag & 0xfff) == TAG_MNAME - else 'name 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # structure tags - elif (tag & 0x6f00) == TAG_STRUCT: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'branch' if (tag & 0xfff) == TAG_BRANCH - else 'data' if (tag & 0xfff) == TAG_DATA - else 'block' if (tag & 0xfff) == TAG_BLOCK - else 'did' if (tag & 0xfff) == TAG_DID - else 'bshrub' if (tag & 0xfff) == TAG_BSHRUB - else 'btree' if (tag & 0xfff) == TAG_BTREE - else 'mroot' if (tag & 0xfff) == TAG_MROOT - else 'mdir' if (tag & 0xfff) == TAG_MDIR - else 'mtree' if (tag & 0xfff) == TAG_MTREE - else 'bmfree' if (tag & 0xfff) == TAG_BMFREE - else 'bminuse' if (tag & 0xfff) == TAG_BMINUSE - else 'bmerased' if (tag & 0xfff) == TAG_BMERASED - else 'bmbad' if (tag & 0xfff) == TAG_BMBAD - else 'bmrange 0x%x' % (tag & 0xf) - if (tag & 0xff0) == TAG_BMRANGE - else 'struct 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # custom attributes - elif (tag & 0x6e00) == TAG_ATTR: - return '%s%sattr 0x%02x%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 's' if tag & 0x100 else 'u', - ((tag & 0x100) >> 1) + (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # alt pointers - elif tag & TAG_ALT: - return 'alt%s%s 0x%03x%s%s' % ( - 'r' if tag & TAG_R else 'b', - 'gt' if tag & TAG_GT else 'le', - tag & 0x0fff, - ' w%d' % weight if weight is not None else '', - ' 0x%x' % (0xffffffff & (toff-size)) - if size and toff is not None - else ' -%d' % size if size - else '') - # checksum tags - elif (tag & 0x7f00) == TAG_CKSUM: - return 'cksum%s%s%s%s%s' % ( - 'q%d' % (tag & 0x3), - 'p' if tag & TAG_PERTURB else '', - ' 0x%02x' % (tag & 0xff) if tag & 0xf8 else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # note tags - elif (tag & 0x7f00) == TAG_NOTE: - return 'note%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # erased-state checksum tags - elif (tag & 0x7f00) == TAG_ECKSUM: - return 'ecksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-checksum delta tags - elif (tag & 0x7f00) == TAG_GCKSUMDELTA: - if global_: - return 'gcksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return 'gcksumdelta%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # unknown tags - else: - return '0x%04x%s%s' % ( - tag, - ' w%d' % weight if weight is not None else '', - ' %d' % size if size is not None else '') - # compute the difference between two paths, returning everything # in a after the paths diverge, as well as the relevant index def pathdelta(a, b): @@ -571,7 +603,7 @@ class Rattr: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.size) + return Tag.repr(self.tag, self.weight, self.size) def __iter__(self): return iter((self.tag, self.weight, self.data)) @@ -650,7 +682,7 @@ class Ralt: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.jump, toff=self.toff) + return Tag.repr(self.tag, self.weight, self.jump, toff=self.toff) def __iter__(self): return iter((self.tag, self.weight, self.jump)) @@ -2831,7 +2863,7 @@ class Gstate: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, 0, self.size, global_=True) + return Tag.repr(self.tag, 0, self.size, global_=True) def __iter__(self): return iter((self.tag, self.data)) diff --git a/scripts/dbgbtree.py b/scripts/dbgbtree.py index 3150b610..224d070d 100755 --- a/scripts/dbgbtree.py +++ b/scripts/dbgbtree.py @@ -18,56 +18,223 @@ except ModuleNotFoundError: crc32c_lib = None -TAG_NULL = 0x0000 ## 0x0000 v--- ---- ---- ---- -TAG_CONFIG = 0x0000 ## 0x00tt v--- ---- -ttt tttt -TAG_MAGIC = 0x0031 # 0x003r v--- ---- --11 --rr -TAG_VERSION = 0x0034 # 0x0034 v--- ---- --11 -1-- -TAG_RCOMPAT = 0x0035 # 0x0035 v--- ---- --11 -1-1 -TAG_WCOMPAT = 0x0036 # 0x0036 v--- ---- --11 -11- -TAG_OCOMPAT = 0x0037 # 0x0037 v--- ---- --11 -111 -TAG_GEOMETRY = 0x0038 # 0x0038 v--- ---- --11 1--- -TAG_NAMELIMIT = 0x0039 # 0x0039 v--- ---- --11 1--1 -TAG_FILELIMIT = 0x003a # 0x003a v--- ---- --11 1-1- -TAG_GDELTA = 0x0100 ## 0x01tt v--- ---1 -ttt ttrr -TAG_GRMDELTA = 0x0100 # 0x0100 v--- ---1 ---- ---- -TAG_GBMAPDELTA = 0x0104 # 0x0104 v--- ---1 ---- -1rr -TAG_NAME = 0x0200 ## 0x02tt v--- --1- -ttt tttt -TAG_BNAME = 0x0200 # 0x0200 v--- --1- ---- ---- -TAG_REG = 0x0201 # 0x0201 v--- --1- ---- ---1 -TAG_DIR = 0x0202 # 0x0202 v--- --1- ---- --1- -TAG_STICKYNOTE = 0x0203 # 0x0203 v--- --1- ---- --11 -TAG_BOOKMARK = 0x0204 # 0x0204 v--- --1- ---- -1-- -TAG_MNAME = 0x0220 # 0x0220 v--- --1- --1- ---- -TAG_STRUCT = 0x0300 ## 0x03tt v--- --11 -ttt ttrr -TAG_BRANCH = 0x0300 # 0x030r v--- --11 ---- --rr -TAG_DATA = 0x0304 # 0x0304 v--- --11 ---- -1-- -TAG_BLOCK = 0x0308 # 0x0308 v--- --11 ---- 1err -TAG_DID = 0x0314 # 0x0314 v--- --11 ---1 -1-- -TAG_BSHRUB = 0x0318 # 0x0318 v--- --11 ---1 1--- -TAG_BTREE = 0x031c # 0x031c v--- --11 ---1 11rr -TAG_MROOT = 0x0321 # 0x032r v--- --11 --1- --rr -TAG_MDIR = 0x0325 # 0x0324 v--- --11 --1- -1rr -TAG_MTREE = 0x032c # 0x032c v--- --11 --1- 11rr -TAG_BMRANGE = 0x0330 # 0x033u v--- --11 --11 uuuu -TAG_BMFREE = 0x0330 # 0x0330 v--- --11 --11 ---- -TAG_BMINUSE = 0x0331 # 0x0331 v--- --11 --11 ---1 -TAG_BMERASED = 0x0332 # 0x0332 v--- --11 --11 --1- -TAG_BMBAD = 0x0333 # 0x0333 v--- --11 --11 --11 -TAG_ATTR = 0x0400 ## 0x04aa v--- -1-a -aaa aaaa -TAG_UATTR = 0x0400 # 0x04aa v--- -1-- -aaa aaaa -TAG_SATTR = 0x0500 # 0x05aa v--- -1-1 -aaa aaaa -TAG_SHRUB = 0x1000 ## 0x1kkk v--1 kkkk -kkk kkkk -TAG_ALT = 0x4000 ## 0x4kkk v1cd kkkk -kkk kkkk +TAG_NULL = 0x0000 ## v--- ---- +--- ---- +TAG_CONFIG = 0x0000 ## v--- ---- +ttt tttt +TAG_MAGIC = 0x0031 # v--- ---- +-11 --rr +TAG_VERSION = 0x0034 # v--- ---- +-11 -1-- +TAG_RCOMPAT = 0x0035 # v--- ---- +-11 -1-1 +TAG_WCOMPAT = 0x0036 # v--- ---- +-11 -11- +TAG_OCOMPAT = 0x0037 # v--- ---- +-11 -111 +TAG_GEOMETRY = 0x0038 # v--- ---- +-11 1--- +TAG_NAMELIMIT = 0x0039 # v--- ---- +-11 1--1 +TAG_FILELIMIT = 0x003a # v--- ---- +-11 1-1- +TAG_GDELTA = 0x0100 ## v--- ---1 +ttt ttrr +TAG_GRMDELTA = 0x0100 # v--- ---1 +--- ---- +TAG_GBMAPDELTA = 0x0104 # v--- ---1 +--- -1rr +TAG_NAME = 0x0200 ## v--- --1- +ttt tttt +TAG_BNAME = 0x0200 # v--- --1- +--- ---- +TAG_REG = 0x0201 # v--- --1- +--- ---1 +TAG_DIR = 0x0202 # v--- --1- +--- --1- +TAG_STICKYNOTE = 0x0203 # v--- --1- +--- --11 +TAG_BOOKMARK = 0x0204 # v--- --1- +--- -1-- +TAG_MNAME = 0x0220 # v--- --1- +-1- ---- +TAG_STRUCT = 0x0300 ## v--- --11 +ttt ttrr +TAG_BRANCH = 0x0300 # v--- --11 +--- --rr +TAG_DATA = 0x0304 # v--- --11 +--- -1-- +TAG_BLOCK = 0x0308 # v--- --11 +--- 1err +TAG_DID = 0x0314 # v--- --11 +--1 -1-- +TAG_BSHRUB = 0x0318 # v--- --11 +--1 1--- +TAG_BTREE = 0x031c # v--- --11 +--1 11rr +TAG_MROOT = 0x0321 # v--- --11 +-1- --rr +TAG_MDIR = 0x0325 # v--- --11 +-1- -1rr +TAG_MTREE = 0x032c # v--- --11 +-1- 11rr +TAG_BMRANGE = 0x0330 # v--- --11 +-11 ++uu +TAG_BMFREE = 0x0330 # v--- --11 +-11 ---- +TAG_BMINUSE = 0x0331 # v--- --11 +-11 ---1 +TAG_BMERASED = 0x0332 # v--- --11 +-11 --1- +TAG_BMBAD = 0x0333 # v--- --11 +-11 --11 +TAG_ATTR = 0x0400 ## v--- -1-a +aaa aaaa +TAG_UATTR = 0x0400 # v--- -1-- +aaa aaaa +TAG_SATTR = 0x0500 # v--- -1-1 +aaa aaaa +TAG_SHRUB = 0x1000 ## v--1 kkkk +kkk kkkk +TAG_ALT = 0x4000 ## v1cd kkkk +kkk kkkk TAG_B = 0x0000 TAG_R = 0x2000 TAG_LE = 0x0000 TAG_GT = 0x1000 -TAG_CKSUM = 0x3000 ## 0x300p v-11 ---- ---- -pqq +TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq TAG_PHASE = 0x0003 TAG_PERTURB = 0x0004 -TAG_NOTE = 0x3100 ## 0x3100 v-11 ---1 ---- ---- -TAG_ECKSUM = 0x3200 ## 0x3200 v-11 --1- ---- ---- -TAG_GCKSUMDELTA = 0x3300 ## 0x3300 v-11 --11 ---- ---- +TAG_NOTE = 0x3100 ## v-11 ---1 ++++ ++++ +TAG_ECKSUM = 0x3200 ## v-11 --1- ++++ ++++ +TAG_GCKSUMDELTA = 0x3300 ## v-11 --11 ++++ ++++ + + +# self-parsing tag repr +class Tag: + def __init__(self, name, tag, encoding, help): + self.name = name + self.tag = tag + self.encoding = encoding + self.help = help + # derive mask from encoding + self.mask = sum( + (1 if x in 'v-01' else 0) << len(self.encoding)-1-i + for i, x in enumerate(self.encoding)) + + def __repr__(self): + return 'Tag(%r, %r, %r)' % ( + self.name, + self.tag, + self.encoding) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + # substitute mask chars when zero + tag = '0x%s' % ''.join( + n if n != '0' else next( + (x for x in self.encoding[i*4:i*4+4] + if x not in 'v-01+'), + '0') + for i, n in enumerate('%04x' % self.tag)) + # group into nibbles + encoding = ' '.join(self.encoding[i*4:i*4+4] + for i in range(len(self.encoding)//4)) + return ('LFS3_%s' % self.name, tag, encoding) + + def specificity(self): + return sum(1 for x in self.encoding if x in 'v-01') + + def matches(self, tag): + return (tag & self.mask) == (self.tag & self.mask) + + def get(self, chars, tag): + return sum( + tag & ((1 if x in chars else 0) << len(self.encoding)-1-i) + for i, x in enumerate(self.encoding)) + + def max(self, chars): + return max(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def min(self, chars): + return min(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def width(self, chars): + return self.max(chars) - self.min(chars) + + def __contains__(self, chars): + return any(x in self.encoding for x in chars) + + @staticmethod + @ft.cache + def tags(): + # parse our script's source to figure out tags + import inspect + import re + tags = [] + tag_pattern = re.compile( + '^(?PTAG_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P(?:[^ ] *?){16}) *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = tag_pattern.match(line) + if m: + tags.append(Tag( + m.group('name'), + globals()[m.group('name')], + m.group('encoding').replace(' ', ''), + m.group('help'))) + return tags + + # find best matching tag + @staticmethod + def find(tag): + # find tags, note this is cached + tags__ = Tag.tags() + + # find the most specific matching tag, ignoring valid bits + return max((t for t in tags__ if t.matches(tag & 0x7fff)), + key=lambda t: t.specificity(), + default=None) + + # human readable tag repr + @staticmethod + def repr(tag, weight=None, size=None, *, + global_=False, + toff=None): + # find the most specific matching tag, ignoring the shrub bit + t = Tag.find(tag & ~(TAG_SHRUB if tag & 0x7000 == TAG_SHRUB else 0)) + + # build repr + r = [] + # normal tag? + if not tag & TAG_ALT: + if t is not None: + # prefix shrub tags with shrub + if tag & 0x7000 == TAG_SHRUB: + r.append('shrub') + # lowercase name + r.append(t.name.split('_', 1)[1].lower()) + # gstate tag? + if global_: + if r[-1] == 'gdelta': + r[-1] = 'gstate' + elif r[-1].endswith('delta'): + r[-1] = r[-1][:-len('delta')] + # include perturb/phase bits + if 'q' in t: + r.append('q%d' % t.get('q', tag)) + if 'p' in t and tag & TAG_PERTURB: + r.append('p') + + # include unmatched fields, but not just redund, and + # only reserved bits if non-zero + if 'tua' in t or ('+' in t and t.get('+', tag) != 0): + r.append(' 0x%0*x' % ( + (t.width('tuar+')+4-1)//4, + t.get('tuar+', tag))) + # unknown tag? + else: + r.append('0x%04x' % tag) + + # weight? + if weight: + r.append(' w%d' % weight) + # size? don't include if null + if size is not None and (size or tag & 0x7fff): + r.append(' %d' % size) + + # alt pointer? + else: + r.append('alt') + r.append('r' if tag & TAG_R else 'b') + r.append('gt' if tag & TAG_GT else 'le') + r.append(' 0x%0*x' % ( + (t.width('k')+4-1)//4, + t.get('k', tag))) + + # weight? + if weight is not None: + r.append(' w%d' % weight) + # jump? + if size and toff is not None: + r.append(' 0x%x' % (0xffffffff & (toff-size))) + elif size: + r.append(' -%d' % size) + + return ''.join(r) # some ways of block geometry representations @@ -186,141 +353,6 @@ def xxd(data, width=16): b if b >= ' ' and b <= '~' else '.' for b in map(chr, data[i:i+width]))) -# human readable tag repr -def tagrepr(tag, weight=None, size=None, *, - global_=False, - toff=None): - # null tags - if (tag & 0x6fff) == TAG_NULL: - return '%snull%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - ' w%d' % weight if weight else '', - ' %d' % size if size else '') - # config tags - elif (tag & 0x6f00) == TAG_CONFIG: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'magic' if (tag & 0xfff) == TAG_MAGIC - else 'version' if (tag & 0xfff) == TAG_VERSION - else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT - else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT - else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY - else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT - else 'filelimit' if (tag & 0xfff) == TAG_FILELIMIT - else 'config 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-state delta tags - elif (tag & 0x6f00) == TAG_GDELTA: - if global_: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grm' if (tag & 0xfff) == TAG_GRMDELTA - else 'gstate 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grmdelta' if (tag & 0xfff) == TAG_GRMDELTA - else 'gbmapdelta' if (tag & 0xfff) == TAG_GBMAPDELTA - else 'gdelta 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # name tags, includes file types - elif (tag & 0x6f00) == TAG_NAME: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'bname' if (tag & 0xfff) == TAG_BNAME - else 'reg' if (tag & 0xfff) == TAG_REG - else 'dir' if (tag & 0xfff) == TAG_DIR - else 'stickynote' if (tag & 0xfff) == TAG_STICKYNOTE - else 'bookmark' if (tag & 0xfff) == TAG_BOOKMARK - else 'mname' if (tag & 0xfff) == TAG_MNAME - else 'name 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # structure tags - elif (tag & 0x6f00) == TAG_STRUCT: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'branch' if (tag & 0xfff) == TAG_BRANCH - else 'data' if (tag & 0xfff) == TAG_DATA - else 'block' if (tag & 0xfff) == TAG_BLOCK - else 'did' if (tag & 0xfff) == TAG_DID - else 'bshrub' if (tag & 0xfff) == TAG_BSHRUB - else 'btree' if (tag & 0xfff) == TAG_BTREE - else 'mroot' if (tag & 0xfff) == TAG_MROOT - else 'mdir' if (tag & 0xfff) == TAG_MDIR - else 'mtree' if (tag & 0xfff) == TAG_MTREE - else 'bmfree' if (tag & 0xfff) == TAG_BMFREE - else 'bminuse' if (tag & 0xfff) == TAG_BMINUSE - else 'bmerased' if (tag & 0xfff) == TAG_BMERASED - else 'bmbad' if (tag & 0xfff) == TAG_BMBAD - else 'bmrange 0x%x' % (tag & 0xf) - if (tag & 0xff0) == TAG_BMRANGE - else 'struct 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # custom attributes - elif (tag & 0x6e00) == TAG_ATTR: - return '%s%sattr 0x%02x%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 's' if tag & 0x100 else 'u', - ((tag & 0x100) >> 1) + (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # alt pointers - elif tag & TAG_ALT: - return 'alt%s%s 0x%03x%s%s' % ( - 'r' if tag & TAG_R else 'b', - 'gt' if tag & TAG_GT else 'le', - tag & 0x0fff, - ' w%d' % weight if weight is not None else '', - ' 0x%x' % (0xffffffff & (toff-size)) - if size and toff is not None - else ' -%d' % size if size - else '') - # checksum tags - elif (tag & 0x7f00) == TAG_CKSUM: - return 'cksum%s%s%s%s%s' % ( - 'q%d' % (tag & 0x3), - 'p' if tag & TAG_PERTURB else '', - ' 0x%02x' % (tag & 0xff) if tag & 0xf8 else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # note tags - elif (tag & 0x7f00) == TAG_NOTE: - return 'note%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # erased-state checksum tags - elif (tag & 0x7f00) == TAG_ECKSUM: - return 'ecksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-checksum delta tags - elif (tag & 0x7f00) == TAG_GCKSUMDELTA: - if global_: - return 'gcksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return 'gcksumdelta%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # unknown tags - else: - return '0x%04x%s%s' % ( - tag, - ' w%d' % weight if weight is not None else '', - ' %d' % size if size is not None else '') - # compute the difference between two paths, returning everything # in a after the paths diverge, as well as the relevant index def pathdelta(a, b): @@ -399,7 +431,7 @@ class Rattr: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.size) + return Tag.repr(self.tag, self.weight, self.size) def __iter__(self): return iter((self.tag, self.weight, self.data)) @@ -478,7 +510,7 @@ class Ralt: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.jump, toff=self.toff) + return Tag.repr(self.tag, self.weight, self.jump, toff=self.toff) def __iter__(self): return iter((self.tag, self.weight, self.jump)) diff --git a/scripts/dbgerr.py b/scripts/dbgerr.py index 6ada8c87..56cb4d90 100755 --- a/scripts/dbgerr.py +++ b/scripts/dbgerr.py @@ -52,25 +52,27 @@ class Err: def line(self): return ('LFS3_%s' % self.name, '%d' % self.code, self.help) -@ft.cache -def errs(): - # parse our script's source to figure out errs - import inspect - import re - errs = [] - err_pattern = re.compile( - '^(?PERR_[^ ]*) *= *(?P[^#]*?) *' - '#+ *(?P.*)$') - for line in (inspect.getsource(inspect.getmodule(inspect.currentframe())) - .replace('\\\n', '') - .splitlines()): - m = err_pattern.match(line) - if m: - errs.append(Err( - m.group('name'), - globals()[m.group('name')], - m.group('help'))) - return errs + @staticmethod + @ft.cache + def errs(): + # parse our script's source to figure out errs + import inspect + import re + errs = [] + err_pattern = re.compile( + '^(?PERR_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = err_pattern.match(line) + if m: + errs.append(Err( + m.group('name'), + globals()[m.group('name')], + m.group('help'))) + return errs def main(errs, *, @@ -79,7 +81,7 @@ def main(errs, *, list_, list = list, builtins.list # find errs - errs__ = globals()['errs']() + errs__ = Err.errs() lines = [] # list all known error codes diff --git a/scripts/dbgflags.py b/scripts/dbgflags.py index 7e486d08..b9f69a3b 100755 --- a/scripts/dbgflags.py +++ b/scripts/dbgflags.py @@ -236,25 +236,27 @@ class Prefix: def __hash__(self): return hash(self.name) -@ft.cache -def prefixes(): - # parse our script's source to figure out prefixes - import inspect - import re - prefixes = [] - prefix_pattern = re.compile( - '^(?PPREFIX_[^ ]*) *= *(?P[^#]*?) *' - '#+ *(?P.*)$') - for line in (inspect.getsource(inspect.getmodule(inspect.currentframe())) - .replace('\\\n', '') - .splitlines()): - m = prefix_pattern.match(line) - if m: - prefixes.append(Prefix( - m.group('name'), - globals()[m.group('name')], - m.group('help'))) - return prefixes + @staticmethod + @ft.cache + def prefixes(): + # parse our script's source to figure out prefixes + import inspect + import re + prefixes = [] + prefix_pattern = re.compile( + '^(?PPREFIX_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = prefix_pattern.match(line) + if m: + prefixes.append(Prefix( + m.group('name'), + globals()[m.group('name')], + m.group('help'))) + return prefixes # self-parsing flags class Flag: @@ -291,45 +293,49 @@ class Flag: def line(self): return ('LFS3_%s' % self.name, '0x%08x' % self.flag, self.help) -@ft.cache -def flags(): - # parse our script's source to figure out flags - import inspect - import re + @staticmethod + @ft.cache + def flags(): + # parse our script's source to figure out flags + import inspect + import re - # limit to known prefixes - prefixes_ = {p.name.split('_', 1)[1].upper(): p for p in prefixes()} - # keep track of last mask - mask_ = None + # limit to known prefixes + prefixes_ = {p.name.split('_', 1)[1].upper(): p + for p in Prefix.prefixes()} + # keep track of last mask + mask_ = None - flags = [] - flag_pattern = re.compile( - '^(?P(?i:%s)_[^ ]*) ' - '*= *(?P[^#]*?) *' - '#+ (?P[^ ]+) *(?P.*)$' - % '|'.join(prefixes_.keys())) - for line in (inspect.getsource(inspect.getmodule(inspect.currentframe())) - .replace('\\\n', '') - .splitlines()): - m = flag_pattern.match(line) - if m: - flags.append(Flag( - m.group('name'), - globals()[m.group('name')], - m.group('help'), - # associate flags -> prefix - prefix=prefixes_[m.group('name').split('_', 1)[0].upper()], - yes='y' in m.group('mode'), - internal='i' in m.group('mode'), - mask='m' in m.group('mode'), - # associate types -> mask - type=mask_ if '^' in m.group('mode') else False)) + flags = [] + flag_pattern = re.compile( + '^(?P(?i:%s)_[^ ]*) ' + '*= *(?P[^#]*?) *' + '#+ (?P[^ ]+) *(?P.*)$' + % '|'.join(prefixes_.keys())) + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = flag_pattern.match(line) + if m: + flags.append(Flag( + m.group('name'), + globals()[m.group('name')], + m.group('help'), + # associate flags -> prefix + prefix=prefixes_[ + m.group('name').split('_', 1)[0].upper()], + yes='y' in m.group('mode'), + internal='i' in m.group('mode'), + mask='m' in m.group('mode'), + # associate types -> mask + type=mask_ if '^' in m.group('mode') else False)) - # keep track of last mask - if flags[-1].mask: - mask_ = flags[-1] + # keep track of last mask + if flags[-1].mask: + mask_ = flags[-1] - return flags + return flags def main(flags, *, @@ -341,7 +347,7 @@ def main(flags, *, all_, all = all, builtins.all # find flags - flags__ = globals()['flags']() + flags__ = Flag.flags() # filter by prefixes if there are any prefixes if prefixes: @@ -441,7 +447,7 @@ if __name__ == "__main__": if getattr(namespace, 'prefixes', None) is None: namespace.prefixes = [] namespace.prefixes.append(self.const) - for p in prefixes(): + for p in Prefix.prefixes(): parser.add_argument( *p.aliases, action=AppendPrefix, diff --git a/scripts/dbglfs3.py b/scripts/dbglfs3.py index 4845b186..9f06669e 100755 --- a/scripts/dbglfs3.py +++ b/scripts/dbglfs3.py @@ -19,57 +19,6 @@ except ModuleNotFoundError: crc32c_lib = None -TAG_NULL = 0x0000 ## 0x0000 v--- ---- ---- ---- -TAG_CONFIG = 0x0000 ## 0x00tt v--- ---- -ttt tttt -TAG_MAGIC = 0x0031 # 0x003r v--- ---- --11 --rr -TAG_VERSION = 0x0034 # 0x0034 v--- ---- --11 -1-- -TAG_RCOMPAT = 0x0035 # 0x0035 v--- ---- --11 -1-1 -TAG_WCOMPAT = 0x0036 # 0x0036 v--- ---- --11 -11- -TAG_OCOMPAT = 0x0037 # 0x0037 v--- ---- --11 -111 -TAG_GEOMETRY = 0x0038 # 0x0038 v--- ---- --11 1--- -TAG_NAMELIMIT = 0x0039 # 0x0039 v--- ---- --11 1--1 -TAG_FILELIMIT = 0x003a # 0x003a v--- ---- --11 1-1- -TAG_GDELTA = 0x0100 ## 0x01tt v--- ---1 -ttt ttrr -TAG_GRMDELTA = 0x0100 # 0x0100 v--- ---1 ---- ---- -TAG_GBMAPDELTA = 0x0104 # 0x0104 v--- ---1 ---- -1rr -TAG_NAME = 0x0200 ## 0x02tt v--- --1- -ttt tttt -TAG_BNAME = 0x0200 # 0x0200 v--- --1- ---- ---- -TAG_REG = 0x0201 # 0x0201 v--- --1- ---- ---1 -TAG_DIR = 0x0202 # 0x0202 v--- --1- ---- --1- -TAG_STICKYNOTE = 0x0203 # 0x0203 v--- --1- ---- --11 -TAG_BOOKMARK = 0x0204 # 0x0204 v--- --1- ---- -1-- -TAG_MNAME = 0x0220 # 0x0220 v--- --1- --1- ---- -TAG_STRUCT = 0x0300 ## 0x03tt v--- --11 -ttt ttrr -TAG_BRANCH = 0x0300 # 0x030r v--- --11 ---- --rr -TAG_DATA = 0x0304 # 0x0304 v--- --11 ---- -1-- -TAG_BLOCK = 0x0308 # 0x0308 v--- --11 ---- 1err -TAG_DID = 0x0314 # 0x0314 v--- --11 ---1 -1-- -TAG_BSHRUB = 0x0318 # 0x0318 v--- --11 ---1 1--- -TAG_BTREE = 0x031c # 0x031c v--- --11 ---1 11rr -TAG_MROOT = 0x0321 # 0x032r v--- --11 --1- --rr -TAG_MDIR = 0x0325 # 0x0324 v--- --11 --1- -1rr -TAG_MTREE = 0x032c # 0x032c v--- --11 --1- 11rr -TAG_BMRANGE = 0x0330 # 0x033u v--- --11 --11 uuuu -TAG_BMFREE = 0x0330 # 0x0330 v--- --11 --11 ---- -TAG_BMINUSE = 0x0331 # 0x0331 v--- --11 --11 ---1 -TAG_BMERASED = 0x0332 # 0x0332 v--- --11 --11 --1- -TAG_BMBAD = 0x0333 # 0x0333 v--- --11 --11 --11 -TAG_ATTR = 0x0400 ## 0x04aa v--- -1-a -aaa aaaa -TAG_UATTR = 0x0400 # 0x04aa v--- -1-- -aaa aaaa -TAG_SATTR = 0x0500 # 0x05aa v--- -1-1 -aaa aaaa -TAG_SHRUB = 0x1000 ## 0x1kkk v--1 kkkk -kkk kkkk -TAG_ALT = 0x4000 ## 0x4kkk v1cd kkkk -kkk kkkk -TAG_B = 0x0000 -TAG_R = 0x2000 -TAG_LE = 0x0000 -TAG_GT = 0x1000 -TAG_CKSUM = 0x3000 ## 0x300p v-11 ---- ---- -pqq -TAG_PHASE = 0x0003 -TAG_PERTURB = 0x0004 -TAG_NOTE = 0x3100 ## 0x3100 v-11 ---1 ---- ---- -TAG_ECKSUM = 0x3200 ## 0x3200 v-11 --1- ---- ---- -TAG_GCKSUMDELTA = 0x3300 ## 0x3300 v-11 --11 ---- ---- - RCOMPAT_NONSTANDARD = 0x00000001 # Non-standard filesystem format RCOMPAT_WRONLY = 0x00000002 # Reading is disallowed RCOMPAT_BMOSS = 0x00000010 # Files may use inlined data @@ -88,6 +37,224 @@ WCOMPAT_DIR = 0x00000010 # Directory file types in use WCOMPAT_GCKSUM = 0x00001000 # Global-checksum in use WCOMPAT_GBMAP = 0x00002000 # Global on-disk block-map in use +TAG_NULL = 0x0000 ## v--- ---- +--- ---- +TAG_CONFIG = 0x0000 ## v--- ---- +ttt tttt +TAG_MAGIC = 0x0031 # v--- ---- +-11 --rr +TAG_VERSION = 0x0034 # v--- ---- +-11 -1-- +TAG_RCOMPAT = 0x0035 # v--- ---- +-11 -1-1 +TAG_WCOMPAT = 0x0036 # v--- ---- +-11 -11- +TAG_OCOMPAT = 0x0037 # v--- ---- +-11 -111 +TAG_GEOMETRY = 0x0038 # v--- ---- +-11 1--- +TAG_NAMELIMIT = 0x0039 # v--- ---- +-11 1--1 +TAG_FILELIMIT = 0x003a # v--- ---- +-11 1-1- +TAG_GDELTA = 0x0100 ## v--- ---1 +ttt ttrr +TAG_GRMDELTA = 0x0100 # v--- ---1 +--- ---- +TAG_GBMAPDELTA = 0x0104 # v--- ---1 +--- -1rr +TAG_NAME = 0x0200 ## v--- --1- +ttt tttt +TAG_BNAME = 0x0200 # v--- --1- +--- ---- +TAG_REG = 0x0201 # v--- --1- +--- ---1 +TAG_DIR = 0x0202 # v--- --1- +--- --1- +TAG_STICKYNOTE = 0x0203 # v--- --1- +--- --11 +TAG_BOOKMARK = 0x0204 # v--- --1- +--- -1-- +TAG_MNAME = 0x0220 # v--- --1- +-1- ---- +TAG_STRUCT = 0x0300 ## v--- --11 +ttt ttrr +TAG_BRANCH = 0x0300 # v--- --11 +--- --rr +TAG_DATA = 0x0304 # v--- --11 +--- -1-- +TAG_BLOCK = 0x0308 # v--- --11 +--- 1err +TAG_DID = 0x0314 # v--- --11 +--1 -1-- +TAG_BSHRUB = 0x0318 # v--- --11 +--1 1--- +TAG_BTREE = 0x031c # v--- --11 +--1 11rr +TAG_MROOT = 0x0321 # v--- --11 +-1- --rr +TAG_MDIR = 0x0325 # v--- --11 +-1- -1rr +TAG_MTREE = 0x032c # v--- --11 +-1- 11rr +TAG_BMRANGE = 0x0330 # v--- --11 +-11 ++uu +TAG_BMFREE = 0x0330 # v--- --11 +-11 ---- +TAG_BMINUSE = 0x0331 # v--- --11 +-11 ---1 +TAG_BMERASED = 0x0332 # v--- --11 +-11 --1- +TAG_BMBAD = 0x0333 # v--- --11 +-11 --11 +TAG_ATTR = 0x0400 ## v--- -1-a +aaa aaaa +TAG_UATTR = 0x0400 # v--- -1-- +aaa aaaa +TAG_SATTR = 0x0500 # v--- -1-1 +aaa aaaa +TAG_SHRUB = 0x1000 ## v--1 kkkk +kkk kkkk +TAG_ALT = 0x4000 ## v1cd kkkk +kkk kkkk +TAG_B = 0x0000 +TAG_R = 0x2000 +TAG_LE = 0x0000 +TAG_GT = 0x1000 +TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq +TAG_PHASE = 0x0003 +TAG_PERTURB = 0x0004 +TAG_NOTE = 0x3100 ## v-11 ---1 ++++ ++++ +TAG_ECKSUM = 0x3200 ## v-11 --1- ++++ ++++ +TAG_GCKSUMDELTA = 0x3300 ## v-11 --11 ++++ ++++ + + +# self-parsing tag repr +class Tag: + def __init__(self, name, tag, encoding, help): + self.name = name + self.tag = tag + self.encoding = encoding + self.help = help + # derive mask from encoding + self.mask = sum( + (1 if x in 'v-01' else 0) << len(self.encoding)-1-i + for i, x in enumerate(self.encoding)) + + def __repr__(self): + return 'Tag(%r, %r, %r)' % ( + self.name, + self.tag, + self.encoding) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + # substitute mask chars when zero + tag = '0x%s' % ''.join( + n if n != '0' else next( + (x for x in self.encoding[i*4:i*4+4] + if x not in 'v-01+'), + '0') + for i, n in enumerate('%04x' % self.tag)) + # group into nibbles + encoding = ' '.join(self.encoding[i*4:i*4+4] + for i in range(len(self.encoding)//4)) + return ('LFS3_%s' % self.name, tag, encoding) + + def specificity(self): + return sum(1 for x in self.encoding if x in 'v-01') + + def matches(self, tag): + return (tag & self.mask) == (self.tag & self.mask) + + def get(self, chars, tag): + return sum( + tag & ((1 if x in chars else 0) << len(self.encoding)-1-i) + for i, x in enumerate(self.encoding)) + + def max(self, chars): + return max(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def min(self, chars): + return min(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def width(self, chars): + return self.max(chars) - self.min(chars) + + def __contains__(self, chars): + return any(x in self.encoding for x in chars) + + @staticmethod + @ft.cache + def tags(): + # parse our script's source to figure out tags + import inspect + import re + tags = [] + tag_pattern = re.compile( + '^(?PTAG_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P(?:[^ ] *?){16}) *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = tag_pattern.match(line) + if m: + tags.append(Tag( + m.group('name'), + globals()[m.group('name')], + m.group('encoding').replace(' ', ''), + m.group('help'))) + return tags + + # find best matching tag + @staticmethod + def find(tag): + # find tags, note this is cached + tags__ = Tag.tags() + + # find the most specific matching tag, ignoring valid bits + return max((t for t in tags__ if t.matches(tag & 0x7fff)), + key=lambda t: t.specificity(), + default=None) + + # human readable tag repr + @staticmethod + def repr(tag, weight=None, size=None, *, + global_=False, + toff=None): + # find the most specific matching tag, ignoring the shrub bit + t = Tag.find(tag & ~(TAG_SHRUB if tag & 0x7000 == TAG_SHRUB else 0)) + + # build repr + r = [] + # normal tag? + if not tag & TAG_ALT: + if t is not None: + # prefix shrub tags with shrub + if tag & 0x7000 == TAG_SHRUB: + r.append('shrub') + # lowercase name + r.append(t.name.split('_', 1)[1].lower()) + # gstate tag? + if global_: + if r[-1] == 'gdelta': + r[-1] = 'gstate' + elif r[-1].endswith('delta'): + r[-1] = r[-1][:-len('delta')] + # include perturb/phase bits + if 'q' in t: + r.append('q%d' % t.get('q', tag)) + if 'p' in t and tag & TAG_PERTURB: + r.append('p') + + # include unmatched fields, but not just redund, and + # only reserved bits if non-zero + if 'tua' in t or ('+' in t and t.get('+', tag) != 0): + r.append(' 0x%0*x' % ( + (t.width('tuar+')+4-1)//4, + t.get('tuar+', tag))) + # unknown tag? + else: + r.append('0x%04x' % tag) + + # weight? + if weight: + r.append(' w%d' % weight) + # size? don't include if null + if size is not None and (size or tag & 0x7fff): + r.append(' %d' % size) + + # alt pointer? + else: + r.append('alt') + r.append('r' if tag & TAG_R else 'b') + r.append('gt' if tag & TAG_GT else 'le') + r.append(' 0x%0*x' % ( + (t.width('k')+4-1)//4, + t.get('k', tag))) + + # weight? + if weight is not None: + r.append(' w%d' % weight) + # jump? + if size and toff is not None: + r.append(' 0x%x' % (0xffffffff & (toff-size))) + elif size: + r.append(' -%d' % size) + + return ''.join(r) + # some ways of block geometry representations # 512 -> 512 @@ -253,141 +420,6 @@ def xxd(data, width=16): b if b >= ' ' and b <= '~' else '.' for b in map(chr, data[i:i+width]))) -# human readable tag repr -def tagrepr(tag, weight=None, size=None, *, - global_=False, - toff=None): - # null tags - if (tag & 0x6fff) == TAG_NULL: - return '%snull%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - ' w%d' % weight if weight else '', - ' %d' % size if size else '') - # config tags - elif (tag & 0x6f00) == TAG_CONFIG: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'magic' if (tag & 0xfff) == TAG_MAGIC - else 'version' if (tag & 0xfff) == TAG_VERSION - else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT - else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT - else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY - else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT - else 'filelimit' if (tag & 0xfff) == TAG_FILELIMIT - else 'config 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-state delta tags - elif (tag & 0x6f00) == TAG_GDELTA: - if global_: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grm' if (tag & 0xfff) == TAG_GRMDELTA - else 'gstate 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grmdelta' if (tag & 0xfff) == TAG_GRMDELTA - else 'gbmapdelta' if (tag & 0xfff) == TAG_GBMAPDELTA - else 'gdelta 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # name tags, includes file types - elif (tag & 0x6f00) == TAG_NAME: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'bname' if (tag & 0xfff) == TAG_BNAME - else 'reg' if (tag & 0xfff) == TAG_REG - else 'dir' if (tag & 0xfff) == TAG_DIR - else 'stickynote' if (tag & 0xfff) == TAG_STICKYNOTE - else 'bookmark' if (tag & 0xfff) == TAG_BOOKMARK - else 'mname' if (tag & 0xfff) == TAG_MNAME - else 'name 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # structure tags - elif (tag & 0x6f00) == TAG_STRUCT: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'branch' if (tag & 0xfff) == TAG_BRANCH - else 'data' if (tag & 0xfff) == TAG_DATA - else 'block' if (tag & 0xfff) == TAG_BLOCK - else 'did' if (tag & 0xfff) == TAG_DID - else 'bshrub' if (tag & 0xfff) == TAG_BSHRUB - else 'btree' if (tag & 0xfff) == TAG_BTREE - else 'mroot' if (tag & 0xfff) == TAG_MROOT - else 'mdir' if (tag & 0xfff) == TAG_MDIR - else 'mtree' if (tag & 0xfff) == TAG_MTREE - else 'bmfree' if (tag & 0xfff) == TAG_BMFREE - else 'bminuse' if (tag & 0xfff) == TAG_BMINUSE - else 'bmerased' if (tag & 0xfff) == TAG_BMERASED - else 'bmbad' if (tag & 0xfff) == TAG_BMBAD - else 'bmrange 0x%x' % (tag & 0xf) - if (tag & 0xff0) == TAG_BMRANGE - else 'struct 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # custom attributes - elif (tag & 0x6e00) == TAG_ATTR: - return '%s%sattr 0x%02x%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 's' if tag & 0x100 else 'u', - ((tag & 0x100) >> 1) + (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # alt pointers - elif tag & TAG_ALT: - return 'alt%s%s 0x%03x%s%s' % ( - 'r' if tag & TAG_R else 'b', - 'gt' if tag & TAG_GT else 'le', - tag & 0x0fff, - ' w%d' % weight if weight is not None else '', - ' 0x%x' % (0xffffffff & (toff-size)) - if size and toff is not None - else ' -%d' % size if size - else '') - # checksum tags - elif (tag & 0x7f00) == TAG_CKSUM: - return 'cksum%s%s%s%s%s' % ( - 'q%d' % (tag & 0x3), - 'p' if tag & TAG_PERTURB else '', - ' 0x%02x' % (tag & 0xff) if tag & 0xf8 else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # note tags - elif (tag & 0x7f00) == TAG_NOTE: - return 'note%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # erased-state checksum tags - elif (tag & 0x7f00) == TAG_ECKSUM: - return 'ecksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-checksum delta tags - elif (tag & 0x7f00) == TAG_GCKSUMDELTA: - if global_: - return 'gcksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return 'gcksumdelta%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # unknown tags - else: - return '0x%04x%s%s' % ( - tag, - ' w%d' % weight if weight is not None else '', - ' %d' % size if size is not None else '') - # compute the difference between two paths, returning everything # in a after the paths diverge, as well as the relevant index def pathdelta(a, b): @@ -466,7 +498,7 @@ class Rattr: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.size) + return Tag.repr(self.tag, self.weight, self.size) def __iter__(self): return iter((self.tag, self.weight, self.data)) @@ -545,7 +577,7 @@ class Ralt: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.jump, toff=self.toff) + return Tag.repr(self.tag, self.weight, self.jump, toff=self.toff) def __iter__(self): return iter((self.tag, self.weight, self.jump)) @@ -2726,7 +2758,7 @@ class Gstate: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, 0, self.size, global_=True) + return Tag.repr(self.tag, 0, self.size, global_=True) def __iter__(self): return iter((self.tag, self.data)) diff --git a/scripts/dbgmtree.py b/scripts/dbgmtree.py index 8bbba8c6..af559d75 100755 --- a/scripts/dbgmtree.py +++ b/scripts/dbgmtree.py @@ -18,56 +18,223 @@ except ModuleNotFoundError: crc32c_lib = None -TAG_NULL = 0x0000 ## 0x0000 v--- ---- ---- ---- -TAG_CONFIG = 0x0000 ## 0x00tt v--- ---- -ttt tttt -TAG_MAGIC = 0x0031 # 0x003r v--- ---- --11 --rr -TAG_VERSION = 0x0034 # 0x0034 v--- ---- --11 -1-- -TAG_RCOMPAT = 0x0035 # 0x0035 v--- ---- --11 -1-1 -TAG_WCOMPAT = 0x0036 # 0x0036 v--- ---- --11 -11- -TAG_OCOMPAT = 0x0037 # 0x0037 v--- ---- --11 -111 -TAG_GEOMETRY = 0x0038 # 0x0038 v--- ---- --11 1--- -TAG_NAMELIMIT = 0x0039 # 0x0039 v--- ---- --11 1--1 -TAG_FILELIMIT = 0x003a # 0x003a v--- ---- --11 1-1- -TAG_GDELTA = 0x0100 ## 0x01tt v--- ---1 -ttt ttrr -TAG_GRMDELTA = 0x0100 # 0x0100 v--- ---1 ---- ---- -TAG_GBMAPDELTA = 0x0104 # 0x0104 v--- ---1 ---- -1rr -TAG_NAME = 0x0200 ## 0x02tt v--- --1- -ttt tttt -TAG_BNAME = 0x0200 # 0x0200 v--- --1- ---- ---- -TAG_REG = 0x0201 # 0x0201 v--- --1- ---- ---1 -TAG_DIR = 0x0202 # 0x0202 v--- --1- ---- --1- -TAG_STICKYNOTE = 0x0203 # 0x0203 v--- --1- ---- --11 -TAG_BOOKMARK = 0x0204 # 0x0204 v--- --1- ---- -1-- -TAG_MNAME = 0x0220 # 0x0220 v--- --1- --1- ---- -TAG_STRUCT = 0x0300 ## 0x03tt v--- --11 -ttt ttrr -TAG_BRANCH = 0x0300 # 0x030r v--- --11 ---- --rr -TAG_DATA = 0x0304 # 0x0304 v--- --11 ---- -1-- -TAG_BLOCK = 0x0308 # 0x0308 v--- --11 ---- 1err -TAG_DID = 0x0314 # 0x0314 v--- --11 ---1 -1-- -TAG_BSHRUB = 0x0318 # 0x0318 v--- --11 ---1 1--- -TAG_BTREE = 0x031c # 0x031c v--- --11 ---1 11rr -TAG_MROOT = 0x0321 # 0x032r v--- --11 --1- --rr -TAG_MDIR = 0x0325 # 0x0324 v--- --11 --1- -1rr -TAG_MTREE = 0x032c # 0x032c v--- --11 --1- 11rr -TAG_BMRANGE = 0x0330 # 0x033u v--- --11 --11 uuuu -TAG_BMFREE = 0x0330 # 0x0330 v--- --11 --11 ---- -TAG_BMINUSE = 0x0331 # 0x0331 v--- --11 --11 ---1 -TAG_BMERASED = 0x0332 # 0x0332 v--- --11 --11 --1- -TAG_BMBAD = 0x0333 # 0x0333 v--- --11 --11 --11 -TAG_ATTR = 0x0400 ## 0x04aa v--- -1-a -aaa aaaa -TAG_UATTR = 0x0400 # 0x04aa v--- -1-- -aaa aaaa -TAG_SATTR = 0x0500 # 0x05aa v--- -1-1 -aaa aaaa -TAG_SHRUB = 0x1000 ## 0x1kkk v--1 kkkk -kkk kkkk -TAG_ALT = 0x4000 ## 0x4kkk v1cd kkkk -kkk kkkk +TAG_NULL = 0x0000 ## v--- ---- +--- ---- +TAG_CONFIG = 0x0000 ## v--- ---- +ttt tttt +TAG_MAGIC = 0x0031 # v--- ---- +-11 --rr +TAG_VERSION = 0x0034 # v--- ---- +-11 -1-- +TAG_RCOMPAT = 0x0035 # v--- ---- +-11 -1-1 +TAG_WCOMPAT = 0x0036 # v--- ---- +-11 -11- +TAG_OCOMPAT = 0x0037 # v--- ---- +-11 -111 +TAG_GEOMETRY = 0x0038 # v--- ---- +-11 1--- +TAG_NAMELIMIT = 0x0039 # v--- ---- +-11 1--1 +TAG_FILELIMIT = 0x003a # v--- ---- +-11 1-1- +TAG_GDELTA = 0x0100 ## v--- ---1 +ttt ttrr +TAG_GRMDELTA = 0x0100 # v--- ---1 +--- ---- +TAG_GBMAPDELTA = 0x0104 # v--- ---1 +--- -1rr +TAG_NAME = 0x0200 ## v--- --1- +ttt tttt +TAG_BNAME = 0x0200 # v--- --1- +--- ---- +TAG_REG = 0x0201 # v--- --1- +--- ---1 +TAG_DIR = 0x0202 # v--- --1- +--- --1- +TAG_STICKYNOTE = 0x0203 # v--- --1- +--- --11 +TAG_BOOKMARK = 0x0204 # v--- --1- +--- -1-- +TAG_MNAME = 0x0220 # v--- --1- +-1- ---- +TAG_STRUCT = 0x0300 ## v--- --11 +ttt ttrr +TAG_BRANCH = 0x0300 # v--- --11 +--- --rr +TAG_DATA = 0x0304 # v--- --11 +--- -1-- +TAG_BLOCK = 0x0308 # v--- --11 +--- 1err +TAG_DID = 0x0314 # v--- --11 +--1 -1-- +TAG_BSHRUB = 0x0318 # v--- --11 +--1 1--- +TAG_BTREE = 0x031c # v--- --11 +--1 11rr +TAG_MROOT = 0x0321 # v--- --11 +-1- --rr +TAG_MDIR = 0x0325 # v--- --11 +-1- -1rr +TAG_MTREE = 0x032c # v--- --11 +-1- 11rr +TAG_BMRANGE = 0x0330 # v--- --11 +-11 ++uu +TAG_BMFREE = 0x0330 # v--- --11 +-11 ---- +TAG_BMINUSE = 0x0331 # v--- --11 +-11 ---1 +TAG_BMERASED = 0x0332 # v--- --11 +-11 --1- +TAG_BMBAD = 0x0333 # v--- --11 +-11 --11 +TAG_ATTR = 0x0400 ## v--- -1-a +aaa aaaa +TAG_UATTR = 0x0400 # v--- -1-- +aaa aaaa +TAG_SATTR = 0x0500 # v--- -1-1 +aaa aaaa +TAG_SHRUB = 0x1000 ## v--1 kkkk +kkk kkkk +TAG_ALT = 0x4000 ## v1cd kkkk +kkk kkkk TAG_B = 0x0000 TAG_R = 0x2000 TAG_LE = 0x0000 TAG_GT = 0x1000 -TAG_CKSUM = 0x3000 ## 0x300p v-11 ---- ---- -pqq +TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq TAG_PHASE = 0x0003 TAG_PERTURB = 0x0004 -TAG_NOTE = 0x3100 ## 0x3100 v-11 ---1 ---- ---- -TAG_ECKSUM = 0x3200 ## 0x3200 v-11 --1- ---- ---- -TAG_GCKSUMDELTA = 0x3300 ## 0x3300 v-11 --11 ---- ---- +TAG_NOTE = 0x3100 ## v-11 ---1 ++++ ++++ +TAG_ECKSUM = 0x3200 ## v-11 --1- ++++ ++++ +TAG_GCKSUMDELTA = 0x3300 ## v-11 --11 ++++ ++++ + + +# self-parsing tag repr +class Tag: + def __init__(self, name, tag, encoding, help): + self.name = name + self.tag = tag + self.encoding = encoding + self.help = help + # derive mask from encoding + self.mask = sum( + (1 if x in 'v-01' else 0) << len(self.encoding)-1-i + for i, x in enumerate(self.encoding)) + + def __repr__(self): + return 'Tag(%r, %r, %r)' % ( + self.name, + self.tag, + self.encoding) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + # substitute mask chars when zero + tag = '0x%s' % ''.join( + n if n != '0' else next( + (x for x in self.encoding[i*4:i*4+4] + if x not in 'v-01+'), + '0') + for i, n in enumerate('%04x' % self.tag)) + # group into nibbles + encoding = ' '.join(self.encoding[i*4:i*4+4] + for i in range(len(self.encoding)//4)) + return ('LFS3_%s' % self.name, tag, encoding) + + def specificity(self): + return sum(1 for x in self.encoding if x in 'v-01') + + def matches(self, tag): + return (tag & self.mask) == (self.tag & self.mask) + + def get(self, chars, tag): + return sum( + tag & ((1 if x in chars else 0) << len(self.encoding)-1-i) + for i, x in enumerate(self.encoding)) + + def max(self, chars): + return max(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def min(self, chars): + return min(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def width(self, chars): + return self.max(chars) - self.min(chars) + + def __contains__(self, chars): + return any(x in self.encoding for x in chars) + + @staticmethod + @ft.cache + def tags(): + # parse our script's source to figure out tags + import inspect + import re + tags = [] + tag_pattern = re.compile( + '^(?PTAG_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P(?:[^ ] *?){16}) *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = tag_pattern.match(line) + if m: + tags.append(Tag( + m.group('name'), + globals()[m.group('name')], + m.group('encoding').replace(' ', ''), + m.group('help'))) + return tags + + # find best matching tag + @staticmethod + def find(tag): + # find tags, note this is cached + tags__ = Tag.tags() + + # find the most specific matching tag, ignoring valid bits + return max((t for t in tags__ if t.matches(tag & 0x7fff)), + key=lambda t: t.specificity(), + default=None) + + # human readable tag repr + @staticmethod + def repr(tag, weight=None, size=None, *, + global_=False, + toff=None): + # find the most specific matching tag, ignoring the shrub bit + t = Tag.find(tag & ~(TAG_SHRUB if tag & 0x7000 == TAG_SHRUB else 0)) + + # build repr + r = [] + # normal tag? + if not tag & TAG_ALT: + if t is not None: + # prefix shrub tags with shrub + if tag & 0x7000 == TAG_SHRUB: + r.append('shrub') + # lowercase name + r.append(t.name.split('_', 1)[1].lower()) + # gstate tag? + if global_: + if r[-1] == 'gdelta': + r[-1] = 'gstate' + elif r[-1].endswith('delta'): + r[-1] = r[-1][:-len('delta')] + # include perturb/phase bits + if 'q' in t: + r.append('q%d' % t.get('q', tag)) + if 'p' in t and tag & TAG_PERTURB: + r.append('p') + + # include unmatched fields, but not just redund, and + # only reserved bits if non-zero + if 'tua' in t or ('+' in t and t.get('+', tag) != 0): + r.append(' 0x%0*x' % ( + (t.width('tuar+')+4-1)//4, + t.get('tuar+', tag))) + # unknown tag? + else: + r.append('0x%04x' % tag) + + # weight? + if weight: + r.append(' w%d' % weight) + # size? don't include if null + if size is not None and (size or tag & 0x7fff): + r.append(' %d' % size) + + # alt pointer? + else: + r.append('alt') + r.append('r' if tag & TAG_R else 'b') + r.append('gt' if tag & TAG_GT else 'le') + r.append(' 0x%0*x' % ( + (t.width('k')+4-1)//4, + t.get('k', tag))) + + # weight? + if weight is not None: + r.append(' w%d' % weight) + # jump? + if size and toff is not None: + r.append(' 0x%x' % (0xffffffff & (toff-size))) + elif size: + r.append(' -%d' % size) + + return ''.join(r) # some ways of block geometry representations @@ -201,141 +368,6 @@ def xxd(data, width=16): b if b >= ' ' and b <= '~' else '.' for b in map(chr, data[i:i+width]))) -# human readable tag repr -def tagrepr(tag, weight=None, size=None, *, - global_=False, - toff=None): - # null tags - if (tag & 0x6fff) == TAG_NULL: - return '%snull%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - ' w%d' % weight if weight else '', - ' %d' % size if size else '') - # config tags - elif (tag & 0x6f00) == TAG_CONFIG: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'magic' if (tag & 0xfff) == TAG_MAGIC - else 'version' if (tag & 0xfff) == TAG_VERSION - else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT - else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT - else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY - else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT - else 'filelimit' if (tag & 0xfff) == TAG_FILELIMIT - else 'config 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-state delta tags - elif (tag & 0x6f00) == TAG_GDELTA: - if global_: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grm' if (tag & 0xfff) == TAG_GRMDELTA - else 'gstate 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grmdelta' if (tag & 0xfff) == TAG_GRMDELTA - else 'gbmapdelta' if (tag & 0xfff) == TAG_GBMAPDELTA - else 'gdelta 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # name tags, includes file types - elif (tag & 0x6f00) == TAG_NAME: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'bname' if (tag & 0xfff) == TAG_BNAME - else 'reg' if (tag & 0xfff) == TAG_REG - else 'dir' if (tag & 0xfff) == TAG_DIR - else 'stickynote' if (tag & 0xfff) == TAG_STICKYNOTE - else 'bookmark' if (tag & 0xfff) == TAG_BOOKMARK - else 'mname' if (tag & 0xfff) == TAG_MNAME - else 'name 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # structure tags - elif (tag & 0x6f00) == TAG_STRUCT: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'branch' if (tag & 0xfff) == TAG_BRANCH - else 'data' if (tag & 0xfff) == TAG_DATA - else 'block' if (tag & 0xfff) == TAG_BLOCK - else 'did' if (tag & 0xfff) == TAG_DID - else 'bshrub' if (tag & 0xfff) == TAG_BSHRUB - else 'btree' if (tag & 0xfff) == TAG_BTREE - else 'mroot' if (tag & 0xfff) == TAG_MROOT - else 'mdir' if (tag & 0xfff) == TAG_MDIR - else 'mtree' if (tag & 0xfff) == TAG_MTREE - else 'bmfree' if (tag & 0xfff) == TAG_BMFREE - else 'bminuse' if (tag & 0xfff) == TAG_BMINUSE - else 'bmerased' if (tag & 0xfff) == TAG_BMERASED - else 'bmbad' if (tag & 0xfff) == TAG_BMBAD - else 'bmrange 0x%x' % (tag & 0xf) - if (tag & 0xff0) == TAG_BMRANGE - else 'struct 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # custom attributes - elif (tag & 0x6e00) == TAG_ATTR: - return '%s%sattr 0x%02x%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 's' if tag & 0x100 else 'u', - ((tag & 0x100) >> 1) + (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # alt pointers - elif tag & TAG_ALT: - return 'alt%s%s 0x%03x%s%s' % ( - 'r' if tag & TAG_R else 'b', - 'gt' if tag & TAG_GT else 'le', - tag & 0x0fff, - ' w%d' % weight if weight is not None else '', - ' 0x%x' % (0xffffffff & (toff-size)) - if size and toff is not None - else ' -%d' % size if size - else '') - # checksum tags - elif (tag & 0x7f00) == TAG_CKSUM: - return 'cksum%s%s%s%s%s' % ( - 'q%d' % (tag & 0x3), - 'p' if tag & TAG_PERTURB else '', - ' 0x%02x' % (tag & 0xff) if tag & 0xf8 else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # note tags - elif (tag & 0x7f00) == TAG_NOTE: - return 'note%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # erased-state checksum tags - elif (tag & 0x7f00) == TAG_ECKSUM: - return 'ecksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-checksum delta tags - elif (tag & 0x7f00) == TAG_GCKSUMDELTA: - if global_: - return 'gcksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return 'gcksumdelta%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # unknown tags - else: - return '0x%04x%s%s' % ( - tag, - ' w%d' % weight if weight is not None else '', - ' %d' % size if size is not None else '') - # compute the difference between two paths, returning everything # in a after the paths diverge, as well as the relevant index def pathdelta(a, b): @@ -414,7 +446,7 @@ class Rattr: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.size) + return Tag.repr(self.tag, self.weight, self.size) def __iter__(self): return iter((self.tag, self.weight, self.data)) @@ -493,7 +525,7 @@ class Ralt: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.jump, toff=self.toff) + return Tag.repr(self.tag, self.weight, self.jump, toff=self.toff) def __iter__(self): return iter((self.tag, self.weight, self.jump)) diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index 759292e6..38d642e6 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -28,56 +28,223 @@ COLORS = [ ] -TAG_NULL = 0x0000 ## 0x0000 v--- ---- ---- ---- -TAG_CONFIG = 0x0000 ## 0x00tt v--- ---- -ttt tttt -TAG_MAGIC = 0x0031 # 0x003r v--- ---- --11 --rr -TAG_VERSION = 0x0034 # 0x0034 v--- ---- --11 -1-- -TAG_RCOMPAT = 0x0035 # 0x0035 v--- ---- --11 -1-1 -TAG_WCOMPAT = 0x0036 # 0x0036 v--- ---- --11 -11- -TAG_OCOMPAT = 0x0037 # 0x0037 v--- ---- --11 -111 -TAG_GEOMETRY = 0x0038 # 0x0038 v--- ---- --11 1--- -TAG_NAMELIMIT = 0x0039 # 0x0039 v--- ---- --11 1--1 -TAG_FILELIMIT = 0x003a # 0x003a v--- ---- --11 1-1- -TAG_GDELTA = 0x0100 ## 0x01tt v--- ---1 -ttt ttrr -TAG_GRMDELTA = 0x0100 # 0x0100 v--- ---1 ---- ---- -TAG_GBMAPDELTA = 0x0104 # 0x0104 v--- ---1 ---- -1rr -TAG_NAME = 0x0200 ## 0x02tt v--- --1- -ttt tttt -TAG_BNAME = 0x0200 # 0x0200 v--- --1- ---- ---- -TAG_REG = 0x0201 # 0x0201 v--- --1- ---- ---1 -TAG_DIR = 0x0202 # 0x0202 v--- --1- ---- --1- -TAG_STICKYNOTE = 0x0203 # 0x0203 v--- --1- ---- --11 -TAG_BOOKMARK = 0x0204 # 0x0204 v--- --1- ---- -1-- -TAG_MNAME = 0x0220 # 0x0220 v--- --1- --1- ---- -TAG_STRUCT = 0x0300 ## 0x03tt v--- --11 -ttt ttrr -TAG_BRANCH = 0x0300 # 0x030r v--- --11 ---- --rr -TAG_DATA = 0x0304 # 0x0304 v--- --11 ---- -1-- -TAG_BLOCK = 0x0308 # 0x0308 v--- --11 ---- 1err -TAG_DID = 0x0314 # 0x0314 v--- --11 ---1 -1-- -TAG_BSHRUB = 0x0318 # 0x0318 v--- --11 ---1 1--- -TAG_BTREE = 0x031c # 0x031c v--- --11 ---1 11rr -TAG_MROOT = 0x0321 # 0x032r v--- --11 --1- --rr -TAG_MDIR = 0x0325 # 0x0324 v--- --11 --1- -1rr -TAG_MTREE = 0x032c # 0x032c v--- --11 --1- 11rr -TAG_BMRANGE = 0x0330 # 0x033u v--- --11 --11 uuuu -TAG_BMFREE = 0x0330 # 0x0330 v--- --11 --11 ---- -TAG_BMINUSE = 0x0331 # 0x0331 v--- --11 --11 ---1 -TAG_BMERASED = 0x0332 # 0x0332 v--- --11 --11 --1- -TAG_BMBAD = 0x0333 # 0x0333 v--- --11 --11 --11 -TAG_ATTR = 0x0400 ## 0x04aa v--- -1-a -aaa aaaa -TAG_UATTR = 0x0400 # 0x04aa v--- -1-- -aaa aaaa -TAG_SATTR = 0x0500 # 0x05aa v--- -1-1 -aaa aaaa -TAG_SHRUB = 0x1000 ## 0x1kkk v--1 kkkk -kkk kkkk -TAG_ALT = 0x4000 ## 0x4kkk v1cd kkkk -kkk kkkk +TAG_NULL = 0x0000 ## v--- ---- +--- ---- +TAG_CONFIG = 0x0000 ## v--- ---- +ttt tttt +TAG_MAGIC = 0x0031 # v--- ---- +-11 --rr +TAG_VERSION = 0x0034 # v--- ---- +-11 -1-- +TAG_RCOMPAT = 0x0035 # v--- ---- +-11 -1-1 +TAG_WCOMPAT = 0x0036 # v--- ---- +-11 -11- +TAG_OCOMPAT = 0x0037 # v--- ---- +-11 -111 +TAG_GEOMETRY = 0x0038 # v--- ---- +-11 1--- +TAG_NAMELIMIT = 0x0039 # v--- ---- +-11 1--1 +TAG_FILELIMIT = 0x003a # v--- ---- +-11 1-1- +TAG_GDELTA = 0x0100 ## v--- ---1 +ttt ttrr +TAG_GRMDELTA = 0x0100 # v--- ---1 +--- ---- +TAG_GBMAPDELTA = 0x0104 # v--- ---1 +--- -1rr +TAG_NAME = 0x0200 ## v--- --1- +ttt tttt +TAG_BNAME = 0x0200 # v--- --1- +--- ---- +TAG_REG = 0x0201 # v--- --1- +--- ---1 +TAG_DIR = 0x0202 # v--- --1- +--- --1- +TAG_STICKYNOTE = 0x0203 # v--- --1- +--- --11 +TAG_BOOKMARK = 0x0204 # v--- --1- +--- -1-- +TAG_MNAME = 0x0220 # v--- --1- +-1- ---- +TAG_STRUCT = 0x0300 ## v--- --11 +ttt ttrr +TAG_BRANCH = 0x0300 # v--- --11 +--- --rr +TAG_DATA = 0x0304 # v--- --11 +--- -1-- +TAG_BLOCK = 0x0308 # v--- --11 +--- 1err +TAG_DID = 0x0314 # v--- --11 +--1 -1-- +TAG_BSHRUB = 0x0318 # v--- --11 +--1 1--- +TAG_BTREE = 0x031c # v--- --11 +--1 11rr +TAG_MROOT = 0x0321 # v--- --11 +-1- --rr +TAG_MDIR = 0x0325 # v--- --11 +-1- -1rr +TAG_MTREE = 0x032c # v--- --11 +-1- 11rr +TAG_BMRANGE = 0x0330 # v--- --11 +-11 ++uu +TAG_BMFREE = 0x0330 # v--- --11 +-11 ---- +TAG_BMINUSE = 0x0331 # v--- --11 +-11 ---1 +TAG_BMERASED = 0x0332 # v--- --11 +-11 --1- +TAG_BMBAD = 0x0333 # v--- --11 +-11 --11 +TAG_ATTR = 0x0400 ## v--- -1-a +aaa aaaa +TAG_UATTR = 0x0400 # v--- -1-- +aaa aaaa +TAG_SATTR = 0x0500 # v--- -1-1 +aaa aaaa +TAG_SHRUB = 0x1000 ## v--1 kkkk +kkk kkkk +TAG_ALT = 0x4000 ## v1cd kkkk +kkk kkkk TAG_B = 0x0000 TAG_R = 0x2000 TAG_LE = 0x0000 TAG_GT = 0x1000 -TAG_CKSUM = 0x3000 ## 0x300p v-11 ---- ---- -pqq +TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq TAG_PHASE = 0x0003 TAG_PERTURB = 0x0004 -TAG_NOTE = 0x3100 ## 0x3100 v-11 ---1 ---- ---- -TAG_ECKSUM = 0x3200 ## 0x3200 v-11 --1- ---- ---- -TAG_GCKSUMDELTA = 0x3300 ## 0x3300 v-11 --11 ---- ---- +TAG_NOTE = 0x3100 ## v-11 ---1 ++++ ++++ +TAG_ECKSUM = 0x3200 ## v-11 --1- ++++ ++++ +TAG_GCKSUMDELTA = 0x3300 ## v-11 --11 ++++ ++++ + + +# self-parsing tag repr +class Tag: + def __init__(self, name, tag, encoding, help): + self.name = name + self.tag = tag + self.encoding = encoding + self.help = help + # derive mask from encoding + self.mask = sum( + (1 if x in 'v-01' else 0) << len(self.encoding)-1-i + for i, x in enumerate(self.encoding)) + + def __repr__(self): + return 'Tag(%r, %r, %r)' % ( + self.name, + self.tag, + self.encoding) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + # substitute mask chars when zero + tag = '0x%s' % ''.join( + n if n != '0' else next( + (x for x in self.encoding[i*4:i*4+4] + if x not in 'v-01+'), + '0') + for i, n in enumerate('%04x' % self.tag)) + # group into nibbles + encoding = ' '.join(self.encoding[i*4:i*4+4] + for i in range(len(self.encoding)//4)) + return ('LFS3_%s' % self.name, tag, encoding) + + def specificity(self): + return sum(1 for x in self.encoding if x in 'v-01') + + def matches(self, tag): + return (tag & self.mask) == (self.tag & self.mask) + + def get(self, chars, tag): + return sum( + tag & ((1 if x in chars else 0) << len(self.encoding)-1-i) + for i, x in enumerate(self.encoding)) + + def max(self, chars): + return max(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def min(self, chars): + return min(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def width(self, chars): + return self.max(chars) - self.min(chars) + + def __contains__(self, chars): + return any(x in self.encoding for x in chars) + + @staticmethod + @ft.cache + def tags(): + # parse our script's source to figure out tags + import inspect + import re + tags = [] + tag_pattern = re.compile( + '^(?PTAG_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P(?:[^ ] *?){16}) *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = tag_pattern.match(line) + if m: + tags.append(Tag( + m.group('name'), + globals()[m.group('name')], + m.group('encoding').replace(' ', ''), + m.group('help'))) + return tags + + # find best matching tag + @staticmethod + def find(tag): + # find tags, note this is cached + tags__ = Tag.tags() + + # find the most specific matching tag, ignoring valid bits + return max((t for t in tags__ if t.matches(tag & 0x7fff)), + key=lambda t: t.specificity(), + default=None) + + # human readable tag repr + @staticmethod + def repr(tag, weight=None, size=None, *, + global_=False, + toff=None): + # find the most specific matching tag, ignoring the shrub bit + t = Tag.find(tag & ~(TAG_SHRUB if tag & 0x7000 == TAG_SHRUB else 0)) + + # build repr + r = [] + # normal tag? + if not tag & TAG_ALT: + if t is not None: + # prefix shrub tags with shrub + if tag & 0x7000 == TAG_SHRUB: + r.append('shrub') + # lowercase name + r.append(t.name.split('_', 1)[1].lower()) + # gstate tag? + if global_: + if r[-1] == 'gdelta': + r[-1] = 'gstate' + elif r[-1].endswith('delta'): + r[-1] = r[-1][:-len('delta')] + # include perturb/phase bits + if 'q' in t: + r.append('q%d' % t.get('q', tag)) + if 'p' in t and tag & TAG_PERTURB: + r.append('p') + + # include unmatched fields, but not just redund, and + # only reserved bits if non-zero + if 'tua' in t or ('+' in t and t.get('+', tag) != 0): + r.append(' 0x%0*x' % ( + (t.width('tuar+')+4-1)//4, + t.get('tuar+', tag))) + # unknown tag? + else: + r.append('0x%04x' % tag) + + # weight? + if weight: + r.append(' w%d' % weight) + # size? don't include if null + if size is not None and (size or tag & 0x7fff): + r.append(' %d' % size) + + # alt pointer? + else: + r.append('alt') + r.append('r' if tag & TAG_R else 'b') + r.append('gt' if tag & TAG_GT else 'le') + r.append(' 0x%0*x' % ( + (t.width('k')+4-1)//4, + t.get('k', tag))) + + # weight? + if weight is not None: + r.append(' w%d' % weight) + # jump? + if size and toff is not None: + r.append(' 0x%x' % (0xffffffff & (toff-size))) + elif size: + r.append(' -%d' % size) + + return ''.join(r) # some ways of block geometry representations @@ -189,141 +356,6 @@ def xxd(data, width=16): b if b >= ' ' and b <= '~' else '.' for b in map(chr, data[i:i+width]))) -# human readable tag repr -def tagrepr(tag, weight=None, size=None, *, - global_=False, - toff=None): - # null tags - if (tag & 0x6fff) == TAG_NULL: - return '%snull%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - ' w%d' % weight if weight else '', - ' %d' % size if size else '') - # config tags - elif (tag & 0x6f00) == TAG_CONFIG: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'magic' if (tag & 0xfff) == TAG_MAGIC - else 'version' if (tag & 0xfff) == TAG_VERSION - else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT - else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT - else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY - else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT - else 'filelimit' if (tag & 0xfff) == TAG_FILELIMIT - else 'config 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-state delta tags - elif (tag & 0x6f00) == TAG_GDELTA: - if global_: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grm' if (tag & 0xfff) == TAG_GRMDELTA - else 'gstate 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grmdelta' if (tag & 0xfff) == TAG_GRMDELTA - else 'gbmapdelta' if (tag & 0xfff) == TAG_GBMAPDELTA - else 'gdelta 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # name tags, includes file types - elif (tag & 0x6f00) == TAG_NAME: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'bname' if (tag & 0xfff) == TAG_BNAME - else 'reg' if (tag & 0xfff) == TAG_REG - else 'dir' if (tag & 0xfff) == TAG_DIR - else 'stickynote' if (tag & 0xfff) == TAG_STICKYNOTE - else 'bookmark' if (tag & 0xfff) == TAG_BOOKMARK - else 'mname' if (tag & 0xfff) == TAG_MNAME - else 'name 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # structure tags - elif (tag & 0x6f00) == TAG_STRUCT: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'branch' if (tag & 0xfff) == TAG_BRANCH - else 'data' if (tag & 0xfff) == TAG_DATA - else 'block' if (tag & 0xfff) == TAG_BLOCK - else 'did' if (tag & 0xfff) == TAG_DID - else 'bshrub' if (tag & 0xfff) == TAG_BSHRUB - else 'btree' if (tag & 0xfff) == TAG_BTREE - else 'mroot' if (tag & 0xfff) == TAG_MROOT - else 'mdir' if (tag & 0xfff) == TAG_MDIR - else 'mtree' if (tag & 0xfff) == TAG_MTREE - else 'bmfree' if (tag & 0xfff) == TAG_BMFREE - else 'bminuse' if (tag & 0xfff) == TAG_BMINUSE - else 'bmerased' if (tag & 0xfff) == TAG_BMERASED - else 'bmbad' if (tag & 0xfff) == TAG_BMBAD - else 'bmrange 0x%x' % (tag & 0xf) - if (tag & 0xff0) == TAG_BMRANGE - else 'struct 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # custom attributes - elif (tag & 0x6e00) == TAG_ATTR: - return '%s%sattr 0x%02x%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 's' if tag & 0x100 else 'u', - ((tag & 0x100) >> 1) + (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # alt pointers - elif tag & TAG_ALT: - return 'alt%s%s 0x%03x%s%s' % ( - 'r' if tag & TAG_R else 'b', - 'gt' if tag & TAG_GT else 'le', - tag & 0x0fff, - ' w%d' % weight if weight is not None else '', - ' 0x%x' % (0xffffffff & (toff-size)) - if size and toff is not None - else ' -%d' % size if size - else '') - # checksum tags - elif (tag & 0x7f00) == TAG_CKSUM: - return 'cksum%s%s%s%s%s' % ( - 'q%d' % (tag & 0x3), - 'p' if tag & TAG_PERTURB else '', - ' 0x%02x' % (tag & 0xff) if tag & 0xf8 else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # note tags - elif (tag & 0x7f00) == TAG_NOTE: - return 'note%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # erased-state checksum tags - elif (tag & 0x7f00) == TAG_ECKSUM: - return 'ecksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-checksum delta tags - elif (tag & 0x7f00) == TAG_GCKSUMDELTA: - if global_: - return 'gcksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return 'gcksumdelta%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # unknown tags - else: - return '0x%04x%s%s' % ( - tag, - ' w%d' % weight if weight is not None else '', - ' %d' % size if size is not None else '') - # a simple wrapper over an open file with bd geometry class Bd: @@ -382,7 +414,7 @@ class Rattr: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.size) + return Tag.repr(self.tag, self.weight, self.size) def __iter__(self): return iter((self.tag, self.weight, self.data)) @@ -461,7 +493,7 @@ class Ralt: return '<%s %s>' % (self.__class__.__name__, self.repr()) def repr(self): - return tagrepr(self.tag, self.weight, self.jump, toff=self.toff) + return Tag.repr(self.tag, self.weight, self.jump, toff=self.toff) def __iter__(self): return iter((self.tag, self.weight, self.jump)) @@ -1625,7 +1657,7 @@ def dbg_log(rbyd, *, else '%d-%d' % (rid-(w-1), rid) if w > 1 else rid, 56+w_width, '%-*s %s' % ( - 21+w_width, tagrepr(tag, w, size, toff=j), + 21+w_width, Tag.repr(tag, w, size, toff=j), next(xxd(data[j+d:j+d+min(size, 8)], 8), '') if not args.get('raw') and not args.get('no_truncate') diff --git a/scripts/dbgtag.py b/scripts/dbgtag.py index 86ca6df9..066408cf 100755 --- a/scripts/dbgtag.py +++ b/scripts/dbgtag.py @@ -4,6 +4,7 @@ if __name__ == "__main__": __import__('sys').path.pop(0) +import functools as ft import io import math as mt import os @@ -11,56 +12,223 @@ import struct import sys -TAG_NULL = 0x0000 ## 0x0000 v--- ---- ---- ---- -TAG_CONFIG = 0x0000 ## 0x00tt v--- ---- -ttt tttt -TAG_MAGIC = 0x0031 # 0x003r v--- ---- --11 --rr -TAG_VERSION = 0x0034 # 0x0034 v--- ---- --11 -1-- -TAG_RCOMPAT = 0x0035 # 0x0035 v--- ---- --11 -1-1 -TAG_WCOMPAT = 0x0036 # 0x0036 v--- ---- --11 -11- -TAG_OCOMPAT = 0x0037 # 0x0037 v--- ---- --11 -111 -TAG_GEOMETRY = 0x0038 # 0x0038 v--- ---- --11 1--- -TAG_NAMELIMIT = 0x0039 # 0x0039 v--- ---- --11 1--1 -TAG_FILELIMIT = 0x003a # 0x003a v--- ---- --11 1-1- -TAG_GDELTA = 0x0100 ## 0x01tt v--- ---1 -ttt ttrr -TAG_GRMDELTA = 0x0100 # 0x0100 v--- ---1 ---- ---- -TAG_GBMAPDELTA = 0x0104 # 0x0104 v--- ---1 ---- -1rr -TAG_NAME = 0x0200 ## 0x02tt v--- --1- -ttt tttt -TAG_BNAME = 0x0200 # 0x0200 v--- --1- ---- ---- -TAG_REG = 0x0201 # 0x0201 v--- --1- ---- ---1 -TAG_DIR = 0x0202 # 0x0202 v--- --1- ---- --1- -TAG_STICKYNOTE = 0x0203 # 0x0203 v--- --1- ---- --11 -TAG_BOOKMARK = 0x0204 # 0x0204 v--- --1- ---- -1-- -TAG_MNAME = 0x0220 # 0x0220 v--- --1- --1- ---- -TAG_STRUCT = 0x0300 ## 0x03tt v--- --11 -ttt ttrr -TAG_BRANCH = 0x0300 # 0x030r v--- --11 ---- --rr -TAG_DATA = 0x0304 # 0x0304 v--- --11 ---- -1-- -TAG_BLOCK = 0x0308 # 0x0308 v--- --11 ---- 1err -TAG_DID = 0x0314 # 0x0314 v--- --11 ---1 -1-- -TAG_BSHRUB = 0x0318 # 0x0318 v--- --11 ---1 1--- -TAG_BTREE = 0x031c # 0x031c v--- --11 ---1 11rr -TAG_MROOT = 0x0321 # 0x032r v--- --11 --1- --rr -TAG_MDIR = 0x0325 # 0x0324 v--- --11 --1- -1rr -TAG_MTREE = 0x032c # 0x032c v--- --11 --1- 11rr -TAG_BMRANGE = 0x0330 # 0x033u v--- --11 --11 uuuu -TAG_BMFREE = 0x0330 # 0x0330 v--- --11 --11 ---- -TAG_BMINUSE = 0x0331 # 0x0331 v--- --11 --11 ---1 -TAG_BMERASED = 0x0332 # 0x0332 v--- --11 --11 --1- -TAG_BMBAD = 0x0333 # 0x0333 v--- --11 --11 --11 -TAG_ATTR = 0x0400 ## 0x04aa v--- -1-a -aaa aaaa -TAG_UATTR = 0x0400 # 0x04aa v--- -1-- -aaa aaaa -TAG_SATTR = 0x0500 # 0x05aa v--- -1-1 -aaa aaaa -TAG_SHRUB = 0x1000 ## 0x1kkk v--1 kkkk -kkk kkkk -TAG_ALT = 0x4000 ## 0x4kkk v1cd kkkk -kkk kkkk +TAG_NULL = 0x0000 ## v--- ---- +--- ---- +TAG_CONFIG = 0x0000 ## v--- ---- +ttt tttt +TAG_MAGIC = 0x0031 # v--- ---- +-11 --rr +TAG_VERSION = 0x0034 # v--- ---- +-11 -1-- +TAG_RCOMPAT = 0x0035 # v--- ---- +-11 -1-1 +TAG_WCOMPAT = 0x0036 # v--- ---- +-11 -11- +TAG_OCOMPAT = 0x0037 # v--- ---- +-11 -111 +TAG_GEOMETRY = 0x0038 # v--- ---- +-11 1--- +TAG_NAMELIMIT = 0x0039 # v--- ---- +-11 1--1 +TAG_FILELIMIT = 0x003a # v--- ---- +-11 1-1- +TAG_GDELTA = 0x0100 ## v--- ---1 +ttt ttrr +TAG_GRMDELTA = 0x0100 # v--- ---1 +--- ---- +TAG_GBMAPDELTA = 0x0104 # v--- ---1 +--- -1rr +TAG_NAME = 0x0200 ## v--- --1- +ttt tttt +TAG_BNAME = 0x0200 # v--- --1- +--- ---- +TAG_REG = 0x0201 # v--- --1- +--- ---1 +TAG_DIR = 0x0202 # v--- --1- +--- --1- +TAG_STICKYNOTE = 0x0203 # v--- --1- +--- --11 +TAG_BOOKMARK = 0x0204 # v--- --1- +--- -1-- +TAG_MNAME = 0x0220 # v--- --1- +-1- ---- +TAG_STRUCT = 0x0300 ## v--- --11 +ttt ttrr +TAG_BRANCH = 0x0300 # v--- --11 +--- --rr +TAG_DATA = 0x0304 # v--- --11 +--- -1-- +TAG_BLOCK = 0x0308 # v--- --11 +--- 1err +TAG_DID = 0x0314 # v--- --11 +--1 -1-- +TAG_BSHRUB = 0x0318 # v--- --11 +--1 1--- +TAG_BTREE = 0x031c # v--- --11 +--1 11rr +TAG_MROOT = 0x0321 # v--- --11 +-1- --rr +TAG_MDIR = 0x0325 # v--- --11 +-1- -1rr +TAG_MTREE = 0x032c # v--- --11 +-1- 11rr +TAG_BMRANGE = 0x0330 # v--- --11 +-11 ++uu +TAG_BMFREE = 0x0330 # v--- --11 +-11 ---- +TAG_BMINUSE = 0x0331 # v--- --11 +-11 ---1 +TAG_BMERASED = 0x0332 # v--- --11 +-11 --1- +TAG_BMBAD = 0x0333 # v--- --11 +-11 --11 +TAG_ATTR = 0x0400 ## v--- -1-a +aaa aaaa +TAG_UATTR = 0x0400 # v--- -1-- +aaa aaaa +TAG_SATTR = 0x0500 # v--- -1-1 +aaa aaaa +TAG_SHRUB = 0x1000 ## v--1 kkkk +kkk kkkk +TAG_ALT = 0x4000 ## v1cd kkkk +kkk kkkk TAG_B = 0x0000 TAG_R = 0x2000 TAG_LE = 0x0000 TAG_GT = 0x1000 -TAG_CKSUM = 0x3000 ## 0x300p v-11 ---- ---- -pqq +TAG_CKSUM = 0x3000 ## v-11 ---- ++++ +pqq TAG_PHASE = 0x0003 TAG_PERTURB = 0x0004 -TAG_NOTE = 0x3100 ## 0x3100 v-11 ---1 ---- ---- -TAG_ECKSUM = 0x3200 ## 0x3200 v-11 --1- ---- ---- -TAG_GCKSUMDELTA = 0x3300 ## 0x3300 v-11 --11 ---- ---- +TAG_NOTE = 0x3100 ## v-11 ---1 ++++ ++++ +TAG_ECKSUM = 0x3200 ## v-11 --1- ++++ ++++ +TAG_GCKSUMDELTA = 0x3300 ## v-11 --11 ++++ ++++ + + +# self-parsing tag repr +class Tag: + def __init__(self, name, tag, encoding, help): + self.name = name + self.tag = tag + self.encoding = encoding + self.help = help + # derive mask from encoding + self.mask = sum( + (1 if x in 'v-01' else 0) << len(self.encoding)-1-i + for i, x in enumerate(self.encoding)) + + def __repr__(self): + return 'Tag(%r, %r, %r)' % ( + self.name, + self.tag, + self.encoding) + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return self.name != other.name + + def __hash__(self): + return hash(self.name) + + def line(self): + # substitute mask chars when zero + tag = '0x%s' % ''.join( + n if n != '0' else next( + (x for x in self.encoding[i*4:i*4+4] + if x not in 'v-01+'), + '0') + for i, n in enumerate('%04x' % self.tag)) + # group into nibbles + encoding = ' '.join(self.encoding[i*4:i*4+4] + for i in range(len(self.encoding)//4)) + return ('LFS3_%s' % self.name, tag, encoding) + + def specificity(self): + return sum(1 for x in self.encoding if x in 'v-01') + + def matches(self, tag): + return (tag & self.mask) == (self.tag & self.mask) + + def get(self, chars, tag): + return sum( + tag & ((1 if x in chars else 0) << len(self.encoding)-1-i) + for i, x in enumerate(self.encoding)) + + def max(self, chars): + return max(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def min(self, chars): + return min(len(self.encoding)-1-i + for i, x in enumerate(self.encoding) if x in chars) + + def width(self, chars): + return self.max(chars) - self.min(chars) + + def __contains__(self, chars): + return any(x in self.encoding for x in chars) + + @staticmethod + @ft.cache + def tags(): + # parse our script's source to figure out tags + import inspect + import re + tags = [] + tag_pattern = re.compile( + '^(?PTAG_[^ ]*) *= *(?P[^#]*?) *' + '#+ *(?P(?:[^ ] *?){16}) *(?P.*)$') + for line in (inspect.getsource( + inspect.getmodule(inspect.currentframe())) + .replace('\\\n', '') + .splitlines()): + m = tag_pattern.match(line) + if m: + tags.append(Tag( + m.group('name'), + globals()[m.group('name')], + m.group('encoding').replace(' ', ''), + m.group('help'))) + return tags + + # find best matching tag + @staticmethod + def find(tag): + # find tags, note this is cached + tags__ = Tag.tags() + + # find the most specific matching tag, ignoring valid bits + return max((t for t in tags__ if t.matches(tag & 0x7fff)), + key=lambda t: t.specificity(), + default=None) + + # human readable tag repr + @staticmethod + def repr(tag, weight=None, size=None, *, + global_=False, + toff=None): + # find the most specific matching tag, ignoring the shrub bit + t = Tag.find(tag & ~(TAG_SHRUB if tag & 0x7000 == TAG_SHRUB else 0)) + + # build repr + r = [] + # normal tag? + if not tag & TAG_ALT: + if t is not None: + # prefix shrub tags with shrub + if tag & 0x7000 == TAG_SHRUB: + r.append('shrub') + # lowercase name + r.append(t.name.split('_', 1)[1].lower()) + # gstate tag? + if global_: + if r[-1] == 'gdelta': + r[-1] = 'gstate' + elif r[-1].endswith('delta'): + r[-1] = r[-1][:-len('delta')] + # include perturb/phase bits + if 'q' in t: + r.append('q%d' % t.get('q', tag)) + if 'p' in t and tag & TAG_PERTURB: + r.append('p') + + # include unmatched fields, but not just redund, and + # only reserved bits if non-zero + if 'tua' in t or ('+' in t and t.get('+', tag) != 0): + r.append(' 0x%0*x' % ( + (t.width('tuar+')+4-1)//4, + t.get('tuar+', tag))) + # unknown tag? + else: + r.append('0x%04x' % tag) + + # weight? + if weight: + r.append(' w%d' % weight) + # size? don't include if null + if size is not None and (size or tag & 0x7fff): + r.append(' %d' % size) + + # alt pointer? + else: + r.append('alt') + r.append('r' if tag & TAG_R else 'b') + r.append('gt' if tag & TAG_GT else 'le') + r.append(' 0x%0*x' % ( + (t.width('k')+4-1)//4, + t.get('k', tag))) + + # weight? + if weight is not None: + r.append(' w%d' % weight) + # jump? + if size and toff is not None: + r.append(' 0x%x' % (0xffffffff & (toff-size))) + elif size: + r.append(' -%d' % size) + + return ''.join(r) # open with '-' for stdin/stdout @@ -93,165 +261,28 @@ def fromtag(data, j=0): size, d_ = fromleb128(data, j+d); d += d_ return tag>>15, tag&0x7fff, weight, size, d -# human readable tag repr -def tagrepr(tag, weight=None, size=None, *, - global_=False, - toff=None): - # null tags - if (tag & 0x6fff) == TAG_NULL: - return '%snull%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - ' w%d' % weight if weight else '', - ' %d' % size if size else '') - # config tags - elif (tag & 0x6f00) == TAG_CONFIG: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'magic' if (tag & 0xfff) == TAG_MAGIC - else 'version' if (tag & 0xfff) == TAG_VERSION - else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT - else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT - else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY - else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT - else 'filelimit' if (tag & 0xfff) == TAG_FILELIMIT - else 'config 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-state delta tags - elif (tag & 0x6f00) == TAG_GDELTA: - if global_: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grm' if (tag & 0xfff) == TAG_GRMDELTA - else 'gstate 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'grmdelta' if (tag & 0xfff) == TAG_GRMDELTA - else 'gbmapdelta' if (tag & 0xfff) == TAG_GBMAPDELTA - else 'gdelta 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # name tags, includes file types - elif (tag & 0x6f00) == TAG_NAME: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'bname' if (tag & 0xfff) == TAG_BNAME - else 'reg' if (tag & 0xfff) == TAG_REG - else 'dir' if (tag & 0xfff) == TAG_DIR - else 'stickynote' if (tag & 0xfff) == TAG_STICKYNOTE - else 'bookmark' if (tag & 0xfff) == TAG_BOOKMARK - else 'mname' if (tag & 0xfff) == TAG_MNAME - else 'name 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # structure tags - elif (tag & 0x6f00) == TAG_STRUCT: - return '%s%s%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 'branch' if (tag & 0xfff) == TAG_BRANCH - else 'data' if (tag & 0xfff) == TAG_DATA - else 'block' if (tag & 0xfff) == TAG_BLOCK - else 'did' if (tag & 0xfff) == TAG_DID - else 'bshrub' if (tag & 0xfff) == TAG_BSHRUB - else 'btree' if (tag & 0xfff) == TAG_BTREE - else 'mroot' if (tag & 0xfff) == TAG_MROOT - else 'mdir' if (tag & 0xfff) == TAG_MDIR - else 'mtree' if (tag & 0xfff) == TAG_MTREE - else 'bmfree' if (tag & 0xfff) == TAG_BMFREE - else 'bminuse' if (tag & 0xfff) == TAG_BMINUSE - else 'bmerased' if (tag & 0xfff) == TAG_BMERASED - else 'bmbad' if (tag & 0xfff) == TAG_BMBAD - else 'bmrange 0x%x' % (tag & 0xf) - if (tag & 0xff0) == TAG_BMRANGE - else 'struct 0x%02x' % (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # custom attributes - elif (tag & 0x6e00) == TAG_ATTR: - return '%s%sattr 0x%02x%s%s' % ( - 'shrub' if tag & TAG_SHRUB else '', - 's' if tag & 0x100 else 'u', - ((tag & 0x100) >> 1) + (tag & 0xff), - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # alt pointers - elif tag & TAG_ALT: - return 'alt%s%s 0x%03x%s%s' % ( - 'r' if tag & TAG_R else 'b', - 'gt' if tag & TAG_GT else 'le', - tag & 0x0fff, - ' w%d' % weight if weight is not None else '', - ' 0x%x' % (0xffffffff & (toff-size)) - if size and toff is not None - else ' -%d' % size if size - else '') - # checksum tags - elif (tag & 0x7f00) == TAG_CKSUM: - return 'cksum%s%s%s%s%s' % ( - 'q%d' % (tag & 0x3), - 'p' if tag & TAG_PERTURB else '', - ' 0x%02x' % (tag & 0xff) if tag & 0xf8 else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # note tags - elif (tag & 0x7f00) == TAG_NOTE: - return 'note%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # erased-state checksum tags - elif (tag & 0x7f00) == TAG_ECKSUM: - return 'ecksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # global-checksum delta tags - elif (tag & 0x7f00) == TAG_GCKSUMDELTA: - if global_: - return 'gcksum%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - else: - return 'gcksumdelta%s%s%s' % ( - ' 0x%02x' % (tag & 0xff) if tag & 0xff else '', - ' w%d' % weight if weight else '', - ' %s' % size if size is not None else '') - # unknown tags - else: - return '0x%04x%s%s' % ( - tag, - ' w%d' % weight if weight is not None else '', - ' %d' % size if size is not None else '') - def list_tags(): - # here be magic, parse our script's source to get to the tag comments - import inspect - import re - tags = [] - tag_pattern = re.compile( - '^(?PTAG_[^ ]*) *= *(?P[^ #]+) *#+ *(?P.*)$') - for line in inspect.getsourcelines( - inspect.getmodule(inspect.currentframe()))[0]: - m = tag_pattern.match(line) - if m: - tags.append(m.groups()) + # find tags + tags__ = Tag.tags() - # find widths for alignment - w = [0] - for n, t, c in tags: - w[0] = max(w[0], len('LFS3_'+n)) + # list + lines = [] + for t in tags__: + lines.append(t.line()) - # print - for n, t, c in tags: - print('%-*s %s' % ( - w[0], 'LFS3_'+n, - c)) + # figure out widths + w = [0, 0] + for l in lines: + w[0] = max(w[0], len(l[0])) + w[1] = max(w[1], len(l[1])) + + # then print results + for l in lines: + print('%-*s %-*s %s' % ( + w[0], l[0], + w[1], l[1], + l[2])) def dbg_tags(data, *, word_bits=32): @@ -265,7 +296,7 @@ def dbg_tags(data, *, for tag in data: lines.append(( ' '.join('%02x' % b for b in struct.pack('>H', tag)), - tagrepr(tag))) + Tag.repr(tag))) # interpret as bytes? else: @@ -280,7 +311,7 @@ def dbg_tags(data, *, lines.append(( ' '.join('%02x' % b for b in data[j:j+d]), - tagrepr(tag, w, size))) + Tag.repr(tag, w, size))) j += d # skip attached data if there is any