#!/usr/bin/env python3 """ backdate.py -- mechanically back-date a WinTesla-era engine header (BT411/BT412 engine/MUNGA*/X.h) to its 1995 form (source410/MUNGA/X.HPP). Transforms (the mechanical part -- ALWAYS eyeball the result and write the .NOTES.md sidecar; content drift is NOT detected here): #pragma once -> #if !defined(X_HPP) / #define X_HPP ... #endif #include "y.h" -> guarded #if !defined(Y_HPP) #include #endif (system includes like are left alone) Usage: py -3 backdate.py """ import os import re import sys def main(): donor, out = sys.argv[1], sys.argv[2] name = os.path.splitext(os.path.basename(out))[0].upper() guard = "%s_HPP" % name text = open(donor, "r", errors="replace").read() # WinTesla wrapped the 1995 static objects in accessor functions (its # static-init-order fix); unwrap them back to plain 1995 definitions. text = re.sub( r"Derivation\s*\*\s*(\w+)::GetClassDerivations\(\)\s*" r"\{\s*static\s+Derivation\s+\w+\s*\(([^;]*?)\);\s*return\s*&\w+;\s*\}", r"Derivation\n\t\1::ClassDerivations(\2);", text, flags=re.S) text = re.sub( r"(?:Receiver::)?MessageHandlerSet\s*&?\s*(\w+)::GetMessageHandlers\(\)\s*" r"\{\s*static\s+(?:\w+::)?MessageHandlerSet\s+\w+\s*\(([^;]*?)\);\s*return\s*\w+;\s*\}", r"\1::MessageHandlerSet\n\t\1::MessageHandlers(\2);", text, flags=re.S) text = re.sub( r"(?:\w+::)?AttributeIndexSet\s*&?\s*(\w+)::GetAttributeIndex\(\)\s*" r"\{\s*static\s+(?:\w+::)?AttributeIndexSet\s+\w+\s*\(([^;]*?)\);\s*return\s*\w+;\s*\}", r"\1::AttributeIndexSet\n\t\1::AttributeIndex(\2);", text, flags=re.S) text = re.sub( r"MemoryBlock\s*\*\s*(\w+)::GetAllocatedMemory\(\)\s*" r"\{\s*static\s+MemoryBlock\s+\w+\s*\(([^;]*?)\);\s*return\s*&\w+;\s*\}", r"MemoryBlock\n\t\1::AllocatedMemory(\2);", text, flags=re.S) body = text.splitlines() res = [] warned = set() for line in body: s = line.strip() if s == "#pragma once": continue # local include, possibly with a relative path -- keep the basename m = re.match(r'#include\s+"(?:[.\\/A-Za-z0-9_]*[\\/])?([A-Za-z0-9_]+)\.h(?:pp)?"', s) if m: inc = m.group(1).lower() g = inc.upper() + "_HPP" res.append("#\tif !defined(%s)" % g) res.append("#\t\tinclude <%s.hpp>" % inc) res.append("#\tendif") continue # post-1994 C++ the 4.52 compiler rejects: de-modernize line = re.sub(r"\b(\w+)::GetClassDerivations\(\)", r"\1::ClassDerivations", line) line = re.sub(r"\b(\w+)::GetMessageHandlers\(\)", r"\1::MessageHandlers", line) line = re.sub(r"\b(\w+)::GetAttributeIndex\(\)", r"\1::AttributeIndex", line) line = re.sub(r"\b(\w+)::GetAllocatedMemory\(\)", r"\1::AllocatedMemory", line) # header-side: 2007 accessor DECLARATIONS -> 1995 static objects line = re.sub(r"static\s+Derivation\s*\*\s*GetClassDerivations\(\);", "static Derivation ClassDerivations;", line) line = re.sub(r"static\s+(?:\w+::)?MessageHandlerSet\s*&?\s*GetMessageHandlers\(\);", "static MessageHandlerSet MessageHandlers;", line) line = re.sub(r"static\s+(?:\w+::)?AttributeIndexSet\s*&?\s*GetAttributeIndex\(\);", "static AttributeIndexSet AttributeIndex;", line) line = line.replace("Shutdown(int remainingApps);", "Shutdown(int remainingApps = 0);") line = re.sub(r"\*?\s*(?", "", ""): if bad in line and "#include" in line: line = "" break line = re.sub(r"\bstd::", "", line) line = re.sub(r"\btrue\b", "True", line) line = re.sub(r"\bfalse\b", "False", line) line = re.sub(r"\bbool\b", "Logical", line) for bad in ("",): if bad in line: line = None break if line is None: continue for marker in ("namespace", "static_cast", "reinterpret_cast", "const_cast"): if marker in s and marker not in warned: warned.add(marker) print("WARNING: post-1994 construct '%s' present -- fix by hand" % marker) res.append(line) # strip leading blank lines while res and not res[0].strip(): res.pop(0) out_text = "\n".join(res) # cleanups after the accessor unwrap: a deref of the former pointer # accessor, and doubled class prefixes from the static rename out_text = re.sub(r"\*\s*(\w+)::ClassDerivations\b", r"\1::ClassDerivations", out_text) out_text = re.sub(r"\b(\w+)::\1::", r"\1::", out_text) is_header = out.upper().endswith((".HPP", ".H", ".THP")) with open(out, "w", newline="\r\n") as f: if is_header: f.write("#if !defined(%s)\n#\tdefine %s\n\n" % (guard, guard)) f.write(out_text) if is_header: f.write("\n\n#endif\n") else: f.write("\n") print("wrote %s (%d lines from %s)" % (out, len(res) + 5, donor)) if __name__ == "__main__": main()