mirror of
https://github.com/pmret/papermario.git
synced 2024-11-08 12:02:30 +01:00
commit
064963aca7
1
.vscode/c_cpp_properties.json
vendored
1
.vscode/c_cpp_properties.json
vendored
@ -12,6 +12,7 @@
|
||||
},
|
||||
"includePath": [
|
||||
"${workspaceFolder}/include",
|
||||
"${workspaceFolder}/build/include",
|
||||
"${workspaceFolder}/src"
|
||||
],
|
||||
"defines": [
|
||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -6,6 +6,7 @@
|
||||
"-std=gnu89",
|
||||
"-Iinclude",
|
||||
"-Iinclude/PR",
|
||||
"-Ibuild/include",
|
||||
"-Isrc",
|
||||
"-D_LANGUAGE_C",
|
||||
"-DSCRIPT(...)={}",
|
||||
|
11
configure.py
11
configure.py
@ -12,6 +12,7 @@ import hashlib
|
||||
|
||||
sys.path.append(os.path.dirname(__file__) + "/tools/splat")
|
||||
import split
|
||||
from segtypes.n64.code import Subsegment
|
||||
|
||||
INCLUDE_ASM_RE = re.compile(r"___INCLUDE_ASM\([^,]+, ([^,]+), ([^,)]+)") # note _ prefix
|
||||
|
||||
@ -60,11 +61,11 @@ def read_splat(splat_config: str):
|
||||
segments[path] = segment
|
||||
|
||||
if isinstance(segment, N64SegCode):
|
||||
for split_file in segment.files:
|
||||
if split_file["subtype"] in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8", "palette"]:
|
||||
for split_file in segment.subsegments:
|
||||
if split_file.type in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8", "palette"]:
|
||||
path = os.path.join(
|
||||
#segment.get_subdir(split_file["subtype"]),
|
||||
split_file["name"] + "." + segment.get_ext(split_file["subtype"])
|
||||
split_file.name + "." + split_file.get_ext()
|
||||
)
|
||||
|
||||
if path in segments:
|
||||
@ -448,13 +449,13 @@ async def main():
|
||||
elif f.endswith(".s"):
|
||||
n.build(obj(f), "as", f)
|
||||
elif f.endswith(".png"):
|
||||
if isinstance(segment, dict):
|
||||
if isinstance(segment, Subsegment):
|
||||
# image within a code section
|
||||
out = "$builddir/" + f + ".bin"
|
||||
infile = find_asset(re.sub(r"\.pal\.png", ".png", f))
|
||||
|
||||
n.build(out, "img", infile, implicit="tools/img/build.py", variables={
|
||||
"img_type": segment["subtype"],
|
||||
"img_type": segment.type,
|
||||
"img_flags": "",
|
||||
})
|
||||
|
||||
|
@ -671,6 +671,7 @@ typedef s16 ItemId;
|
||||
typedef UNK_TYPE PlayerAnim;
|
||||
#define PlayerAnim_STAND_STILL 0x00010000
|
||||
#define PlayerAnim_CROUCH_STILL 0x00010001
|
||||
#define PlayerAnim_2 0x00010002 // TODO
|
||||
#define PlayerAnim_PANTING 0x00010003
|
||||
#define PlayerAnim_WALKING 0x00010004
|
||||
#define PlayerAnim_RUNNING 0x00010005
|
||||
|
@ -31,14 +31,14 @@ Gfx N(coconutDL)[] = {
|
||||
gsSPEndDisplayList(),
|
||||
};
|
||||
|
||||
s32 D_802A1B50_7304B0[] = {
|
||||
s32 N(coconutItemModelCommandList)[] = {
|
||||
0x00000004, 0x0000000D, 0x00000001, sizeof(N(coconutDL)) / sizeof(s32), &N(coconutDL), 0x00000002, 0x00000000,
|
||||
};
|
||||
|
||||
Script N(main) = SCRIPT({
|
||||
SI_VAR(10) =c ItemId_COCONUT;
|
||||
|
||||
await D_802A1240_72F960;
|
||||
await N(UseItemWithEffect);
|
||||
|
||||
UseCamPreset(3);
|
||||
MoveBattleCamOver(15);
|
||||
@ -47,7 +47,7 @@ Script N(main) = SCRIPT({
|
||||
PlaySound(SoundId_THROW);
|
||||
sleep 3;
|
||||
|
||||
func_802D3474(SI_VAR(10), D_802A1B50_7304B0);
|
||||
func_802D3474(SI_VAR(10), N(coconutItemModelCommandList));
|
||||
|
||||
$x = 1.0;
|
||||
MultiplyByActorScale($x);
|
||||
@ -91,5 +91,5 @@ Script N(main) = SCRIPT({
|
||||
|
||||
func_802D3624(SI_VAR(10));
|
||||
|
||||
await D_802A1670_72FFD0; // back to home pos
|
||||
await N(PlayerGoHome);
|
||||
});
|
||||
|
@ -7,8 +7,8 @@
|
||||
#undef NAMESPACE
|
||||
#define NAMESPACE battle_item_coconut
|
||||
|
||||
Script D_802A1240_72F960;
|
||||
Script D_802A1670_72FFD0;
|
||||
Script N(UseItemWithEffect);
|
||||
Script N(PlayerGoHome);
|
||||
|
||||
ApiStatus func_802A1000_72F720(ScriptInstance* script, s32 isInitialCall);
|
||||
ApiStatus func_802A11D4_72F8F4(ScriptInstance* script, s32 isInitialCall);
|
||||
|
@ -3,7 +3,7 @@
|
||||
MenuIcon* D_802A1E80;
|
||||
void* D_80108A64; // an image
|
||||
|
||||
// Returns time to sleep for on SI_VAR(0).
|
||||
// Returns time to sleep for on $x.
|
||||
ApiStatus N(GiveRefund)(ScriptInstance* script, s32 isInitialCall) {
|
||||
BattleStatus* battleStatus = BATTLE_STATUS;
|
||||
Actor* player = PLAYER_ACTOR;
|
||||
@ -60,93 +60,108 @@ ApiStatus N(GiveRefundCleanup)(ScriptInstance* script, s32 isInitialCall) {
|
||||
return ApiStatus_DONE2;
|
||||
}
|
||||
|
||||
Script D_802A1240_72F960 = SCRIPT({
|
||||
/// Provide arg `TRUE` on `SI_VAR(1)` to disable refunding.
|
||||
Script N(UseItemWithEffect) = SCRIPT({
|
||||
if (SI_VAR(1) == 0) {
|
||||
UseCamPreset(69);
|
||||
UseCamPreset(69); // Nice
|
||||
sleep 10;
|
||||
PlaySoundAtActor(0, 8333);
|
||||
SetAnimation(0, 0, 0x1001F);
|
||||
GetActorPos(0, SI_VAR(0), SI_VAR(1), SI_VAR(2));
|
||||
SI_VAR(0) += 18;
|
||||
SetActorSpeed(0, 4.0);
|
||||
SetGoalPos(0, SI_VAR(0), SI_VAR(1), SI_VAR(2));
|
||||
|
||||
PlaySoundAtActor(ActorID_PLAYER, 8333);
|
||||
SetAnimation(ActorID_PLAYER, 0, PlayerAnim_GOT_ITEM);
|
||||
GetActorPos(ActorID_PLAYER, $x, $y, $z);
|
||||
$x += 18;
|
||||
SetActorSpeed(ActorID_PLAYER, 4.0);
|
||||
SetGoalPos(ActorID_PLAYER, $x, $y, $z);
|
||||
PlayerRunToGoal(0);
|
||||
SI_VAR(1) += 45;
|
||||
SI_VAR(3) = SI_VAR(1);
|
||||
SI_VAR(3) += 10;
|
||||
SI_VAR(3) += 2;
|
||||
PlayEffect(51, 1, SI_VAR(0), SI_VAR(3), SI_VAR(2), 1.0, 30, 0, 0, 0, 0, 0, 0, 0);
|
||||
MakeItemEntity(SI_VAR(10), SI_VAR(0), SI_VAR(1), SI_VAR(2), 1, 0);
|
||||
SI_VAR(10) = SI_VAR(0);
|
||||
|
||||
$y += 45;
|
||||
$effectY = $y;
|
||||
$effectY += 10;
|
||||
$effectY += 2;
|
||||
PlayEffect(51, 1, $x, $effectY, $z, 1.0, 30, 0, 0, 0, 0, 0, 0, 0);
|
||||
MakeItemEntity(SI_VAR(10), $x, $y, $z, 1, 0);
|
||||
SI_VAR(10) = $x;
|
||||
|
||||
N(GiveRefund)();
|
||||
sleep SI_VAR(0);
|
||||
sleep $x;
|
||||
|
||||
sleep 15;
|
||||
|
||||
N(GiveRefundCleanup)();
|
||||
RemoveItemEntity(SI_VAR(10));
|
||||
} else {
|
||||
GetActorPos(0, SI_VAR(0), SI_VAR(1), SI_VAR(2));
|
||||
PlaySoundAtActor(0, 8333);
|
||||
SetAnimation(0, 0, 0x1001F);
|
||||
// No refund.
|
||||
|
||||
GetActorPos(ActorID_PLAYER, $x, $y, $z);
|
||||
PlaySoundAtActor(ActorID_PLAYER, 8333);
|
||||
SetAnimation(ActorID_PLAYER, 0, PlayerAnim_GOT_ITEM);
|
||||
sleep 4;
|
||||
SI_VAR(1) += 45;
|
||||
SI_VAR(3) = SI_VAR(1);
|
||||
SI_VAR(3) += 10;
|
||||
SI_VAR(3) += 2;
|
||||
PlayEffect(51, 1, SI_VAR(0), SI_VAR(3), SI_VAR(2), 1.0, 30, 0, 0, 0, 0, 0, 0, 0);
|
||||
MakeItemEntity(SI_VAR(10), SI_VAR(0), SI_VAR(1), SI_VAR(2), 1, 0);
|
||||
SI_VAR(10) = SI_VAR(0);
|
||||
|
||||
$y += 45;
|
||||
$effectY = $y;
|
||||
$effectY += 10;
|
||||
$effectY += 2;
|
||||
PlayEffect(51, 1, $x, $effectY, $z, 1.0, 30, 0, 0, 0, 0, 0, 0, 0);
|
||||
MakeItemEntity(SI_VAR(10), $x, $y, $z, 1, 0);
|
||||
SI_VAR(10) = $x;
|
||||
|
||||
sleep 15;
|
||||
RemoveItemEntity(SI_VAR(10));
|
||||
}
|
||||
});
|
||||
|
||||
Script D_802A1544_72FC64 = SCRIPT({
|
||||
Script N(UseItem) = SCRIPT({
|
||||
UseCamPreset(19);
|
||||
SetBattleCamTarget(0xFFFFFFAB, 1, 0);
|
||||
SetBattleCamTarget(-85, 1, 0);
|
||||
SetBattleCamOffsetZ(41);
|
||||
SetBattleCamZoom(248);
|
||||
MoveBattleCamOver(30);
|
||||
sleep 10;
|
||||
SetAnimation(0, 0, 0x1001F);
|
||||
GetActorPos(0, SI_VAR(0), SI_VAR(1), SI_VAR(2));
|
||||
SI_VAR(1) += 45;
|
||||
MakeItemEntity(SI_VAR(10), SI_VAR(0), SI_VAR(1), SI_VAR(2), 1, 0);
|
||||
SI_VAR(14) = SI_VAR(0);
|
||||
|
||||
SetAnimation(ActorID_PLAYER, 0, PlayerAnim_GOT_ITEM);
|
||||
GetActorPos(ActorID_PLAYER, $x, $y, $z);
|
||||
$y += 45;
|
||||
MakeItemEntity(SI_VAR(10), $x, $y, $z, 1, 0);
|
||||
SI_VAR(14) = $x;
|
||||
|
||||
N(GiveRefund)();
|
||||
sleep SI_VAR(0);
|
||||
sleep $x;
|
||||
|
||||
sleep 15;
|
||||
|
||||
N(GiveRefundCleanup)();
|
||||
RemoveItemEntity(SI_VAR(14));
|
||||
});
|
||||
|
||||
Script D_802A1670_72FFD0 = SCRIPT({
|
||||
UseIdleAnimation(0, 0);
|
||||
SetGoalToHome(0);
|
||||
SetActorSpeed(0, 8.0);
|
||||
SetAnimation(0, 0, PlayerAnim_RUNNING);
|
||||
Script N(PlayerGoHome) = SCRIPT({
|
||||
UseIdleAnimation(ActorID_PLAYER, 0);
|
||||
SetGoalToHome(ActorID_PLAYER);
|
||||
SetActorSpeed(ActorID_PLAYER, 8.0);
|
||||
SetAnimation(ActorID_PLAYER, 0, PlayerAnim_RUNNING);
|
||||
PlayerRunToGoal(0);
|
||||
SetAnimation(0, 0, 0x10002);
|
||||
UseIdleAnimation(0, 1);
|
||||
|
||||
SetAnimation(ActorID_PLAYER, 0, PlayerAnim_2);
|
||||
UseIdleAnimation(ActorID_PLAYER, 1);
|
||||
});
|
||||
|
||||
Script Script_802A170C = SCRIPT({
|
||||
Script N(EatItem) = SCRIPT({
|
||||
spawn {
|
||||
loop 4 {
|
||||
PlaySoundAtActor(0, 0x2095);
|
||||
sleep 10;
|
||||
}
|
||||
}
|
||||
SetAnimation(0, 0, 0x1001C);
|
||||
SetAnimation(ActorID_PLAYER, 0, PlayerAnim_EAT);
|
||||
sleep 45;
|
||||
});
|
||||
|
||||
Script D_802A1784_7300E4 = SCRIPT({
|
||||
Script N(DrinkItem) = SCRIPT({
|
||||
spawn {
|
||||
loop 4 {
|
||||
PlaySoundAtActor(0, 0x2095);
|
||||
sleep 10;
|
||||
}
|
||||
}
|
||||
SetAnimation(0, 0, 0x10025);
|
||||
SetAnimation(ActorID_PLAYER, 0, PlayerAnim_DRINK);
|
||||
sleep 45;
|
||||
});
|
@ -39,6 +39,7 @@ script_parser = Lark(r"""
|
||||
| stmt_no_semi
|
||||
|
||||
?stmt: call
|
||||
| var_decl
|
||||
| "goto" label -> label_goto
|
||||
| "return" -> return_stmt
|
||||
| "break" -> break_stmt
|
||||
@ -103,6 +104,8 @@ script_parser = Lark(r"""
|
||||
loop_stmt: "loop" [expr] block
|
||||
loop_until_stmt: "loop" block "until" "(" expr cond_op expr ")"
|
||||
|
||||
var_decl: ("int"|"float") variable
|
||||
|
||||
?expr: c_const_expr
|
||||
| ESCAPED_STRING
|
||||
| SIGNED_INT
|
||||
@ -597,6 +600,9 @@ class Compile(Transformer):
|
||||
name = tree.children[0]
|
||||
return self.alloc.variables.index(name) - 30000000
|
||||
|
||||
def var_decl(self, tree):
|
||||
return []
|
||||
|
||||
def label_decl(self, tree):
|
||||
if len(tree.children) == 1:
|
||||
label = tree.children[0]
|
||||
|
1560
tools/splat.yaml
1560
tools/splat.yaml
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/ethteck/splat.git
|
||||
branch = master
|
||||
commit = 733546feac9b29b100553aca1bc9916e1c211f5b
|
||||
parent = f8a344e713fbe1244ae4f28cf9329179c46123b1
|
||||
commit = 7e40c55d56f779521350fa881aa4dbdf30032a8c
|
||||
parent = 68e927503b880c53ebb03a74030479ca519eb9bc
|
||||
method = merge
|
||||
cmdver = 0.4.3
|
||||
|
@ -23,11 +23,17 @@ Internally, there's a new Symbol class which stores information about a symbol a
|
||||
|
||||
**data example**: `gSomeDataVar = 0x80024233; // type:data size:0x100`
|
||||
|
||||
As always, feel free to reach out to me with any questions, suggestions, or feedback.
|
||||
|
||||
## 0.6.1: `assets_dir` option
|
||||
### 0.6.1: `assets_dir` option
|
||||
|
||||
This release adds a new `assets_dir` option in `splat.yaml`s that allows you to override the default `img`, `bin`, and other directories that segments output to.
|
||||
|
||||
Want to interdisperse split assets with your sourcecode? `assets_dir: src`!
|
||||
Want to have all assets live in a single directory? `assets_dir: assets`!
|
||||
|
||||
### 0.6.2: Subsections
|
||||
I've begun a refactor of the code "files" code, which makes everything cleaner and easier to extend.
|
||||
There's also a new option, `create_new_c_files`, which disables the creation of nonexistent c files. This behavior is on by default, but if you want to disable it for any reason, you now have the option to do so.
|
||||
|
||||
I am also working on adding bss support as well. It should almost be all set, aside from the changes needed in the linker script.
|
||||
|
||||
**Breaking change**: The `files` field in `code` segments should now be renamed to `subsections`.
|
||||
|
@ -1,5 +1,4 @@
|
||||
PyYAML>=5.3.1,<6
|
||||
pypng==0.0.20
|
||||
colorama>=0.4.4,<0.5
|
||||
python-ranges>=0.1.3,<0.2
|
||||
capstone>=4.0.2,<5
|
||||
capstone>=4.0.2,<5
|
||||
|
@ -1,3 +1,4 @@
|
||||
from typing import get_args
|
||||
from capstone import *
|
||||
from capstone.mips import *
|
||||
|
||||
@ -43,54 +44,111 @@ def get_funcs_defined_in_c(c_file):
|
||||
|
||||
return set(m.group(2) for m in C_FUNC_RE.finditer(text))
|
||||
|
||||
class Subsegment():
|
||||
|
||||
def __init__(self, start, end, name, type, vram, args):
|
||||
self.rom_start = start
|
||||
self.rom_end = end
|
||||
self.size = self.rom_end - self.rom_start
|
||||
self.name = name
|
||||
self.vram_start = vram
|
||||
self.vram_end = vram + self.size
|
||||
self.type = type
|
||||
self.args = args
|
||||
|
||||
# TODO maybe move to a better place
|
||||
if self.type in [".bss", "bss"]:
|
||||
self.rom_start = 0
|
||||
self.rom_end = 0
|
||||
self.size = self.args[0]
|
||||
self.vram_end = self.vram_start + self.size
|
||||
|
||||
def contains_vram(self, addr):
|
||||
return self.vram_start <= addr < self.vram_end
|
||||
|
||||
def get_out_subdir(self, options):
|
||||
if self.type in ["c", ".data", ".rodata", ".bss"]:
|
||||
return "src"
|
||||
elif self.type in ["asm", "hasm", "header"]:
|
||||
return "asm"
|
||||
elif self.type == "bin":
|
||||
return options.get("assets_dir", "bin")
|
||||
elif self.type in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8", "palette"]:
|
||||
return options.get("assets_dir", "img")
|
||||
return self.type
|
||||
|
||||
def get_ld_obj_type(self, section_name):
|
||||
if self.type in "c":
|
||||
return ".text"
|
||||
elif self.type in ["bin", ".data", "data", "i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8", "palette"]:
|
||||
return ".data"
|
||||
elif self.type in [".rodata", "rodata"]:
|
||||
return ".rodata"
|
||||
elif self.type in [".bss", "bss"]:
|
||||
return ".bss"
|
||||
return section_name
|
||||
|
||||
def get_ext(self):
|
||||
if self.type in ["c", ".data", ".rodata", ".bss"]:
|
||||
return "c"
|
||||
elif self.type in ["asm", "hasm", "header"]:
|
||||
return "s"
|
||||
elif self.type == "bin":
|
||||
return "bin"
|
||||
elif self.type in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8"]:
|
||||
return "png"
|
||||
elif self.type == "palette":
|
||||
return "pal.png"
|
||||
return self.type
|
||||
|
||||
def should_run(self, options):
|
||||
return self.type in options["modes"] or "all" in options["modes"]
|
||||
|
||||
|
||||
class N64SegCode(N64Segment):
|
||||
def parse_segment_files(self, segment, seg_start, seg_end, seg_name, seg_vram):
|
||||
prefix = seg_name if seg_name.endswith("/") else f"{seg_name}_"
|
||||
def parse_subsegments(self, segment_yaml):
|
||||
prefix = self.name if self.name.endswith("/") else f"{self.name}_"
|
||||
|
||||
ret = []
|
||||
prev_start = -1
|
||||
|
||||
if "files" in segment:
|
||||
for i, split_file in enumerate(segment["files"]):
|
||||
if type(split_file) is dict:
|
||||
start = split_file["start"]
|
||||
end = split_file["end"]
|
||||
name = None if "name" not in split_file else split_file["name"]
|
||||
subtype = split_file["type"]
|
||||
args = []
|
||||
else:
|
||||
start = split_file[0]
|
||||
end = seg_end if i == len(segment["files"]) - 1 else segment["files"][i + 1][0]
|
||||
name = None if len(split_file) < 3 else split_file[2]
|
||||
subtype = split_file[1]
|
||||
args = split_file[3:]
|
||||
if "subsections" not in segment_yaml:
|
||||
print(f"Error: Code segment {self.name} is missing a 'subsections' field")
|
||||
sys.exit(2)
|
||||
|
||||
if start < prev_start:
|
||||
print(f"Error: Code segment {seg_name} has files out of ascending rom order (0x{prev_start:X} followed by 0x{start:X})")
|
||||
sys.exit(1)
|
||||
for i, subsection_yaml in enumerate(segment_yaml["subsections"]):
|
||||
if type(subsection_yaml) is dict:
|
||||
start = subsection_yaml["start"]
|
||||
end = subsection_yaml["end"]
|
||||
name = subsection_yaml.get("name", None)
|
||||
typ = subsection_yaml["type"]
|
||||
args = subsection_yaml.get("args", [])
|
||||
else:
|
||||
start = subsection_yaml[0]
|
||||
end = self.rom_end if i == len(segment_yaml["subsections"]) - 1 else segment_yaml["subsections"][i + 1][0]
|
||||
name = None if len(subsection_yaml) < 3 else subsection_yaml[2]
|
||||
typ = subsection_yaml[1]
|
||||
args = subsection_yaml[3:]
|
||||
|
||||
if not name:
|
||||
name = self.get_default_name(start) if seg_name == self.get_default_name(seg_start) else f"{prefix}{start:X}"
|
||||
elif seg_name.endswith("/"):
|
||||
name = seg_name + name
|
||||
if start < prev_start:
|
||||
print(f"Error: Code segment {self.name} contains subsections which are out of ascending rom order (0x{prev_start:X} followed by 0x{start:X})")
|
||||
sys.exit(1)
|
||||
|
||||
vram = seg_vram + (start - seg_start)
|
||||
if not name:
|
||||
name = self.get_default_name(start) if self.name == self.get_default_name(self.rom_start) else f"{prefix}{start:X}"
|
||||
elif self.name.endswith("/"):
|
||||
name = self.name + name
|
||||
|
||||
fl = {"start": start, "end": end, "name": name, "vram": vram, "subtype": subtype, "args": args}
|
||||
vram = self.rom_to_ram(start)
|
||||
|
||||
ret.append(fl)
|
||||
prev_start = start
|
||||
else:
|
||||
fl = {"start": seg_start, "end": seg_end,
|
||||
"name": seg_name, "vram": seg_vram, "subtype": "asm"}
|
||||
ret.append(fl)
|
||||
ret.append(Subsegment(start, end, name, typ, vram, args))
|
||||
prev_start = start
|
||||
|
||||
return ret
|
||||
|
||||
def __init__(self, segment, next_segment, options):
|
||||
super().__init__(segment, next_segment, options)
|
||||
self.files = self.parse_segment_files(segment, self.rom_start, self.rom_end, self.name, self.vram_start)
|
||||
self.subsegments = self.parse_subsegments(segment)
|
||||
self.is_overlay = segment.get("overlay", False)
|
||||
self.all_symbols = ()
|
||||
self.seg_symbols = {} # Symbols known to be in this segment
|
||||
@ -307,9 +365,9 @@ class N64SegCode(N64Segment):
|
||||
return ret
|
||||
|
||||
def get_file_for_addr(self, addr):
|
||||
for fl in self.files:
|
||||
if addr >= fl["vram"] and addr < fl["vram"] + fl["end"] - fl["start"]:
|
||||
return fl
|
||||
for sub in self.subsegments:
|
||||
if sub.contains_vram(addr):
|
||||
return sub
|
||||
return None
|
||||
|
||||
def update_access_mnemonic(self, sym, mnemonic):
|
||||
@ -490,7 +548,6 @@ class N64SegCode(N64Segment):
|
||||
return ret
|
||||
|
||||
def should_run(self):
|
||||
# we have lots of subtypes
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
@ -509,17 +566,23 @@ class N64SegCode(N64Segment):
|
||||
|
||||
return True
|
||||
|
||||
def get_symbols_for_file(self, split_file):
|
||||
def get_symbols_for_file(self, sub):
|
||||
ret = []
|
||||
|
||||
vram_start = split_file["vram"]
|
||||
vram_end = split_file["vram"] + split_file["end"] - split_file["start"]
|
||||
|
||||
for symbol_addr in self.seg_symbols:
|
||||
for symbol in self.seg_symbols[symbol_addr]:
|
||||
if symbol.vram_start >= vram_start and symbol.vram_end < vram_end:
|
||||
if sub.contains_vram(symbol.vram_start):
|
||||
ret.append(symbol)
|
||||
|
||||
ret.sort(key=lambda s:s.vram_start)
|
||||
|
||||
# Ensure we start at the beginning
|
||||
if len(ret) == 0 or ret[0].vram_start != sub.vram_start:
|
||||
ret.insert(0, self.get_symbol(sub.vram_start, create=True, define=True, local_only=True))
|
||||
|
||||
# Make a dummy symbol here that marks the end of the previous symbol's disasm range
|
||||
ret.append(Symbol(sub.vram_end))
|
||||
|
||||
return ret
|
||||
|
||||
def disassemble_symbol(self, sym_bytes, sym_type):
|
||||
@ -530,12 +593,22 @@ class N64SegCode(N64Segment):
|
||||
|
||||
if sym_type == "double":
|
||||
slen = 8
|
||||
elif sym_type in ["float", "word", "jtbl"]:
|
||||
slen = 4
|
||||
elif sym_type == "short":
|
||||
slen = 2
|
||||
else:
|
||||
elif sym_type == "byte":
|
||||
slen = 1
|
||||
else:
|
||||
slen = 4
|
||||
|
||||
if sym_type == "ascii":
|
||||
try:
|
||||
ascii_str = sym_bytes.decode("EUC-JP")
|
||||
ascii_str = ascii_str.replace("\\", "\\\\")
|
||||
ascii_str = ascii_str.replace("\x00", "\\0")
|
||||
sym_str += f'"{ascii_str}"'
|
||||
return sym_str
|
||||
except:
|
||||
return self.disassemble_symbol(sym_bytes, "word")
|
||||
|
||||
i = 0
|
||||
while i < len(sym_bytes):
|
||||
@ -583,79 +656,51 @@ class N64SegCode(N64Segment):
|
||||
|
||||
return sym_str
|
||||
|
||||
def disassemble_data(self, split_file, rom_bytes):
|
||||
rodata_encountered = split_file["subtype"] == "rodata"
|
||||
def disassemble_data(self, sub, rom_bytes):
|
||||
rodata_encountered = sub.type == "rodata"
|
||||
ret = ".include \"macro.inc\"\n\n"
|
||||
ret += f'.section .{split_file["subtype"]}'
|
||||
ret += f'.section .{sub.type}'
|
||||
|
||||
# Todo remove when we have class for file
|
||||
file_size = split_file['end'] - split_file['start']
|
||||
|
||||
if file_size == 0:
|
||||
if sub.size == 0:
|
||||
return None
|
||||
|
||||
syms = self.get_symbols_for_file(split_file)
|
||||
syms.sort(key=lambda s:s.vram_start)
|
||||
|
||||
if len(syms) == 0:
|
||||
self.warn("No symbol accesses detected for " + split_file["name"] + "; the output will most likely be an ugly blob")
|
||||
|
||||
# Ensure we start at the beginning
|
||||
if len(syms) == 0 or syms[0].vram_start != split_file["vram"]:
|
||||
syms.insert(0, self.get_symbol(split_file["vram"], create=True, define=True, local_only=True))
|
||||
|
||||
vram_end = split_file["vram"] + file_size
|
||||
if syms[-1].vram_start != vram_end:
|
||||
# Make a dummy symbol here that marks the end of the previous symbol's disasm range
|
||||
syms.append(Symbol(vram_end))
|
||||
syms = self.get_symbols_for_file(sub)
|
||||
|
||||
for i in range(len(syms) - 1):
|
||||
mnemonic = syms[i].access_mnemonic
|
||||
start = syms[i].vram_start
|
||||
end = syms[i + 1].vram_start
|
||||
sym_rom_start = start - split_file["vram"] + split_file["start"]
|
||||
sym_rom_end = end - split_file["vram"] + split_file["start"]
|
||||
sym = self.get_symbol(start, create=True, define=True, local_only=True)
|
||||
sym = self.get_symbol(syms[i].vram_start, create=True, define=True, local_only=True)
|
||||
sym_str = f"\n\nglabel {sym.name}\n"
|
||||
sym_bytes = rom_bytes[sym_rom_start : sym_rom_end]
|
||||
dis_start = self.ram_to_rom(syms[i].vram_start)
|
||||
dis_end = self.ram_to_rom(syms[i + 1].vram_start)
|
||||
|
||||
# TODO: Hack for null mnemonic - move elsewhere later, probably
|
||||
if not mnemonic:
|
||||
mnemonic = "addiu"
|
||||
|
||||
# .ascii
|
||||
if self.is_valid_ascii(sym_bytes) and mnemonic == "addiu":
|
||||
# mnemonic thing may be too picky, we'll see
|
||||
try:
|
||||
ascii_str = sym_bytes.decode("EUC-JP")
|
||||
ascii_str = ascii_str.replace("\\", "\\\\")
|
||||
ascii_str = ascii_str.replace("\x00", "\\0")
|
||||
sym_str += f'.ascii "{ascii_str}"'
|
||||
ret += sym_str
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback to raw data
|
||||
if syms[i].type == "jtbl":
|
||||
stype = "jtbl"
|
||||
elif len(sym_bytes) % 8 == 0 and mnemonic in double_mnemonics:
|
||||
stype = "double"
|
||||
elif len(sym_bytes) % 4 == 0 and mnemonic in word_mnemonics:
|
||||
stype = "word"
|
||||
elif len(sym_bytes) % 4 == 0 and mnemonic in float_mnemonics:
|
||||
stype = "float"
|
||||
elif len(sym_bytes) % 2 == 0 and mnemonic in short_mnemonics:
|
||||
stype = "short"
|
||||
if sub.type == "bss":
|
||||
sym_len = dis_end - dis_start
|
||||
ret += f".space 0x{sym_len:X}"
|
||||
else:
|
||||
stype = "byte"
|
||||
sym_bytes = rom_bytes[dis_start : dis_end]
|
||||
|
||||
if not rodata_encountered and mnemonic == "jtbl":
|
||||
rodata_encountered = True
|
||||
ret += "\n\n\n.section .rodata"
|
||||
# Checking if the mnemonic is addiu may be too picky - we'll see
|
||||
if self.is_valid_ascii(sym_bytes) and mnemonic == "addiu":
|
||||
stype = "ascii"
|
||||
elif syms[i].type == "jtbl":
|
||||
stype = "jtbl"
|
||||
elif len(sym_bytes) % 8 == 0 and mnemonic in double_mnemonics:
|
||||
stype = "double"
|
||||
elif len(sym_bytes) % 4 == 0 and mnemonic in float_mnemonics:
|
||||
stype = "float"
|
||||
elif len(sym_bytes) % 4 == 0 and mnemonic in word_mnemonics or not mnemonic:
|
||||
stype = "word"
|
||||
elif len(sym_bytes) % 2 == 0 and mnemonic in short_mnemonics:
|
||||
stype = "short"
|
||||
else:
|
||||
stype = "byte"
|
||||
|
||||
sym_str += self.disassemble_symbol(sym_bytes, stype)
|
||||
ret += sym_str
|
||||
if not rodata_encountered and mnemonic == "jtbl":
|
||||
rodata_encountered = True
|
||||
ret += "\n\n\n.section .rodata"
|
||||
|
||||
sym_str += self.disassemble_symbol(sym_bytes, stype)
|
||||
ret += sym_str
|
||||
|
||||
ret += "\n"
|
||||
|
||||
@ -702,6 +747,39 @@ class N64SegCode(N64Segment):
|
||||
if found:
|
||||
break
|
||||
|
||||
def create_c_asm_file(self, funcs_text, func, out_dir, sub, func_name):
|
||||
if self.options.get("compiler", "IDO") == "GCC":
|
||||
out_lines = self.get_gcc_inc_header()
|
||||
else:
|
||||
out_lines = []
|
||||
out_lines.extend(funcs_text[func][0])
|
||||
out_lines.append("")
|
||||
|
||||
outpath = Path(os.path.join(out_dir, sub.name, func_name + ".s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write("\n".join(out_lines))
|
||||
self.log(f"Disassembled {func_name} to {outpath}")
|
||||
|
||||
def create_c_file(self, funcs_text, sub, asm_out_dir, base_path, c_path):
|
||||
c_lines = self.get_c_preamble()
|
||||
|
||||
for func in funcs_text:
|
||||
func_name = self.get_symbol(func, type="func", local_only=True).name
|
||||
if self.options.get("compiler", "IDO") == "GCC":
|
||||
c_lines.append("INCLUDE_ASM(s32, \"{}\", {});".format(sub.name, func_name))
|
||||
else:
|
||||
asm_outpath = Path(os.path.join(asm_out_dir, sub.name, func_name + ".s"))
|
||||
rel_asm_outpath = os.path.relpath(asm_outpath, base_path)
|
||||
c_lines.append(f"#pragma GLOBAL_ASM(\"{rel_asm_outpath}\")")
|
||||
c_lines.append("")
|
||||
|
||||
Path(c_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(c_path, "w") as f:
|
||||
f.write("\n".join(c_lines))
|
||||
print(f"Wrote {sub.name} to {c_path}")
|
||||
|
||||
def split(self, rom_bytes, base_path):
|
||||
md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + CS_MODE_BIG_ENDIAN)
|
||||
md.detail = True
|
||||
@ -709,21 +787,19 @@ class N64SegCode(N64Segment):
|
||||
|
||||
palettes = {}
|
||||
|
||||
for split_file in self.files:
|
||||
file_type = split_file["subtype"]
|
||||
|
||||
if file_type in ["asm", "hasm", "c"]:
|
||||
if self.type not in self.options["modes"] and "all" not in self.options["modes"]:
|
||||
for sub in self.subsegments:
|
||||
if sub.type in ["asm", "hasm", "c"]:
|
||||
if not sub.should_run(self.options):
|
||||
continue
|
||||
|
||||
if split_file["start"] == split_file["end"]:
|
||||
if sub.rom_start == sub.rom_end:
|
||||
continue
|
||||
|
||||
out_dir = self.create_split_dir(base_path, "asm")
|
||||
asm_out_dir = self.create_split_dir(base_path, "asm")
|
||||
|
||||
rom_addr = split_file["start"]
|
||||
rom_addr = sub.rom_start
|
||||
|
||||
insns = [insn for insn in md.disasm(rom_bytes[split_file["start"]: split_file["end"]], split_file["vram"])]
|
||||
insns = [insn for insn in md.disasm(rom_bytes[sub.rom_start: sub.rom_end], sub.vram_start)]
|
||||
|
||||
funcs = self.process_insns(insns, rom_addr)
|
||||
|
||||
@ -735,11 +811,11 @@ class N64SegCode(N64Segment):
|
||||
self.gather_jumptable_labels(rom_bytes)
|
||||
funcs_text = self.add_labels(funcs)
|
||||
|
||||
if file_type == "c":
|
||||
if sub.type == "c":
|
||||
c_path = os.path.join(
|
||||
base_path,
|
||||
self.get_subdir(split_file["subtype"]),
|
||||
split_file["name"] + "." + self.get_ext(split_file["subtype"])
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
|
||||
if os.path.exists(c_path):
|
||||
@ -748,114 +824,82 @@ class N64SegCode(N64Segment):
|
||||
else:
|
||||
defined_funcs = set()
|
||||
|
||||
out_dir = self.create_split_dir(
|
||||
base_path, os.path.join("asm", "nonmatchings"))
|
||||
asm_out_dir = self.create_split_dir(base_path, os.path.join("asm", "nonmatchings"))
|
||||
|
||||
for func in funcs_text:
|
||||
func_name = self.get_symbol(func, type="func", local_only=True).name
|
||||
|
||||
if func_name not in defined_funcs:
|
||||
if self.options.get("compiler", "IDO") == "GCC":
|
||||
out_lines = self.get_gcc_inc_header()
|
||||
else:
|
||||
out_lines = []
|
||||
out_lines.extend(funcs_text[func][0])
|
||||
out_lines.append("")
|
||||
|
||||
outpath = Path(os.path.join(
|
||||
out_dir, split_file["name"], func_name + ".s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write("\n".join(out_lines))
|
||||
self.log(f"Disassembled {func_name} to {outpath}")
|
||||
|
||||
# Creation of c files
|
||||
if not os.path.exists(c_path): # and some option is enabled
|
||||
c_lines = self.get_c_preamble()
|
||||
|
||||
for func in funcs_text:
|
||||
func_name = self.get_symbol(func, type="func", local_only=True).name
|
||||
if self.options.get("compiler", "IDO") == "GCC":
|
||||
c_lines.append("INCLUDE_ASM(s32, \"{}\", {});".format(split_file["name"], func_name))
|
||||
else:
|
||||
outpath = Path(os.path.join(out_dir, split_file["name"], func_name + ".s"))
|
||||
rel_outpath = os.path.relpath(outpath, base_path)
|
||||
c_lines.append(f"#pragma GLOBAL_ASM(\"{rel_outpath}\")")
|
||||
c_lines.append("")
|
||||
|
||||
Path(c_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(c_path, "w") as f:
|
||||
f.write("\n".join(c_lines))
|
||||
print(f"Wrote {split_file['name']} to {c_path}")
|
||||
self.create_c_asm_file(funcs_text, func, asm_out_dir, sub, func_name)
|
||||
|
||||
if not os.path.exists(c_path) and self.options.get("create_new_c_files", True):
|
||||
self.create_c_file(funcs_text, sub, asm_out_dir, base_path, c_path)
|
||||
else:
|
||||
out_lines = self.get_asm_header()
|
||||
for func in funcs_text:
|
||||
out_lines.extend(funcs_text[func][0])
|
||||
out_lines.append("")
|
||||
|
||||
outpath = Path(os.path.join(out_dir, split_file["name"] + ".s"))
|
||||
outpath = Path(os.path.join(asm_out_dir, sub.name + ".s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write("\n".join(out_lines))
|
||||
|
||||
elif file_type in ["data", "rodata"] and (file_type in self.options["modes"] or "all" in self.options["modes"]):
|
||||
out_dir = self.create_split_dir(base_path, os.path.join("asm", "data"))
|
||||
elif sub.type in ["data", "rodata", "bss"] and sub.should_run(self.options):
|
||||
asm_out_dir = self.create_split_dir(base_path, os.path.join("asm", "data"))
|
||||
|
||||
outpath = Path(os.path.join(out_dir, split_file["name"] + f".{file_type}.s"))
|
||||
outpath = Path(os.path.join(asm_out_dir, sub.name + f".{sub.type}.s"))
|
||||
outpath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
file_text = self.disassemble_data(split_file, rom_bytes)
|
||||
file_text = self.disassemble_data(sub, rom_bytes)
|
||||
if file_text:
|
||||
with open(outpath, "w", newline="\n") as f:
|
||||
f.write(file_text)
|
||||
|
||||
elif file_type == "bin" and ("bin" in self.options["modes"] or "all" in self.options["modes"]):
|
||||
elif sub.type == "bin" and sub.should_run(self.options):
|
||||
bin_path = os.path.join(
|
||||
base_path,
|
||||
self.get_subdir(split_file["subtype"]),
|
||||
split_file["name"] + "." + self.get_ext(split_file["subtype"])
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
|
||||
Path(bin_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(bin_path, "wb") as f:
|
||||
f.write(rom_bytes[split_file["start"]: split_file["end"]])
|
||||
f.write(rom_bytes[sub.rom_start : sub.rom_end])
|
||||
|
||||
elif file_type in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8"]:
|
||||
elif sub.type in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8"]:
|
||||
pass
|
||||
|
||||
elif file_type == "palette":
|
||||
elif sub.type == "palette":
|
||||
from segtypes.n64.palette import N64SegPalette
|
||||
|
||||
out_path = os.path.join(
|
||||
base_path,
|
||||
self.get_subdir(split_file["subtype"]),
|
||||
split_file["name"] + "." + self.get_ext(split_file["subtype"])
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
img_bytes = rom_bytes[split_file["start"]:split_file["end"]]
|
||||
img_bytes = rom_bytes[sub.rom_start : sub.rom_end]
|
||||
|
||||
palette = N64SegPalette.parse_palette(img_bytes)
|
||||
palettes[split_file["name"]] = palette
|
||||
palettes[sub.name] = palette
|
||||
|
||||
import png
|
||||
|
||||
for split_file in self.files:
|
||||
file_type = split_file["subtype"]
|
||||
img_bytes = rom_bytes[split_file["start"]:split_file["end"]]
|
||||
for sub in self.subsegments:
|
||||
img_bytes = rom_bytes[sub.rom_start : sub.rom_end]
|
||||
|
||||
out_path = os.path.join(
|
||||
base_path,
|
||||
self.get_subdir(split_file["subtype"]),
|
||||
split_file["name"] + "." + self.get_ext(split_file["subtype"])
|
||||
sub.get_out_subdir(self.options),
|
||||
sub.name + "." + sub.get_ext()
|
||||
)
|
||||
|
||||
if file_type == "ci4" and (file_type in self.options["modes"] or "all" in self.options["modes"] or "img" in self.options["modes"]):
|
||||
if sub.type == "ci4" and (sub.should_run(self.options) or "img" in self.options["modes"]):
|
||||
from segtypes.n64.ci4 import N64SegCi4
|
||||
|
||||
width, height = split_file["args"]
|
||||
palette = palettes[split_file["name"]]
|
||||
width, height = sub.args
|
||||
palette = palettes[sub.name]
|
||||
image = N64SegCi4.parse_image(img_bytes, width, height)
|
||||
|
||||
w = png.Writer(width, height, palette=palette)
|
||||
@ -868,53 +912,15 @@ class N64SegCode(N64Segment):
|
||||
|
||||
# TODO write orphaned palettes
|
||||
|
||||
def get_subdir(self, subtype):
|
||||
if subtype in ["c", ".data", ".rodata", ".bss"]:
|
||||
return "src"
|
||||
elif subtype in ["asm", "hasm", "header"]:
|
||||
return "asm"
|
||||
elif subtype == "bin":
|
||||
return self.options.get("assets_dir", "bin")
|
||||
elif subtype in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8", "palette"]:
|
||||
return self.options.get("assets_dir", "img")
|
||||
return subtype
|
||||
|
||||
@staticmethod
|
||||
def get_ext(subtype):
|
||||
if subtype in ["c", ".data", ".rodata", ".bss"]:
|
||||
return "c"
|
||||
elif subtype in ["asm", "hasm", "header"]:
|
||||
return "s"
|
||||
elif subtype == "bin":
|
||||
return "bin"
|
||||
elif subtype in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8"]:
|
||||
return "png"
|
||||
elif subtype == "palette":
|
||||
return "pal.png"
|
||||
return subtype
|
||||
|
||||
@staticmethod
|
||||
def get_ld_obj_type(subtype, section_name):
|
||||
if subtype in "c":
|
||||
return ".text"
|
||||
elif subtype in ["bin", ".data", "data", "i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8", "palette"]:
|
||||
return ".data"
|
||||
elif subtype in [".rodata", "rodata"]:
|
||||
return ".rodata"
|
||||
elif subtype == ".bss":
|
||||
return ".bss"
|
||||
return section_name
|
||||
|
||||
def get_ld_files(self):
|
||||
def transform(split_file):
|
||||
subdir = self.get_subdir(split_file["subtype"])
|
||||
obj_type = self.get_ld_obj_type(split_file["subtype"], ".text")
|
||||
ext = self.get_ext(split_file['subtype'])
|
||||
start = split_file["start"]
|
||||
def transform(sub):
|
||||
subdir = sub.get_out_subdir(self.options)
|
||||
obj_type = sub.get_ld_obj_type(".text")
|
||||
ext = sub.get_ext()
|
||||
|
||||
return subdir, f"{split_file['name']}.{ext}", obj_type, start
|
||||
return subdir, f"{sub.name}.{ext}", obj_type, sub.rom_start
|
||||
|
||||
return [transform(file) for file in self.files]
|
||||
return [transform(file) for file in self.subsegments]
|
||||
|
||||
def get_ld_section_name(self):
|
||||
path = PurePath(self.name)
|
||||
|
@ -1,6 +1,5 @@
|
||||
from pathlib import Path, PurePath
|
||||
from util import log
|
||||
import os
|
||||
import re
|
||||
|
||||
default_subalign = 16
|
||||
|
@ -1,7 +1,6 @@
|
||||
#! /usr/bin/python3
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import zlib
|
||||
|
||||
parser = argparse.ArgumentParser(description='Gives information on n64 roms')
|
||||
@ -80,7 +79,7 @@ def get_info_bytes(rom_bytes, encoding):
|
||||
cic = get_cic(rom_bytes)
|
||||
entry_point = get_entry_point(program_counter, cic)
|
||||
|
||||
# todo add support for
|
||||
# TODO: add support for
|
||||
# compression_formats = []
|
||||
# for format in ["Yay0", "vpk0"]:
|
||||
# if rom_bytes.find(bytes(format, "ASCII")) != -1:
|
||||
|
Loading…
Reference in New Issue
Block a user