Decode the tie LOD block: it is a table of 16-byte records
The tie block's word at +0x00 was read as an element count, and the reading failed in a way worth recording: the payload length divided by it is a whole number on 0 of 922 blocks, at any record size and any header up to 512 bytes. That is the correct measurement of the wrong hypothesis -- the word never described the payload. Range measured: every LOD block of level 1 whose word is a non-zero multiple of 16, plus the two read by hand in full. Method: locate each block's record table in PCSX2's RAM by its twelve field bytes, which are the only distinctive 12-byte run in a block, then compare every record against its disc copy. Each block has several copies in RAM, and the one that matches is the loaded one -- checking the first hit is what made the first attempt report 0 of 16. Result: on 16 of 16 blocks, every record's leading word has become table_address + word, and the other twelve bytes are byte-identical to the disc. So a block is [16-byte header][record table][vertex payload], the records are 16 bytes -- a leading u32 pointer plus twelve bytes of fields -- and the header's word at +0x00 is the byte offset from the block start to the payload, that is, the length of the table. That is what the multiple-of-16 was: the table holds 16-byte records. It is never zero because there is always at least the first record, and its 42 distinct values are table lengths. Each record's pointer is relative to the table start, relocated at load to table + word. TieBlockHeader::data_offset and TieBlockHeader::records carry it, bound by the offset, with TieRecord for the twelve field bytes, and the disc test asserts the table is bounded on every block. What the records' fields name and what the payload holds are still open -- the payload is known to be variable-length, float-shaped, not LZ77 and not VIF1.
This commit is contained in:
@@ -2805,6 +2805,7 @@ fn tie_classes_resolve_through_both_indirections() {
|
||||
let mut hdr_ok = 0usize;
|
||||
let mut count_mult16 = 0usize;
|
||||
let mut reserved_02_zero = 0usize;
|
||||
let mut tables_bounded = 0usize;
|
||||
let mut packed_ok = 0usize;
|
||||
let mut padding_all_zero = 0usize;
|
||||
let mut packed_seen: Vec<u16> = Vec::new();
|
||||
@@ -2833,9 +2834,16 @@ fn tie_classes_resolve_through_both_indirections() {
|
||||
};
|
||||
|
||||
blocks += 1;
|
||||
if blk.header.count % 16 == 0 {
|
||||
// The word at +0 is the data offset -- the record table's
|
||||
// length -- so it is a non-zero multiple of 16, and it bounds
|
||||
// the table.
|
||||
if blk.header.data_offset % 16 == 0 && blk.header.data_offset != 0 {
|
||||
count_mult16 += 1
|
||||
}
|
||||
let table = blk.header.records(&core[s..]).count();
|
||||
if table == blk.header.record_count() && table > 0 {
|
||||
tables_bounded += 1
|
||||
}
|
||||
if blk.header.reserved_02 == 0 {
|
||||
reserved_02_zero += 1
|
||||
}
|
||||
@@ -2871,9 +2879,15 @@ fn tie_classes_resolve_through_both_indirections() {
|
||||
assert_eq!(
|
||||
count_mult16,
|
||||
blocks,
|
||||
"count not a multiple of 16 on {} of {blocks}",
|
||||
"the data offset is not a non-zero multiple of 16 on {} of {blocks}",
|
||||
blocks - count_mult16
|
||||
);
|
||||
assert_eq!(
|
||||
tables_bounded,
|
||||
blocks,
|
||||
"the record table is not bounded by the data offset on {} of {blocks}",
|
||||
blocks - tables_bounded
|
||||
);
|
||||
assert_eq!(
|
||||
reserved_02_zero,
|
||||
blocks,
|
||||
|
||||
Reference in New Issue
Block a user