chore: establish project baseline

This commit is contained in:
OpenCode
2026-09-21 15:51:47 -03:00
commit 9dcc263217
459 changed files with 447890 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
"""Extract control->column bindings from the SGV executable's embedded Delphi binary DFM forms (READ-ONLY).
python scripts/extract_sgv_dfm_bindings.py <SGV.exe> [--needle COD_ORIGEM_MERCADORIA] [--out file.json]
Reads the exe bytes (never modifies it), locates every 'TPF0' form stream that contains the needle, parses the
Delphi binary DFM and reports, for every data-aware control, its DataField/DataSource, Caption/Text-like properties
and the chain of parent containers (group boxes, tab sheets) — i.e. the on-screen label context.
"""
import argparse
import json
import re
import struct
import sys
VA = dict(NULL=0, LIST=1, INT8=2, INT16=3, INT32=4, EXTENDED=5, STRING=6, IDENT=7, FALSE=8, TRUE=9, BINARY=10, SET=11, LSTRING=12, NIL=13,
COLLECTION=14, SINGLE=15, CURRENCY=16, DATE=17, WSTRING=18, INT64=19, UTF8=20, DOUBLE=21)
class R:
def __init__(self, data, pos=0):
self.d, self.p = data, pos
def u8(self):
v = self.d[self.p]; self.p += 1; return v
def take(self, n):
v = self.d[self.p:self.p + n]
if len(v) < n:
raise ValueError("eof")
self.p += n; return v
def sstr(self):
return self.take(self.u8()).decode("latin-1")
def value(self):
t = self.u8()
if t == VA["NULL"] or t == VA["NIL"]: return None
if t == VA["LIST"]:
out = []
while self.d[self.p] != 0:
out.append(self.value())
self.p += 1; return out
if t == VA["INT8"]: return struct.unpack("<b", self.take(1))[0]
if t == VA["INT16"]: return struct.unpack("<h", self.take(2))[0]
if t == VA["INT32"]: return struct.unpack("<i", self.take(4))[0]
if t == VA["EXTENDED"]: self.take(10); return "<ext>"
if t in (VA["STRING"], VA["IDENT"]): return self.sstr()
if t == VA["FALSE"]: return False
if t == VA["TRUE"]: return True
if t == VA["BINARY"]:
n = struct.unpack("<I", self.take(4))[0]; self.take(n); return "<binary>"
if t == VA["SET"]:
out = []
while True:
s = self.sstr()
if not s: break
out.append(s)
return out
if t == VA["LSTRING"] or t == VA["UTF8"]:
n = struct.unpack("<I", self.take(4))[0]; return self.take(n).decode("utf-8", "replace" if t == VA["UTF8"] else "replace")
if t == VA["WSTRING"]:
n = struct.unpack("<I", self.take(4))[0]; return self.take(n * 2).decode("utf-16-le", "replace")
if t == VA["SINGLE"]: self.take(4); return "<single>"
if t == VA["CURRENCY"] or t == VA["DATE"] or t == VA["DOUBLE"] or t == VA["INT64"]: self.take(8); return "<8b>"
if t == VA["COLLECTION"]:
out = []
while self.d[self.p] != 0:
if self.d[self.p] in (2, 3, 4): # optional collection order value
self.value()
if self.d[self.p] == 1: # vaList marker that opens every collection item
self.p += 1
item = {}
while self.d[self.p] != 0:
key = self.sstr() # evaluate the name BEFORE the value (Python evaluates the RHS first)
item[key] = self.value()
self.p += 1; out.append(item)
self.p += 1; return out
raise ValueError(f"unknown value type {t} at {self.p}")
def component(self):
first = self.u8()
if first & 0xF0 == 0xF0: # ObjInherit / ObjFlags prefix
if first & 2: # ffChildPos
self.value()
cls = self.sstr()
else:
self.p -= 1; cls = self.sstr()
name = self.sstr()
props = {}
while self.d[self.p] != 0:
pn = self.sstr(); props[pn] = self.value()
self.p += 1
kids = []
while self.d[self.p] != 0:
kids.append(self.component())
self.p += 1
return {"class": cls, "name": name, "props": props, "children": kids}
def walk(node, chain, out):
cap = node["props"].get("Caption")
here = chain + ([f"{node['class']}:{node['name']}" + (f"[{cap}]" if isinstance(cap, str) and cap else "")] if node["class"] != "TQuery" else [])
p = node["props"]
if "DataField" in p or node["class"].startswith(("TLabel", "TRz", "TGroup", "TTab", "TRadio")):
out.append({"class": node["class"], "name": node["name"], "DataField": p.get("DataField"), "DataSource": p.get("DataSource"), "Caption": cap,
"Items": p.get("Items"), "Values": p.get("Values"), "FocusControl": p.get("FocusControl"), "Left": p.get("Left"), "Top": p.get("Top"), "path": [c for c in chain]})
for k in node["children"]:
walk(k, here, out)
def find_forms(data, needle):
forms = []
for m in re.finditer(b"TPF0", data):
start = m.start()
try:
r = R(data, start + 4)
tree = r.component()
except Exception:
continue
blob = data[start:r.p]
if needle in blob:
forms.append((start, r.p, tree))
return forms
def main(argv=None):
ap = argparse.ArgumentParser()
ap.add_argument("exe"); ap.add_argument("--needle", default="COD_ORIGEM_MERCADORIA"); ap.add_argument("--out")
a = ap.parse_args(argv)
data = open(a.exe, "rb").read()
forms = find_forms(data, a.needle.encode())
result = []
for start, end, tree in forms:
controls = []
walk(tree, [], controls)
result.append({"offset": start, "size": end - start, "form_class": tree["class"], "form_name": tree["name"], "controls": controls})
text = json.dumps(result, ensure_ascii=False, indent=1, default=str)
if a.out:
open(a.out, "w", encoding="utf-8").write(text)
print(f"forms containing {a.needle!r}: {[(f['form_class'], f['size'], len(f['controls'])) for f in result]}")
if __name__ == "__main__":
main()