Skip to content

Commit

Permalink
Enforce effect size limits at build time (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
bates64 authored Oct 15, 2024
1 parent 17cdc0f commit 648bbb5
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 18 deletions.
1 change: 0 additions & 1 deletion include/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,6 @@ void clear_character_set(void);
void clear_trigger_data(void);
void clear_script_list(void);
void clear_entity_data(b32);
void check_effect_sizes(void);
void clear_effect_data(void);

void clear_saved_variables(void);
Expand Down
11 changes: 0 additions & 11 deletions src/effects.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@ void set_effect_pos_offset(EffectInstance* effect, f32 x, f32 y, f32 z) {
((f32*)data)[3] = z;
}

void check_effect_sizes(void) {
s32 i;

for (i = 0; i < ARRAY_COUNT(gEffectTable); i++) {
s32 size = gEffectTable[i].dmaEnd - gEffectTable[i].dmaStart;
if (size > 0x1000) {
osSyncPrintf("WARNING: Effect 0x%x == 0x%x bytes (0x1000 limit)\n", i, size);
}
}
}

void clear_effect_data(void) {
s32 i;

Expand Down
1 change: 0 additions & 1 deletion src/main_loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,6 @@ void load_engine_data(void) {
clear_player_data();
init_encounter_status();
clear_screen_overlays();
check_effect_sizes();
clear_effect_data();
clear_saved_variables();
clear_item_entity_data();
Expand Down
41 changes: 41 additions & 0 deletions tools/build/check_segment_sizes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from sys import argv
import json
import subprocess
import sys

# Checks each segment-size pair in JSON object are valid for the given ELF.
# usage: python3 check_segment_sizes.py <elf> <json>

argv.pop(0) # python3
elf_path = argv.pop(0)
jsonstr = argv.pop(0)

def read_elf():
result = subprocess.run(["mips-linux-gnu-nm", elf_path], stdout=subprocess.PIPE)
lines = result.stdout.decode().split("\n")
symbols = {}
for line in lines:
splitted = line.split(" ")
if len(splitted) == 3:
address, kind, symbol = splitted
if symbol.endswith("_VRAM") or symbol.endswith("_VRAM_END"):
symbols[symbol] = int(address, 16)
return symbols

segment_size_map = json.loads(jsonstr)
symbols = read_elf()

fail = False
for segment_name, max_size in segment_size_map.items():
start_address = symbols[segment_name + "_VRAM"]
end_address = symbols[segment_name + "_VRAM_END"]
true_size = end_address - start_address
if true_size > max_size:
size_diff = true_size - max_size
print(f"\033[31msegment '{segment_name}' is oversized!\033[0m (size is {true_size:X}; +{size_diff:X} compared to max size {max_size:X})", file=sys.stderr)
fail = True
if fail:
print("help: segment(s) are too big to fit in their overlay(s). to fix this, write less code/data, or modify the engine and splat.yaml to increase the limit", file=sys.stderr)
exit(1)
else:
print("ok")
37 changes: 33 additions & 4 deletions tools/build/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sys
import ninja_syntax
from glob import glob
import json

# Configuration:
VERSIONS = ["us"]
Expand Down Expand Up @@ -327,6 +328,12 @@ def write_ninja_rules(

ninja.rule("flips", command=f"bash -c '{BUILD_TOOLS}/floating/flips $baserom $in $out || true'")

ninja.rule(
"check_segment_sizes",
description="check segment sizes $in",
command=f"$python {BUILD_TOOLS}/check_segment_sizes.py $in $data > $out",
)


def write_ninja_for_tools(ninja: ninja_syntax.Writer):
ninja.rule(
Expand Down Expand Up @@ -1275,6 +1282,14 @@ def build(
f"ver/{self.version}/checksum.sha1",
implicit=[str(self.rom_path())],
)
else:
ninja.build(
str(self.rom_ok_path()),
"check_segment_sizes",
str(self.elf_path()),
variables={"data": json.dumps(json.dumps(self.get_segment_max_sizes(), separators=(',', ':')))},
implicit=[str(self.rom_path())],
)

ninja.build(
str(self.patch_path()),
Expand All @@ -1286,6 +1301,23 @@ def build(
ninja.build("generated_code_" + self.version, "phony", generated_code)
ninja.build("inc_img_bins_" + self.version, "phony", inc_img_bins)



def get_segment_max_sizes(self):
assert self.linker_entries is not None
segment_size_map = {}

# depth-first search
def visit(segment):
if hasattr(segment, "parent") and segment.parent is not None:
visit(segment.parent)
if hasattr(segment, "yaml") and isinstance(segment.yaml, dict) and "max_size" in segment.yaml:
segment_size_map[segment.name] = segment.yaml["max_size"]
for entry in self.linker_entries:
visit(entry.segment)

return segment_size_map

def make_current(self, ninja: ninja_syntax.Writer):
current = Path("ver/current")

Expand Down Expand Up @@ -1505,8 +1537,5 @@ def make_current(self, ninja: ninja_syntax.Writer):
assert first_configure, "no versions configured"
first_configure.make_current(ninja)

if non_matching:
ninja.build("all", "phony", [str(first_configure.rom_path())])
else:
ninja.build("all", "phony", all_rom_oks)
ninja.build("all", "phony", all_rom_oks)
ninja.default("all")
Loading

0 comments on commit 648bbb5

Please sign in to comment.