Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions nx2/chunks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"""
import bisect
import collections
import glob
import multiprocessing
import os
import re
Expand Down Expand Up @@ -200,13 +201,14 @@ def emit_function(e, texts):
while os.path.exists(os.path.join(gen_dir, "code_%03d.c" % n)): # (fewer chunks than the last lift)
os.remove(os.path.join(gen_dir, "code_%03d.c" % n))
n += 1
# (a lift with no code_hot.c: no hot list, A64_HOT_CHUNK unset, or none of the listed functions in it. write() removes a
# code_hot.c, but split_hot.py has renamed it code_hot.c.whole and left code_hot_*.c, which build.py compiled again: the
# earlier lift's hot functions, each defined in a chunk too, linked twice)
if not any(os.path.basename(f) == "code_hot.c" for f in files):
for f in os.listdir(gen_dir):
if f == "code_hot.c.whole" or HOT_PIECE.fullmatch(f):
os.remove(os.path.join(gen_dir, f))
# (an earlier lift's hot pieces go now, with or without a new code_hot.c, before the patches and the page-mark groups read
# code_*.c: split_hot.py makes this lift's after it. A lift with no code_hot.c (no hot list, A64_HOT_CHUNK unset, or none of the
# listed functions in it): split_hot.py had renamed the old one code_hot.c.whole and left code_hot_*.c, which build.py compiled
# again, the earlier lift's hot functions, each defined in a chunk too, linked twice. A lift with one: the old pieces, patched
# already, and the new whole file each held the output-size places, and nx2/oot_patches.py's count of them failed the lift)
for f in os.listdir(gen_dir):
if f == "code_hot.c.whole" or HOT_PIECE.fullmatch(f):
os.remove(os.path.join(gen_dir, f))
files += split_big(gen_dir, files)
save(img_dir, starts or b.recorded)
print("chunks: %d, boundaries %s" % (len(files), "kept" if starts else "from sizes (saved)"))
Expand Down Expand Up @@ -291,3 +293,12 @@ def split_big(gen_dir, files, budget=None):
os.remove(os.path.join(gen_dir, f))
write_costs(gen_dir, costs)
return made


def edit_c(gen, stats, supported, whole=True):
"""(nx2/lift.py, after the lift writes its C; here, beside the writing, so it imports without the lift's tools) The edits of the written C, in this order: the game's patches (nx2/oot_patches.py, which find the lift's exact text; the
supported build's whole standalone lift fails when an output-size edit finds no place), then, last, the page marks of each
group of stores shared (a64_lift.group_marks), which changes that text. stats: the lift's, for its report"""
import oot_patches
oot_patches.apply(gen, protect_hearts=supported, required=supported and whole)
a64_lift.group_marks(sorted(glob.glob(os.path.join(gen, "code_*.c"))), stats)
5 changes: 4 additions & 1 deletion nx2/lift.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
import argparse
import collections
import glob
import json
import os
import struct
Expand Down Expand Up @@ -110,6 +111,8 @@ def coverage(L, img, out_dir):
return total




def main():
kit.wait_for_quiet('the lift')
p = argparse.ArgumentParser()
Expand Down Expand Up @@ -180,7 +183,7 @@ def main():
counters=[] if a.no_polls else [poll.stats])
import oot_patches
main_build = next((m["build_id"] for m in meta["modules"] if m["name"] == "main"), "")
oot_patches.apply(gen, protect_hearts=a.standalone and main_build == oot_patches.LIFE_BUILD_ID)
chunks.edit_c(gen, L.stats, a.standalone and main_build == oot_patches.LIFE_BUILD_ID, whole=not a.max_func)
a64_rtgen.write(gen)
if a.standalone:
start, exit_ = standalone.write_image_c(gen, img, meta, a.exefs)
Expand Down
24 changes: 17 additions & 7 deletions nx2/oot_patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ def patch_text_speed(s):
return s.replace('#include "a64.h"\n', '#include "a64.h"\n' + TEXT_DECL, 1)


def apply(gen, *, protect_hearts=False):
def apply(gen, *, protect_hearts=False, required=False):
"""required: the lift of the supported build (LIFE_BUILD_ID), whole: a missing output-size edit stops it (apply_output_size).
The other patches only warn when their place is not found once."""
import oot_native_resolution # (HOS_SCENE_NATIVE=1: the scene's base scale 1.0; off by default since #849's output resolution)
oot_native_resolution.apply(gen)
done = {"dynamic resolution branch": 0, "text speed block": 0, "fixed path's scale": 0}
Expand Down Expand Up @@ -206,7 +208,7 @@ def apply(gen, *, protect_hearts=False):
for what, n in done.items():
if n != 1:
print("oot_patches: WARNING: the %s was found %d times" % (what, n))
apply_output_size(gen)
apply_output_size(gen, required=required)


# Output size (host/nn_applet.c hos_output_size: the Insert menu's Output resolution, 2560 x 1440 as shipped):
Expand Down Expand Up @@ -269,17 +271,22 @@ def apply(gen, *, protect_hearts=False):
]


def apply_output_size(gen):
def apply_output_size(gen, required=False):
"""required (the supported build's whole lift): each edit must be made, or found made, exactly once, else SystemExit: without
one the Insert menu's Output resolution is ignored, or a 4K output runs out of the render targets' pool. Elsewhere (another
build, part of a lift, by hand) a warning"""
found = [0] * len(OUT_EDITS)
for path in sorted(glob.glob(os.path.join(gen, "code_*.c"))):
with open(path, encoding="utf-8", newline="") as f:
s = f.read()
hit = False
for i, (old, new) in enumerate(OUT_EDITS):
if new in s: # (applied already: a second run. Each new text holds its old one, which would be patched again)
found[i] += 1
elif old in s:
found[i] += s.count(old)
# (every place: each made one, and each old text outside a made one; a new text can hold its old one. A file with
# both is counted for both, and its old ones are left: a second made place or a stray raw one is not once)
made = s.count(new)
raw = (s.replace(new, "\0") if made else s).count(old)
found[i] += made + raw
if raw and not made:
s = s.replace(old, new)
hit = True
if hit:
Expand All @@ -289,6 +296,9 @@ def apply_output_size(gen):
f.write(s)
print("oot_patches: output size in", os.path.basename(path))
if found != [1] * len(OUT_EDITS):
if required:
raise SystemExit("oot_patches: the output size places were found %s times, not once each (OUT_EDITS %s): the "
"lifted C no longer has the text they match" % (found, [i for i, n in enumerate(found) if n != 1]))
print("oot_patches: WARNING: the output size places were found %s times" % found)


Expand Down
3 changes: 2 additions & 1 deletion nx2/test_leaf.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ def build(tmp, P, roots, funcs, meta, img_size, addr64):
if addr64:
rt = rtgen.write(os.path.join(tmp, "rt64")) # (its a64.h shadows the vendored one)
src = ['#include "a64.h"'] + ["void f_%08x(Ctx *A64_NOESC);" % e for e in sorted(funcs)]
src += [P.emitted[e][0] for e in sorted(funcs)]
body = "\n".join(P.emitted[e][0] for e in sorted(funcs))
src.append(body if os.environ.get("A64_MARK_EACH") else a64_lift.mark_groups(body)) # (nx2/lift.py's last step)
src.append(HARNESS % {
"table": ",".join("f_%08x" % e for _, e, _ in roots),
"regions": ",".join("{%#xULL,%#xULL}" % r for r in writable_regions(meta)),
Expand Down
Loading
Loading