fix(assets): parse native CTL tags 2 and 5
Docs Deploy / Build and Deploy MkDocs (push) Successful in 33s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 33s
This commit is contained in:
2 files changed
+47
-7
No files matched your search
@@ -3599,7 +3599,7 @@ fn control_i32_at(payload: &[u8], offset: usize) -> Option<i32> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONTROL_TYPED_COMMON_PARSER_TAGS: &[u32] = &[1, 8, 9, 12, 13, 15, 19, 21, 25, 26];
|
const CONTROL_TYPED_COMMON_PARSER_TAGS: &[u32] = &[1, 2, 5, 8, 9, 12, 13, 15, 19, 21, 25, 26];
|
||||||
const CONTROL_TYPED_RECORD_FIXED_BYTES: usize = 0xB0;
|
const CONTROL_TYPED_RECORD_FIXED_BYTES: usize = 0xB0;
|
||||||
const CONTROL_TYPED_RECORD_COUNT_OFFSET: usize = 0xAC;
|
const CONTROL_TYPED_RECORD_COUNT_OFFSET: usize = 0xAC;
|
||||||
const CONTROL_TYPED_RECORD_LIMIT: usize = 4096;
|
const CONTROL_TYPED_RECORD_LIMIT: usize = 4096;
|
||||||
@@ -4297,20 +4297,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn component_control_unproven_effect_tags_preserve_node_bindings() {
|
fn component_control_unknown_effect_tag_preserves_node_bindings() {
|
||||||
let source = ResourceKey {
|
let source = ResourceKey {
|
||||||
archive: parse_path("controls.rlb").expect("archive"),
|
archive: parse_path("controls.rlb").expect("archive"),
|
||||||
name: resource_name(b"parts.ctl"),
|
name: resource_name(b"parts.ctl"),
|
||||||
type_id: None,
|
type_id: None,
|
||||||
};
|
};
|
||||||
for tag in [2_u32, 5, 0xDEAD_BEEF] {
|
|
||||||
let mut payload = component_control_payload(&[(1, 0.0, 1.0, 0.25, 0)]);
|
let mut payload = component_control_payload(&[(1, 0.0, 1.0, 0.25, 0)]);
|
||||||
payload[12..16].copy_from_slice(&1_u32.to_le_bytes());
|
payload[12..16].copy_from_slice(&1_u32.to_le_bytes());
|
||||||
payload[16..20].copy_from_slice(&1_u32.to_le_bytes());
|
payload[16..20].copy_from_slice(&1_u32.to_le_bytes());
|
||||||
payload.extend_from_slice(&tag.to_le_bytes());
|
payload.extend_from_slice(&0xDEAD_BEEFu32.to_le_bytes());
|
||||||
|
|
||||||
let control = decode_component_control_payload(&payload, source.clone(), 2, 3)
|
let control = decode_component_control_payload(&payload, source.clone(), 2, 3)
|
||||||
.expect("unproven tag is preserved as unsupported");
|
.expect("unknown tag is retained");
|
||||||
|
|
||||||
assert_eq!(control.bindings.len(), 1);
|
assert_eq!(control.bindings.len(), 1);
|
||||||
assert_eq!(control.bindings[0].node_index, 1);
|
assert_eq!(control.bindings[0].node_index, 1);
|
||||||
@@ -4319,11 +4318,10 @@ mod tests {
|
|||||||
control.effect_section,
|
control.effect_section,
|
||||||
PreparedControlEffectSection::Unsupported {
|
PreparedControlEffectSection::Unsupported {
|
||||||
typed_record_index: Some(0),
|
typed_record_index: Some(0),
|
||||||
typed_record_tag: Some(tag),
|
typed_record_tag: Some(0xDEAD_BEEF),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn component_control_zero_length_typed_record_ends_before_group_header() {
|
fn component_control_zero_length_typed_record_ends_before_group_header() {
|
||||||
@@ -4350,6 +4348,39 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn component_control_tags2_and5_share_the_typed_cursor_parser() {
|
||||||
|
let source = ResourceKey {
|
||||||
|
archive: parse_path("controls.rlb").expect("archive"),
|
||||||
|
name: resource_name(b"parts.ctl"),
|
||||||
|
type_id: None,
|
||||||
|
};
|
||||||
|
let mut payload = component_control_payload(&[(1, 0.0, 1.0, 0.25, 0)]);
|
||||||
|
payload[12..16].copy_from_slice(&3_u32.to_le_bytes());
|
||||||
|
append_typed_record(&mut payload, 2, 0);
|
||||||
|
append_typed_record(&mut payload, 5, 2);
|
||||||
|
payload.extend_from_slice(b"fx\0");
|
||||||
|
append_typed_record(&mut payload, 21, 0);
|
||||||
|
|
||||||
|
let control = decode_component_control_payload(&payload, source, 2, 0)
|
||||||
|
.expect("tags 2 and 5 share the native typed cursor parser");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
control
|
||||||
|
.typed_records
|
||||||
|
.iter()
|
||||||
|
.map(|record| record.tag)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
[2, 5, 21],
|
||||||
|
"tag 21 must begin at the cursor returned after tag 5's string"
|
||||||
|
);
|
||||||
|
assert_eq!(control.typed_records[1].string_bytes, b"fx");
|
||||||
|
assert_eq!(
|
||||||
|
control.effect_section,
|
||||||
|
PreparedControlEffectSection::Parsed(Vec::new())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn component_control_malformed_recognized_typed_tail_is_rejected() {
|
fn component_control_malformed_recognized_typed_tail_is_rejected() {
|
||||||
let source = ResourceKey {
|
let source = ResourceKey {
|
||||||
|
|||||||
@@ -424,6 +424,15 @@ type 4, в том числе четыре `f_smalllight_y` с offsets 104–107
|
|||||||
offsets 119–121. В `fr_l_gener.ctl` найдено 35 строк type 4, включая
|
offsets 119–121. В `fr_l_gener.ctl` найдено 35 строк type 4, включая
|
||||||
`f_gener_ball` с offset 85.
|
`f_gener_ball` с offset 85.
|
||||||
|
|
||||||
|
Control.dll `+0x8B10` передаёт указатель текущей записи диспетчеру `+0x2D4B0`.
|
||||||
|
Теги 2 и 5 направляются в общий читатель `+0x21D50`: таблица виртуальных методов
|
||||||
|
тега 2 (`+0x3CBD8`) вызывает его через обёртку `+0x29650`, а таблица тега 5
|
||||||
|
(`+0x3CDF8`) указывает прямо на него в позиции `+0x1C`. Читатель потребляет
|
||||||
|
заголовок размером `0xB0` байт со счётчиком `u32` по `+0xAC`, затем `count`
|
||||||
|
значений `u32`, слово длины и, при ненулевой длине, строку с завершающим NUL;
|
||||||
|
указатель возврата указывает на следующую запись. Поэтому для тегов 2 и 5 подтверждён общий
|
||||||
|
структурный формат; смысл их полей пока не назначен.
|
||||||
|
|
||||||
Resolver прототипов хранит явную registry-ссылку на `.cpt` отдельно от ссылки
|
Resolver прототипов хранит явную registry-ссылку на `.cpt` отдельно от ссылки
|
||||||
на `.ctl`. Если строки type 4 разобраны и явная ссылка на CPT есть, подготовка
|
на `.ctl`. Если строки type 4 разобраны и явная ссылка на CPT есть, подготовка
|
||||||
читает ровно указанные archive/name; имя CPT не выводится из имени CTL. В CPT
|
читает ровно указанные archive/name; имя CPT не выводится из имени CTL. В CPT
|
||||||
|
|||||||
Reference in new issue
Block a user