re: recover the IDXD record-key / field-tag hash (8643/8643)
Closes the 4-byte record key. tag_hash is name_hash's shape -- byte-sum checksum in the top byte over a 24-bit modular polynomial -- with two different constants: modulus 0x00FFFFDF (2^24-33, prime) instead of 0x00FFF9D7, and no lowercasing, so tags are case-sensitive. name_hash explains 0 of 8643. Recovered from the tables rather than the executable: every inline field name is a known (name -> tag) pair, and comparing names differing in one character gives the per-position weights 1, 0x100, 0x10000, 0x21, 0x2100, ... -- a byte leaving bit 24 re-enters as 33, i.e. reduction mod 2^24-33. Holds where it is easy to get wrong (distance 8 and 9 carry correctly). A record's key is the tag of its own name: FormationSet rosters 362/362, UnitGroup rosters 281/281, S02 squadron names 111/111 -- so records can be addressed by name without reading the roster first. Implemented in Python (unitgroup.tag_hash) and Rust (sylpheed_formats::hash::tag_hash) with 3 new unit tests carrying disc-derived vectors; cargo test -p sylpheed-formats --lib hash is 8/8 green. Not settled: the guest routine is unlocated, so this uses exact modular arithmetic where the game may use a Barrett step without final fixup.
This commit is contained in:
@@ -48,6 +48,24 @@ def name_hash(s):
|
||||
a = (a - (q * MODULUS)) & 0xFFFFFFFF
|
||||
return (((b << 24) & 0xFF000000) | (a & 0x00FFFFFF)) & 0xFFFFFFFF
|
||||
|
||||
TAG_MODULUS = (1 << 24) - 33 # 0x00FFFFDF, prime
|
||||
|
||||
def tag_hash(s):
|
||||
"""IDXD record key / field tag -- NOT name_hash.
|
||||
|
||||
Same shape as name_hash (8-bit byte-sum checksum over a 24-bit modular
|
||||
polynomial) but modulo 0x00FFFFDF instead of 0x00FFF9D7, and NOT
|
||||
lowercased, so tags are case-sensitive. Recovered empirically from the 8643
|
||||
(name -> tag) pairs the tables themselves carry; `unitgroup.py --checktags`
|
||||
re-verifies all of them. A record's key is the tag of its own name, which
|
||||
each table lists in an in-table roster record.
|
||||
"""
|
||||
b = s.encode()
|
||||
lo = 0
|
||||
for c in b:
|
||||
lo = (lo * 256 + c) % TAG_MODULUS
|
||||
return ((sum(b) & 0xFF) << 24) | lo
|
||||
|
||||
def read_entry(pak, h):
|
||||
idx = open(pak, 'rb').read()
|
||||
base = pak[:-4]
|
||||
|
||||
Reference in New Issue
Block a user