Map instance records carry their own length
They are not a fixed 76 bytes. The first int of each record is its length, and while most placements are 76, every track also has eight of 140, two of 80 and one of 336 - 560 on Paingod's. Striding a fixed 76 landed mid-record on those, and hunting forward for the next plausible quaternion then locked onto arbitrary bytes: that is where the impossible class ids came from, and the "resource id" 1065353216, which is 0x3F800000 - float 1.0. Reading the length instead, all eighteen tracks parse to exactly their declared instance count with no bytes left over. That is the check that was missing before. It does not change what gets drawn, because the extra records were never drawable anyway. It does mean the parse is no longer guessing.
This commit is contained in:
File diff suppressed because one or more lines are too long
+22
-11
@@ -83,20 +83,31 @@ def read_gauge_image(res, addr, size):
|
||||
|
||||
|
||||
def instances(res, addr, size, count, gauge_ids):
|
||||
"""Map instance records: 76 bytes, model gauge-image id at +44,
|
||||
position at +48, unit quaternion at +60. The quaternion validates the
|
||||
record; a record whose +44 is not a gauge image is simply not drawn,
|
||||
exactly as DrawStatic skips it."""
|
||||
"""Map instance records are variable length and say so: the first int
|
||||
of a record is its own length in bytes. Resource id at +44, position
|
||||
at +48, quaternion at +60.
|
||||
|
||||
Most records are 76 bytes - a scenery placement - but every track also
|
||||
carries eight of 140, two of 80 and one of 336 (560 on Paingod's).
|
||||
Striding a fixed 76 lands mid-record on those, and hunting forward for
|
||||
the next plausible quaternion then locks onto arbitrary bytes: that is
|
||||
where the impossible class ids and the "resource id" of 1065353216
|
||||
came from, which is 0x3F800000, float 1.0. Reading the length instead
|
||||
makes all eighteen tracks parse to exactly their declared count with
|
||||
no bytes left over.
|
||||
|
||||
A record whose +44 is not a gauge image is not drawn, exactly as
|
||||
DrawStatic skips an entity with no gauge image."""
|
||||
out, o, end = [], addr + 4, addr + size
|
||||
while len(out) < count and o + 76 <= end:
|
||||
while len(out) < count and o + 8 <= end:
|
||||
length = struct.unpack_from('<i', res, o)[0]
|
||||
if not (40 <= length <= 1024) or o + length > end:
|
||||
break
|
||||
gid = struct.unpack_from('<i', res, o + 44)[0]
|
||||
pos = struct.unpack_from('<3f', res, o + 48)
|
||||
q = struct.unpack_from('<4f', res, o + 60)
|
||||
if abs(sum(v * v for v in q) - 1.0) < 0.02 and all(abs(v) < 1e5 for v in pos):
|
||||
gid = struct.unpack_from('<i', res, o + 44)[0]
|
||||
out.append((gid if gid in gauge_ids else None, pos, q))
|
||||
o += 76
|
||||
else:
|
||||
o += 4
|
||||
out.append((gid if gid in gauge_ids else None, pos, q))
|
||||
o += length
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user