Widen save-world finance neighborhood window

This commit is contained in:
Jan Petykiewicz 2026-04-17 21:01:45 -07:00
commit a73a789bac
4 changed files with 42 additions and 15 deletions

View file

@ -66,6 +66,9 @@ dividend / stock-capital logic can extend one owned market reader instead of ano
counter. The next bundled annual-finance reader seam is now rehosted on top of that same market
state too, deriving assigned shares, public float, and rounded cached share price from one shared
company market reader instead of scattering more finance helpers across the runtime. A checked-in
The fixed-world finance neighborhood itself is now widened to 16 dwords rooted at `[world+0x11]`,
so future issue-`0x38/0x39` closure can build on a broader owned restore-state window rather than
another narrow one-off probe. A checked-in
The working rule on the remaining frontier is explicit now too: when a lane is still ambiguous, we
should prefer rehosting the owning source state or the real reader/setter family rather than
guessing one more derived leaf field from nearby offsets. A checked-in

View file

@ -109,6 +109,8 @@ const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS: [(&str, usize)
("issue_neighbor_candidate_1", 0x31),
("issue_neighbor_candidate_2", 0x35),
];
const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_ROOT_RELATIVE_OFFSET: usize = 0x11;
const RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS: usize = 16;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_SLOT_SELECTOR_RELATIVE_OFFSET: usize = 0x83;
const RT3_SAVE_WORLD_BLOCK_CAMPAIGN_OVERRIDE_FLAG_RELATIVE_OFFSET: usize = 0xc1;
const RT3_SAVE_WORLD_BLOCK_CHAIRMAN_ROLE_GATE_RELATIVE_OFFSET: usize = 0x0bbf;
@ -8705,12 +8707,8 @@ fn parse_save_world_finance_neighborhood_probe(
continue;
}
let dword_candidates = RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS
.iter()
.map(|(label, relative_offset)| {
build_save_dword_candidate(bytes, payload_offset, label, *relative_offset)
})
.collect::<Option<Vec<_>>>()?;
let dword_candidates =
build_save_world_finance_neighborhood_candidates(bytes, payload_offset)?;
return Some(SmpSaveWorldFinanceNeighborhoodProbe {
profile_family: profile.profile_family.clone(),
@ -8736,6 +8734,24 @@ fn parse_save_world_finance_neighborhood_probe(
None
}
fn build_save_world_finance_neighborhood_candidates(
bytes: &[u8],
payload_offset: usize,
) -> Option<Vec<SmpSaveDwordCandidate>> {
(0..RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS)
.map(|index| {
let relative_offset =
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_ROOT_RELATIVE_OFFSET + index * 4;
let label = RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS
.iter()
.find(|(_, named_offset)| *named_offset == relative_offset)
.map(|(name, _)| (*name).to_string())
.unwrap_or_else(|| format!("finance_neighborhood_word_{:02}", index + 1));
build_save_dword_candidate(bytes, payload_offset, &label, relative_offset)
})
.collect()
}
fn parse_save_world_economic_tuning_probe(
bytes: &[u8],
file_extension_hint: Option<&str>,
@ -15431,12 +15447,10 @@ mod tests {
let payload_offset = chunk_tag_offset + 4;
bytes[chunk_tag_offset..chunk_tag_offset + 4]
.copy_from_slice(&RT3_SAVE_WORLD_BLOCK_CHUNK_TAG.to_le_bytes());
for (index, (_, relative_offset)) in
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS
.iter()
.enumerate()
{
bytes[payload_offset + *relative_offset..payload_offset + *relative_offset + 4]
for index in 0..RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS {
let relative_offset =
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_ROOT_RELATIVE_OFFSET + index * 4;
bytes[payload_offset + relative_offset..payload_offset + relative_offset + 4]
.copy_from_slice(&((index as u32) + 1).to_le_bytes());
}
let next_chunk_offset = payload_offset + RT3_SAVE_WORLD_BLOCK_LEN;
@ -15458,7 +15472,7 @@ mod tests {
assert_eq!(probe.payload_offset, payload_offset);
assert_eq!(
probe.dword_candidates.len(),
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS.len()
RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS
);
assert_eq!(
probe.dword_candidates[0].label,
@ -15474,6 +15488,12 @@ mod tests {
);
assert_eq!(probe.dword_candidates[9].relative_offset_hex, "0x35");
assert_eq!(probe.dword_candidates[9].value_i32, 10);
assert_eq!(probe.dword_candidates[10].label, "finance_neighborhood_word_11");
assert_eq!(probe.dword_candidates[10].relative_offset_hex, "0x39");
assert_eq!(probe.dword_candidates[10].value_i32, 11);
assert_eq!(probe.dword_candidates[15].label, "finance_neighborhood_word_16");
assert_eq!(probe.dword_candidates[15].relative_offset_hex, "0x4d");
assert_eq!(probe.dword_candidates[15].value_i32, 16);
}
#[test]

View file

@ -121,7 +121,9 @@ The highest-value next passes are now:
seam is now rehosted for the grounded `0x37` lane, and selected-company summaries now expose the
unassigned share pool derived from outstanding shares minus chairman-held shares for later annual
finance logic; that same owned company market state now also backs a bundled annual-finance
reader seam for assigned shares, public float, and rounded cached share price
reader seam for assigned shares, public float, and rounded cached share price; the fixed-world
finance neighborhood is now widened to 16 dwords rooted at `[world+0x11]` so later issue-family
closure can target a broader owned restore-state window
- the project rule on the remaining closure work is now explicit too: when one runtime-facing field
is still ambiguous, prefer rehosting the owning source state or real reader/setter family first
instead of guessing another derived leaf field from neighboring raw offsets

View file

@ -209,7 +209,9 @@ chairman-held shares, so later dividend / stock-capital work can extend a shared
reader instead of guessing another finance leaf. The same owned company market state now also
supports a bundled annual-finance reader seam for assigned shares, public float, and rounded
cached share price, which is a better base for later dividend / issue-calendar simulation than
scattered single-field helpers.
scattered single-field helpers. The fixed-world finance neighborhood is now widened to 16 dwords
rooted at `[world+0x11]`, so later issue-`0x38/0x39` closure can build on a broader owned
restore-state window instead of another narrow probe.
## Why This Boundary