From f2ada0e2febdea47f702749f0fd8c81a04d26871 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 17 Aug 2024 19:38:23 +0200 Subject: [PATCH] Barebones patching system --- .vscode/launch.json | 15 ++++++++++++ buildtool/patcher/filepatcher.py | 42 ++++++++++++++++++++++++-------- buildtool/patcher/polly.py | 12 ++++++--- src/patches/public.patch | 4 +++ src/patches/public.xml | 6 ----- test.py | 3 +++ 6 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 test.py diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b180193 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "test.py", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/buildtool/patcher/filepatcher.py b/buildtool/patcher/filepatcher.py index 77d8656..6f9efad 100644 --- a/buildtool/patcher/filepatcher.py +++ b/buildtool/patcher/filepatcher.py @@ -1,26 +1,48 @@ -from polly import parse_patch_file -from polly import parse_location +from .polly import parse_patch_file +from .polly import resolve_position from pathlib import Path from ..task.util import * -def read_insertion(action, patch_file): - with open(patch_file, 'r') as f: - src = f.read() - location = parse_location(action["at"], src) +def read_insertion(action, src): + position = resolve_position(action["at"], src) + return {"content": action["content"], "pos": position} def process_patch_file(patch_file: str, check_date: bool = True): patches = parse_patch_file(patch_file) for patch in patches: - target_dest = (PATCHED_RSC_DIR / patch["target"]) + target_src = EXTRACTED_DIR / patch["target"] + target_dest = PATCHED_RSC_DIR / patch["target"] + + # Skip old patches if check_date and target_dest.exists() and target_dest.stat().st_mtime > patch["timestamp"]: - # Patch skipped + print(f"Skipping patch {patch['target']}...") continue - insertions = [read_insertion(action) for action in patch["actions"]] + print(f"Patching {patch['target']} with {len(patch['actions'])} actions") + + # Read the source of the file + with open(target_src, 'r') as f: + src = f.read() + + # Resolve the actual char locations in the patches + insertions = [read_insertion(action, src) for action in patch["actions"]] insertions = [x for x in insertions if x is not None] - \ No newline at end of file + for insertion in insertions: + src = src[:insertion["pos"]] + insertion["content"] + src[insertion["pos"]:] + + # Correct insertions that follow this one + for i2 in insertions: + # Only update insertions that come *after* this one + if i2 == insertion or i2["pos"] < insertion["pos"]: + continue + + i2["pos"] += len(insertion["content"]) + + # Write the changed output + with open(target_dest, 'w') as f: + f.write(src) \ No newline at end of file diff --git a/buildtool/patcher/polly.py b/buildtool/patcher/polly.py index b6011a2..948e4f7 100644 --- a/buildtool/patcher/polly.py +++ b/buildtool/patcher/polly.py @@ -30,22 +30,26 @@ def parse_location(location: Token): m = re.match(r"ln([0-9]+)(?:c([0-9]+))?", location.value) if m: ln, col = m.groups() - return {"type": "lncol", "line": ln, "column": col} + return {"type": "lncol", "line": int(ln), "column": int(col) if col else 0} m = re.match(r"ch([0-9]+)", location.value) if m: ch = m.groups()[0] - return {"type": "char", "index": ch} + return {"type": "char", "index": int(ch)} raise RuntimeError("Cannot parse location") -def absolute_location(location, src: str): +def resolve_position(location, src: str): if location["type"] == "char": return location["index"] elif location["type"] == "lncol": j = 0 for i, x in enumerate(src.split('\n')): - if i == + if (i+1) == location["line"]: + if location["column"] > len(x): + print(f"Cannot insert at line {location['line']} column {location['column']}. The line is not that long.") + exit(-1) + return j + location["column"] j += len(x) + 1 print(f"Cannot insert at line {location['line']}. The file is not long enough.") diff --git a/src/patches/public.patch b/src/patches/public.patch index ce3524f..730ed5c 100644 --- a/src/patches/public.patch +++ b/src/patches/public.patch @@ -1,12 +1,16 @@ file 'res/values/public.xml' insert ln17595 << @end + + + + @end #17595 \ No newline at end of file diff --git a/src/patches/public.xml b/src/patches/public.xml index d98e644..e84d585 100644 --- a/src/patches/public.xml +++ b/src/patches/public.xml @@ -6817,7 +6817,6 @@ - @@ -7851,8 +7850,6 @@ - - @@ -10820,7 +10817,6 @@ - @@ -11525,7 +11521,6 @@ - @@ -12554,7 +12549,6 @@ - diff --git a/test.py b/test.py new file mode 100644 index 0000000..1458932 --- /dev/null +++ b/test.py @@ -0,0 +1,3 @@ +from buildtool.patcher.filepatcher import process_patch_file + +process_patch_file('src/patches/public.patch', check_date=True) \ No newline at end of file