75 lines
5.6 KiB
Python
75 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Regenerate reviewable Rust constants from the preserved GPL engine source.
|
|
This development tool is not used by the Rust game at build or runtime.
|
|
"""
|
|
from pathlib import Path
|
|
import re
|
|
PACKAGE=Path(__file__).resolve().parents[1]
|
|
ROOT=PACKAGE.parent
|
|
ids={k:int(v,16) for k,v in re.findall(r'#define\s+(FILE\w+)\s+(0x[0-9a-fA-F]+)',(ROOT/'raptor/src/fileids.h').read_text())}
|
|
enum=(ROOT/'raptor/src/objects.h').read_text().split('S_FORWARD_GUNS',1)[1].split('}OBJ_TYPE')[0]
|
|
names=['S_FORWARD_GUNS']+re.findall(r'^\s*(S_\w+)\s*[,\n]',enum,re.M)
|
|
names=[n for n in names if n!='S_LAST_OBJECT']
|
|
values={**ids,**{n:i for i,n in enumerate(names)},'S_ALL':0,'S_AIR':1,'S_GROUND':2,'S_GRALL':3,'S_GTILE':4,'S_SUCK':5,'S_SHOOT':0,'S_LINE':1,'S_BEAM':2,'MAX_SHIELD':100}
|
|
def number(s,default=0):
|
|
s=s.strip()
|
|
if s in values:return values[s]
|
|
if re.fullmatch(r'-?\d+',s):return int(s)
|
|
if re.fullmatch(r'0x[0-9a-fA-F]+',s):return int(s,16)
|
|
if '/' in s:
|
|
a,b=s.split('/')
|
|
return number(a)//number(b)
|
|
raise ValueError(f'Unsupported numeric expression: {s}')
|
|
def blocks(file,var,start_pattern):
|
|
text=(ROOT/file).read_text(); starts=list(re.finditer(start_pattern,text));result={}
|
|
for i,m in enumerate(starts):
|
|
block=text[m.end():starts[i+1].start() if i+1<len(starts) else len(text)]
|
|
f={}
|
|
wanted={'item','numframes','cost','start_cnt','max_cnt','specialw','onlyflag'} if var=='lib' else {'lumpnum','numframes','hits','speed','maxspeed','shoot_rate','ht','startframe','delayflag','use_plot','move_flag','fplrx','fplry','meffect','beam'}
|
|
for k,v in re.findall(rf'{var}->(\w+)\s*=\s*([^;]+);',block):
|
|
if k in wanted:
|
|
f[k]=f.get(v.strip().removeprefix(var+'->')) if v.strip().startswith(var+'->') else number(v)
|
|
result[m[1]]=f
|
|
return result
|
|
objects=blocks('raptor/src/objects.cpp','lib',r'lib\s*=\s*&obj_lib\[(S_\w+)\];')
|
|
weapons=blocks('raptor/src/shots.cpp','slib',r'slib\s*=\s*&shot_lib\[(S_\w+)\];')
|
|
id_of=lambda n:'base:'+n[2:].lower().replace('missle','missile')
|
|
name_of=lambda n:n[2:].replace('_',' ').title().replace('Missle','Missile')
|
|
out=['// Generated by tools/generate_legacy_tables.py; source is GPL-2.0-or-later.','pub struct LegacyObject{pub index:u32,pub id:&\'static str,pub name:&\'static str,pub asset:u32,pub frames:u32,pub cost:u32,pub max_count:u32,pub initial_count:u32,pub special:bool}','pub struct LegacyWeapon{pub index:u32,pub id:&\'static str,pub name:&\'static str,pub asset:u32,pub frames:u32,pub damage:i32,pub speed:i32,pub cooldown:u32,pub target:u32,pub maxspeed:i32,pub startframe:u32,pub delay:bool,pub plot:bool,pub moving:bool,pub follow_x:bool,pub follow_y:bool,pub effect:bool,pub beam:u32}','pub fn objects()->Vec<LegacyObject>{vec![']
|
|
for n in names:
|
|
f=objects.get(n,{})
|
|
out.append(f'LegacyObject{{index:{values[n]},id:"{id_of(n)}",name:"{name_of(n)}",asset:{max(0,f.get("item",0))},frames:{f.get("numframes",1)},cost:{max(0,f.get("cost",0))*(f.get("start_cnt",1) if f.get("onlyflag") else 1)},max_count:{max(1,f.get("max_cnt",1))},initial_count:{max(1,f.get("start_cnt",1))},special:{str(bool(f.get("specialw",0))).lower()}}},')
|
|
out+= [']}','pub fn weapons()->Vec<LegacyWeapon>{vec![']
|
|
for n,f in weapons.items():
|
|
out.append(f'LegacyWeapon{{index:{values[n]},id:"{id_of(n)}",name:"{name_of(n)}",asset:{max(0,f.get("lumpnum",0))},frames:{f.get("numframes",1)},damage:{f.get("hits",1)},speed:{f.get("speed",0)},cooldown:{f.get("shoot_rate",1)},target:{f.get("ht",0)},maxspeed:{f.get("maxspeed",0)},startframe:{f.get("startframe",0)},delay:{str(bool(f.get("delayflag",0))).lower()},plot:{str(bool(f.get("use_plot",0))).lower()},moving:{str(bool(f.get("move_flag",0))).lower()},follow_x:{str(bool(f.get("fplrx",0))).lower()},follow_y:{str(bool(f.get("fplry",0))).lower()},effect:{str(bool(f.get("meffect",0))).lower()},beam:{f.get("beam",0)}}},')
|
|
out +=[']}']
|
|
rap=(ROOT/'raptor/src/rap.cpp').read_text()
|
|
for name in ['o_gun1','o_gun2','o_gun3']:
|
|
block=re.search(rf'{name}\[8\]\s*=\s*\{{(.*?)\}};',rap,re.S)[1]
|
|
nums=[int(x.strip(),0) for x in block.split(',') if x.strip()]
|
|
out.append(f'pub const {name.upper()}: [i32; 8] = ['+', '.join(map(str,nums))+'];')
|
|
fx=(ROOT/'raptor/src/fx.cpp').read_text()
|
|
starts=list(re.finditer(r'lib\s*=\s*&fx_items\[(FX_\w+)\];',fx))
|
|
out.append("pub struct LegacySound {pub name: &'static str,pub asset:u32,pub pitch:i32,pub random:bool,pub volume:i32,pub priority:i32}")
|
|
out.append('pub fn sounds()->Vec<LegacySound>{vec![')
|
|
for i,m in enumerate(starts):
|
|
block=fx[m.end():starts[i+1].start() if i+1<len(starts) else len(fx)]
|
|
item=re.search(r'lib->item\s*=\s*GLB_GetItemID\("(\w+)"\)',block)
|
|
if not item: continue
|
|
matches=[v for k,v in ids.items() if k.endswith('_'+item[1])]
|
|
if len(matches)!=1: raise ValueError(f'Ambiguous sound {item[1]}: {matches}')
|
|
pitch=int(re.search(r'lib->pitch\s*=\s*(-?\d+)',block)[1])
|
|
random=int(re.search(r'lib->rpflag\s*=\s*(\d+)',block)[1])
|
|
volume=int(re.search(r'lib->vol\s*=\s*(-?\d+)',block)[1])
|
|
priority=int(re.search(r'lib->pri\s*=\s*(-?\d+)',block)[1])
|
|
out.append(f'LegacySound{{name:"{m[1]}",asset:{matches[0]},pitch:{pitch},random:{str(bool(random)).lower()},volume:{volume},priority:{priority}}},')
|
|
out.append(']}')
|
|
audio=(ROOT/'raptor/src/i_oplmusic.cpp').read_text()
|
|
for name,rust in [('frequency_curve','FREQUENCY_CURVE'),('volume_mapping_table','VOLUME_CURVE')]:
|
|
block=re.search(rf'{name}\[\]\s*=\s*\{{(.*?)\}};',audio,re.S)[1]
|
|
block=re.sub(r'//[^\n]*','',block)
|
|
nums=[int(x.strip(),0) for x in block.split(',') if x.strip()]
|
|
out.append(f'pub const {rust}: [u16; {len(nums)}] = ['+', '.join(map(str,nums))+'];')
|
|
(PACKAGE/'src/legacy_tables.rs').write_text('\n'.join(out)+'\n')
|
|
print(f'Generated {len(names)} items and {len(weapons)} weapons')
|