2024-04-14 21:51:32 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
# SPDX-FileCopyrightText: © 2024 ZeldaRET
|
|
|
|
# SPDX-License-Identifier: CC0-1.0
|
|
|
|
|
2024-08-03 00:31:15 +00:00
|
|
|
# Usage: preprocess.py [flags] -- [compile command minus input file...] [single input file]
|
2024-04-14 21:51:32 +00:00
|
|
|
# Preprocess a C file to:
|
|
|
|
# * Re-encode from UTF-8 to EUC-JP (the repo uses UTF-8 for text encoding, but
|
|
|
|
# the strings in the ROM are encoded in EUC-JP)
|
2024-08-03 00:31:15 +00:00
|
|
|
# * Replace `#pragma increment_block_number` with fake structs for controlling BSS ordering
|
2024-04-14 21:51:32 +00:00
|
|
|
|
2024-08-03 00:31:15 +00:00
|
|
|
import argparse
|
2024-04-14 21:51:32 +00:00
|
|
|
from pathlib import Path
|
2024-08-10 00:13:59 +00:00
|
|
|
import re
|
2024-04-14 21:51:32 +00:00
|
|
|
import tempfile
|
|
|
|
import subprocess
|
|
|
|
import sys
|
2024-08-10 00:13:59 +00:00
|
|
|
import typing
|
2024-04-14 21:51:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
def fail(message):
|
|
|
|
print(message, file=sys.stderr)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
2024-08-10 00:13:59 +00:00
|
|
|
def process_file(
|
|
|
|
version: str,
|
|
|
|
filename: str,
|
|
|
|
input: typing.TextIO,
|
|
|
|
output: typing.TextIO,
|
|
|
|
):
|
2024-04-14 21:51:32 +00:00
|
|
|
output.write(f'#line 1 "{filename}"\n')
|
2024-08-10 00:13:59 +00:00
|
|
|
# whether the current line follows a #pragma increment_block_number,
|
|
|
|
# including continuation lines (lines after a \-ending line)
|
|
|
|
in_pragma_incblocknum = False
|
|
|
|
# the line where the #pragma increment_block_number is
|
|
|
|
pragma_incblocknum_first_line_num = None
|
|
|
|
# all the lines from the #pragma increment_block_number line to the last
|
|
|
|
# continuation line, as a list[str]
|
|
|
|
pragma_incblocknum_lines = None
|
2024-04-14 21:51:32 +00:00
|
|
|
for i, line in enumerate(input, start=1):
|
2024-08-10 00:13:59 +00:00
|
|
|
if not in_pragma_incblocknum and line.startswith(
|
|
|
|
"#pragma increment_block_number"
|
|
|
|
):
|
|
|
|
in_pragma_incblocknum = True
|
|
|
|
pragma_incblocknum_first_line_num = i
|
|
|
|
pragma_incblocknum_lines = []
|
|
|
|
|
|
|
|
if in_pragma_incblocknum:
|
|
|
|
if line.endswith("\\\n"):
|
|
|
|
pragma_incblocknum_lines.append(line)
|
|
|
|
else:
|
|
|
|
in_pragma_incblocknum = False
|
|
|
|
pragma_incblocknum_lines.append(line)
|
|
|
|
amount = 0
|
|
|
|
for s in pragma_incblocknum_lines:
|
|
|
|
# Note if we had two versions like "abc-def-version" and "def-version"
|
|
|
|
# then this code would find either given "def-version", but
|
|
|
|
# thankfully we don't have such nested version names.
|
|
|
|
m = re.search(rf"{version}:(\d+)\b", s)
|
|
|
|
if m:
|
|
|
|
amount = int(m.group(1))
|
|
|
|
break
|
|
|
|
|
|
|
|
# Always generate at least one struct,
|
|
|
|
# so that fix_bss.py can know where the increment_block_number pragmas are
|
|
|
|
if amount == 0:
|
|
|
|
amount = 256
|
|
|
|
|
|
|
|
# Write fake structs for BSS ordering
|
|
|
|
# pragma_incblocknum_first_line_num is used for symbol uniqueness, and
|
|
|
|
# also by fix_bss.py to locate the pragma these symbols originate from.
|
|
|
|
for j in range(amount):
|
|
|
|
output.write(
|
|
|
|
"struct increment_block_number_"
|
|
|
|
f"{pragma_incblocknum_first_line_num:05}_{j:03};\n"
|
2024-08-03 00:31:15 +00:00
|
|
|
)
|
2024-08-10 00:13:59 +00:00
|
|
|
output.write(f'#line {i + 1} "{filename}"\n')
|
2024-04-14 21:51:32 +00:00
|
|
|
else:
|
|
|
|
output.write(line)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2024-08-03 00:31:15 +00:00
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-v", "--oot-version", help="Which version should be processed")
|
|
|
|
parser.add_argument(
|
|
|
|
"args",
|
|
|
|
nargs="+",
|
|
|
|
)
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
filename = Path(args.args[-1])
|
2024-04-14 21:51:32 +00:00
|
|
|
with tempfile.TemporaryDirectory(prefix="oot_") as tmpdir:
|
|
|
|
tmpfile = Path(tmpdir) / filename.name
|
|
|
|
|
|
|
|
with open(filename, mode="r", encoding="utf-8") as input:
|
|
|
|
with open(tmpfile, mode="w", encoding="euc-jp") as output:
|
2024-08-03 00:31:15 +00:00
|
|
|
process_file(args.oot_version, filename, input, output)
|
2024-04-14 21:51:32 +00:00
|
|
|
|
2024-08-03 00:31:15 +00:00
|
|
|
compile_command = args.args[:-1] + ["-I", filename.parent, tmpfile]
|
2024-04-14 21:51:32 +00:00
|
|
|
process = subprocess.run(compile_command)
|
|
|
|
return process.returncode
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
try:
|
|
|
|
sys.exit(main())
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
sys.exit(1)
|