Add hook debug tooling and refine RT3 atlas
This commit is contained in:
parent
860d1aed90
commit
57bf0666e0
38 changed files with 14437 additions and 873 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -118,12 +118,19 @@ name = "rrt-cli"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rrt-model",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rrt-hook"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rrt-model",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rrt-model"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,2 @@
|
|||
query_address,function_address,name,size,calling_convention,signature,caller_count,callers,callee_count,callees,data_ref_count,data_refs,entry_excerpt
|
||||
0x00595440,0x00595440,multiplayer_transport_init_selector_slot,100,cdecl,"fcn.00595440(int32_t arg_4h, int32_t arg_39ch, int32_t arg_59bh);",2,0x00593841@0x00593790:multiplayer_transport_handle_names_query_response; 0x00593b25@0x00593b00:multiplayer_transport_handle_selector_update_response,3,0x00595490->0x005951f0:multiplayer_transport_service_status_pump; 0x00595499->0x00596fc0:multiplayer_transport_submit_profile_key_query_bundle_default; 0x0059547f->0x005a18a0:string_copy_bounded_zerofill,1,0x0059544f->0x005c87a8," 595420: movl $0x89ffff98, %esp # imm = 0x89FFFF98 | 595425: movl $0x9a4, %esi # imm = 0x9A4 | 59542a: popl %edi | 59542b: popl %esi | 59542c: addl $0x190, %esp # imm = 0x190 | 595432: retl | 595433: nop | 595434: nop | 595435: nop | 595436: nop | 595437: nop | 595438: nop | 595439: nop | 59543a: nop | 59543b: nop | 59543c: nop | 59543d: nop | 59543e: nop | 59543f: nop | 595440: movl 0x4(%esp), %eax | 595444: testl %eax, %eax | 595446: pushl %ebp | 595447: pushl %esi | 595448: pushl %edi | 595449: movl %edx, %edi | 59544b: movl %ecx, %esi | 59544d: jne 0x595454 <.text+0x194454> | 59544f: movl $0x5c87a8, %eax # imm = 0x5C87A8 | 595454: movl %edi, %ecx | 595456: shll $0x9, %ecx | 595459: leal (%ecx,%esi), %ebp | 59545c: pushl $0x200 # imm = 0x200"
|
||||
0x00596da0,0x00596da0,multiplayer_transport_submit_profile_key_query_bundle,497,cdecl,fcn.00596da0(uint_least32_t arg_4h);,2,0x00596faa@0x00596fa0:multiplayer_transport_submit_profile_key_query_bundle_with_context; 0x00596fc7@0x00596fc0:multiplayer_transport_submit_profile_key_query_bundle_default,13,0x00596eb6->0x0058ec50:multiplayer_transport_submit_getkey_command_and_wait; 0x00596f7a->0x0058ef20:multiplayer_transport_submit_setchankey_pair_list_command_and_wait; 0x00596e68->0x0058f380:tracked_heap_alloc_with_header; 0x00596f03->0x0058f380:tracked_heap_alloc_with_header; 0x00596ebf->0x0058f3c0:tracked_heap_free_with_header; 0x00596f83->0x0058f3c0:tracked_heap_free_with_header; 0x00596e56->0x0058f8f0:fcn.0058f8f0; 0x00596edf->0x0058f8f0:fcn.0058f8f0; 0x00596df0->0x0058f9c0:hashed_entry_table_lookup; 0x00596e22->0x0058f9c0:hashed_entry_table_lookup; 0x00596e36->0x0058f9c0:hashed_entry_table_lookup; 0x00596e8a->0x0058fa00:generic_callback_list_for_each; 0x00596f21->0x0058fa00:generic_callback_list_for_each,9,"0x00596e85->0x00596c80; 0x00596f1c->0x00596c80; 0x00596eaf->0x00596c90; 0x00596f67->0x00596ce0; 0x00596f6e->0x005e1e1c; 0x00596e1a->0x005e2260:""b_flags""; 0x00596f4d->0x005e2260:""b_flags""; 0x00596de8->0x005e22e8:""username""; 0x00596f32->0x005e22e8:""username"""," 596d80: calll 0x596b90 <.text+0x195b90> | 596d85: addl $0x4, %esi | 596d88: decl %ebx | 596d89: jne 0x596d70 <.text+0x195d70> | 596d8b: popl %edi | 596d8c: popl %esi | 596d8d: popl %ebp | 596d8e: popl %ebx | 596d8f: retl $0x18 | 596d92: nop | 596d93: nop | 596d94: nop | 596d95: nop | 596d96: nop | 596d97: nop | 596d98: nop | 596d99: nop | 596d9a: nop | 596d9b: nop | 596d9c: nop | 596d9d: nop | 596d9e: nop | 596d9f: nop | 596da0: subl $0x10, %esp | 596da3: pushl %ebp | 596da4: pushl %edi | 596da5: movl %eax, %edi | 596da7: movl 0x390(%esi,%edi,4), %eax | 596dae: xorl %ebp, %ebp | 596db0: cmpl %ebp, %eax | 596db2: jne 0x596dc1 <.text+0x195dc1> | 596db4: cmpl %ebp, 0x384(%esi,%edi,4) | 596dbb: je 0x596f89 <.text+0x195f89>"
|
||||
0x00596fa0,0x00596fa0,multiplayer_transport_submit_profile_key_query_bundle_with_context,19,cdecl,fcn.00596fa0(int32_t arg_4h);,1,0x0059f935,1,0x00596faa->0x00596da0:multiplayer_transport_submit_profile_key_query_bundle,0,," 596f80: decl %esp | 596f81: andb $0x18, %al | 596f83: calll 0x58f3c0 <.text+0x18e3c0> | 596f88: popl %ebx | 596f89: popl %edi | 596f8a: popl %ebp | 596f8b: addl $0x10, %esp | 596f8e: retl $0x4 | 596f91: nop | 596f92: nop | 596f93: nop | 596f94: nop | 596f95: nop | 596f96: nop | 596f97: nop | 596f98: nop | 596f99: nop | 596f9a: nop | 596f9b: nop | 596f9c: nop | 596f9d: nop | 596f9e: nop | 596f9f: nop | 596fa0: pushl %esi | 596fa1: movl %edx, %eax | 596fa3: movl 0x8(%esp), %edx | 596fa7: pushl %edx | 596fa8: movl %ecx, %esi | 596faa: calll 0x596da0 <.text+0x195da0> | 596faf: popl %esi | 596fb0: retl $0x4 | 596fb3: nop | 596fb4: nop | 596fb5: nop | 596fb6: nop | 596fb7: nop | 596fb8: nop | 596fb9: nop | 596fba: nop | 596fbb: nop | 596fbc: nop | 596fbd: nop | 596fbe: nop | 596fbf: nop"
|
||||
0x00596fc0,0x00596fc0,multiplayer_transport_submit_profile_key_query_bundle_default,14,cdecl,fcn.00596fc0();,2,0x00595499@0x00595440:multiplayer_transport_init_selector_slot; 0x0059fbb5,1,0x00596fc7->0x00596da0:multiplayer_transport_submit_profile_key_query_bundle,0,," 596fa0: pushl %esi | 596fa1: movl %edx, %eax | 596fa3: movl 0x8(%esp), %edx | 596fa7: pushl %edx | 596fa8: movl %ecx, %esi | 596faa: calll 0x596da0 <.text+0x195da0> | 596faf: popl %esi | 596fb0: retl $0x4 | 596fb3: nop | 596fb4: nop | 596fb5: nop | 596fb6: nop | 596fb7: nop | 596fb8: nop | 596fb9: nop | 596fba: nop | 596fbb: nop | 596fbc: nop | 596fbd: nop | 596fbe: nop | 596fbf: nop | 596fc0: pushl %esi | 596fc1: movl %edx, %eax | 596fc3: pushl $0x0 | 596fc5: movl %ecx, %esi | 596fc7: calll 0x596da0 <.text+0x195da0> | 596fcc: popl %esi | 596fcd: retl | 596fce: nop | 596fcf: nop | 596fd0: movl 0x4(%esp), %eax | 596fd4: pushl %esi | 596fd5: movl %edx, %esi | 596fd7: movl 0x398(%eax), %edx | 596fdd: testl %edx, %edx | 596fdf: pushl %edi"
|
||||
0x00596fd0,0x00596fd0,multiplayer_transport_dispatch_status_route_event,219,cdecl,data.00596fd0(int32_t arg_4h);,0,,6,0x00597017->0x0058bce0:fcn.0058bce0; 0x00597054->0x0058bce0:fcn.0058bce0; 0x00597029->0x0058cd40:fcn.0058cd40; 0x0059703f->0x0058cd40:fcn.0058cd40; 0x0059706d->0x0058cd40:fcn.0058cd40; 0x00597084->0x0058cd40:fcn.0058cd40,3,"0x00597008->0x005970ac; 0x00597001->0x005970c4; 0x0059704d->0x005ce1c8:""openstaging"""," 596fb0: retl $0x4 | 596fb3: nop | 596fb4: nop | 596fb5: nop | 596fb6: nop | 596fb7: nop | 596fb8: nop | 596fb9: nop | 596fba: nop | 596fbb: nop | 596fbc: nop | 596fbd: nop | 596fbe: nop | 596fbf: nop | 596fc0: pushl %esi | 596fc1: movl %edx, %eax | 596fc3: pushl $0x0 | 596fc5: movl %ecx, %esi | 596fc7: calll 0x596da0 <.text+0x195da0> | 596fcc: popl %esi | 596fcd: retl | 596fce: nop | 596fcf: nop | 596fd0: movl 0x4(%esp), %eax | 596fd4: pushl %esi | 596fd5: movl %edx, %esi | 596fd7: movl 0x398(%eax), %edx | 596fdd: testl %edx, %edx | 596fdf: pushl %edi | 596fe0: je 0x597077 <.text+0x196077> | 596fe6: movl 0xb44(%eax), %edx | 596fec: testl %edx, %edx | 596fee: je 0x596ff9 <.text+0x195ff9>"
|
||||
0x005973d0,0x005973d0,multiplayer_transport_try_connect_status_route,170,cdecl,fcn.005973d0(int32_t arg_4h);,2,0x0058daf1@0x0058dae0:fcn.0058dae0; 0x005965be@0x005965a0:multiplayer_transport_try_connect_status_route_once,4,0x00597460->0x0058bc90:fcn.0058bc90; 0x0059743c->0x0058c9b0:fcn.0058c9b0; 0x0059742d->0x0058cc40:fcn.0058cc40; 0x005973e7->0x00597350:multiplayer_transport_release_status_route,7,0x0059740f->0x00596fd0; 0x0059740a->0x005970e0; 0x00597405->0x00597180; 0x00597400->0x005971b0; 0x005973fb->0x00597270; 0x005973f6->0x005972c0; 0x0059745b->0x00597330," 5973b0: pushl %esi | 5973b1: movl %ecx, %esi | 5973b3: movl 0x1ecc(%esi), %ecx | 5973b9: testl %ecx, %ecx | 5973bb: je 0x5973cc <.text+0x1963cc> | 5973bd: calll 0x58cfd0 <.text+0x18bfd0> | 5973c2: movl $0x0, 0x1ecc(%esi) | 5973cc: popl %esi | 5973cd: retl | 5973ce: nop | 5973cf: nop | 5973d0: pushl %ebx | 5973d1: pushl %esi | 5973d2: movl %ecx, %esi | 5973d4: movl 0xaf0(%esi), %eax | 5973da: testl %eax, %eax | 5973dc: pushl %edi | 5973dd: leal 0xaf0(%esi), %edi | 5973e3: movl %edx, %ebx | 5973e5: je 0x5973ec <.text+0x1963ec> | 5973e7: calll 0x597350 <.text+0x196350> | 5973ec: cmpl $-0x1, %ebx | 5973ef: movl 0xb34(%esi), %eax"
|
||||
0x004333f0,0x004333f0,shell_setup_build_file_list_records_from_current_root_and_pattern,676,cdecl,"fcn.004333f0(int32_t arg_8h, int32_t arg_c30h);",1,0x004336b8@0x004336a0:shell_setup_file_list_construct_and_scan_dataset,14,0x004334de->0x00433260:fcn.00433260; 0x004335b7->0x00433260:fcn.00433260; 0x00433409->0x004839b0:shell_setup_query_file_list_uses_map_extension_pattern; 0x0043344c->0x004839e0:shell_setup_query_file_list_root_dir_name; 0x00433433->0x00518de0:fcn.00518de0; 0x0043345e->0x00518de0:fcn.00518de0; 0x0043348b->0x00518de0:fcn.00518de0; 0x004334ae->0x00518de0:fcn.00518de0; 0x0043355a->0x00518de0:fcn.00518de0; 0x0043357d->0x00518de0:fcn.00518de0; 0x00433638->0x0051c920:localization_lookup_display_label_by_stem_or_fallback; 0x00433682->0x0051dc60:fcn.0051dc60; 0x004335f7->0x0051df90:fcn.0051df90; 0x0043350c->0x005a125d:fcn.005a125d,8,"0x004334b3->0x005c8190; 0x0043347f->0x005c9d08:""%s%s""; 0x0043354e->0x005c9d08:""%s%s""; 0x00433452->0x005c9d10; 0x0043342e->0x005c9d14:""*.gm*""; 0x00433427->0x005c9d1c:""*.smp""; 0x00433402->0x006cec74; 0x00433438->0x006cec74"," 4333d0: movl %ds, %edi | 4333d2: strw -0x29da1732(%ebx) | 4333d9: ltrw -0x75(%esi) | 4333dd: ldsl 0x5b(%ebp), %ebx | 4333e0: popl %edi | 4333e1: addl $0x404f4, %esp # imm = 0x404F4 | 4333e7: retl $0x4 | 4333ea: nop | 4333eb: nop | 4333ec: nop | 4333ed: nop | 4333ee: nop | 4333ef: nop | 4333f0: subl $0xcf8, %esp # imm = 0xCF8 | 4333f6: pushl %ebx | 4333f7: pushl %ebp | 4333f8: movl %ecx, %ebp | 4333fa: pushl %esi | 4333fb: movl $0x0, (%ebp) | 433402: movl 0x6cec74, %ecx | 433408: pushl %edi | 433409: calll 0x4839b0 <.text+0x829b0> | 43340e: testl %eax, %eax"
|
||||
|
|
|
|||
|
|
|
@ -5,685 +5,108 @@
|
|||
|
||||
## Function Targets
|
||||
|
||||
### `0x00595440` -> `0x00595440` `multiplayer_transport_init_selector_slot`
|
||||
### `0x004333f0` -> `0x004333f0` `shell_setup_build_file_list_records_from_current_root_and_pattern`
|
||||
|
||||
- Size: `100`
|
||||
- Size: `676`
|
||||
- Calling convention: `cdecl`
|
||||
- Signature: `fcn.00595440(int32_t arg_4h, int32_t arg_39ch, int32_t arg_59bh);`
|
||||
- Signature: `fcn.004333f0(int32_t arg_8h, int32_t arg_c30h);`
|
||||
|
||||
Entry excerpt:
|
||||
|
||||
```asm
|
||||
595420: movl $0x89ffff98, %esp # imm = 0x89FFFF98
|
||||
595425: movl $0x9a4, %esi # imm = 0x9A4
|
||||
59542a: popl %edi
|
||||
59542b: popl %esi
|
||||
59542c: addl $0x190, %esp # imm = 0x190
|
||||
595432: retl
|
||||
595433: nop
|
||||
595434: nop
|
||||
595435: nop
|
||||
595436: nop
|
||||
595437: nop
|
||||
595438: nop
|
||||
595439: nop
|
||||
59543a: nop
|
||||
59543b: nop
|
||||
59543c: nop
|
||||
59543d: nop
|
||||
59543e: nop
|
||||
59543f: nop
|
||||
595440: movl 0x4(%esp), %eax
|
||||
595444: testl %eax, %eax
|
||||
595446: pushl %ebp
|
||||
595447: pushl %esi
|
||||
595448: pushl %edi
|
||||
595449: movl %edx, %edi
|
||||
59544b: movl %ecx, %esi
|
||||
59544d: jne 0x595454 <.text+0x194454>
|
||||
59544f: movl $0x5c87a8, %eax # imm = 0x5C87A8
|
||||
595454: movl %edi, %ecx
|
||||
595456: shll $0x9, %ecx
|
||||
595459: leal (%ecx,%esi), %ebp
|
||||
59545c: pushl $0x200 # imm = 0x200
|
||||
4333d0: movl %ds, %edi
|
||||
4333d2: strw -0x29da1732(%ebx)
|
||||
4333d9: ltrw -0x75(%esi)
|
||||
4333dd: ldsl 0x5b(%ebp), %ebx
|
||||
4333e0: popl %edi
|
||||
4333e1: addl $0x404f4, %esp # imm = 0x404F4
|
||||
4333e7: retl $0x4
|
||||
4333ea: nop
|
||||
4333eb: nop
|
||||
4333ec: nop
|
||||
4333ed: nop
|
||||
4333ee: nop
|
||||
4333ef: nop
|
||||
4333f0: subl $0xcf8, %esp # imm = 0xCF8
|
||||
4333f6: pushl %ebx
|
||||
4333f7: pushl %ebp
|
||||
4333f8: movl %ecx, %ebp
|
||||
4333fa: pushl %esi
|
||||
4333fb: movl $0x0, (%ebp)
|
||||
433402: movl 0x6cec74, %ecx
|
||||
433408: pushl %edi
|
||||
433409: calll 0x4839b0 <.text+0x829b0>
|
||||
43340e: testl %eax, %eax
|
||||
```
|
||||
|
||||
Callers:
|
||||
- `0x00593841` in `0x00593790` `multiplayer_transport_handle_names_query_response`
|
||||
- `0x00593b25` in `0x00593b00` `multiplayer_transport_handle_selector_update_response`
|
||||
- `0x004336b8` in `0x004336a0` `shell_setup_file_list_construct_and_scan_dataset`
|
||||
|
||||
Caller xref excerpts:
|
||||
|
||||
#### `0x00593841`
|
||||
#### `0x004336b8`
|
||||
|
||||
```asm
|
||||
593821: movl $0x1000000, %esp # imm = 0x1000000
|
||||
593826: addb %al, (%eax)
|
||||
593828: addb %ch, %bl
|
||||
59382a: orb %bh, %al
|
||||
59382c: xchgb %al, 0xb(%eax)
|
||||
59382f: addb %al, (%eax)
|
||||
593831: addl %eax, (%eax)
|
||||
593833: addb %al, (%eax)
|
||||
593835: movl 0x20(%edi), %edx
|
||||
593838: pushl %ebx
|
||||
593839: pushl %edx
|
||||
59383a: movl $0x2, %edx
|
||||
59383f: movl %esi, %ecx
|
||||
593841: calll 0x595440 <.text+0x194440>
|
||||
593846: movl 0x1c(%esp), %eax
|
||||
59384a: cmpl %ebp, %eax
|
||||
59384c: jle 0x59387f <.text+0x19287f>
|
||||
59384e: movl 0x20(%esp), %ebx
|
||||
593852: movl 0x24(%esp), %ebp
|
||||
593856: subl %ebx, %ebp
|
||||
593858: movl %eax, 0x28(%esp)
|
||||
59385c: leal (%esp), %esp
|
||||
593860: movl (%ebx,%ebp), %eax
|
||||
```
|
||||
|
||||
#### `0x00593b25`
|
||||
|
||||
```asm
|
||||
593b05: andb $0x20, %al
|
||||
593b07: movl 0x38(%esi), %eax
|
||||
593b0a: testl %eax, %eax
|
||||
593b0c: movl (%esi), %ebx
|
||||
593b0e: pushl %edi
|
||||
593b0f: movl %edx, %edi
|
||||
593b11: movl %edi, 0xc(%esp)
|
||||
593b15: jne 0x593b93 <.text+0x192b93>
|
||||
593b17: testl %edi, %edi
|
||||
593b19: movl 0x1c(%esi), %edx
|
||||
593b1c: movl %ebx, %ecx
|
||||
593b1e: je 0x593b67 <.text+0x192b67>
|
||||
593b20: pushl $0x5c87a8 # imm = 0x5C87A8
|
||||
593b25: calll 0x595440 <.text+0x194440>
|
||||
593b2a: movl 0x18(%esp), %eax
|
||||
593b2e: testl %eax, %eax
|
||||
593b30: jle 0x593b6e <.text+0x192b6e>
|
||||
593b32: movl 0x1c(%esp), %edi
|
||||
593b36: pushl %ebp
|
||||
593b37: movl 0x24(%esp), %ebp
|
||||
593b3b: subl %edi, %ebp
|
||||
593b3d: movl %eax, 0x28(%esp)
|
||||
593b41: movl (%edi,%ebp), %eax
|
||||
593b44: movl 0x1c(%esi), %ecx
|
||||
433698: nop
|
||||
433699: nop
|
||||
43369a: nop
|
||||
43369b: nop
|
||||
43369c: nop
|
||||
43369d: nop
|
||||
43369e: nop
|
||||
43369f: nop
|
||||
4336a0: movl 0x4(%esp), %eax
|
||||
4336a4: pushl %esi
|
||||
4336a5: movl %ecx, %esi
|
||||
4336a7: pushl %eax
|
||||
4336a8: movl %eax, 0x4(%esi)
|
||||
4336ab: movl $0x0, 0x8(%esi)
|
||||
4336b2: movl $0x0, (%esi)
|
||||
4336b8: calll 0x4333f0 <.text+0x323f0>
|
||||
4336bd: movl %esi, %eax
|
||||
4336bf: popl %esi
|
||||
4336c0: retl $0x4
|
||||
4336c3: nop
|
||||
4336c4: nop
|
||||
4336c5: nop
|
||||
4336c6: nop
|
||||
4336c7: nop
|
||||
4336c8: nop
|
||||
4336c9: nop
|
||||
4336ca: nop
|
||||
4336cb: nop
|
||||
4336cc: nop
|
||||
4336cd: nop
|
||||
4336ce: nop
|
||||
4336cf: nop
|
||||
4336d0: movl %ecx, %eax
|
||||
4336d2: xorl %ecx, %ecx
|
||||
4336d4: movl %ecx, 0x4cae(%eax)
|
||||
```
|
||||
|
||||
Direct internal callees:
|
||||
- `0x00595490` -> `0x005951f0` `multiplayer_transport_service_status_pump`
|
||||
- `0x00595499` -> `0x00596fc0` `multiplayer_transport_submit_profile_key_query_bundle_default`
|
||||
- `0x0059547f` -> `0x005a18a0` `string_copy_bounded_zerofill`
|
||||
- `0x004334de` -> `0x00433260` `fcn.00433260`
|
||||
- `0x004335b7` -> `0x00433260` `fcn.00433260`
|
||||
- `0x00433409` -> `0x004839b0` `shell_setup_query_file_list_uses_map_extension_pattern`
|
||||
- `0x0043344c` -> `0x004839e0` `shell_setup_query_file_list_root_dir_name`
|
||||
- `0x00433433` -> `0x00518de0` `fcn.00518de0`
|
||||
- `0x0043345e` -> `0x00518de0` `fcn.00518de0`
|
||||
- `0x0043348b` -> `0x00518de0` `fcn.00518de0`
|
||||
- `0x004334ae` -> `0x00518de0` `fcn.00518de0`
|
||||
- `0x0043355a` -> `0x00518de0` `fcn.00518de0`
|
||||
- `0x0043357d` -> `0x00518de0` `fcn.00518de0`
|
||||
- `0x00433638` -> `0x0051c920` `localization_lookup_display_label_by_stem_or_fallback`
|
||||
- `0x00433682` -> `0x0051dc60` `fcn.0051dc60`
|
||||
- `0x004335f7` -> `0x0051df90` `fcn.0051df90`
|
||||
- `0x0043350c` -> `0x005a125d` `fcn.005a125d`
|
||||
|
||||
Data refs:
|
||||
- `0x0059544f` -> `0x005c87a8`
|
||||
|
||||
### `0x00596da0` -> `0x00596da0` `multiplayer_transport_submit_profile_key_query_bundle`
|
||||
|
||||
- Size: `497`
|
||||
- Calling convention: `cdecl`
|
||||
- Signature: `fcn.00596da0(uint_least32_t arg_4h);`
|
||||
|
||||
Entry excerpt:
|
||||
|
||||
```asm
|
||||
596d80: calll 0x596b90 <.text+0x195b90>
|
||||
596d85: addl $0x4, %esi
|
||||
596d88: decl %ebx
|
||||
596d89: jne 0x596d70 <.text+0x195d70>
|
||||
596d8b: popl %edi
|
||||
596d8c: popl %esi
|
||||
596d8d: popl %ebp
|
||||
596d8e: popl %ebx
|
||||
596d8f: retl $0x18
|
||||
596d92: nop
|
||||
596d93: nop
|
||||
596d94: nop
|
||||
596d95: nop
|
||||
596d96: nop
|
||||
596d97: nop
|
||||
596d98: nop
|
||||
596d99: nop
|
||||
596d9a: nop
|
||||
596d9b: nop
|
||||
596d9c: nop
|
||||
596d9d: nop
|
||||
596d9e: nop
|
||||
596d9f: nop
|
||||
596da0: subl $0x10, %esp
|
||||
596da3: pushl %ebp
|
||||
596da4: pushl %edi
|
||||
596da5: movl %eax, %edi
|
||||
596da7: movl 0x390(%esi,%edi,4), %eax
|
||||
596dae: xorl %ebp, %ebp
|
||||
596db0: cmpl %ebp, %eax
|
||||
596db2: jne 0x596dc1 <.text+0x195dc1>
|
||||
596db4: cmpl %ebp, 0x384(%esi,%edi,4)
|
||||
596dbb: je 0x596f89 <.text+0x195f89>
|
||||
```
|
||||
|
||||
Callers:
|
||||
- `0x00596faa` in `0x00596fa0` `multiplayer_transport_submit_profile_key_query_bundle_with_context`
|
||||
- `0x00596fc7` in `0x00596fc0` `multiplayer_transport_submit_profile_key_query_bundle_default`
|
||||
|
||||
Caller xref excerpts:
|
||||
|
||||
#### `0x00596faa`
|
||||
|
||||
```asm
|
||||
596f8a: popl %ebp
|
||||
596f8b: addl $0x10, %esp
|
||||
596f8e: retl $0x4
|
||||
596f91: nop
|
||||
596f92: nop
|
||||
596f93: nop
|
||||
596f94: nop
|
||||
596f95: nop
|
||||
596f96: nop
|
||||
596f97: nop
|
||||
596f98: nop
|
||||
596f99: nop
|
||||
596f9a: nop
|
||||
596f9b: nop
|
||||
596f9c: nop
|
||||
596f9d: nop
|
||||
596f9e: nop
|
||||
596f9f: nop
|
||||
596fa0: pushl %esi
|
||||
596fa1: movl %edx, %eax
|
||||
596fa3: movl 0x8(%esp), %edx
|
||||
596fa7: pushl %edx
|
||||
596fa8: movl %ecx, %esi
|
||||
596faa: calll 0x596da0 <.text+0x195da0>
|
||||
596faf: popl %esi
|
||||
596fb0: retl $0x4
|
||||
596fb3: nop
|
||||
596fb4: nop
|
||||
596fb5: nop
|
||||
596fb6: nop
|
||||
596fb7: nop
|
||||
596fb8: nop
|
||||
596fb9: nop
|
||||
596fba: nop
|
||||
596fbb: nop
|
||||
596fbc: nop
|
||||
596fbd: nop
|
||||
596fbe: nop
|
||||
596fbf: nop
|
||||
596fc0: pushl %esi
|
||||
596fc1: movl %edx, %eax
|
||||
596fc3: pushl $0x0
|
||||
596fc5: movl %ecx, %esi
|
||||
596fc7: calll 0x596da0 <.text+0x195da0>
|
||||
```
|
||||
|
||||
#### `0x00596fc7`
|
||||
|
||||
```asm
|
||||
596fa7: pushl %edx
|
||||
596fa8: movl %ecx, %esi
|
||||
596faa: calll 0x596da0 <.text+0x195da0>
|
||||
596faf: popl %esi
|
||||
596fb0: retl $0x4
|
||||
596fb3: nop
|
||||
596fb4: nop
|
||||
596fb5: nop
|
||||
596fb6: nop
|
||||
596fb7: nop
|
||||
596fb8: nop
|
||||
596fb9: nop
|
||||
596fba: nop
|
||||
596fbb: nop
|
||||
596fbc: nop
|
||||
596fbd: nop
|
||||
596fbe: nop
|
||||
596fbf: nop
|
||||
596fc0: pushl %esi
|
||||
596fc1: movl %edx, %eax
|
||||
596fc3: pushl $0x0
|
||||
596fc5: movl %ecx, %esi
|
||||
596fc7: calll 0x596da0 <.text+0x195da0>
|
||||
596fcc: popl %esi
|
||||
596fcd: retl
|
||||
596fce: nop
|
||||
596fcf: nop
|
||||
596fd0: movl 0x4(%esp), %eax
|
||||
596fd4: pushl %esi
|
||||
596fd5: movl %edx, %esi
|
||||
596fd7: movl 0x398(%eax), %edx
|
||||
596fdd: testl %edx, %edx
|
||||
596fdf: pushl %edi
|
||||
596fe0: je 0x597077 <.text+0x196077>
|
||||
596fe6: movl 0xb44(%eax), %edx
|
||||
```
|
||||
|
||||
Direct internal callees:
|
||||
- `0x00596eb6` -> `0x0058ec50` `multiplayer_transport_submit_getkey_command_and_wait`
|
||||
- `0x00596f7a` -> `0x0058ef20` `multiplayer_transport_submit_setchankey_pair_list_command_and_wait`
|
||||
- `0x00596e68` -> `0x0058f380` `tracked_heap_alloc_with_header`
|
||||
- `0x00596f03` -> `0x0058f380` `tracked_heap_alloc_with_header`
|
||||
- `0x00596ebf` -> `0x0058f3c0` `tracked_heap_free_with_header`
|
||||
- `0x00596f83` -> `0x0058f3c0` `tracked_heap_free_with_header`
|
||||
- `0x00596e56` -> `0x0058f8f0` `fcn.0058f8f0`
|
||||
- `0x00596edf` -> `0x0058f8f0` `fcn.0058f8f0`
|
||||
- `0x00596df0` -> `0x0058f9c0` `hashed_entry_table_lookup`
|
||||
- `0x00596e22` -> `0x0058f9c0` `hashed_entry_table_lookup`
|
||||
- `0x00596e36` -> `0x0058f9c0` `hashed_entry_table_lookup`
|
||||
- `0x00596e8a` -> `0x0058fa00` `generic_callback_list_for_each`
|
||||
- `0x00596f21` -> `0x0058fa00` `generic_callback_list_for_each`
|
||||
|
||||
Data refs:
|
||||
- `0x00596e85` -> `0x00596c80`
|
||||
- `0x00596f1c` -> `0x00596c80`
|
||||
- `0x00596eaf` -> `0x00596c90`
|
||||
- `0x00596f67` -> `0x00596ce0`
|
||||
- `0x00596f6e` -> `0x005e1e1c`
|
||||
- `0x00596e1a` -> `0x005e2260` "b_flags"
|
||||
- `0x00596f4d` -> `0x005e2260` "b_flags"
|
||||
- `0x00596de8` -> `0x005e22e8` "username"
|
||||
- `0x00596f32` -> `0x005e22e8` "username"
|
||||
|
||||
### `0x00596fa0` -> `0x00596fa0` `multiplayer_transport_submit_profile_key_query_bundle_with_context`
|
||||
|
||||
- Size: `19`
|
||||
- Calling convention: `cdecl`
|
||||
- Signature: `fcn.00596fa0(int32_t arg_4h);`
|
||||
|
||||
Entry excerpt:
|
||||
|
||||
```asm
|
||||
596f80: decl %esp
|
||||
596f81: andb $0x18, %al
|
||||
596f83: calll 0x58f3c0 <.text+0x18e3c0>
|
||||
596f88: popl %ebx
|
||||
596f89: popl %edi
|
||||
596f8a: popl %ebp
|
||||
596f8b: addl $0x10, %esp
|
||||
596f8e: retl $0x4
|
||||
596f91: nop
|
||||
596f92: nop
|
||||
596f93: nop
|
||||
596f94: nop
|
||||
596f95: nop
|
||||
596f96: nop
|
||||
596f97: nop
|
||||
596f98: nop
|
||||
596f99: nop
|
||||
596f9a: nop
|
||||
596f9b: nop
|
||||
596f9c: nop
|
||||
596f9d: nop
|
||||
596f9e: nop
|
||||
596f9f: nop
|
||||
596fa0: pushl %esi
|
||||
596fa1: movl %edx, %eax
|
||||
596fa3: movl 0x8(%esp), %edx
|
||||
596fa7: pushl %edx
|
||||
596fa8: movl %ecx, %esi
|
||||
596faa: calll 0x596da0 <.text+0x195da0>
|
||||
596faf: popl %esi
|
||||
596fb0: retl $0x4
|
||||
596fb3: nop
|
||||
596fb4: nop
|
||||
596fb5: nop
|
||||
596fb6: nop
|
||||
596fb7: nop
|
||||
596fb8: nop
|
||||
596fb9: nop
|
||||
596fba: nop
|
||||
596fbb: nop
|
||||
596fbc: nop
|
||||
596fbd: nop
|
||||
596fbe: nop
|
||||
596fbf: nop
|
||||
```
|
||||
|
||||
Callers:
|
||||
- `0x0059f935`
|
||||
|
||||
Caller xref excerpts:
|
||||
|
||||
#### `0x0059f935`
|
||||
|
||||
```asm
|
||||
59f915: <unknown>
|
||||
59f917: testl %eax, %eax
|
||||
59f919: je 0x59f92e <.text+0x19e92e>
|
||||
59f91b: movl 0x10(%esp), %edx
|
||||
59f91f: movl 0x8(%esp), %eax
|
||||
59f923: pushl %edx
|
||||
59f924: pushl %eax
|
||||
59f925: movl %edi, %edx
|
||||
59f927: movl %esi, %ecx
|
||||
59f929: calll 0x594b60 <.text+0x193b60>
|
||||
59f92e: movl 0x18(%esp), %edx
|
||||
59f932: pushl %edi
|
||||
59f933: movl %esi, %ecx
|
||||
59f935: calll 0x596fa0 <.text+0x195fa0>
|
||||
59f93a: movl 0x18(%esp), %edx
|
||||
59f93e: pushl %edi
|
||||
59f93f: movl %esi, %ecx
|
||||
59f941: calll 0x592ee0 <.text+0x191ee0>
|
||||
59f946: cmpl $0x2, 0x18(%esp)
|
||||
59f94b: jne 0x59f9b1 <.text+0x19e9b1>
|
||||
59f94d: movl 0xb54(%esi), %eax
|
||||
59f953: testl %eax, %eax
|
||||
```
|
||||
|
||||
Direct internal callees:
|
||||
- `0x00596faa` -> `0x00596da0` `multiplayer_transport_submit_profile_key_query_bundle`
|
||||
|
||||
Data refs:
|
||||
- none
|
||||
|
||||
### `0x00596fc0` -> `0x00596fc0` `multiplayer_transport_submit_profile_key_query_bundle_default`
|
||||
|
||||
- Size: `14`
|
||||
- Calling convention: `cdecl`
|
||||
- Signature: `fcn.00596fc0();`
|
||||
|
||||
Entry excerpt:
|
||||
|
||||
```asm
|
||||
596fa0: pushl %esi
|
||||
596fa1: movl %edx, %eax
|
||||
596fa3: movl 0x8(%esp), %edx
|
||||
596fa7: pushl %edx
|
||||
596fa8: movl %ecx, %esi
|
||||
596faa: calll 0x596da0 <.text+0x195da0>
|
||||
596faf: popl %esi
|
||||
596fb0: retl $0x4
|
||||
596fb3: nop
|
||||
596fb4: nop
|
||||
596fb5: nop
|
||||
596fb6: nop
|
||||
596fb7: nop
|
||||
596fb8: nop
|
||||
596fb9: nop
|
||||
596fba: nop
|
||||
596fbb: nop
|
||||
596fbc: nop
|
||||
596fbd: nop
|
||||
596fbe: nop
|
||||
596fbf: nop
|
||||
596fc0: pushl %esi
|
||||
596fc1: movl %edx, %eax
|
||||
596fc3: pushl $0x0
|
||||
596fc5: movl %ecx, %esi
|
||||
596fc7: calll 0x596da0 <.text+0x195da0>
|
||||
596fcc: popl %esi
|
||||
596fcd: retl
|
||||
596fce: nop
|
||||
596fcf: nop
|
||||
596fd0: movl 0x4(%esp), %eax
|
||||
596fd4: pushl %esi
|
||||
596fd5: movl %edx, %esi
|
||||
596fd7: movl 0x398(%eax), %edx
|
||||
596fdd: testl %edx, %edx
|
||||
596fdf: pushl %edi
|
||||
```
|
||||
|
||||
Callers:
|
||||
- `0x00595499` in `0x00595440` `multiplayer_transport_init_selector_slot`
|
||||
- `0x0059fbb5`
|
||||
|
||||
Caller xref excerpts:
|
||||
|
||||
#### `0x00595499`
|
||||
|
||||
```asm
|
||||
595479: addb %al, (%eax)
|
||||
59547b: addb %al, (%eax)
|
||||
59547d: addb %al, (%eax)
|
||||
59547f: calll 0x5a18a0 <.text+0x1a08a0>
|
||||
595484: addl $0xc, %esp
|
||||
595487: movl %esi, %ecx
|
||||
595489: movb $0x0, 0x59b(%ebp)
|
||||
595490: calll 0x5951f0 <.text+0x1941f0>
|
||||
595495: movl %edi, %edx
|
||||
595497: movl %esi, %ecx
|
||||
595499: calll 0x596fc0 <.text+0x195fc0>
|
||||
59549e: popl %edi
|
||||
59549f: popl %esi
|
||||
5954a0: popl %ebp
|
||||
5954a1: retl $0x4
|
||||
5954a4: nop
|
||||
5954a5: nop
|
||||
5954a6: nop
|
||||
5954a7: nop
|
||||
5954a8: nop
|
||||
5954a9: nop
|
||||
5954aa: nop
|
||||
5954ab: nop
|
||||
5954ac: nop
|
||||
5954ad: nop
|
||||
5954ae: nop
|
||||
5954af: nop
|
||||
5954b0: pushl %ebx
|
||||
5954b1: pushl %esi
|
||||
5954b2: pushl %edi
|
||||
5954b3: movl %edx, %edi
|
||||
5954b5: movl %ecx, %esi
|
||||
5954b7: movl 0x384(%esi,%edi,4), %eax
|
||||
```
|
||||
|
||||
#### `0x0059fbb5`
|
||||
|
||||
```asm
|
||||
59fb95: movl (%edi,%esi), %ecx
|
||||
59fb98: movl 0x20(%esp), %edx
|
||||
59fb9c: pushl %ecx
|
||||
59fb9d: pushl %edx
|
||||
59fb9e: movl (%esi), %edx
|
||||
59fba0: movl %ebp, %ecx
|
||||
59fba2: calll 0x594f20 <.text+0x193f20>
|
||||
59fba7: addl $0x4, %esi
|
||||
59fbaa: decl %ebx
|
||||
59fbab: jne 0x59fb95 <.text+0x19eb95>
|
||||
59fbad: popl %edi
|
||||
59fbae: popl %esi
|
||||
59fbaf: movl 0x18(%esp), %edx
|
||||
59fbb3: movl %ebp, %ecx
|
||||
59fbb5: calll 0x596fc0 <.text+0x195fc0>
|
||||
59fbba: movl 0x18(%esp), %edx
|
||||
59fbbe: movl %ebp, %ecx
|
||||
59fbc0: calll 0x592fc0 <.text+0x191fc0>
|
||||
59fbc5: popl %ebx
|
||||
59fbc6: popl %ebp
|
||||
59fbc7: retl $0x10
|
||||
59fbca: nop
|
||||
59fbcb: nop
|
||||
59fbcc: nop
|
||||
59fbcd: nop
|
||||
59fbce: nop
|
||||
59fbcf: nop
|
||||
59fbd0: pushl %esi
|
||||
59fbd1: movl 0x14(%esp), %esi
|
||||
```
|
||||
|
||||
Direct internal callees:
|
||||
- `0x00596fc7` -> `0x00596da0` `multiplayer_transport_submit_profile_key_query_bundle`
|
||||
|
||||
Data refs:
|
||||
- none
|
||||
|
||||
### `0x00596fd0` -> `0x00596fd0` `multiplayer_transport_dispatch_status_route_event`
|
||||
|
||||
- Size: `219`
|
||||
- Calling convention: `cdecl`
|
||||
- Signature: `data.00596fd0(int32_t arg_4h);`
|
||||
|
||||
Entry excerpt:
|
||||
|
||||
```asm
|
||||
596fb0: retl $0x4
|
||||
596fb3: nop
|
||||
596fb4: nop
|
||||
596fb5: nop
|
||||
596fb6: nop
|
||||
596fb7: nop
|
||||
596fb8: nop
|
||||
596fb9: nop
|
||||
596fba: nop
|
||||
596fbb: nop
|
||||
596fbc: nop
|
||||
596fbd: nop
|
||||
596fbe: nop
|
||||
596fbf: nop
|
||||
596fc0: pushl %esi
|
||||
596fc1: movl %edx, %eax
|
||||
596fc3: pushl $0x0
|
||||
596fc5: movl %ecx, %esi
|
||||
596fc7: calll 0x596da0 <.text+0x195da0>
|
||||
596fcc: popl %esi
|
||||
596fcd: retl
|
||||
596fce: nop
|
||||
596fcf: nop
|
||||
596fd0: movl 0x4(%esp), %eax
|
||||
596fd4: pushl %esi
|
||||
596fd5: movl %edx, %esi
|
||||
596fd7: movl 0x398(%eax), %edx
|
||||
596fdd: testl %edx, %edx
|
||||
596fdf: pushl %edi
|
||||
596fe0: je 0x597077 <.text+0x196077>
|
||||
596fe6: movl 0xb44(%eax), %edx
|
||||
596fec: testl %edx, %edx
|
||||
596fee: je 0x596ff9 <.text+0x195ff9>
|
||||
```
|
||||
|
||||
Callers:
|
||||
- none
|
||||
|
||||
Direct internal callees:
|
||||
- `0x00597017` -> `0x0058bce0` `fcn.0058bce0`
|
||||
- `0x00597054` -> `0x0058bce0` `fcn.0058bce0`
|
||||
- `0x00597029` -> `0x0058cd40` `fcn.0058cd40`
|
||||
- `0x0059703f` -> `0x0058cd40` `fcn.0058cd40`
|
||||
- `0x0059706d` -> `0x0058cd40` `fcn.0058cd40`
|
||||
- `0x00597084` -> `0x0058cd40` `fcn.0058cd40`
|
||||
|
||||
Data refs:
|
||||
- `0x00597008` -> `0x005970ac`
|
||||
- `0x00597001` -> `0x005970c4`
|
||||
- `0x0059704d` -> `0x005ce1c8` "openstaging"
|
||||
|
||||
### `0x005973d0` -> `0x005973d0` `multiplayer_transport_try_connect_status_route`
|
||||
|
||||
- Size: `170`
|
||||
- Calling convention: `cdecl`
|
||||
- Signature: `fcn.005973d0(int32_t arg_4h);`
|
||||
|
||||
Entry excerpt:
|
||||
|
||||
```asm
|
||||
5973b0: pushl %esi
|
||||
5973b1: movl %ecx, %esi
|
||||
5973b3: movl 0x1ecc(%esi), %ecx
|
||||
5973b9: testl %ecx, %ecx
|
||||
5973bb: je 0x5973cc <.text+0x1963cc>
|
||||
5973bd: calll 0x58cfd0 <.text+0x18bfd0>
|
||||
5973c2: movl $0x0, 0x1ecc(%esi)
|
||||
5973cc: popl %esi
|
||||
5973cd: retl
|
||||
5973ce: nop
|
||||
5973cf: nop
|
||||
5973d0: pushl %ebx
|
||||
5973d1: pushl %esi
|
||||
5973d2: movl %ecx, %esi
|
||||
5973d4: movl 0xaf0(%esi), %eax
|
||||
5973da: testl %eax, %eax
|
||||
5973dc: pushl %edi
|
||||
5973dd: leal 0xaf0(%esi), %edi
|
||||
5973e3: movl %edx, %ebx
|
||||
5973e5: je 0x5973ec <.text+0x1963ec>
|
||||
5973e7: calll 0x597350 <.text+0x196350>
|
||||
5973ec: cmpl $-0x1, %ebx
|
||||
5973ef: movl 0xb34(%esi), %eax
|
||||
```
|
||||
|
||||
Callers:
|
||||
- `0x0058daf1` in `0x0058dae0` `fcn.0058dae0`
|
||||
- `0x005965be` in `0x005965a0` `multiplayer_transport_try_connect_status_route_once`
|
||||
|
||||
Caller xref excerpts:
|
||||
|
||||
#### `0x0058daf1`
|
||||
|
||||
```asm
|
||||
58dad1: addb %bl, -0x3e(%esi)
|
||||
58dad4: orb %al, (%eax)
|
||||
58dad6: nop
|
||||
58dad7: nop
|
||||
58dad8: nop
|
||||
58dad9: nop
|
||||
58dada: nop
|
||||
58dadb: nop
|
||||
58dadc: nop
|
||||
58dadd: nop
|
||||
58dade: nop
|
||||
58dadf: nop
|
||||
58dae0: movb 0x60(%ecx), %al
|
||||
58dae3: testb %al, %al
|
||||
58dae5: jne 0x58daec <.text+0x18caec>
|
||||
58dae7: xorl %eax, %eax
|
||||
58dae9: retl $0x4
|
||||
58daec: movl 0x4(%esp), %eax
|
||||
58daf0: pushl %eax
|
||||
58daf1: calll 0x5973d0 <.text+0x1963d0>
|
||||
58daf6: negl %eax
|
||||
58daf8: sbbl %eax, %eax
|
||||
58dafa: negl %eax
|
||||
58dafc: retl $0x4
|
||||
58daff: nop
|
||||
58db00: movb 0x60(%ecx), %al
|
||||
58db03: testb %al, %al
|
||||
58db05: je 0x58db16 <.text+0x18cb16>
|
||||
58db07: movl $0x1, 0xb44(%ecx)
|
||||
```
|
||||
|
||||
#### `0x005965be`
|
||||
|
||||
```asm
|
||||
59659e: nop
|
||||
59659f: nop
|
||||
5965a0: movl 0xb40(%ecx), %eax
|
||||
5965a6: testl %eax, %eax
|
||||
5965a8: je 0x5965af <.text+0x1955af>
|
||||
5965aa: xorl %eax, %eax
|
||||
5965ac: retl $0x4
|
||||
5965af: movl 0x4(%esp), %eax
|
||||
5965b3: pushl %eax
|
||||
5965b4: movl $0x1, 0xb40(%ecx)
|
||||
5965be: calll 0x5973d0 <.text+0x1963d0>
|
||||
5965c3: negl %eax
|
||||
5965c5: sbbl %eax, %eax
|
||||
5965c7: negl %eax
|
||||
5965c9: retl $0x4
|
||||
5965cc: nop
|
||||
5965cd: nop
|
||||
5965ce: nop
|
||||
5965cf: nop
|
||||
5965d0: testl %edx, %edx
|
||||
5965d2: pushl %esi
|
||||
5965d3: movl %ecx, %esi
|
||||
5965d5: je 0x5965dc <.text+0x1955dc>
|
||||
5965d7: calll 0x597350 <.text+0x196350>
|
||||
5965dc: movl 0xb40(%esi), %eax
|
||||
```
|
||||
|
||||
Direct internal callees:
|
||||
- `0x00597460` -> `0x0058bc90` `fcn.0058bc90`
|
||||
- `0x0059743c` -> `0x0058c9b0` `fcn.0058c9b0`
|
||||
- `0x0059742d` -> `0x0058cc40` `fcn.0058cc40`
|
||||
- `0x005973e7` -> `0x00597350` `multiplayer_transport_release_status_route`
|
||||
|
||||
Data refs:
|
||||
- `0x0059740f` -> `0x00596fd0`
|
||||
- `0x0059740a` -> `0x005970e0`
|
||||
- `0x00597405` -> `0x00597180`
|
||||
- `0x00597400` -> `0x005971b0`
|
||||
- `0x005973fb` -> `0x00597270`
|
||||
- `0x005973f6` -> `0x005972c0`
|
||||
- `0x0059745b` -> `0x00597330`
|
||||
- `0x004334b3` -> `0x005c8190`
|
||||
- `0x0043347f` -> `0x005c9d08` "%s%s"
|
||||
- `0x0043354e` -> `0x005c9d08` "%s%s"
|
||||
- `0x00433452` -> `0x005c9d10`
|
||||
- `0x0043342e` -> `0x005c9d14` "*.gm*"
|
||||
- `0x00433427` -> `0x005c9d1c` "*.smp"
|
||||
- `0x00433402` -> `0x006cec74`
|
||||
- `0x00433438` -> `0x006cec74`
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
2232
artifacts/exports/rt3-1.06/runtime-effect-service-depth7-subgraph.md
Normal file
2232
artifacts/exports/rt3-1.06/runtime-effect-service-depth7-subgraph.md
Normal file
File diff suppressed because it is too large
Load diff
92
artifacts/exports/rt3-1.06/setup-window-subgraph.dot
Normal file
92
artifacts/exports/rt3-1.06/setup-window-subgraph.dot
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
digraph shell_load {
|
||||
graph [rankdir=LR, labelloc="t", labeljust="l"];
|
||||
label="Setup Window Dispatch Subgraph";
|
||||
node [shape=box, style="rounded,filled", fillcolor="#f8f8f8", color="#555555", fontname="Helvetica"];
|
||||
edge [color="#666666", fontname="Helvetica"];
|
||||
subgraph cluster_bootstrap {
|
||||
label="bootstrap";
|
||||
color="#cccccc";
|
||||
"0x00482ec0" [label="0x00482ec0\\nshell_transition_mode", fillcolor="#f8f8f8"];
|
||||
"0x004840e0" [label="0x004840e0\\nbootstrap_init_shell_window_services", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_map {
|
||||
label="map";
|
||||
color="#cccccc";
|
||||
"0x00434300" [label="0x00434300\\nworld_runtime_release_global_services", fillcolor="#f8f8f8"];
|
||||
"0x004384d0" [label="0x004384d0\\nworld_run_post_load_generation_pipeline", fillcolor="#f8f8f8"];
|
||||
"0x00438890" [label="0x00438890\\nshell_active_mode_run_profile_startup_and_load_dispatch", fillcolor="#f8f8f8"];
|
||||
"0x00443a50" [label="0x00443a50\\nworld_entry_transition_and_runtime_bringup", fillcolor="#f8f8f8"];
|
||||
"0x00445ac0" [label="0x00445ac0\\nshell_map_file_entry_coordinator", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_shell {
|
||||
label="shell";
|
||||
color="#cccccc";
|
||||
"0x0046b780" [label="0x0046b780\\nmultiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync", fillcolor="#f8f8f8"];
|
||||
"0x004b8dc0" [label="0x004b8dc0\\nshell_campaign_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004b8e60" [label="0x004b8e60\\nshell_campaign_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004c7bc0" [label="0x004c7bc0\\nshell_credits_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004c7fc0" [label="0x004c7fc0\\nshell_credits_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004dfbe0" [label="0x004dfbe0\\nshell_game_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004dfd70" [label="0x004dfd70\\nshell_game_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004ea620" [label="0x004ea620\\nshell_load_screen_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004ea730" [label="0x004ea730\\nshell_load_screen_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004ee3a0" [label="0x004ee3a0\\nmultiplayer_reset_tool_globals", fillcolor="#f8f8f8"];
|
||||
"0x004efe80" [label="0x004efe80\\nmultiplayer_window_init_globals", fillcolor="#f8f8f8"];
|
||||
"0x00502220" [label="0x00502220\\nshell_setup_window_publish_selected_profile_labels_and_preview_surface [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00502550" [label="0x00502550\\nshell_setup_window_refresh_selection_lists_and_summary_fields [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00502910" [label="0x00502910\\nshell_setup_window_refresh_mode_dependent_lists [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00502c00" [label="0x00502c00\\nshell_setup_window_select_launch_mode_and_apply_shell_state [seed]", fillcolor="#ffe9a8"];
|
||||
"0x005033d0" [label="0x005033d0\\nshell_setup_window_handle_message [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00504010" [label="0x00504010\\nshell_setup_window_construct [seed]", fillcolor="#ffe9a8"];
|
||||
"0x005174e0" [label="0x005174e0\\nshell_video_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x00517570" [label="0x00517570\\nshell_video_window_destroy", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_support {
|
||||
label="support";
|
||||
color="#cccccc";
|
||||
"0x00559520" [label="0x00559520\\nsurface_init_rgba_pixel_buffer", fillcolor="#f8f8f8"];
|
||||
}
|
||||
"0x00434300" -> "0x00443a50";
|
||||
"0x00434300" -> "0x00482ec0";
|
||||
"0x00438890" -> "0x004384d0";
|
||||
"0x00438890" -> "0x00445ac0";
|
||||
"0x00438890" -> "0x005033d0";
|
||||
"0x00443a50" -> "0x00438890";
|
||||
"0x00443a50" -> "0x00482ec0";
|
||||
"0x00445ac0" -> "0x00443a50";
|
||||
"0x0046b780" -> "0x00438890";
|
||||
"0x0046b780" -> "0x00445ac0";
|
||||
"0x00482ec0" -> "0x00438890";
|
||||
"0x00482ec0" -> "0x00443a50";
|
||||
"0x00482ec0" -> "0x004840e0";
|
||||
"0x00482ec0" -> "0x004b8dc0";
|
||||
"0x00482ec0" -> "0x004b8e60";
|
||||
"0x00482ec0" -> "0x004c7bc0";
|
||||
"0x00482ec0" -> "0x004c7fc0";
|
||||
"0x00482ec0" -> "0x004dfbe0";
|
||||
"0x00482ec0" -> "0x004dfd70";
|
||||
"0x00482ec0" -> "0x004ea620";
|
||||
"0x00482ec0" -> "0x004ea730";
|
||||
"0x00482ec0" -> "0x004efe80";
|
||||
"0x00482ec0" -> "0x00504010";
|
||||
"0x00482ec0" -> "0x005174e0";
|
||||
"0x00482ec0" -> "0x00517570";
|
||||
"0x004840e0" -> "0x00482ec0";
|
||||
"0x004b8dc0" -> "0x004b8e60";
|
||||
"0x004c7bc0" -> "0x004c7fc0";
|
||||
"0x004dfbe0" -> "0x004dfd70";
|
||||
"0x004ea620" -> "0x004ea730";
|
||||
"0x004ea730" -> "0x004ea620";
|
||||
"0x004ee3a0" -> "0x00482ec0";
|
||||
"0x00502220" -> "0x00502c00";
|
||||
"0x00502220" -> "0x00559520";
|
||||
"0x00502910" -> "0x00502c00";
|
||||
"0x00502c00" -> "0x00438890";
|
||||
"0x00502c00" -> "0x005033d0";
|
||||
"0x005033d0" -> "0x00502220";
|
||||
"0x005033d0" -> "0x00502550";
|
||||
"0x005033d0" -> "0x00502910";
|
||||
"0x005033d0" -> "0x00502c00";
|
||||
"0x005033d0" -> "0x00504010";
|
||||
"0x00517570" -> "0x005174e0";
|
||||
}
|
||||
102
artifacts/exports/rt3-1.06/setup-window-subgraph.md
Normal file
102
artifacts/exports/rt3-1.06/setup-window-subgraph.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Setup Window Dispatch Subgraph
|
||||
|
||||
- Nodes: `27`
|
||||
- Edges: `43`
|
||||
- Seeds: `0x00502220`, `0x00502550`, `0x00502910`, `0x00502c00`, `0x005033d0`, `0x00504010`
|
||||
- Graphviz: `setup-window-subgraph.dot`
|
||||
|
||||
## Nodes
|
||||
|
||||
| Address | Name | Subsystem | Confidence |
|
||||
| --- | --- | --- | --- |
|
||||
| `0x00434300` | `world_runtime_release_global_services` | `map` | `3` |
|
||||
| `0x004384d0` | `world_run_post_load_generation_pipeline` | `map` | `4` |
|
||||
| `0x00438890` | `shell_active_mode_run_profile_startup_and_load_dispatch` | `map` | `4` |
|
||||
| `0x00443a50` | `world_entry_transition_and_runtime_bringup` | `map` | `4` |
|
||||
| `0x00445ac0` | `shell_map_file_entry_coordinator` | `map` | `4` |
|
||||
| `0x0046b780` | `multiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync` | `shell` | `4` |
|
||||
| `0x00482ec0` | `shell_transition_mode` | `bootstrap` | `4` |
|
||||
| `0x004840e0` | `bootstrap_init_shell_window_services` | `bootstrap` | `4` |
|
||||
| `0x004b8dc0` | `shell_campaign_window_destroy` | `shell` | `4` |
|
||||
| `0x004b8e60` | `shell_campaign_window_construct` | `shell` | `4` |
|
||||
| `0x004c7bc0` | `shell_credits_window_destroy` | `shell` | `4` |
|
||||
| `0x004c7fc0` | `shell_credits_window_construct` | `shell` | `4` |
|
||||
| `0x004dfbe0` | `shell_game_window_construct` | `shell` | `4` |
|
||||
| `0x004dfd70` | `shell_game_window_destroy` | `shell` | `4` |
|
||||
| `0x004ea620` | `shell_load_screen_window_construct` | `shell` | `4` |
|
||||
| `0x004ea730` | `shell_load_screen_window_destroy` | `shell` | `4` |
|
||||
| `0x004ee3a0` | `multiplayer_reset_tool_globals` | `shell` | `3` |
|
||||
| `0x004efe80` | `multiplayer_window_init_globals` | `shell` | `4` |
|
||||
| `0x00502220` | `shell_setup_window_publish_selected_profile_labels_and_preview_surface` | `shell` | `3` |
|
||||
| `0x00502550` | `shell_setup_window_refresh_selection_lists_and_summary_fields` | `shell` | `3` |
|
||||
| `0x00502910` | `shell_setup_window_refresh_mode_dependent_lists` | `shell` | `3` |
|
||||
| `0x00502c00` | `shell_setup_window_select_launch_mode_and_apply_shell_state` | `shell` | `4` |
|
||||
| `0x005033d0` | `shell_setup_window_handle_message` | `shell` | `3` |
|
||||
| `0x00504010` | `shell_setup_window_construct` | `shell` | `4` |
|
||||
| `0x005174e0` | `shell_video_window_construct` | `shell` | `4` |
|
||||
| `0x00517570` | `shell_video_window_destroy` | `shell` | `4` |
|
||||
| `0x00559520` | `surface_init_rgba_pixel_buffer` | `support` | `3` |
|
||||
|
||||
## Edges
|
||||
|
||||
- `0x00434300` `world_runtime_release_global_services`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
-> `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
-> `0x005033d0` `shell_setup_window_handle_message`
|
||||
- `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
- `0x0046b780` `multiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
- `0x00482ec0` `shell_transition_mode`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x004840e0` `bootstrap_init_shell_window_services`
|
||||
-> `0x004b8dc0` `shell_campaign_window_destroy`
|
||||
-> `0x004b8e60` `shell_campaign_window_construct`
|
||||
-> `0x004c7bc0` `shell_credits_window_destroy`
|
||||
-> `0x004c7fc0` `shell_credits_window_construct`
|
||||
-> `0x004dfbe0` `shell_game_window_construct`
|
||||
-> `0x004dfd70` `shell_game_window_destroy`
|
||||
-> `0x004ea620` `shell_load_screen_window_construct`
|
||||
-> `0x004ea730` `shell_load_screen_window_destroy`
|
||||
-> `0x004efe80` `multiplayer_window_init_globals`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
-> `0x005174e0` `shell_video_window_construct`
|
||||
-> `0x00517570` `shell_video_window_destroy`
|
||||
- `0x004840e0` `bootstrap_init_shell_window_services`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x004b8dc0` `shell_campaign_window_destroy`
|
||||
-> `0x004b8e60` `shell_campaign_window_construct`
|
||||
- `0x004c7bc0` `shell_credits_window_destroy`
|
||||
-> `0x004c7fc0` `shell_credits_window_construct`
|
||||
- `0x004dfbe0` `shell_game_window_construct`
|
||||
-> `0x004dfd70` `shell_game_window_destroy`
|
||||
- `0x004ea620` `shell_load_screen_window_construct`
|
||||
-> `0x004ea730` `shell_load_screen_window_destroy`
|
||||
- `0x004ea730` `shell_load_screen_window_destroy`
|
||||
-> `0x004ea620` `shell_load_screen_window_construct`
|
||||
- `0x004ee3a0` `multiplayer_reset_tool_globals`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x00502220` `shell_setup_window_publish_selected_profile_labels_and_preview_surface`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
-> `0x00559520` `surface_init_rgba_pixel_buffer`
|
||||
- `0x00502910` `shell_setup_window_refresh_mode_dependent_lists`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
- `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x005033d0` `shell_setup_window_handle_message`
|
||||
- `0x005033d0` `shell_setup_window_handle_message`
|
||||
-> `0x00502220` `shell_setup_window_publish_selected_profile_labels_and_preview_surface`
|
||||
-> `0x00502550` `shell_setup_window_refresh_selection_lists_and_summary_fields`
|
||||
-> `0x00502910` `shell_setup_window_refresh_mode_dependent_lists`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
- `0x00517570` `shell_video_window_destroy`
|
||||
-> `0x005174e0` `shell_video_window_construct`
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
digraph shell_load {
|
||||
graph [rankdir=LR, labelloc="t", labeljust="l"];
|
||||
label="Setup Window Submode Subgraph (Depth 5, Forward Only)";
|
||||
node [shape=box, style="rounded,filled", fillcolor="#f8f8f8", color="#555555", fontname="Helvetica"];
|
||||
edge [color="#666666", fontname="Helvetica"];
|
||||
subgraph cluster_bootstrap {
|
||||
label="bootstrap";
|
||||
color="#cccccc";
|
||||
"0x00482ec0" [label="0x00482ec0\\nshell_transition_mode", fillcolor="#f8f8f8"];
|
||||
"0x004840e0" [label="0x004840e0\\nbootstrap_init_shell_window_services", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_map {
|
||||
label="map";
|
||||
color="#cccccc";
|
||||
"0x00402cb0" [label="0x00402cb0\\ncity_connection_try_build_route_with_optional_direct_site_placement", fillcolor="#f8f8f8"];
|
||||
"0x00404640" [label="0x00404640\\ncity_connection_bonus_try_compact_route_builder_from_region_entry", fillcolor="#f8f8f8"];
|
||||
"0x004046a0" [label="0x004046a0\\ncity_connection_bonus_build_peer_route_candidate", fillcolor="#f8f8f8"];
|
||||
"0x00404c60" [label="0x00404c60\\ncity_connection_try_build_route_between_region_entry_pair", fillcolor="#f8f8f8"];
|
||||
"0x0040e360" [label="0x0040e360\\nplaced_structure_refresh_linked_site_anchor_position_triplet_for_local_runtime", fillcolor="#f8f8f8"];
|
||||
"0x0040e450" [label="0x0040e450\\nplaced_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem", fillcolor="#f8f8f8"];
|
||||
"0x0040ee10" [label="0x0040ee10\\nplaced_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon", fillcolor="#f8f8f8"];
|
||||
"0x0040ef10" [label="0x0040ef10\\nplaced_structure_finalize_creation_or_rebuild_local_runtime_state", fillcolor="#f8f8f8"];
|
||||
"0x00412ca0" [label="0x00412ca0\\nworld_region_pick_commercial_profile_label_by_region_rank", fillcolor="#f8f8f8"];
|
||||
"0x004133b0" [label="0x004133b0\\nplaced_structure_collection_refresh_local_runtime_records_and_position_scalars", fillcolor="#f8f8f8"];
|
||||
"0x004134d0" [label="0x004134d0\\nplaced_structure_collection_allocate_and_construct_entry", fillcolor="#f8f8f8"];
|
||||
"0x00413f50" [label="0x00413f50\\nplaced_structure_local_runtime_site_id_queue_pop_next", fillcolor="#f8f8f8"];
|
||||
"0x00414470" [label="0x00414470\\nplaced_structure_cache_projected_rect_profile_slot_id", fillcolor="#f8f8f8"];
|
||||
"0x00414480" [label="0x00414480\\nplaced_structure_local_runtime_site_id_queue_count", fillcolor="#f8f8f8"];
|
||||
"0x00415f20" [label="0x00415f20\\nplaced_structure_recursive_collect_connected_component_tile_bounds", fillcolor="#f8f8f8"];
|
||||
"0x00416e20" [label="0x00416e20\\nindexed_collection_resolve_live_entry_id_by_stem_string", fillcolor="#f8f8f8"];
|
||||
"0x00417840" [label="0x00417840\\nplaced_structure_project_candidate_grid_extent_offset_by_rotation", fillcolor="#f8f8f8"];
|
||||
"0x00418a60" [label="0x00418a60\\nplaced_structure_clone_template_local_runtime_record_for_subject_and_refresh_component_bounds", fillcolor="#f8f8f8"];
|
||||
"0x004197e0" [label="0x004197e0\\nplaced_structure_validate_projected_candidate_placement", fillcolor="#f8f8f8"];
|
||||
"0x0041e220" [label="0x0041e220\\nstructure_candidate_is_enabled_for_current_year", fillcolor="#f8f8f8"];
|
||||
"0x0041e2b0" [label="0x0041e2b0\\nstructure_candidate_rebuild_local_service_metrics", fillcolor="#f8f8f8"];
|
||||
"0x0041ea50" [label="0x0041ea50\\nworld_setup_building_collection_phase", fillcolor="#f8f8f8"];
|
||||
"0x0041f9b0" [label="0x0041f9b0\\nworld_region_count_structure_profiles_before_year_for_category", fillcolor="#f8f8f8"];
|
||||
"0x0041fac0" [label="0x0041fac0\\nworld_region_read_structure_profile_label_and_weight_by_index", fillcolor="#f8f8f8"];
|
||||
"0x00421b60" [label="0x00421b60\\nworld_region_collection_seed_default_regions", fillcolor="#f8f8f8"];
|
||||
"0x00421c20" [label="0x00421c20\\nworld_region_collection_run_building_population_pass", fillcolor="#f8f8f8"];
|
||||
"0x00422900" [label="0x00422900\\nworld_region_accumulate_structure_category_totals", fillcolor="#f8f8f8"];
|
||||
"0x00422be0" [label="0x00422be0\\nworld_region_count_placed_structures_for_category", fillcolor="#f8f8f8"];
|
||||
"0x00422ee0" [label="0x00422ee0\\nworld_region_try_place_candidate_structure", fillcolor="#f8f8f8"];
|
||||
"0x004235c0" [label="0x004235c0\\nworld_region_balance_structure_demand_and_place_candidates", fillcolor="#f8f8f8"];
|
||||
"0x00437220" [label="0x00437220\\nworld_build_chairman_profile_slot_records", fillcolor="#f8f8f8"];
|
||||
"0x004377a0" [label="0x004377a0\\nworld_seed_default_chairman_profile_slots", fillcolor="#f8f8f8"];
|
||||
"0x004384d0" [label="0x004384d0\\nworld_run_post_load_generation_pipeline", fillcolor="#f8f8f8"];
|
||||
"0x00438890" [label="0x00438890\\nshell_active_mode_run_profile_startup_and_load_dispatch", fillcolor="#f8f8f8"];
|
||||
"0x00443a50" [label="0x00443a50\\nworld_entry_transition_and_runtime_bringup", fillcolor="#f8f8f8"];
|
||||
"0x00445ac0" [label="0x00445ac0\\nshell_map_file_entry_coordinator", fillcolor="#f8f8f8"];
|
||||
"0x00446d40" [label="0x00446d40\\nworld_load_saved_runtime_state_bundle", fillcolor="#f8f8f8"];
|
||||
"0x0044fb70" [label="0x0044fb70\\nworld_compute_transport_and_pricing_grid", fillcolor="#f8f8f8"];
|
||||
"0x00477820" [label="0x00477820\\nprofile_collection_count_active_chairman_records", fillcolor="#f8f8f8"];
|
||||
"0x00477860" [label="0x00477860\\nprofile_collection_get_nth_active_chairman_record", fillcolor="#f8f8f8"];
|
||||
"0x0047d440" [label="0x0047d440\\nworld_conditionally_seed_named_starting_railroad_companies", fillcolor="#f8f8f8"];
|
||||
"0x004882e0" [label="0x004882e0\\nworld_region_border_overlay_rebuild", fillcolor="#f8f8f8"];
|
||||
"0x004a01a0" [label="0x004a01a0\\nroute_entry_collection_try_build_path_between_optional_endpoint_entries", fillcolor="#f8f8f8"];
|
||||
"0x004a5280" [label="0x004a5280\\naux_route_entry_tracker_query_route_entry_pair_metric_via_weighted_recursive_search", fillcolor="#f8f8f8"];
|
||||
"0x004a5900" [label="0x004a5900\\naux_route_entry_tracker_query_route_entry_pair_metric_via_recursive_neighbor_walk", fillcolor="#f8f8f8"];
|
||||
"0x004a65b0" [label="0x004a65b0\\naux_route_entry_tracker_dispatch_route_entry_pair_metric_query", fillcolor="#f8f8f8"];
|
||||
"0x004a6630" [label="0x004a6630\\naux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks", fillcolor="#f8f8f8"];
|
||||
"0x004c9da0" [label="0x004c9da0\\nmap_editor_chairman_slot_panel_format_slot_card", fillcolor="#f8f8f8"];
|
||||
"0x004cc2d0" [label="0x004cc2d0\\nmap_editor_chairman_slot_panel_construct", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_shell {
|
||||
label="shell";
|
||||
color="#cccccc";
|
||||
"0x0042a970" [label="0x0042a970\\nshell_open_file_dialog_copy_selected_path_and_restore_cwd", fillcolor="#f8f8f8"];
|
||||
"0x004333f0" [label="0x004333f0\\nshell_setup_build_file_list_records_from_current_root_and_pattern", fillcolor="#f8f8f8"];
|
||||
"0x004336a0" [label="0x004336a0\\nshell_setup_file_list_construct_and_scan_dataset", fillcolor="#f8f8f8"];
|
||||
"0x00434050" [label="0x00434050\\nshell_has_auxiliary_preview_owner", fillcolor="#f8f8f8"];
|
||||
"0x0046a6c0" [label="0x0046a6c0\\nmultiplayer_session_event_publish_registration_field", fillcolor="#f8f8f8"];
|
||||
"0x004839b0" [label="0x004839b0\\nshell_setup_query_file_list_uses_map_extension_pattern", fillcolor="#f8f8f8"];
|
||||
"0x004839e0" [label="0x004839e0\\nshell_setup_query_file_list_root_dir_name", fillcolor="#f8f8f8"];
|
||||
"0x00484910" [label="0x00484910\\nshell_save_graphics_config", fillcolor="#f8f8f8"];
|
||||
"0x004b8dc0" [label="0x004b8dc0\\nshell_campaign_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004b8e60" [label="0x004b8e60\\nshell_campaign_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004b9a20" [label="0x004b9a20\\nshell_building_detail_refresh_flagged_service_capability_rows", fillcolor="#f8f8f8"];
|
||||
"0x004ba3d0" [label="0x004ba3d0\\nshell_building_detail_refresh_subject_cargo_and_service_rows", fillcolor="#f8f8f8"];
|
||||
"0x004bad20" [label="0x004bad20\\nshell_building_detail_refresh_subject_pair_value_rows", fillcolor="#f8f8f8"];
|
||||
"0x004c2ca0" [label="0x004c2ca0\\nshell_company_detail_window_refresh_controls", fillcolor="#f8f8f8"];
|
||||
"0x004c3890" [label="0x004c3890\\nshell_company_detail_issue_bond_offer_flow", fillcolor="#f8f8f8"];
|
||||
"0x004c3f30" [label="0x004c3f30\\nshell_company_detail_issue_stock_offer_flow", fillcolor="#f8f8f8"];
|
||||
"0x004c46d0" [label="0x004c46d0\\nshell_company_detail_buyback_stock_flow", fillcolor="#f8f8f8"];
|
||||
"0x004c4c70" [label="0x004c4c70\\nshell_company_detail_setup_dividend_rate_adjust_controls", fillcolor="#f8f8f8"];
|
||||
"0x004c4e30" [label="0x004c4e30\\nshell_company_detail_render_change_dividend_rate_dialog", fillcolor="#f8f8f8"];
|
||||
"0x004c5140" [label="0x004c5140\\nshell_company_detail_handle_change_dividend_rate_dialog_message", fillcolor="#f8f8f8"];
|
||||
"0x004c5360" [label="0x004c5360\\nshell_company_detail_change_dividend_rate_flow", fillcolor="#f8f8f8"];
|
||||
"0x004c56a0" [label="0x004c56a0\\nshell_company_detail_window_handle_message", fillcolor="#f8f8f8"];
|
||||
"0x004c5a0e" [label="0x004c5a0e\\nshell_company_detail_resign_chairmanship_flow", fillcolor="#f8f8f8"];
|
||||
"0x004c5b99" [label="0x004c5b99\\nshell_company_detail_bankruptcy_flow", fillcolor="#f8f8f8"];
|
||||
"0x004c5fc9" [label="0x004c5fc9\\nshell_company_detail_buy_territory_access_rights_flow", fillcolor="#f8f8f8"];
|
||||
"0x004c7bc0" [label="0x004c7bc0\\nshell_credits_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004c7fc0" [label="0x004c7fc0\\nshell_credits_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004c98a0" [label="0x004c98a0\\nshell_open_custom_modal_dialog_with_callbacks", fillcolor="#f8f8f8"];
|
||||
"0x004dd010" [label="0x004dd010\\nshell_file_request_dialog_collect_target_path", fillcolor="#f8f8f8"];
|
||||
"0x004ddbd0" [label="0x004ddbd0\\nshell_detail_panel_transition_manager", fillcolor="#f8f8f8"];
|
||||
"0x004dfbe0" [label="0x004dfbe0\\nshell_game_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004dfd70" [label="0x004dfd70\\nshell_game_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004e0ba0" [label="0x004e0ba0\\ngame_uppermost_window_handle_message", fillcolor="#f8f8f8"];
|
||||
"0x004ea620" [label="0x004ea620\\nshell_load_screen_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004ea730" [label="0x004ea730\\nshell_load_screen_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004eb0b0" [label="0x004eb0b0\\nshell_open_grayscale_map_tga_picker_and_stage_selection", fillcolor="#f8f8f8"];
|
||||
"0x004ec640" [label="0x004ec640\\nshell_company_detail_attempt_merger_flow", fillcolor="#f8f8f8"];
|
||||
"0x004ee0e0" [label="0x004ee0e0\\nmultiplayer_open_staged_text_entry_dialog", fillcolor="#f8f8f8"];
|
||||
"0x004efe80" [label="0x004efe80\\nmultiplayer_window_init_globals", fillcolor="#f8f8f8"];
|
||||
"0x004fe120" [label="0x004fe120\\nshell_has_settings_window", fillcolor="#f8f8f8"];
|
||||
"0x00501e50" [label="0x00501e50\\nshell_open_settings_window", fillcolor="#f8f8f8"];
|
||||
"0x00501f20" [label="0x00501f20\\nshell_query_registry_open_command_for_http_or_rtf_target", fillcolor="#f8f8f8"];
|
||||
"0x00502030" [label="0x00502030\\nshell_setup_window_draw_table_driven_payload_category_row", fillcolor="#f8f8f8"];
|
||||
"0x00502160" [label="0x00502160\\nshell_setup_window_set_first_persisted_selector_flag_or_index_and_save_config", fillcolor="#f8f8f8"];
|
||||
"0x005021c0" [label="0x005021c0\\nshell_setup_window_set_second_persisted_selector_flag_or_index_and_save_config", fillcolor="#f8f8f8"];
|
||||
"0x00502220" [label="0x00502220\\nshell_setup_window_publish_selected_profile_labels_and_preview_surface [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00502550" [label="0x00502550\\nshell_setup_window_refresh_selection_lists_and_summary_fields [seed]", fillcolor="#ffe9a8"];
|
||||
"0x005027b0" [label="0x005027b0\\nshell_setup_window_refresh_file_backed_selection_list_panel [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00502910" [label="0x00502910\\nshell_setup_window_refresh_mode_dependent_lists [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00502c00" [label="0x00502c00\\nshell_setup_window_select_launch_mode_and_apply_shell_state [seed]", fillcolor="#ffe9a8"];
|
||||
"0x005033d0" [label="0x005033d0\\nshell_setup_window_handle_message [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00504010" [label="0x00504010\\nshell_setup_window_construct [seed]", fillcolor="#ffe9a8"];
|
||||
"0x0050ccc0" [label="0x0050ccc0\\nshell_company_detail_attempt_chairmanship_takeover_flow", fillcolor="#f8f8f8"];
|
||||
"0x005174e0" [label="0x005174e0\\nshell_video_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x00517570" [label="0x00517570\\nshell_video_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x0051c920" [label="0x0051c920\\nlocalization_lookup_display_label_by_stem_or_fallback", fillcolor="#f8f8f8"];
|
||||
"0x0051eea0" [label="0x0051eea0\\nshell_save_display_runtime_config", fillcolor="#f8f8f8"];
|
||||
"0x0053f830" [label="0x0053f830\\nshell_window_find_registered_child_control_by_id", fillcolor="#f8f8f8"];
|
||||
"0x0053f9c0" [label="0x0053f9c0\\nshell_window_register_child_control_sorted_by_priority_and_optional_tag", fillcolor="#f8f8f8"];
|
||||
"0x0053fa50" [label="0x0053fa50\\nshell_window_bind_resource_and_initialize_child_control_links", fillcolor="#f8f8f8"];
|
||||
"0x005411c0" [label="0x005411c0\\nshell_query_tga_header_is_supported_truecolor_image", fillcolor="#f8f8f8"];
|
||||
"0x00552560" [label="0x00552560\\nshell_queue_world_anchor_marker", fillcolor="#f8f8f8"];
|
||||
"0x00558130" [label="0x00558130\\nshell_child_control_set_owner_resolve_caption_and_refresh", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_simulation {
|
||||
label="simulation";
|
||||
color="#cccccc";
|
||||
"0x00404ce0" [label="0x00404ce0\\nsimulation_try_select_and_publish_company_start_or_city_connection_news", fillcolor="#f8f8f8"];
|
||||
"0x00423d70" [label="0x00423d70\\ncompany_repay_bond_slot_and_compact_debt_table", fillcolor="#f8f8f8"];
|
||||
"0x00426260" [label="0x00426260\\ncompany_compute_board_approved_dividend_rate_ceiling", fillcolor="#f8f8f8"];
|
||||
"0x00426d60" [label="0x00426d60\\ncompany_deactivate_and_clear_chairman_share_links", fillcolor="#f8f8f8"];
|
||||
"0x00437b20" [label="0x00437b20\\nsimulation_run_chunked_fast_forward_burst", fillcolor="#f8f8f8"];
|
||||
"0x00482d10" [label="0x00482d10\\nruntime_query_cached_local_exe_version_float", fillcolor="#f8f8f8"];
|
||||
"0x00482d80" [label="0x00482d80\\nruntime_query_cached_local_exe_version_string", fillcolor="#f8f8f8"];
|
||||
"0x00482e00" [label="0x00482e00\\nruntime_query_hundredths_scaled_build_version", fillcolor="#f8f8f8"];
|
||||
"0x00517d40" [label="0x00517d40\\nindexed_collection_entry_id_is_live", fillcolor="#f8f8f8"];
|
||||
"0x00518140" [label="0x00518140\\nindexed_collection_resolve_live_entry_by_id", fillcolor="#f8f8f8"];
|
||||
"0x00518380" [label="0x00518380\\nindexed_collection_get_nth_live_entry_id", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_support {
|
||||
label="support";
|
||||
color="#cccccc";
|
||||
"0x00559520" [label="0x00559520\\nsurface_init_rgba_pixel_buffer", fillcolor="#f8f8f8"];
|
||||
}
|
||||
"0x00402cb0" -> "0x00404640";
|
||||
"0x00402cb0" -> "0x004046a0";
|
||||
"0x00402cb0" -> "0x00404c60";
|
||||
"0x00402cb0" -> "0x00404ce0";
|
||||
"0x00402cb0" -> "0x0040ef10";
|
||||
"0x00402cb0" -> "0x004134d0";
|
||||
"0x00402cb0" -> "0x00417840";
|
||||
"0x00402cb0" -> "0x004197e0";
|
||||
"0x00402cb0" -> "0x00482e00";
|
||||
"0x00402cb0" -> "0x004a01a0";
|
||||
"0x00404640" -> "0x00402cb0";
|
||||
"0x00404640" -> "0x004046a0";
|
||||
"0x004046a0" -> "0x00402cb0";
|
||||
"0x004046a0" -> "0x00404640";
|
||||
"0x00404c60" -> "0x00402cb0";
|
||||
"0x00404c60" -> "0x00404ce0";
|
||||
"0x00404ce0" -> "0x00404c60";
|
||||
"0x0040e360" -> "0x0040ee10";
|
||||
"0x0040e450" -> "0x004133b0";
|
||||
"0x0040e450" -> "0x00414470";
|
||||
"0x0040e450" -> "0x00416e20";
|
||||
"0x0040e450" -> "0x00418a60";
|
||||
"0x0040ee10" -> "0x0040e360";
|
||||
"0x0040ee10" -> "0x004133b0";
|
||||
"0x0040ee10" -> "0x00415f20";
|
||||
"0x0040ee10" -> "0x00434050";
|
||||
"0x00412ca0" -> "0x004235c0";
|
||||
"0x004133b0" -> "0x0040e450";
|
||||
"0x004133b0" -> "0x0040ee10";
|
||||
"0x004133b0" -> "0x00413f50";
|
||||
"0x004133b0" -> "0x00414480";
|
||||
"0x004134d0" -> "0x00422ee0";
|
||||
"0x00413f50" -> "0x004133b0";
|
||||
"0x00414470" -> "0x0040e450";
|
||||
"0x00414470" -> "0x00418a60";
|
||||
"0x00414480" -> "0x004133b0";
|
||||
"0x00415f20" -> "0x0040ee10";
|
||||
"0x00416e20" -> "0x0040e450";
|
||||
"0x00416e20" -> "0x00518140";
|
||||
"0x00416e20" -> "0x00518380";
|
||||
"0x00417840" -> "0x00402cb0";
|
||||
"0x00417840" -> "0x004197e0";
|
||||
"0x00418a60" -> "0x0040e450";
|
||||
"0x004197e0" -> "0x00402cb0";
|
||||
"0x004197e0" -> "0x00417840";
|
||||
"0x0041e2b0" -> "0x0041e220";
|
||||
"0x0041e2b0" -> "0x0041ea50";
|
||||
"0x0041ea50" -> "0x0041e2b0";
|
||||
"0x0041ea50" -> "0x00518140";
|
||||
"0x0041ea50" -> "0x00518380";
|
||||
"0x0041f9b0" -> "0x004235c0";
|
||||
"0x00421b60" -> "0x004384d0";
|
||||
"0x00421c20" -> "0x004235c0";
|
||||
"0x00422900" -> "0x004235c0";
|
||||
"0x00422be0" -> "0x004235c0";
|
||||
"0x00422ee0" -> "0x004134d0";
|
||||
"0x00422ee0" -> "0x004235c0";
|
||||
"0x004235c0" -> "0x00412ca0";
|
||||
"0x004235c0" -> "0x0041f9b0";
|
||||
"0x004235c0" -> "0x0041fac0";
|
||||
"0x004235c0" -> "0x00422900";
|
||||
"0x004235c0" -> "0x00422be0";
|
||||
"0x004235c0" -> "0x00422ee0";
|
||||
"0x00426260" -> "0x004c5140";
|
||||
"0x004333f0" -> "0x004336a0";
|
||||
"0x004333f0" -> "0x004839b0";
|
||||
"0x004333f0" -> "0x004839e0";
|
||||
"0x004333f0" -> "0x0051c920";
|
||||
"0x004336a0" -> "0x004333f0";
|
||||
"0x004336a0" -> "0x00502c00";
|
||||
"0x00437220" -> "0x004384d0";
|
||||
"0x004377a0" -> "0x004cc2d0";
|
||||
"0x00437b20" -> "0x004384d0";
|
||||
"0x004384d0" -> "0x004133b0";
|
||||
"0x004384d0" -> "0x0041ea50";
|
||||
"0x004384d0" -> "0x00421b60";
|
||||
"0x004384d0" -> "0x00421c20";
|
||||
"0x004384d0" -> "0x00437220";
|
||||
"0x004384d0" -> "0x004377a0";
|
||||
"0x004384d0" -> "0x00437b20";
|
||||
"0x004384d0" -> "0x0044fb70";
|
||||
"0x004384d0" -> "0x0047d440";
|
||||
"0x004384d0" -> "0x004882e0";
|
||||
"0x00438890" -> "0x004384d0";
|
||||
"0x00438890" -> "0x00445ac0";
|
||||
"0x00438890" -> "0x005033d0";
|
||||
"0x00443a50" -> "0x00438890";
|
||||
"0x00443a50" -> "0x00482ec0";
|
||||
"0x00445ac0" -> "0x00443a50";
|
||||
"0x00445ac0" -> "0x00446d40";
|
||||
"0x00445ac0" -> "0x004dd010";
|
||||
"0x0046a6c0" -> "0x00482d80";
|
||||
"0x00477820" -> "0x0047d440";
|
||||
"0x00477860" -> "0x0047d440";
|
||||
"0x0047d440" -> "0x004377a0";
|
||||
"0x0047d440" -> "0x00477820";
|
||||
"0x0047d440" -> "0x00477860";
|
||||
"0x00482d10" -> "0x00482d80";
|
||||
"0x00482d10" -> "0x00482e00";
|
||||
"0x00482d80" -> "0x0046a6c0";
|
||||
"0x00482d80" -> "0x00482d10";
|
||||
"0x00482e00" -> "0x00402cb0";
|
||||
"0x00482e00" -> "0x00482d10";
|
||||
"0x00482e00" -> "0x004a65b0";
|
||||
"0x00482ec0" -> "0x00438890";
|
||||
"0x00482ec0" -> "0x00443a50";
|
||||
"0x00482ec0" -> "0x004840e0";
|
||||
"0x00482ec0" -> "0x004b8dc0";
|
||||
"0x00482ec0" -> "0x004b8e60";
|
||||
"0x00482ec0" -> "0x004c7bc0";
|
||||
"0x00482ec0" -> "0x004c7fc0";
|
||||
"0x00482ec0" -> "0x004dfbe0";
|
||||
"0x00482ec0" -> "0x004dfd70";
|
||||
"0x00482ec0" -> "0x004ea620";
|
||||
"0x00482ec0" -> "0x004ea730";
|
||||
"0x00482ec0" -> "0x004efe80";
|
||||
"0x00482ec0" -> "0x00504010";
|
||||
"0x00482ec0" -> "0x005174e0";
|
||||
"0x00482ec0" -> "0x00517570";
|
||||
"0x004839b0" -> "0x004333f0";
|
||||
"0x004839e0" -> "0x004333f0";
|
||||
"0x004840e0" -> "0x00482ec0";
|
||||
"0x00484910" -> "0x0051eea0";
|
||||
"0x004882e0" -> "0x004384d0";
|
||||
"0x004a01a0" -> "0x00402cb0";
|
||||
"0x004a01a0" -> "0x00518140";
|
||||
"0x004a5280" -> "0x004a65b0";
|
||||
"0x004a5900" -> "0x004a5280";
|
||||
"0x004a5900" -> "0x004a65b0";
|
||||
"0x004a65b0" -> "0x00482e00";
|
||||
"0x004a65b0" -> "0x004a5280";
|
||||
"0x004a65b0" -> "0x004a5900";
|
||||
"0x004a65b0" -> "0x004a6630";
|
||||
"0x004a6630" -> "0x004a65b0";
|
||||
"0x004b8dc0" -> "0x004b8e60";
|
||||
"0x004ba3d0" -> "0x004b9a20";
|
||||
"0x004ba3d0" -> "0x004bad20";
|
||||
"0x004ba3d0" -> "0x00517d40";
|
||||
"0x004ba3d0" -> "0x00518140";
|
||||
"0x004ba3d0" -> "0x0051c920";
|
||||
"0x004bad20" -> "0x004ba3d0";
|
||||
"0x004c2ca0" -> "0x004c56a0";
|
||||
"0x004c3890" -> "0x004c56a0";
|
||||
"0x004c3f30" -> "0x004c56a0";
|
||||
"0x004c46d0" -> "0x004c56a0";
|
||||
"0x004c4c70" -> "0x004c4e30";
|
||||
"0x004c4c70" -> "0x004c5140";
|
||||
"0x004c4c70" -> "0x004c5360";
|
||||
"0x004c4c70" -> "0x0053f9c0";
|
||||
"0x004c4e30" -> "0x004c5360";
|
||||
"0x004c4e30" -> "0x004c98a0";
|
||||
"0x004c5140" -> "0x00426260";
|
||||
"0x004c5140" -> "0x004c5360";
|
||||
"0x004c5140" -> "0x004c98a0";
|
||||
"0x004c5360" -> "0x004c4c70";
|
||||
"0x004c5360" -> "0x004c4e30";
|
||||
"0x004c5360" -> "0x004c5140";
|
||||
"0x004c5360" -> "0x004c56a0";
|
||||
"0x004c56a0" -> "0x00423d70";
|
||||
"0x004c56a0" -> "0x00426d60";
|
||||
"0x004c56a0" -> "0x004c2ca0";
|
||||
"0x004c56a0" -> "0x004c3890";
|
||||
"0x004c56a0" -> "0x004c3f30";
|
||||
"0x004c56a0" -> "0x004c46d0";
|
||||
"0x004c56a0" -> "0x004c5360";
|
||||
"0x004c56a0" -> "0x004c5a0e";
|
||||
"0x004c56a0" -> "0x004c5b99";
|
||||
"0x004c56a0" -> "0x004c5fc9";
|
||||
"0x004c56a0" -> "0x004ddbd0";
|
||||
"0x004c56a0" -> "0x004ec640";
|
||||
"0x004c56a0" -> "0x0050ccc0";
|
||||
"0x004c5a0e" -> "0x004c56a0";
|
||||
"0x004c5b99" -> "0x004c56a0";
|
||||
"0x004c5fc9" -> "0x004c56a0";
|
||||
"0x004c7bc0" -> "0x004c7fc0";
|
||||
"0x004c7fc0" -> "0x0053fa50";
|
||||
"0x004c98a0" -> "0x004ee0e0";
|
||||
"0x004cc2d0" -> "0x004c9da0";
|
||||
"0x004dd010" -> "0x004839b0";
|
||||
"0x004dfbe0" -> "0x004dfd70";
|
||||
"0x004dfbe0" -> "0x0053fa50";
|
||||
"0x004e0ba0" -> "0x0053f830";
|
||||
"0x004ea620" -> "0x004ea730";
|
||||
"0x004ea620" -> "0x0053fa50";
|
||||
"0x004ea730" -> "0x004ea620";
|
||||
"0x004ec640" -> "0x004c56a0";
|
||||
"0x004ee0e0" -> "0x00484910";
|
||||
"0x004ee0e0" -> "0x004c98a0";
|
||||
"0x00502030" -> "0x00504010";
|
||||
"0x00502030" -> "0x00552560";
|
||||
"0x00502160" -> "0x00484910";
|
||||
"0x00502160" -> "0x005021c0";
|
||||
"0x00502160" -> "0x00504010";
|
||||
"0x005021c0" -> "0x00484910";
|
||||
"0x005021c0" -> "0x00502160";
|
||||
"0x005021c0" -> "0x00504010";
|
||||
"0x00502220" -> "0x00502c00";
|
||||
"0x00502220" -> "0x0053f830";
|
||||
"0x00502220" -> "0x00559520";
|
||||
"0x005027b0" -> "0x004336a0";
|
||||
"0x005027b0" -> "0x00502220";
|
||||
"0x005027b0" -> "0x00502c00";
|
||||
"0x00502910" -> "0x005027b0";
|
||||
"0x00502910" -> "0x00502c00";
|
||||
"0x00502c00" -> "0x004336a0";
|
||||
"0x00502c00" -> "0x00438890";
|
||||
"0x00502c00" -> "0x00482d80";
|
||||
"0x00502c00" -> "0x004eb0b0";
|
||||
"0x00502c00" -> "0x005027b0";
|
||||
"0x00502c00" -> "0x00502910";
|
||||
"0x00502c00" -> "0x005033d0";
|
||||
"0x005033d0" -> "0x0042a970";
|
||||
"0x005033d0" -> "0x00484910";
|
||||
"0x005033d0" -> "0x004eb0b0";
|
||||
"0x005033d0" -> "0x004fe120";
|
||||
"0x005033d0" -> "0x00501e50";
|
||||
"0x005033d0" -> "0x00501f20";
|
||||
"0x005033d0" -> "0x00502220";
|
||||
"0x005033d0" -> "0x00502550";
|
||||
"0x005033d0" -> "0x00502910";
|
||||
"0x005033d0" -> "0x00502c00";
|
||||
"0x005033d0" -> "0x00504010";
|
||||
"0x005033d0" -> "0x005411c0";
|
||||
"0x00504010" -> "0x00502030";
|
||||
"0x00504010" -> "0x00502160";
|
||||
"0x00504010" -> "0x005021c0";
|
||||
"0x00504010" -> "0x00502550";
|
||||
"0x00504010" -> "0x00502910";
|
||||
"0x00504010" -> "0x00502c00";
|
||||
"0x00504010" -> "0x0053f830";
|
||||
"0x00504010" -> "0x0053f9c0";
|
||||
"0x00504010" -> "0x0053fa50";
|
||||
"0x0050ccc0" -> "0x004c56a0";
|
||||
"0x005174e0" -> "0x0053fa50";
|
||||
"0x00517570" -> "0x00484910";
|
||||
"0x00517570" -> "0x005174e0";
|
||||
"0x00517d40" -> "0x004ba3d0";
|
||||
"0x00517d40" -> "0x00518140";
|
||||
"0x0051c920" -> "0x004ba3d0";
|
||||
"0x0053f830" -> "0x004e0ba0";
|
||||
"0x0053f830" -> "0x00502220";
|
||||
"0x0053f830" -> "0x00504010";
|
||||
"0x0053f9c0" -> "0x004c4c70";
|
||||
"0x0053f9c0" -> "0x00558130";
|
||||
"0x005411c0" -> "0x005033d0";
|
||||
"0x00558130" -> "0x0053f9c0";
|
||||
}
|
||||
|
|
@ -0,0 +1,486 @@
|
|||
# Setup Window Submode Subgraph (Depth 5, Forward Only)
|
||||
|
||||
- Nodes: `126`
|
||||
- Edges: `246`
|
||||
- Seeds: `0x00502220`, `0x00502550`, `0x005027b0`, `0x00502910`, `0x00502c00`, `0x005033d0`, `0x00504010`
|
||||
- Graphviz: `setup-window-submodes-depth5-forward-subgraph.dot`
|
||||
|
||||
## Nodes
|
||||
|
||||
| Address | Name | Subsystem | Confidence |
|
||||
| --- | --- | --- | --- |
|
||||
| `0x00402cb0` | `city_connection_try_build_route_with_optional_direct_site_placement` | `map` | `3` |
|
||||
| `0x00404640` | `city_connection_bonus_try_compact_route_builder_from_region_entry` | `map` | `3` |
|
||||
| `0x004046a0` | `city_connection_bonus_build_peer_route_candidate` | `map` | `4` |
|
||||
| `0x00404c60` | `city_connection_try_build_route_between_region_entry_pair` | `map` | `3` |
|
||||
| `0x00404ce0` | `simulation_try_select_and_publish_company_start_or_city_connection_news` | `simulation` | `3` |
|
||||
| `0x0040e360` | `placed_structure_refresh_linked_site_anchor_position_triplet_for_local_runtime` | `map` | `2` |
|
||||
| `0x0040e450` | `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem` | `map` | `2` |
|
||||
| `0x0040ee10` | `placed_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon` | `map` | `2` |
|
||||
| `0x0040ef10` | `placed_structure_finalize_creation_or_rebuild_local_runtime_state` | `map` | `3` |
|
||||
| `0x00412ca0` | `world_region_pick_commercial_profile_label_by_region_rank` | `map` | `4` |
|
||||
| `0x004133b0` | `placed_structure_collection_refresh_local_runtime_records_and_position_scalars` | `map` | `2` |
|
||||
| `0x004134d0` | `placed_structure_collection_allocate_and_construct_entry` | `map` | `3` |
|
||||
| `0x00413f50` | `placed_structure_local_runtime_site_id_queue_pop_next` | `map` | `3` |
|
||||
| `0x00414470` | `placed_structure_cache_projected_rect_profile_slot_id` | `map` | `3` |
|
||||
| `0x00414480` | `placed_structure_local_runtime_site_id_queue_count` | `map` | `3` |
|
||||
| `0x00415f20` | `placed_structure_recursive_collect_connected_component_tile_bounds` | `map` | `3` |
|
||||
| `0x00416e20` | `indexed_collection_resolve_live_entry_id_by_stem_string` | `map` | `3` |
|
||||
| `0x00417840` | `placed_structure_project_candidate_grid_extent_offset_by_rotation` | `map` | `3` |
|
||||
| `0x00418a60` | `placed_structure_clone_template_local_runtime_record_for_subject_and_refresh_component_bounds` | `map` | `2` |
|
||||
| `0x004197e0` | `placed_structure_validate_projected_candidate_placement` | `map` | `3` |
|
||||
| `0x0041e220` | `structure_candidate_is_enabled_for_current_year` | `map` | `3` |
|
||||
| `0x0041e2b0` | `structure_candidate_rebuild_local_service_metrics` | `map` | `3` |
|
||||
| `0x0041ea50` | `world_setup_building_collection_phase` | `map` | `3` |
|
||||
| `0x0041f9b0` | `world_region_count_structure_profiles_before_year_for_category` | `map` | `4` |
|
||||
| `0x0041fac0` | `world_region_read_structure_profile_label_and_weight_by_index` | `map` | `4` |
|
||||
| `0x00421b60` | `world_region_collection_seed_default_regions` | `map` | `4` |
|
||||
| `0x00421c20` | `world_region_collection_run_building_population_pass` | `map` | `4` |
|
||||
| `0x00422900` | `world_region_accumulate_structure_category_totals` | `map` | `3` |
|
||||
| `0x00422be0` | `world_region_count_placed_structures_for_category` | `map` | `3` |
|
||||
| `0x00422ee0` | `world_region_try_place_candidate_structure` | `map` | `4` |
|
||||
| `0x004235c0` | `world_region_balance_structure_demand_and_place_candidates` | `map` | `4` |
|
||||
| `0x00423d70` | `company_repay_bond_slot_and_compact_debt_table` | `simulation` | `4` |
|
||||
| `0x00426260` | `company_compute_board_approved_dividend_rate_ceiling` | `simulation` | `4` |
|
||||
| `0x00426d60` | `company_deactivate_and_clear_chairman_share_links` | `simulation` | `4` |
|
||||
| `0x0042a970` | `shell_open_file_dialog_copy_selected_path_and_restore_cwd` | `shell` | `3` |
|
||||
| `0x004333f0` | `shell_setup_build_file_list_records_from_current_root_and_pattern` | `shell` | `3` |
|
||||
| `0x004336a0` | `shell_setup_file_list_construct_and_scan_dataset` | `shell` | `3` |
|
||||
| `0x00434050` | `shell_has_auxiliary_preview_owner` | `shell` | `4` |
|
||||
| `0x00437220` | `world_build_chairman_profile_slot_records` | `map` | `4` |
|
||||
| `0x004377a0` | `world_seed_default_chairman_profile_slots` | `map` | `4` |
|
||||
| `0x00437b20` | `simulation_run_chunked_fast_forward_burst` | `simulation` | `3` |
|
||||
| `0x004384d0` | `world_run_post_load_generation_pipeline` | `map` | `4` |
|
||||
| `0x00438890` | `shell_active_mode_run_profile_startup_and_load_dispatch` | `map` | `4` |
|
||||
| `0x00443a50` | `world_entry_transition_and_runtime_bringup` | `map` | `4` |
|
||||
| `0x00445ac0` | `shell_map_file_entry_coordinator` | `map` | `4` |
|
||||
| `0x00446d40` | `world_load_saved_runtime_state_bundle` | `map` | `4` |
|
||||
| `0x0044fb70` | `world_compute_transport_and_pricing_grid` | `map` | `3` |
|
||||
| `0x0046a6c0` | `multiplayer_session_event_publish_registration_field` | `shell` | `3` |
|
||||
| `0x00477820` | `profile_collection_count_active_chairman_records` | `map` | `4` |
|
||||
| `0x00477860` | `profile_collection_get_nth_active_chairman_record` | `map` | `4` |
|
||||
| `0x0047d440` | `world_conditionally_seed_named_starting_railroad_companies` | `map` | `4` |
|
||||
| `0x00482d10` | `runtime_query_cached_local_exe_version_float` | `simulation` | `3` |
|
||||
| `0x00482d80` | `runtime_query_cached_local_exe_version_string` | `simulation` | `3` |
|
||||
| `0x00482e00` | `runtime_query_hundredths_scaled_build_version` | `simulation` | `3` |
|
||||
| `0x00482ec0` | `shell_transition_mode` | `bootstrap` | `4` |
|
||||
| `0x004839b0` | `shell_setup_query_file_list_uses_map_extension_pattern` | `shell` | `3` |
|
||||
| `0x004839e0` | `shell_setup_query_file_list_root_dir_name` | `shell` | `3` |
|
||||
| `0x004840e0` | `bootstrap_init_shell_window_services` | `bootstrap` | `4` |
|
||||
| `0x00484910` | `shell_save_graphics_config` | `shell` | `4` |
|
||||
| `0x004882e0` | `world_region_border_overlay_rebuild` | `map` | `4` |
|
||||
| `0x004a01a0` | `route_entry_collection_try_build_path_between_optional_endpoint_entries` | `map` | `3` |
|
||||
| `0x004a5280` | `aux_route_entry_tracker_query_route_entry_pair_metric_via_weighted_recursive_search` | `map` | `3` |
|
||||
| `0x004a5900` | `aux_route_entry_tracker_query_route_entry_pair_metric_via_recursive_neighbor_walk` | `map` | `3` |
|
||||
| `0x004a65b0` | `aux_route_entry_tracker_dispatch_route_entry_pair_metric_query` | `map` | `3` |
|
||||
| `0x004a6630` | `aux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks` | `map` | `3` |
|
||||
| `0x004b8dc0` | `shell_campaign_window_destroy` | `shell` | `4` |
|
||||
| `0x004b8e60` | `shell_campaign_window_construct` | `shell` | `4` |
|
||||
| `0x004b9a20` | `shell_building_detail_refresh_flagged_service_capability_rows` | `shell` | `3` |
|
||||
| `0x004ba3d0` | `shell_building_detail_refresh_subject_cargo_and_service_rows` | `shell` | `3` |
|
||||
| `0x004bad20` | `shell_building_detail_refresh_subject_pair_value_rows` | `shell` | `3` |
|
||||
| `0x004c2ca0` | `shell_company_detail_window_refresh_controls` | `shell` | `4` |
|
||||
| `0x004c3890` | `shell_company_detail_issue_bond_offer_flow` | `shell` | `4` |
|
||||
| `0x004c3f30` | `shell_company_detail_issue_stock_offer_flow` | `shell` | `4` |
|
||||
| `0x004c46d0` | `shell_company_detail_buyback_stock_flow` | `shell` | `4` |
|
||||
| `0x004c4c70` | `shell_company_detail_setup_dividend_rate_adjust_controls` | `shell` | `4` |
|
||||
| `0x004c4e30` | `shell_company_detail_render_change_dividend_rate_dialog` | `shell` | `4` |
|
||||
| `0x004c5140` | `shell_company_detail_handle_change_dividend_rate_dialog_message` | `shell` | `4` |
|
||||
| `0x004c5360` | `shell_company_detail_change_dividend_rate_flow` | `shell` | `4` |
|
||||
| `0x004c56a0` | `shell_company_detail_window_handle_message` | `shell` | `4` |
|
||||
| `0x004c5a0e` | `shell_company_detail_resign_chairmanship_flow` | `shell` | `4` |
|
||||
| `0x004c5b99` | `shell_company_detail_bankruptcy_flow` | `shell` | `4` |
|
||||
| `0x004c5fc9` | `shell_company_detail_buy_territory_access_rights_flow` | `shell` | `4` |
|
||||
| `0x004c7bc0` | `shell_credits_window_destroy` | `shell` | `4` |
|
||||
| `0x004c7fc0` | `shell_credits_window_construct` | `shell` | `4` |
|
||||
| `0x004c98a0` | `shell_open_custom_modal_dialog_with_callbacks` | `shell` | `4` |
|
||||
| `0x004c9da0` | `map_editor_chairman_slot_panel_format_slot_card` | `map` | `4` |
|
||||
| `0x004cc2d0` | `map_editor_chairman_slot_panel_construct` | `map` | `4` |
|
||||
| `0x004dd010` | `shell_file_request_dialog_collect_target_path` | `shell` | `4` |
|
||||
| `0x004ddbd0` | `shell_detail_panel_transition_manager` | `shell` | `3` |
|
||||
| `0x004dfbe0` | `shell_game_window_construct` | `shell` | `4` |
|
||||
| `0x004dfd70` | `shell_game_window_destroy` | `shell` | `4` |
|
||||
| `0x004e0ba0` | `game_uppermost_window_handle_message` | `shell` | `4` |
|
||||
| `0x004ea620` | `shell_load_screen_window_construct` | `shell` | `4` |
|
||||
| `0x004ea730` | `shell_load_screen_window_destroy` | `shell` | `4` |
|
||||
| `0x004eb0b0` | `shell_open_grayscale_map_tga_picker_and_stage_selection` | `shell` | `3` |
|
||||
| `0x004ec640` | `shell_company_detail_attempt_merger_flow` | `shell` | `4` |
|
||||
| `0x004ee0e0` | `multiplayer_open_staged_text_entry_dialog` | `shell` | `3` |
|
||||
| `0x004efe80` | `multiplayer_window_init_globals` | `shell` | `4` |
|
||||
| `0x004fe120` | `shell_has_settings_window` | `shell` | `4` |
|
||||
| `0x00501e50` | `shell_open_settings_window` | `shell` | `4` |
|
||||
| `0x00501f20` | `shell_query_registry_open_command_for_http_or_rtf_target` | `shell` | `3` |
|
||||
| `0x00502030` | `shell_setup_window_draw_table_driven_payload_category_row` | `shell` | `3` |
|
||||
| `0x00502160` | `shell_setup_window_set_first_persisted_selector_flag_or_index_and_save_config` | `shell` | `3` |
|
||||
| `0x005021c0` | `shell_setup_window_set_second_persisted_selector_flag_or_index_and_save_config` | `shell` | `3` |
|
||||
| `0x00502220` | `shell_setup_window_publish_selected_profile_labels_and_preview_surface` | `shell` | `3` |
|
||||
| `0x00502550` | `shell_setup_window_refresh_selection_lists_and_summary_fields` | `shell` | `3` |
|
||||
| `0x005027b0` | `shell_setup_window_refresh_file_backed_selection_list_panel` | `shell` | `3` |
|
||||
| `0x00502910` | `shell_setup_window_refresh_mode_dependent_lists` | `shell` | `3` |
|
||||
| `0x00502c00` | `shell_setup_window_select_launch_mode_and_apply_shell_state` | `shell` | `4` |
|
||||
| `0x005033d0` | `shell_setup_window_handle_message` | `shell` | `3` |
|
||||
| `0x00504010` | `shell_setup_window_construct` | `shell` | `4` |
|
||||
| `0x0050ccc0` | `shell_company_detail_attempt_chairmanship_takeover_flow` | `shell` | `4` |
|
||||
| `0x005174e0` | `shell_video_window_construct` | `shell` | `4` |
|
||||
| `0x00517570` | `shell_video_window_destroy` | `shell` | `4` |
|
||||
| `0x00517d40` | `indexed_collection_entry_id_is_live` | `simulation` | `4` |
|
||||
| `0x00518140` | `indexed_collection_resolve_live_entry_by_id` | `simulation` | `4` |
|
||||
| `0x00518380` | `indexed_collection_get_nth_live_entry_id` | `simulation` | `4` |
|
||||
| `0x0051c920` | `localization_lookup_display_label_by_stem_or_fallback` | `shell` | `4` |
|
||||
| `0x0051eea0` | `shell_save_display_runtime_config` | `shell` | `4` |
|
||||
| `0x0053f830` | `shell_window_find_registered_child_control_by_id` | `shell` | `3` |
|
||||
| `0x0053f9c0` | `shell_window_register_child_control_sorted_by_priority_and_optional_tag` | `shell` | `3` |
|
||||
| `0x0053fa50` | `shell_window_bind_resource_and_initialize_child_control_links` | `shell` | `4` |
|
||||
| `0x005411c0` | `shell_query_tga_header_is_supported_truecolor_image` | `shell` | `3` |
|
||||
| `0x00552560` | `shell_queue_world_anchor_marker` | `shell` | `3` |
|
||||
| `0x00558130` | `shell_child_control_set_owner_resolve_caption_and_refresh` | `shell` | `3` |
|
||||
| `0x00559520` | `surface_init_rgba_pixel_buffer` | `support` | `3` |
|
||||
|
||||
## Edges
|
||||
|
||||
- `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x00404640` `city_connection_bonus_try_compact_route_builder_from_region_entry`
|
||||
-> `0x004046a0` `city_connection_bonus_build_peer_route_candidate`
|
||||
-> `0x00404c60` `city_connection_try_build_route_between_region_entry_pair`
|
||||
-> `0x00404ce0` `simulation_try_select_and_publish_company_start_or_city_connection_news`
|
||||
-> `0x0040ef10` `placed_structure_finalize_creation_or_rebuild_local_runtime_state`
|
||||
-> `0x004134d0` `placed_structure_collection_allocate_and_construct_entry`
|
||||
-> `0x00417840` `placed_structure_project_candidate_grid_extent_offset_by_rotation`
|
||||
-> `0x004197e0` `placed_structure_validate_projected_candidate_placement`
|
||||
-> `0x00482e00` `runtime_query_hundredths_scaled_build_version`
|
||||
-> `0x004a01a0` `route_entry_collection_try_build_path_between_optional_endpoint_entries`
|
||||
- `0x00404640` `city_connection_bonus_try_compact_route_builder_from_region_entry`
|
||||
-> `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x004046a0` `city_connection_bonus_build_peer_route_candidate`
|
||||
- `0x004046a0` `city_connection_bonus_build_peer_route_candidate`
|
||||
-> `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x00404640` `city_connection_bonus_try_compact_route_builder_from_region_entry`
|
||||
- `0x00404c60` `city_connection_try_build_route_between_region_entry_pair`
|
||||
-> `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x00404ce0` `simulation_try_select_and_publish_company_start_or_city_connection_news`
|
||||
- `0x00404ce0` `simulation_try_select_and_publish_company_start_or_city_connection_news`
|
||||
-> `0x00404c60` `city_connection_try_build_route_between_region_entry_pair`
|
||||
- `0x0040e360` `placed_structure_refresh_linked_site_anchor_position_triplet_for_local_runtime`
|
||||
-> `0x0040ee10` `placed_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon`
|
||||
- `0x0040e450` `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem`
|
||||
-> `0x004133b0` `placed_structure_collection_refresh_local_runtime_records_and_position_scalars`
|
||||
-> `0x00414470` `placed_structure_cache_projected_rect_profile_slot_id`
|
||||
-> `0x00416e20` `indexed_collection_resolve_live_entry_id_by_stem_string`
|
||||
-> `0x00418a60` `placed_structure_clone_template_local_runtime_record_for_subject_and_refresh_component_bounds`
|
||||
- `0x0040ee10` `placed_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon`
|
||||
-> `0x0040e360` `placed_structure_refresh_linked_site_anchor_position_triplet_for_local_runtime`
|
||||
-> `0x004133b0` `placed_structure_collection_refresh_local_runtime_records_and_position_scalars`
|
||||
-> `0x00415f20` `placed_structure_recursive_collect_connected_component_tile_bounds`
|
||||
-> `0x00434050` `shell_has_auxiliary_preview_owner`
|
||||
- `0x00412ca0` `world_region_pick_commercial_profile_label_by_region_rank`
|
||||
-> `0x004235c0` `world_region_balance_structure_demand_and_place_candidates`
|
||||
- `0x004133b0` `placed_structure_collection_refresh_local_runtime_records_and_position_scalars`
|
||||
-> `0x0040e450` `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem`
|
||||
-> `0x0040ee10` `placed_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon`
|
||||
-> `0x00413f50` `placed_structure_local_runtime_site_id_queue_pop_next`
|
||||
-> `0x00414480` `placed_structure_local_runtime_site_id_queue_count`
|
||||
- `0x004134d0` `placed_structure_collection_allocate_and_construct_entry`
|
||||
-> `0x00422ee0` `world_region_try_place_candidate_structure`
|
||||
- `0x00413f50` `placed_structure_local_runtime_site_id_queue_pop_next`
|
||||
-> `0x004133b0` `placed_structure_collection_refresh_local_runtime_records_and_position_scalars`
|
||||
- `0x00414470` `placed_structure_cache_projected_rect_profile_slot_id`
|
||||
-> `0x0040e450` `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem`
|
||||
-> `0x00418a60` `placed_structure_clone_template_local_runtime_record_for_subject_and_refresh_component_bounds`
|
||||
- `0x00414480` `placed_structure_local_runtime_site_id_queue_count`
|
||||
-> `0x004133b0` `placed_structure_collection_refresh_local_runtime_records_and_position_scalars`
|
||||
- `0x00415f20` `placed_structure_recursive_collect_connected_component_tile_bounds`
|
||||
-> `0x0040ee10` `placed_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon`
|
||||
- `0x00416e20` `indexed_collection_resolve_live_entry_id_by_stem_string`
|
||||
-> `0x0040e450` `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem`
|
||||
-> `0x00518140` `indexed_collection_resolve_live_entry_by_id`
|
||||
-> `0x00518380` `indexed_collection_get_nth_live_entry_id`
|
||||
- `0x00417840` `placed_structure_project_candidate_grid_extent_offset_by_rotation`
|
||||
-> `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x004197e0` `placed_structure_validate_projected_candidate_placement`
|
||||
- `0x00418a60` `placed_structure_clone_template_local_runtime_record_for_subject_and_refresh_component_bounds`
|
||||
-> `0x0040e450` `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem`
|
||||
- `0x004197e0` `placed_structure_validate_projected_candidate_placement`
|
||||
-> `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x00417840` `placed_structure_project_candidate_grid_extent_offset_by_rotation`
|
||||
- `0x0041e2b0` `structure_candidate_rebuild_local_service_metrics`
|
||||
-> `0x0041e220` `structure_candidate_is_enabled_for_current_year`
|
||||
-> `0x0041ea50` `world_setup_building_collection_phase`
|
||||
- `0x0041ea50` `world_setup_building_collection_phase`
|
||||
-> `0x0041e2b0` `structure_candidate_rebuild_local_service_metrics`
|
||||
-> `0x00518140` `indexed_collection_resolve_live_entry_by_id`
|
||||
-> `0x00518380` `indexed_collection_get_nth_live_entry_id`
|
||||
- `0x0041f9b0` `world_region_count_structure_profiles_before_year_for_category`
|
||||
-> `0x004235c0` `world_region_balance_structure_demand_and_place_candidates`
|
||||
- `0x00421b60` `world_region_collection_seed_default_regions`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x00421c20` `world_region_collection_run_building_population_pass`
|
||||
-> `0x004235c0` `world_region_balance_structure_demand_and_place_candidates`
|
||||
- `0x00422900` `world_region_accumulate_structure_category_totals`
|
||||
-> `0x004235c0` `world_region_balance_structure_demand_and_place_candidates`
|
||||
- `0x00422be0` `world_region_count_placed_structures_for_category`
|
||||
-> `0x004235c0` `world_region_balance_structure_demand_and_place_candidates`
|
||||
- `0x00422ee0` `world_region_try_place_candidate_structure`
|
||||
-> `0x004134d0` `placed_structure_collection_allocate_and_construct_entry`
|
||||
-> `0x004235c0` `world_region_balance_structure_demand_and_place_candidates`
|
||||
- `0x004235c0` `world_region_balance_structure_demand_and_place_candidates`
|
||||
-> `0x00412ca0` `world_region_pick_commercial_profile_label_by_region_rank`
|
||||
-> `0x0041f9b0` `world_region_count_structure_profiles_before_year_for_category`
|
||||
-> `0x0041fac0` `world_region_read_structure_profile_label_and_weight_by_index`
|
||||
-> `0x00422900` `world_region_accumulate_structure_category_totals`
|
||||
-> `0x00422be0` `world_region_count_placed_structures_for_category`
|
||||
-> `0x00422ee0` `world_region_try_place_candidate_structure`
|
||||
- `0x00426260` `company_compute_board_approved_dividend_rate_ceiling`
|
||||
-> `0x004c5140` `shell_company_detail_handle_change_dividend_rate_dialog_message`
|
||||
- `0x004333f0` `shell_setup_build_file_list_records_from_current_root_and_pattern`
|
||||
-> `0x004336a0` `shell_setup_file_list_construct_and_scan_dataset`
|
||||
-> `0x004839b0` `shell_setup_query_file_list_uses_map_extension_pattern`
|
||||
-> `0x004839e0` `shell_setup_query_file_list_root_dir_name`
|
||||
-> `0x0051c920` `localization_lookup_display_label_by_stem_or_fallback`
|
||||
- `0x004336a0` `shell_setup_file_list_construct_and_scan_dataset`
|
||||
-> `0x004333f0` `shell_setup_build_file_list_records_from_current_root_and_pattern`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
- `0x00437220` `world_build_chairman_profile_slot_records`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x004377a0` `world_seed_default_chairman_profile_slots`
|
||||
-> `0x004cc2d0` `map_editor_chairman_slot_panel_construct`
|
||||
- `0x00437b20` `simulation_run_chunked_fast_forward_burst`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
-> `0x004133b0` `placed_structure_collection_refresh_local_runtime_records_and_position_scalars`
|
||||
-> `0x0041ea50` `world_setup_building_collection_phase`
|
||||
-> `0x00421b60` `world_region_collection_seed_default_regions`
|
||||
-> `0x00421c20` `world_region_collection_run_building_population_pass`
|
||||
-> `0x00437220` `world_build_chairman_profile_slot_records`
|
||||
-> `0x004377a0` `world_seed_default_chairman_profile_slots`
|
||||
-> `0x00437b20` `simulation_run_chunked_fast_forward_burst`
|
||||
-> `0x0044fb70` `world_compute_transport_and_pricing_grid`
|
||||
-> `0x0047d440` `world_conditionally_seed_named_starting_railroad_companies`
|
||||
-> `0x004882e0` `world_region_border_overlay_rebuild`
|
||||
- `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
-> `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
-> `0x005033d0` `shell_setup_window_handle_message`
|
||||
- `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x00446d40` `world_load_saved_runtime_state_bundle`
|
||||
-> `0x004dd010` `shell_file_request_dialog_collect_target_path`
|
||||
- `0x0046a6c0` `multiplayer_session_event_publish_registration_field`
|
||||
-> `0x00482d80` `runtime_query_cached_local_exe_version_string`
|
||||
- `0x00477820` `profile_collection_count_active_chairman_records`
|
||||
-> `0x0047d440` `world_conditionally_seed_named_starting_railroad_companies`
|
||||
- `0x00477860` `profile_collection_get_nth_active_chairman_record`
|
||||
-> `0x0047d440` `world_conditionally_seed_named_starting_railroad_companies`
|
||||
- `0x0047d440` `world_conditionally_seed_named_starting_railroad_companies`
|
||||
-> `0x004377a0` `world_seed_default_chairman_profile_slots`
|
||||
-> `0x00477820` `profile_collection_count_active_chairman_records`
|
||||
-> `0x00477860` `profile_collection_get_nth_active_chairman_record`
|
||||
- `0x00482d10` `runtime_query_cached_local_exe_version_float`
|
||||
-> `0x00482d80` `runtime_query_cached_local_exe_version_string`
|
||||
-> `0x00482e00` `runtime_query_hundredths_scaled_build_version`
|
||||
- `0x00482d80` `runtime_query_cached_local_exe_version_string`
|
||||
-> `0x0046a6c0` `multiplayer_session_event_publish_registration_field`
|
||||
-> `0x00482d10` `runtime_query_cached_local_exe_version_float`
|
||||
- `0x00482e00` `runtime_query_hundredths_scaled_build_version`
|
||||
-> `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x00482d10` `runtime_query_cached_local_exe_version_float`
|
||||
-> `0x004a65b0` `aux_route_entry_tracker_dispatch_route_entry_pair_metric_query`
|
||||
- `0x00482ec0` `shell_transition_mode`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x004840e0` `bootstrap_init_shell_window_services`
|
||||
-> `0x004b8dc0` `shell_campaign_window_destroy`
|
||||
-> `0x004b8e60` `shell_campaign_window_construct`
|
||||
-> `0x004c7bc0` `shell_credits_window_destroy`
|
||||
-> `0x004c7fc0` `shell_credits_window_construct`
|
||||
-> `0x004dfbe0` `shell_game_window_construct`
|
||||
-> `0x004dfd70` `shell_game_window_destroy`
|
||||
-> `0x004ea620` `shell_load_screen_window_construct`
|
||||
-> `0x004ea730` `shell_load_screen_window_destroy`
|
||||
-> `0x004efe80` `multiplayer_window_init_globals`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
-> `0x005174e0` `shell_video_window_construct`
|
||||
-> `0x00517570` `shell_video_window_destroy`
|
||||
- `0x004839b0` `shell_setup_query_file_list_uses_map_extension_pattern`
|
||||
-> `0x004333f0` `shell_setup_build_file_list_records_from_current_root_and_pattern`
|
||||
- `0x004839e0` `shell_setup_query_file_list_root_dir_name`
|
||||
-> `0x004333f0` `shell_setup_build_file_list_records_from_current_root_and_pattern`
|
||||
- `0x004840e0` `bootstrap_init_shell_window_services`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x00484910` `shell_save_graphics_config`
|
||||
-> `0x0051eea0` `shell_save_display_runtime_config`
|
||||
- `0x004882e0` `world_region_border_overlay_rebuild`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x004a01a0` `route_entry_collection_try_build_path_between_optional_endpoint_entries`
|
||||
-> `0x00402cb0` `city_connection_try_build_route_with_optional_direct_site_placement`
|
||||
-> `0x00518140` `indexed_collection_resolve_live_entry_by_id`
|
||||
- `0x004a5280` `aux_route_entry_tracker_query_route_entry_pair_metric_via_weighted_recursive_search`
|
||||
-> `0x004a65b0` `aux_route_entry_tracker_dispatch_route_entry_pair_metric_query`
|
||||
- `0x004a5900` `aux_route_entry_tracker_query_route_entry_pair_metric_via_recursive_neighbor_walk`
|
||||
-> `0x004a5280` `aux_route_entry_tracker_query_route_entry_pair_metric_via_weighted_recursive_search`
|
||||
-> `0x004a65b0` `aux_route_entry_tracker_dispatch_route_entry_pair_metric_query`
|
||||
- `0x004a65b0` `aux_route_entry_tracker_dispatch_route_entry_pair_metric_query`
|
||||
-> `0x00482e00` `runtime_query_hundredths_scaled_build_version`
|
||||
-> `0x004a5280` `aux_route_entry_tracker_query_route_entry_pair_metric_via_weighted_recursive_search`
|
||||
-> `0x004a5900` `aux_route_entry_tracker_query_route_entry_pair_metric_via_recursive_neighbor_walk`
|
||||
-> `0x004a6630` `aux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks`
|
||||
- `0x004a6630` `aux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks`
|
||||
-> `0x004a65b0` `aux_route_entry_tracker_dispatch_route_entry_pair_metric_query`
|
||||
- `0x004b8dc0` `shell_campaign_window_destroy`
|
||||
-> `0x004b8e60` `shell_campaign_window_construct`
|
||||
- `0x004ba3d0` `shell_building_detail_refresh_subject_cargo_and_service_rows`
|
||||
-> `0x004b9a20` `shell_building_detail_refresh_flagged_service_capability_rows`
|
||||
-> `0x004bad20` `shell_building_detail_refresh_subject_pair_value_rows`
|
||||
-> `0x00517d40` `indexed_collection_entry_id_is_live`
|
||||
-> `0x00518140` `indexed_collection_resolve_live_entry_by_id`
|
||||
-> `0x0051c920` `localization_lookup_display_label_by_stem_or_fallback`
|
||||
- `0x004bad20` `shell_building_detail_refresh_subject_pair_value_rows`
|
||||
-> `0x004ba3d0` `shell_building_detail_refresh_subject_cargo_and_service_rows`
|
||||
- `0x004c2ca0` `shell_company_detail_window_refresh_controls`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c3890` `shell_company_detail_issue_bond_offer_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c3f30` `shell_company_detail_issue_stock_offer_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c46d0` `shell_company_detail_buyback_stock_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c4c70` `shell_company_detail_setup_dividend_rate_adjust_controls`
|
||||
-> `0x004c4e30` `shell_company_detail_render_change_dividend_rate_dialog`
|
||||
-> `0x004c5140` `shell_company_detail_handle_change_dividend_rate_dialog_message`
|
||||
-> `0x004c5360` `shell_company_detail_change_dividend_rate_flow`
|
||||
-> `0x0053f9c0` `shell_window_register_child_control_sorted_by_priority_and_optional_tag`
|
||||
- `0x004c4e30` `shell_company_detail_render_change_dividend_rate_dialog`
|
||||
-> `0x004c5360` `shell_company_detail_change_dividend_rate_flow`
|
||||
-> `0x004c98a0` `shell_open_custom_modal_dialog_with_callbacks`
|
||||
- `0x004c5140` `shell_company_detail_handle_change_dividend_rate_dialog_message`
|
||||
-> `0x00426260` `company_compute_board_approved_dividend_rate_ceiling`
|
||||
-> `0x004c5360` `shell_company_detail_change_dividend_rate_flow`
|
||||
-> `0x004c98a0` `shell_open_custom_modal_dialog_with_callbacks`
|
||||
- `0x004c5360` `shell_company_detail_change_dividend_rate_flow`
|
||||
-> `0x004c4c70` `shell_company_detail_setup_dividend_rate_adjust_controls`
|
||||
-> `0x004c4e30` `shell_company_detail_render_change_dividend_rate_dialog`
|
||||
-> `0x004c5140` `shell_company_detail_handle_change_dividend_rate_dialog_message`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
-> `0x00423d70` `company_repay_bond_slot_and_compact_debt_table`
|
||||
-> `0x00426d60` `company_deactivate_and_clear_chairman_share_links`
|
||||
-> `0x004c2ca0` `shell_company_detail_window_refresh_controls`
|
||||
-> `0x004c3890` `shell_company_detail_issue_bond_offer_flow`
|
||||
-> `0x004c3f30` `shell_company_detail_issue_stock_offer_flow`
|
||||
-> `0x004c46d0` `shell_company_detail_buyback_stock_flow`
|
||||
-> `0x004c5360` `shell_company_detail_change_dividend_rate_flow`
|
||||
-> `0x004c5a0e` `shell_company_detail_resign_chairmanship_flow`
|
||||
-> `0x004c5b99` `shell_company_detail_bankruptcy_flow`
|
||||
-> `0x004c5fc9` `shell_company_detail_buy_territory_access_rights_flow`
|
||||
-> `0x004ddbd0` `shell_detail_panel_transition_manager`
|
||||
-> `0x004ec640` `shell_company_detail_attempt_merger_flow`
|
||||
-> `0x0050ccc0` `shell_company_detail_attempt_chairmanship_takeover_flow`
|
||||
- `0x004c5a0e` `shell_company_detail_resign_chairmanship_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c5b99` `shell_company_detail_bankruptcy_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c5fc9` `shell_company_detail_buy_territory_access_rights_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004c7bc0` `shell_credits_window_destroy`
|
||||
-> `0x004c7fc0` `shell_credits_window_construct`
|
||||
- `0x004c7fc0` `shell_credits_window_construct`
|
||||
-> `0x0053fa50` `shell_window_bind_resource_and_initialize_child_control_links`
|
||||
- `0x004c98a0` `shell_open_custom_modal_dialog_with_callbacks`
|
||||
-> `0x004ee0e0` `multiplayer_open_staged_text_entry_dialog`
|
||||
- `0x004cc2d0` `map_editor_chairman_slot_panel_construct`
|
||||
-> `0x004c9da0` `map_editor_chairman_slot_panel_format_slot_card`
|
||||
- `0x004dd010` `shell_file_request_dialog_collect_target_path`
|
||||
-> `0x004839b0` `shell_setup_query_file_list_uses_map_extension_pattern`
|
||||
- `0x004dfbe0` `shell_game_window_construct`
|
||||
-> `0x004dfd70` `shell_game_window_destroy`
|
||||
-> `0x0053fa50` `shell_window_bind_resource_and_initialize_child_control_links`
|
||||
- `0x004e0ba0` `game_uppermost_window_handle_message`
|
||||
-> `0x0053f830` `shell_window_find_registered_child_control_by_id`
|
||||
- `0x004ea620` `shell_load_screen_window_construct`
|
||||
-> `0x004ea730` `shell_load_screen_window_destroy`
|
||||
-> `0x0053fa50` `shell_window_bind_resource_and_initialize_child_control_links`
|
||||
- `0x004ea730` `shell_load_screen_window_destroy`
|
||||
-> `0x004ea620` `shell_load_screen_window_construct`
|
||||
- `0x004ec640` `shell_company_detail_attempt_merger_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x004ee0e0` `multiplayer_open_staged_text_entry_dialog`
|
||||
-> `0x00484910` `shell_save_graphics_config`
|
||||
-> `0x004c98a0` `shell_open_custom_modal_dialog_with_callbacks`
|
||||
- `0x00502030` `shell_setup_window_draw_table_driven_payload_category_row`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
-> `0x00552560` `shell_queue_world_anchor_marker`
|
||||
- `0x00502160` `shell_setup_window_set_first_persisted_selector_flag_or_index_and_save_config`
|
||||
-> `0x00484910` `shell_save_graphics_config`
|
||||
-> `0x005021c0` `shell_setup_window_set_second_persisted_selector_flag_or_index_and_save_config`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
- `0x005021c0` `shell_setup_window_set_second_persisted_selector_flag_or_index_and_save_config`
|
||||
-> `0x00484910` `shell_save_graphics_config`
|
||||
-> `0x00502160` `shell_setup_window_set_first_persisted_selector_flag_or_index_and_save_config`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
- `0x00502220` `shell_setup_window_publish_selected_profile_labels_and_preview_surface`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
-> `0x0053f830` `shell_window_find_registered_child_control_by_id`
|
||||
-> `0x00559520` `surface_init_rgba_pixel_buffer`
|
||||
- `0x005027b0` `shell_setup_window_refresh_file_backed_selection_list_panel`
|
||||
-> `0x004336a0` `shell_setup_file_list_construct_and_scan_dataset`
|
||||
-> `0x00502220` `shell_setup_window_publish_selected_profile_labels_and_preview_surface`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
- `0x00502910` `shell_setup_window_refresh_mode_dependent_lists`
|
||||
-> `0x005027b0` `shell_setup_window_refresh_file_backed_selection_list_panel`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
- `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
-> `0x004336a0` `shell_setup_file_list_construct_and_scan_dataset`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00482d80` `runtime_query_cached_local_exe_version_string`
|
||||
-> `0x004eb0b0` `shell_open_grayscale_map_tga_picker_and_stage_selection`
|
||||
-> `0x005027b0` `shell_setup_window_refresh_file_backed_selection_list_panel`
|
||||
-> `0x00502910` `shell_setup_window_refresh_mode_dependent_lists`
|
||||
-> `0x005033d0` `shell_setup_window_handle_message`
|
||||
- `0x005033d0` `shell_setup_window_handle_message`
|
||||
-> `0x0042a970` `shell_open_file_dialog_copy_selected_path_and_restore_cwd`
|
||||
-> `0x00484910` `shell_save_graphics_config`
|
||||
-> `0x004eb0b0` `shell_open_grayscale_map_tga_picker_and_stage_selection`
|
||||
-> `0x004fe120` `shell_has_settings_window`
|
||||
-> `0x00501e50` `shell_open_settings_window`
|
||||
-> `0x00501f20` `shell_query_registry_open_command_for_http_or_rtf_target`
|
||||
-> `0x00502220` `shell_setup_window_publish_selected_profile_labels_and_preview_surface`
|
||||
-> `0x00502550` `shell_setup_window_refresh_selection_lists_and_summary_fields`
|
||||
-> `0x00502910` `shell_setup_window_refresh_mode_dependent_lists`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
-> `0x005411c0` `shell_query_tga_header_is_supported_truecolor_image`
|
||||
- `0x00504010` `shell_setup_window_construct`
|
||||
-> `0x00502030` `shell_setup_window_draw_table_driven_payload_category_row`
|
||||
-> `0x00502160` `shell_setup_window_set_first_persisted_selector_flag_or_index_and_save_config`
|
||||
-> `0x005021c0` `shell_setup_window_set_second_persisted_selector_flag_or_index_and_save_config`
|
||||
-> `0x00502550` `shell_setup_window_refresh_selection_lists_and_summary_fields`
|
||||
-> `0x00502910` `shell_setup_window_refresh_mode_dependent_lists`
|
||||
-> `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
-> `0x0053f830` `shell_window_find_registered_child_control_by_id`
|
||||
-> `0x0053f9c0` `shell_window_register_child_control_sorted_by_priority_and_optional_tag`
|
||||
-> `0x0053fa50` `shell_window_bind_resource_and_initialize_child_control_links`
|
||||
- `0x0050ccc0` `shell_company_detail_attempt_chairmanship_takeover_flow`
|
||||
-> `0x004c56a0` `shell_company_detail_window_handle_message`
|
||||
- `0x005174e0` `shell_video_window_construct`
|
||||
-> `0x0053fa50` `shell_window_bind_resource_and_initialize_child_control_links`
|
||||
- `0x00517570` `shell_video_window_destroy`
|
||||
-> `0x00484910` `shell_save_graphics_config`
|
||||
-> `0x005174e0` `shell_video_window_construct`
|
||||
- `0x00517d40` `indexed_collection_entry_id_is_live`
|
||||
-> `0x004ba3d0` `shell_building_detail_refresh_subject_cargo_and_service_rows`
|
||||
-> `0x00518140` `indexed_collection_resolve_live_entry_by_id`
|
||||
- `0x0051c920` `localization_lookup_display_label_by_stem_or_fallback`
|
||||
-> `0x004ba3d0` `shell_building_detail_refresh_subject_cargo_and_service_rows`
|
||||
- `0x0053f830` `shell_window_find_registered_child_control_by_id`
|
||||
-> `0x004e0ba0` `game_uppermost_window_handle_message`
|
||||
-> `0x00502220` `shell_setup_window_publish_selected_profile_labels_and_preview_surface`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
- `0x0053f9c0` `shell_window_register_child_control_sorted_by_priority_and_optional_tag`
|
||||
-> `0x004c4c70` `shell_company_detail_setup_dividend_rate_adjust_controls`
|
||||
-> `0x00558130` `shell_child_control_set_owner_resolve_caption_and_refresh`
|
||||
- `0x005411c0` `shell_query_tga_header_is_supported_truecolor_image`
|
||||
-> `0x005033d0` `shell_setup_window_handle_message`
|
||||
- `0x00558130` `shell_child_control_set_owner_resolve_caption_and_refresh`
|
||||
-> `0x0053f9c0` `shell_window_register_child_control_sorted_by_priority_and_optional_tag`
|
||||
1156
artifacts/exports/rt3-1.06/setup-window-submodes-depth5-subgraph.dot
Normal file
1156
artifacts/exports/rt3-1.06/setup-window-submodes-depth5-subgraph.dot
Normal file
File diff suppressed because it is too large
Load diff
1450
artifacts/exports/rt3-1.06/setup-window-submodes-depth5-subgraph.md
Normal file
1450
artifacts/exports/rt3-1.06/setup-window-submodes-depth5-subgraph.md
Normal file
File diff suppressed because it is too large
Load diff
141
artifacts/exports/rt3-1.06/shell-load-subgraph.dot
Normal file
141
artifacts/exports/rt3-1.06/shell-load-subgraph.dot
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
digraph shell_load {
|
||||
graph [rankdir=LR, labelloc="t", labeljust="l"];
|
||||
label="Shell Load Startup Subgraph";
|
||||
node [shape=box, style="rounded,filled", fillcolor="#f8f8f8", color="#555555", fontname="Helvetica"];
|
||||
edge [color="#666666", fontname="Helvetica"];
|
||||
subgraph cluster_bootstrap {
|
||||
label="bootstrap";
|
||||
color="#cccccc";
|
||||
"0x00482ec0" [label="0x00482ec0\\nshell_transition_mode [seed]", fillcolor="#ffe9a8"];
|
||||
"0x004840e0" [label="0x004840e0\\nbootstrap_init_shell_window_services", fillcolor="#f8f8f8"];
|
||||
"0x00521390" [label="0x00521390\\nbootstrap_destroy_shell_service_bundle", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_map {
|
||||
label="map";
|
||||
color="#cccccc";
|
||||
"0x004133b0" [label="0x004133b0\\nplaced_structure_collection_refresh_local_runtime_records_and_position_scalars", fillcolor="#f8f8f8"];
|
||||
"0x0041ea50" [label="0x0041ea50\\nworld_setup_building_collection_phase", fillcolor="#f8f8f8"];
|
||||
"0x00421b60" [label="0x00421b60\\nworld_region_collection_seed_default_regions", fillcolor="#f8f8f8"];
|
||||
"0x00421c20" [label="0x00421c20\\nworld_region_collection_run_building_population_pass", fillcolor="#f8f8f8"];
|
||||
"0x00434300" [label="0x00434300\\nworld_runtime_release_global_services", fillcolor="#f8f8f8"];
|
||||
"0x00437220" [label="0x00437220\\nworld_build_chairman_profile_slot_records", fillcolor="#f8f8f8"];
|
||||
"0x004377a0" [label="0x004377a0\\nworld_seed_default_chairman_profile_slots", fillcolor="#f8f8f8"];
|
||||
"0x004384d0" [label="0x004384d0\\nworld_run_post_load_generation_pipeline", fillcolor="#f8f8f8"];
|
||||
"0x00438890" [label="0x00438890\\nshell_active_mode_run_profile_startup_and_load_dispatch [seed]", fillcolor="#ffe9a8"];
|
||||
"0x00443a50" [label="0x00443a50\\nworld_entry_transition_and_runtime_bringup", fillcolor="#f8f8f8"];
|
||||
"0x00445ac0" [label="0x00445ac0\\nshell_map_file_entry_coordinator", fillcolor="#f8f8f8"];
|
||||
"0x00446d40" [label="0x00446d40\\nworld_load_saved_runtime_state_bundle", fillcolor="#f8f8f8"];
|
||||
"0x0044fb70" [label="0x0044fb70\\nworld_compute_transport_and_pricing_grid", fillcolor="#f8f8f8"];
|
||||
"0x0047d440" [label="0x0047d440\\nworld_conditionally_seed_named_starting_railroad_companies", fillcolor="#f8f8f8"];
|
||||
"0x004882e0" [label="0x004882e0\\nworld_region_border_overlay_rebuild", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_render {
|
||||
label="render";
|
||||
color="#cccccc";
|
||||
"0x0043f640" [label="0x0043f640\\nworld_render_station_candidate_service_map_overlay", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_shell {
|
||||
label="shell";
|
||||
color="#cccccc";
|
||||
"0x0046b780" [label="0x0046b780\\nmultiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync", fillcolor="#f8f8f8"];
|
||||
"0x00483f70" [label="0x00483f70\\nshell_service_pump_iteration", fillcolor="#f8f8f8"];
|
||||
"0x00484910" [label="0x00484910\\nshell_save_graphics_config", fillcolor="#f8f8f8"];
|
||||
"0x004b8dc0" [label="0x004b8dc0\\nshell_campaign_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004b8e60" [label="0x004b8e60\\nshell_campaign_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004c7bc0" [label="0x004c7bc0\\nshell_credits_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004c7fc0" [label="0x004c7fc0\\nshell_credits_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004dd010" [label="0x004dd010\\nshell_file_request_dialog_collect_target_path", fillcolor="#f8f8f8"];
|
||||
"0x004dfbe0" [label="0x004dfbe0\\nshell_game_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004dfd70" [label="0x004dfd70\\nshell_game_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004dfdf0" [label="0x004dfdf0\\nshell_ensure_game_message_window", fillcolor="#f8f8f8"];
|
||||
"0x004e3a80" [label="0x004e3a80\\nshell_load_screen_window_handle_message", fillcolor="#f8f8f8"];
|
||||
"0x004e5300" [label="0x004e5300\\nshell_load_screen_render_player_detail_stock_holdings_panel", fillcolor="#f8f8f8"];
|
||||
"0x004e5a80" [label="0x004e5a80\\nshell_render_company_overview_panel_header_and_optional_change_affordance", fillcolor="#f8f8f8"];
|
||||
"0x004e5cf0" [label="0x004e5cf0\\nshell_format_company_governance_and_economy_status_panel", fillcolor="#f8f8f8"];
|
||||
"0x004ea620" [label="0x004ea620\\nshell_load_screen_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x004ea720" [label="0x004ea720\\nshell_load_screen_window_is_open", fillcolor="#f8f8f8"];
|
||||
"0x004ea730" [label="0x004ea730\\nshell_load_screen_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x004ee3a0" [label="0x004ee3a0\\nmultiplayer_reset_tool_globals", fillcolor="#f8f8f8"];
|
||||
"0x004ee950" [label="0x004ee950\\nmultiplayer_load_selected_map_preview_surface", fillcolor="#f8f8f8"];
|
||||
"0x004efe80" [label="0x004efe80\\nmultiplayer_window_init_globals", fillcolor="#f8f8f8"];
|
||||
"0x00502720" [label="0x00502720\\npaint_terrain_tool_init_globals", fillcolor="#f8f8f8"];
|
||||
"0x00504010" [label="0x00504010\\nshell_setup_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x005174e0" [label="0x005174e0\\nshell_video_window_construct", fillcolor="#f8f8f8"];
|
||||
"0x00517570" [label="0x00517570\\nshell_video_window_destroy", fillcolor="#f8f8f8"];
|
||||
"0x005204b0" [label="0x005204b0\\nshell_flush_deferred_work_queues", fillcolor="#f8f8f8"];
|
||||
}
|
||||
subgraph cluster_simulation {
|
||||
label="simulation";
|
||||
color="#cccccc";
|
||||
"0x00437b20" [label="0x00437b20\\nsimulation_run_chunked_fast_forward_burst", fillcolor="#f8f8f8"];
|
||||
}
|
||||
"0x00421b60" -> "0x004384d0";
|
||||
"0x00434300" -> "0x00443a50";
|
||||
"0x00434300" -> "0x00446d40";
|
||||
"0x00434300" -> "0x00482ec0";
|
||||
"0x00437220" -> "0x004384d0";
|
||||
"0x00437b20" -> "0x004384d0";
|
||||
"0x004384d0" -> "0x004133b0";
|
||||
"0x004384d0" -> "0x0041ea50";
|
||||
"0x004384d0" -> "0x00421b60";
|
||||
"0x004384d0" -> "0x00421c20";
|
||||
"0x004384d0" -> "0x00437220";
|
||||
"0x004384d0" -> "0x004377a0";
|
||||
"0x004384d0" -> "0x00437b20";
|
||||
"0x004384d0" -> "0x0044fb70";
|
||||
"0x004384d0" -> "0x0047d440";
|
||||
"0x004384d0" -> "0x004882e0";
|
||||
"0x00438890" -> "0x004384d0";
|
||||
"0x00438890" -> "0x00445ac0";
|
||||
"0x0043f640" -> "0x0046b780";
|
||||
"0x00443a50" -> "0x00438890";
|
||||
"0x00443a50" -> "0x00482ec0";
|
||||
"0x00445ac0" -> "0x00443a50";
|
||||
"0x00445ac0" -> "0x00446d40";
|
||||
"0x00445ac0" -> "0x004dd010";
|
||||
"0x0046b780" -> "0x00438890";
|
||||
"0x0046b780" -> "0x00445ac0";
|
||||
"0x0047d440" -> "0x004377a0";
|
||||
"0x00482ec0" -> "0x00438890";
|
||||
"0x00482ec0" -> "0x00443a50";
|
||||
"0x00482ec0" -> "0x004840e0";
|
||||
"0x00482ec0" -> "0x004b8dc0";
|
||||
"0x00482ec0" -> "0x004b8e60";
|
||||
"0x00482ec0" -> "0x004c7bc0";
|
||||
"0x00482ec0" -> "0x004c7fc0";
|
||||
"0x00482ec0" -> "0x004dfbe0";
|
||||
"0x00482ec0" -> "0x004dfd70";
|
||||
"0x00482ec0" -> "0x004ea620";
|
||||
"0x00482ec0" -> "0x004ea730";
|
||||
"0x00482ec0" -> "0x004efe80";
|
||||
"0x00482ec0" -> "0x00504010";
|
||||
"0x00482ec0" -> "0x005174e0";
|
||||
"0x00482ec0" -> "0x00517570";
|
||||
"0x004840e0" -> "0x00482ec0";
|
||||
"0x004840e0" -> "0x00483f70";
|
||||
"0x004840e0" -> "0x00521390";
|
||||
"0x004882e0" -> "0x004384d0";
|
||||
"0x004b8dc0" -> "0x004b8e60";
|
||||
"0x004c7bc0" -> "0x004c7fc0";
|
||||
"0x004dfbe0" -> "0x004dfd70";
|
||||
"0x004dfbe0" -> "0x004dfdf0";
|
||||
"0x004e3a80" -> "0x004e5300";
|
||||
"0x004e3a80" -> "0x004e5a80";
|
||||
"0x004e3a80" -> "0x004e5cf0";
|
||||
"0x004e5a80" -> "0x004e5cf0";
|
||||
"0x004ea620" -> "0x004e3a80";
|
||||
"0x004ea620" -> "0x004e5300";
|
||||
"0x004ea620" -> "0x004e5a80";
|
||||
"0x004ea620" -> "0x004e5cf0";
|
||||
"0x004ea620" -> "0x004ea720";
|
||||
"0x004ea620" -> "0x004ea730";
|
||||
"0x004ea720" -> "0x004ea620";
|
||||
"0x004ea720" -> "0x004ea730";
|
||||
"0x004ea730" -> "0x004ea620";
|
||||
"0x004ee3a0" -> "0x00482ec0";
|
||||
"0x004ee950" -> "0x004efe80";
|
||||
"0x00502720" -> "0x004ee3a0";
|
||||
"0x00517570" -> "0x00484910";
|
||||
"0x00517570" -> "0x005174e0";
|
||||
"0x005204b0" -> "0x00443a50";
|
||||
}
|
||||
156
artifacts/exports/rt3-1.06/shell-load-subgraph.md
Normal file
156
artifacts/exports/rt3-1.06/shell-load-subgraph.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
# Shell Load Startup Subgraph
|
||||
|
||||
- Nodes: `46`
|
||||
- Edges: `69`
|
||||
- Seeds: `0x00438890`, `0x00482ec0`
|
||||
- Graphviz: `shell-load-subgraph.dot`
|
||||
|
||||
## Nodes
|
||||
|
||||
| Address | Name | Subsystem | Confidence |
|
||||
| --- | --- | --- | --- |
|
||||
| `0x004133b0` | `placed_structure_collection_refresh_local_runtime_records_and_position_scalars` | `map` | `2` |
|
||||
| `0x0041ea50` | `world_setup_building_collection_phase` | `map` | `3` |
|
||||
| `0x00421b60` | `world_region_collection_seed_default_regions` | `map` | `4` |
|
||||
| `0x00421c20` | `world_region_collection_run_building_population_pass` | `map` | `4` |
|
||||
| `0x00434300` | `world_runtime_release_global_services` | `map` | `3` |
|
||||
| `0x00437220` | `world_build_chairman_profile_slot_records` | `map` | `4` |
|
||||
| `0x004377a0` | `world_seed_default_chairman_profile_slots` | `map` | `4` |
|
||||
| `0x00437b20` | `simulation_run_chunked_fast_forward_burst` | `simulation` | `3` |
|
||||
| `0x004384d0` | `world_run_post_load_generation_pipeline` | `map` | `4` |
|
||||
| `0x00438890` | `shell_active_mode_run_profile_startup_and_load_dispatch` | `map` | `4` |
|
||||
| `0x0043f640` | `world_render_station_candidate_service_map_overlay` | `render` | `4` |
|
||||
| `0x00443a50` | `world_entry_transition_and_runtime_bringup` | `map` | `4` |
|
||||
| `0x00445ac0` | `shell_map_file_entry_coordinator` | `map` | `4` |
|
||||
| `0x00446d40` | `world_load_saved_runtime_state_bundle` | `map` | `4` |
|
||||
| `0x0044fb70` | `world_compute_transport_and_pricing_grid` | `map` | `3` |
|
||||
| `0x0046b780` | `multiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync` | `shell` | `4` |
|
||||
| `0x0047d440` | `world_conditionally_seed_named_starting_railroad_companies` | `map` | `4` |
|
||||
| `0x00482ec0` | `shell_transition_mode` | `bootstrap` | `4` |
|
||||
| `0x00483f70` | `shell_service_pump_iteration` | `shell` | `4` |
|
||||
| `0x004840e0` | `bootstrap_init_shell_window_services` | `bootstrap` | `4` |
|
||||
| `0x00484910` | `shell_save_graphics_config` | `shell` | `4` |
|
||||
| `0x004882e0` | `world_region_border_overlay_rebuild` | `map` | `4` |
|
||||
| `0x004b8dc0` | `shell_campaign_window_destroy` | `shell` | `4` |
|
||||
| `0x004b8e60` | `shell_campaign_window_construct` | `shell` | `4` |
|
||||
| `0x004c7bc0` | `shell_credits_window_destroy` | `shell` | `4` |
|
||||
| `0x004c7fc0` | `shell_credits_window_construct` | `shell` | `4` |
|
||||
| `0x004dd010` | `shell_file_request_dialog_collect_target_path` | `shell` | `4` |
|
||||
| `0x004dfbe0` | `shell_game_window_construct` | `shell` | `4` |
|
||||
| `0x004dfd70` | `shell_game_window_destroy` | `shell` | `4` |
|
||||
| `0x004dfdf0` | `shell_ensure_game_message_window` | `shell` | `4` |
|
||||
| `0x004e3a80` | `shell_load_screen_window_handle_message` | `shell` | `4` |
|
||||
| `0x004e5300` | `shell_load_screen_render_player_detail_stock_holdings_panel` | `shell` | `4` |
|
||||
| `0x004e5a80` | `shell_render_company_overview_panel_header_and_optional_change_affordance` | `shell` | `4` |
|
||||
| `0x004e5cf0` | `shell_format_company_governance_and_economy_status_panel` | `shell` | `4` |
|
||||
| `0x004ea620` | `shell_load_screen_window_construct` | `shell` | `4` |
|
||||
| `0x004ea720` | `shell_load_screen_window_is_open` | `shell` | `4` |
|
||||
| `0x004ea730` | `shell_load_screen_window_destroy` | `shell` | `4` |
|
||||
| `0x004ee3a0` | `multiplayer_reset_tool_globals` | `shell` | `3` |
|
||||
| `0x004ee950` | `multiplayer_load_selected_map_preview_surface` | `shell` | `4` |
|
||||
| `0x004efe80` | `multiplayer_window_init_globals` | `shell` | `4` |
|
||||
| `0x00502720` | `paint_terrain_tool_init_globals` | `shell` | `4` |
|
||||
| `0x00504010` | `shell_setup_window_construct` | `shell` | `4` |
|
||||
| `0x005174e0` | `shell_video_window_construct` | `shell` | `4` |
|
||||
| `0x00517570` | `shell_video_window_destroy` | `shell` | `4` |
|
||||
| `0x005204b0` | `shell_flush_deferred_work_queues` | `shell` | `4` |
|
||||
| `0x00521390` | `bootstrap_destroy_shell_service_bundle` | `bootstrap` | `4` |
|
||||
|
||||
## Edges
|
||||
|
||||
- `0x00421b60` `world_region_collection_seed_default_regions`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x00434300` `world_runtime_release_global_services`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x00446d40` `world_load_saved_runtime_state_bundle`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x00437220` `world_build_chairman_profile_slot_records`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x00437b20` `simulation_run_chunked_fast_forward_burst`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
-> `0x004133b0` `placed_structure_collection_refresh_local_runtime_records_and_position_scalars`
|
||||
-> `0x0041ea50` `world_setup_building_collection_phase`
|
||||
-> `0x00421b60` `world_region_collection_seed_default_regions`
|
||||
-> `0x00421c20` `world_region_collection_run_building_population_pass`
|
||||
-> `0x00437220` `world_build_chairman_profile_slot_records`
|
||||
-> `0x004377a0` `world_seed_default_chairman_profile_slots`
|
||||
-> `0x00437b20` `simulation_run_chunked_fast_forward_burst`
|
||||
-> `0x0044fb70` `world_compute_transport_and_pricing_grid`
|
||||
-> `0x0047d440` `world_conditionally_seed_named_starting_railroad_companies`
|
||||
-> `0x004882e0` `world_region_border_overlay_rebuild`
|
||||
- `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
-> `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
- `0x0043f640` `world_render_station_candidate_service_map_overlay`
|
||||
-> `0x0046b780` `multiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync`
|
||||
- `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x00446d40` `world_load_saved_runtime_state_bundle`
|
||||
-> `0x004dd010` `shell_file_request_dialog_collect_target_path`
|
||||
- `0x0046b780` `multiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00445ac0` `shell_map_file_entry_coordinator`
|
||||
- `0x0047d440` `world_conditionally_seed_named_starting_railroad_companies`
|
||||
-> `0x004377a0` `world_seed_default_chairman_profile_slots`
|
||||
- `0x00482ec0` `shell_transition_mode`
|
||||
-> `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
-> `0x004840e0` `bootstrap_init_shell_window_services`
|
||||
-> `0x004b8dc0` `shell_campaign_window_destroy`
|
||||
-> `0x004b8e60` `shell_campaign_window_construct`
|
||||
-> `0x004c7bc0` `shell_credits_window_destroy`
|
||||
-> `0x004c7fc0` `shell_credits_window_construct`
|
||||
-> `0x004dfbe0` `shell_game_window_construct`
|
||||
-> `0x004dfd70` `shell_game_window_destroy`
|
||||
-> `0x004ea620` `shell_load_screen_window_construct`
|
||||
-> `0x004ea730` `shell_load_screen_window_destroy`
|
||||
-> `0x004efe80` `multiplayer_window_init_globals`
|
||||
-> `0x00504010` `shell_setup_window_construct`
|
||||
-> `0x005174e0` `shell_video_window_construct`
|
||||
-> `0x00517570` `shell_video_window_destroy`
|
||||
- `0x004840e0` `bootstrap_init_shell_window_services`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
-> `0x00483f70` `shell_service_pump_iteration`
|
||||
-> `0x00521390` `bootstrap_destroy_shell_service_bundle`
|
||||
- `0x004882e0` `world_region_border_overlay_rebuild`
|
||||
-> `0x004384d0` `world_run_post_load_generation_pipeline`
|
||||
- `0x004b8dc0` `shell_campaign_window_destroy`
|
||||
-> `0x004b8e60` `shell_campaign_window_construct`
|
||||
- `0x004c7bc0` `shell_credits_window_destroy`
|
||||
-> `0x004c7fc0` `shell_credits_window_construct`
|
||||
- `0x004dfbe0` `shell_game_window_construct`
|
||||
-> `0x004dfd70` `shell_game_window_destroy`
|
||||
-> `0x004dfdf0` `shell_ensure_game_message_window`
|
||||
- `0x004e3a80` `shell_load_screen_window_handle_message`
|
||||
-> `0x004e5300` `shell_load_screen_render_player_detail_stock_holdings_panel`
|
||||
-> `0x004e5a80` `shell_render_company_overview_panel_header_and_optional_change_affordance`
|
||||
-> `0x004e5cf0` `shell_format_company_governance_and_economy_status_panel`
|
||||
- `0x004e5a80` `shell_render_company_overview_panel_header_and_optional_change_affordance`
|
||||
-> `0x004e5cf0` `shell_format_company_governance_and_economy_status_panel`
|
||||
- `0x004ea620` `shell_load_screen_window_construct`
|
||||
-> `0x004e3a80` `shell_load_screen_window_handle_message`
|
||||
-> `0x004e5300` `shell_load_screen_render_player_detail_stock_holdings_panel`
|
||||
-> `0x004e5a80` `shell_render_company_overview_panel_header_and_optional_change_affordance`
|
||||
-> `0x004e5cf0` `shell_format_company_governance_and_economy_status_panel`
|
||||
-> `0x004ea720` `shell_load_screen_window_is_open`
|
||||
-> `0x004ea730` `shell_load_screen_window_destroy`
|
||||
- `0x004ea720` `shell_load_screen_window_is_open`
|
||||
-> `0x004ea620` `shell_load_screen_window_construct`
|
||||
-> `0x004ea730` `shell_load_screen_window_destroy`
|
||||
- `0x004ea730` `shell_load_screen_window_destroy`
|
||||
-> `0x004ea620` `shell_load_screen_window_construct`
|
||||
- `0x004ee3a0` `multiplayer_reset_tool_globals`
|
||||
-> `0x00482ec0` `shell_transition_mode`
|
||||
- `0x004ee950` `multiplayer_load_selected_map_preview_surface`
|
||||
-> `0x004efe80` `multiplayer_window_init_globals`
|
||||
- `0x00502720` `paint_terrain_tool_init_globals`
|
||||
-> `0x004ee3a0` `multiplayer_reset_tool_globals`
|
||||
- `0x00517570` `shell_video_window_destroy`
|
||||
-> `0x00484910` `shell_save_graphics_config`
|
||||
-> `0x005174e0` `shell_video_window_construct`
|
||||
- `0x005204b0` `shell_flush_deferred_work_queues`
|
||||
-> `0x00443a50` `world_entry_transition_and_runtime_bringup`
|
||||
|
|
@ -6,4 +6,6 @@ license.workspace = true
|
|||
|
||||
[dependencies]
|
||||
rrt-model = { path = "../rrt-model" }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
sha2.workspace = true
|
||||
|
|
|
|||
|
|
@ -6,10 +6,34 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use rrt_model::{
|
||||
BINARY_SUMMARY_PATH, CANONICAL_EXE_PATH, CONTROL_LOOP_ATLAS_PATH, FUNCTION_MAP_PATH,
|
||||
REQUIRED_ATLAS_HEADINGS, REQUIRED_EXPORTS, load_binary_summary, load_function_map,
|
||||
REQUIRED_ATLAS_HEADINGS, REQUIRED_EXPORTS,
|
||||
finance::{FinanceOutcome, FinanceSnapshot},
|
||||
load_binary_summary, load_function_map,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
enum Command {
|
||||
Validate { repo_root: PathBuf },
|
||||
FinanceEval { snapshot_path: PathBuf },
|
||||
FinanceDiff { left_path: PathBuf, right_path: PathBuf },
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct FinanceDiffEntry {
|
||||
path: String,
|
||||
left: Value,
|
||||
right: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct FinanceDiffReport {
|
||||
matches: bool,
|
||||
difference_count: usize,
|
||||
differences: Vec<FinanceDiffEntry>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = real_main() {
|
||||
eprintln!("error: {err}");
|
||||
|
|
@ -18,22 +42,155 @@ fn main() {
|
|||
}
|
||||
|
||||
fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let repo_root = parse_repo_root()?;
|
||||
validate_required_files(&repo_root)?;
|
||||
validate_binary_summary(&repo_root)?;
|
||||
validate_function_map(&repo_root)?;
|
||||
validate_control_loop_atlas(&repo_root)?;
|
||||
println!("baseline validation passed");
|
||||
match parse_command()? {
|
||||
Command::Validate { repo_root } => {
|
||||
validate_required_files(&repo_root)?;
|
||||
validate_binary_summary(&repo_root)?;
|
||||
validate_function_map(&repo_root)?;
|
||||
validate_control_loop_atlas(&repo_root)?;
|
||||
println!("baseline validation passed");
|
||||
}
|
||||
Command::FinanceEval { snapshot_path } => {
|
||||
run_finance_eval(&snapshot_path)?;
|
||||
}
|
||||
Command::FinanceDiff {
|
||||
left_path,
|
||||
right_path,
|
||||
} => {
|
||||
run_finance_diff(&left_path, &right_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_repo_root() -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
|
||||
let mut args = env::args().skip(1);
|
||||
match (args.next().as_deref(), args.next(), args.next()) {
|
||||
(None, None, None) => Ok(env::current_dir()?),
|
||||
(Some("validate"), None, None) => Ok(env::current_dir()?),
|
||||
(Some("validate"), Some(path), None) => Ok(PathBuf::from(path)),
|
||||
_ => Err("usage: rrt-cli [validate [repo-root]]".into()),
|
||||
match (args.next().as_deref(), args.next(), args.next(), args.next()) {
|
||||
(None, None, None, None) => Ok(Command::Validate {
|
||||
repo_root: env::current_dir()?,
|
||||
}),
|
||||
(Some("validate"), None, None, None) => Ok(Command::Validate {
|
||||
repo_root: env::current_dir()?,
|
||||
}),
|
||||
(Some("validate"), Some(path), None, None) => Ok(Command::Validate {
|
||||
repo_root: PathBuf::from(path),
|
||||
}),
|
||||
(Some("finance"), Some(subcommand), Some(path), None) if subcommand == "eval" => {
|
||||
Ok(Command::FinanceEval {
|
||||
snapshot_path: PathBuf::from(path),
|
||||
})
|
||||
}
|
||||
(Some("finance"), Some(subcommand), Some(left), Some(right)) if subcommand == "diff" => {
|
||||
Ok(Command::FinanceDiff {
|
||||
left_path: PathBuf::from(left),
|
||||
right_path: PathBuf::from(right),
|
||||
})
|
||||
}
|
||||
_ => Err(
|
||||
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json>]"
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_finance_eval(snapshot_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let outcome = load_finance_outcome(snapshot_path)?;
|
||||
println!("{}", serde_json::to_string_pretty(&outcome)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_finance_diff(
|
||||
left_path: &Path,
|
||||
right_path: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let left = load_finance_outcome(left_path)?;
|
||||
let right = load_finance_outcome(right_path)?;
|
||||
let report = diff_finance_outcomes(&left, &right)?;
|
||||
println!("{}", serde_json::to_string_pretty(&report)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_finance_outcome(path: &Path) -> Result<FinanceOutcome, Box<dyn std::error::Error>> {
|
||||
let text = fs::read_to_string(path)?;
|
||||
if let Ok(snapshot) = serde_json::from_str::<FinanceSnapshot>(&text) {
|
||||
return Ok(snapshot.evaluate());
|
||||
}
|
||||
if let Ok(outcome) = serde_json::from_str::<FinanceOutcome>(&text) {
|
||||
return Ok(outcome);
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"unable to parse {} as FinanceSnapshot or FinanceOutcome",
|
||||
path.display()
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
||||
fn diff_finance_outcomes(
|
||||
left: &FinanceOutcome,
|
||||
right: &FinanceOutcome,
|
||||
) -> Result<FinanceDiffReport, Box<dyn std::error::Error>> {
|
||||
let left_value = serde_json::to_value(left)?;
|
||||
let right_value = serde_json::to_value(right)?;
|
||||
let mut differences = Vec::new();
|
||||
collect_json_differences("$", &left_value, &right_value, &mut differences);
|
||||
|
||||
Ok(FinanceDiffReport {
|
||||
matches: differences.is_empty(),
|
||||
difference_count: differences.len(),
|
||||
differences,
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_json_differences(
|
||||
path: &str,
|
||||
left: &Value,
|
||||
right: &Value,
|
||||
differences: &mut Vec<FinanceDiffEntry>,
|
||||
) {
|
||||
match (left, right) {
|
||||
(Value::Object(left_map), Value::Object(right_map)) => {
|
||||
let mut keys = BTreeSet::new();
|
||||
keys.extend(left_map.keys().cloned());
|
||||
keys.extend(right_map.keys().cloned());
|
||||
|
||||
for key in keys {
|
||||
let next_path = format!("{path}.{key}");
|
||||
match (left_map.get(&key), right_map.get(&key)) {
|
||||
(Some(left_value), Some(right_value)) => {
|
||||
collect_json_differences(&next_path, left_value, right_value, differences);
|
||||
}
|
||||
(left_value, right_value) => differences.push(FinanceDiffEntry {
|
||||
path: next_path,
|
||||
left: left_value.cloned().unwrap_or(Value::Null),
|
||||
right: right_value.cloned().unwrap_or(Value::Null),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
(Value::Array(left_items), Value::Array(right_items)) => {
|
||||
let max_len = left_items.len().max(right_items.len());
|
||||
for index in 0..max_len {
|
||||
let next_path = format!("{path}[{index}]");
|
||||
match (left_items.get(index), right_items.get(index)) {
|
||||
(Some(left_value), Some(right_value)) => {
|
||||
collect_json_differences(&next_path, left_value, right_value, differences);
|
||||
}
|
||||
(left_value, right_value) => differences.push(FinanceDiffEntry {
|
||||
path: next_path,
|
||||
left: left_value.cloned().unwrap_or(Value::Null),
|
||||
right: right_value.cloned().unwrap_or(Value::Null),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ if left != right => differences.push(FinanceDiffEntry {
|
||||
path: path.to_string(),
|
||||
left: left.clone(),
|
||||
right: right.clone(),
|
||||
}),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,3 +300,62 @@ fn sha256_file(path: &Path) -> Result<String, Box<dyn std::error::Error>> {
|
|||
|
||||
Ok(format!("{:x}", hasher.finalize()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rrt_model::finance::{AnnualFinanceDecision, AnnualFinanceEvaluation, CompanyFinanceState, DebtRestructureSummary};
|
||||
|
||||
#[test]
|
||||
fn loads_snapshot_as_outcome() {
|
||||
let snapshot = FinanceSnapshot {
|
||||
policy: rrt_model::finance::AnnualFinancePolicy {
|
||||
dividends_allowed: false,
|
||||
..rrt_model::finance::AnnualFinancePolicy::default()
|
||||
},
|
||||
company: CompanyFinanceState::default(),
|
||||
};
|
||||
let path = write_temp_json("snapshot", &snapshot);
|
||||
|
||||
let outcome = load_finance_outcome(&path).expect("snapshot should load");
|
||||
assert_eq!(outcome.evaluation.decision, AnnualFinanceDecision::NoAction);
|
||||
|
||||
let _ = fs::remove_file(path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diffs_outcomes_recursively() {
|
||||
let left = FinanceOutcome {
|
||||
evaluation: AnnualFinanceEvaluation::no_action(),
|
||||
post_company: CompanyFinanceState::default(),
|
||||
};
|
||||
let mut right = left.clone();
|
||||
right.post_company.current_cash = 123;
|
||||
right.evaluation.debt_restructure = DebtRestructureSummary {
|
||||
retired_principal: 10,
|
||||
issued_principal: 20,
|
||||
};
|
||||
|
||||
let report = diff_finance_outcomes(&left, &right).expect("diff should succeed");
|
||||
assert!(!report.matches);
|
||||
assert!(report
|
||||
.differences
|
||||
.iter()
|
||||
.any(|entry| entry.path == "$.post_company.current_cash"));
|
||||
assert!(report
|
||||
.differences
|
||||
.iter()
|
||||
.any(|entry| entry.path == "$.evaluation.debt_restructure.retired_principal"));
|
||||
}
|
||||
|
||||
fn write_temp_json<T: Serialize>(stem: &str, value: &T) -> PathBuf {
|
||||
let nonce = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.expect("system time should be after epoch")
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!("rrt-cli-{stem}-{nonce}.json"));
|
||||
let bytes = serde_json::to_vec_pretty(value).expect("json serialization should succeed");
|
||||
fs::write(&path, bytes).expect("temp json should be written");
|
||||
path
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,3 +7,8 @@ license.workspace = true
|
|||
[lib]
|
||||
name = "dinput8"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
rrt-model = { path = "../rrt-model" }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,10 +1,144 @@
|
|||
#![cfg_attr(not(windows), allow(dead_code))]
|
||||
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rrt_model::finance::{
|
||||
AnnualFinancePolicy, BondPosition, CompanyFinanceState, FinanceOutcome, FinanceSnapshot,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FinanceLogPaths {
|
||||
pub snapshot_path: PathBuf,
|
||||
pub outcome_path: PathBuf,
|
||||
}
|
||||
|
||||
pub fn sample_finance_snapshot() -> FinanceSnapshot {
|
||||
FinanceSnapshot {
|
||||
policy: AnnualFinancePolicy {
|
||||
dividends_allowed: false,
|
||||
..AnnualFinancePolicy::default()
|
||||
},
|
||||
company: CompanyFinanceState {
|
||||
current_cash: 100_000,
|
||||
support_adjusted_share_price: 27.5,
|
||||
book_value_per_share: 20.0,
|
||||
outstanding_share_count: 60_000,
|
||||
recent_net_profits: [40_000, 30_000, 20_000],
|
||||
recent_revenue_totals: [250_000, 240_000, 230_000],
|
||||
bonds: vec![
|
||||
BondPosition {
|
||||
principal: 150_000,
|
||||
coupon_rate: 0.12,
|
||||
years_remaining: 12,
|
||||
},
|
||||
BondPosition {
|
||||
principal: 10_000,
|
||||
coupon_rate: 0.10,
|
||||
years_remaining: 10,
|
||||
},
|
||||
],
|
||||
..CompanyFinanceState::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_finance_snapshot_bundle(
|
||||
base_dir: &Path,
|
||||
stem: &str,
|
||||
snapshot: &FinanceSnapshot,
|
||||
) -> io::Result<FinanceLogPaths> {
|
||||
fs::create_dir_all(base_dir)?;
|
||||
|
||||
let snapshot_path = base_dir.join(format!("rrt_finance_{stem}_snapshot.json"));
|
||||
let outcome_path = base_dir.join(format!("rrt_finance_{stem}_outcome.json"));
|
||||
let outcome: FinanceOutcome = snapshot.evaluate();
|
||||
|
||||
let snapshot_json = serde_json::to_vec_pretty(snapshot)
|
||||
.map_err(|err| io::Error::other(format!("serialize snapshot: {err}")))?;
|
||||
let outcome_json = serde_json::to_vec_pretty(&outcome)
|
||||
.map_err(|err| io::Error::other(format!("serialize outcome: {err}")))?;
|
||||
|
||||
fs::write(&snapshot_path, snapshot_json)?;
|
||||
fs::write(&outcome_path, outcome_json)?;
|
||||
|
||||
Ok(FinanceLogPaths {
|
||||
snapshot_path,
|
||||
outcome_path,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_finance_snapshot_only(
|
||||
base_dir: &Path,
|
||||
stem: &str,
|
||||
snapshot: &FinanceSnapshot,
|
||||
) -> io::Result<PathBuf> {
|
||||
fs::create_dir_all(base_dir)?;
|
||||
|
||||
let snapshot_path = base_dir.join(format!("rrt_finance_{stem}_snapshot.json"));
|
||||
let snapshot_json = serde_json::to_vec_pretty(snapshot)
|
||||
.map_err(|err| io::Error::other(format!("serialize snapshot: {err}")))?;
|
||||
fs::write(&snapshot_path, snapshot_json)?;
|
||||
|
||||
Ok(snapshot_path)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct IndexedCollectionProbeRow {
|
||||
pub entry_id: usize,
|
||||
pub live: bool,
|
||||
pub resolved_ptr: usize,
|
||||
pub active_flag: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct IndexedCollectionProbe {
|
||||
pub collection_addr: usize,
|
||||
pub flat_payload: bool,
|
||||
pub stride: u32,
|
||||
pub id_bound: i32,
|
||||
pub payload_ptr: usize,
|
||||
pub tombstone_ptr: usize,
|
||||
pub first_rows: Vec<IndexedCollectionProbeRow>,
|
||||
}
|
||||
|
||||
pub fn write_indexed_collection_probe(
|
||||
base_dir: &Path,
|
||||
stem: &str,
|
||||
probe: &IndexedCollectionProbe,
|
||||
) -> io::Result<PathBuf> {
|
||||
fs::create_dir_all(base_dir)?;
|
||||
|
||||
let path = base_dir.join(format!("rrt_finance_{stem}_collection_probe.json"));
|
||||
let json = serde_json::to_vec_pretty(probe)
|
||||
.map_err(|err| io::Error::other(format!("serialize collection probe: {err}")))?;
|
||||
fs::write(&path, json)?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows_hook {
|
||||
use super::{
|
||||
IndexedCollectionProbe, IndexedCollectionProbeRow, sample_finance_snapshot,
|
||||
write_finance_snapshot_bundle, write_finance_snapshot_only, write_indexed_collection_probe,
|
||||
};
|
||||
use core::ffi::{c_char, c_void};
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
use std::env;
|
||||
use std::fmt::Write as _;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use rrt_model::finance::{
|
||||
AnnualFinancePolicy, BondPosition, CompanyFinanceState, FinanceSnapshot, GrowthSetting,
|
||||
};
|
||||
|
||||
const DLL_PROCESS_ATTACH: u32 = 1;
|
||||
const E_FAIL: i32 = 0x8000_4005_u32 as i32;
|
||||
|
|
@ -17,10 +151,92 @@ mod windows_hook {
|
|||
|
||||
static LOG_PATH: &[u8] = b"rrt_hook_attach.log\0";
|
||||
static ATTACH_MESSAGE: &[u8] = b"rrt-hook: process attach\n";
|
||||
static FINANCE_CAPTURE_STARTED_MESSAGE: &[u8] = b"rrt-hook: finance capture thread started\n";
|
||||
static FINANCE_CAPTURE_SCAN_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance capture raw collection scan\n";
|
||||
static FINANCE_CAPTURE_PROBE_DUMP_WRITTEN_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance collection probe written\n";
|
||||
static FINANCE_CAPTURE_COMPANY_RESOLVED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance capture company resolved\n";
|
||||
static FINANCE_CAPTURE_PROBE_WRITTEN_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance probe snapshot written\n";
|
||||
static FINANCE_CAPTURE_TIMEOUT_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance capture timed out\n";
|
||||
static AUTO_LOAD_STARTED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load hook armed\n";
|
||||
static AUTO_LOAD_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell-pump hook installed\n";
|
||||
static AUTO_LOAD_READY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load ready gate passed\n";
|
||||
static AUTO_LOAD_DEFERRED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load restore deferred to later shell-pump turn\n";
|
||||
static AUTO_LOAD_CALLING_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load restore calling\n";
|
||||
static AUTO_LOAD_OWNER_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load larger owner entering\n";
|
||||
static AUTO_LOAD_OWNER_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load larger owner returned\n";
|
||||
static AUTO_LOAD_TRIGGERED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load restore invoked\n";
|
||||
static AUTO_LOAD_SUCCESS_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load request reported success\n";
|
||||
static AUTO_LOAD_FAILURE_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load request reported failure\n";
|
||||
static DEBUG_MESSAGE: &[u8] = b"rrt-hook: DllMain process attach\0";
|
||||
static DIRECT_INPUT8_CREATE_NAME: &[u8] = b"DirectInput8Create\0";
|
||||
static mut REAL_DINPUT8_CREATE: Option<DirectInput8CreateFn> = None;
|
||||
static FINANCE_TEMPLATE_EMITTED: AtomicBool = AtomicBool::new(false);
|
||||
static FINANCE_CAPTURE_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
static FINANCE_COLLECTION_PROBE_WRITTEN: AtomicBool = AtomicBool::new(false);
|
||||
static AUTO_LOAD_THREAD_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
static AUTO_LOAD_HOOK_INSTALLED: AtomicBool = AtomicBool::new(false);
|
||||
static AUTO_LOAD_ATTEMPTED: AtomicBool = AtomicBool::new(false);
|
||||
static AUTO_LOAD_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
|
||||
static AUTO_LOAD_DEFERRED: AtomicBool = AtomicBool::new(false);
|
||||
static AUTO_LOAD_LAST_GATE_MASK: AtomicU32 = AtomicU32::new(u32::MAX);
|
||||
static AUTO_LOAD_READY_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
static AUTO_LOAD_SAVE_STEM: OnceLock<String> = OnceLock::new();
|
||||
static mut SHELL_PUMP_TRAMPOLINE: usize = 0;
|
||||
|
||||
const COMPANY_COLLECTION_ADDR: usize = 0x0062be10;
|
||||
const SHELL_CONTROLLER_PTR_ADDR: usize = 0x006d4024;
|
||||
const SHELL_STATE_PTR_ADDR: usize = 0x006cec74;
|
||||
const ACTIVE_MODE_PTR_ADDR: usize = 0x006cec78;
|
||||
const SHELL_PUMP_ADDR: usize = 0x00483f70;
|
||||
const SHELL_STATE_ACTIVE_MODE_OFFSET: usize = 0x08;
|
||||
const SHELL_STATE_ACTIVE_MODE_OBJECT_OFFSET: usize = 0x0c;
|
||||
const RUNTIME_PROFILE_PTR_ADDR: usize = 0x006cec7c;
|
||||
const RUNTIME_PROFILE_MANUAL_LOAD_PATH_OFFSET: usize = 0x11;
|
||||
const RUNTIME_PROFILE_PENDING_LOAD_BYTE_OFFSET: usize = 0x97;
|
||||
const INDEXED_COLLECTION_FLAT_FLAG_OFFSET: usize = 0x04;
|
||||
const INDEXED_COLLECTION_STRIDE_OFFSET: usize = 0x08;
|
||||
const INDEXED_COLLECTION_ID_BOUND_OFFSET: usize = 0x14;
|
||||
const INDEXED_COLLECTION_PAYLOAD_OFFSET: usize = 0x30;
|
||||
const INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET: usize = 0x34;
|
||||
const COMPANY_ACTIVE_OFFSET: usize = 0x3f;
|
||||
const COMPANY_OUTSTANDING_SHARES_OFFSET: usize = 0x47;
|
||||
const COMPANY_COMPANY_VALUE_OFFSET: usize = 0x57;
|
||||
const COMPANY_BOND_COUNT_OFFSET: usize = 0x5b;
|
||||
const COMPANY_BOND_TABLE_OFFSET: usize = 0x5f;
|
||||
const COMPANY_FOUNDING_YEAR_OFFSET: usize = 0x157;
|
||||
const COMPANY_LAST_BANKRUPTCY_YEAR_OFFSET: usize = 0x163;
|
||||
const COMPANY_CITY_CONNECTION_LATCH_OFFSET: usize = 0x0d18;
|
||||
const COMPANY_LINKED_TRANSIT_LATCH_OFFSET: usize = 0x0d56;
|
||||
|
||||
const SCENARIO_CURRENT_YEAR_OFFSET: usize = 0x0d;
|
||||
const SCENARIO_BUILDING_DENSITY_GROWTH_OFFSET: usize = 0x4c7c;
|
||||
const SCENARIO_BANKRUPTCY_TOGGLE_OFFSET: usize = 0x4a8f;
|
||||
const SCENARIO_BOND_TOGGLE_OFFSET: usize = 0x4a8b;
|
||||
const SCENARIO_STOCK_TOGGLE_OFFSET: usize = 0x4a87;
|
||||
const SCENARIO_DIVIDEND_TOGGLE_OFFSET: usize = 0x4a93;
|
||||
|
||||
const MAX_CAPTURE_POLL_ATTEMPTS: usize = 120;
|
||||
const CAPTURE_POLL_INTERVAL: Duration = Duration::from_secs(1);
|
||||
const AUTO_LOAD_READY_POLLS: u32 = 30;
|
||||
const AUTO_LOAD_DEFER_POLLS: u32 = 5;
|
||||
const MEM_COMMIT: u32 = 0x1000;
|
||||
const MEM_RESERVE: u32 = 0x2000;
|
||||
const PAGE_EXECUTE_READWRITE: u32 = 0x40;
|
||||
unsafe extern "system" {
|
||||
fn CreateFileA(
|
||||
lp_file_name: *const c_char,
|
||||
|
|
@ -46,10 +262,28 @@ mod windows_hook {
|
|||
) -> i32;
|
||||
fn CloseHandle(handle: isize) -> i32;
|
||||
fn DisableThreadLibraryCalls(module: *mut c_void) -> i32;
|
||||
fn FlushInstructionCache(
|
||||
process: *mut c_void,
|
||||
base_address: *const c_void,
|
||||
size: usize,
|
||||
) -> i32;
|
||||
fn GetCurrentProcess() -> *mut c_void;
|
||||
fn GetSystemDirectoryA(buffer: *mut u8, size: u32) -> u32;
|
||||
fn GetProcAddress(module: isize, name: *const c_char) -> *mut c_void;
|
||||
fn LoadLibraryA(name: *const c_char) -> isize;
|
||||
fn OutputDebugStringA(output: *const c_char);
|
||||
fn VirtualAlloc(
|
||||
address: *mut c_void,
|
||||
size: usize,
|
||||
allocation_type: u32,
|
||||
protect: u32,
|
||||
) -> *mut c_void;
|
||||
fn VirtualProtect(
|
||||
address: *mut c_void,
|
||||
size: usize,
|
||||
new_protect: u32,
|
||||
old_protect: *mut u32,
|
||||
) -> i32;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
|
@ -67,7 +301,8 @@ mod windows_hook {
|
|||
out: *mut *mut c_void,
|
||||
outer: *mut c_void,
|
||||
) -> i32;
|
||||
|
||||
type ShellPumpFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
type LargerManualLoadOwnerFn = unsafe extern "thiscall" fn(*mut u8, u32, u32);
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn DllMain(
|
||||
module: *mut c_void,
|
||||
|
|
@ -92,6 +327,10 @@ mod windows_hook {
|
|||
out: *mut *mut c_void,
|
||||
outer: *mut c_void,
|
||||
) -> i32 {
|
||||
maybe_emit_finance_template_bundle();
|
||||
maybe_start_finance_capture_thread();
|
||||
maybe_install_auto_load_hook();
|
||||
|
||||
let direct_input8_create = unsafe { load_direct_input8_create() };
|
||||
match direct_input8_create {
|
||||
Some(callback) => unsafe { callback(instance, version, riid, out, outer) },
|
||||
|
|
@ -100,6 +339,10 @@ mod windows_hook {
|
|||
}
|
||||
|
||||
unsafe fn append_attach_log() {
|
||||
append_log_message(ATTACH_MESSAGE);
|
||||
}
|
||||
|
||||
fn append_log_message(message: &[u8]) {
|
||||
let handle = unsafe {
|
||||
CreateFileA(
|
||||
LOG_PATH.as_ptr().cast(),
|
||||
|
|
@ -120,8 +363,8 @@ mod windows_hook {
|
|||
let _ = unsafe {
|
||||
WriteFile(
|
||||
handle,
|
||||
ATTACH_MESSAGE.as_ptr().cast(),
|
||||
ATTACH_MESSAGE.len() as u32,
|
||||
message.as_ptr().cast(),
|
||||
message.len() as u32,
|
||||
&mut bytes_written,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
|
|
@ -129,6 +372,613 @@ mod windows_hook {
|
|||
let _ = unsafe { CloseHandle(handle) };
|
||||
}
|
||||
|
||||
fn append_log_line(line: &str) {
|
||||
append_log_message(line.as_bytes());
|
||||
}
|
||||
|
||||
fn maybe_emit_finance_template_bundle() {
|
||||
if env::var_os("RRT_WRITE_FINANCE_TEMPLATE").is_none() {
|
||||
return;
|
||||
}
|
||||
if FINANCE_TEMPLATE_EMITTED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
let base_dir = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let _ = write_finance_snapshot_bundle(
|
||||
&base_dir,
|
||||
"attach_template",
|
||||
&sample_finance_snapshot(),
|
||||
);
|
||||
}
|
||||
|
||||
fn maybe_start_finance_capture_thread() {
|
||||
if env::var_os("RRT_WRITE_FINANCE_CAPTURE").is_none() {
|
||||
return;
|
||||
}
|
||||
if FINANCE_CAPTURE_STARTED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
append_log_message(FINANCE_CAPTURE_STARTED_MESSAGE);
|
||||
let base_dir = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let _ = thread::Builder::new()
|
||||
.name("rrt-finance-capture".to_string())
|
||||
.spawn(move || {
|
||||
for _ in 0..MAX_CAPTURE_POLL_ATTEMPTS {
|
||||
if !FINANCE_COLLECTION_PROBE_WRITTEN.load(Ordering::Acquire) {
|
||||
if let Some(probe) = unsafe { capture_company_collection_probe() } {
|
||||
if write_indexed_collection_probe(&base_dir, "attach_probe", &probe)
|
||||
.is_ok()
|
||||
{
|
||||
FINANCE_COLLECTION_PROBE_WRITTEN.store(true, Ordering::Release);
|
||||
append_log_message(FINANCE_CAPTURE_PROBE_DUMP_WRITTEN_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(snapshot) = unsafe { try_capture_probe_snapshot() } {
|
||||
append_log_message(FINANCE_CAPTURE_COMPANY_RESOLVED_MESSAGE);
|
||||
if write_finance_snapshot_only(&base_dir, "attach_probe", &snapshot)
|
||||
.is_ok()
|
||||
{
|
||||
append_log_message(FINANCE_CAPTURE_PROBE_WRITTEN_MESSAGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
thread::sleep(CAPTURE_POLL_INTERVAL);
|
||||
}
|
||||
|
||||
append_log_message(FINANCE_CAPTURE_TIMEOUT_MESSAGE);
|
||||
});
|
||||
}
|
||||
|
||||
fn maybe_install_auto_load_hook() {
|
||||
let save_stem = match env::var("RRT_AUTO_LOAD_SAVE") {
|
||||
Ok(value) if !value.trim().is_empty() => value,
|
||||
_ => return,
|
||||
};
|
||||
let _ = AUTO_LOAD_SAVE_STEM.set(save_stem);
|
||||
if AUTO_LOAD_HOOK_INSTALLED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
append_log_message(AUTO_LOAD_STARTED_MESSAGE);
|
||||
AUTO_LOAD_THREAD_STARTED.store(true, Ordering::Release);
|
||||
if unsafe { install_shell_pump_hook() } {
|
||||
append_log_message(AUTO_LOAD_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_auto_load_worker(save_stem: &str) {
|
||||
append_log_message(AUTO_LOAD_CALLING_MESSAGE);
|
||||
let staged = unsafe { invoke_manual_load_branch(save_stem) };
|
||||
if staged {
|
||||
append_log_message(AUTO_LOAD_TRIGGERED_MESSAGE);
|
||||
append_log_message(AUTO_LOAD_SUCCESS_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
AUTO_LOAD_IN_PROGRESS.store(false, Ordering::Release);
|
||||
}
|
||||
|
||||
unsafe fn invoke_manual_load_branch(save_stem: &str) -> bool {
|
||||
if save_stem.is_empty() || save_stem.as_bytes().contains(&0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
let runtime_profile = unsafe { read_ptr(RUNTIME_PROFILE_PTR_ADDR as *const u8) };
|
||||
let active_mode = unsafe { resolve_active_mode_ptr() };
|
||||
if shell_state.is_null() || runtime_profile.is_null() || active_mode.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let path_seed = unsafe { runtime_profile.add(RUNTIME_PROFILE_MANUAL_LOAD_PATH_OFFSET) };
|
||||
if unsafe { write_c_string(path_seed, 260, save_stem.as_bytes()) }.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
ptr::write_unaligned(
|
||||
runtime_profile
|
||||
.add(RUNTIME_PROFILE_PENDING_LOAD_BYTE_OFFSET)
|
||||
.cast::<u8>(),
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
let larger_owner: LargerManualLoadOwnerFn =
|
||||
unsafe { mem::transmute(0x00438890usize) };
|
||||
|
||||
let global_active_mode = unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) };
|
||||
|
||||
if global_active_mode.is_null() {
|
||||
unsafe {
|
||||
ptr::write_unaligned(
|
||||
(ACTIVE_MODE_PTR_ADDR as *mut u8).cast::<usize>(),
|
||||
active_mode as usize,
|
||||
)
|
||||
};
|
||||
}
|
||||
append_log_message(AUTO_LOAD_OWNER_ENTRY_MESSAGE);
|
||||
unsafe { larger_owner(active_mode, 1, 0) };
|
||||
append_log_message(AUTO_LOAD_OWNER_RETURNED_MESSAGE);
|
||||
if global_active_mode.is_null() {
|
||||
unsafe {
|
||||
ptr::write_unaligned((ACTIVE_MODE_PTR_ADDR as *mut u8).cast::<usize>(), 0)
|
||||
};
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn write_c_string(
|
||||
destination: *mut u8,
|
||||
capacity: usize,
|
||||
bytes: &[u8],
|
||||
) -> Option<()> {
|
||||
if bytes.len() + 1 > capacity {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe { ptr::write_bytes(destination, 0, capacity) };
|
||||
unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), destination, bytes.len()) };
|
||||
Some(())
|
||||
}
|
||||
|
||||
unsafe fn try_capture_probe_snapshot() -> Option<FinanceSnapshot> {
|
||||
append_log_message(FINANCE_CAPTURE_SCAN_MESSAGE);
|
||||
let company = unsafe { resolve_first_active_company()? };
|
||||
Some(unsafe { capture_probe_snapshot_from_company(company) })
|
||||
}
|
||||
|
||||
unsafe fn runtime_saved_world_restore_gate_mask() -> u32 {
|
||||
let mut mask = 0_u32;
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
if !shell_state.is_null() {
|
||||
mask |= 0x1;
|
||||
}
|
||||
let shell_controller = unsafe { read_ptr(SHELL_CONTROLLER_PTR_ADDR as *const u8) };
|
||||
if !shell_controller.is_null() {
|
||||
mask |= 0x2;
|
||||
}
|
||||
let active_mode = unsafe { resolve_active_mode_ptr() };
|
||||
if !active_mode.is_null() {
|
||||
mask |= 0x4;
|
||||
}
|
||||
mask
|
||||
}
|
||||
|
||||
unsafe fn current_mode_id() -> u32 {
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
if shell_state.is_null() {
|
||||
return 0;
|
||||
}
|
||||
unsafe { read_u32(shell_state.add(SHELL_STATE_ACTIVE_MODE_OFFSET)) }
|
||||
}
|
||||
|
||||
fn auto_load_ready_polls() -> u32 {
|
||||
env::var("RRT_AUTO_LOAD_READY_POLLS")
|
||||
.ok()
|
||||
.and_then(|value| value.parse::<u32>().ok())
|
||||
.filter(|value| *value > 0)
|
||||
.unwrap_or(AUTO_LOAD_READY_POLLS)
|
||||
}
|
||||
|
||||
fn auto_load_defer_polls() -> u32 {
|
||||
env::var("RRT_AUTO_LOAD_DEFER_POLLS")
|
||||
.ok()
|
||||
.and_then(|value| value.parse::<u32>().ok())
|
||||
.unwrap_or(AUTO_LOAD_DEFER_POLLS)
|
||||
}
|
||||
|
||||
unsafe extern "fastcall" fn shell_pump_detour(this: *mut u8, _edx: usize) -> i32 {
|
||||
let trampoline: ShellPumpFn = unsafe { mem::transmute(SHELL_PUMP_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
maybe_service_auto_load_on_main_thread();
|
||||
result
|
||||
}
|
||||
|
||||
fn maybe_service_auto_load_on_main_thread() {
|
||||
if !AUTO_LOAD_HOOK_INSTALLED.load(Ordering::Acquire)
|
||||
|| AUTO_LOAD_ATTEMPTED.load(Ordering::Acquire)
|
||||
|| AUTO_LOAD_IN_PROGRESS.load(Ordering::Acquire)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let gate_mask = unsafe { runtime_saved_world_restore_gate_mask() };
|
||||
let last_gate_mask = AUTO_LOAD_LAST_GATE_MASK.swap(gate_mask, Ordering::AcqRel);
|
||||
if gate_mask != last_gate_mask {
|
||||
log_auto_load_gate_mask(gate_mask);
|
||||
}
|
||||
|
||||
let mode_id = unsafe { current_mode_id() };
|
||||
let ready = gate_mask == 0x7 && mode_id == 2;
|
||||
let ready_count = if ready {
|
||||
AUTO_LOAD_READY_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
AUTO_LOAD_READY_COUNT.store(0, Ordering::Release);
|
||||
AUTO_LOAD_DEFERRED.store(false, Ordering::Release);
|
||||
0
|
||||
};
|
||||
|
||||
let ready_polls = auto_load_ready_polls();
|
||||
if ready_count < ready_polls {
|
||||
return;
|
||||
}
|
||||
|
||||
if !AUTO_LOAD_DEFERRED.load(Ordering::Acquire) {
|
||||
AUTO_LOAD_DEFERRED.store(true, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_READY_MESSAGE);
|
||||
append_log_message(AUTO_LOAD_DEFERRED_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ready_count < ready_polls.saturating_add(auto_load_defer_polls()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if AUTO_LOAD_ATTEMPTED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(save_stem) = AUTO_LOAD_SAVE_STEM.get() else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
return;
|
||||
};
|
||||
|
||||
AUTO_LOAD_IN_PROGRESS.store(true, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_READY_MESSAGE);
|
||||
run_auto_load_worker(save_stem);
|
||||
}
|
||||
|
||||
fn log_auto_load_gate_mask(mask: u32) {
|
||||
let mut line = String::from("rrt-hook: auto load gate mask ");
|
||||
let global_active_mode = unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) } as usize;
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
let mode_id = if shell_state.is_null() {
|
||||
0
|
||||
} else {
|
||||
unsafe { read_u32(shell_state.add(SHELL_STATE_ACTIVE_MODE_OFFSET)) as usize }
|
||||
};
|
||||
let field_active_mode_object = if shell_state.is_null() {
|
||||
0
|
||||
} else {
|
||||
unsafe { read_ptr(shell_state.add(SHELL_STATE_ACTIVE_MODE_OBJECT_OFFSET)) as usize }
|
||||
};
|
||||
let _ = write!(
|
||||
&mut line,
|
||||
"0x{mask:01x} shell_state={} shell_controller={} active_mode={} global_active_mode=0x{global_active_mode:08x} mode_id=0x{mode_id:08x} field_active_mode_object=0x{field_active_mode_object:08x}\n",
|
||||
(mask & 0x1) != 0,
|
||||
(mask & 0x2) != 0,
|
||||
(mask & 0x4) != 0,
|
||||
);
|
||||
append_log_line(&line);
|
||||
}
|
||||
|
||||
unsafe fn resolve_active_mode_ptr() -> *mut u8 {
|
||||
let global_active_mode = unsafe { resolve_global_active_mode_ptr() };
|
||||
if !global_active_mode.is_null() {
|
||||
return global_active_mode;
|
||||
}
|
||||
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
if shell_state.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
unsafe { read_ptr(shell_state.add(SHELL_STATE_ACTIVE_MODE_OBJECT_OFFSET)) }
|
||||
}
|
||||
|
||||
unsafe fn resolve_global_active_mode_ptr() -> *mut u8 {
|
||||
unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) }
|
||||
}
|
||||
|
||||
unsafe fn install_shell_pump_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 8;
|
||||
let target = SHELL_PUMP_ADDR as *mut u8;
|
||||
let trampoline_size = STOLEN_LEN + 5;
|
||||
let trampoline = unsafe {
|
||||
VirtualAlloc(
|
||||
ptr::null_mut(),
|
||||
trampoline_size,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
)
|
||||
} as *mut u8;
|
||||
if trampoline.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe { ptr::copy_nonoverlapping(target, trampoline, STOLEN_LEN) };
|
||||
unsafe {
|
||||
write_rel32_jump(
|
||||
trampoline.add(STOLEN_LEN),
|
||||
target.add(STOLEN_LEN) as usize,
|
||||
)
|
||||
};
|
||||
|
||||
let mut old_protect = 0_u32;
|
||||
if unsafe {
|
||||
VirtualProtect(
|
||||
target.cast(),
|
||||
STOLEN_LEN,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&mut old_protect,
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe { write_rel32_jump(target, shell_pump_detour as *const () as usize) };
|
||||
unsafe { ptr::write(target.add(5), 0x90) };
|
||||
unsafe { ptr::write(target.add(6), 0x90) };
|
||||
unsafe { ptr::write(target.add(7), 0x90) };
|
||||
let mut restore_protect = 0_u32;
|
||||
let _ = unsafe {
|
||||
VirtualProtect(
|
||||
target.cast(),
|
||||
STOLEN_LEN,
|
||||
old_protect,
|
||||
&mut restore_protect,
|
||||
)
|
||||
};
|
||||
let _ = unsafe {
|
||||
FlushInstructionCache(
|
||||
GetCurrentProcess(),
|
||||
target.cast(),
|
||||
STOLEN_LEN,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
SHELL_PUMP_TRAMPOLINE = trampoline as usize;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn write_rel32_jump(location: *mut u8, destination: usize) {
|
||||
unsafe { ptr::write(location, 0xE9) };
|
||||
let next_ip = unsafe { location.add(5) } as usize;
|
||||
let relative = (destination as isize - next_ip as isize) as i32;
|
||||
unsafe { ptr::write_unaligned(location.add(1).cast::<i32>(), relative) };
|
||||
}
|
||||
|
||||
unsafe fn resolve_first_active_company() -> Option<*mut u8> {
|
||||
let collection = COMPANY_COLLECTION_ADDR as *const u8;
|
||||
let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
|
||||
if id_bound <= 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
for entry_id in 1..=id_bound as usize {
|
||||
if unsafe { indexed_collection_entry_id_is_live(collection, entry_id) } {
|
||||
let company = unsafe { indexed_collection_resolve_live_entry_by_id(collection, entry_id) };
|
||||
if !company.is_null() && unsafe { read_u8(company.add(COMPANY_ACTIVE_OFFSET)) != 0 } {
|
||||
return Some(company);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
unsafe fn capture_company_collection_probe() -> Option<IndexedCollectionProbe> {
|
||||
let collection = COMPANY_COLLECTION_ADDR as *const u8;
|
||||
let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
|
||||
if id_bound <= 0 {
|
||||
return Some(IndexedCollectionProbe {
|
||||
collection_addr: COMPANY_COLLECTION_ADDR,
|
||||
flat_payload: unsafe {
|
||||
read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0
|
||||
},
|
||||
stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) },
|
||||
id_bound,
|
||||
payload_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize
|
||||
},
|
||||
tombstone_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize
|
||||
},
|
||||
first_rows: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut first_rows = Vec::new();
|
||||
let sample_bound = (id_bound as usize).min(8);
|
||||
for entry_id in 1..=sample_bound {
|
||||
let live = unsafe { indexed_collection_entry_id_is_live(collection, entry_id) };
|
||||
let resolved_ptr = unsafe {
|
||||
indexed_collection_resolve_live_entry_by_id(collection, entry_id) as usize
|
||||
};
|
||||
let active_flag = if resolved_ptr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { read_u8((resolved_ptr as *const u8).add(COMPANY_ACTIVE_OFFSET)) })
|
||||
};
|
||||
first_rows.push(IndexedCollectionProbeRow {
|
||||
entry_id,
|
||||
live,
|
||||
resolved_ptr,
|
||||
active_flag,
|
||||
});
|
||||
}
|
||||
|
||||
Some(IndexedCollectionProbe {
|
||||
collection_addr: COMPANY_COLLECTION_ADDR,
|
||||
flat_payload: unsafe {
|
||||
read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0
|
||||
},
|
||||
stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) },
|
||||
id_bound,
|
||||
payload_ptr: unsafe { read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize },
|
||||
tombstone_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize
|
||||
},
|
||||
first_rows,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn capture_probe_snapshot_from_company(company: *mut u8) -> FinanceSnapshot {
|
||||
let scenario = unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) } as *const u8;
|
||||
let current_year = unsafe { read_u16(scenario.add(SCENARIO_CURRENT_YEAR_OFFSET)) };
|
||||
let founding_year = unsafe { read_u16(company.add(COMPANY_FOUNDING_YEAR_OFFSET)) };
|
||||
let last_bankruptcy_year =
|
||||
unsafe { read_u16(company.add(COMPANY_LAST_BANKRUPTCY_YEAR_OFFSET)) };
|
||||
let outstanding_share_count =
|
||||
unsafe { read_u32(company.add(COMPANY_OUTSTANDING_SHARES_OFFSET)) };
|
||||
let bonds = unsafe { capture_bonds(company, current_year) };
|
||||
let company_value = unsafe { read_u32(company.add(COMPANY_COMPANY_VALUE_OFFSET)) as i64 };
|
||||
let growth_setting = unsafe {
|
||||
growth_setting_from_raw(read_u8(
|
||||
scenario.add(SCENARIO_BUILDING_DENSITY_GROWTH_OFFSET),
|
||||
))
|
||||
};
|
||||
|
||||
FinanceSnapshot {
|
||||
policy: AnnualFinancePolicy {
|
||||
annual_mode: 0x0c,
|
||||
bankruptcy_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_BANKRUPTCY_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
bond_issuance_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_BOND_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
stock_actions_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_STOCK_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
dividends_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_DIVIDEND_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
growth_setting,
|
||||
..AnnualFinancePolicy::default()
|
||||
},
|
||||
company: CompanyFinanceState {
|
||||
active: unsafe { read_u8(company.add(COMPANY_ACTIVE_OFFSET)) != 0 },
|
||||
years_since_founding: year_delta(current_year, founding_year),
|
||||
years_since_last_bankruptcy: year_delta(current_year, last_bankruptcy_year),
|
||||
current_company_value: company_value,
|
||||
outstanding_share_count,
|
||||
city_connection_bonus_latch: unsafe {
|
||||
read_u8(company.add(COMPANY_CITY_CONNECTION_LATCH_OFFSET)) != 0
|
||||
},
|
||||
linked_transit_service_latch: unsafe {
|
||||
read_u8(company.add(COMPANY_LINKED_TRANSIT_LATCH_OFFSET)) != 0
|
||||
},
|
||||
chairman_buyback_factor: None,
|
||||
bonds,
|
||||
..CompanyFinanceState::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn capture_bonds(company: *mut u8, current_year: u16) -> Vec<BondPosition> {
|
||||
let bond_count = unsafe { read_u8(company.add(COMPANY_BOND_COUNT_OFFSET)) as usize };
|
||||
let table = unsafe { company.add(COMPANY_BOND_TABLE_OFFSET) };
|
||||
let mut bonds = Vec::with_capacity(bond_count);
|
||||
|
||||
for index in 0..bond_count {
|
||||
let slot = unsafe { table.add(index * 12) };
|
||||
let principal = unsafe { read_i32(slot) } as i64;
|
||||
let maturity_year = unsafe { read_u32(slot.add(4)) };
|
||||
let coupon_rate = unsafe { read_f32(slot.add(8)) } as f64;
|
||||
|
||||
bonds.push(BondPosition {
|
||||
principal,
|
||||
coupon_rate,
|
||||
years_remaining: maturity_year
|
||||
.saturating_sub(current_year as u32)
|
||||
.min(u8::MAX as u32) as u8,
|
||||
});
|
||||
}
|
||||
|
||||
bonds
|
||||
}
|
||||
|
||||
fn growth_setting_from_raw(raw: u8) -> GrowthSetting {
|
||||
match raw {
|
||||
1 => GrowthSetting::ExpansionBias,
|
||||
2 => GrowthSetting::DividendSuppressed,
|
||||
_ => GrowthSetting::Neutral,
|
||||
}
|
||||
}
|
||||
|
||||
fn year_delta(current_year: u16, past_year: u16) -> u8 {
|
||||
current_year
|
||||
.saturating_sub(past_year)
|
||||
.min(u8::MAX as u16) as u8
|
||||
}
|
||||
|
||||
unsafe fn indexed_collection_entry_id_is_live(collection: *const u8, entry_id: usize) -> bool {
|
||||
let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
|
||||
if entry_id == 0 || entry_id > id_bound.max(0) as usize {
|
||||
return false;
|
||||
}
|
||||
|
||||
let tombstone_bits = unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET))
|
||||
};
|
||||
if tombstone_bits.is_null() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let bit_index = entry_id as u32;
|
||||
let word = unsafe {
|
||||
ptr::read_unaligned(tombstone_bits.add((bit_index / 32) as usize).cast::<u32>())
|
||||
};
|
||||
(word & (1_u32 << (bit_index % 32))) == 0
|
||||
}
|
||||
|
||||
unsafe fn indexed_collection_resolve_live_entry_by_id(
|
||||
collection: *const u8,
|
||||
entry_id: usize,
|
||||
) -> *mut u8 {
|
||||
if !unsafe { indexed_collection_entry_id_is_live(collection, entry_id) } {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let payload = unsafe { read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) };
|
||||
if payload.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let stride = unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) as usize };
|
||||
let flat = unsafe { read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0 };
|
||||
|
||||
if flat {
|
||||
unsafe { payload.add(stride * entry_id) }
|
||||
} else {
|
||||
unsafe { ptr::read_unaligned(payload.add(stride * entry_id).cast::<*mut u8>()) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn read_u8(address: *const u8) -> u8 {
|
||||
unsafe { ptr::read_unaligned(address) }
|
||||
}
|
||||
|
||||
unsafe fn read_u16(address: *const u8) -> u16 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<u16>()) }
|
||||
}
|
||||
|
||||
unsafe fn read_u32(address: *const u8) -> u32 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<u32>()) }
|
||||
}
|
||||
|
||||
unsafe fn read_i32(address: *const u8) -> i32 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<i32>()) }
|
||||
}
|
||||
|
||||
unsafe fn read_f32(address: *const u8) -> f32 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<f32>()) }
|
||||
}
|
||||
|
||||
unsafe fn read_ptr(address: *const u8) -> *mut u8 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<*mut u8>()) }
|
||||
}
|
||||
|
||||
unsafe fn load_direct_input8_create() -> Option<DirectInput8CreateFn> {
|
||||
if let Some(callback) = unsafe { REAL_DINPUT8_CREATE } {
|
||||
return Some(callback);
|
||||
|
|
@ -168,3 +1018,30 @@ mod windows_hook {
|
|||
pub fn host_build_marker() -> &'static str {
|
||||
"rrt-hook host build"
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[test]
|
||||
fn writes_snapshot_bundle_to_disk() {
|
||||
let nonce = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("system time should be after epoch")
|
||||
.as_nanos();
|
||||
let dir = std::env::temp_dir().join(format!("rrt-hook-finance-{nonce}"));
|
||||
let paths = write_finance_snapshot_bundle(&dir, "testcase", &sample_finance_snapshot())
|
||||
.expect("bundle should be written");
|
||||
|
||||
let snapshot_json = fs::read_to_string(&paths.snapshot_path).expect("snapshot should exist");
|
||||
let outcome_json = fs::read_to_string(&paths.outcome_path).expect("outcome should exist");
|
||||
|
||||
assert!(snapshot_json.contains("\"policy\""));
|
||||
assert!(outcome_json.contains("\"evaluation\""));
|
||||
|
||||
let _ = fs::remove_file(&paths.snapshot_path);
|
||||
let _ = fs::remove_file(&paths.outcome_path);
|
||||
let _ = fs::remove_dir(&dir);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
957
crates/rrt-model/src/finance.rs
Normal file
957
crates/rrt-model/src/finance.rs
Normal file
|
|
@ -0,0 +1,957 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub enum GrowthSetting {
|
||||
#[default]
|
||||
Neutral,
|
||||
ExpansionBias,
|
||||
DividendSuppressed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub struct BondPosition {
|
||||
pub principal: i64,
|
||||
pub coupon_rate: f64,
|
||||
pub years_remaining: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum BankruptcyReason {
|
||||
EarlyStress,
|
||||
DeepDistress,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AnnualReportMetric {
|
||||
NetProfits,
|
||||
RevenueAggregate,
|
||||
FuelCost,
|
||||
RevenuePerShare,
|
||||
EarningsPerShare,
|
||||
DividendPerShare,
|
||||
BookValuePerShare,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DebtNewsOutcome {
|
||||
RefinanceOnly,
|
||||
RefinanceAndBorrow,
|
||||
RefinanceAndPayDown,
|
||||
DebtPayoffOnly,
|
||||
NewBorrowingOnly,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct DebtRestructureSummary {
|
||||
pub retired_principal: i64,
|
||||
pub issued_principal: i64,
|
||||
}
|
||||
|
||||
impl DebtRestructureSummary {
|
||||
pub fn classify(self) -> Option<DebtNewsOutcome> {
|
||||
match (self.retired_principal > 0, self.issued_principal > 0) {
|
||||
(false, false) => None,
|
||||
(false, true) => Some(DebtNewsOutcome::NewBorrowingOnly),
|
||||
(true, false) => Some(DebtNewsOutcome::DebtPayoffOnly),
|
||||
(true, true) if self.retired_principal == self.issued_principal => {
|
||||
Some(DebtNewsOutcome::RefinanceOnly)
|
||||
}
|
||||
(true, true) if self.issued_principal > self.retired_principal => {
|
||||
Some(DebtNewsOutcome::RefinanceAndBorrow)
|
||||
}
|
||||
(true, true) => Some(DebtNewsOutcome::RefinanceAndPayDown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum AnnualFinanceDecision {
|
||||
NoAction,
|
||||
DeclareBankruptcy {
|
||||
reason: BankruptcyReason,
|
||||
},
|
||||
IssueBond {
|
||||
count: u32,
|
||||
principal_per_bond: i64,
|
||||
term_years: u8,
|
||||
},
|
||||
RepurchasePublicShares {
|
||||
share_count: u32,
|
||||
price_per_share: f64,
|
||||
},
|
||||
IssuePublicShares {
|
||||
share_count_per_tranche: u32,
|
||||
tranche_count: u32,
|
||||
price_per_share: f64,
|
||||
},
|
||||
AdjustDividend {
|
||||
old_rate: f64,
|
||||
new_rate: f64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AnnualFinanceEvaluation {
|
||||
pub decision: AnnualFinanceDecision,
|
||||
pub debt_restructure: DebtRestructureSummary,
|
||||
pub debt_news: Option<DebtNewsOutcome>,
|
||||
pub repurchased_share_count: u32,
|
||||
pub issued_share_count: u32,
|
||||
}
|
||||
|
||||
impl AnnualFinanceEvaluation {
|
||||
pub fn no_action() -> Self {
|
||||
Self {
|
||||
decision: AnnualFinanceDecision::NoAction,
|
||||
debt_restructure: DebtRestructureSummary::default(),
|
||||
debt_news: None,
|
||||
repurchased_share_count: 0,
|
||||
issued_share_count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AnnualFinancePolicy {
|
||||
pub annual_mode: u8,
|
||||
pub build_103_plus: bool,
|
||||
pub bankruptcy_allowed: bool,
|
||||
pub bond_issuance_allowed: bool,
|
||||
pub stock_actions_allowed: bool,
|
||||
pub dividends_allowed: bool,
|
||||
pub growth_setting: GrowthSetting,
|
||||
pub stock_issue_cash_buffer: i64,
|
||||
}
|
||||
|
||||
impl Default for AnnualFinancePolicy {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
annual_mode: 0x0c,
|
||||
build_103_plus: true,
|
||||
bankruptcy_allowed: true,
|
||||
bond_issuance_allowed: true,
|
||||
stock_actions_allowed: true,
|
||||
dividends_allowed: true,
|
||||
growth_setting: GrowthSetting::Neutral,
|
||||
stock_issue_cash_buffer: 30_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct CompanyFinanceState {
|
||||
pub active: bool,
|
||||
pub years_since_founding: u8,
|
||||
pub years_since_last_bankruptcy: u8,
|
||||
pub current_cash: i64,
|
||||
pub current_company_value: i64,
|
||||
pub support_adjusted_share_price: f64,
|
||||
pub book_value_per_share: f64,
|
||||
pub current_fuel_cost: i64,
|
||||
pub current_dividend_per_share: f64,
|
||||
pub board_dividend_ceiling: f64,
|
||||
pub outstanding_share_count: u32,
|
||||
pub unassigned_share_count: u32,
|
||||
pub city_connection_bonus_latch: bool,
|
||||
pub linked_transit_service_latch: bool,
|
||||
pub chairman_buyback_factor: Option<f64>,
|
||||
pub recent_net_profits: [i64; 3],
|
||||
pub recent_revenue_totals: [i64; 3],
|
||||
pub recent_revenue_per_share: [f64; 3],
|
||||
pub recent_earnings_per_share: [f64; 3],
|
||||
pub recent_dividend_per_share: [f64; 3],
|
||||
pub bonds: Vec<BondPosition>,
|
||||
}
|
||||
|
||||
impl Default for CompanyFinanceState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active: true,
|
||||
years_since_founding: 5,
|
||||
years_since_last_bankruptcy: 20,
|
||||
current_cash: 0,
|
||||
current_company_value: 1_000_000,
|
||||
support_adjusted_share_price: 25.0,
|
||||
book_value_per_share: 20.0,
|
||||
current_fuel_cost: 0,
|
||||
current_dividend_per_share: 0.0,
|
||||
board_dividend_ceiling: 2.0,
|
||||
outstanding_share_count: 20_000,
|
||||
unassigned_share_count: 10_000,
|
||||
city_connection_bonus_latch: false,
|
||||
linked_transit_service_latch: false,
|
||||
chairman_buyback_factor: None,
|
||||
recent_net_profits: [10_000, 10_000, 10_000],
|
||||
recent_revenue_totals: [200_000, 180_000, 160_000],
|
||||
recent_revenue_per_share: [1.2, 1.1, 1.0],
|
||||
recent_earnings_per_share: [1.0, 0.9, 0.8],
|
||||
recent_dividend_per_share: [0.2, 0.2, 0.1],
|
||||
bonds: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CompanyFinanceState {
|
||||
pub const BOND_PRINCIPAL: i64 = 500_000;
|
||||
pub const BOND_TERM_YEARS: u8 = 30;
|
||||
pub const SHARE_LOT: u32 = 1_000;
|
||||
|
||||
pub fn total_debt_principal(&self) -> i64 {
|
||||
self.bonds.iter().map(|bond| bond.principal.max(0)).sum()
|
||||
}
|
||||
|
||||
pub fn highest_coupon_bond(&self) -> Option<BondPosition> {
|
||||
self.bonds
|
||||
.iter()
|
||||
.copied()
|
||||
.max_by(|left, right| left.coupon_rate.total_cmp(&right.coupon_rate))
|
||||
}
|
||||
|
||||
pub fn simulate_cash_after_full_bond_repayment(&self) -> i64 {
|
||||
self.current_cash - self.total_debt_principal()
|
||||
}
|
||||
|
||||
pub fn declare_bankruptcy(&mut self) {
|
||||
for bond in &mut self.bonds {
|
||||
bond.principal /= 2;
|
||||
}
|
||||
self.current_company_value /= 2;
|
||||
self.years_since_last_bankruptcy = 0;
|
||||
}
|
||||
|
||||
pub fn issue_bond(&mut self, coupon_rate: f64, count: u32) {
|
||||
for _ in 0..count {
|
||||
self.bonds.push(BondPosition {
|
||||
principal: Self::BOND_PRINCIPAL,
|
||||
coupon_rate,
|
||||
years_remaining: Self::BOND_TERM_YEARS,
|
||||
});
|
||||
self.current_cash += Self::BOND_PRINCIPAL;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repurchase_public_shares(&mut self, share_count: u32, price_per_share: f64) {
|
||||
let repurchased = share_count.min(self.unassigned_share_count);
|
||||
self.unassigned_share_count -= repurchased;
|
||||
self.outstanding_share_count = self.outstanding_share_count.saturating_sub(repurchased);
|
||||
self.current_cash -= (repurchased as f64 * price_per_share).round() as i64;
|
||||
}
|
||||
|
||||
pub fn issue_public_shares(&mut self, share_count: u32, price_per_share: f64) {
|
||||
self.outstanding_share_count = self.outstanding_share_count.saturating_add(share_count);
|
||||
self.unassigned_share_count = self.unassigned_share_count.saturating_add(share_count);
|
||||
self.current_cash += (share_count as f64 * price_per_share).round() as i64;
|
||||
}
|
||||
|
||||
pub fn set_dividend_rate(&mut self, new_rate: f64) {
|
||||
self.current_dividend_per_share = new_rate.clamp(0.0, self.board_dividend_ceiling);
|
||||
}
|
||||
|
||||
pub fn read_recent_metric(
|
||||
&self,
|
||||
metric: AnnualReportMetric,
|
||||
years_ago: usize,
|
||||
) -> Option<f64> {
|
||||
match metric {
|
||||
AnnualReportMetric::FuelCost if years_ago == 0 => Some(self.current_fuel_cost as f64),
|
||||
AnnualReportMetric::BookValuePerShare if years_ago == 0 => Some(self.book_value_per_share),
|
||||
AnnualReportMetric::NetProfits => self
|
||||
.recent_net_profits
|
||||
.get(years_ago)
|
||||
.copied()
|
||||
.map(|value| value as f64),
|
||||
AnnualReportMetric::RevenueAggregate => self
|
||||
.recent_revenue_totals
|
||||
.get(years_ago)
|
||||
.copied()
|
||||
.map(|value| value as f64),
|
||||
AnnualReportMetric::RevenuePerShare => {
|
||||
self.recent_revenue_per_share.get(years_ago).copied()
|
||||
}
|
||||
AnnualReportMetric::EarningsPerShare => {
|
||||
self.recent_earnings_per_share.get(years_ago).copied()
|
||||
}
|
||||
AnnualReportMetric::DividendPerShare => {
|
||||
self.recent_dividend_per_share.get(years_ago).copied()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_recent_metric_window(
|
||||
&self,
|
||||
metric: AnnualReportMetric,
|
||||
years: usize,
|
||||
) -> Vec<f64> {
|
||||
(0..years)
|
||||
.filter_map(|years_ago| self.read_recent_metric(metric, years_ago))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn weighted_recent_metric(
|
||||
&self,
|
||||
metric: AnnualReportMetric,
|
||||
weights: &[f64],
|
||||
) -> Option<f64> {
|
||||
let mut numerator = 0.0;
|
||||
let mut denominator = 0.0;
|
||||
for (years_ago, weight) in weights.iter().copied().enumerate() {
|
||||
let value = self.read_recent_metric(metric, years_ago)?;
|
||||
numerator += value * weight;
|
||||
denominator += weight;
|
||||
}
|
||||
|
||||
(denominator > 0.0).then_some(numerator / denominator)
|
||||
}
|
||||
|
||||
pub fn apply_annual_decision(&mut self, decision: &AnnualFinanceDecision) {
|
||||
match *decision {
|
||||
AnnualFinanceDecision::NoAction => {}
|
||||
AnnualFinanceDecision::DeclareBankruptcy { .. } => self.declare_bankruptcy(),
|
||||
AnnualFinanceDecision::IssueBond {
|
||||
count,
|
||||
principal_per_bond: _,
|
||||
term_years: _,
|
||||
} => {
|
||||
let coupon = self
|
||||
.highest_coupon_bond()
|
||||
.map(|bond| bond.coupon_rate)
|
||||
.unwrap_or(0.10);
|
||||
self.issue_bond(coupon, count);
|
||||
}
|
||||
AnnualFinanceDecision::RepurchasePublicShares {
|
||||
share_count,
|
||||
price_per_share,
|
||||
} => self.repurchase_public_shares(share_count, price_per_share),
|
||||
AnnualFinanceDecision::IssuePublicShares {
|
||||
share_count_per_tranche,
|
||||
tranche_count,
|
||||
price_per_share,
|
||||
} => self.issue_public_shares(
|
||||
share_count_per_tranche.saturating_mul(tranche_count),
|
||||
price_per_share,
|
||||
),
|
||||
AnnualFinanceDecision::AdjustDividend { new_rate, .. } => {
|
||||
self.set_dividend_rate(new_rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FinanceSnapshot {
|
||||
pub policy: AnnualFinancePolicy,
|
||||
pub company: CompanyFinanceState,
|
||||
}
|
||||
|
||||
impl FinanceSnapshot {
|
||||
pub fn evaluate(&self) -> FinanceOutcome {
|
||||
let evaluation = evaluate_annual_finance_policy_detailed(&self.policy, &self.company);
|
||||
let mut post_company = self.company.clone();
|
||||
post_company.apply_annual_decision(&evaluation.decision);
|
||||
|
||||
FinanceOutcome {
|
||||
evaluation,
|
||||
post_company,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FinanceOutcome {
|
||||
pub evaluation: AnnualFinanceEvaluation,
|
||||
pub post_company: CompanyFinanceState,
|
||||
}
|
||||
|
||||
pub fn evaluate_annual_finance_policy(
|
||||
policy: &AnnualFinancePolicy,
|
||||
company: &CompanyFinanceState,
|
||||
) -> AnnualFinanceDecision {
|
||||
evaluate_annual_finance_policy_detailed(policy, company).decision
|
||||
}
|
||||
|
||||
pub fn evaluate_annual_finance_policy_detailed(
|
||||
policy: &AnnualFinancePolicy,
|
||||
company: &CompanyFinanceState,
|
||||
) -> AnnualFinanceEvaluation {
|
||||
if !company.active {
|
||||
return AnnualFinanceEvaluation::no_action();
|
||||
}
|
||||
|
||||
if should_bankrupt_early(policy, company) {
|
||||
return AnnualFinanceEvaluation {
|
||||
decision: AnnualFinanceDecision::DeclareBankruptcy {
|
||||
reason: BankruptcyReason::EarlyStress,
|
||||
},
|
||||
..AnnualFinanceEvaluation::no_action()
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(evaluation) = issue_bond_evaluation(policy, company) {
|
||||
return evaluation;
|
||||
}
|
||||
|
||||
if let Some(evaluation) = repurchase_evaluation(policy, company) {
|
||||
return evaluation;
|
||||
}
|
||||
|
||||
if should_bankrupt_deep_distress(policy, company) {
|
||||
return AnnualFinanceEvaluation {
|
||||
decision: AnnualFinanceDecision::DeclareBankruptcy {
|
||||
reason: BankruptcyReason::DeepDistress,
|
||||
},
|
||||
..AnnualFinanceEvaluation::no_action()
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(evaluation) = issue_stock_evaluation(policy, company) {
|
||||
return evaluation;
|
||||
}
|
||||
|
||||
if let Some(evaluation) = dividend_evaluation(policy, company) {
|
||||
return evaluation;
|
||||
}
|
||||
|
||||
AnnualFinanceEvaluation::no_action()
|
||||
}
|
||||
|
||||
fn should_bankrupt_early(policy: &AnnualFinancePolicy, company: &CompanyFinanceState) -> bool {
|
||||
if policy.annual_mode != 0x0c || !policy.bankruptcy_allowed {
|
||||
return false;
|
||||
}
|
||||
if company.years_since_last_bankruptcy < 13 || company.years_since_founding < 4 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let current_revenue = company.recent_revenue_totals[0];
|
||||
let stress_ladder: i64 = if current_revenue < 120_000 {
|
||||
-600_000
|
||||
} else if current_revenue < 230_000 {
|
||||
-1_100_000
|
||||
} else if current_revenue < 340_000 {
|
||||
-1_600_000
|
||||
} else {
|
||||
-2_000_000
|
||||
};
|
||||
|
||||
let failed_profit_years = company
|
||||
.recent_net_profits
|
||||
.iter()
|
||||
.filter(|profit| **profit <= 0)
|
||||
.count();
|
||||
let net_profit_sum: i64 = company.recent_net_profits.iter().sum();
|
||||
let share_price_floor = if failed_profit_years == 3 { 20.0 } else { 15.0 };
|
||||
let fuel_gate = (stress_ladder.abs() as f64 * 0.08).round() as i64;
|
||||
|
||||
failed_profit_years >= 2
|
||||
&& net_profit_sum <= -60_000
|
||||
&& company.support_adjusted_share_price >= share_price_floor
|
||||
&& company.current_fuel_cost >= fuel_gate
|
||||
}
|
||||
|
||||
fn should_bankrupt_deep_distress(
|
||||
policy: &AnnualFinancePolicy,
|
||||
company: &CompanyFinanceState,
|
||||
) -> bool {
|
||||
policy.bankruptcy_allowed
|
||||
&& company.current_cash < -300_000
|
||||
&& company.years_since_founding >= 3
|
||||
&& company.years_since_last_bankruptcy >= 5
|
||||
&& company.recent_net_profits.iter().all(|profit| *profit <= -20_000)
|
||||
}
|
||||
|
||||
fn issue_bond_evaluation(
|
||||
policy: &AnnualFinancePolicy,
|
||||
company: &CompanyFinanceState,
|
||||
) -> Option<AnnualFinanceEvaluation> {
|
||||
if !policy.bond_issuance_allowed {
|
||||
return None;
|
||||
}
|
||||
|
||||
let simulated_cash = company.simulate_cash_after_full_bond_repayment();
|
||||
let target_floor = if company.linked_transit_service_latch {
|
||||
-30_000
|
||||
} else {
|
||||
-250_000
|
||||
};
|
||||
if simulated_cash >= target_floor {
|
||||
return None;
|
||||
}
|
||||
|
||||
let shortfall = (target_floor - simulated_cash).max(0) as u64;
|
||||
let count = shortfall.div_ceil(CompanyFinanceState::BOND_PRINCIPAL as u64) as u32;
|
||||
let issued_principal = count.max(1) as i64 * CompanyFinanceState::BOND_PRINCIPAL;
|
||||
let debt_restructure = DebtRestructureSummary {
|
||||
retired_principal: 0,
|
||||
issued_principal,
|
||||
};
|
||||
|
||||
Some(AnnualFinanceEvaluation {
|
||||
decision: AnnualFinanceDecision::IssueBond {
|
||||
count: count.max(1),
|
||||
principal_per_bond: CompanyFinanceState::BOND_PRINCIPAL,
|
||||
term_years: CompanyFinanceState::BOND_TERM_YEARS,
|
||||
},
|
||||
debt_news: debt_restructure.classify(),
|
||||
debt_restructure,
|
||||
..AnnualFinanceEvaluation::no_action()
|
||||
})
|
||||
}
|
||||
|
||||
fn repurchase_evaluation(
|
||||
policy: &AnnualFinancePolicy,
|
||||
company: &CompanyFinanceState,
|
||||
) -> Option<AnnualFinanceEvaluation> {
|
||||
if !policy.stock_actions_allowed
|
||||
|| !company.city_connection_bonus_latch
|
||||
|| matches!(policy.growth_setting, GrowthSetting::DividendSuppressed)
|
||||
|| company.unassigned_share_count < CompanyFinanceState::SHARE_LOT
|
||||
|| company.current_company_value < 800_000
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut factor = company.chairman_buyback_factor.unwrap_or(1.0);
|
||||
if matches!(policy.growth_setting, GrowthSetting::ExpansionBias) {
|
||||
factor *= 1.6;
|
||||
}
|
||||
|
||||
let batch = CompanyFinanceState::SHARE_LOT;
|
||||
let affordability_gate = company.support_adjusted_share_price * factor * batch as f64 * 1.2;
|
||||
if company.current_cash < affordability_gate.round() as i64 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AnnualFinanceEvaluation {
|
||||
decision: AnnualFinanceDecision::RepurchasePublicShares {
|
||||
share_count: batch,
|
||||
price_per_share: company.support_adjusted_share_price,
|
||||
},
|
||||
repurchased_share_count: batch,
|
||||
..AnnualFinanceEvaluation::no_action()
|
||||
})
|
||||
}
|
||||
|
||||
fn issue_stock_evaluation(
|
||||
policy: &AnnualFinancePolicy,
|
||||
company: &CompanyFinanceState,
|
||||
) -> Option<AnnualFinanceEvaluation> {
|
||||
if !policy.build_103_plus
|
||||
|| !policy.stock_actions_allowed
|
||||
|| !policy.bond_issuance_allowed
|
||||
|| company.bonds.len() < 2
|
||||
|| company.years_since_founding < 1
|
||||
|| company.support_adjusted_share_price < 22.0
|
||||
|| company.book_value_per_share <= 0.0
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let highest_coupon = company.highest_coupon_bond()?;
|
||||
if company.current_cash >= highest_coupon.principal + policy.stock_issue_cash_buffer {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut tranche =
|
||||
((company.outstanding_share_count / 10) / CompanyFinanceState::SHARE_LOT)
|
||||
* CompanyFinanceState::SHARE_LOT;
|
||||
tranche = tranche.max(2_000);
|
||||
while tranche >= CompanyFinanceState::SHARE_LOT
|
||||
&& company.support_adjusted_share_price * tranche as f64 > 55_000.0
|
||||
{
|
||||
tranche -= CompanyFinanceState::SHARE_LOT;
|
||||
}
|
||||
if tranche < CompanyFinanceState::SHARE_LOT {
|
||||
return None;
|
||||
}
|
||||
|
||||
let price_to_book = company.support_adjusted_share_price / company.book_value_per_share;
|
||||
if price_to_book < required_price_to_book_ratio(highest_coupon.coupon_rate) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AnnualFinanceEvaluation {
|
||||
decision: AnnualFinanceDecision::IssuePublicShares {
|
||||
share_count_per_tranche: tranche,
|
||||
tranche_count: 2,
|
||||
price_per_share: company.support_adjusted_share_price,
|
||||
},
|
||||
issued_share_count: tranche * 2,
|
||||
..AnnualFinanceEvaluation::no_action()
|
||||
})
|
||||
}
|
||||
|
||||
fn dividend_evaluation(
|
||||
policy: &AnnualFinancePolicy,
|
||||
company: &CompanyFinanceState,
|
||||
) -> Option<AnnualFinanceEvaluation> {
|
||||
if !policy.dividends_allowed || company.years_since_founding < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let weighted_target = company
|
||||
.weighted_recent_metric(AnnualReportMetric::EarningsPerShare, &[3.0, 2.0, 1.0])
|
||||
.unwrap_or(0.0);
|
||||
let mut target = weighted_target.max(0.0);
|
||||
|
||||
if company.unassigned_share_count < CompanyFinanceState::SHARE_LOT
|
||||
&& company.outstanding_share_count > 0
|
||||
&& company.current_cash > 0
|
||||
{
|
||||
target += company.current_cash as f64 / company.outstanding_share_count as f64;
|
||||
}
|
||||
|
||||
target = match policy.growth_setting {
|
||||
GrowthSetting::Neutral => target,
|
||||
GrowthSetting::ExpansionBias => target * 0.66,
|
||||
GrowthSetting::DividendSuppressed => 0.0,
|
||||
};
|
||||
target = quantize_tenths(target.clamp(0.0, company.board_dividend_ceiling));
|
||||
|
||||
if (target - company.current_dividend_per_share).abs() <= 0.1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AnnualFinanceEvaluation {
|
||||
decision: AnnualFinanceDecision::AdjustDividend {
|
||||
old_rate: company.current_dividend_per_share,
|
||||
new_rate: target,
|
||||
},
|
||||
..AnnualFinanceEvaluation::no_action()
|
||||
})
|
||||
}
|
||||
|
||||
fn required_price_to_book_ratio(coupon_rate: f64) -> f64 {
|
||||
if coupon_rate <= 0.07 {
|
||||
1.3
|
||||
} else if coupon_rate <= 0.08 {
|
||||
1.2
|
||||
} else if coupon_rate <= 0.09 {
|
||||
1.1
|
||||
} else if coupon_rate <= 0.10 {
|
||||
0.95
|
||||
} else if coupon_rate <= 0.11 {
|
||||
0.8
|
||||
} else if coupon_rate <= 0.12 {
|
||||
0.62
|
||||
} else if coupon_rate <= 0.13 {
|
||||
0.5
|
||||
} else {
|
||||
0.35
|
||||
}
|
||||
}
|
||||
|
||||
fn quantize_tenths(value: f64) -> f64 {
|
||||
(value * 10.0).round() / 10.0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn base_policy() -> AnnualFinancePolicy {
|
||||
AnnualFinancePolicy::default()
|
||||
}
|
||||
|
||||
fn base_company() -> CompanyFinanceState {
|
||||
CompanyFinanceState {
|
||||
bonds: vec![
|
||||
BondPosition {
|
||||
principal: 400_000,
|
||||
coupon_rate: 0.10,
|
||||
years_remaining: 10,
|
||||
},
|
||||
BondPosition {
|
||||
principal: 300_000,
|
||||
coupon_rate: 0.12,
|
||||
years_remaining: 12,
|
||||
},
|
||||
],
|
||||
..CompanyFinanceState::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn early_bankruptcy_precedes_other_actions() {
|
||||
let policy = base_policy();
|
||||
let company = CompanyFinanceState {
|
||||
current_fuel_cost: 90_000,
|
||||
support_adjusted_share_price: 21.0,
|
||||
recent_net_profits: [-30_000, -25_000, -20_000],
|
||||
recent_revenue_totals: [150_000, 140_000, 130_000],
|
||||
city_connection_bonus_latch: true,
|
||||
linked_transit_service_latch: true,
|
||||
..base_company()
|
||||
};
|
||||
|
||||
let decision = evaluate_annual_finance_policy(&policy, &company);
|
||||
assert_eq!(
|
||||
decision,
|
||||
AnnualFinanceDecision::DeclareBankruptcy {
|
||||
reason: BankruptcyReason::EarlyStress,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bond_issue_precedes_stock_issue_when_cash_window_fails() {
|
||||
let policy = base_policy();
|
||||
let company = CompanyFinanceState {
|
||||
current_cash: -900_000,
|
||||
support_adjusted_share_price: 30.0,
|
||||
book_value_per_share: 20.0,
|
||||
linked_transit_service_latch: true,
|
||||
recent_net_profits: [20_000, 10_000, 5_000],
|
||||
..base_company()
|
||||
};
|
||||
|
||||
let decision = evaluate_annual_finance_policy(&policy, &company);
|
||||
assert_eq!(
|
||||
decision,
|
||||
AnnualFinanceDecision::IssueBond {
|
||||
count: 4,
|
||||
principal_per_bond: CompanyFinanceState::BOND_PRINCIPAL,
|
||||
term_years: CompanyFinanceState::BOND_TERM_YEARS,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stock_issue_checks_liquidity_before_valuation() {
|
||||
let policy = AnnualFinancePolicy {
|
||||
bond_issuance_allowed: false,
|
||||
dividends_allowed: false,
|
||||
..base_policy()
|
||||
};
|
||||
let company = CompanyFinanceState {
|
||||
current_cash: 500_000,
|
||||
support_adjusted_share_price: 30.0,
|
||||
book_value_per_share: 20.0,
|
||||
recent_net_profits: [40_000, 30_000, 20_000],
|
||||
recent_revenue_totals: [250_000, 240_000, 230_000],
|
||||
..base_company()
|
||||
};
|
||||
|
||||
let decision = evaluate_annual_finance_policy(&policy, &company);
|
||||
assert_eq!(decision, AnnualFinanceDecision::NoAction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stock_issue_emits_two_tranches_when_gates_pass() {
|
||||
let policy = AnnualFinancePolicy {
|
||||
dividends_allowed: false,
|
||||
..base_policy()
|
||||
};
|
||||
let company = CompanyFinanceState {
|
||||
current_cash: 100_000,
|
||||
support_adjusted_share_price: 27.5,
|
||||
book_value_per_share: 20.0,
|
||||
outstanding_share_count: 60_000,
|
||||
recent_net_profits: [40_000, 30_000, 20_000],
|
||||
recent_revenue_totals: [250_000, 240_000, 230_000],
|
||||
bonds: vec![
|
||||
BondPosition {
|
||||
principal: 150_000,
|
||||
coupon_rate: 0.12,
|
||||
years_remaining: 12,
|
||||
},
|
||||
BondPosition {
|
||||
principal: 10_000,
|
||||
coupon_rate: 0.10,
|
||||
years_remaining: 10,
|
||||
},
|
||||
],
|
||||
..base_company()
|
||||
};
|
||||
|
||||
let decision = evaluate_annual_finance_policy(&policy, &company);
|
||||
assert_eq!(
|
||||
decision,
|
||||
AnnualFinanceDecision::IssuePublicShares {
|
||||
share_count_per_tranche: 2_000,
|
||||
tranche_count: 2,
|
||||
price_per_share: 27.5,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recent_metric_reader_exposes_report_lanes() {
|
||||
let company = CompanyFinanceState {
|
||||
current_fuel_cost: 12_345,
|
||||
recent_net_profits: [11_000, 22_000, 33_000],
|
||||
recent_revenue_totals: [101_000, 102_000, 103_000],
|
||||
recent_revenue_per_share: [1.6, 1.5, 1.4],
|
||||
recent_earnings_per_share: [1.3, 1.2, 1.1],
|
||||
recent_dividend_per_share: [0.4, 0.3, 0.2],
|
||||
book_value_per_share: 19.5,
|
||||
..base_company()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
company.read_recent_metric(AnnualReportMetric::NetProfits, 1),
|
||||
Some(22_000.0)
|
||||
);
|
||||
assert_eq!(
|
||||
company.read_recent_metric(AnnualReportMetric::RevenuePerShare, 2),
|
||||
Some(1.4)
|
||||
);
|
||||
assert_eq!(
|
||||
company.read_recent_metric(AnnualReportMetric::FuelCost, 0),
|
||||
Some(12_345.0)
|
||||
);
|
||||
assert_eq!(
|
||||
company.read_recent_metric(AnnualReportMetric::BookValuePerShare, 0),
|
||||
Some(19.5)
|
||||
);
|
||||
assert_eq!(
|
||||
company.weighted_recent_metric(AnnualReportMetric::EarningsPerShare, &[3.0, 2.0, 1.0]),
|
||||
Some(1.2333333333333334)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debt_news_classifies_borrow_and_paydown_paths() {
|
||||
assert_eq!(
|
||||
DebtRestructureSummary {
|
||||
retired_principal: 500_000,
|
||||
issued_principal: 500_000,
|
||||
}
|
||||
.classify(),
|
||||
Some(DebtNewsOutcome::RefinanceOnly)
|
||||
);
|
||||
assert_eq!(
|
||||
DebtRestructureSummary {
|
||||
retired_principal: 300_000,
|
||||
issued_principal: 500_000,
|
||||
}
|
||||
.classify(),
|
||||
Some(DebtNewsOutcome::RefinanceAndBorrow)
|
||||
);
|
||||
assert_eq!(
|
||||
DebtRestructureSummary {
|
||||
retired_principal: 500_000,
|
||||
issued_principal: 300_000,
|
||||
}
|
||||
.classify(),
|
||||
Some(DebtNewsOutcome::RefinanceAndPayDown)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detailed_evaluation_carries_share_and_debt_side_effects() {
|
||||
let policy = base_policy();
|
||||
let company = CompanyFinanceState {
|
||||
current_cash: -900_000,
|
||||
support_adjusted_share_price: 30.0,
|
||||
book_value_per_share: 20.0,
|
||||
linked_transit_service_latch: true,
|
||||
recent_net_profits: [20_000, 10_000, 5_000],
|
||||
..base_company()
|
||||
};
|
||||
|
||||
let evaluation = evaluate_annual_finance_policy_detailed(&policy, &company);
|
||||
assert_eq!(
|
||||
evaluation.decision,
|
||||
AnnualFinanceDecision::IssueBond {
|
||||
count: 4,
|
||||
principal_per_bond: CompanyFinanceState::BOND_PRINCIPAL,
|
||||
term_years: CompanyFinanceState::BOND_TERM_YEARS,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
evaluation.debt_news,
|
||||
Some(DebtNewsOutcome::NewBorrowingOnly)
|
||||
);
|
||||
assert_eq!(evaluation.debt_restructure.issued_principal, 2_000_000);
|
||||
assert_eq!(evaluation.repurchased_share_count, 0);
|
||||
assert_eq!(evaluation.issued_share_count, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_evaluation_applies_post_state_transition() {
|
||||
let snapshot = FinanceSnapshot {
|
||||
policy: AnnualFinancePolicy {
|
||||
dividends_allowed: false,
|
||||
..base_policy()
|
||||
},
|
||||
company: CompanyFinanceState {
|
||||
current_cash: 100_000,
|
||||
support_adjusted_share_price: 27.5,
|
||||
book_value_per_share: 20.0,
|
||||
outstanding_share_count: 60_000,
|
||||
recent_net_profits: [40_000, 30_000, 20_000],
|
||||
recent_revenue_totals: [250_000, 240_000, 230_000],
|
||||
bonds: vec![
|
||||
BondPosition {
|
||||
principal: 150_000,
|
||||
coupon_rate: 0.12,
|
||||
years_remaining: 12,
|
||||
},
|
||||
BondPosition {
|
||||
principal: 10_000,
|
||||
coupon_rate: 0.10,
|
||||
years_remaining: 10,
|
||||
},
|
||||
],
|
||||
..base_company()
|
||||
},
|
||||
};
|
||||
|
||||
let outcome = snapshot.evaluate();
|
||||
assert_eq!(
|
||||
outcome.evaluation.decision,
|
||||
AnnualFinanceDecision::IssuePublicShares {
|
||||
share_count_per_tranche: 2_000,
|
||||
tranche_count: 2,
|
||||
price_per_share: 27.5,
|
||||
}
|
||||
);
|
||||
assert_eq!(outcome.evaluation.issued_share_count, 4_000);
|
||||
assert_eq!(outcome.post_company.outstanding_share_count, 64_000);
|
||||
assert_eq!(outcome.post_company.unassigned_share_count, 14_000);
|
||||
assert_eq!(outcome.post_company.current_cash, 210_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dividend_target_is_quantized_and_clamped() {
|
||||
let policy = AnnualFinancePolicy {
|
||||
bond_issuance_allowed: false,
|
||||
..base_policy()
|
||||
};
|
||||
let company = CompanyFinanceState {
|
||||
current_cash: 20_000,
|
||||
board_dividend_ceiling: 0.9,
|
||||
current_dividend_per_share: 0.2,
|
||||
unassigned_share_count: 500,
|
||||
outstanding_share_count: 10_000,
|
||||
recent_earnings_per_share: [1.4, 1.1, 0.9],
|
||||
..base_company()
|
||||
};
|
||||
|
||||
let decision = evaluate_annual_finance_policy(&policy, &company);
|
||||
assert_eq!(
|
||||
decision,
|
||||
AnnualFinanceDecision::AdjustDividend {
|
||||
old_rate: 0.2,
|
||||
new_rate: 0.9,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bankruptcy_mutator_halves_bond_principal() {
|
||||
let mut company = base_company();
|
||||
company.current_company_value = 900_000;
|
||||
company.years_since_last_bankruptcy = 25;
|
||||
|
||||
company.apply_annual_decision(&AnnualFinanceDecision::DeclareBankruptcy {
|
||||
reason: BankruptcyReason::DeepDistress,
|
||||
});
|
||||
|
||||
assert_eq!(company.bonds[0].principal, 200_000);
|
||||
assert_eq!(company.bonds[1].principal, 150_000);
|
||||
assert_eq!(company.current_company_value, 450_000);
|
||||
assert_eq!(company.years_since_last_bankruptcy, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
pub mod finance;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ Current bounded compatibility impact:
|
|||
`0x00408f70`, because that target sums raw site cache `+0x12` rather than the weighted `+0x0e`
|
||||
or promoted `+0x16` lanes
|
||||
|
||||
Highest-value open edge:
|
||||
Latest finance closure:
|
||||
|
||||
- The remaining semantic meaning of the annual finance policy branches is now narrower:
|
||||
stat-family `0x2329/0x1d` is now bounded as current `Book Value Per Share` in the stock-issue
|
||||
|
|
@ -72,27 +72,80 @@ Highest-value open edge:
|
|||
bond-rate-versus-support ladder, the broader support-adjusted share-price or public-support lane
|
||||
is now bounded through `company_compute_public_support_adjusted_share_price_scalar` `0x00424fd0`,
|
||||
and its immediate feeder is now bounded as
|
||||
`company_compute_cached_recent_per_share_performance_subscore` `0x004248d0`, which weighs recent
|
||||
`Revenue Per Share`, `Earnings Per Share`, and `Dividend Per Share` lanes `0x1e/0x1f/0x20`
|
||||
against current `Book Value Per Share`, while `0x21` now routes through
|
||||
`company_compute_cached_recent_per_share_performance_subscore` `0x004248d0`, which uses an
|
||||
explicit recent-history ladder: the current partial year is weighted by `(5 * [world+0x0f]) - 5`,
|
||||
the prior four full years use `48/36/24/12` for `Earnings Per Share` and `Revenue Per Share`
|
||||
lanes `0x1f/0x1e`, and the adjacent `Dividend Per Share` non-decline trend comparisons on lane
|
||||
`0x20` use weights `9/8/7/6`, all before the tail folds those lanes against current `Book Value
|
||||
Per Share`, while `0x21` now routes through
|
||||
`company_compute_five_year_weighted_shareholder_return` `0x004246b0` and its paired hidden
|
||||
shareholder-payout lane `0x23`,
|
||||
raw slot `0x09` aligns with the Income Statement fuel-cost lane surfaced by tooltip `1309`, and
|
||||
derived slot `0x2b` now reads as the rolling net-profits lane. The main remaining gap is the
|
||||
exact weight schedule and gate ordering used inside the already-bounded annual bankruptcy,
|
||||
debt, repurchase, stock-issue, and dividend branches. The current strongest read is now:
|
||||
derived slot `0x2b` now reads as the rolling net-profits lane. The exact stock-issue cutoff
|
||||
ordering is now closed, and the current grounded read is:
|
||||
bankruptcy = first the year, toggle, and cooldown gates, then the three-year scan, then the
|
||||
revenue-band-selected cash-and-debt ladder, then the support-adjusted share-price threshold,
|
||||
then the fuel-cost lane and net-profit accumulator; the later deep-distress fallback then
|
||||
re-checks cash below `-300000` plus the first three recent profit years at or below `-20000`.
|
||||
Stock issuance = first the bond/stock toggle gates, then the outstanding-share tranche sizing,
|
||||
then the `55000` proceeds cap, then the support-adjusted price-to-book screen against the
|
||||
highest live coupon band. Dividend adjustment = first the toggle and age gates, then the
|
||||
weighted recent-profit-per-share target, then the small-unassigned-share cash supplement, then
|
||||
the board ceiling clamp. The remaining gap is now mostly the near-cutoff ordering inside the
|
||||
stock-issue lane, and the current strongest read places the liquidity gate at `0x2329/0x0d`
|
||||
before the valuation gate at `0x2329/0x1d`. The dividend-ceiling clamp is already separately
|
||||
bounded on the dividend branch. That is now a final precedence question, not a missing
|
||||
ownership gap in the annual finance verbs themselves. The debt-news tail is also now explicit:
|
||||
Stock issuance = first the bond/stock toggles, live-bond count, and founding-age gates, then
|
||||
tranche sizing from outstanding shares, then the `55000` proceeds-cap trim, then the pressured
|
||||
support-adjusted share-price recompute and price-to-book derivation, followed by the tested
|
||||
gates in fixed order: share-price floor `22`, proceeds floor `55000`, current cash
|
||||
`0x2329/0x0d` against highest-coupon principal plus `5000`, one later stock-issue cooldown gate
|
||||
driven by the current issue mixed-radix calendar tuple at `[company+0x16b/+0x16f]` versus the
|
||||
active world absolute calendar counter `[world+0x15]`, and only then the coupon-versus-price-to-book ladder
|
||||
`0.07/1.3 .. 0.14/0.35`. Dividend
|
||||
adjustment = first the toggle and age gates, then the weighted recent-profit-per-share target,
|
||||
then the small-unassigned-share cash supplement, then the board ceiling clamp. The debt-news tail
|
||||
is also now explicit:
|
||||
it compares total retired versus newly issued principal to pick `2882..2886`, and the
|
||||
stock-buyback tail separately publishes `2887` from the accumulated repurchased-share count.
|
||||
|
||||
Current finance closure detail:
|
||||
|
||||
- The stock-issue cooldown stamp is now structurally closed:
|
||||
`[company+0x16b/+0x16f]` holds the current issue calendar-point tuple,
|
||||
`[company+0x173/+0x177]` preserves the prior tuple,
|
||||
and the cooldown gate converts the current tuple through `0x0051d3c0` before comparing against
|
||||
the active world absolute calendar counter `[world+0x15]`. The tuple model is no longer just
|
||||
“calendar-like”: current local arithmetic now bounds it as the same mixed-radix
|
||||
`12 x 28 x 3 x 60` family unpacked by `0x0051d460`.
|
||||
- The recent per-share performance feeder `0x004248d0` is now tighter than before:
|
||||
its four tail lanes are `EPS * 10`, `Revenue Per Share`, a dividend-strength lane derived from
|
||||
the weighted non-decline ratio on slot `0x20`, and current `Book Value Per Share` from slot
|
||||
`0x1d`; base lane weights are `40/10/20/30`, startup companies ramp those weights with the
|
||||
age table `0/0/0/100 -> 25/25/35/100 -> 50/50/65/100 -> 75/75/85/100 -> 100/100/100/100`,
|
||||
the strongest lane is boosted by `1.25` while the weakest is reduced by `0.8`, the bounded
|
||||
intermediate `[company+0x0d19]` uses `((difficulty + 1.0) / 2.0)` with a floor at `0.5`,
|
||||
and the final cached score uses the raw difficulty table
|
||||
`0.8/0.9/1.0/1.1/1.2/0.9/0.95/1.0` plus the post-bankruptcy smoothing factor.
|
||||
- The broader support-adjusted share-price consumer `0x00424fd0` is tighter now too:
|
||||
it starts from that recent per-share feeder, can interpolate it against the older support field
|
||||
`[company+0x57]` on the young-company path using one capped progress term from
|
||||
`[world+0x15]`, `[company+0x0d07]`, and founding year `[company+0x157]`, clamps the caller's
|
||||
share-count pressure term to `[-0.2, 0.2]`, can refresh mutable support field `[company+0x4f]`
|
||||
from that staged pressure, derives one share-count growth term from `(shares / 20000)^0.33`,
|
||||
then runs the resulting support scalar through the later threshold ladder
|
||||
`0.6 / 0.45 / 0.3 / 1.7 / 2.5 / 4.0 / 6.0` before multiplying by scenario issue `0x37` and
|
||||
rounding into the cached share-price lane `[company+0x0d7b]`.
|
||||
- The surrounding finance issue split is now much tighter:
|
||||
`0x00425320` is the bounded company `Credit Rating` score used by the shell debt summary and the
|
||||
bond-offer rejection path on localized id `974`, with raw issue slot `0x38` folded in at the
|
||||
tail; `0x00424580` is the explicit `Prime Rate` lane fed by raw issue slot `0x39`; and
|
||||
`0x00424fd0` then multiplies the equity-side share-price scalar by issue slot `0x37`.
|
||||
So the strongest current read is:
|
||||
`0x38 = debt-market / creditworthiness`,
|
||||
`0x39 = prime-rate / market-rate pressure`,
|
||||
`0x37 = broader equity-market / investor confidence in company or chairman performance`.
|
||||
- The raw formula side is now mostly closed, and what remains is mostly whether RT3 exposed one exact
|
||||
UI caption for issue `0x37` beyond the already-grounded investor-performance strings. The
|
||||
negative results now sharpen that last edge too: the nearby `0x460a90` / `0x473620`
|
||||
registration blocks are camera-view and related shell-command families, not finance issue
|
||||
owners; and the editor-side `Stock Prices` label belongs to a separate float-tuning block
|
||||
`[state+0x0bde..0x0bf6]`, not the issue table behind `0x37`. The shell-resource side is now
|
||||
tighter too: extracting `CompanyDetail.win` from `rt3_2WIN.PK4` did not uncover any separate
|
||||
plain-English finance caption or investor label for this lane, and the recovered shell owner path
|
||||
already routes section-0 through the dynamic-text widget `0x947f` plus formatter
|
||||
`shell_format_company_governance_and_economy_status_panel` `0x004e5cf0`. So the strongest
|
||||
current UI-facing text for issue `0x37` remains the investor-attitude sentence family
|
||||
`1217` and `3048/3049`, not one standalone caption row like `Credit Rating:` or `Prime Rate:`.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,10 @@ Current grounded runtime chain:
|
|||
- `placed_structure_refresh_local_service_score_bundle`
|
||||
- `placed_structure_query_candidate_local_service_metrics`
|
||||
- `placed_structure_count_candidates_with_local_service_metrics`
|
||||
- `placed_structure_get_nth_candidate_id_with_local_service_metrics`
|
||||
- `placed_structure_query_cached_express_service_class_score`
|
||||
- `placed_structure_refresh_candidate_local_service_comparison_cache_against_peer_site`
|
||||
- `placed_structure_select_best_candidate_id_by_local_service_score`
|
||||
|
||||
Current grounded shell-side consumers:
|
||||
|
||||
|
|
@ -51,14 +54,19 @@ What this note is for:
|
|||
- Per-site local service tables and caps
|
||||
- Station-detail and station-list read-side summaries
|
||||
|
||||
Highest-value open edge:
|
||||
Resolved edge:
|
||||
|
||||
- The deeper gameplay meaning of the per-site local service tables is now mostly about whether
|
||||
those scores stay confined to placement, overlay, and station-summary presentation through the
|
||||
query helpers, or whether the same query outputs also feed live cargo-economy filter flags,
|
||||
per-site service-cap rebuilds, or nearby-structure behavior beyond the currently bounded shell
|
||||
summaries. The grounded chain strongly suggests the read-side path is dominant, the query
|
||||
helpers themselves still read as read-only consumers, and the only grounded mutation bridge is
|
||||
the collection-wide cargo-economy filter refresh. The remaining open question is whether that
|
||||
filter refresh is the full policy sink for the local service tables or whether an additional
|
||||
mutation-side writer exists outside the local corpus.
|
||||
- The per-site local service query family is now bounded as predominantly read-side. Above the
|
||||
mutation and rebuild lane, the grounded helpers are:
|
||||
`placed_structure_query_candidate_local_service_metrics`,
|
||||
`placed_structure_count_candidates_with_local_service_metrics`,
|
||||
`placed_structure_get_nth_candidate_id_with_local_service_metrics`,
|
||||
`placed_structure_query_cached_express_service_class_score`,
|
||||
`placed_structure_refresh_candidate_local_service_comparison_cache_against_peer_site`, and
|
||||
`placed_structure_select_best_candidate_id_by_local_service_score`.
|
||||
- Their grounded callers are shell summaries, shell detail rows and popups, the world preview
|
||||
overlay, one company-side autoroute score cache rebuild, and one peer-site comparison cache.
|
||||
None of the newly closed callers acts as a policy writer back into candidate or site state.
|
||||
- The only grounded mutation bridge on this edge remains
|
||||
`structure_candidate_collection_refresh_cargo_economy_filter_flags`, which writes the derived
|
||||
enabled or filtered state into `[entry+0x56]` before rebuilding the visible counts.
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ What this note is for:
|
|||
- GameSpy-facing callback and live-route semantics
|
||||
- Selector-view refresh, retry, and probe state
|
||||
|
||||
Highest-value open edge:
|
||||
Latest local closure:
|
||||
|
||||
- The remaining multiplayer edge is narrower now:
|
||||
- The remaining multiplayer branch is narrower now:
|
||||
the status-route callback vector is bounded through selector-text or averaged-sample publication,
|
||||
control-id-list seeding, scalar-query forwarding, and the validated cookie or extended-payload
|
||||
callbacks. The selector-view sidecar is tighter too: `[entry+0x80]` now reads as the averaged
|
||||
|
|
@ -49,6 +49,10 @@ Highest-value open edge:
|
|||
`0x5906f0` tears down the decoded schema dictionary rooted at `[this+0x08]`, `0x590540/0x5905a0`
|
||||
acquire and release refcounted shared schema strings through the global pool, and `0x5908c0`
|
||||
now reads as the live receive/decode state machine serviced by `0x591290` in table states `2/3`.
|
||||
The owner-callback mode split above that runtime is tighter now too: mode `0` comes from the
|
||||
generic append-notify lane `0x590370`, mode `1` from compact upsert `0x590d00`, mode `2` from
|
||||
generic remove-notify `0x590430`, and modes `6/5/3` from the receive/decode state machine
|
||||
`0x5908c0`.
|
||||
The higher transport bring-up split is tighter too: `0x596090` now clearly constructs
|
||||
`[transport+0xba4]` through `0x5905e0` with owner callback `0x595a40`, then seeds the local
|
||||
field-cache family `[transport+0x1724]` through `0x5a08f0` with helper `0x595b60`, and then
|
||||
|
|
@ -83,15 +87,31 @@ Highest-value open edge:
|
|||
`+0x0c/+0x10/+0x18` metadata triplet and the replay modes later consume the pointer through
|
||||
`0x5933a0`. The negative result is stronger too: local text-side xrefs still show no direct
|
||||
store to `[transport+0x1778]`, and a wider sweep also failed to show any obvious `lea`-based
|
||||
replay-band writer. So the sidecar writer remains upstream of this leaf capacity publisher. The
|
||||
replay-band writer. The owned request lifecycle now tightens that further too:
|
||||
`0x593330/0x593370/0x593380/0x5934e0/0x5933a0` fully own `[transport+0x1780]`, `0x1784`, and
|
||||
`0x1788`, while still leaving `[transport+0x1778]` outside that family; the neighboring active
|
||||
opcode reset path `0x5929a0` also only targets `[transport+0x17fc]`. Constructor and teardown
|
||||
passes around `0x596090/0x5961b0/0x5962e0` tighten that negative result further: those owners
|
||||
seed or clear the neighboring replay-band fields while still leaving `[transport+0x1778]`
|
||||
untouched. A full-binary literal-offset sweep tightens it further still: the only direct
|
||||
`0x1778` hit in `RT3.exe` is the read in `0x595bc0`. One nearby ambiguity is now closed too: the
|
||||
mode-`5` mirror path in `0x595a40` and `0x595e10` does not seed `[transport+0x1778]`; it writes
|
||||
`[transport+0x54]` and mirrors the same staged route companion dword only into queue-side slot
|
||||
`[transport+0x1724+0x24]` through `0x005a0940`. So the sidecar writer remains upstream of this
|
||||
leaf capacity publisher. The
|
||||
payload split is tighter too:
|
||||
`0x592ae0` now grounds opcode `2` as a seven-dword descriptor payload with an owned string slot
|
||||
at `+0x08`, so live mode supplies a populated payload while modes `3` and `5` deliberately
|
||||
enqueue an all-zero payload and reuse only the sidecar metadata in the wrapper. Those two modes
|
||||
are tighter now too: they are not generic replay guesses, they are the live receive-state owner
|
||||
callbacks emitted by `0x5911e0 -> 0x5908c0`. So they are best read as delayed metadata replays
|
||||
over one cached work record, not over a standalone custom cache object. The producer side is
|
||||
tighter now too: callback-table attach,
|
||||
over one cached work record, not over a standalone custom cache object. The capacity owner split
|
||||
itself is tighter now too: `0x595bc0` only does real work for modes
|
||||
`0`, `3`, and `5`; the upstream table still delivers modes `1`, `2`, and `6`, but those are
|
||||
explicit no-ops in the capacity leaf. So the callback-owner edge is now effectively closed: the
|
||||
route-callback table feeds the capacity publisher only through the live append lane and the two
|
||||
replay lanes, while the remaining missing piece is still the upstream sidecar producer, not the
|
||||
owner-mode wiring. The producer side is tighter now too: callback-table attach,
|
||||
bound-route requests, selector-text route requests, and the type-`9` text fastpath all stage
|
||||
that same `+0x0c/+0x10/+0x18` triplet through `0x5934e0`, so the capacity replay sidecar is
|
||||
clearly reusing one broader transport work-record family. The generic owner-callback split above that family is tighter now
|
||||
|
|
@ -125,34 +145,52 @@ Highest-value open edge:
|
|||
`0x5904d0` family, not a vague collection clear.
|
||||
The callback-table attach side now
|
||||
constrains the same work-record metadata family a little further too: `0x593650` deliberately
|
||||
duplicates one caller metadata dword into both work fields `+0x0c` and `+0x18`, while preserving
|
||||
the remaining caller callback function pointer in `+0x10`. The lower opcode wrappers are tighter
|
||||
now too: both `0x592a40` and `0x592a70` consume that staged triplet in the order `( callback fn
|
||||
+0x10, callback companion +0x18, drain context id +0x0c )`. So the replay-side triplet is
|
||||
clearly a broader transport callback-wrapper family, not one fixed route-only tuple. The type-`9`
|
||||
text fastpath confirms the same split from the other side too: `0x593d00` only emits the
|
||||
follow-on callback lane when work field `+0x10` is nonnull, and then forwards `(+0x10, +0x18,
|
||||
+0x0c)` into `0x593170` as callback function, callback companion, and trailing drain context.
|
||||
duplicates its first caller metadata dword into both work fields `+0x0c` and `+0x10`, while
|
||||
carrying the second caller metadata dword in `+0x18`. The lower opcode wrappers are tighter now
|
||||
too: `0x592a40` turned out not to be the real 0x08-byte binding leaf at all, but the explicit
|
||||
opcode-`1` trigger wrapper whose constructor is a no-op and whose active-side service is
|
||||
`0x5913c0`. The real lower binding leaf is `0x592c40`, which builds the local `0x08`-byte
|
||||
payload later deep-copied by the explicit opcode-`5` family `0x591540/0x591570/0x591580`. The
|
||||
earlier opcode-`4` reading was just the table-indexing mistake: `0x5928a0` multiplies the pushed
|
||||
selector by `0x10`, so selector `4` lands on the row at `0x5e2044`, not the row at `0x5e2034`.
|
||||
So
|
||||
the replay-side triplet is still a broader transport callback-wrapper family, not one fixed
|
||||
route-only tuple. The type-`9` text fastpath confirms the same split from the other side too:
|
||||
`0x593d00` only emits the follow-on callback lane when work field `+0x10` is nonnull, and then
|
||||
forwards `(+0x10, +0x18, +0x0c)` into `0x593170` as callback function, callback companion, and
|
||||
trailing drain context.
|
||||
The nearby field-subscription side is tighter too: `0x592b50` now clearly uses
|
||||
`[transport+0x1774]` as a cached progress percentage under the `[transport+0xba4]` callback
|
||||
table, and `0x5962e0` seeds that percentage to `1` just before the first immediate mode-`3`
|
||||
snapshot. The nearby route-callback-table lifecycle is tighter now too: `0x596090` seeds
|
||||
`[transport+0xba0]` as the callback-plumbing enable latch, clears staged payload slot
|
||||
`[transport+0xb50]`, and constructs the three owner branches rooted at `[transport+0xba4]`,
|
||||
`[transport+0x1164]`, and `[transport+0x18bc]`. The matching local cleanup is tighter too:
|
||||
`0x5962c0` is the explicit staged route-callback payload clear on `[transport+0xb50]`, while
|
||||
`0x595ce0` now clearly resets only the capacity-descriptor route callback table at
|
||||
`[transport+0x1164]`, not the field-subscription table at `[transport+0xba4]`. The only
|
||||
meaningful gap left on the capacity side is the still-unrecovered writer that stages
|
||||
`[transport+0x1778]`. The carried sidecar fields are no longer anonymous: current evidence now
|
||||
says they are just the same cached callback-wrapper triplet reused by other work-record families,
|
||||
namely drain context id `+0x0c`, callback function `+0x10`, and callback companion `+0x18`.
|
||||
The negative result is stronger now too: the neighboring replay-band fields
|
||||
`[transport+0x176c]`, `[transport+0x1770]`, `[transport+0x1774]`, `[transport+0x177c]`,
|
||||
`[transport+0x1780]`, and `[transport+0x1784]` all have direct local lifecycle owners, but
|
||||
`[transport+0x1778]` still only appears as the single read in `0x595bc0`. So this is no longer
|
||||
a local ownership gap: no local writer is grounded, and the remaining staging path is best read
|
||||
as upstream and indirect rather than one ordinary direct field store in the local text cluster.
|
||||
`[transport+0x1164]`, and `[transport+0x18bc]`; `0x596210` is the recurring service sweep over
|
||||
those same three tables plus the local field-cache and queued-descriptor families; `0x596060`
|
||||
is the explicit `gsi_am_rating` reset; and `0x596530` is the reopen-from-stored-label sibling
|
||||
above that same am-rating table. The matching local cleanup is tighter too: `0x5962c0` is the
|
||||
explicit staged route-callback payload clear on `[transport+0xb50]`, while `0x595ce0` now
|
||||
clearly resets only the capacity-descriptor route callback table at `[transport+0x1164]`, not
|
||||
the field-subscription table at `[transport+0xba4]`. The only meaningful gap left on the
|
||||
capacity side is no longer a local writer search. The carried
|
||||
sidecar fields are no longer anonymous: current evidence now says they are just the same cached
|
||||
callback-wrapper triplet reused by other work-record families, namely drain context id `+0x0c`,
|
||||
callback function `+0x10`, and callback companion `+0x18`. The negative result is stronger now
|
||||
too: the neighboring replay-band fields `[transport+0x176c]`, `[transport+0x1770]`,
|
||||
`[transport+0x1774]`, `[transport+0x177c]`, `[transport+0x1780]`, and `[transport+0x1784]` all
|
||||
have direct local lifecycle owners, but `[transport+0x1778]` still only appears as the single
|
||||
read in `0x595bc0`; even the broader callback-owner lifecycle now skips it while touching those
|
||||
neighbors, including bring-up `0x596090`, recurring service `0x596210`, am-rating reset/reopen
|
||||
`0x596060/0x596530`, teardown `0x5961b0`, and field-subscription open `0x5962e0`. The
|
||||
constructor now closes that negative result further: `0x58dc50` bulk-zeroes the full transport
|
||||
body and still never seeds `[transport+0x1778]` before later explicit neighbor initialization.
|
||||
So this edge is now locally closed: no writer is grounded anywhere in `RT3.exe`, and the
|
||||
remaining staging path is best read as an upstream callback or worker handoff rather than one
|
||||
missing ordinary field store in the local text cluster. The callback-binding family at
|
||||
`0x5934e0 -> 0x593650 -> 0x58f2f0` now gives the cleanest local boundary for that claim:
|
||||
RT3 stages and later consumes the shared work-record metadata triplet, but the sidecar itself
|
||||
still appears only as a borrowed cached pointer at `0x595bc0`, never as a locally seeded
|
||||
replay-band field.
|
||||
One adjacent staged-route callback is
|
||||
tighter now too: `0x595860` is the submit-result handler below
|
||||
`0x5958e0`, using the already-grounded third selector-generation counter at `[transport+0xac0]`
|
||||
|
|
@ -212,10 +250,10 @@ Highest-value open edge:
|
|||
gate and the same queued fallback, mode `2` removes pending descriptors from the queued family,
|
||||
mode `3` forces mode `2` when the primary-endpoint table is empty, mode `4` updates the deferred
|
||||
route-status state around `[this+0x1ed4]` and `[this+0x1ed8]`, and mode `5` copies the staged
|
||||
route companion dword at `[this+0x490]` into `[this+0x54]` while mirroring that value into the
|
||||
local field-cache family. The route-event dispatcher side is cleaner too:
|
||||
route companion dword at `[this+0x490]` into `[this+0x54]` while mirroring that value into
|
||||
queue-side slot `[this+0x1724+0x24]` through `0x005a0940`. The route-event dispatcher side is cleaner too:
|
||||
the mode-`5` tails in both callback families do not copy a descriptor-local field. They mirror the
|
||||
transport-staged companion dword at `[this+0x490]` into `[this+0x54]` and the local field-cache
|
||||
family instead. The `gsi_am_rating` maintenance lane is tighter now too: it sorts the
|
||||
transport-staged companion dword at `[this+0x490]` into `[this+0x54]` and that same queue-side
|
||||
slot instead, not into `[this+0x1778]`. The `gsi_am_rating` maintenance lane is tighter now too: it sorts the
|
||||
primary-endpoint descriptor table through `0x590310` in mode `1` with key `gsi_am_rating`, then
|
||||
selects the new head through `0x590480` rather than treating the table as pure insertion order.
|
||||
|
|
|
|||
|
|
@ -125,17 +125,19 @@ What this note is for:
|
|||
- Route-style peer-link emission and route-link state
|
||||
- Track-laying-capacity interactions with route synthesis
|
||||
|
||||
Highest-value open edge:
|
||||
Latest local closure:
|
||||
|
||||
- The remaining semantic edge here is now mostly about ranking, not company pressure:
|
||||
the pre-`1.03` versus `1.03+` tracker metric split still looks meaningful for the weighted
|
||||
linked-transit site-score cache at `company_rebuild_linked_transit_autoroute_site_score_cache`
|
||||
`0x00407bd0` and the seeded peer-route chooser at `company_build_linked_transit_autoroute_entry`
|
||||
`0x00408380`, but current evidence says the company train-pressure target still sums raw site
|
||||
cache `+0x12` rather than the weighted lanes fed by step count and continuity. The lower
|
||||
route-entry pair metric still has other grounded consumers such as the initial route sweep and
|
||||
the train route validator through `0x004a6630`, but no downstream consumer of the cached weighted
|
||||
site lanes `+0x0e/+0x16` is currently grounded beyond the autoroute selector `0x00408280` and
|
||||
builder `0x00408380`. So the remaining question here is no longer about current local ownership;
|
||||
it is only whether some still-ungrounded consumer outside the current corpus also reads those
|
||||
cached weighted lanes.
|
||||
- The linked-transit cache split is now locally bounded rather than just suggestive:
|
||||
the pre-`1.03` versus `1.03+` tracker metric dispatcher still matters for the weighted
|
||||
autoroute cache lanes at `company_rebuild_linked_transit_autoroute_site_score_cache`
|
||||
`0x00407bd0`, but the grounded downstream chain now stops at route choice rather than company
|
||||
pressure. Cache lane `+0x16` feeds the owned-site selector
|
||||
`company_select_best_owned_linked_transit_site_by_autoroute_score` `0x00408280`, and the
|
||||
weighted peer-side bands then feed `company_build_linked_transit_autoroute_entry`
|
||||
`0x00408380`; train-side append and add-train helpers `0x00409770` and `0x00409830` only
|
||||
inherit that weighted choice by calling the builder. By contrast, the company-wide roster target
|
||||
at `company_compute_owned_linked_transit_site_score_total` `0x00408f70` still sums only raw
|
||||
cache `+0x12` before `company_balance_linked_transit_train_roster` `0x00409950` reacts. So this
|
||||
local semantic edge is effectively closed: the compatibility split perturbs ranked site choice
|
||||
and seeded autoroute peer choice, not the later train-count target, and any further
|
||||
weighted-lane consumer would have to live outside the currently grounded local chain.
|
||||
|
|
|
|||
|
|
@ -129,12 +129,11 @@ What this note is for:
|
|||
- Track-lay and station-placement semantics
|
||||
- Train detail and trainbuy command ownership
|
||||
|
||||
Highest-value open edge:
|
||||
Current bounded conclusion:
|
||||
|
||||
- Whether any later gameplay-owned mode introduces a distinct input or frame owner for ordinary
|
||||
world interaction, or whether the grounded `shell_controller_window_message_dispatch` plus
|
||||
- Current evidence points to one stable reading: no later gameplay-owned mode introduces a distinct
|
||||
outer input or frame owner for ordinary world interaction. The grounded
|
||||
`shell_controller_window_message_dispatch` plus
|
||||
`simulation_frame_accumulate_and_step_world` path remains the sole coordinator after world
|
||||
entry and the first grounded non-camera world-input coordinator. Current evidence now points
|
||||
strongly to the second reading: the shell-fed input and frame path remains the only grounded
|
||||
coordinator after world entry, and no separate outer gameplay loop or gameplay-only input object
|
||||
is grounded in the local corpus.
|
||||
entry and the first grounded non-camera world-input coordinator, and no separate outer
|
||||
gameplay loop or gameplay-only input object is grounded in the local corpus.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ What this note is for:
|
|||
- File-flow and content-load boundaries
|
||||
- World bring-up versus shell-owned cadence questions
|
||||
|
||||
Highest-value open edge:
|
||||
Current bounded conclusion:
|
||||
|
||||
- Whether any long-lived gameplay cadence later escapes the bootstrap-owned shell service loop
|
||||
entirely, or only nests under it.
|
||||
- No later long-lived gameplay cadence is currently grounded outside the bootstrap-owned shell
|
||||
service loop; the local evidence still supports the nested-under-shell reading.
|
||||
|
|
|
|||
|
|
@ -103,15 +103,18 @@ transition.
|
|||
|
||||
## Map and Scenario Content Load
|
||||
|
||||
- Roots: `shell_map_file_entry_coordinator` at `0x00445ac0`, the first grounded world-entry branch
|
||||
- Roots: `shell_map_file_entry_coordinator` at `0x00445ac0`, the larger active-mode profile owner
|
||||
`shell_active_mode_run_profile_startup_and_load_dispatch` at `0x00438890`, the shell-mode
|
||||
switcher `shell_transition_mode` at `0x00482ec0`, the first grounded world-entry branch
|
||||
`world_entry_transition_and_runtime_bringup` at `0x00443a50`,
|
||||
`shell_map_file_world_bundle_coordinator` at `0x00445de0`, reference-database setup via
|
||||
`map_bundle_open_reference_databases` at `0x00444dd0`, and narrower loaders such as
|
||||
`map_load_geographic_label_database` and `map_load_city_database`.
|
||||
- Trigger/Cadence: shell tutorial launch, editor or detail-panel file actions through `fileopt.win`,
|
||||
map-scenario open paths, and scenario-text export batch commands.
|
||||
- Key Dispatchers: `shell_map_file_entry_coordinator`, `world_entry_transition_and_runtime_bringup`,
|
||||
`world_runtime_release_global_services`, `shell_map_file_world_bundle_coordinator`,
|
||||
- Key Dispatchers: `shell_map_file_entry_coordinator`,
|
||||
`shell_active_mode_run_profile_startup_and_load_dispatch`, `shell_transition_mode`,
|
||||
`world_entry_transition_and_runtime_bringup`, `world_runtime_release_global_services`, `shell_map_file_world_bundle_coordinator`,
|
||||
`map_bundle_open_reference_databases`, `map_load_geographic_label_database`,
|
||||
`map_load_city_database`, `scenario_text_export_build_language_file`,
|
||||
`scenario_text_export_report_language_file`, `scenario_text_export_batch_process_maps`.
|
||||
|
|
@ -137,7 +140,188 @@ transition.
|
|||
mode `11` is tighter now too: it still maps to `.gmt`, but instead of looking like another
|
||||
gameplay save family it conditionally diverts into the same `.gmt` preview-surface pipeline owned
|
||||
by the Multiplayer preview dataset object at `0x006cd8d8`, and only falls back to the normal
|
||||
reference-bundle path when that dataset object is absent.
|
||||
reference-bundle path when that dataset object is absent. The shell-side mode owner above those
|
||||
file coordinators is clearer now too. `shell_transition_mode` no longer reads like a generic mode
|
||||
switch: its constructor jump table now resolves mode `1` to `Game.win`, mode `2` to `Setup.win`,
|
||||
mode `3` to `Video.win`, mode `4` to `LoadScreen.win`, mode `5` to `Multiplayer.win`, mode `6`
|
||||
to `Credits.win`, and mode `7` to `Campaign.win`. The strongest current load-side owner inside
|
||||
that table remains the mode-`4` branch around `0x4830ca`, which publishes the new active mode
|
||||
object into `0x006cec78` and then calls `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
as `thiscall(active_mode, 1, 0)`. The caller split above that owner is tighter now too:
|
||||
`world_entry_transition_and_runtime_bringup` reaches the same owner at `0x443b57` with `(0, 0)`
|
||||
after dismissing the current shell detail panel and servicing `0x4834e0(0, 0)`, while the
|
||||
saved-runtime path at `0x446d7f` does the same before immediately building the `.smp` bundle
|
||||
payloads through `0x530c80/0x531150/0x531360`. That makes the `LoadScreen.win` startup lane the
|
||||
only currently grounded caller that enters `0x438890` with `(1, 0)` instead of `(0, 0)`. The
|
||||
internal selector split in `0x438890` is tighter now too: `[0x006cec7c+0x01]` is a separate
|
||||
seven-way startup selector, not the shell mode id. Values `1` and `7` load `Tutorial_2.gmp` and
|
||||
`Tutorial_1.gmp`, values `3/5/6` collapse into the same profile-seeded file-load lane through
|
||||
`0x445ac0([0x006cec7c]+0x11, 4, &out_success)`, value `2` is a world-root initialization lane
|
||||
that allocates `0x0062c120` and then forces selector `3`, and value `4` is the setup-side world
|
||||
reset or regeneration lane that rebuilds `0x0062c120` from `0x006d14cc/0x006d14d0` before later
|
||||
world setup continues. The write side is tighter now too: `Campaign.win` writes selector `6`,
|
||||
`Multiplayer.win` writes selector `3` on one pending-status path, and the larger `Setup.win`
|
||||
dispatcher writes selectors `2`, `3`, `4`, and `5` on its validated launch branches. That makes
|
||||
the file-load subfamily read less like one generic save-open branch and more like a shared
|
||||
profile-file lane reused by setup, multiplayer, and campaign owners. The world-entry owner
|
||||
boundary is tighter now too: `world_entry_transition_and_runtime_bringup` at `0x00443a50` no
|
||||
longer stops at the initial shell transition and world allocation head. The same grounded
|
||||
function continues through the larger post-load generation tail up to `0x00444dc2`, which means
|
||||
the later `Setting up Players and Companies...` and neighboring post-load passes are not
|
||||
floating raw callsites after all. That same owner now clearly covers the event-runtime refresh
|
||||
through `0x433130`, chairman-profile materialization through `0x437220`, neighboring route and
|
||||
tracker refresh families, and the one-shot kind-`8` runtime-effect service through `0x432f40`
|
||||
before clearing shell-profile latch `[0x006cec7c+0x97]`. The `Setup.win` dispatcher
|
||||
is less opaque now too: the early `0x0bc1..0x0c24` family is mostly fixed submode selection above
|
||||
`0x00502c00`, except for the separate `0x0bc2/0x0bc5/0x0bc6/0x0bc7` shell-open quartet above
|
||||
`0x00501f20`; `0x0c1f` is the settings-window escape; `0x0c1e/0x0c20/0x0c22` are direct shell
|
||||
requests into `0x00482150`; the fixed submode buttons now have concrete lower targets such as
|
||||
`0x0bc1/0x0bc8 -> 15`, `0x0bc3 -> 16`, `0x0bc4 -> 1`, `0x0c80 -> 13`, `0x0c1c -> 17`,
|
||||
`0x0c1d -> 2`, `0x0c24 -> 14`, `0x0c81 -> 14`, `0x0c82 -> 6`, `0x0c83 -> 10`, and
|
||||
`0x0c84 -> 12`; the
|
||||
`0x0ce6/0x0ce7/0x0d49/0x0d4a/0x0e82/0x0e83` branches are bounded list or slider adjustments on
|
||||
staged setup fields; and the later `0x0dca/0x0dcb/0x0de9/0x0df3/0x0e81/0x0f6f/0x0f70` controls
|
||||
are the explicit selector-writing launch buttons rather than one anonymous validated-launch blob.
|
||||
The constructor-side callbacks are tighter too: control `0x0ce8` is a table-driven payload-label
|
||||
draw callback above `0x00502030`, not another launch root, and controls `0x0e86` and `0x0e87`
|
||||
do not select more setup roots; they update the persisted shell-state selector pairs at
|
||||
`[0x006cec74+0x233/+0x237]` and `[0x006cec74+0x23b/+0x23f]` through `0x00502160` and
|
||||
`0x005021c0`, then immediately save config through `0x00484910(1)`. The constructor body is
|
||||
tighter too: it seeds the initial `Setup.win` state by running `0x502910`, `0x502550`, and
|
||||
`0x502c00(1)` before the user interacts with the window, installs `0x0c80..0x0c86` and
|
||||
`0x0f6f..0x0f71` as homogeneous button bands, and treats `0x0e88` as one separate special
|
||||
control with retuned float fields rather than another ordinary launch root. The remaining
|
||||
optional constructor child `0x0bd0` is tighter now too: it is built from a separate template
|
||||
block and optional owned heap object before registration, not another hidden setup-root button.
|
||||
The generic shell helper layer beneath that constructor is tighter now too: `0x53fa50` is the
|
||||
shared resource-bind and child-list initialization helper, `0x53f830` is the child-control
|
||||
lookup-by-id helper over the intrusive list at `[this+0x70]`, `0x558130` is the child-control
|
||||
finalizer that stamps the owner pointer and resolves localized captions before the control goes
|
||||
live, and `0x53f9c0` is the ordered child-control registration helper used by the optional
|
||||
`0x0bd0` branch and other shell dialogs.
|
||||
The submode selector itself is tighter now too because its button-state lane is mostly decoded:
|
||||
`0xbc5` tracks submode `15`, `0xbc6` tracks `16`, `0xbba` tracks `2`, `0xbbb` tracks `3`,
|
||||
`0xbc3` tracks `13`, `0xbbc` groups `3/4/5`, `0xbbd` tracks `4`, `0xbbe` tracks `5`, `0xbbf`
|
||||
groups `6/12/14`, `0xbc0` tracks `7`, `0xbc1` tracks `8`, `0xbc2` tracks `9`, `0xe75` tracks
|
||||
`10`, and `0xf6e` tracks `17`. RT3.lng and the setup art families now also make several of those
|
||||
top-level roots read less like anonymous ids and more like real menu panels: submode `1` is the
|
||||
strongest current landing-panel fit, `2` is the `Single Player` root, `7/8/9` are the
|
||||
`Editor` / `New Map` / `Load Map` family, `15` is `Extras`, and `17` is `Tutorial`. By
|
||||
elimination against the separate shell `Credits.win` mode, the remaining top-level `Setup.win`
|
||||
roots now most safely read as `13 = Multi Player` and `16 = High Scores`.
|
||||
The file-backed side is tighter too: `0x502c00` now maps submodes exactly as `4 -> dataset 5`,
|
||||
`9 -> 4`, `6 -> 8`, `10 -> 6`, `12 -> 10`, and `14 -> 9`, so the saved-game-backed family is
|
||||
no longer one blurred list pane but a small set of stable dataset variants above
|
||||
`0x4333f0/0x4336a0`.
|
||||
The file-backed header split is tighter too: `0x5027b0` now maps the top header-control ids as
|
||||
`4 -> 0xd4b`, `9 -> 0xde8`, `6/12/14 -> 0xdf2`, and `10 -> 0xe85`. RT3.lng closes one earlier
|
||||
mistake here: those values are local setup control or resource ids, not localized text ids.
|
||||
The setup art bundle now tightens that family split one step further too: under `rt3_2WIN.PK4`
|
||||
the distinct file-backed setup art families are the shared `Setup_Load` lane and the separate
|
||||
`Setup_Sandbox` lane, which matches the selector-side evidence that mode `10` is the
|
||||
sandbox-backed `new` list while mode `12` is its `load` sibling. Combined with the builder at
|
||||
`0x4333f0`, that shows only submodes `4` and `10` using the alternate localized-stem list-label
|
||||
path; `6`, `9`, `12`, and `14` keep the direct filename-normalization lane.
|
||||
The grouped `3/4/5` family is narrower now too: `0x0ce5` is no longer part of it, because that
|
||||
control writes selector `3` and then selects submode `9` as the `Load Map` sibling. The nearby
|
||||
`0x0dcb` branch instead conditionally selects submode `5` or `3`, which keeps `3` as the
|
||||
strongest current `New Game` / `Options` companion and `5` as the strongest current `Sandbox`
|
||||
companion. The file-backed single-player side is tighter in the same way now: modes `4` and `10`
|
||||
are the only siblings using the alternate localized-stem row-label path, so they now read most
|
||||
safely as the two setup-local template or profile list variants rather than ordinary save lists.
|
||||
Mode `10` is the stronger one of the pair because neighboring validated launch control `0x0e81`
|
||||
both routes into selector `5` and sets sandbox byte `[0x006cec7c+0x82] = 1`, which makes it the
|
||||
strongest current fit for the setup-local `New Sandbox` list. The distinct `Setup_Sandbox` art
|
||||
family in `rt3_2WIN.PK4` now reinforces that same split one step further, which makes mode `12`
|
||||
the strongest closed fit for the paired `Load Sandbox` lane; mode `4` is therefore the strongest
|
||||
remaining non-sandbox peer in that same pair and now most safely reads as the setup-local `New
|
||||
Scenario`-style chooser. Modes `6`, `12`, and `14` now tighten one step further as the three
|
||||
selector-`3` direct-filename setup-local `load` siblings because they stay on the direct
|
||||
filename-normalization lane, share the same `0xdf2` header family, and clear the same
|
||||
presence-style latch `[0x006cec7c+0x97]`; mode `14` is the strongest current landing panel for
|
||||
that cluster because `0x0c24` jumps to it directly while `0x0c82` and `0x0c84` only reach the
|
||||
sibling modes `6` and `12` from inside the same load family. Current control-pairing and
|
||||
setup-art evidence now make `12 = Load Sandbox` the strongest closed per-submode assignment. The
|
||||
remaining non-sandbox pair is closed now too: the deeper bundle filter at `0x433260`
|
||||
distinguishes dataset `9` from dataset `8` by one extra nonzero payload-flag family, and the
|
||||
editor-side metadata path now grounds `[0x006cec78+0x66de]` as the direct `Campaign Scenario`
|
||||
checkbox bit because `editorDetail.win` ties control `0x5b6e` to localized ids `3160/3161`.
|
||||
That makes dataset `9` the campaign-designated load family and dataset `8` the ordinary scenario
|
||||
load family, so `14 = Load Campaign` and `6 = Load Scenario` now read as grounded rather than
|
||||
residual. `0x502910` is
|
||||
tighter in a
|
||||
corrective way too: it is not a mode-`3`-only helper after all, but the shared non-file-backed
|
||||
payload panel that formats
|
||||
`0xcf3/0xcf4/0xcf5/0xd4f`, mirrors option byte `[0x006cec7c+0x7d]` into `0x0ce9..0x0ced`,
|
||||
rebuilds row host `0x0ce8` from payload bytes `[+0x31b/+0x31c]`, and mirrors live row markers
|
||||
into `[0x006cec7c+0x87]`. That makes mode `3` just one user of the shared payload-driven panel,
|
||||
not the sole owner of it.
|
||||
The payload-helper cluster under that panel is tighter now too: `0x502220` does not just
|
||||
republish labels and the preview surface. It first re-enters
|
||||
`shell_setup_load_selected_profile_bundle_into_payload_record` `0x442400`, which clears one full
|
||||
`0x100f2`-byte setup payload record, builds a rooted path from the staged profile stem, opens the
|
||||
selected bundle through `0x530c80`, and then reads either the ordinary saved-profile chunk family
|
||||
or the map-style chunk family through `0x531150/0x531360` depending on the selected extension
|
||||
shape. Only after that does `0x502220` copy payload fields `+0x14/+0x3b2/+0x3ba/+0x20` into the
|
||||
staged runtime profile through `0x47be50`, which in turn normalizes the payload category bytes at
|
||||
`[payload+0x31a + row*9]` and the local marker-slot bytes at `[payload+0x2c9..]` through
|
||||
`0x47bc80`. The adjacent region-side worker family is tighter in a negative way too: the setup
|
||||
payload loader is now clearly separate from the broader region-building and placement cluster
|
||||
around `0x422320..0x423d30`, whose current grounded helpers now include `0x422320`, `0x4228b0`,
|
||||
`0x422900`, `0x422a70`, `0x422be0`, `0x422ee0`, `0x4234e0`, `0x4235c0`, and
|
||||
`world_region_refresh_cached_category_totals_and_weight_slots` `0x423d30` rather than one hidden
|
||||
setup-only panel. The leading region helper is no longer unnamed either: `0x422320` is now
|
||||
bounded as the recurring cached-structure-scalar normalization pass that writes
|
||||
`[region+0x2e2/+0x2e6/+0x2ea/+0x2ee]`, while `0x422a70` is the shared placement-validation and
|
||||
commit gate beneath both the per-region worker and one second world-side placement loop. The
|
||||
neighboring `0x4234e0` accessor is tighter too: it is the shared projected structure-count scalar
|
||||
query by category that later feeds both the per-region placement family and the map-editor city
|
||||
count stats report. So the remaining gap is no longer “what are these setup payload helpers
|
||||
doing,” but only how aggressive we want to be when naming the last top-level setup roots from
|
||||
mostly RT3.lng and asset-side evidence.
|
||||
The adjacent summary helper is tighter too: `0x502550` is not another hidden submode owner. It
|
||||
republishes the staged path tail in `0xe7f`, the scalar summary in `0xe84`, the two persisted
|
||||
selector lists in `0xe86/0xe87`, and the config toggles in `0xe88/0xe89/0xe8a`.
|
||||
The remaining top-level gap is cleaner now too: submode `16` still has no distinct downstream
|
||||
helper or launch branch in the local selector/refresh family, but it is no longer a blank bucket.
|
||||
Current evidence now bounds it as the `0x0bc3 -> 16` top-level button whose visual-state lane is
|
||||
surfaced through control `0xbc6`, and the strongest residual fit is `High Scores` because
|
||||
`Credits` already lives in separate shell mode `6`.
|
||||
The validated launch lane is tighter now too: it no longer just writes selector `3` or `5` as
|
||||
one undifferentiated blob, and it no longer includes `0x0ce5` or `0x0dcb`. `0x0ce5` is the
|
||||
`Load Map` selector because it writes selector `3` and then selects submode `9`, while `0x0dcb`
|
||||
is the later conditional `5/3` companion-selector sibling rather than a file launch. The actual
|
||||
validated staged-profile lane is now bounded more narrowly: `0x0cf6` and `0x0e81` are the
|
||||
selector-`5` siblings, while the neighboring selector-`3` validated lane is at least shared by
|
||||
`0x0de9` and `0x0df3`; the shared bridge at `0x4425d0` then forces `[0x006cec7c+0xc5] = 1`,
|
||||
mirrors payload bytes into `[profile+0xc4]`, `[profile+0x7d]`, and `[profile+0xc6..+0xd5]`, and
|
||||
only then issues shell request `0x0cc`. Only `0x0e81` also sets `[0x006cec7c+0x82] = 1`, which
|
||||
is currently the strongest sandbox-side anchor beneath the later `.gmx` load family.
|
||||
That launch band is slightly tighter now too: `0x0dca` is the grayscale-map picker branch above
|
||||
the `TGA Files (*.tga)` / `.\Data\GrayscaleMaps` helper at `0x004eb0b0`, not another ordinary
|
||||
save-file validator. That launch band is tighter in a second way too: `0x0ddf` is not a lobby
|
||||
branch after all, but the separate windowed-mode-gated grayscale-heightmap generation path. It
|
||||
reopens the `TGA Files (*.tga)` / `.\Data\GrayscaleMaps` picker through `0x0042a970`, validates
|
||||
the chosen image through `0x005411c0`, and only then writes startup selector `2`. The fixed-
|
||||
button side is tighter too: submode `15` now aligns with the `Extras` family because it sits
|
||||
above the Readme/Weblinks shell-open quartet and the return-to-extras sibling, while submode
|
||||
`17` now aligns with the tutorial chooser because it is the only branch that later exposes
|
||||
startup selectors `1` and `7`, which RT3 uses for `Tutorial_2.gmp` and `Tutorial_1.gmp`. The
|
||||
map-root family is tighter too: submodes `7/8/9` are the only setup branch that flips the file
|
||||
root into
|
||||
`maps\\*.gm*`, with mode `8` specifically the grayscale-heightmap picker and mode `9` the
|
||||
file-backed map-list sibling.
|
||||
Submode `13` is slightly tighter now too: current local evidence shows that it stays outside
|
||||
both the dedicated file-backed pane and the mode-`3` option-heavy pane, so it is best read for
|
||||
now as one top-level non-file-backed setup branch rather than another saved-game list variant.
|
||||
The setup-side file roots are tighter now too: the shared file-list builder at `0x4333f0`
|
||||
formats either `saved games\\*.smp` or `maps\\*.gm*` from the shell-state fields at
|
||||
`[0x006cec74+0x68/+0x6c]`, with the separate `data\\tutorial` root only appearing on the
|
||||
tutorial-side `[shell+0x6c] == 2` branch. That means the `Setup.win` submode families are no
|
||||
longer one generic file pane: the ordinary setup-backed `.smp` family is broader than the
|
||||
narrower file-list pane, because modes `3/4/5/6/10/12/14` stay on the ordinary saved-game side
|
||||
while only `4/6/9/10/12/14` re-enter the dedicated file-list panel at `0x5027b0`; the `maps`
|
||||
branch is the narrower setup or tutorial launch family above the same shared record builder.
|
||||
- Evidence: function-map map/scenario rows, analysis-context exports for `0x00445ac0`, `0x00445de0`,
|
||||
`0x00443a50`, `0x00434300`, and `0x00444dd0`, plus objdump string and mode-table evidence for
|
||||
`.gmp`, `.gmx`, `.gmc`, `.gms`, `.gmt`, `.smp`, `Quicksave`, the `0x004dd010` mode table at
|
||||
|
|
@ -276,6 +460,10 @@ transition.
|
|||
callback `0x595bc0`, while
|
||||
`multiplayer_transport_route_callback_table_service_receive_decode_state_machine` `0x5908c0`
|
||||
is the current live receive/decode state machine serviced by `0x591290` in table states `2/3`.
|
||||
The callback-owner mode split above that runtime is now explicit too: append-notify `0x590370`
|
||||
publishes mode `0`, compact upsert `0x590d00` publishes mode `1`, remove-notify `0x590430`
|
||||
publishes mode `2`, and the live receive/decode path `0x5908c0` publishes modes `6`, `5`, and
|
||||
`3`.
|
||||
The route-handle lifecycle above that decode path is tighter now too: `0x590740` cleanly resets
|
||||
one table's live route plus decode-side runtime without destroying the outer object; `0x5907a0`
|
||||
is the broader destroy path that also releases the active descriptor collection; `0x590ed0`
|
||||
|
|
@ -303,8 +491,18 @@ transition.
|
|||
in `0x595bc0` reads the same `+0x0c/+0x10/+0x18` metadata triplet and replay modes later consume
|
||||
the pointer through `0x5933a0`. The negative result is stronger too: local text-side xrefs still
|
||||
show no direct store to `[transport+0x1778]`, and a wider local sweep also failed to show any
|
||||
obvious `lea`-based replay-band writer, so the sidecar writer remains upstream of this leaf
|
||||
publisher. Mode `0` is now also tied more cleanly to the generic descriptor append-notify lane
|
||||
obvious `lea`-based replay-band writer. The transient-request lifecycle tightens that further:
|
||||
`0x593330/0x593370/0x593380/0x5934e0/0x5933a0` now fully bound `[transport+0x1780]`, `0x1784`,
|
||||
and `0x1788` without ever touching `[transport+0x1778]`, and the neighboring active-opcode reset
|
||||
helper `0x5929a0` is likewise scoped only to `[transport+0x17fc]`. A broader constructor and
|
||||
teardown pass tightened that further too: `0x596090`, `0x5961b0`, and `0x5962e0` all touch the
|
||||
neighboring replay-band fields without ever seeding `[transport+0x1778]`. A full-binary
|
||||
literal-offset sweep tightens it further still: the only direct `0x1778` hit in `RT3.exe` is
|
||||
the read in `0x595bc0`. One nearby ambiguity is now closed too: the
|
||||
mode-`5` mirror path in `0x595a40` and `0x595e10` does not target `[transport+0x1778]`; it
|
||||
writes `[transport+0x54]` and mirrors the same staged route companion dword only into queue-side
|
||||
slot `[transport+0x1724+0x24]` through `0x005a0940`. So the sidecar writer remains upstream of this
|
||||
leaf publisher. Mode `0` is now also tied more cleanly to the generic descriptor append-notify lane
|
||||
at `0x590370`, while mode `2` stays outside this helper as the separate
|
||||
remove-notify-and-stage path at `0x590430`. The opcode-`2` payload boundary is tighter too:
|
||||
`0x592ae0` now grounds that payload
|
||||
|
|
@ -314,6 +512,10 @@ transition.
|
|||
receive-state owner callbacks emitted by `0x5911e0 -> 0x5908c0`, not loose generic replay
|
||||
guesses. So those paths are better read as delayed metadata replays over one cached work record,
|
||||
not over a separate anonymous cache blob. The neighboring
|
||||
capacity-owner split is tighter now too: `0x595bc0` only stages descriptor records for modes
|
||||
`0`, `3`, and `5`; the upstream route-callback-table owner still delivers modes `1`, `2`, and
|
||||
`6`, but those are explicit no-ops in this capacity leaf. So the owner wiring itself is no
|
||||
longer the open edge; only the upstream sidecar producer remains unresolved. The neighboring
|
||||
work queue is tighter too: `0x593330/0x593370/0x593380` now bound `[transport+0x1780]` as the
|
||||
construct/clear/destroy owner family, while `0x5933a0`, `0x5934e0`, and `0x593570` ground the
|
||||
remove, allocate, and completion side over that same collection. The small sibling `0x593400` is
|
||||
|
|
@ -335,11 +537,13 @@ transition.
|
|||
remove-notify-and-stage lane, `0x590490` releases the staged list, and `0x5904d0` releases the
|
||||
active descriptor collection before tearing that staged list down. That also makes the earlier
|
||||
`0x5962e0` “release active descriptors” step explicit. The callback-table attach side now constrains the
|
||||
same work-record metadata family a little further too: `0x593650` deliberately duplicates one
|
||||
caller metadata dword into both fields `+0x0c` and `+0x18`, while preserving the remaining
|
||||
caller callback function pointer in `+0x10`. The lower opcode wrappers are tighter now too:
|
||||
`0x592a40` and `0x592a70` both consume that staged triplet in the order `( callback fn +0x10,
|
||||
callback companion +0x18, drain context id +0x0c )`. The producer side is tighter too:
|
||||
same work-record metadata family a little further too: `0x593650` deliberately duplicates its
|
||||
first caller metadata dword into both fields `+0x0c` and `+0x10`, while carrying the second
|
||||
caller metadata dword in `+0x18`. The lower opcode wrappers are tighter now too: `0x592a40`
|
||||
turned out to be the explicit opcode-`1` trigger wrapper whose constructor is a no-op and whose
|
||||
active-side service is `0x5913c0`, while `0x592c40` is the real `0x08`-byte explicit opcode-`5`
|
||||
binding leaf. The earlier opcode-`4` read was just the table-indexing mistake in `0x5928a0`:
|
||||
selector `4` lands on the row at `0x5e2044`, not the row at `0x5e2034`. The producer side is tighter too:
|
||||
bound-route requests, selector-text route requests, and the type-`9` text fastpath also stage
|
||||
that same triplet through `0x5934e0`, and the fastpath shim `0x593d00` now gives the cleanest
|
||||
proof of the callback split by only re-emitting the follow-on lane when `+0x10` is nonnull and
|
||||
|
|
@ -351,19 +555,29 @@ transition.
|
|||
`1` just before the first immediate mode-`3` snapshot. The nearby route-callback-table
|
||||
lifecycle is tighter now too: `0x596090` seeds `[transport+0xba0]` as the callback-plumbing
|
||||
enable latch, clears staged payload slot `[transport+0xb50]`, and constructs the three owner
|
||||
branches rooted at `[transport+0xba4]`, `[transport+0x1164]`, and `[transport+0x18bc]`. The
|
||||
branches rooted at `[transport+0xba4]`, `[transport+0x1164]`, and `[transport+0x18bc]`;
|
||||
`0x596210` is the recurring service sweep over those same three tables plus the local field
|
||||
cache and queued-descriptor family; `0x596060` is the explicit `gsi_am_rating` reset; and
|
||||
`0x596530` is the reopen-from-stored-label sibling above that same am-rating table. The
|
||||
matching local cleanup is tighter too: `0x5962c0` is the explicit staged route-callback payload
|
||||
clear on `[transport+0xb50]`, while `0x595ce0` now clearly resets only the capacity-descriptor
|
||||
route callback table at `[transport+0x1164]`, not the field-subscription table at
|
||||
`[transport+0xba4]`. The remaining gap on the capacity side is
|
||||
therefore narrower: mainly the still-unrecovered writer that stages `[transport+0x1778]`. The
|
||||
carried sidecar fields themselves now read more cleanly as the cached callback-wrapper triplet
|
||||
reused elsewhere (`drain context id +0x0c`, `callback fn +0x10`, `callback companion +0x18`),
|
||||
and the negative result is stronger too: nearby replay-band fields `[transport+0x176c]`,
|
||||
`[transport+0xba4]`. The remaining gap on the capacity side is therefore narrower: the carried
|
||||
sidecar fields themselves now read more cleanly as the cached callback-wrapper triplet reused
|
||||
elsewhere (`drain context id +0x0c`, `callback fn +0x10`, `callback companion +0x18`), and the
|
||||
negative result is stronger too: nearby replay-band fields `[transport+0x176c]`,
|
||||
`[transport+0x1770]`, `[transport+0x1774]`, `[transport+0x177c]`, `[transport+0x1780]`, and
|
||||
`[transport+0x1784]` all have direct local owners while `[transport+0x1778]` still appears only
|
||||
as the single read in `0x595bc0`. So the remaining writer looks upstream and indirect rather
|
||||
than like one missing ordinary field store in the local cluster. The adjacent staged-route
|
||||
as the single read in `0x595bc0`; even the broader callback-owner lifecycle now skips it while
|
||||
seeding, servicing, resetting, reopening, or tearing down those neighboring tables and caches.
|
||||
The constructor now closes that local search further: `0x58dc50` bulk-zeroes the full transport
|
||||
body and still never writes a nonzero value into `[transport+0x1778]` before later explicit
|
||||
neighbor initialization. The callback-binding owner stack now tightens that boundary too:
|
||||
`0x5934e0` stages the shared work-record metadata triplet, `0x593650` binds it into the
|
||||
callback-table worker path, and `0x593570` later consumes and republishes it, while
|
||||
`[transport+0x1778]` still appears only as the borrowed sidecar read in `0x595bc0`. So this
|
||||
edge is now locally closed, and the remaining producer looks like an upstream callback or worker
|
||||
handoff rather than one missing ordinary field store in the local cluster. The adjacent staged-route
|
||||
callback side is tighter too: `0x595860` is now bounded as the
|
||||
submit-result handler beneath `0x5958e0`, and the old `[transport+0xac0]` ambiguity there is now
|
||||
gone. That branch is using the already-grounded third selector-generation counter at `[0xac0]`
|
||||
|
|
@ -424,7 +638,7 @@ transition.
|
|||
callback-marshaling wrappers `0x591480` and `0x591510`, not read through any dedicated semantic
|
||||
accessor yet. The route-event dispatcher side is tighter too: the mode-`5` tails in both
|
||||
callback families do not copy a descriptor-local field but instead mirror the transport-staged
|
||||
companion dword at `[this+0x490]` into `[this+0x54]` and the local field-cache family. The
|
||||
companion dword at `[this+0x490]` into `[this+0x54]` and queue-side slot `[this+0x1724+0x24]`. The
|
||||
`gsi_am_rating` maintenance lane is tighter now too: after pruning failed descriptors it sorts
|
||||
the surviving primary-endpoint table through `0x590310` in mode `1` with key `gsi_am_rating`,
|
||||
then selects the new head through `0x590480` before re-entering the route-transition path. The
|
||||
|
|
@ -492,7 +706,7 @@ transition.
|
|||
queued `gsi_am_rating` split, mode `1` for the ready-bit plus queued fallback, mode `2` for
|
||||
pending-descriptor cleanup, mode `3` for the empty-table fallback, mode `4` for deferred
|
||||
route-status recovery, and mode `5` for copying the staged route companion dword into `[this+0x54]`
|
||||
and the local field cache. The current grounded mode transitions still switch by releasing route
|
||||
and queue-side slot `[this+0x1724+0x24]`. The current grounded mode transitions still switch by releasing route
|
||||
objects through `multiplayer_gamespy_route_release_and_free` and rebuilding them through
|
||||
`multiplayer_transport_try_connect_live_route`, not by mutating callback slots in place. The
|
||||
parser behavior is now tighter as well: semicolon lines only dispatch when
|
||||
|
|
@ -553,13 +767,43 @@ transition.
|
|||
stepper `simulation_advance_to_target_calendar_point`, but current grounded callers put that
|
||||
helper inside the larger post-load generation pipeline `world_run_post_load_generation_pipeline`
|
||||
at `0x004384d0` under the `Seeding Economy...` phase rather than under the ordinary player-facing
|
||||
speed buttons. That setup pipeline is now clearer at the progress-banner level too: localized id
|
||||
`318` `Computing Transportation and Pricing...` stays visible while the pipeline runs
|
||||
speed buttons. That setup pipeline is now clearer at the progress-banner level too: on the fuller
|
||||
setup path it first runs one preliminary named-candidate availability prepass through `0x00437743`
|
||||
before any visible progress banner is posted. After that, localized id `318`
|
||||
`Computing Transportation and Pricing...` stays visible while the pipeline runs
|
||||
`world_compute_transport_and_pricing_grid` `0x0044fb70`, the early collection-owned staging pass
|
||||
`world_setup_building_collection_phase` `0x0041ea50`, and the conditional region pair
|
||||
`world_region_collection_seed_default_regions` `0x00421b60` plus
|
||||
`world_region_border_overlay_rebuild` `0x004882e0`; only then does the code post id `319` `Setting
|
||||
up Players and Companies...`. That `319` lane is no longer just a shell-state placeholder: its
|
||||
`world_region_border_overlay_rebuild` `0x004882e0`; that border pass is now tighter too: the
|
||||
outer owner refreshes the companion region set `0x006cfc9c` through the reset-and-assign wrapper
|
||||
`0x00487650` above the heavier record initializer `0x00487540`, then re-enters `0x004881b0` to
|
||||
refresh the raw per-region cell-count band from the live world raster, and the inner emitter
|
||||
`0x00487de0` clears prior chunked segment queues through `0x00533cf0`, scans the live region
|
||||
raster, and appends fresh border-segment records through `0x00536ea0`. The lower presentation
|
||||
helper strip under the same owner is now explicit too: `0x00533970/0x00533980` query the cached
|
||||
world-grid X/Y maxima, `0x00533ae0/0x00533af0/0x00533b00` expose the secondary-raster and
|
||||
companion byte-raster roots, and `0x00533b20/0x00533b30/0x00533b70/0x00533b90` expose the
|
||||
normalized coordinate, strip-offset, and sample-triplet tables used by shell-side overlay
|
||||
staging. If shell-state gate
|
||||
`[0x006cec74+0x174]` is set it then posts id `320` `Setting Up Buildings...` for
|
||||
`world_region_collection_run_building_population_pass` `0x00421c20`; if `[0x006cec74+0x178] > 0`
|
||||
it then posts id `321` `Seeding Economy...` for `simulation_run_chunked_fast_forward_burst`
|
||||
`0x00437b20`; only after those setup-side gates does the code post id `319`
|
||||
`Setting up Players and Companies...`. That `319` lane is no longer just a shell-state placeholder: its
|
||||
earlier hidden prepass is tighter now too: `0x00437743` is the scenario-side named
|
||||
candidate-availability seeding pass over the live candidate pool `0x0062b268`, feeding the
|
||||
override collection at `[state+0x66b2]` through `0x00434f20` before any visible banner is posted.
|
||||
That candidate-side table now has a grounded fixed record layout too: each entry is a `0x22`-byte
|
||||
blob with a zero-terminated candidate-name slot at `[entry+0x00..+0x1d]` and one trailing
|
||||
availability dword at `[entry+0x1e]`, read through `0x00434ea0` and mirrored later into
|
||||
`[candidate+0x7ac]`. The same scenario state keeps a locomotive-side sibling collection at
|
||||
`[state+0x66b6]`, read through `0x00435030` and updated through `0x004350b0`, which is now the
|
||||
shared named locomotive-availability lane beneath the `Locomotives` editor page and the
|
||||
startup-side locomotive seeding branches. That locomotive-side table is the same pattern at a
|
||||
larger width: each entry is a `0x41`-byte blob with a zero-terminated locomotive-name slot at
|
||||
`[entry+0x00..+0x3c]` and one trailing availability dword at `[entry+0x3d]`, later mirrored into
|
||||
`[loco+0x7b]`. Both override dwords now read most safely as simple availability bits rather than
|
||||
wider mode enums. That `319` lane is no longer just a shell-state placeholder: its
|
||||
primary grounded work is still the chairman-profile pair
|
||||
`world_seed_default_chairman_profile_slots` `0x004377a0` plus
|
||||
`world_build_chairman_profile_slot_records` `0x00437220`, which seed the 16 selector bytes at
|
||||
|
|
@ -582,8 +826,12 @@ transition.
|
|||
surface beside that chairman panel is clearer now too.
|
||||
`map_editor_scenario_metadata_panel_refresh_controls` `0x004ca790` republishes the scenario
|
||||
description from `[0x006cec78+0x672e]`, the start-year trio `[+0x66ca]`, `[+0x66d2]`, and
|
||||
`[+0x66ce]`, and the paired boolean toggles `[+0x66de]` plus inverse `[+0x66f3]` across control
|
||||
band `0x5b69..0x5b74`, while `map_editor_scenario_metadata_panel_refresh_briefing_mode`
|
||||
`[+0x66ce]`, the direct campaign-designated bit `[+0x66de]` through control `0x5b6e`, and the
|
||||
inverse of the paired metadata byte `[+0x66f3]` through control `0x5b74`. The resource-side
|
||||
anchor is now explicit too: `editorDetail.win` carries localized ids `3160/3161` `Campaign
|
||||
Scenario` and `If checked, this map will be reserved as a campaign scenario.` inside the control
|
||||
record rooted at `0x5b6e`, so `[+0x66de]` is now the grounded campaign-scenario flag rather than
|
||||
an anonymous metadata boolean. `map_editor_scenario_metadata_panel_refresh_briefing_mode`
|
||||
`0x004ca670` now bounds the single-player versus multiplayer briefing switch by flipping selector
|
||||
`0x621f50`, publishing localized headings `1491` and `3586`, and refreshing the two briefing texts
|
||||
from `[state+0x4f30]` and `[+0x5ae9]`. The companion dispatcher
|
||||
|
|
@ -695,10 +943,24 @@ transition.
|
|||
longer just an abstract issue table lookup because its merger callsite uses issue id `0x3a`, which
|
||||
lines up directly with localized id `726` saying merger votes depend on public attitude toward the
|
||||
management of the two companies. By contrast the broader support-adjusted share-price helper
|
||||
`company_compute_public_support_adjusted_share_price_scalar` `0x00424fd0` uses the broader issue
|
||||
id `0x37`, which currently looks like a more general company-management or public-sentiment slot
|
||||
rather than the merger-only attitude term. The supporting stat layer is bounded more cleanly now
|
||||
too: the
|
||||
`company_compute_public_support_adjusted_share_price_scalar` `0x00424fd0` uses issue id `0x37`,
|
||||
while the finance-side debt helpers now bound `0x38` and `0x39` separately as the explicit
|
||||
credit-rating and prime-rate lanes. That leaves `0x37` as the broader investor-confidence lane
|
||||
behind equity support, share price, and adjacent governance pressure, with the strongest current
|
||||
text anchors coming from the investor-attitude strings `1217` and `3048/3049` about company or
|
||||
chairman performance rather than from the merger-only management-attitude term. One older local lead is now ruled out too: the
|
||||
`0x460a90` / `0x473620` setup block is the camera-view hotkey family over localized ids
|
||||
`3482..3489` (`Select/Assign Camera View 5..8`), not issue-opinion infrastructure, so it should
|
||||
no longer be used as evidence for the player-facing meaning of issue `0x37`. The editor-side
|
||||
`Stock Prices` label is also no longer being used as direct evidence here: the `0x4ca980` /
|
||||
`0x4cadf0` panel owns a separate float-tuning block `[state+0x0bde..0x0bf6]`, with
|
||||
`[state+0x0bde]` merely mirroring `[state+0x0be2]`, so that label belongs to a different
|
||||
settings family than the issue table behind `0x37`. A direct shell-resource follow-up now narrows
|
||||
the remaining caption question too: the extracted `CompanyDetail.win` blob from `rt3_2WIN.PK4`
|
||||
contains no plain-English investor or finance caption for this lane, which matches the owner-side
|
||||
read that section-0 is a dynamic `0x947f` text widget fed by
|
||||
`shell_format_company_governance_and_economy_status_panel` `0x004e5cf0` rather than a separate
|
||||
fixed label row. The supporting stat layer is bounded more cleanly now too: the
|
||||
surrounding `0x0b` setup in the merger and takeover offer builders is formatter mode rather than a
|
||||
player-facing issue number, while the company-side stat wrapper
|
||||
`company_read_year_or_control_transfer_metric_value` `0x0042a5d0` now reads as a generic
|
||||
|
|
@ -722,14 +984,43 @@ transition.
|
|||
`world_region_collection_run_building_population_pass` `0x00421c20` plus the deeper per-region
|
||||
worker `world_region_balance_structure_demand_and_place_candidates` `0x004235c0`, while
|
||||
`[0x006cec74+0x178]` directly fronts id `321` `Seeding Economy...` and the chunked burst helper
|
||||
`simulation_run_chunked_fast_forward_burst`; id `322` then fronts `Calculating Heights...`. The
|
||||
`simulation_run_chunked_fast_forward_burst`, whose grounded tail now also sweeps the live region
|
||||
collection through `world_region_refresh_cached_category_totals_and_weight_slots` `0x00423d30`;
|
||||
id `322` then fronts `Calculating Heights...`. The
|
||||
master `+0x68` flag is no longer just structural: the shell load/save coordinators now use the
|
||||
same flag to force the editor-map `.gmp` family, so current evidence treats it as the broader
|
||||
editor-map mode above those later setup branches rather than an unnamed generic toggle. That
|
||||
worker is no longer one opaque building bucket: current grounded categories split into `House`, a
|
||||
weighted region-profile family surfaced through the `Industry Weightings` stats path, and a third
|
||||
branch whose low-level fallback token is `Commercial` but whose aligned stats label is `City
|
||||
Support`. The same lower helper also reappears later on a slower simulation-side cadence with
|
||||
Support`. The same placement-commit gate beneath it, `world_region_validate_and_commit_candidate_placement`
|
||||
`0x00422a70`, is also corroborated by the separate world-side randomized batch helper
|
||||
`world_try_place_random_structure_batch_from_compact_record` `0x00430270`, which retries
|
||||
placements around one compact center/radius record rather than the ordinary per-region demand
|
||||
deficits. That batch placer now sits under a wider compact runtime-effect dispatcher,
|
||||
`world_apply_compact_runtime_effect_record_to_resolved_targets` `0x00431b20`, so the world-side
|
||||
branch is no longer just “another caller near `0x43051e`” but one real effect family alongside
|
||||
the ordinary region-demand worker. Above that, the live scenario event collection at `0x0062be18`
|
||||
now has a tighter runtime-effect lane too: `scenario_runtime_effect_record_service_and_dispatch_linked_compact_effects`
|
||||
`0x004323a0` services one live runtime-effect record, dispatches its linked compact effects
|
||||
through `0x00431b20`, and can synthesize follow-on records through
|
||||
`scenario_runtime_effect_record_build_followon_effect_from_compact_record_and_targets`
|
||||
`0x00430b50`, which in turn allocates new live records through
|
||||
`scenario_event_collection_allocate_runtime_effect_record_from_compact_payload` `0x00432ea0`.
|
||||
Above that, `scenario_event_collection_service_runtime_effect_records_for_trigger_kind`
|
||||
`0x00432f40` now bounds the collection-wide loop that services those live runtime-effect records
|
||||
for one trigger kind. That trigger split is tighter now too: recurring simulation maintenance
|
||||
drives kinds `1`, `0`, `3`, and `2`; the neighboring route-style follow-on drives `5` and `4`;
|
||||
startup-company and named-railroad creation branches drive `7`; kind `6` is now bounded as a
|
||||
mixed post-change family spanning the placed-structure post-create tail, one build-version-gated
|
||||
company-startup or roster-refresh tail, and the route-entry post-change sweep on `0x006cfca8`;
|
||||
one world-entry-side one-shot gate drives `8` and then clears shell-profile latch
|
||||
`[0x006cec7c+0x97]`; the briefing-text query lane is kind `9`; and the collection dirty latch
|
||||
still forces the internal rerun at kind `0x0a`.
|
||||
That moves this branch out of the “isolated world-side placement oddity” bucket and into a real
|
||||
scenario-runtime effect pipeline with a mostly bounded trigger-kind family.
|
||||
The same lower helper also reappears later on a slower
|
||||
simulation-side cadence with
|
||||
scale `1/12`, so it no longer looks like one-time setup glue only. The actual game-speed control
|
||||
family remains separate, rooted at `world_set_game_speed_mode` `0x00434680`,
|
||||
`world_adjust_game_speed_mode_delta` `0x00434850`, and `world_toggle_pause_or_restore_game_speed`
|
||||
|
|
@ -1355,7 +1646,10 @@ transition.
|
|||
current anchor site, then either appends that entry through `0x004b3160` and refreshes the new
|
||||
trailing selection through `0x004b2f00`, or rotates one existing slot in place when the local
|
||||
two-entry cap has already been reached. So this edge now reaches an actual train-side autoroute
|
||||
append lane rather than stopping at anonymous company-side cache cells.
|
||||
append lane rather than stopping at anonymous company-side cache cells. The weighted cache lanes
|
||||
still do not escape that route-choice family directly: this train-side append path only inherits
|
||||
the weighted site and peer choice by calling `0x00408380`, not by reading cache `+0x0e/+0x16`
|
||||
itself.
|
||||
The train-side follow-on above that seed path is bounded now too. The owning company can count
|
||||
its live roster through `company_count_owned_trains` `0x004264c0`, measure one aggregate linked
|
||||
transit site pressure through `company_compute_owned_linked_transit_site_score_total`
|
||||
|
|
@ -1367,7 +1661,9 @@ transition.
|
|||
branch when exactly two eligible linked transit sites survive.
|
||||
Because it consumes `+0x12` rather than `+0x0e` or `+0x16`, current evidence now says the tracker
|
||||
compatibility split is more important for seeded route choice and ranked site choice than for the
|
||||
final company train-count target itself.
|
||||
final company train-count target itself. The local linked-transit chain is now bounded enough to
|
||||
say that directly: weighted cache lanes feed `0x00408280 -> 0x00408380 -> 0x00409770/0x00409830`,
|
||||
while the separate pressure or roster lane feeds `0x00408f70 -> 0x00409950` from raw `+0x12`.
|
||||
The balancer then applies two age bands to company-owned trains:
|
||||
very old trains are removed when the roster already exceeds target or upgraded when it still
|
||||
needs capacity, while the mid-age band can trigger one narrower upgrade pass.
|
||||
|
|
@ -1469,12 +1765,16 @@ transition.
|
|||
`[+0x4a8b]` and `[+0x4a87]` clear, at least two bond slots live, and at least one year since
|
||||
founding. It derives one issue batch from outstanding shares rounded down to `1000`-share lots
|
||||
with floor `2000`, trims that batch until the broader support-adjusted share-price scalar times
|
||||
batch no longer exceeds the `55000` gate, requires that scalar at least `22`, resolves the
|
||||
highest-coupon live bond slot, and then uses current cash from `0x2329/0x0d` as a gate against
|
||||
that slot's principal plus a small fixed buffer before it compares the chosen bond-rate lane
|
||||
against a normalized scalar ratio built from the same support-adjusted share-price lane and
|
||||
current `Book Value Per Share` from `0x2329/0x1d` through the piecewise approval ladder
|
||||
`0.07/1.3 -> 0.14/0.35`. On success it issues two
|
||||
batch no longer exceeds the `55000` gate, recomputes the pressured support-adjusted share-price
|
||||
scalar and the paired `price/book` ratio, then tests the remaining gates in a fixed order:
|
||||
share-price floor `22`, proceeds floor `55000`, current cash from `0x2329/0x0d` against the
|
||||
chosen highest-coupon bond principal plus `5000`, one later stock-issue cooldown gate that
|
||||
converts the current issue mixed-radix calendar tuple at `[company+0x16b/+0x16f]` through
|
||||
`calendar_point_pack_tuple_to_absolute_counter` `0x0051d3c0` and compares the result against the
|
||||
active world absolute calendar counter `[world+0x15]`, and only then the coupon-versus-price-to-book
|
||||
approval ladder `0.07/1.3 -> 0.14/0.35`. The issue mutator preserves the previous tuple in
|
||||
`[company+0x173/+0x177]` and refreshes the current tuple from `[world+0x0d/+0x11]` on the
|
||||
derived-pricing lane. On success it issues two
|
||||
same-sized tranches through repeated `company_issue_public_shares_and_raise_capital` calls and
|
||||
publishes a separate equity-offering news family rooted at localized id `4053`, not the earlier
|
||||
debt or buyback headline family.
|
||||
|
|
@ -1615,24 +1915,149 @@ transition.
|
|||
too: category `0` falls back to `House`, category `2` is the year-gated weighted region-profile
|
||||
family that also feeds the localized `Industry Weightings` stats panel, and category `3` now
|
||||
reaches a separate pool-driven picker whose fallback label is `Commercial` but whose aligned
|
||||
player-facing stats bucket is `City Support`. The remaining setup-side uncertainty has therefore
|
||||
narrowed again: the region seed and border-overlay pair clearly complete before the `Setting up
|
||||
Players and Companies...` banner is posted; `[0x006cec74+0x174]` now looks like the direct
|
||||
building-population gate; `[0x006cec74+0x178]` now looks like the direct seeding-burst gate; and
|
||||
`[0x006cec74+0x68]` now aligns with editor-map mode because the same flag forces the `.gmp` family
|
||||
in the shell file coordinators while suppressing the later building and seeding branches and
|
||||
diverting the deeper region worker into alternate logic. The `319` lane itself is no longer the
|
||||
player-facing stats bucket is `City Support`. The normalized region band is tighter too:
|
||||
`world_region_normalize_cached_structure_balance_scalars` `0x00422320` no longer just writes an
|
||||
anonymous cached preview band at `[region+0x2e2/+0x2e6/+0x2ea/+0x2ee]`. Current growth-report
|
||||
evidence now grounds `[region+0x2e2]` as the weighted-profit-margin scalar and `[region+0x2ee]`
|
||||
as the annual-density-adjust scalar later formatted as a percent in `Stats - City/Region`, with
|
||||
`[region+0x2e6/+0x2ea]` left as the intermediate normalized-delta and clamped companion slots
|
||||
beneath that final adjust term. The remaining setup-side uncertainty has therefore narrowed
|
||||
again: the region seed and border-overlay pair clearly complete before the `Setting up Players and
|
||||
Companies...` banner is posted; `[0x006cec74+0x174]` now looks like the direct building-population
|
||||
gate; `[0x006cec74+0x178]` now looks like the direct seeding-burst gate; and `[0x006cec74+0x68]`
|
||||
now aligns with editor-map mode because the same flag forces the `.gmp` family in the shell file
|
||||
coordinators while suppressing the later building and seeding branches and diverting the deeper
|
||||
region worker into alternate logic. The `319` lane itself is no longer the
|
||||
open structural gap; it now clearly owns chairman-profile slot seeding, profile-record
|
||||
materialization, a shell editor surface over the same local record family, and a separate
|
||||
live-company presentation path through the company-list window. The later interior order of that
|
||||
same `319` lane is tighter now too: after the route-entry collection refresh on `0x006cfca8` it
|
||||
refreshes the auxiliary route-entry tracker collection `0x006cfcb4`, then runs
|
||||
`placed_structure_collection_refresh_local_runtime_records_and_position_scalars` `0x004133b0`,
|
||||
then a flagged world-grid cleanup sweep through `0x00448af0/0x00533fe0`, and only after that the
|
||||
later route-entry post-pass at `0x00491c20`. The same later lane now also reaches a separate
|
||||
event-side runtime branch: the live event collection at `0x0062be18` re-enters
|
||||
`scenario_event_collection_refresh_runtime_records_from_packed_state` `0x00433130`, which in
|
||||
turn materializes each live event record through
|
||||
then a flagged world-grid cleanup sweep through the compact grid-flag query
|
||||
`0x00448af0` plus the neighboring local chunk-cell write helper `0x00533fe0`, and only after
|
||||
that the later route-entry post-pass at `0x00491c20`. The same `319` lane is tighter internally
|
||||
now too:
|
||||
before that later world and shell reactivation tail, `world_entry_transition_and_runtime_bringup`
|
||||
runs one distinct post-bundle status and runtime refresh phase that posts progress ids `0x196`
|
||||
and `0x197` through `0x005193f0/0x00540120` with paired `0x004834e0` follow-ons, refreshes the
|
||||
live event collection at `0x0062be18` through
|
||||
`scenario_event_collection_refresh_runtime_records_from_packed_state` `0x00433130`, rebuilds the
|
||||
scenario-side port-or-warehouse cargo recipe runtime tables through `0x00435630`, and then runs
|
||||
the named-candidate availability preseed through `0x00437743`. One later subphase is tighter now
|
||||
too: before the broad world-reactivation sweep it posts progress ids `0x32dc/0x3714/0x3715`,
|
||||
reloads one `0x108`-byte packed profile block through `0x00531150`, conditionally copies staged
|
||||
runtime-profile bytes back into `0x006cec7c` while latch `[profile+0x97]` is set, mirrors the
|
||||
grounded campaign-scenario bit `[profile+0xc5]` and sandbox bit `[profile+0x82]` into world
|
||||
bytes `[world+0x66de]` and `[world+0x66f2]`, restores the selected year/profile lane through
|
||||
`[profile+0x77]` into `[world+0x05/+0x09/+0x15]` through
|
||||
`world_set_selected_year_and_refresh_calendar_presentation_state` `0x00409e80`; that restore now
|
||||
has the explicit companion `world_refresh_selected_year_bucket_scalar_band` `0x00433bd0`, which
|
||||
rebuilds the dependent selected-year bucket floats after the packed year changes; and then
|
||||
rehydrates the named locomotive availability collection at `[world+0x66b6]` through
|
||||
`locomotive_collection_refresh_runtime_availability_overrides_and_usage_state` `0x00461e00`.
|
||||
That locomotive-side restore is tighter now too: its tail explicitly re-enters
|
||||
`scenario_state_refresh_cached_available_locomotive_rating` `0x00436af0`, which rebuilds one
|
||||
cached available-locomotive rating at `[state+0x4cbe]` from the current year plus the strongest
|
||||
surviving available locomotive-side rating scalar `[loco+0x20]`, and the tiny query sibling
|
||||
`0x00434080` is now bounded as the shell-side clamped read helper over that same cached field,
|
||||
with the grounded shell-side reader later bucketing that value against `40/50/70/85/100`. The
|
||||
same rehydrate band also refreshes the live structure-candidate filter and year-visible counts
|
||||
through `structure_candidate_collection_refresh_filter_and_year_visible_counts` `0x0041e970`,
|
||||
rebuilding the paired per-slot bands at `[candidates+0x246]` and `[candidates+0x16e]` and the
|
||||
aggregate counts at `[candidates+0x31a]` and `[candidates+0x242]`; the same late checkpoint also
|
||||
re-enters `placed_structure_collection_seed_candidate_subtype2_runtime_latch` `0x00434d40`,
|
||||
which seeds runtime dword `[candidate+0x7b0]` across subtype-`2` candidate records before the
|
||||
later world-wide reactivation sweep. That checkpoint also now has an explicit shell-facing scalar
|
||||
publisher: `world_publish_shell_controller_progress_scalar_from_year_thresholds_or_selector_overrides`
|
||||
`0x004354a0` writes one clamped `0..255` value into the current shell presentation object,
|
||||
sourcing it either from the shell selector override pairs or from the scenario-side year-threshold
|
||||
band rooted at `[state+0x3a/+0x51/+0x55/+0x59/+0x5d/+0x61]`; and just ahead of the later
|
||||
scenario-side recipe rebuild, the same band also re-enters
|
||||
`scenario_state_ensure_derived_year_threshold_band` `0x00435603`, which only falls into its
|
||||
heavier rebuild body while `[state+0x3a] < 2` and otherwise leaves the derived year-threshold
|
||||
companion slots `[state+0x51/+0x55/+0x59/+0x5d/+0x61]` unchanged. The neighboring late status
|
||||
checkpoints around progress ids `0x196` and `0x197` also share one explicit stage gate now:
|
||||
`world_query_global_stage_counter_reached_late_reactivation_threshold` `0x00444dc5` compares the
|
||||
global counter `0x00620e94` against threshold `0x9901`, and the two current callers use a
|
||||
negative result to clear `[world+0x39]` before the broader world and shell reactivation sweep.
|
||||
The later reactivation tail is tighter now too: it includes the region-center world-grid flag
|
||||
reseed pass
|
||||
`0x0044c4b0`, which clears bit `0x10` across the live grid and then marks one representative
|
||||
center cell for each class-`0` region through `0x00455f60`; its immediate sibling `0x0044c450`
|
||||
then reruns `placed_structure_rebuild_candidate_cargo_service_bitsets` `0x0042c690` across every
|
||||
live grid cell, and the next helper `0x0044ce60` scans the secondary raster at `[world+0x2135]`
|
||||
for cells with any bits in mask `0x3e`, caching min/max bounds plus a marked-cell count in
|
||||
`[world+0x21c6..+0x21d6]`; the larger sibling `0x0044c670` then consumes those cached bounds to
|
||||
normalize the same raster and rebuild one dependent overlay/cache surface before the later
|
||||
route-style rebuild, shell-window, and briefing branches. That overlay side is tighter now too:
|
||||
after `0x0044c670` resolves scaled surface dimensions through `0x00534c50`, it walks one local
|
||||
`3 x 32` sample lattice through the static offset tables at `0x00624b28/0x00624b48`, keeps only
|
||||
secondary-raster classes `4..0x0d`, folds several interpolated `0x0051db80` samples into one
|
||||
strongest local score, writes packed overlay pixels into the staged surface buffer, and only then
|
||||
publishes that staged overlay through `0x00534af0`. The lower helper layer under that overlay
|
||||
pass is tighter now too: `0x00534e10` is the reusable secondary-raster class-set
|
||||
predicate for classes `1/3/4/5`, `0x00534e50` is the smaller neighboring class-subset predicate
|
||||
for `1/4`, `0x00534ec0` covers `2/4/5`, `0x00534f00` covers `3/5`, `0x00534e90` is the
|
||||
marked-bit query over the same 3-byte cell family, and
|
||||
`0x00533e70` and `0x00534160` are the coarser siblings over the overlay table at `[world+0x1685]`:
|
||||
the first clears coarse chunk objects across one clamped rectangle, while the second ensures one
|
||||
chunk object and seeds local marks through its deeper stamp helper. One level up, the neighboring
|
||||
rect owner `0x005374d0` now reads as the shared secondary-overlay refresh pass: it reruns the
|
||||
local sample and unsigned-word reducers `0x00536230/0x00536420`, rebuilds the signed vector byte
|
||||
planes through `0x00536710`, and then rebuilds the multiscale support surfaces through
|
||||
`0x00533890`, whose inner reducers now explicitly target the packed sample-triplet buffer plus
|
||||
the float and unsigned-word support planes rooted at the five-entry per-scale families
|
||||
`[world+0x15f1..+0x1601]`, `[world+0x1605..+0x1615]`, and `[world+0x1619..+0x1629]`. The setup
|
||||
side of that same family is tighter now too:
|
||||
`0x005375c0` is the shared ensure-and-seed owner that allocates the sample, sidecar, mask,
|
||||
raster, vector, and coarse-cell tables together; crucially, it seeds `[world+0x1655]` with byte
|
||||
`0x02` and `[world+0x1659]` with byte `0x01`, which closes the default-fill split. The local
|
||||
evidence now also supports a stronger negative conclusion: unlike `[world+0x1655]`, that second
|
||||
mask plane is not part of the actively rebuilt runtime overlay path, and in the grounded local
|
||||
corpus it behaves only as a separately seeded, cleared, and persisted sibling plane. One level
|
||||
lower, the
|
||||
base-plane allocator `0x00532c80` now reads more cleanly too: it is the narrower owner that
|
||||
clears `[world+0x15e1]`, optionally applies the current grid dimensions, allocates the base
|
||||
float-summary plane `[world+0x1605]`, the four sidecar byte planes `[world+0x1631..+0x163d]`,
|
||||
both one-byte mask planes `[world+0x1655/+0x1659]`, and the packed secondary raster
|
||||
`[world+0x165d]`, then seeds those planes with the same `0x02/0x01/0x00` default split. One
|
||||
level higher again, the broader world-presentation reinitializer `0x00537e60` now sits above
|
||||
that base allocator and the larger support-family ensure path `0x005375c0`: it stores the live
|
||||
grid dimensions, hard-resets the whole overlay runtime family through `0x00532590`,
|
||||
reinitializes the secondary-overlay family for those dimensions, and then republishes the
|
||||
neighboring overlay constants and support owners used by both the world-side reattach branch and
|
||||
the `.smp` restore-side presentation rebuild path, including several owners that all funnel
|
||||
through the shared static-template slot allocator `0x00532ad0` over the local `0x100` pointer
|
||||
band at `[world+0x08]`. Those neighboring owners are tighter now too: `0x00535070` is the small
|
||||
primary overlay-surface-or-template setup owner, while `0x005356e0` and `0x00535890` seed two
|
||||
larger static-template slot bands rooted at `[world+0x1568/+0x156c/+0x1574/+0x1578]` and
|
||||
`[world+0x1560/+0x1564]` respectively; the remaining heavier sibling `0x00535430` now reads as a
|
||||
shared four-slot overlay-surface rebuild owner that resamples one source or fallback descriptor
|
||||
into a short local slot strip above `[world+0x155c]`. The tail of that same reinitializer is
|
||||
tighter now too: after the larger support-family setup it seeds one seven-entry default overlay
|
||||
companion set through `0x005373b0`, whose inner allocator `0x00535950` populates the local
|
||||
`0x1b`-entry slot table from the static template rows `0x005dd300..0x005dd378`. The lifecycle
|
||||
side is tighter in the same way now: `0x00536044` is the shared teardown owner that frees those same
|
||||
three five-entry support families together with both mask planes, the packed secondary raster,
|
||||
the vector-byte planes, the local staging buffer, and the neighboring sidecar or coarse-cell
|
||||
tables. The remaining base-float lane is tighter too: the larger rebuild owner
|
||||
`0x00538360` now clearly writes one base float-summary field into `[world+0x1605]`, clears both
|
||||
one-byte mask planes, and then only repopulates the primary mask plane `[world+0x1655]` for the
|
||||
qualifying class-`1` interior cells before re-entering `0x00532d90` to normalize that base
|
||||
float-summary plane globally and `0x00532f60` to expand positive cells through one caller radius.
|
||||
That asymmetry is now enough to close the local semantic edge: `[world+0x1655]` is the actively
|
||||
rebuilt primary overlay mask, while `[world+0x1659]` is only the separately seeded and persisted
|
||||
secondary mask sibling with no comparably grounded distinct read-side consumer. The only grounded
|
||||
getter call to its root accessor `0x00533b60` is the shell staging branch at `0x00525bad`, and
|
||||
that branch immediately discards the returned pointer. The bundle side is now explicit too:
|
||||
`.smp` save-load treats the two mask planes as separate payloads with chunk ids `0x2cee` for
|
||||
`[world+0x1655]` and `0x2d51` for `[world+0x1659]`, while the neighboring `0x2d49/0x2d50`
|
||||
branches are the separate packed secondary-raster import lanes rather than alternate consumers
|
||||
of the second mask plane. So, in the mapped local code, `0x1659` is best treated as a persisted
|
||||
compatibility or seed-state sibling, not as a second actively consumed runtime overlay mask. The event
|
||||
side is tighter too:
|
||||
that `0x00433130` pass in turn materializes each live event record through
|
||||
`scenario_event_refresh_runtime_record_from_packed_state` `0x0042db20`. Current shell-side xrefs
|
||||
now tighten that event branch too: the first rebuilt linked row family under `0x0042db20` aligns
|
||||
with the standalone condition list later queried by `EventConditions.win`, while the second
|
||||
|
|
@ -1754,18 +2179,31 @@ transition.
|
|||
`company_compute_cached_recent_per_share_performance_subscore`,
|
||||
`company_compute_five_year_weighted_shareholder_return`, and
|
||||
`company_compute_public_support_adjusted_share_price_scalar` bound the recent per-share
|
||||
performance and investor-support/share-price blend beneath those vote resolvers,
|
||||
performance and investor-support/share-price blend beneath those vote resolvers; the recent
|
||||
per-share feeder now has a grounded four-lane tail too, with current partial-year weight
|
||||
`(5 * [world+0x0f]) - 5`, prior full-year weights `48/36/24/12` on `0x1f/0x1e`, dividend
|
||||
non-decline pair weights `9/8/7/6` on `0x20`, lane weights `40/10/20/30`, the startup age ramp
|
||||
`0/0/0/100 -> 25/25/35/100 -> 50/50/65/100 -> 75/75/85/100 -> 100/100/100/100`, a strongest-lane
|
||||
`*1.25` boost, a weakest-lane `*0.8` reduction, and separate bounded-intermediate versus final
|
||||
difficulty applications under `0x005f33b8`; the next consumer `0x00424fd0` is also tighter now,
|
||||
with the young-company interpolation against `[company+0x57]`, caller pressure clamped to
|
||||
`[-0.2, 0.2]`, one `(shares / 20000)^0.33` share-count growth term, and the later threshold
|
||||
ladder `0.6 / 0.45 / 0.3 / 1.7 / 2.5 / 4.0 / 6.0` before the issue-`0x37` multiplier,
|
||||
`scenario_state_compute_issue_opinion_multiplier` now bounds the
|
||||
next layer of optional company, chairman, and territory-specific opinion overrides on the active
|
||||
scenario state, and the broader stat-reader family around
|
||||
`company_read_control_transfer_metric_slot` and
|
||||
`company_read_year_or_control_transfer_metric_value` is no longer just a merger-premium helper.
|
||||
Current grounded callers show the same metric family feeding the annual shareholder-revolt and
|
||||
creditor-liquidation lane surfaced by localized ids `300..304`, so the remaining gap is now mostly
|
||||
semantic: the exact player-facing names of the support-and-governance metric slots behind issue
|
||||
ids `0x37` and `0x3a`, plus any later chairmanship-control side effects beyond the already
|
||||
grounded success or failure commit points. The packed simulation calendar tuple semantics also
|
||||
remain open. The `TrackLay.win` family now clearly owns `Lay single track.` `Lay double track.`
|
||||
creditor-liquidation lane surfaced by localized ids `300..304`, while the debt-side shell and
|
||||
bond lane now separately close `0x38` as `Credit Rating` and `0x39` as `Prime Rate`. That means
|
||||
the remaining gap is now mostly gone on the UI side too: issue `0x37` is already bounded to the
|
||||
same investor-confidence family as the equity-support and governance-pressure paths, and current
|
||||
grounded UI evidence still stops at the investor-attitude sentence family rather than one
|
||||
standalone caption. The calendar side is tighter now too:
|
||||
`[world+0x15]` is the absolute counter for the same mixed-radix `12 x 28 x 3 x 60`
|
||||
year-plus-subfield tuple packed by `0x0051d3c0` and unpacked by `0x0051d460`, not just a vague
|
||||
“calendar-like” blob. The `TrackLay.win` family now clearly owns `Lay single track.` `Lay double track.`
|
||||
and `Bulldoze` as its three primary modes, its bridge selector, its wrapped frequency preferences,
|
||||
and a strongly aligned pair of `Auto-Hide Trees During Track Lay` and `Auto-Show Grade During
|
||||
Track Lay` toggles; the `StationPlace.win` family now clearly owns its six top-level category
|
||||
|
|
@ -2055,10 +2493,16 @@ transition.
|
|||
`0x0042cb30` is the final recent-service clamp over the primary per-candidate word table. Above
|
||||
that refresh lane, `placed_structure_query_candidate_local_service_metrics` `0x0047e240` is the
|
||||
first higher-level query, `placed_structure_count_candidates_with_local_service_metrics`
|
||||
`0x0047e330` counts how many candidates currently produce that query, and
|
||||
`0x0047e330` counts how many candidates currently produce that query,
|
||||
`placed_structure_get_nth_candidate_id_with_local_service_metrics` `0x0047e620` is the ordinal
|
||||
selector over that same visible candidate family,
|
||||
`placed_structure_query_cached_express_service_class_score` `0x0047e390` caches one parallel class
|
||||
score for the express family now strongly aligned with `Passengers`, `Mail`, and `Troops`. Those
|
||||
site-side scores now have grounded shell read-side consumers too:
|
||||
score for the express family now strongly aligned with `Passengers`, `Mail`, and `Troops`,
|
||||
`placed_structure_refresh_candidate_local_service_comparison_cache_against_peer_site`
|
||||
`0x0047eb90` rebuilds one peer-site comparison cache over the same local-service inputs, and
|
||||
`placed_structure_select_best_candidate_id_by_local_service_score` `0x0047f910` is the best-hit
|
||||
selector above the direct and directional local-service query pair. Those site-side scores now
|
||||
have grounded shell read-side consumers too:
|
||||
`shell_station_detail_format_freight_and_express_summary` `0x00506be0` formats the visible
|
||||
`Freight: %1` and `Express: %1` lines in `StationDetail.win`, and the same station-detail family
|
||||
now has a deeper candidate-service lane:
|
||||
|
|
|
|||
353
docs/debug-load-workflow.md
Normal file
353
docs/debug-load-workflow.md
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
# Debug Load Workflow
|
||||
|
||||
Use this when comparing:
|
||||
|
||||
- one successful manual load of `hh`
|
||||
- one failing hook-driven auto-load attempt
|
||||
|
||||
The goal is to compare the real successful owner path above `0x00445ac0` against the failing hook-driven path.
|
||||
|
||||
## Current Findings
|
||||
|
||||
From the current logs:
|
||||
|
||||
- successful manual load now has a grounded pre-call site at `0x004390cb` with:
|
||||
- `ECX = 0x02af5840`
|
||||
- `[0x006cec78] = 0x02af5840`
|
||||
- `[0x006cec74] = 0x01d81230`
|
||||
- top-of-stack dwords:
|
||||
- `arg1 = 0x01db4739`
|
||||
- `arg2 = 4`
|
||||
- `arg3 = 0x0022fb50`
|
||||
- next dword = `0x026d7b88`
|
||||
- the subsequent successful `0x00445ac0` entry still has:
|
||||
- `ret = 0x004390d0`
|
||||
- `arg1 = 0x01db4739`
|
||||
- `arg2 = 4`
|
||||
- `arg3 = 0x0022fb50`
|
||||
- older failing auto-load attempts never reached `0x00445ac0`
|
||||
- the earlier failing breakpoint was `0x00517cf0` with:
|
||||
- `[0x006cec78] = 0`
|
||||
- `[0x006cec74] = 0x01d81230`
|
||||
- the staged request globals at `0x006ce9b8..0x006ce9c4` and `0x006d1270..0x006d127c` are zero on the successful manual path
|
||||
|
||||
That older `0x00517cf0` result is no longer the current blocker. The hook now reaches the real coordinator entry, so the remaining gap is later shell timing or re-entrancy, not request-latch shape.
|
||||
|
||||
The disassembly at `0x004390b0..0x004390cb` is now the strongest grounded manual-load branch:
|
||||
|
||||
- it writes `[0x006cec74+0x6c] = 1`
|
||||
- it computes `arg1` from `([0x006cec7c] + 0x11)`
|
||||
- it pushes `arg2 = 4`
|
||||
- it passes `arg3 = &out_success`
|
||||
- and then calls `0x00445ac0`
|
||||
|
||||
So any hook experiment that does not reproduce that exact shape is no longer a plausible match for the successful manual path.
|
||||
|
||||
## Latest Auto-Load Comparison
|
||||
|
||||
The newest hook-driven debugger run now reaches `0x00445ac0` directly.
|
||||
|
||||
At the auto-load `0x00445ac0` breakpoint:
|
||||
|
||||
- stack:
|
||||
- `ret = 0x7650505c` inside `dinput8`
|
||||
- `arg1 = 0x01db4739`
|
||||
- `arg2 = 4`
|
||||
- `arg3 = 0x0022fcf8`
|
||||
- globals:
|
||||
- `[0x006cec74] = 0x01d81230`
|
||||
- `[0x006cec7c] = 0x01db4728`
|
||||
- `[0x006cec78] = 0x026d7b88`
|
||||
|
||||
Compared to the successful manual path:
|
||||
|
||||
- `arg1` matches exactly: `0x01db4739`
|
||||
- `arg2` matches exactly: `4`
|
||||
- `[0x006cec74]` matches exactly: `0x01d81230`
|
||||
- `[0x006cec7c]` still matches the same runtime-profile base used to derive `arg1`
|
||||
- `[0x006cec78]` is now non-null and published before entry
|
||||
|
||||
So the hook is no longer missing the coordinator entry shape. The remaining question is no longer "can we reach `0x00445ac0`?" but "does the live non-debugger call return successfully and trigger the actual restore transition?"
|
||||
|
||||
## Latest Live Crash
|
||||
|
||||
The latest non-debugger auto-load run now reaches:
|
||||
|
||||
- `rrt-hook: auto load ready gate passed`
|
||||
- `rrt-hook: auto load restore calling`
|
||||
|
||||
and then crashes at:
|
||||
|
||||
- `0x0053fea6`
|
||||
|
||||
The local disassembly around `0x0053fe90` shows a shell-side list traversal over `[this+0x74]` that walks linked entries and calls a virtual method on each. The crash instruction at `0x0053fea6` dereferences one traversed entry:
|
||||
|
||||
- `mov eax, DWORD PTR [esi]`
|
||||
|
||||
That strongly suggests the current hook is invoking the restore from the right call shape but on the wrong shell-pump turn. The active hypothesis is now timing or re-entrancy:
|
||||
|
||||
- the hook detects readiness and fires restore on the same shell-pump turn
|
||||
- RT3 later re-enters shell object traversal in a phase where one list entry is still invalid
|
||||
|
||||
So the next experiment is to defer the actual restore by additional ready shell-pump turns instead of firing on the first ready turn.
|
||||
|
||||
## Manual Owner Tail
|
||||
|
||||
The branch at `0x004390b0..0x004390ea` now has a grounded post-call tail too:
|
||||
|
||||
- `0x004390cb` calls `0x00445ac0`
|
||||
- `0x004390d0` immediately calls `0x004834e0(0, 1)` on `0x006cec74`
|
||||
- if `out_success != 0` or `esi != 0`, `0x004390ea` calls `0x004384d0`
|
||||
- then `0x004390ef` calls `0x0053f310` on `0x00ccbb20`
|
||||
- then `0x00439104` calls `0x004834e0(0, 1)` again
|
||||
|
||||
The successful manual breakpoint at `0x004390cb` shows `ESI = 0` and `EDI = 1`, so the manual load branch only forces the `0x004384d0` post-load pipeline when `out_success` comes back nonzero.
|
||||
|
||||
That makes the current hook gap narrower still: even with the correct `0x00445ac0` arguments, returning directly into `dinput8` skips RT3's own owner-tail work unless we mirror it ourselves.
|
||||
|
||||
## Owner Xrefs Above `0x438890`
|
||||
|
||||
The containing owner at `0x00438890` is now grounded as a larger `thiscall` shell owner with two stack arguments. Current xrefs found in local disassembly are:
|
||||
|
||||
- `0x00443b57`
|
||||
- `0x00446d7f`
|
||||
- `0x0046b8bc`
|
||||
- `0x004830ca`
|
||||
|
||||
The strongest caller so far is `0x004830ca`:
|
||||
|
||||
- it publishes `0x006cec78 = eax`
|
||||
- then calls `0x00438890` as `thiscall(active_mode, 1, 0)`
|
||||
- it sits inside `shell_transition_mode`
|
||||
- it is the branch that constructs `LoadScreen.win` through `0x004ea620`
|
||||
- and it continues through shell-window follow-up on `0x006d401c` after the `0x00438890` call
|
||||
|
||||
The surrounding mode map is tighter now too:
|
||||
|
||||
- mode `1` = `Game.win`
|
||||
- mode `2` = `Setup.win`
|
||||
- mode `3` = `Video.win`
|
||||
- mode `4` = `LoadScreen.win`
|
||||
- mode `5` = `Multiplayer.win`
|
||||
- mode `6` = `Credits.win`
|
||||
- mode `7` = `Campaign.win`
|
||||
|
||||
That makes `0x00438890(active_mode, 1, 0)` the strongest current RT3-native entry candidate for reproducing the successful manual load branch, because it owns the internal dispatch that later reaches `0x004390cb`.
|
||||
|
||||
Current static xrefs also tighten the broader ownership split:
|
||||
|
||||
- `0x00443b57` calls `0x00438890` from the world-entry side, but with `(0, 0)` after dismissing the current shell detail panel and servicing `0x4834e0(0, 0)`
|
||||
- `0x00446d7f` calls it from the saved-runtime restore side with the same `(0, 0)` shape before immediately building `.smp` bundle payloads through `0x530c80/0x531150/0x531360`
|
||||
- `0x0046b8bc` calls it from the multiplayer preview family before a later `0x00445ac0` call
|
||||
- `0x004830ca` calls it from the shell-side active-mode branch with the clearest `(1, 0)` setup
|
||||
|
||||
So the function is no longer just a guessed hook target. It is now a real shared owner above world-entry, saved-runtime restore, multiplayer preview, and shell-side active-mode startup branches.
|
||||
|
||||
The internal selector split inside `0x00438890` is tighter now too:
|
||||
|
||||
- `[0x006cec7c+0x01]` is a startup-profile selector, not the shell mode id
|
||||
- selector values `1` and `7` share the tutorial lane at `0x00438f67`, which writes
|
||||
`[0x006cec74+0x6c] = 2` and loads `Tutorial_2.gmp` or `Tutorial_1.gmp`
|
||||
- selector `2` is a world-root initialization lane at `0x00438fbe` that allocates `0x0062c120`
|
||||
when needed, runs `0x0044faf0`, and then forces the selector to `3`
|
||||
- selector `4` is a setup-side world reset or regeneration lane at `0x00439038` that rebuilds
|
||||
`0x0062c120` from setup globals `0x006d14cc/0x006d14d0`, then runs `0x00535100` and `0x0040b830`
|
||||
- selector values `3`, `5`, and `6` collapse into the same profile-seeded file-load lane at
|
||||
`0x004390b0..0x004390ea`
|
||||
- selector `6` is the one variant that explicitly writes `[0x006cec74+0x6c] = 1` before the
|
||||
shared file-load call
|
||||
|
||||
Current grounded writers now tighten those values too:
|
||||
|
||||
- `Campaign.win` writes selector `6` at `0x004b8a2f`
|
||||
- `Multiplayer.win` writes selector `3` on one pending-status branch at `0x004f041e`
|
||||
- the larger `Setup.win` dispatcher around `0x005033d0..0x00503b7b` writes selectors `2`, `3`, `4`,
|
||||
and `5` on several validated launch branches
|
||||
- so the shared file-load lane is now best read as one reused profile-file startup family rather
|
||||
than one owner-specific manual-load path
|
||||
|
||||
That means the successful manual-load branch is not the whole function. It is one three-selector
|
||||
subfamily inside a broader startup dispatcher that also owns tutorial and fresh-world setup lanes.
|
||||
|
||||
The multiplayer preview side is also tighter now:
|
||||
|
||||
- `0x0046b8bc` publishes `0x006cec78`
|
||||
- calls `0x00438890` as `thiscall(active_mode, 0, 0)`
|
||||
- clears `[0x006cec74+0x6c]`
|
||||
- and only then calls `0x00445ac0(0x006ce630, [0x006ce9c0], 0)`
|
||||
|
||||
That makes the preview relaunch path clearly different from the manual load branch, not just a differently staged copy of it.
|
||||
|
||||
## Latest Headless Debugger Result
|
||||
|
||||
The scripted auto-load debugger run is now useful without manual interaction:
|
||||
|
||||
- all breakpoints were set successfully:
|
||||
- `0x00438890`
|
||||
- `0x004390cb`
|
||||
- `0x00445ac0`
|
||||
- `0x0053fea6`
|
||||
- but only `0x0053fea6` actually fired in the captured run
|
||||
|
||||
So the current non-interactive path is good enough to gather repeatable crash-side state, but it also tells us that the current auto-load code path is still not obviously traversing the larger-owner breakpoints under `winedbg`. The next step is therefore more hook-side logging around the `0x00438890` call itself rather than more manual debugger work.
|
||||
|
||||
The latest static pivot also means the next reverse-engineering step does not require a live run:
|
||||
|
||||
- compare the mode-`4` `LoadScreen.win` owner path at `0x004830ca` against the world-entry and
|
||||
saved-runtime callers of `0x00438890`
|
||||
- compare how the `(1, 0)` `LoadScreen.win` lane diverges from the `(0, 0)` world-entry and
|
||||
saved-runtime lanes before control reaches the shared `0x004390b0` manual-load branch
|
||||
- only then return to hook experiments
|
||||
|
||||
## Launchers
|
||||
|
||||
Manual debugger run:
|
||||
|
||||
```bash
|
||||
tools/run_rt3_winedbg.sh
|
||||
```
|
||||
|
||||
Auto-load debugger run:
|
||||
|
||||
```bash
|
||||
tools/run_hook_auto_load_winedbg.sh hh
|
||||
```
|
||||
|
||||
Both scripts use `/opt/wine-stable/bin/winedbg` explicitly, so they do not depend on `winedbg` being on `PATH`.
|
||||
They also default to:
|
||||
|
||||
- their matching command file in [tools/](/home/jan/projects/rrt/tools)
|
||||
- a logfile in the repo root:
|
||||
- [rt3_manual_load_winedbg.log](/home/jan/projects/rrt/rt3_manual_load_winedbg.log)
|
||||
- [rt3_auto_load_winedbg.log](/home/jan/projects/rrt/rt3_auto_load_winedbg.log)
|
||||
|
||||
To save the full interactive debugger session to a file, set `RRT_WINEDBG_LOG`:
|
||||
|
||||
```bash
|
||||
RRT_WINEDBG_LOG=/tmp/rt3-manual-load-winedbg.log tools/run_rt3_winedbg.sh
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```bash
|
||||
RRT_WINEDBG_LOG=/tmp/rt3-auto-load-winedbg.log tools/run_hook_auto_load_winedbg.sh hh
|
||||
```
|
||||
|
||||
Those wrappers use `script`, so both the commands you type and the debugger output are captured.
|
||||
|
||||
`winedbg` under `/opt/wine-stable` also supports command files directly:
|
||||
|
||||
```bash
|
||||
tools/run_rt3_winedbg.sh
|
||||
```
|
||||
|
||||
and:
|
||||
|
||||
```bash
|
||||
tools/run_hook_auto_load_winedbg.sh hh
|
||||
```
|
||||
|
||||
Override either default if needed:
|
||||
|
||||
```bash
|
||||
RRT_WINEDBG_LOG=/tmp/rt3-manual-load-winedbg.log tools/run_rt3_winedbg.sh
|
||||
```
|
||||
|
||||
Ready-made debugger command files are also provided:
|
||||
|
||||
- [winedbg_manual_load_445ac0.cmd](/home/jan/projects/rrt/tools/winedbg_manual_load_445ac0.cmd)
|
||||
- [winedbg_auto_load_compare.cmd](/home/jan/projects/rrt/tools/winedbg_auto_load_compare.cmd)
|
||||
|
||||
If you do not use `RRT_WINEDBG_CMD_FILE`, you can still open those files and paste their contents into the debugger manually.
|
||||
|
||||
Both scripts rebuild `rrt-hook`, copy `dinput8.dll` into the Wine RT3 directory, and launch RT3 under `winedbg`.
|
||||
|
||||
## Successful Manual Load
|
||||
|
||||
1. Launch:
|
||||
|
||||
```bash
|
||||
tools/run_rt3_winedbg.sh
|
||||
```
|
||||
|
||||
2. The default command file now breaks on both:
|
||||
- `0x004390cb` first
|
||||
- `0x00445ac0` second
|
||||
|
||||
3. In RT3, load save `hh` manually.
|
||||
|
||||
4. The command file will dump:
|
||||
- registers
|
||||
- top-of-stack dwords
|
||||
- `0x006cec74`
|
||||
- `0x006cec7c`
|
||||
- `0x006cec78`
|
||||
- `0x006ce9b8..0x006ce9c4`
|
||||
- `0x006d1270..0x006d127c`
|
||||
- backtrace
|
||||
|
||||
Focus on:
|
||||
|
||||
- whether the first hit is `0x004390cb` or `0x00445ac0`
|
||||
- caller address
|
||||
- `ecx`
|
||||
- the three stack arguments
|
||||
- `0x006cec74`
|
||||
- `0x006cec7c`
|
||||
- `0x006cec78`
|
||||
- `0x006ce9b8..0x006ce9c4`
|
||||
- `0x006d1270..`
|
||||
|
||||
## Failing Auto-Load Run
|
||||
|
||||
1. Launch:
|
||||
|
||||
```bash
|
||||
tools/run_hook_auto_load_winedbg.sh hh
|
||||
```
|
||||
|
||||
2. The default command file now scripts a fuller non-interactive capture sequence:
|
||||
- `0x00438890`
|
||||
- `0x004390cb`
|
||||
- `0x00445ac0`
|
||||
- `0x0053fea6`
|
||||
|
||||
3. Let the hook run.
|
||||
|
||||
4. The command file will dump the same register, stack, global, and backtrace state at the first hit.
|
||||
|
||||
5. Compare that output directly against the successful manual run.
|
||||
|
||||
So the current auto debugger path is now mostly headless:
|
||||
|
||||
- launch `tools/run_hook_auto_load_winedbg.sh hh`
|
||||
- let the scripted breakpoints run
|
||||
- inspect [rt3_auto_load_winedbg.log](/home/jan/projects/rrt/rt3_auto_load_winedbg.log)
|
||||
|
||||
Manual typing is no longer required for the main auto-load comparison path unless we need an additional ad hoc breakpoint.
|
||||
|
||||
If the run still crashes and you need even earlier crash-side inspection after that, add one temporary extra breakpoint manually for:
|
||||
|
||||
- `0x00517cf0`
|
||||
|
||||
## Optional Host-Side GDB Fallback
|
||||
|
||||
If `winedbg` is too clumsy for repeated crashes, attach host `gdb` to the crashing Wine process after RT3 starts:
|
||||
|
||||
```bash
|
||||
pgrep -af 'wine.*RT3.exe'
|
||||
gdb -p <pid>
|
||||
```
|
||||
|
||||
Useful commands in `gdb`:
|
||||
|
||||
```gdb
|
||||
set pagination off
|
||||
handle SIGSEGV stop print
|
||||
continue
|
||||
bt
|
||||
info registers
|
||||
x/16wx $esp
|
||||
```
|
||||
|
||||
This is mainly for cleaner backtraces after the fault PC is already known from `winedbg`.
|
||||
170
docs/shell-load-graph.md
Normal file
170
docs/shell-load-graph.md
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
# Shell Load Graph
|
||||
|
||||
Generated shell-load startup graph artifacts live under `artifacts/exports/rt3-1.06/`:
|
||||
|
||||
- `shell-load-subgraph.dot`
|
||||
- `shell-load-subgraph.md`
|
||||
- `setup-window-subgraph.dot`
|
||||
- `setup-window-subgraph.md`
|
||||
- `setup-window-submodes-depth5-subgraph.dot`
|
||||
- `setup-window-submodes-depth5-subgraph.md`
|
||||
- `setup-window-submodes-depth5-forward-subgraph.dot`
|
||||
- `setup-window-submodes-depth5-forward-subgraph.md`
|
||||
- `runtime-effect-service-depth7-subgraph.dot`
|
||||
- `runtime-effect-service-depth7-subgraph.md`
|
||||
- `runtime-effect-service-depth7-forward-subgraph.dot`
|
||||
- `runtime-effect-service-depth7-forward-subgraph.md`
|
||||
|
||||
The current graph is intentionally bounded rather than whole-program:
|
||||
|
||||
- seed nodes:
|
||||
- `0x00438890` `shell_active_mode_run_profile_startup_and_load_dispatch`
|
||||
- `0x00482ec0` `shell_transition_mode`
|
||||
- traversal:
|
||||
- note-address references in `function-map.csv`
|
||||
- depth `2`
|
||||
- backreferences enabled
|
||||
|
||||
This keeps the artifact useful for naming and branch comparison instead of exploding into generic
|
||||
utility helpers.
|
||||
|
||||
The paired `Setup.win` graph is a narrower owner-family export:
|
||||
|
||||
- seeds:
|
||||
- `0x00504010` `shell_setup_window_construct`
|
||||
- `0x005033d0` `shell_setup_window_handle_message`
|
||||
- `0x00502c00` `shell_setup_window_select_launch_mode_and_apply_shell_state`
|
||||
- `0x00502910` `shell_setup_window_refresh_mode_dependent_lists`
|
||||
- `0x00502550` `shell_setup_window_refresh_selection_lists_and_summary_fields`
|
||||
- `0x00502220` `shell_setup_window_publish_selected_profile_labels_and_preview_surface`
|
||||
|
||||
Regenerate it with:
|
||||
|
||||
```bash
|
||||
python3 tools/py/export_function_subgraph.py \
|
||||
artifacts/exports/rt3-1.06/function-map.csv \
|
||||
artifacts/exports/rt3-1.06/shell-load-subgraph \
|
||||
--seed 0x00438890 \
|
||||
--seed 0x00482ec0 \
|
||||
--depth 2 \
|
||||
--include-backrefs \
|
||||
--title "Shell Load Startup Subgraph"
|
||||
```
|
||||
|
||||
And for the narrower `Setup.win` graph:
|
||||
|
||||
```bash
|
||||
python3 tools/py/export_function_subgraph.py \
|
||||
artifacts/exports/rt3-1.06/function-map.csv \
|
||||
artifacts/exports/rt3-1.06/setup-window-subgraph \
|
||||
--seed 0x00504010 \
|
||||
--seed 0x005033d0 \
|
||||
--seed 0x00502c00 \
|
||||
--seed 0x00502910 \
|
||||
--seed 0x00502550 \
|
||||
--seed 0x00502220 \
|
||||
--depth 2 \
|
||||
--include-backrefs \
|
||||
--title "Setup Window Dispatch Subgraph"
|
||||
```
|
||||
|
||||
The current exporter is intentionally note-driven:
|
||||
|
||||
- nodes come from `function-map.csv`
|
||||
- edges come from address references embedded in notes
|
||||
- so it is best used as a bounded reasoning aid over already-curated rows, not as a substitute for
|
||||
raw disassembly or xref recovery
|
||||
|
||||
There is now also a deeper `Setup.win` submode export:
|
||||
|
||||
- the backref-enabled depth-5 variant is exploratory and broad
|
||||
- seeds: the same constructor, dispatcher, selector, and refresh owners
|
||||
- depth: `5`
|
||||
- backrefs: enabled
|
||||
- current size: `369` nodes / `753` edges
|
||||
- the forward-only depth-5 variant is the recommended artifact for submode work
|
||||
- seeds: the same constructor, dispatcher, selector, and refresh owners
|
||||
- depth: `5`
|
||||
- backrefs: disabled
|
||||
- current size: `126` nodes / `246` edges
|
||||
|
||||
Generate the exploratory backref-enabled graph with:
|
||||
|
||||
```bash
|
||||
python3 tools/py/export_function_subgraph.py \
|
||||
artifacts/exports/rt3-1.06/function-map.csv \
|
||||
artifacts/exports/rt3-1.06/setup-window-submodes-depth5-subgraph \
|
||||
--seed 0x00504010 \
|
||||
--seed 0x005033d0 \
|
||||
--seed 0x00502c00 \
|
||||
--seed 0x00502910 \
|
||||
--seed 0x005027b0 \
|
||||
--seed 0x00502550 \
|
||||
--seed 0x00502220 \
|
||||
--depth 5 \
|
||||
--include-backrefs \
|
||||
--title "Setup Window Submode Subgraph (Depth 5)"
|
||||
```
|
||||
|
||||
Generate the narrower forward-only graph with:
|
||||
|
||||
```bash
|
||||
python3 tools/py/export_function_subgraph.py \
|
||||
artifacts/exports/rt3-1.06/function-map.csv \
|
||||
artifacts/exports/rt3-1.06/setup-window-submodes-depth5-forward-subgraph \
|
||||
--seed 0x00504010 \
|
||||
--seed 0x005033d0 \
|
||||
--seed 0x00502c00 \
|
||||
--seed 0x00502910 \
|
||||
--seed 0x005027b0 \
|
||||
--seed 0x00502550 \
|
||||
--seed 0x00502220 \
|
||||
--depth 5 \
|
||||
--title "Setup Window Submode Subgraph (Depth 5, Forward Only)"
|
||||
```
|
||||
|
||||
There is now also a deeper runtime-effect export around the scenario event collection service loop:
|
||||
|
||||
- the backref-enabled depth-7 variant is exploratory and broad
|
||||
- seeds:
|
||||
- `0x004323a0` `scenario_runtime_effect_record_service_and_dispatch_linked_compact_effects`
|
||||
- `0x00431b20` `world_apply_compact_runtime_effect_record_to_resolved_targets`
|
||||
- `0x00430b50` `scenario_runtime_effect_record_build_followon_effect_from_compact_record_and_targets`
|
||||
- `0x00432ea0` `scenario_event_collection_allocate_runtime_effect_record_from_compact_payload`
|
||||
- depth: `7`
|
||||
- backrefs: enabled
|
||||
- current size: `525` nodes / `1235` edges
|
||||
- the forward-only depth-7 variant is the recommended artifact for this branch
|
||||
- same seeds
|
||||
- depth: `7`
|
||||
- backrefs: disabled
|
||||
- current size: `293` nodes / `752` edges
|
||||
|
||||
Generate the exploratory backref-enabled graph with:
|
||||
|
||||
```bash
|
||||
python3 tools/py/export_function_subgraph.py \
|
||||
artifacts/exports/rt3-1.06/function-map.csv \
|
||||
artifacts/exports/rt3-1.06/runtime-effect-service-depth7-subgraph \
|
||||
--seed 0x004323a0 \
|
||||
--seed 0x00431b20 \
|
||||
--seed 0x00430b50 \
|
||||
--seed 0x00432ea0 \
|
||||
--depth 7 \
|
||||
--include-backrefs \
|
||||
--title "Scenario Runtime Effect Service Subgraph (Depth 7)"
|
||||
```
|
||||
|
||||
Generate the narrower forward-only graph with:
|
||||
|
||||
```bash
|
||||
python3 tools/py/export_function_subgraph.py \
|
||||
artifacts/exports/rt3-1.06/function-map.csv \
|
||||
artifacts/exports/rt3-1.06/runtime-effect-service-depth7-forward-subgraph \
|
||||
--seed 0x004323a0 \
|
||||
--seed 0x00431b20 \
|
||||
--seed 0x00430b50 \
|
||||
--seed 0x00432ea0 \
|
||||
--depth 7 \
|
||||
--title "Scenario Runtime Effect Service Subgraph (Depth 7, Forward Only)"
|
||||
```
|
||||
260
tools/py/export_function_subgraph.py
Normal file
260
tools/py/export_function_subgraph.py
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import re
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]{8}")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Row:
|
||||
address: int
|
||||
size: str
|
||||
name: str
|
||||
subsystem: str
|
||||
calling_convention: str
|
||||
prototype_status: str
|
||||
source_tool: str
|
||||
confidence: str
|
||||
notes: str
|
||||
verified_against: str
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Export a bounded function subgraph from function-map.csv notes."
|
||||
)
|
||||
parser.add_argument("function_map", type=Path)
|
||||
parser.add_argument("output_prefix", type=Path)
|
||||
parser.add_argument(
|
||||
"--seed",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Seed function address in hex. May be repeated.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--depth",
|
||||
type=int,
|
||||
default=2,
|
||||
help="Traversal depth over note-address references.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--title",
|
||||
default="Function Subgraph",
|
||||
help="Title used in the markdown summary.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--include-backrefs",
|
||||
action="store_true",
|
||||
help="Also traverse rows that reference currently included nodes.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
if not args.seed:
|
||||
parser.error("at least one --seed is required")
|
||||
return args
|
||||
|
||||
|
||||
def parse_hex(text: str) -> int:
|
||||
value = text.strip().lower()
|
||||
if value.startswith("0x"):
|
||||
value = value[2:]
|
||||
return int(value, 16)
|
||||
|
||||
|
||||
def fmt_addr(value: int) -> str:
|
||||
return f"0x{value:08x}"
|
||||
|
||||
|
||||
def load_rows(path: Path) -> dict[int, Row]:
|
||||
with path.open(newline="", encoding="utf-8") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
rows: dict[int, Row] = {}
|
||||
for raw in reader:
|
||||
address = parse_hex(raw["address"])
|
||||
rows[address] = Row(
|
||||
address=address,
|
||||
size=raw["size"],
|
||||
name=raw["name"],
|
||||
subsystem=raw["subsystem"],
|
||||
calling_convention=raw["calling_convention"],
|
||||
prototype_status=raw["prototype_status"],
|
||||
source_tool=raw["source_tool"],
|
||||
confidence=raw["confidence"],
|
||||
notes=raw["notes"],
|
||||
verified_against=raw["verified_against"],
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def extract_note_refs(rows: dict[int, Row]) -> dict[int, set[int]]:
|
||||
refs: dict[int, set[int]] = {}
|
||||
known = set(rows)
|
||||
for address, row in rows.items():
|
||||
hits = {parse_hex(match.group(0)) for match in ADDRESS_RE.finditer(row.notes)}
|
||||
refs[address] = {hit for hit in hits if hit in known and hit != address}
|
||||
return refs
|
||||
|
||||
|
||||
def build_backrefs(refs: dict[int, set[int]]) -> dict[int, set[int]]:
|
||||
backrefs: dict[int, set[int]] = {address: set() for address in refs}
|
||||
for src, dsts in refs.items():
|
||||
for dst in dsts:
|
||||
backrefs.setdefault(dst, set()).add(src)
|
||||
return backrefs
|
||||
|
||||
|
||||
def walk_subgraph(
|
||||
rows: dict[int, Row],
|
||||
refs: dict[int, set[int]],
|
||||
seeds: list[int],
|
||||
depth: int,
|
||||
include_backrefs: bool,
|
||||
) -> set[int]:
|
||||
backrefs = build_backrefs(refs)
|
||||
seen: set[int] = set()
|
||||
queue: deque[tuple[int, int]] = deque((seed, 0) for seed in seeds if seed in rows)
|
||||
|
||||
while queue:
|
||||
address, level = queue.popleft()
|
||||
if address in seen:
|
||||
continue
|
||||
seen.add(address)
|
||||
if level >= depth:
|
||||
continue
|
||||
|
||||
for dst in sorted(refs.get(address, ())):
|
||||
if dst not in seen:
|
||||
queue.append((dst, level + 1))
|
||||
|
||||
if include_backrefs:
|
||||
for src in sorted(backrefs.get(address, ())):
|
||||
if src not in seen:
|
||||
queue.append((src, level + 1))
|
||||
|
||||
return seen
|
||||
|
||||
|
||||
def quote_dot(text: str) -> str:
|
||||
return text.replace("\\", "\\\\").replace('"', '\\"')
|
||||
|
||||
|
||||
def emit_dot(
|
||||
rows: dict[int, Row],
|
||||
refs: dict[int, set[int]],
|
||||
included: set[int],
|
||||
seeds: set[int],
|
||||
output_path: Path,
|
||||
title: str,
|
||||
) -> None:
|
||||
subsystems: dict[str, list[Row]] = {}
|
||||
for address in sorted(included):
|
||||
row = rows[address]
|
||||
subsystems.setdefault(row.subsystem, []).append(row)
|
||||
|
||||
lines: list[str] = [
|
||||
"digraph shell_load {",
|
||||
' graph [rankdir=LR, labelloc="t", labeljust="l"];',
|
||||
f' label="{quote_dot(title)}";',
|
||||
' node [shape=box, style="rounded,filled", fillcolor="#f8f8f8", color="#555555", fontname="Helvetica"];',
|
||||
' edge [color="#666666", fontname="Helvetica"];',
|
||||
]
|
||||
|
||||
for subsystem in sorted(subsystems):
|
||||
cluster_id = subsystem.replace("-", "_")
|
||||
lines.append(f' subgraph cluster_{cluster_id} {{')
|
||||
lines.append(f' label="{quote_dot(subsystem)}";')
|
||||
lines.append(' color="#cccccc";')
|
||||
for row in subsystems[subsystem]:
|
||||
seed_mark = " [seed]" if row.address in seeds else ""
|
||||
label = f"{fmt_addr(row.address)}\\n{row.name}{seed_mark}"
|
||||
fill = "#ffe9a8" if row.address in seeds else "#f8f8f8"
|
||||
lines.append(
|
||||
f' "{fmt_addr(row.address)}" [label="{quote_dot(label)}", fillcolor="{fill}"];'
|
||||
)
|
||||
lines.append(" }")
|
||||
|
||||
for src in sorted(included):
|
||||
for dst in sorted(refs.get(src, ())):
|
||||
if dst not in included:
|
||||
continue
|
||||
lines.append(f' "{fmt_addr(src)}" -> "{fmt_addr(dst)}";')
|
||||
|
||||
lines.append("}")
|
||||
output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def emit_markdown(
|
||||
rows: dict[int, Row],
|
||||
refs: dict[int, set[int]],
|
||||
included: set[int],
|
||||
seeds: set[int],
|
||||
output_path: Path,
|
||||
title: str,
|
||||
dot_path: Path,
|
||||
) -> None:
|
||||
included_rows = [rows[address] for address in sorted(included)]
|
||||
edge_count = sum(
|
||||
1
|
||||
for src in included
|
||||
for dst in refs.get(src, ())
|
||||
if dst in included
|
||||
)
|
||||
|
||||
lines = [
|
||||
f"# {title}",
|
||||
"",
|
||||
f"- Nodes: `{len(included_rows)}`",
|
||||
f"- Edges: `{edge_count}`",
|
||||
f"- Seeds: {', '.join(f'`{fmt_addr(seed)}`' for seed in sorted(seeds))}",
|
||||
f"- Graphviz: `{dot_path.name}`",
|
||||
"",
|
||||
"## Nodes",
|
||||
"",
|
||||
"| Address | Name | Subsystem | Confidence |",
|
||||
"| --- | --- | --- | --- |",
|
||||
]
|
||||
|
||||
for row in included_rows:
|
||||
lines.append(
|
||||
f"| `{fmt_addr(row.address)}` | `{row.name}` | `{row.subsystem}` | `{row.confidence}` |"
|
||||
)
|
||||
|
||||
lines.extend(["", "## Edges", ""])
|
||||
for src in sorted(included):
|
||||
dsts = [dst for dst in sorted(refs.get(src, ())) if dst in included]
|
||||
if not dsts:
|
||||
continue
|
||||
src_row = rows[src]
|
||||
lines.append(f"- `{fmt_addr(src)}` `{src_row.name}`")
|
||||
for dst in dsts:
|
||||
dst_row = rows[dst]
|
||||
lines.append(f" -> `{fmt_addr(dst)}` `{dst_row.name}`")
|
||||
|
||||
output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
rows = load_rows(args.function_map)
|
||||
refs = extract_note_refs(rows)
|
||||
seeds = [parse_hex(seed) for seed in args.seed]
|
||||
included = walk_subgraph(rows, refs, seeds, args.depth, args.include_backrefs)
|
||||
|
||||
output_prefix = args.output_prefix.resolve()
|
||||
output_prefix.parent.mkdir(parents=True, exist_ok=True)
|
||||
dot_path = output_prefix.with_suffix(".dot")
|
||||
md_path = output_prefix.with_suffix(".md")
|
||||
|
||||
emit_dot(rows, refs, included, set(seeds), dot_path, args.title)
|
||||
emit_markdown(rows, refs, included, set(seeds), md_path, args.title, dot_path)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
43
tools/run_hook_auto_load.sh
Executable file
43
tools/run_hook_auto_load.sh
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
game_dir="$repo_root/rt3_wineprefix/drive_c/rt3"
|
||||
log_path="$game_dir/rrt_hook_attach.log"
|
||||
proxy_path="$game_dir/dinput8.dll"
|
||||
save_stem="${1:-hh}"
|
||||
timeout_seconds="${RRT_HOOK_TIMEOUT_SECONDS:-60}"
|
||||
. "$HOME/.local/share/cargo/env"
|
||||
|
||||
cargo build -p rrt-hook --target i686-pc-windows-gnu
|
||||
|
||||
rm -f "$log_path"
|
||||
cp "$repo_root/target/i686-pc-windows-gnu/debug/dinput8.dll" "$proxy_path"
|
||||
|
||||
echo "launcher: start=$(date --iso-8601=seconds)"
|
||||
echo "launcher: save_stem=$save_stem timeout_seconds=$timeout_seconds"
|
||||
|
||||
set +e
|
||||
(
|
||||
cd "$game_dir"
|
||||
export RRT_AUTO_LOAD_SAVE="$save_stem"
|
||||
export WINEPREFIX="$repo_root/rt3_wineprefix"
|
||||
export WINEDLLOVERRIDES="dinput8=n,b"
|
||||
|
||||
timeout "${timeout_seconds}s" /opt/wine-stable/bin/wine RT3.exe
|
||||
) >/tmp/rrt-hook-auto-load-wine.log 2>&1 &
|
||||
launcher_pid=$!
|
||||
echo "launcher: wrapper_pid=$launcher_pid"
|
||||
wait "$launcher_pid"
|
||||
launcher_status=$?
|
||||
set -e
|
||||
|
||||
echo "launcher: exit_status=$launcher_status"
|
||||
echo "launcher: end=$(date --iso-8601=seconds)"
|
||||
|
||||
if [[ -f "$log_path" ]]; then
|
||||
cat "$log_path"
|
||||
else
|
||||
echo "attach-log-missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
31
tools/run_hook_auto_load_winedbg.sh
Executable file
31
tools/run_hook_auto_load_winedbg.sh
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
game_dir="$repo_root/rt3_wineprefix/drive_c/rt3"
|
||||
proxy_path="$game_dir/dinput8.dll"
|
||||
save_stem="${1:-hh}"
|
||||
. "$HOME/.local/share/cargo/env"
|
||||
|
||||
cargo build -p rrt-hook --target i686-pc-windows-gnu
|
||||
cp "$repo_root/target/i686-pc-windows-gnu/debug/dinput8.dll" "$proxy_path"
|
||||
|
||||
cd "$game_dir"
|
||||
export RRT_AUTO_LOAD_SAVE="$save_stem"
|
||||
export WINEPREFIX="$repo_root/rt3_wineprefix"
|
||||
export WINEDLLOVERRIDES="dinput8=n,b"
|
||||
|
||||
cmd=(/opt/wine-stable/bin/winedbg)
|
||||
cmd_file="${RRT_WINEDBG_CMD_FILE:-$repo_root/tools/winedbg_auto_load_compare.cmd}"
|
||||
if [[ -n "$cmd_file" ]]; then
|
||||
cmd+=(--file "$cmd_file")
|
||||
fi
|
||||
cmd+=(RT3.exe)
|
||||
|
||||
log_file="${RRT_WINEDBG_LOG:-$repo_root/rt3_auto_load_winedbg.log}"
|
||||
if [[ -n "$log_file" ]]; then
|
||||
cmd_string="$(printf '%q ' "${cmd[@]}")"
|
||||
exec script -qefc "${cmd_string% }" "$log_file"
|
||||
fi
|
||||
|
||||
exec "${cmd[@]}"
|
||||
|
|
@ -13,9 +13,11 @@ cargo build -p rrt-hook --target i686-pc-windows-gnu
|
|||
rm -f "$log_path"
|
||||
cp "$repo_root/target/i686-pc-windows-gnu/debug/dinput8.dll" "$proxy_path"
|
||||
|
||||
#RRT_WRITE_FINANCE_TEMPLATE=1 \
|
||||
(
|
||||
cd "$game_dir"
|
||||
timeout 8s env \
|
||||
timeout 600s env \
|
||||
RRT_WRITE_FINANCE_CAPTURE=1 \
|
||||
WINEPREFIX="$repo_root/rt3_wineprefix" \
|
||||
WINEDLLOVERRIDES="dinput8=n,b" \
|
||||
/opt/wine-stable/bin/wine RT3.exe
|
||||
|
|
|
|||
29
tools/run_rt3_winedbg.sh
Executable file
29
tools/run_rt3_winedbg.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
game_dir="$repo_root/rt3_wineprefix/drive_c/rt3"
|
||||
proxy_path="$game_dir/dinput8.dll"
|
||||
. "$HOME/.local/share/cargo/env"
|
||||
|
||||
cargo build -p rrt-hook --target i686-pc-windows-gnu
|
||||
cp "$repo_root/target/i686-pc-windows-gnu/debug/dinput8.dll" "$proxy_path"
|
||||
|
||||
cd "$game_dir"
|
||||
export WINEPREFIX="$repo_root/rt3_wineprefix"
|
||||
export WINEDLLOVERRIDES="dinput8=n,b"
|
||||
|
||||
cmd=(/opt/wine-stable/bin/winedbg)
|
||||
cmd_file="${RRT_WINEDBG_CMD_FILE:-$repo_root/tools/winedbg_manual_load_445ac0.cmd}"
|
||||
if [[ -n "$cmd_file" ]]; then
|
||||
cmd+=(--file "$cmd_file")
|
||||
fi
|
||||
cmd+=(RT3.exe)
|
||||
|
||||
log_file="${RRT_WINEDBG_LOG:-$repo_root/rt3_manual_load_winedbg.log}"
|
||||
if [[ -n "$log_file" ]]; then
|
||||
cmd_string="$(printf '%q ' "${cmd[@]}")"
|
||||
exec script -qefc "${cmd_string% }" "$log_file"
|
||||
fi
|
||||
|
||||
exec "${cmd[@]}"
|
||||
76
tools/winedbg_auto_load_compare.cmd
Normal file
76
tools/winedbg_auto_load_compare.cmd
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
break *0x00438890
|
||||
break *0x004390cb
|
||||
break *0x00445ac0
|
||||
break *0x0053fea6
|
||||
cont
|
||||
info reg
|
||||
print/x *(unsigned int*)($esp)
|
||||
print/x *(unsigned int*)($esp+4)
|
||||
print/x *(unsigned int*)($esp+8)
|
||||
print/x *(unsigned int*)($esp+12)
|
||||
print/x *(unsigned int*)0x006cec74
|
||||
print/x *(unsigned int*)0x006cec7c
|
||||
print/x *(unsigned int*)0x006cec78
|
||||
print/x *(unsigned int*)0x006ce9b8
|
||||
print/x *(unsigned int*)0x006ce9bc
|
||||
print/x *(unsigned int*)0x006ce9c0
|
||||
print/x *(unsigned int*)0x006ce9c4
|
||||
print/x *(unsigned int*)0x006d1270
|
||||
print/x *(unsigned int*)0x006d1274
|
||||
print/x *(unsigned int*)0x006d1278
|
||||
print/x *(unsigned int*)0x006d127c
|
||||
bt
|
||||
cont
|
||||
info reg
|
||||
print/x *(unsigned int*)($esp)
|
||||
print/x *(unsigned int*)($esp+4)
|
||||
print/x *(unsigned int*)($esp+8)
|
||||
print/x *(unsigned int*)($esp+12)
|
||||
print/x *(unsigned int*)0x006cec74
|
||||
print/x *(unsigned int*)0x006cec7c
|
||||
print/x *(unsigned int*)0x006cec78
|
||||
print/x *(unsigned int*)0x006ce9b8
|
||||
print/x *(unsigned int*)0x006ce9bc
|
||||
print/x *(unsigned int*)0x006ce9c0
|
||||
print/x *(unsigned int*)0x006ce9c4
|
||||
print/x *(unsigned int*)0x006d1270
|
||||
print/x *(unsigned int*)0x006d1274
|
||||
print/x *(unsigned int*)0x006d1278
|
||||
print/x *(unsigned int*)0x006d127c
|
||||
bt
|
||||
cont
|
||||
info reg
|
||||
print/x *(unsigned int*)($esp)
|
||||
print/x *(unsigned int*)($esp+4)
|
||||
print/x *(unsigned int*)($esp+8)
|
||||
print/x *(unsigned int*)($esp+12)
|
||||
print/x *(unsigned int*)0x006cec74
|
||||
print/x *(unsigned int*)0x006cec7c
|
||||
print/x *(unsigned int*)0x006cec78
|
||||
print/x *(unsigned int*)0x006ce9b8
|
||||
print/x *(unsigned int*)0x006ce9bc
|
||||
print/x *(unsigned int*)0x006ce9c0
|
||||
print/x *(unsigned int*)0x006ce9c4
|
||||
print/x *(unsigned int*)0x006d1270
|
||||
print/x *(unsigned int*)0x006d1274
|
||||
print/x *(unsigned int*)0x006d1278
|
||||
print/x *(unsigned int*)0x006d127c
|
||||
bt
|
||||
cont
|
||||
info reg
|
||||
print/x *(unsigned int*)($esp)
|
||||
print/x *(unsigned int*)($esp+4)
|
||||
print/x *(unsigned int*)($esp+8)
|
||||
print/x *(unsigned int*)($esp+12)
|
||||
print/x *(unsigned int*)0x006cec74
|
||||
print/x *(unsigned int*)0x006cec7c
|
||||
print/x *(unsigned int*)0x006cec78
|
||||
print/x *(unsigned int*)0x006ce9b8
|
||||
print/x *(unsigned int*)0x006ce9bc
|
||||
print/x *(unsigned int*)0x006ce9c0
|
||||
print/x *(unsigned int*)0x006ce9c4
|
||||
print/x *(unsigned int*)0x006d1270
|
||||
print/x *(unsigned int*)0x006d1274
|
||||
print/x *(unsigned int*)0x006d1278
|
||||
print/x *(unsigned int*)0x006d127c
|
||||
bt
|
||||
20
tools/winedbg_manual_load_445ac0.cmd
Normal file
20
tools/winedbg_manual_load_445ac0.cmd
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
break *0x004390cb
|
||||
break *0x00445ac0
|
||||
cont
|
||||
info reg
|
||||
print/x *(unsigned int*)($esp)
|
||||
print/x *(unsigned int*)($esp+4)
|
||||
print/x *(unsigned int*)($esp+8)
|
||||
print/x *(unsigned int*)($esp+12)
|
||||
print/x *(unsigned int*)0x006cec74
|
||||
print/x *(unsigned int*)0x006cec7c
|
||||
print/x *(unsigned int*)0x006cec78
|
||||
print/x *(unsigned int*)0x006ce9b8
|
||||
print/x *(unsigned int*)0x006ce9bc
|
||||
print/x *(unsigned int*)0x006ce9c0
|
||||
print/x *(unsigned int*)0x006ce9c4
|
||||
print/x *(unsigned int*)0x006d1270
|
||||
print/x *(unsigned int*)0x006d1274
|
||||
print/x *(unsigned int*)0x006d1278
|
||||
print/x *(unsigned int*)0x006d127c
|
||||
bt
|
||||
Loading…
Add table
Add a link
Reference in a new issue