splat64 0.36.2__py3-none-any.whl → 0.36.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- splat/__init__.py +1 -1
- splat/disassembler/__init__.py +0 -1
- splat/disassembler/null_disassembler.py +0 -1
- splat/disassembler/spimdisasm_disassembler.py +1 -1
- splat/platforms/ps2.py +0 -2
- splat/scripts/create_config.py +207 -5
- splat/scripts/split.py +9 -9
- splat/segtypes/common/asm.py +1 -3
- splat/segtypes/common/bin.py +2 -1
- splat/segtypes/common/bss.py +1 -1
- splat/segtypes/common/c.py +29 -10
- splat/segtypes/common/code.py +1 -1
- splat/segtypes/common/codesubsegment.py +4 -4
- splat/segtypes/common/data.py +2 -2
- splat/segtypes/common/databin.py +0 -1
- splat/segtypes/common/rodata.py +3 -3
- splat/segtypes/common/rodatabin.py +0 -1
- splat/segtypes/common/segment.py +1 -1
- splat/segtypes/linker_entry.py +19 -14
- splat/segtypes/n64/ci.py +2 -0
- splat/segtypes/n64/gfx.py +0 -1
- splat/segtypes/n64/img.py +4 -0
- splat/segtypes/n64/palette.py +21 -2
- splat/segtypes/segment.py +7 -8
- splat/util/__init__.py +1 -0
- splat/util/cache_handler.py +1 -1
- splat/util/conf.py +5 -5
- splat/util/n64/rominfo.py +1 -1
- splat/util/ps2/__init__.py +1 -0
- splat/util/ps2/ps2elfinfo.py +249 -0
- splat/util/psx/psxexeinfo.py +2 -3
- splat/util/relocs.py +3 -3
- splat/util/symbols.py +17 -10
- splat/util/utils.py +3 -3
- {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/METADATA +12 -12
- {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/RECORD +39 -37
- {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/WHEEL +1 -1
- {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/entry_points.txt +0 -0
- {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/licenses/LICENSE +0 -0
splat/segtypes/n64/img.py
CHANGED
|
@@ -64,6 +64,10 @@ class N64SegImg(Segment):
|
|
|
64
64
|
log.error(
|
|
65
65
|
f"Error: {self.name} should end at 0x{self.rom_start + expected_len:X}, but it ends at 0x{self.rom_end:X}\n(hint: add a 'bin' segment after it)"
|
|
66
66
|
)
|
|
67
|
+
elif actual_len < expected_len:
|
|
68
|
+
log.error(
|
|
69
|
+
f"Error: {self.name} should end at 0x{self.rom_start + expected_len:X}, but it ends at 0x{self.rom_end:X}"
|
|
70
|
+
)
|
|
67
71
|
|
|
68
72
|
def out_path(self) -> Path:
|
|
69
73
|
type_extension = f".{self.type}" if self.image_type_in_extension else ""
|
splat/segtypes/n64/palette.py
CHANGED
|
@@ -23,7 +23,21 @@ class N64SegPalette(Segment):
|
|
|
23
23
|
f"segment {self.name} needs to know where it ends; add a position marker [0xDEADBEEF] after it"
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
-
if
|
|
26
|
+
if isinstance(self.yaml, dict) and "size" in self.yaml:
|
|
27
|
+
yaml_size: int = self.yaml["size"]
|
|
28
|
+
if isinstance(self.rom_start, int) and isinstance(self.rom_end, int):
|
|
29
|
+
rom_len = self.rom_end - self.rom_start
|
|
30
|
+
if rom_len < yaml_size:
|
|
31
|
+
log.error(
|
|
32
|
+
f"Error: {self.name} has a `size` value of 0x{yaml_size:X}, but this is smaller than the actual rom size of the palette (0x{rom_len:X}, start: 0x{self.rom_start:X}, end: 0x{self.rom_end:X})"
|
|
33
|
+
)
|
|
34
|
+
elif rom_len > yaml_size:
|
|
35
|
+
log.error(
|
|
36
|
+
f"Warning: {self.name} has a `size` value of 0x{yaml_size:X}, but this is larger than the end of the palette (0x{rom_len:X}, start: 0x{self.rom_start:X}, end: 0x{self.rom_end:X})\n(hint add a 'bin' segment after this palette with address 0x{self.rom_end:X})",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
size = yaml_size
|
|
40
|
+
else:
|
|
27
41
|
assert self.rom_end is not None
|
|
28
42
|
assert self.rom_start is not None
|
|
29
43
|
actual_len = self.rom_end - self.rom_start
|
|
@@ -39,7 +53,11 @@ class N64SegPalette(Segment):
|
|
|
39
53
|
log.error(
|
|
40
54
|
f"Error: {self.name} (0x{actual_len:X} bytes) is not a valid palette size ({', '.join(hex(s) for s in VALID_SIZES)})\n{hint_msg}"
|
|
41
55
|
)
|
|
56
|
+
size = actual_len
|
|
57
|
+
else:
|
|
58
|
+
size = 0
|
|
42
59
|
|
|
60
|
+
self.palette_size: int = size
|
|
43
61
|
self.global_id: Optional[str] = (
|
|
44
62
|
self.yaml.get("global_id") if isinstance(self.yaml, dict) else None
|
|
45
63
|
)
|
|
@@ -61,7 +79,8 @@ class N64SegPalette(Segment):
|
|
|
61
79
|
return palette
|
|
62
80
|
|
|
63
81
|
def parse_palette(self, rom_bytes) -> List[Tuple[int, int, int, int]]:
|
|
64
|
-
|
|
82
|
+
assert self.rom_start is not None
|
|
83
|
+
data = rom_bytes[self.rom_start : self.rom_start + self.palette_size]
|
|
65
84
|
|
|
66
85
|
return N64SegPalette.parse_palette_bytes(data)
|
|
67
86
|
|
splat/segtypes/segment.py
CHANGED
|
@@ -101,15 +101,15 @@ class Segment:
|
|
|
101
101
|
|
|
102
102
|
if options.opts.allow_segment_overrides:
|
|
103
103
|
segment_class = Segment.get_extension_segment_class(seg_type)
|
|
104
|
-
if segment_class
|
|
104
|
+
if segment_class is None:
|
|
105
105
|
segment_class = Segment.get_base_segment_class(seg_type)
|
|
106
106
|
else:
|
|
107
107
|
segment_class = Segment.get_base_segment_class(seg_type)
|
|
108
|
-
if segment_class
|
|
108
|
+
if segment_class is None:
|
|
109
109
|
# Look in extensions
|
|
110
110
|
segment_class = Segment.get_extension_segment_class(seg_type)
|
|
111
111
|
|
|
112
|
-
if segment_class
|
|
112
|
+
if segment_class is None:
|
|
113
113
|
log.error(
|
|
114
114
|
f"could not load segment type '{seg_type}'\n(hint: confirm your extension directory is configured correctly)"
|
|
115
115
|
)
|
|
@@ -538,9 +538,9 @@ class Segment:
|
|
|
538
538
|
|
|
539
539
|
@property
|
|
540
540
|
def subalign(self) -> Optional[int]:
|
|
541
|
-
assert (
|
|
542
|
-
|
|
543
|
-
)
|
|
541
|
+
assert self.parent is None, (
|
|
542
|
+
f"subalign is not valid for non-top-level segments. ({self})"
|
|
543
|
+
)
|
|
544
544
|
return self.given_subalign
|
|
545
545
|
|
|
546
546
|
@property
|
|
@@ -811,8 +811,7 @@ class Segment:
|
|
|
811
811
|
items = [
|
|
812
812
|
i
|
|
813
813
|
for i in items
|
|
814
|
-
if i.segment is None
|
|
815
|
-
or Segment.visible_ram(self, i.segment)
|
|
814
|
+
if (i.segment is None or Segment.visible_ram(self, i.segment))
|
|
816
815
|
and (type == i.type)
|
|
817
816
|
]
|
|
818
817
|
|
splat/util/__init__.py
CHANGED
|
@@ -7,6 +7,7 @@ from . import n64 as n64
|
|
|
7
7
|
from . import options as options
|
|
8
8
|
from . import palettes as palettes
|
|
9
9
|
from . import progress_bar as progress_bar
|
|
10
|
+
from . import ps2 as ps2
|
|
10
11
|
from . import psx as psx
|
|
11
12
|
from . import relocs as relocs
|
|
12
13
|
from . import statistics as statistics
|
splat/util/cache_handler.py
CHANGED
|
@@ -20,7 +20,7 @@ class Cache:
|
|
|
20
20
|
log.write(f"Loaded cache ({len(self.cache.keys())} items)")
|
|
21
21
|
except Exception:
|
|
22
22
|
log.write(
|
|
23
|
-
|
|
23
|
+
"Not able to load cache file. Discarding old cache", status="warn"
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
# invalidate entire cache if options change
|
splat/util/conf.py
CHANGED
|
@@ -10,10 +10,10 @@ from typing import Any, Dict, List, Optional
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
# This unused import makes the yaml library faster. don't remove
|
|
13
|
-
import pylibyaml #
|
|
13
|
+
import pylibyaml # noqa: F401
|
|
14
14
|
import yaml
|
|
15
15
|
|
|
16
|
-
from . import
|
|
16
|
+
from . import options, vram_classes
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _merge_configs(main_config, additional_config, additional_config_path):
|
|
@@ -26,15 +26,15 @@ def _merge_configs(main_config, additional_config, additional_config_path):
|
|
|
26
26
|
for curkey in additional_config:
|
|
27
27
|
if curkey not in main_config:
|
|
28
28
|
main_config[curkey] = additional_config[curkey]
|
|
29
|
-
elif type(main_config[curkey])
|
|
29
|
+
elif type(main_config[curkey]) is not type(additional_config[curkey]):
|
|
30
30
|
raise TypeError(
|
|
31
31
|
f"Could not merge {str(additional_config_path)}: type for key '{curkey}' in configs does not match"
|
|
32
32
|
)
|
|
33
33
|
else:
|
|
34
34
|
# keys exist and match, see if a list to append
|
|
35
|
-
if type(main_config[curkey])
|
|
35
|
+
if type(main_config[curkey]) is list:
|
|
36
36
|
main_config[curkey] += additional_config[curkey]
|
|
37
|
-
elif type(main_config[curkey])
|
|
37
|
+
elif type(main_config[curkey]) is dict:
|
|
38
38
|
# need to merge sub areas
|
|
39
39
|
main_config[curkey] = _merge_configs(
|
|
40
40
|
main_config[curkey],
|
splat/util/n64/rominfo.py
CHANGED
|
@@ -437,7 +437,7 @@ def get_info_bytes(rom_bytes: bytes, header_encoding: str) -> N64Rom:
|
|
|
437
437
|
|
|
438
438
|
try:
|
|
439
439
|
name = rom_bytes[0x20:0x34].decode(header_encoding).rstrip(" \0") or "empty"
|
|
440
|
-
except:
|
|
440
|
+
except UnicodeError:
|
|
441
441
|
sys.exit(
|
|
442
442
|
"splat could not decode the game name;"
|
|
443
443
|
" try using a different encoding by passing the --header-encoding argument"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import ps2elfinfo as ps2elfinfo
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import spimdisasm
|
|
8
|
+
from spimdisasm.elf32 import (
|
|
9
|
+
Elf32File,
|
|
10
|
+
Elf32Constants,
|
|
11
|
+
Elf32SectionHeaderFlag,
|
|
12
|
+
Elf32ObjectFileType,
|
|
13
|
+
)
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from .. import log
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
ELF_SECTION_MAPPING: dict[str, str] = {
|
|
20
|
+
".text": "asm",
|
|
21
|
+
".data": "data",
|
|
22
|
+
".rodata": "rodata",
|
|
23
|
+
".bss": "bss",
|
|
24
|
+
".sdata": "sdata",
|
|
25
|
+
".sbss": "sbss",
|
|
26
|
+
".gcc_except_table": "gcc_except_table",
|
|
27
|
+
".lit4": "lit4",
|
|
28
|
+
".lit8": "lit8",
|
|
29
|
+
".ctor": "ctor",
|
|
30
|
+
".vtables": "vtables",
|
|
31
|
+
".vutext": "textbin", # No "proper" support yet
|
|
32
|
+
".vudata": "databin", # No "proper" support yet
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Section to not put into the elf_section_names list, because splat doesn't
|
|
36
|
+
# know have support for them yet.
|
|
37
|
+
ELF_SECTIONS_IGNORE: set[str] = {
|
|
38
|
+
".vutext",
|
|
39
|
+
".vudata",
|
|
40
|
+
".vubss",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
ELF_SMALL_SECTIONS: set[str] = {
|
|
44
|
+
".lit4",
|
|
45
|
+
".lit8",
|
|
46
|
+
".sdata",
|
|
47
|
+
".srdata",
|
|
48
|
+
".sbss",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclasses.dataclass
|
|
53
|
+
class Ps2Elf:
|
|
54
|
+
entrypoint: int
|
|
55
|
+
segs: list[FakeSegment]
|
|
56
|
+
size: int
|
|
57
|
+
compiler: str
|
|
58
|
+
elf_section_names: list[tuple[str, bool]]
|
|
59
|
+
gp: Optional[int]
|
|
60
|
+
ld_gp_expression: Optional[str]
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def get_info(elf_path: Path, elf_bytes: bytes) -> Optional[Ps2Elf]:
|
|
64
|
+
# Avoid spimdisasm from complaining about unknown sections.
|
|
65
|
+
spimdisasm.common.GlobalConfig.QUIET = True
|
|
66
|
+
|
|
67
|
+
elf = Elf32File(elf_bytes)
|
|
68
|
+
if elf.header.type != Elf32ObjectFileType.EXEC.value:
|
|
69
|
+
log.write("Elf file is not an EXEC type.", status="warn")
|
|
70
|
+
return None
|
|
71
|
+
if elf.header.machine != 8:
|
|
72
|
+
# 8 corresponds to EM_MIPS
|
|
73
|
+
# We only care about mips binaries.
|
|
74
|
+
log.write("Elf file is not a MIPS binary.", status="warn")
|
|
75
|
+
return None
|
|
76
|
+
if Elf32Constants.Elf32HeaderFlag._5900 not in elf.elfFlags:
|
|
77
|
+
log.write("Missing 5900 flag", status="warn")
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
if elf.reginfo is not None and elf.reginfo.gpValue != 0:
|
|
81
|
+
gp = elf.reginfo.gpValue
|
|
82
|
+
else:
|
|
83
|
+
gp = None
|
|
84
|
+
first_small_section_info: Optional[tuple[str, int]] = None
|
|
85
|
+
|
|
86
|
+
first_segment_name = "cod"
|
|
87
|
+
segs = [FakeSegment(first_segment_name, 0, 0, [])]
|
|
88
|
+
|
|
89
|
+
# TODO: check `.comment` section for any compiler info
|
|
90
|
+
compiler = "EEGCC"
|
|
91
|
+
|
|
92
|
+
elf_section_names: list[tuple[str, bool]] = []
|
|
93
|
+
|
|
94
|
+
first_offset: Optional[int] = None
|
|
95
|
+
rom_size = 0
|
|
96
|
+
|
|
97
|
+
previous_type = Elf32Constants.Elf32SectionHeaderType.PROGBITS
|
|
98
|
+
do_new_segs = False
|
|
99
|
+
|
|
100
|
+
# Loop over all the sections.
|
|
101
|
+
# Treat every normal elf section as part as the "main" segment.
|
|
102
|
+
# If we see a PROGBITS after a NOBITS then we shift the behavior into
|
|
103
|
+
# putting every new elf section into their own segment, this can happen
|
|
104
|
+
# when the elf contains a few special sections like `.mfifo`.
|
|
105
|
+
section_headers = sorted(elf.sectionHeaders, key=lambda x: x.offset)
|
|
106
|
+
for section in section_headers:
|
|
107
|
+
if section.size == 0:
|
|
108
|
+
# Skip over empty sections
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
name = elf.shstrtab[section.name]
|
|
112
|
+
if name == ".mwcats":
|
|
113
|
+
compiler = "MWCCPS2"
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
flags, _unknown_flags = Elf32SectionHeaderFlag.parseFlags(section.flags)
|
|
117
|
+
# if _unknown_flags != 0:
|
|
118
|
+
# print(name, f"0x{_unknown_flags:08X}")
|
|
119
|
+
if Elf32SectionHeaderFlag.ALLOC not in flags:
|
|
120
|
+
# We don't care about non-alloc sections
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
# We want PROGBITS (actual data) and NOBITS (bss and similar)
|
|
124
|
+
typ = Elf32Constants.Elf32SectionHeaderType.fromValue(section.type)
|
|
125
|
+
is_nobits = typ == Elf32Constants.Elf32SectionHeaderType.NOBITS
|
|
126
|
+
if typ == Elf32Constants.Elf32SectionHeaderType.PROGBITS:
|
|
127
|
+
if previous_type == Elf32Constants.Elf32SectionHeaderType.NOBITS:
|
|
128
|
+
do_new_segs = True
|
|
129
|
+
pass
|
|
130
|
+
elif typ == Elf32Constants.Elf32SectionHeaderType.NOBITS:
|
|
131
|
+
pass
|
|
132
|
+
elif typ == Elf32Constants.Elf32SectionHeaderType.MIPS_REGINFO:
|
|
133
|
+
continue
|
|
134
|
+
else:
|
|
135
|
+
log.write(
|
|
136
|
+
f"Unknown section type '{typ}' ({name}) found in the elf",
|
|
137
|
+
status="warn",
|
|
138
|
+
)
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
if first_offset is None:
|
|
142
|
+
first_offset = section.offset
|
|
143
|
+
|
|
144
|
+
if first_small_section_info is None and name in ELF_SMALL_SECTIONS:
|
|
145
|
+
first_small_section_info = (name, section.addr)
|
|
146
|
+
|
|
147
|
+
start = section.offset - first_offset
|
|
148
|
+
size = section.size
|
|
149
|
+
|
|
150
|
+
if do_new_segs:
|
|
151
|
+
segs.append(FakeSegment(name.lstrip("."), 0, start, []))
|
|
152
|
+
|
|
153
|
+
# Try to map this section to something splat can understand.
|
|
154
|
+
splat_segment_type = ELF_SECTION_MAPPING.get(name)
|
|
155
|
+
if splat_segment_type is None:
|
|
156
|
+
# Let's infer based on the section's flags
|
|
157
|
+
if is_nobits:
|
|
158
|
+
splat_segment_type = "bss"
|
|
159
|
+
elif Elf32SectionHeaderFlag.EXECINSTR in flags:
|
|
160
|
+
splat_segment_type = "asm"
|
|
161
|
+
elif Elf32SectionHeaderFlag.WRITE in flags:
|
|
162
|
+
splat_segment_type = "data"
|
|
163
|
+
else:
|
|
164
|
+
# Whatever...
|
|
165
|
+
splat_segment_type = "rodata"
|
|
166
|
+
|
|
167
|
+
if name.startswith("."):
|
|
168
|
+
valid_for_splat = (
|
|
169
|
+
name in ELF_SECTION_MAPPING
|
|
170
|
+
and name not in ELF_SECTIONS_IGNORE
|
|
171
|
+
and not do_new_segs
|
|
172
|
+
)
|
|
173
|
+
elf_section_names.append((name, valid_for_splat))
|
|
174
|
+
|
|
175
|
+
new_section = ElfSection(
|
|
176
|
+
name,
|
|
177
|
+
splat_segment_type,
|
|
178
|
+
section.addr,
|
|
179
|
+
start,
|
|
180
|
+
size,
|
|
181
|
+
is_nobits,
|
|
182
|
+
)
|
|
183
|
+
segs[-1].sections.append(new_section)
|
|
184
|
+
if is_nobits:
|
|
185
|
+
segs[-1].bss_size += size
|
|
186
|
+
else:
|
|
187
|
+
rom_size = start + size
|
|
188
|
+
|
|
189
|
+
previous_type = typ
|
|
190
|
+
|
|
191
|
+
# There are some games where they just squashed most sections into a
|
|
192
|
+
# single one, making the elf_section_names list pretty useless.
|
|
193
|
+
# We try to detect this and provide a default list if that's the case,
|
|
194
|
+
# hoping for the best.
|
|
195
|
+
if len(elf_section_names) < 4:
|
|
196
|
+
elf_section_names = [
|
|
197
|
+
(".text", True),
|
|
198
|
+
(".vutext", False),
|
|
199
|
+
(".data", True),
|
|
200
|
+
(".vudata", False),
|
|
201
|
+
(".rodata", True),
|
|
202
|
+
(".gcc_except_table", True),
|
|
203
|
+
(".lit8", True),
|
|
204
|
+
(".lit4", True),
|
|
205
|
+
(".sdata", True),
|
|
206
|
+
(".sbss", True),
|
|
207
|
+
(".bss", True),
|
|
208
|
+
(".vubss", False),
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
# Fixup vram address of segments
|
|
212
|
+
for seg in segs:
|
|
213
|
+
seg.vram = seg.sections[0].vram
|
|
214
|
+
|
|
215
|
+
ld_gp_expression = None
|
|
216
|
+
if gp is not None and first_small_section_info is not None:
|
|
217
|
+
section_name, section_address = first_small_section_info
|
|
218
|
+
if gp > section_address:
|
|
219
|
+
diff = gp - section_address
|
|
220
|
+
ld_gp_expression = f"{first_segment_name}_{section_name.strip('.').upper()}_START + 0x{diff:04X}"
|
|
221
|
+
|
|
222
|
+
return Ps2Elf(
|
|
223
|
+
elf.header.entry,
|
|
224
|
+
segs,
|
|
225
|
+
rom_size,
|
|
226
|
+
compiler,
|
|
227
|
+
elf_section_names,
|
|
228
|
+
gp,
|
|
229
|
+
ld_gp_expression,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclasses.dataclass
|
|
234
|
+
class FakeSegment:
|
|
235
|
+
name: str
|
|
236
|
+
vram: int
|
|
237
|
+
start: int
|
|
238
|
+
sections: list[ElfSection]
|
|
239
|
+
bss_size: int = 0
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@dataclasses.dataclass
|
|
243
|
+
class ElfSection:
|
|
244
|
+
name: str
|
|
245
|
+
splat_segment_type: str
|
|
246
|
+
vram: int
|
|
247
|
+
start: int
|
|
248
|
+
size: int
|
|
249
|
+
is_nobits: bool
|
splat/util/psx/psxexeinfo.py
CHANGED
|
@@ -12,7 +12,6 @@ import dataclasses
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
14
|
import rabbitizer
|
|
15
|
-
import spimdisasm
|
|
16
15
|
|
|
17
16
|
# PSX EXE has the following layout
|
|
18
17
|
# header ; 0x80 bytes
|
|
@@ -215,11 +214,11 @@ def main():
|
|
|
215
214
|
|
|
216
215
|
print(f"Entrypoint: 0x{exe.entrypoint:08X}")
|
|
217
216
|
|
|
218
|
-
print(
|
|
217
|
+
print("Initial GP: ", end="")
|
|
219
218
|
if exe.initial_gp != 0:
|
|
220
219
|
print(f"0x{exe.initial_gp:08X}")
|
|
221
220
|
else:
|
|
222
|
-
print(
|
|
221
|
+
print("No")
|
|
223
222
|
|
|
224
223
|
print()
|
|
225
224
|
print(f"Destination VRAM: 0x{exe.destination_vram:08X}")
|
splat/util/relocs.py
CHANGED
|
@@ -94,13 +94,13 @@ def initialize():
|
|
|
94
94
|
|
|
95
95
|
if rom_addr is None:
|
|
96
96
|
log.parsing_error_preamble(path, line_num, line)
|
|
97
|
-
log.error(
|
|
97
|
+
log.error("Missing required 'rom' attribute for reloc")
|
|
98
98
|
if reloc_type is None:
|
|
99
99
|
log.parsing_error_preamble(path, line_num, line)
|
|
100
|
-
log.error(
|
|
100
|
+
log.error("Missing required 'reloc' attribute for reloc")
|
|
101
101
|
if symbol_name is None:
|
|
102
102
|
log.parsing_error_preamble(path, line_num, line)
|
|
103
|
-
log.error(
|
|
103
|
+
log.error("Missing required 'symbol' attribute for reloc")
|
|
104
104
|
|
|
105
105
|
reloc = Reloc(rom_addr, reloc_type, symbol_name)
|
|
106
106
|
if addend is not None:
|
splat/util/symbols.py
CHANGED
|
@@ -226,7 +226,9 @@ def handle_sym_addrs(
|
|
|
226
226
|
tf_val = (
|
|
227
227
|
True
|
|
228
228
|
if is_truey(attr_val)
|
|
229
|
-
else False
|
|
229
|
+
else False
|
|
230
|
+
if is_falsey(attr_val)
|
|
231
|
+
else None
|
|
230
232
|
)
|
|
231
233
|
if tf_val is None:
|
|
232
234
|
log.parsing_error_preamble(path, line_num, line)
|
|
@@ -432,9 +434,9 @@ def initialize_spim_context(all_segments: "List[Segment]") -> None:
|
|
|
432
434
|
overlaps_found = False
|
|
433
435
|
# Check the vram range of the global segment does not overlap with any overlay segment
|
|
434
436
|
for ovl_segment in overlay_segments:
|
|
435
|
-
assert (
|
|
436
|
-
ovl_segment.vramStart
|
|
437
|
-
)
|
|
437
|
+
assert ovl_segment.vramStart <= ovl_segment.vramEnd, (
|
|
438
|
+
f"{ovl_segment.vramStart:08X} {ovl_segment.vramEnd:08X}"
|
|
439
|
+
)
|
|
438
440
|
if (
|
|
439
441
|
ovl_segment.vramEnd > global_vram_start
|
|
440
442
|
and global_vram_end > ovl_segment.vramStart
|
|
@@ -446,20 +448,20 @@ def initialize_spim_context(all_segments: "List[Segment]") -> None:
|
|
|
446
448
|
overlaps_found = True
|
|
447
449
|
if overlaps_found:
|
|
448
450
|
log.write(
|
|
449
|
-
|
|
451
|
+
"Many overlaps between non-global and global segments were found.",
|
|
450
452
|
)
|
|
451
453
|
log.write(
|
|
452
|
-
|
|
454
|
+
"This is usually caused by missing `exclusive_ram_id` tags on segments that have a higher vram address than other `exclusive_ram_id`-tagged segments"
|
|
453
455
|
)
|
|
454
456
|
if len(global_segments_after_overlays) > 0:
|
|
455
457
|
log.write(
|
|
456
|
-
|
|
458
|
+
"These segments are the main suspects for missing a `exclusive_ram_id` tag:",
|
|
457
459
|
status="warn",
|
|
458
460
|
)
|
|
459
461
|
for seg in global_segments_after_overlays:
|
|
460
462
|
log.write(f" '{seg.name}', rom: 0x{seg.rom_start:06X}")
|
|
461
463
|
else:
|
|
462
|
-
log.write(
|
|
464
|
+
log.write("No suspected segments??", status="warn")
|
|
463
465
|
log.error("Stopping due to the above errors")
|
|
464
466
|
|
|
465
467
|
# pass the global symbols to spimdisasm
|
|
@@ -598,8 +600,13 @@ def add_symbol_to_spim_section(
|
|
|
598
600
|
return context_sym
|
|
599
601
|
|
|
600
602
|
|
|
603
|
+
# force_in_segment=True when the symbol belongs to this specific segment.
|
|
604
|
+
# force_in_segment=False when this symbol is just a reference.
|
|
601
605
|
def create_symbol_from_spim_symbol(
|
|
602
|
-
segment: "Segment",
|
|
606
|
+
segment: "Segment",
|
|
607
|
+
context_sym: spimdisasm.common.ContextSymbol,
|
|
608
|
+
*,
|
|
609
|
+
force_in_segment: bool,
|
|
603
610
|
) -> "Symbol":
|
|
604
611
|
in_segment = False
|
|
605
612
|
|
|
@@ -629,7 +636,7 @@ def create_symbol_from_spim_symbol(
|
|
|
629
636
|
in_segment = segment.contains_vram(context_sym.vram)
|
|
630
637
|
|
|
631
638
|
sym = segment.create_symbol(
|
|
632
|
-
context_sym.vram, in_segment, type=sym_type, reference=True
|
|
639
|
+
context_sym.vram, force_in_segment or in_segment, type=sym_type, reference=True
|
|
633
640
|
)
|
|
634
641
|
|
|
635
642
|
if sym.given_name is None and context_sym.name is not None:
|
splat/util/utils.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import List, Optional, TypeVar
|
|
|
3
3
|
T = TypeVar("T")
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def list_index(
|
|
7
|
-
if value not in
|
|
6
|
+
def list_index(the_list: List[T], value: T) -> Optional[int]:
|
|
7
|
+
if value not in the_list:
|
|
8
8
|
return None
|
|
9
|
-
return
|
|
9
|
+
return the_list.index(value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splat64
|
|
3
|
-
Version: 0.36.
|
|
3
|
+
Version: 0.36.4
|
|
4
4
|
Summary: A binary splitting tool to assist with decompilation and modding projects
|
|
5
5
|
Project-URL: Repository, https://github.com/ethteck/splat
|
|
6
6
|
Project-URL: Issues, https://github.com/ethteck/splat/issues
|
|
@@ -30,27 +30,27 @@ License-File: LICENSE
|
|
|
30
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
31
31
|
Classifier: Programming Language :: Python :: 3
|
|
32
32
|
Requires-Python: >=3.9
|
|
33
|
-
Requires-Dist: colorama
|
|
34
|
-
Requires-Dist: intervaltree
|
|
35
|
-
Requires-Dist: pylibyaml
|
|
36
|
-
Requires-Dist: pyyaml
|
|
37
|
-
Requires-Dist: tqdm
|
|
33
|
+
Requires-Dist: colorama==0.4.6
|
|
34
|
+
Requires-Dist: intervaltree==3.1.0
|
|
35
|
+
Requires-Dist: pylibyaml==0.1.0
|
|
36
|
+
Requires-Dist: pyyaml==6.0.3
|
|
37
|
+
Requires-Dist: tqdm==4.67.1
|
|
38
38
|
Provides-Extra: dev
|
|
39
|
-
Requires-Dist: black; extra == 'dev'
|
|
40
39
|
Requires-Dist: crunch64<1.0.0,>=0.5.1; extra == 'dev'
|
|
41
40
|
Requires-Dist: mypy; extra == 'dev'
|
|
42
41
|
Requires-Dist: n64img>=0.3.3; extra == 'dev'
|
|
43
|
-
Requires-Dist: pygfxd; extra == 'dev'
|
|
42
|
+
Requires-Dist: pygfxd>=1.0.5; extra == 'dev'
|
|
44
43
|
Requires-Dist: rabbitizer<2.0.0,>=1.12.0; extra == 'dev'
|
|
45
|
-
Requires-Dist:
|
|
44
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
45
|
+
Requires-Dist: spimdisasm<2.0.0,>=1.38.0; extra == 'dev'
|
|
46
46
|
Requires-Dist: types-colorama; extra == 'dev'
|
|
47
47
|
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
48
48
|
Provides-Extra: mips
|
|
49
49
|
Requires-Dist: crunch64<1.0.0,>=0.5.1; extra == 'mips'
|
|
50
50
|
Requires-Dist: n64img>=0.3.3; extra == 'mips'
|
|
51
|
-
Requires-Dist: pygfxd; extra == 'mips'
|
|
51
|
+
Requires-Dist: pygfxd>=1.0.5; extra == 'mips'
|
|
52
52
|
Requires-Dist: rabbitizer<2.0.0,>=1.12.0; extra == 'mips'
|
|
53
|
-
Requires-Dist: spimdisasm<2.0.0,>=1.
|
|
53
|
+
Requires-Dist: spimdisasm<2.0.0,>=1.38.0; extra == 'mips'
|
|
54
54
|
Description-Content-Type: text/markdown
|
|
55
55
|
|
|
56
56
|
# splat
|
|
@@ -76,7 +76,7 @@ The brackets corresponds to the optional dependencies to install while installin
|
|
|
76
76
|
If you use a `requirements.txt` file in your repository, then you can add this library with the following line:
|
|
77
77
|
|
|
78
78
|
```txt
|
|
79
|
-
splat64[mips]>=0.36.
|
|
79
|
+
splat64[mips]>=0.36.4,<1.0.0
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
### Optional dependencies
|