|
67 | 67 | # Kinds seen on a position descriptor: a left/right anchor pair. |
68 | 68 | ANCHOR_KINDS = (21, 22) |
69 | 69 |
|
| 70 | +# A field's CUID is hashed with its *class* CUID as the seed, so the same field |
| 71 | +# name has a different hash in every class that declares it. These two groups |
| 72 | +# therefore coexist rather than conflict: the drawable hierarchy below, and the |
| 73 | +# resource classes it points at. |
70 | 74 | CLASS_NAMES = { |
| 75 | + # drawables |
71 | 76 | 0x8E2F8293: "CDrawObject", 0x90AA9E70: "CGUIElement", |
72 | 77 | 0xA806B8EE: "CAligningObject", 0xA3C213B8: "CBitmapObject", |
73 | 78 | 0x71B5DB06: "CTextBase", 0x3EBA6425: "CTextObject", |
74 | 79 | 0xBA066794: "CFormattedTextObject", 0x77F80FB9: "CTextArea", |
| 80 | + 0x2267FE33: "<CPoint>", 0x0AA3495D: "<CSize>", |
| 81 | + # resources |
75 | 82 | 0xDA63396B: "CDisplay", 0x92AE6BAD: "CColor", |
76 | 83 | 0x57E931C3: "CAlignment", 0xC6D7DD90: "CBitmap", |
77 | 84 | 0xE0D04503: "CFontFormat", 0x0B60D4CA: "CFontFile", |
78 | | - 0x5C8F9492: "CResourceTable", 0x25B3AC9A: "<string>", |
79 | | - 0x2267FE33: "<CPoint>", 0x0AA3495D: "<CSize>", |
| 85 | + 0x5C8F9492: "CResourceTable", 0x25B3AC9A: "CHBString", |
| 86 | + 0x60436E39: "CFont", 0x8AC3362F: "CFontReferences", |
| 87 | + 0x2F71594A: "CFontTextCombination", 0xE161265A: "CPlacementFontCombination", |
| 88 | + 0x3DC09477: "CPlacementCombination", 0xB9106E94: "CBitmapColorCombination", |
| 89 | + 0xD7CE702B: "CColors", 0xD8492763: "CColorMasks", |
| 90 | + 0x1F53E9FF: "CColorReferences", 0x833EFC17: "PositionResource", |
| 91 | + 0x8425447B: "AlignmentResource", |
80 | 92 | } |
81 | 93 |
|
82 | 94 | FIELD_NAMES = { |
| 95 | + # on the drawable classes |
83 | 96 | 0x0A2B4963: "mParentID", 0x7F9A5557: "mDrawOrder", |
84 | 97 | 0x7A1CC624: "mPositionResID", 0xF790B5F2: "mSizeResID", |
85 | 98 | 0xEBD70139: "mPosition", 0x45CF85C4: "mSize", |
86 | 99 | 0x5FFAEE1E: "m_childrenIDs", 0x1A2BEA44: "m_resourceTableID", |
87 | 100 | 0x5A894D17: "mAlignmentResID", 0x79CC0437: "mAlignment", |
88 | 101 | 0x94EA844C: "mBmpResID", 0xF9F3B843: "mColorsResID", |
89 | 102 | 0xD7C9A631: "mTextResID", 0xB397877F: "mFontResID", |
90 | | - 0x9960974E: "point", 0x26693065: "size", |
| 103 | + 0x621C612D: "mFontResID", 0x9960974E: "point", |
| 104 | + 0x26693065: "size", |
| 105 | + # on CFont / CFontFormat / CFontFile -- the chain a label's size comes from |
| 106 | + 0xAFB73BC7: "mFontFormatResID", 0x650043A0: "mColorsResID", |
| 107 | + 0x84477324: "mFontFileResID", 0xA2E58771: "mHeight", |
| 108 | + 0xD080B74E: "mWidth", 0xEE757E54: "mEngine", |
| 109 | + 0x92DD65C3: "mHorizontalDPI", 0x2E858ACE: "mVerticalDPI", |
| 110 | + 0x70160B06: "mFileName", 0xFFF5F83A: "mOutlineWidth", |
| 111 | + 0x1EB8A8EF: "mFontResID", 0x5F028ED0: "mFontResID", |
| 112 | + 0x5F90203A: "mFonts", 0x5C8C1726: "mString", |
| 113 | + # on CBitmap |
| 114 | + 0xEC9570D7: "mMode", 0x93DF2ACC: "mBitsPerPixel", |
| 115 | + 0x78BDA50A: "mWidth", 0xE70B5DA7: "mHeight", |
| 116 | + 0x0800845E: "mPaletteCount", 0x9A979BD7: "mPixelData", |
| 117 | + # on the resource-side placement classes |
| 118 | + 0x8F1AC150: "mPoint", 0xC0654178: "mSize", |
| 119 | + 0x10432267: "mPosition", 0x2B5B2DE3: "mSize", |
| 120 | + 0xE4E168FB: "mPositionResID", 0xD967BDC7: "mSizeResID", |
| 121 | + 0xE46C3005: "mBmpResID", 0x98165484: "mTextResID", |
| 122 | + 0xA433A1BF: "mColorsResID", 0xF1BF6A3D: "mParentID", |
| 123 | + 0xA3EFFD51: "m_childrenIDs", 0xD0B3623F: "m_resourceTableID", |
| 124 | + 0x516D5D62: "mDrawOrder", |
91 | 125 | } |
92 | 126 |
|
93 | 127 |
|
@@ -398,6 +432,58 @@ def screens(self, min_elements=4, limit=400): |
398 | 432 | break |
399 | 433 | return uniq |
400 | 434 |
|
| 435 | + def font_px(self, rid): |
| 436 | + """Pixel height of the font an element draws in, or None. |
| 437 | +
|
| 438 | + The chain is element -> mFontResID -> CFont -> mFontFormatResID -> |
| 439 | + CFontFormat.mHeight, and CFontFormat also names the .ttf via CFontFile, |
| 440 | + so the real typeface is knowable too -- the files are in IFS2. Using the |
| 441 | + recorded size rather than one fixed size is what stops every screen |
| 442 | + looking alike: a heading and a list row differ by more than their text. |
| 443 | + """ |
| 444 | + r = self.record(rid) |
| 445 | + if not r: |
| 446 | + return None |
| 447 | + fid = r.get("mFontResID") or 0 |
| 448 | + if not fid: |
| 449 | + return None |
| 450 | + f = self.record(fid) |
| 451 | + if f is None: # a descriptor: follow its variants |
| 452 | + for _kind, pid in self.desc.get(fid, ()): |
| 453 | + f = self.record(pid) |
| 454 | + if f is not None: |
| 455 | + break |
| 456 | + if not f: |
| 457 | + return None |
| 458 | + ffid = f.get("mFontFormatResID") or 0 |
| 459 | + fmt = self.record(ffid) if ffid else None |
| 460 | + if fmt is None and ffid: |
| 461 | + for _kind, pid in self.desc.get(ffid, ()): |
| 462 | + fmt = self.record(pid) |
| 463 | + if fmt is not None: |
| 464 | + break |
| 465 | + h = (fmt or {}).get("mHeight") |
| 466 | + if isinstance(h, int) and 4 <= h <= 96: |
| 467 | + return h |
| 468 | + return None |
| 469 | + |
| 470 | + def font_file(self, rid): |
| 471 | + """The .ttf an element's font resolves to, if the chain is complete.""" |
| 472 | + r = self.record(rid) |
| 473 | + fid = (r or {}).get("mFontResID") or 0 |
| 474 | + f = self.record(fid) if fid else None |
| 475 | + ffid = (f or {}).get("mFontFormatResID") or 0 |
| 476 | + fmt = self.record(ffid) if ffid else None |
| 477 | + fileid = (fmt or {}).get("mFontFileResID") or 0 |
| 478 | + ff = self.record(fileid) if fileid else None |
| 479 | + name = (ff or {}).get("mFileName") |
| 480 | + if isinstance(name, bytes): |
| 481 | + try: |
| 482 | + return name.decode("utf-8", "replace").strip("\x00") |
| 483 | + except Exception: |
| 484 | + return None |
| 485 | + return name if isinstance(name, str) else None |
| 486 | + |
401 | 487 | def name_of(self, rid, scan=600): |
402 | 488 | """A human name for a screen: the first text found inside it. |
403 | 489 |
|
|
0 commit comments