Rehost selected-year bucket companion bands
This commit is contained in:
parent
f9b3cf8571
commit
b5d194f678
9 changed files with 326 additions and 20 deletions
|
|
@ -129,7 +129,10 @@ living only as ad hoc world flags.
|
|||
The selected-year seam is now doing the same thing: the checked-in `0x00433bd0` year ladder now
|
||||
drives a derived selected-year bucket scalar in runtime restore state, and the economic-tuning
|
||||
mirror `[world+0x0bde]` now rebuilds from tuning lane `0` instead of freezing one stale load-time
|
||||
word.
|
||||
word. That same checked-in owner family now also rebuilds the direct bucket trio
|
||||
`[world+0x65/+0x69/+0x6d]`, the complement trio `[world+0x71/+0x75/+0x79]`, and the scaled
|
||||
companion trio `[world+0x7d/+0x81/+0x85]` from the selected-year bucket scalar instead of
|
||||
preserving stale save-time residue.
|
||||
Those bankruptcy branches now follow the grounded owner semantics too: they stamp the bankruptcy
|
||||
year and halve live bond principals in place instead of treating bankruptcy as a liquidation path.
|
||||
The same save-native live bond-slot surface now also carries per-slot maturity years all the way
|
||||
|
|
|
|||
|
|
@ -5,6 +5,26 @@
|
|||
"pair_count": 21,
|
||||
"terminal_scalar_virtual_address": "0x005f3a24",
|
||||
"terminal_scalar_value": 123.0,
|
||||
"direct_lane_multipliers": [
|
||||
0.9,
|
||||
1.05,
|
||||
0.7
|
||||
],
|
||||
"complement_formula": {
|
||||
"divisor": 50.0,
|
||||
"multiplier": 0.65,
|
||||
"bias": 0.35,
|
||||
"scale": 0.001190483570098877,
|
||||
"floor": 0.0001,
|
||||
"build_106_multiplier": 1.15,
|
||||
"cap": 0.15
|
||||
},
|
||||
"scaled_companion_formula": {
|
||||
"numerator": 50.0,
|
||||
"multiplier": 0.7,
|
||||
"bias": 0.3,
|
||||
"scale": 75.0
|
||||
},
|
||||
"entries": [
|
||||
{
|
||||
"year": 1800,
|
||||
|
|
|
|||
|
|
@ -982,6 +982,12 @@ fn project_save_slice_components(
|
|||
.cached_available_locomotive_rating_value_f32_text
|
||||
.clone()
|
||||
}),
|
||||
selected_year_bucket_direct_lane_raw_u32: Vec::new(),
|
||||
selected_year_bucket_direct_lane_value_f32_text: Vec::new(),
|
||||
selected_year_bucket_complement_lane_raw_u32: Vec::new(),
|
||||
selected_year_bucket_complement_lane_value_f32_text: Vec::new(),
|
||||
selected_year_bucket_scaled_companion_lane_raw_u32: Vec::new(),
|
||||
selected_year_bucket_scaled_companion_lane_value_f32_text: Vec::new(),
|
||||
selected_year_bucket_scalar_raw_u32: None,
|
||||
selected_year_bucket_scalar_value_f32_text: None,
|
||||
selected_year_gap_scalar_raw_u32: None,
|
||||
|
|
|
|||
|
|
@ -1423,6 +1423,18 @@ pub struct RuntimeWorldRestoreState {
|
|||
#[serde(default)]
|
||||
pub selected_year_bucket_scalar_value_f32_text: Option<String>,
|
||||
#[serde(default)]
|
||||
pub selected_year_bucket_direct_lane_raw_u32: Vec<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_year_bucket_direct_lane_value_f32_text: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub selected_year_bucket_complement_lane_raw_u32: Vec<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_year_bucket_complement_lane_value_f32_text: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub selected_year_bucket_scaled_companion_lane_raw_u32: Vec<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_year_bucket_scaled_companion_lane_value_f32_text: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub selected_year_gap_scalar_raw_u32: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_year_gap_scalar_value_f32_text: Option<String>,
|
||||
|
|
@ -2423,6 +2435,37 @@ impl RuntimeState {
|
|||
self.world_restore.selected_year_bucket_scalar_raw_u32 = Some(value.to_bits());
|
||||
self.world_restore
|
||||
.selected_year_bucket_scalar_value_f32_text = Some(format!("{value:.6}"));
|
||||
if let Some(bands) = runtime_selected_year_bucket_bands_from_scalar(value) {
|
||||
self.world_restore.selected_year_bucket_direct_lane_raw_u32 =
|
||||
bands.direct.iter().map(|lane| lane.to_bits()).collect();
|
||||
self.world_restore
|
||||
.selected_year_bucket_direct_lane_value_f32_text = bands
|
||||
.direct
|
||||
.iter()
|
||||
.map(|lane| format!("{lane:.6}"))
|
||||
.collect();
|
||||
self.world_restore
|
||||
.selected_year_bucket_complement_lane_raw_u32 =
|
||||
bands.complement.iter().map(|lane| lane.to_bits()).collect();
|
||||
self.world_restore
|
||||
.selected_year_bucket_complement_lane_value_f32_text = bands
|
||||
.complement
|
||||
.iter()
|
||||
.map(|lane| format!("{lane:.6}"))
|
||||
.collect();
|
||||
self.world_restore
|
||||
.selected_year_bucket_scaled_companion_lane_raw_u32 = bands
|
||||
.scaled_companion
|
||||
.iter()
|
||||
.map(|lane| lane.to_bits())
|
||||
.collect();
|
||||
self.world_restore
|
||||
.selected_year_bucket_scaled_companion_lane_value_f32_text = bands
|
||||
.scaled_companion
|
||||
.iter()
|
||||
.map(|lane| format!("{lane:.6}"))
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
if let Some(value) = runtime_world_selected_year_gap_scalar_from_year_word(year_word) {
|
||||
self.world_restore.selected_year_gap_scalar_raw_u32 = Some(value.to_bits());
|
||||
|
|
@ -2455,6 +2498,9 @@ impl RuntimeState {
|
|||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct CheckedInSelectedYearBucketLadderArtifact {
|
||||
direct_lane_multipliers: Vec<f32>,
|
||||
complement_formula: CheckedInSelectedYearBucketComplementFormula,
|
||||
scaled_companion_formula: CheckedInSelectedYearBucketScaledCompanionFormula,
|
||||
entries: Vec<CheckedInSelectedYearBucketLadderEntry>,
|
||||
}
|
||||
|
||||
|
|
@ -2464,21 +2510,44 @@ struct CheckedInSelectedYearBucketLadderEntry {
|
|||
value: f32,
|
||||
}
|
||||
|
||||
fn checked_in_selected_year_bucket_ladder() -> &'static [CheckedInSelectedYearBucketLadderEntry] {
|
||||
static LADDER: OnceLock<Vec<CheckedInSelectedYearBucketLadderEntry>> = OnceLock::new();
|
||||
LADDER
|
||||
.get_or_init(|| {
|
||||
serde_json::from_str::<CheckedInSelectedYearBucketLadderArtifact>(include_str!(
|
||||
"../../../artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json"
|
||||
))
|
||||
.expect("checked-in selected-year bucket ladder should parse")
|
||||
.entries
|
||||
})
|
||||
.as_slice()
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct CheckedInSelectedYearBucketComplementFormula {
|
||||
divisor: f32,
|
||||
multiplier: f32,
|
||||
bias: f32,
|
||||
scale: f32,
|
||||
floor: f32,
|
||||
build_106_multiplier: f32,
|
||||
cap: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct CheckedInSelectedYearBucketScaledCompanionFormula {
|
||||
numerator: f32,
|
||||
multiplier: f32,
|
||||
bias: f32,
|
||||
scale: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct RuntimeSelectedYearBucketBands {
|
||||
direct: [f32; 3],
|
||||
complement: [f32; 3],
|
||||
scaled_companion: [f32; 3],
|
||||
}
|
||||
|
||||
fn checked_in_selected_year_bucket_ladder() -> &'static CheckedInSelectedYearBucketLadderArtifact {
|
||||
static LADDER: OnceLock<CheckedInSelectedYearBucketLadderArtifact> = OnceLock::new();
|
||||
LADDER.get_or_init(|| {
|
||||
serde_json::from_str::<CheckedInSelectedYearBucketLadderArtifact>(include_str!(
|
||||
"../../../artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json"
|
||||
))
|
||||
.expect("checked-in selected-year bucket ladder should parse")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn runtime_world_selected_year_bucket_scalar_from_year_word(year_word: u32) -> Option<f32> {
|
||||
let ladder = checked_in_selected_year_bucket_ladder();
|
||||
let ladder = &checked_in_selected_year_bucket_ladder().entries;
|
||||
if ladder.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -2500,6 +2569,41 @@ pub fn runtime_world_selected_year_bucket_scalar_from_year_word(year_word: u32)
|
|||
ladder.last().map(|entry| entry.value)
|
||||
}
|
||||
|
||||
fn runtime_selected_year_bucket_bands_from_scalar(
|
||||
scalar: f32,
|
||||
) -> Option<RuntimeSelectedYearBucketBands> {
|
||||
let artifact = checked_in_selected_year_bucket_ladder();
|
||||
if artifact.direct_lane_multipliers.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
let direct = [
|
||||
scalar * artifact.direct_lane_multipliers[0],
|
||||
scalar * artifact.direct_lane_multipliers[1],
|
||||
scalar * artifact.direct_lane_multipliers[2],
|
||||
];
|
||||
let mut complement = [0.0f32; 3];
|
||||
let mut scaled_companion = [0.0f32; 3];
|
||||
for (index, direct_value) in direct.iter().copied().enumerate() {
|
||||
let mut x = (((direct_value / artifact.complement_formula.divisor)
|
||||
* artifact.complement_formula.multiplier)
|
||||
+ artifact.complement_formula.bias)
|
||||
* artifact.complement_formula.scale;
|
||||
x = x.max(artifact.complement_formula.floor);
|
||||
x *= artifact.complement_formula.build_106_multiplier;
|
||||
x = x.min(artifact.complement_formula.cap);
|
||||
complement[index] = 1.0 - x;
|
||||
scaled_companion[index] = (((artifact.scaled_companion_formula.numerator / direct_value)
|
||||
* artifact.scaled_companion_formula.multiplier)
|
||||
+ artifact.scaled_companion_formula.bias)
|
||||
* artifact.scaled_companion_formula.scale;
|
||||
}
|
||||
Some(RuntimeSelectedYearBucketBands {
|
||||
direct,
|
||||
complement,
|
||||
scaled_companion,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn runtime_world_selected_year_gap_scalar_from_year_word(year_word: u32) -> Option<f32> {
|
||||
let normalized = (year_word as f64 - 1850.0) / 150.0;
|
||||
if !normalized.is_finite() {
|
||||
|
|
@ -5134,6 +5238,12 @@ mod tests {
|
|||
economic_tuning_lane_value_f32_text: Vec::new(),
|
||||
selected_year_bucket_scalar_raw_u32: None,
|
||||
selected_year_bucket_scalar_value_f32_text: None,
|
||||
selected_year_bucket_direct_lane_raw_u32: Vec::new(),
|
||||
selected_year_bucket_direct_lane_value_f32_text: Vec::new(),
|
||||
selected_year_bucket_complement_lane_raw_u32: Vec::new(),
|
||||
selected_year_bucket_complement_lane_value_f32_text: Vec::new(),
|
||||
selected_year_bucket_scaled_companion_lane_raw_u32: Vec::new(),
|
||||
selected_year_bucket_scaled_companion_lane_value_f32_text: Vec::new(),
|
||||
selected_year_gap_scalar_raw_u32: None,
|
||||
selected_year_gap_scalar_value_f32_text: None,
|
||||
absolute_counter_restore_kind: Some(
|
||||
|
|
@ -8136,6 +8246,13 @@ mod tests {
|
|||
runtime_world_selected_year_bucket_scalar_from_year_word(2000),
|
||||
Some(123.0)
|
||||
);
|
||||
let bands = runtime_selected_year_bucket_bands_from_scalar(25.0)
|
||||
.expect("selected-year bucket companion bands");
|
||||
assert!((bands.direct[0] - 22.5).abs() < 1e-6);
|
||||
assert!((bands.direct[1] - 26.25).abs() < 1e-5);
|
||||
assert!((bands.direct[2] - 17.5).abs() < 1e-6);
|
||||
assert!((bands.complement[0] - 0.999121).abs() < 1e-6);
|
||||
assert!((bands.scaled_companion[0] - 139.16667).abs() < 1e-4);
|
||||
assert_eq!(
|
||||
runtime_world_selected_year_gap_scalar_from_year_word(1830),
|
||||
Some((1.0f32 / 3.0).clamp(1.0 / 3.0, 1.0))
|
||||
|
|
@ -8224,6 +8341,36 @@ mod tests {
|
|||
.as_deref(),
|
||||
Some("70.000000")
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.world_restore
|
||||
.selected_year_bucket_direct_lane_value_f32_text,
|
||||
vec![
|
||||
"63.000000".to_string(),
|
||||
"73.500000".to_string(),
|
||||
"49.000000".to_string()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.world_restore
|
||||
.selected_year_bucket_complement_lane_value_f32_text,
|
||||
vec![
|
||||
"0.998400".to_string(),
|
||||
"0.998213".to_string(),
|
||||
"0.998649".to_string()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
state
|
||||
.world_restore
|
||||
.selected_year_bucket_scaled_companion_lane_value_f32_text,
|
||||
vec![
|
||||
"64.166672".to_string(),
|
||||
"58.214291".to_string(),
|
||||
"76.071426".to_string()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
state.world_restore.selected_year_gap_scalar_raw_u32,
|
||||
Some(((50.0f32 / 150.0).clamp(1.0 / 3.0, 1.0)).to_bits())
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@ pub struct RuntimeSummary {
|
|||
pub world_restore_cached_available_locomotive_rating_value_f32_text: Option<String>,
|
||||
pub world_restore_selected_year_bucket_scalar_raw_u32: Option<u32>,
|
||||
pub world_restore_selected_year_bucket_scalar_value_f32_text: Option<String>,
|
||||
pub world_restore_selected_year_bucket_direct_lane_count: usize,
|
||||
pub world_restore_selected_year_bucket_direct_lane_value_f32_text: Vec<String>,
|
||||
pub world_restore_selected_year_bucket_complement_lane_count: usize,
|
||||
pub world_restore_selected_year_bucket_complement_lane_value_f32_text: Vec<String>,
|
||||
pub world_restore_selected_year_bucket_scaled_companion_lane_count: usize,
|
||||
pub world_restore_selected_year_bucket_scaled_companion_lane_value_f32_text: Vec<String>,
|
||||
pub world_restore_selected_year_gap_scalar_raw_u32: Option<u32>,
|
||||
pub world_restore_selected_year_gap_scalar_value_f32_text: Option<String>,
|
||||
pub world_restore_absolute_counter_restore_kind: Option<String>,
|
||||
|
|
@ -480,6 +486,30 @@ impl RuntimeSummary {
|
|||
.world_restore
|
||||
.selected_year_bucket_scalar_value_f32_text
|
||||
.clone(),
|
||||
world_restore_selected_year_bucket_direct_lane_count: state
|
||||
.world_restore
|
||||
.selected_year_bucket_direct_lane_raw_u32
|
||||
.len(),
|
||||
world_restore_selected_year_bucket_direct_lane_value_f32_text: state
|
||||
.world_restore
|
||||
.selected_year_bucket_direct_lane_value_f32_text
|
||||
.clone(),
|
||||
world_restore_selected_year_bucket_complement_lane_count: state
|
||||
.world_restore
|
||||
.selected_year_bucket_complement_lane_raw_u32
|
||||
.len(),
|
||||
world_restore_selected_year_bucket_complement_lane_value_f32_text: state
|
||||
.world_restore
|
||||
.selected_year_bucket_complement_lane_value_f32_text
|
||||
.clone(),
|
||||
world_restore_selected_year_bucket_scaled_companion_lane_count: state
|
||||
.world_restore
|
||||
.selected_year_bucket_scaled_companion_lane_raw_u32
|
||||
.len(),
|
||||
world_restore_selected_year_bucket_scaled_companion_lane_value_f32_text: state
|
||||
.world_restore
|
||||
.selected_year_bucket_scaled_companion_lane_value_f32_text
|
||||
.clone(),
|
||||
world_restore_selected_year_gap_scalar_raw_u32: state
|
||||
.world_restore
|
||||
.selected_year_gap_scalar_raw_u32,
|
||||
|
|
@ -1818,6 +1848,36 @@ mod tests {
|
|||
cached_available_locomotive_rating_value_f32_text: Some("20.000000".to_string()),
|
||||
selected_year_bucket_scalar_raw_u32: Some(25.0f32.to_bits()),
|
||||
selected_year_bucket_scalar_value_f32_text: Some("25.000000".to_string()),
|
||||
selected_year_bucket_direct_lane_raw_u32: vec![
|
||||
22.5f32.to_bits(),
|
||||
26.25f32.to_bits(),
|
||||
17.5f32.to_bits(),
|
||||
],
|
||||
selected_year_bucket_direct_lane_value_f32_text: vec![
|
||||
"22.500000".to_string(),
|
||||
"26.250000".to_string(),
|
||||
"17.500000".to_string(),
|
||||
],
|
||||
selected_year_bucket_complement_lane_raw_u32: vec![
|
||||
0.999121f32.to_bits(),
|
||||
0.998998f32.to_bits(),
|
||||
0.999210f32.to_bits(),
|
||||
],
|
||||
selected_year_bucket_complement_lane_value_f32_text: vec![
|
||||
"0.999121".to_string(),
|
||||
"0.998998".to_string(),
|
||||
"0.999210".to_string(),
|
||||
],
|
||||
selected_year_bucket_scaled_companion_lane_raw_u32: vec![
|
||||
139.16667f32.to_bits(),
|
||||
122.5f32.to_bits(),
|
||||
171.42857f32.to_bits(),
|
||||
],
|
||||
selected_year_bucket_scaled_companion_lane_value_f32_text: vec![
|
||||
"139.166672".to_string(),
|
||||
"122.500000".to_string(),
|
||||
"171.428574".to_string(),
|
||||
],
|
||||
selected_year_gap_scalar_raw_u32: Some(0x3eaaaaab),
|
||||
selected_year_gap_scalar_value_f32_text: Some("0.333333".to_string()),
|
||||
..RuntimeWorldRestoreState::default()
|
||||
|
|
@ -1982,6 +2042,30 @@ mod tests {
|
|||
.as_deref(),
|
||||
Some("25.000000")
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_selected_year_bucket_direct_lane_count,
|
||||
3
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_selected_year_bucket_direct_lane_value_f32_text,
|
||||
vec!["22.500000", "26.250000", "17.500000"]
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_selected_year_bucket_complement_lane_count,
|
||||
3
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_selected_year_bucket_complement_lane_value_f32_text,
|
||||
vec!["0.999121", "0.998998", "0.999210"]
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_selected_year_bucket_scaled_companion_lane_count,
|
||||
3
|
||||
);
|
||||
assert_eq!(
|
||||
summary.world_restore_selected_year_bucket_scaled_companion_lane_value_f32_text,
|
||||
vec!["139.166672", "122.500000", "171.428574"]
|
||||
);
|
||||
assert_eq!(summary.world_restore_economic_tuning_lane_count, 6);
|
||||
assert_eq!(
|
||||
summary.world_restore_economic_tuning_lane_value_f32_text,
|
||||
|
|
|
|||
|
|
@ -177,6 +177,10 @@ The highest-value next passes are now:
|
|||
drives a derived selected-year bucket scalar in runtime restore state, and the economic-tuning
|
||||
mirror `[world+0x0bde]` now rebuilds from tuning lane `0` instead of freezing one stale
|
||||
load-time word
|
||||
- that same selected-year owner family now also rebuilds the direct bucket trio
|
||||
`[world+0x65/+0x69/+0x6d]`, the complement trio `[world+0x71/+0x75/+0x79]`, and the scaled
|
||||
companion trio `[world+0x7d/+0x81/+0x85]` from the bucket scalar instead of preserving stale
|
||||
save-time residue
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -9,12 +9,9 @@ Working rule:
|
|||
|
||||
## Next
|
||||
|
||||
- Rehost the next selected-year periodic-boundary world seam under
|
||||
`simulation_service_periodic_boundary_work`, extending the now-grounded selected-year bucket
|
||||
scalar into the direct bucket trio `[world+0x65/+0x69/+0x6d]` and any safe follow-on companion
|
||||
lanes rooted in `0x00433bd0`.
|
||||
- Expand the selected-year world-owner surface beyond the stepped calendar, gap scalar,
|
||||
bucket-scalar, mirror, and locomotive-policy lanes when the owning reader/rebuild family is
|
||||
- Rehost the next periodic-boundary world seam after the selected-year bucket family, favoring
|
||||
owner-state rebuilds that can advance shellless simulation without freezing load-time residue.
|
||||
- Keep widening selected-year world-owner state only when a full owning reader/rebuild family is
|
||||
grounded strongly enough to avoid one-off leaf guesses.
|
||||
|
||||
## In Progress
|
||||
|
|
@ -51,6 +48,10 @@ Working rule:
|
|||
and runtime restore state now derives both the selected-year bucket scalar and the
|
||||
`[world+0x0bde]` economic-tuning mirror from owner-family inputs instead of preserving stale
|
||||
load-time residue.
|
||||
- That same selected-year owner family now also rebuilds the direct bucket trio
|
||||
`[world+0x65/+0x69/+0x6d]`, the complement trio `[world+0x71/+0x75/+0x79]`, and the scaled
|
||||
companion trio `[world+0x7d/+0x81/+0x85]` from the checked-in `0x00433bd0` artifact instead of
|
||||
preserving stale save-time residue.
|
||||
- Company cash, confiscation, and major governance effects now write through owner state instead of
|
||||
drifting from market/cache readers.
|
||||
- Company credit rating, prime rate, book value per share, investor confidence, and management
|
||||
|
|
|
|||
|
|
@ -223,7 +223,10 @@ the fixed world block, so the `All Steam/Diesel/Electric Locos Avail.` descripto
|
|||
through owner state instead of living only as mirrored world flags. The selected-year seam now
|
||||
follows the same owner rule: the checked-in `0x00433bd0` year ladder now drives a derived
|
||||
selected-year bucket scalar in runtime restore state, and the economic-tuning mirror `[world+0x0bde]`
|
||||
now rebuilds from tuning lane `0` instead of freezing one stale load-time word. The same owned company annual-finance state
|
||||
now rebuilds from tuning lane `0` instead of freezing one stale load-time word. That same
|
||||
checked-in owner family now also rebuilds the direct bucket trio `[world+0x65/+0x69/+0x6d]`, the
|
||||
complement trio `[world+0x71/+0x75/+0x79]`, and the scaled companion trio `[world+0x7d/+0x81/+0x85]`
|
||||
from the bucket scalar instead of preserving stale save-time residue. The same owned company annual-finance state
|
||||
now also drives a shared company market reader seam for stock-capital, salary, bonus, and the full
|
||||
two-word current/prior issue-calendar tuples, which is a better base for shellless finance
|
||||
simulation than summary-only helpers. That same owned annual-finance state now also derives elapsed
|
||||
|
|
|
|||
|
|
@ -10,6 +10,18 @@ IMAGE_BASE = 0x400000
|
|||
PAIRED_YEAR_VALUE_TABLE_VA = 0x005F3980
|
||||
PAIR_COUNT = 21
|
||||
TERMINAL_SCALAR_VA = 0x005F3A24
|
||||
DIRECT_MULTIPLIER_VAS = [0x005C8888, 0x005C9ED8, 0x005C8680]
|
||||
COMPLEMENT_DIVISOR_VA = 0x005C8A20
|
||||
COMPLEMENT_MULTIPLIER_VA = 0x005C8DA8
|
||||
COMPLEMENT_BIAS_VA = 0x005C8618
|
||||
COMPLEMENT_SCALE_VA = 0x005C9ED0
|
||||
COMPLEMENT_FLOOR_VA = 0x005C88A0
|
||||
COMPLEMENT_BUILD_106_MULTIPLIER_VA = 0x005C8878
|
||||
COMPLEMENT_CAP_VA = 0x005C8988
|
||||
SCALED_COMPANION_NUMERATOR_VA = 0x005C8A20
|
||||
SCALED_COMPANION_MULTIPLIER_VA = 0x005C8680
|
||||
SCALED_COMPANION_BIAS_VA = 0x005C88C8
|
||||
SCALED_COMPANION_SCALE_VA = 0x005C9EC8
|
||||
|
||||
|
||||
def read_u32_table(blob: bytes, va: int, count: int) -> list[int]:
|
||||
|
|
@ -20,6 +32,14 @@ def read_u32_table(blob: bytes, va: int, count: int) -> list[int]:
|
|||
return [struct.unpack("<I", data[i : i + 4])[0] for i in range(0, len(data), 4)]
|
||||
|
||||
|
||||
def read_f64(blob: bytes, va: int) -> float:
|
||||
offset = va - IMAGE_BASE
|
||||
data = blob[offset : offset + 8]
|
||||
if len(data) != 8:
|
||||
raise ValueError(f"f64 at {va:#x} truncated")
|
||||
return struct.unpack("<d", data)[0]
|
||||
|
||||
|
||||
def build_artifact(exe_bytes: bytes) -> dict[str, object]:
|
||||
raw_pairs = read_u32_table(exe_bytes, PAIRED_YEAR_VALUE_TABLE_VA, PAIR_COUNT * 2)
|
||||
terminal_scalar_raw = read_u32_table(exe_bytes, TERMINAL_SCALAR_VA, 1)[0]
|
||||
|
|
@ -33,6 +53,24 @@ def build_artifact(exe_bytes: bytes) -> dict[str, object]:
|
|||
"pair_count": PAIR_COUNT,
|
||||
"terminal_scalar_virtual_address": f"0x{TERMINAL_SCALAR_VA:08x}",
|
||||
"terminal_scalar_value": float(terminal_scalar_raw),
|
||||
"direct_lane_multipliers": [
|
||||
read_f64(exe_bytes, va) for va in DIRECT_MULTIPLIER_VAS
|
||||
],
|
||||
"complement_formula": {
|
||||
"divisor": read_f64(exe_bytes, COMPLEMENT_DIVISOR_VA),
|
||||
"multiplier": read_f64(exe_bytes, COMPLEMENT_MULTIPLIER_VA),
|
||||
"bias": read_f64(exe_bytes, COMPLEMENT_BIAS_VA),
|
||||
"scale": read_f64(exe_bytes, COMPLEMENT_SCALE_VA),
|
||||
"floor": read_f64(exe_bytes, COMPLEMENT_FLOOR_VA),
|
||||
"build_106_multiplier": read_f64(exe_bytes, COMPLEMENT_BUILD_106_MULTIPLIER_VA),
|
||||
"cap": read_f64(exe_bytes, COMPLEMENT_CAP_VA),
|
||||
},
|
||||
"scaled_companion_formula": {
|
||||
"numerator": read_f64(exe_bytes, SCALED_COMPANION_NUMERATOR_VA),
|
||||
"multiplier": read_f64(exe_bytes, SCALED_COMPANION_MULTIPLIER_VA),
|
||||
"bias": read_f64(exe_bytes, SCALED_COMPANION_BIAS_VA),
|
||||
"scale": read_f64(exe_bytes, SCALED_COMPANION_SCALE_VA),
|
||||
},
|
||||
"entries": entries,
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue