|
#!/usr/bin/env python3 |
|
|
|
import sys |
|
from pathlib import Path |
|
|
|
MAGIC = b"$SVS" |
|
FF_RUN = 8 # number of consecutive FF bytes defining an FF section |
|
CONTEXT = 16 # bytes shown around each boundary |
|
|
|
|
|
def hexline(data, start_offset): |
|
return " ".join(f"{b:02X}" for b in data) |
|
|
|
|
|
if len(sys.argv) != 3: |
|
print(f"Usage: {sys.argv[0]} original.bin modified.bin") |
|
sys.exit(1) |
|
|
|
src = Path(sys.argv[1]) |
|
dst = Path(sys.argv[2]) |
|
|
|
data = bytearray(src.read_bytes()) |
|
|
|
print(f"Input: {src}") |
|
print(f"Size: {len(data):,} bytes") |
|
print() |
|
|
|
if len(data) != 0x800000: |
|
print("ERROR: input is not 8 MiB (0x800000 bytes)") |
|
sys.exit(1) |
|
|
|
positions = [] |
|
pos = 0 |
|
|
|
while True: |
|
pos = data.find(MAGIC, pos) |
|
|
|
if pos == -1: |
|
break |
|
|
|
positions.append(pos) |
|
pos += len(MAGIC) |
|
|
|
print(f"Found {len(positions)} '$SVS' block(s)") |
|
print() |
|
|
|
if not positions: |
|
sys.exit("Nothing to modify.") |
|
|
|
|
|
blocks = [] |
|
|
|
for start in positions: |
|
|
|
# Search for first FF_RUN-length FF section after $SVS |
|
search_from = start + len(MAGIC) |
|
ff_start = None |
|
|
|
for i in range(search_from, len(data) - FF_RUN + 1): |
|
if data[i:i + FF_RUN] == b"\xFF" * FF_RUN: |
|
ff_start = i |
|
break |
|
|
|
if ff_start is None: |
|
print(f"ERROR: no FF section found after 0x{start:06X}") |
|
sys.exit(1) |
|
|
|
end = ff_start # FF section itself remains untouched |
|
|
|
# Context around beginning |
|
before_start = max(0, start - CONTEXT) |
|
before = data[before_start:start] |
|
|
|
# Context around end |
|
after_end = min(len(data), ff_start + FF_RUN + CONTEXT) |
|
after = data[ff_start:after_end] |
|
|
|
print("=" * 70) |
|
print(f"BLOCK") |
|
print(f" start : 0x{start:06X} ({start:,})") |
|
print(f" end : 0x{end - 1:06X} ({end - 1:,})") |
|
print(f" size : {end - start:,} bytes") |
|
print() |
|
|
|
print("Before block:") |
|
print(f" offset 0x{before_start:06X}: {hexline(before, before_start)}") |
|
print() |
|
|
|
print("Block beginning:") |
|
print(f" offset 0x{start:06X}: {hexline(data[start:start + 32], start)}") |
|
print() |
|
|
|
print("Block ending / following FF:") |
|
print(f" offset 0x{max(start, end - CONTEXT):06X}: " |
|
f"{hexline(data[max(start, end - CONTEXT):after_end], max(start, end - CONTEXT))}") |
|
print() |
|
|
|
blocks.append((start, end)) |
|
|
|
|
|
print("=" * 70) |
|
print() |
|
print("Blocks found:") |
|
for n, (start, end) in enumerate(blocks, 1): |
|
print( |
|
f"{n}: 0x{start:06X} - 0x{end - 1:06X} " |
|
f"({end - start:,} bytes)" |
|
) |
|
|
|
print() |
|
answer = input("Replace these blocks with FF? [y/N]: ").strip().lower() |
|
|
|
if answer != "y": |
|
print("Aborted. No output written.") |
|
sys.exit(0) |
|
|
|
for start, end in blocks: |
|
data[start:end] = b"\xFF" * (end - start) |
|
|
|
dst.write_bytes(data) |
|
|
|
print() |
|
print(f"Written: {dst}") |
|
print(f"Size: {len(data):,} bytes") |
|
|
|
if len(data) != len(src.read_bytes()): |
|
print("ERROR: size changed!") |
|
sys.exit(1) |
|
|
|
print("Size verified: unchanged.") |
|
|