nmarshall commited on
Commit
8316c9e
·
1 Parent(s): b8c9238

qvm: complete bounded weapon body

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -454,3 +454,6 @@ Research progress: RE-588 [report](research/QVM_FIRE_RESOURCE.md); remaining nat
454
 
455
 
456
  Research progress: RE-589 [report](research/QVM_FIRE_DELAY.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
454
 
455
 
456
  Research progress: RE-589 [report](research/QVM_FIRE_DELAY.md); remaining native/content/runtime/platform scope stays open.
457
+
458
+
459
+ Research progress: RE-590 [report](research/QVM_WEAPON_COMPLETE_BODY.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -812,3 +812,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
812
 
813
 
814
  **RE-589 — Final firing delay and last-round exception:** 52989..53045 adds the retained selected delay to PS44 only if the post-debit magazine is nonzero or selectedID27; other empty magazines return without adding it. [Evidence](research/QVM_FIRE_DELAY.md).
 
 
 
 
812
 
813
 
814
  **RE-589 — Final firing delay and last-round exception:** 52989..53045 adds the retained selected delay to PS44 only if the post-debit magazine is nonzero or selectedID27; other empty magazines return without adding it. [Evidence](research/QVM_FIRE_DELAY.md).
815
+
816
+
817
+ **RE-590 — Complete bounded weapon body and actual main consumer suffix:** The entire50746 QVM body now executes with recovered children and agrees with independent staged oracles; actual main54444..54463 resumes through firing and non-firing returns with live state handoffs. [Evidence](research/QVM_WEAPON_COMPLETE_BODY.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -770,3 +770,6 @@ Research progress: RE-588 [report](research/QVM_FIRE_RESOURCE.md); remaining nat
770
 
771
 
772
  Research progress: RE-589 [report](research/QVM_FIRE_DELAY.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
770
 
771
 
772
  Research progress: RE-589 [report](research/QVM_FIRE_DELAY.md); remaining native/content/runtime/platform scope stays open.
773
+
774
+
775
+ Research progress: RE-590 [report](research/QVM_WEAPON_COMPLETE_BODY.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -638,3 +638,8 @@ RE-580 additional regression: all19 reports from RE-560–569 and RE-571–579 r
638
  ## 2026-10-20 — RE-589 Final firing delay and last-round exception (separate task commit)
639
 
640
  52989..53045 adds the retained selected delay to PS44 only if the post-debit magazine is nonzero or selectedID27; other empty magazines return without adding it. 108 independent timer/delay/ID/magazine probes plus actual debit handoffs for IDs22/27 check wrap and last-round timer behavior without reloading the delay record. Full suite: 871 tests pass. Report regenerates byte-identically (376037 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
638
  ## 2026-10-20 — RE-589 Final firing delay and last-round exception (separate task commit)
639
 
640
  52989..53045 adds the retained selected delay to PS44 only if the post-debit magazine is nonzero or selectedID27; other empty magazines return without adding it. 108 independent timer/delay/ID/magazine probes plus actual debit handoffs for IDs22/27 check wrap and last-round timer behavior without reloading the delay record. Full suite: 871 tests pass. Report regenerates byte-identically (376037 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
641
+
642
+
643
+ ## 2026-10-20 — RE-590 Complete bounded weapon body and actual main consumer suffix (separate task commit)
644
+
645
+ The entire50746 QVM body now executes with recovered children and agrees with independent staged oracles; actual main54444..54463 resumes through firing and non-firing returns with live state handoffs. 12 complete-body scenarios and two six-child main suffixes check every reached stage, complete final state, direct callee inventory and source-order event/animation effects. Full suite: 875 tests pass. Report regenerates byte-identically (1246760 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -811,3 +811,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
811
 
812
 
813
  - [x] RE-589 Final firing delay and last-round exception ([report](research/QVM_FIRE_DELAY.md)).
 
 
 
 
811
 
812
 
813
  - [x] RE-589 Final firing delay and last-round exception ([report](research/QVM_FIRE_DELAY.md)).
814
+
815
+
816
+ - [x] RE-590 Complete bounded weapon body and actual main consumer suffix ([report](research/QVM_WEAPON_COMPLETE_BODY.md)).
docs/research/QVM_WEAPON_COMPLETE_BODY.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Complete bounded weapon body and actual main consumer suffix
2
+
3
+ RE-590. Reproduce with `python3 scripts/analyze_qvm_weapon_complete_body.py`; check with `python3 scripts/test_qvm_weapon_complete_body.py`. The [machine report](qvm-weapon-complete-body.json) retains source identity and bounded fixtures.
4
+
5
+ Actual50747..53045 is now interpreted as one bounded body, with explicit byte24 and real recovered50250/50397/49327/48798/49028/49652/49918/50309/animation/event children. Interaction traces remain supplied. A separate staged reconstruction preserves the same parent locals and has independent state/delay oracles for each slice.12 fixtures cover ordinary/last/empty, flag4, charge start/release, burst, lowering, reload, death, preserved mode and new selection. Whole-body results match those oracles.
6
+
7
+ All direct call target families in this function are inventoried. Actual main54444..54463 now runs all six consumers in one driver: reload, interaction, complete weapon, torso, locomotion, water. Two fixtures verify actual handoffs and queue ordering. The earlier firing-body suspension has been closed for these finite fixture paths; no unknown child is silently treated as successful.
8
+
9
+ This is complete bounded QVM function coverage, not exhaustive legal-state coverage or full game execution. FRAME260/264 still only publishes a firing request. Earlier main setup/timers/movement/environment and final native snap54471 are omitted; native projectile/hitscan outcomes, input legality and platform/runtime parity remain open. Next: connect publication to the actual outer event consumer and test repeated calls with recovered timers.
10
+
11
+ No original executable/full QVM/native collision/physics/rendering executes. Native responses and legal input domains are supplied or unverified where stated; full runtime/macOS/Linux Rust/Bevy/Solari gameplay remains open.
docs/research/README.md CHANGED
@@ -371,3 +371,6 @@ The extension collector adds five focused tests for native tail calls, command-r
371
 
372
 
373
  [RE-589: Final firing delay and last-round exception](QVM_FIRE_DELAY.md). Reproduce with `python3 scripts/analyze_qvm_fire_delay.py`; check with `python3 scripts/test_qvm_fire_delay.py`.
 
 
 
 
371
 
372
 
373
  [RE-589: Final firing delay and last-round exception](QVM_FIRE_DELAY.md). Reproduce with `python3 scripts/analyze_qvm_fire_delay.py`; check with `python3 scripts/test_qvm_fire_delay.py`.
374
+
375
+
376
+ [RE-590: Complete bounded weapon body and actual main consumer suffix](QVM_WEAPON_COMPLETE_BODY.md). Reproduce with `python3 scripts/analyze_qvm_weapon_complete_body.py`; check with `python3 scripts/test_qvm_weapon_complete_body.py`.
docs/research/qvm-weapon-complete-body.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_weapon_complete_body.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Entire bounded50746 body and actual main consumer suffix with recovered children."""
3
+ from pathlib import Path
4
+ from qvm_research_support import source,execute
5
+ from qvm_firing_support import seed,state,fire_handlers
6
+ from analyze_qvm_weapon_prefix import LOCAL,mode_switch
7
+ from analyze_qvm_packed_controls import control
8
+ from analyze_qvm_weapon_select import select
9
+ from analyze_qvm_weapon_complete import complete
10
+ from analyze_qvm_reload_request import reload
11
+ from analyze_qvm_interaction_request import interaction
12
+ from analyze_qvm_torso_selector import torso
13
+ from analyze_qvm_locomotion import locomotion
14
+ from analyze_qvm_water_events import water_events
15
+ from analyze_qvm_weapon_return_chains import pre_fire
16
+ from analyze_qvm_fire_gate import gate
17
+ from analyze_qvm_fire_availability import availability
18
+ from analyze_qvm_fire_ammo import ammo
19
+ from analyze_qvm_fire_pose import pose
20
+ from analyze_qvm_fire_counter import counter
21
+ from analyze_qvm_fire_resource import resource
22
+ from analyze_qvm_fire_delay import delay
23
+ from analyze_qvm_acceleration import PS,FRAME
24
+ from analyze_qvm import write_json
25
+ MAIN=0x78000000
26
+
27
+ def weapon(ins,m,cmd=(0,0,0),contents=33554432):
28
+ initial=state(m);hs=fire_handlers()
29
+ def leaf(entry):
30
+ def apply(i,m,a,c):m,r=control(i,m,entry,c);return m,c,r['execution']['return'] or 0,r
31
+ return apply
32
+ def simple(fn):
33
+ def apply(i,m,a,c):m,r=fn(i,m,c);return m,c,0,r
34
+ return apply
35
+ def selected(i,m,a,c):m,r=select(i,m,a[8],c);return m,c,0,r
36
+ def reloaded(i,m,a,c):m,r=reload(i,m,a[8],c);return m,c,0,r
37
+ def action(i,m,a,c):m,r=interaction(i,m,contents,c);return m,c,0,r
38
+ hs.update({50250:leaf(50250),50397:leaf(50397),49327:simple(mode_switch),48798:selected,49028:simple(complete),49652:reloaded,49918:action})
39
+ m,c,r=execute(ins,50747,53046,m,cmd,LOCAL,hs,byte_inputs={FRAME+24:m[FRAME+24]&255})
40
+ return m,{'initial':initial,'execution':r,'final':state(m)}
41
+
42
+ def staged(ins,m,cmd=(0,0,0),contents=33554432):
43
+ initial=state(m);m,p=pre_fire(ins,m,cmd,contents);stop=p['stop'];rows=[]
44
+ for name,at,fn in [('gate',51802,gate),('availability',52085,availability),('ammo',52154,ammo),('pose',52253,pose),('counter',52730,counter),('resource',52873,resource),('delay',52989,delay)]:
45
+ if stop!=at:break
46
+ m,r=fn(ins,m,cmd,preserve_locals=True);rows.append({'name':name,'probe':r});stop=r['execution'].get('stop',53046)
47
+ assert stop==53046
48
+ return m,{'initial':initial,'pre_fire':p,'firing_stages':rows,'stop':stop,'final':state(m)}
49
+
50
+ def main_suffix(ins,m,cmd=(0,0,0),contents=33554432):
51
+ initial=state(m)
52
+ def reload_child(i,m,a,c):m,r=reload(i,m,a[8],c);return m,c,0,r
53
+ def action(i,m,a,c):m,r=interaction(i,m,contents,c);return m,c,0,r
54
+ def child(fn):
55
+ def apply(i,m,a,c):m,r=fn(i,m,c);return m,c,0,r
56
+ return apply
57
+ def firing(i,m,a,c):m,r=weapon(i,m,c,contents);return m,c,0,r
58
+ m,c,r=execute(ins,54444,54464,m,cmd,MAIN,{49652:reload_child,49918:action,50746:firing,49117:child(torso),47884:child(locomotion),48716:child(water_events)})
59
+ return m,{'initial':initial,'execution':r,'final':state(m),'stop_before':54464}
60
+
61
+ def main():
62
+ q,raw,h,ins=source();rows=[];mains=[]
63
+ cases=[('fire',22,2,{}),('last-round',22,1,{}),('empty',22,0,{}),('flag4',1,0,{}),('charge-start',11,2,{}),('charge-release',11,2,{PS+220:64,FRAME+20:0}),('burst',22,5,{4244+432*22+48:3,4244+432*22+8:100,4244+432*22+44:25}),('lowering',22,2,{PS+148:2,PS+44:16}),('reload',22,2,{PS+220:8,PS+44:16}),('dead',22,2,{PS+208:0}),('mode-local',1,1,{PS+312:1|(1<<8)|(1<<16)|(3<<24),4244+432+12:0}),('new-selection',1,2,{FRAME+24:22,PS+316:22|(2<<8)})]
64
+ for name,ident,mag,changes in cases:
65
+ m=seed(raw,h,ident,mag);m.update({FRAME+260:0,FRAME+264:0,35023932:16});m.update(changes);a,x=weapon(ins,dict(m));b,y=staged(ins,dict(m));assert state(a)==state(b),name;rows.append({'name':name,'supplied_changes':{str(k):v for k,v in changes.items()},'whole':x,'staged':y})
66
+ for mag in [0,2]:
67
+ m=seed(raw,h,22,mag);m.update({FRAME+260:0,FRAME+264:0,FRAME+212:3,35024028:0,35023932:16});m,p=main_suffix(ins,m);mains.append(p)
68
+ sites=[{'instruction':i['index'],'target':ins[i['index']-1]['operand']} for i in ins[50747:53046] if i['opcode']=='CALL']
69
+ write_json(Path('docs/research/qvm-weapon-complete-body.json'),{'source':q['source'],'sha256':q['sha256'],'direct_call_sites':sites,'probes':rows,'main_suffixes':mains,'limits':'Complete finite50746 body with recovered child calls and supplied interaction traces/durations. Actual54444..54463 six-call suffix included; no earlier main setup/movement/environment or native vector snap54471. Published firing request is not native shot execution; no full-game/reference tick claim.'})
70
+ if __name__=='__main__':main()
scripts/test_qvm_weapon_complete_body.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest,copy
2
+ from pathlib import Path
3
+ from test_qvm_weapon_return_chains import selection_state
4
+ from test_qvm_weapon_guards import guard_state
5
+ from test_qvm_weapon_ready import ready_state
6
+ from test_qvm_reload_complete import reload_complete_state
7
+ from test_qvm_weapon_phases import phase_state
8
+ from test_qvm_fire_gate import gate_state
9
+ from test_qvm_fire_availability import availability_state
10
+ from test_qvm_fire_ammo import ammo_state
11
+ from test_qvm_fire_pose import full_pose_choice
12
+ from test_qvm_fire_pose_priority import pose_apply
13
+ from test_qvm_fire_counter import counter_state
14
+ from test_qvm_fire_resource import resource_state
15
+ from test_qvm_fire_delay import delay_state
16
+ from test_qvm_main_consumers import reload_state,interaction_state
17
+ from test_qvm_torso_selector import request
18
+ from test_qvm_locomotion_prefix import animation
19
+ from test_qvm_locomotion import locomotion_state
20
+ from test_qvm_water_events import enqueue,expected
21
+
22
+ def pre_state(test,p):
23
+ a=copy.deepcopy(p['initial']);stop=None;oracles={'guards':guard_state,'ready':ready_state,'reload':reload_complete_state,'phases':phase_state}
24
+ for stage in p['stages']:
25
+ q=stage['probe'];test.assertEqual(q['initial'],a)
26
+ if stage['name']=='selection':a,stop=selection_state(test,q)
27
+ else:result=oracles[stage['name']](q);a,stop=result[:2];test.assertEqual(q['final'],a);test.assertEqual(q['execution']['stop'],stop)
28
+ test.assertEqual(p['final'],a);test.assertEqual(p['stop'],stop);return a,stop
29
+
30
+ def complete_state(test,p):
31
+ a=copy.deepcopy(p['initial']);pre,stop=pre_state(test,p['pre_fire']);a.update(pre);oracles={'gate':gate_state,'availability':availability_state,'ammo':ammo_state,'resource':resource_state,'delay':delay_state};entries={'gate':51802,'availability':52085,'ammo':52154,'pose':52253,'counter':52730,'resource':52873,'delay':52989};selected=None
32
+ for stage in p['firing_stages']:
33
+ name,q=stage['name'],stage['probe'];test.assertEqual(stop,entries[name]);test.assertEqual(q['initial'],a)
34
+ if name=='pose':a=pose_apply(q,full_pose_choice(q));stop=52730
35
+ elif name=='counter':a,selected,stop=counter_state(q);test.assertEqual(q['selected_delay'],selected)
36
+ else:
37
+ if name=='delay':test.assertEqual(q['selected_delay'],selected)
38
+ a,stop=oracles[name](q)
39
+ test.assertEqual(q['final'],a);test.assertEqual(q['execution'].get('stop',53046),stop)
40
+ test.assertEqual(stop,53046);test.assertEqual(p['final'],a);return a
41
+ class WeaponCompleteBodyTests(unittest.TestCase):
42
+ @classmethod
43
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-weapon-complete-body.json').read_text())
44
+ def test_whole_body_matches_independent_stage_oracles(self):
45
+ self.assertEqual(len(self.r['probes']),12)
46
+ for p in self.r['probes']:self.assertEqual(p['whole']['initial'],p['staged']['initial']);self.assertEqual(p['whole']['final'],complete_state(self,p['staged']))
47
+ def test_actual_main_six_child_handoffs_and_queue_order(self):
48
+ for p in self.r['main_suffixes']:
49
+ self.assertEqual([c['target'] for c in p['execution']['calls']],[49652,49918,50746,49117,47884,48716]);a=copy.deepcopy(p['initial']);children=[x['effect'] for x in p['execution']['children']]
50
+ for q,oracle in zip(children[:2],[reload_state,interaction_state]):self.assertEqual(q['initial'],{k:a[k] for k in q['initial']});a.update(oracle(q))
51
+ weapon=children[2];self.assertEqual(weapon['initial'],a);name='empty' if not a['packed']&65280 else 'fire';ref=next(x for x in self.r['probes'] if x['name']==name);a=complete_state(self,ref['staged']);self.assertEqual(weapon['final'],a);torso,loco,water=children[3:];self.assertEqual(torso['initial'],{k:a[k] for k in torso['initial']});r=request(torso)
52
+ if r is not None:animation(a,39482,r,0)
53
+ self.assertEqual(torso['final'],{k:a[k] for k in torso['final']});self.assertEqual(loco['initial'],{k:a[k] for k in loco['initial']});a.update(locomotion_state(loco)[0]);self.assertEqual(water['initial'],{k:a[k] for k in water['initial']});a['queue']=enqueue(a['queue'],expected(water));self.assertEqual(p['final'],a);self.assertEqual(p['stop_before'],54464)
54
+ def test_all_direct_child_families_are_recovered(self):self.assertEqual(set(c['target'] for c in self.r['direct_call_sites']),{50250,50397,49327,48798,49028,49652,49918,50309,39482,39552,39238})
55
+ def test_last_round_burst_charge_and_no_native_shot_claim(self):
56
+ rows={p['name']:p['whole']['final'] for p in self.r['probes']};self.assertEqual(rows['fire']['weapon_timer'],75);self.assertEqual(rows['last-round']['weapon_timer'],0);self.assertEqual(rows['burst']['counter204'],2);self.assertEqual(rows['burst']['weapon_timer'],25);self.assertEqual(rows['charge-start']['request260'],0);self.assertEqual(rows['charge-release']['request260'],1);self.assertIn('not native shot execution',self.r['limits'])
57
+ if __name__=='__main__':unittest.main()