splat64 0.37.1__py3-none-any.whl → 0.37.3__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 CHANGED
@@ -1,7 +1,7 @@
1
1
  __package_name__ = __name__
2
2
 
3
3
  # Should be synced with pyproject.toml
4
- __version__ = "0.37.1"
4
+ __version__ = "0.37.3"
5
5
  __author__ = "ethteck"
6
6
 
7
7
  from . import util as util
@@ -193,32 +193,37 @@ segments:
193
193
  file_presets.write_all_files()
194
194
 
195
195
  # Write reloc_addrs.txt file
196
- reloc_addrs = []
197
- if rom.entrypoint_info.bss_start_address is not None:
198
- reloc_addrs.append(
199
- f"rom:0x{rom.entrypoint_info.bss_start_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_START"
200
- )
201
- reloc_addrs.append(
202
- f"rom:0x{rom.entrypoint_info.bss_start_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_START"
203
- )
204
- reloc_addrs.append("")
205
- if rom.entrypoint_info.bss_size is not None:
206
- reloc_addrs.append(
207
- f"rom:0x{rom.entrypoint_info.bss_size.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_SIZE"
208
- )
209
- reloc_addrs.append(
210
- f"rom:0x{rom.entrypoint_info.bss_size.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_SIZE"
211
- )
212
- reloc_addrs.append("")
213
- if rom.entrypoint_info.bss_end_address is not None:
196
+ reloc_addrs: list[str] = []
197
+
198
+ addresses_info: list[tuple[Optional[rominfo.EntryAddressInfo], str]] = [
199
+ (rom.entrypoint_info.main_address, "main"),
200
+ (rom.entrypoint_info.bss_start_address, "main_BSS_START"),
201
+ (rom.entrypoint_info.bss_size, "main_BSS_SIZE"),
202
+ (rom.entrypoint_info.bss_end_address, "main_BSS_END"),
203
+ ]
204
+
205
+ for addr_info, sym_name in addresses_info:
206
+ if addr_info is None:
207
+ continue
208
+ if addr_info.ori:
209
+ # Avoid emitting relocations for `ori`s since `%lo` doesn't support it.
210
+ continue
211
+ if addr_info.rom_hi == addr_info.rom_lo:
212
+ # hi and lo may be the same for the "main" address, i.e. a direct jal.
213
+ continue
214
+
214
215
  reloc_addrs.append(
215
- f"rom:0x{rom.entrypoint_info.bss_end_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_END"
216
+ f"rom:0x{addr_info.rom_hi:06X} reloc:MIPS_HI16 symbol:{sym_name}"
216
217
  )
217
218
  reloc_addrs.append(
218
- f"rom:0x{rom.entrypoint_info.bss_end_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_END"
219
+ f"rom:0x{addr_info.rom_lo:06X} reloc:MIPS_LO16 symbol:{sym_name}"
219
220
  )
220
221
  reloc_addrs.append("")
221
- if rom.entrypoint_info.stack_top is not None:
222
+
223
+ if (
224
+ rom.entrypoint_info.stack_top is not None
225
+ and not rom.entrypoint_info.stack_top.ori
226
+ ):
222
227
  reloc_addrs.append(
223
228
  '// This entry corresponds to the "stack top", which is the end of the array used as the stack for the main segment.'
224
229
  )
splat/scripts/split.py CHANGED
@@ -6,6 +6,8 @@ import importlib
6
6
  from typing import Any, Dict, List, Optional, Set, Tuple, Union
7
7
  from pathlib import Path
8
8
 
9
+ from collections import defaultdict, deque
10
+
9
11
  from .. import __package_name__, __version__
10
12
  from ..disassembler import disassembler_instance
11
13
  from ..util import cache_handler, progress_bar, vram_classes, statistics, file_presets
@@ -222,6 +224,47 @@ def calc_segment_dependences(
222
224
  return vram_class_to_follows_segments
223
225
 
224
226
 
227
+ def sort_segments_by_vram_class_dependency(
228
+ all_segments: List[Segment],
229
+ ) -> List[Segment]:
230
+ # map all "_VRAM_END" strings to segments
231
+ end_sym_to_seg: Dict[str, Segment] = {}
232
+ for seg in all_segments:
233
+ end_sym_to_seg[get_segment_vram_end_symbol_name(seg)] = seg
234
+
235
+ # build dependency graph: A -> B means "A must come before B"
236
+ graph: Dict[Segment, List[Segment]] = defaultdict(list)
237
+ indeg: Dict[Segment, int] = {seg: 0 for seg in all_segments}
238
+
239
+ for seg in all_segments:
240
+ sym = seg.vram_symbol
241
+ if sym is None:
242
+ continue
243
+ dep = end_sym_to_seg.get(sym)
244
+ if dep is None or dep is seg:
245
+ continue
246
+ graph[dep].append(seg)
247
+ indeg[seg] += 1
248
+
249
+ # stable topo sort with queue seeded in original order
250
+ q = deque([seg for seg in all_segments if indeg[seg] == 0])
251
+ out: List[Segment] = []
252
+
253
+ while q:
254
+ n = q.popleft()
255
+ out.append(n)
256
+ for m in graph.get(n, []):
257
+ indeg[m] -= 1
258
+ if indeg[m] == 0:
259
+ q.append(m)
260
+
261
+ assert len(out) == len(all_segments), (
262
+ "Encountered cyclic dependency when reordering segments by vram class."
263
+ )
264
+
265
+ return out
266
+
267
+
225
268
  def read_target_binary() -> bytes:
226
269
  rom_bytes = options.opts.target_path.read_bytes()
227
270
 
@@ -317,6 +360,9 @@ def do_split(
317
360
 
318
361
 
319
362
  def write_linker_script(all_segments: List[Segment]) -> LinkerWriter:
363
+ if options.opts.ld_sort_segments_by_vram_class_dependency:
364
+ all_segments = sort_segments_by_vram_class_dependency(all_segments)
365
+
320
366
  vram_class_dependencies = calc_segment_dependences(all_segments)
321
367
  vram_classes_to_search = set(vram_class_dependencies.keys())
322
368
 
@@ -51,7 +51,7 @@ class CommonSegC(CommonSegCodeSubsegment):
51
51
 
52
52
  @staticmethod
53
53
  def get_funcs_defined_in_c(c_file: Path) -> Set[str]:
54
- with open(c_file, "r") as f:
54
+ with open(c_file, "r", encoding="utf-8") as f:
55
55
  text = CommonSegC.strip_c_comments(f.read())
56
56
 
57
57
  return set(m.group(1) for m in C_FUNC_RE.finditer(text))
@@ -105,7 +105,7 @@ class CommonSegC(CommonSegCodeSubsegment):
105
105
 
106
106
  @staticmethod
107
107
  def get_global_asm_funcs(c_file: Path) -> Set[str]:
108
- with c_file.open() as f:
108
+ with c_file.open(encoding="utf-8") as f:
109
109
  text = CommonSegC.strip_c_comments(f.read())
110
110
  if options.opts.compiler == IDO:
111
111
  return set(m.group(2) for m in C_GLOBAL_ASM_IDO_RE.finditer(text))
@@ -114,7 +114,7 @@ class CommonSegC(CommonSegCodeSubsegment):
114
114
 
115
115
  @staticmethod
116
116
  def get_global_asm_rodata_syms(c_file: Path) -> Set[str]:
117
- with c_file.open() as f:
117
+ with c_file.open(encoding="utf-8") as f:
118
118
  text = CommonSegC.strip_c_comments(f.read())
119
119
  if options.opts.compiler == IDO:
120
120
  return set(m.group(2) for m in C_GLOBAL_ASM_IDO_RE.finditer(text))
splat/util/n64/rominfo.py CHANGED
@@ -74,13 +74,14 @@ class EntryAddressInfo:
74
74
  value: int
75
75
  rom_hi: int
76
76
  rom_lo: int
77
+ ori: bool
77
78
 
78
79
  @staticmethod
79
80
  def new(
80
- value: Optional[int], hi: Optional[int], lo: Optional[int]
81
+ value: Optional[int], hi: Optional[int], lo: Optional[int], ori: Optional[int]
81
82
  ) -> Optional["EntryAddressInfo"]:
82
83
  if value is not None and hi is not None and lo is not None:
83
- return EntryAddressInfo(value, hi, lo)
84
+ return EntryAddressInfo(value, hi, lo, ori == lo)
84
85
  return None
85
86
 
86
87
 
@@ -94,6 +95,7 @@ class N64EntrypointInfo:
94
95
  main_address: Optional[EntryAddressInfo]
95
96
  stack_top: Optional[EntryAddressInfo]
96
97
  traditional_entrypoint: bool
98
+ ori_entrypoint: bool
97
99
 
98
100
  def segment_size(self) -> int:
99
101
  if self.data_size is not None:
@@ -120,6 +122,10 @@ class N64EntrypointInfo:
120
122
  completed_pair = [False for _ in range(32)]
121
123
  hi_assignments: List[Optional[int]] = [None for _ in range(32)]
122
124
  lo_assignments: List[Optional[int]] = [None for _ in range(32)]
125
+ # We need to track if something was paired using an ori instead of an
126
+ # addiu or similar, because if that's the case we can't emit normal
127
+ # relocations in the generated symbol_addrs file for it.
128
+ ori_assignments: List[Optional[int]] = [None for _ in range(32)]
123
129
 
124
130
  register_bss_address: Optional[int] = None
125
131
  register_bss_size: Optional[int] = None
@@ -130,6 +136,7 @@ class N64EntrypointInfo:
130
136
  bss_end_address: Optional[EntryAddressInfo] = None
131
137
 
132
138
  traditional_entrypoint = True
139
+ ori_entrypoint = False
133
140
  decrementing_bss_routine = True
134
141
  data_size: Optional[int] = None
135
142
  func_call_target: Optional[EntryAddressInfo] = None
@@ -162,8 +169,10 @@ class N64EntrypointInfo:
162
169
  register_values[insn.rs.value] + insn.getProcessedImmediate()
163
170
  )
164
171
  completed_pair[insn.rt.value] = True
165
- if not insn.isUnsigned():
166
- lo_assignments[insn.rt.value] = current_rom
172
+ lo_assignments[insn.rt.value] = current_rom
173
+ if insn.isUnsigned():
174
+ ori_assignments[insn.rt.value] = current_rom
175
+ ori_entrypoint = True
167
176
  elif insn.doesStore():
168
177
  if insn.rt == rabbitizer.RegGprO32.zero:
169
178
  # Try to detect the zero-ing bss algorithm
@@ -209,11 +218,13 @@ class N64EntrypointInfo:
209
218
  register_values[insn.rs.value],
210
219
  hi_assignments[insn.rs.value],
211
220
  lo_assignments[insn.rs.value],
221
+ ori_assignments[insn.rs.value],
212
222
  )
213
223
  bss_end_address = EntryAddressInfo.new(
214
224
  register_values[insn.rt.value],
215
225
  hi_assignments[insn.rt.value],
216
226
  lo_assignments[insn.rt.value],
227
+ ori_assignments[insn.rt.value],
217
228
  )
218
229
 
219
230
  elif insn.isFunctionCall():
@@ -222,7 +233,7 @@ class N64EntrypointInfo:
222
233
  # entrypoint to actual code.
223
234
  traditional_entrypoint = False
224
235
  func_call_target = EntryAddressInfo(
225
- insn.getInstrIndexAsVram(), current_rom, current_rom
236
+ insn.getInstrIndexAsVram(), current_rom, current_rom, False
226
237
  )
227
238
 
228
239
  elif insn.uniqueId == rabbitizer.InstrId.cpu_break:
@@ -255,12 +266,14 @@ class N64EntrypointInfo:
255
266
  register_values[register_bss_address],
256
267
  hi_assignments[register_bss_address],
257
268
  lo_assignments[register_bss_address],
269
+ ori_assignments[register_bss_address],
258
270
  )
259
271
  if register_bss_size is not None:
260
272
  bss_size = EntryAddressInfo.new(
261
273
  register_values[register_bss_size],
262
274
  hi_assignments[register_bss_size],
263
275
  lo_assignments[register_bss_size],
276
+ ori_assignments[register_bss_size],
264
277
  )
265
278
 
266
279
  if register_main_address is not None:
@@ -268,6 +281,7 @@ class N64EntrypointInfo:
268
281
  register_values[register_main_address],
269
282
  hi_assignments[register_main_address],
270
283
  lo_assignments[register_main_address],
284
+ ori_assignments[register_main_address],
271
285
  )
272
286
  else:
273
287
  main_address = None
@@ -276,6 +290,7 @@ class N64EntrypointInfo:
276
290
  register_values[rabbitizer.RegGprO32.sp.value],
277
291
  hi_assignments[rabbitizer.RegGprO32.sp.value],
278
292
  lo_assignments[rabbitizer.RegGprO32.sp.value],
293
+ ori_assignments[rabbitizer.RegGprO32.sp.value],
279
294
  )
280
295
 
281
296
  if not traditional_entrypoint:
@@ -300,6 +315,7 @@ class N64EntrypointInfo:
300
315
  main_address,
301
316
  stack_top,
302
317
  traditional_entrypoint,
318
+ ori_entrypoint,
303
319
  )
304
320
 
305
321
 
splat/util/options.py CHANGED
@@ -127,6 +127,8 @@ class SplatOpts:
127
127
  # `vram_symbol` / `follows_classes` (vram_class options) to calculate vram addresses in the linker script.
128
128
  # If disabled, this uses the plain integer values for vram addresses defined in the yaml.
129
129
  ld_use_symbolic_vram_addresses: bool
130
+ # Ensures segments of vram classes with dependencies (`vram_symbol` / `follows_classes`) are written to the linker script AFTER the segments they depend on.
131
+ ld_sort_segments_by_vram_class_dependency: bool
130
132
  # Change linker script generation to allow partially linking segments. Requires both `ld_partial_scripts_path` and `ld_partial_build_segments_path` to be set.
131
133
  ld_partial_linking: bool
132
134
  # Folder were each intermediary linker script will be written to.
@@ -512,6 +514,9 @@ def _parse_yaml(
512
514
  ld_sections_allowlist=p.parse_opt("ld_sections_allowlist", list, []),
513
515
  ld_sections_denylist=p.parse_opt("ld_sections_denylist", list, []),
514
516
  ld_wildcard_sections=p.parse_opt("ld_wildcard_sections", bool, False),
517
+ ld_sort_segments_by_vram_class_dependency=p.parse_opt(
518
+ "ld_sort_segments_by_vram_class_dependency", bool, False
519
+ ),
515
520
  ld_use_symbolic_vram_addresses=p.parse_opt(
516
521
  "ld_use_symbolic_vram_addresses", bool, True
517
522
  ),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splat64
3
- Version: 0.37.1
3
+ Version: 0.37.3
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
@@ -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.37.1,<1.0.0
79
+ splat64[mips]>=0.37.3,<1.0.0
80
80
  ```
81
81
 
82
82
  ### Optional dependencies
@@ -1,4 +1,4 @@
1
- splat/__init__.py,sha256=u9i1mpp1a75UqxBlneTIdu6BW5x33bBJEUxT2u14sKQ,291
1
+ splat/__init__.py,sha256=w5um4mpnXXvWNHsMD12aO20ze_KjLSuiu018fbT8CHs,291
2
2
  splat/__main__.py,sha256=T333dHDgr-2HYYhtARnYMEjdECnYiNIKfcXDD99o22A,732
3
3
  splat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  splat/disassembler/__init__.py,sha256=LXuCElHxOvbb9xuSSeswT8W_v78chwtqjKq_9nxFVzQ,167
@@ -14,8 +14,8 @@ splat/platforms/psp.py,sha256=rCr_uf1SuHNaMUXjIC0GyoA_463OQZv0se2KMarWry0,125
14
14
  splat/platforms/psx.py,sha256=YxQERdOBr4p3ab9Wk80FNhVYi-uvmh7p_jeykSFp23M,40
15
15
  splat/scripts/__init__.py,sha256=OY0nHg6a7JB437Sb96qLbZ_7ByVsal1gStj35wJAI_Y,101
16
16
  splat/scripts/capy.py,sha256=svbOfLO34-QN3xLiBy9vk2RGs_To8TWMWEKBw6yx2xQ,3674
17
- splat/scripts/create_config.py,sha256=Xt0BgS9ql5GfLrNKWQRMuKWea0sXUygRV1_wCYGrrPE,17949
18
- splat/scripts/split.py,sha256=rstKTy1osrD4Cqp07NGNVpLeBVQSNbeeTYC-AORrSJs,21937
17
+ splat/scripts/create_config.py,sha256=Fabj4UpDKJ7vEmSYl89sg8j57ktyQTT6rKaWNUuE5Sk,17881
18
+ splat/scripts/split.py,sha256=mOqaaXWX47EQpZiAmfMFLFpEBN5-KsZ_Uk2sl1SNh7k,23334
19
19
  splat/segtypes/__init__.py,sha256=-upUw_4JGQtvyp6IfTMzOq_CK3xvVaT_0K0_EipHyOo,208
20
20
  splat/segtypes/linker_entry.py,sha256=WHFdVFC_NW6_ub-LJbfwze-FZGCTRtjEZHz_bhIkX-M,25160
21
21
  splat/segtypes/segment.py,sha256=IcmF7FxDrJ1ZP5AKFO-skNrGfYhGIYwawyepKzeTF3A,30619
@@ -24,7 +24,7 @@ splat/segtypes/common/asm.py,sha256=gpR4uZ57l4Q9G0XTOeO1JIk9P5d8xXBxRfkrPIN-M1A,
24
24
  splat/segtypes/common/asmtu.py,sha256=C52kKh-8YeDHu0EucEfQ-tQMtDgfKfwAJ6wwiW6nOBU,354
25
25
  splat/segtypes/common/bin.py,sha256=qZeuvHNSrRuxdk-8V0xW0tcFhtzPk6doyf43E_t7D9Q,1250
26
26
  splat/segtypes/common/bss.py,sha256=cX0RO7LRcBRO6t463AUSSV8sGdlxZTcd5_McZjjqfFo,3107
27
- splat/segtypes/common/c.py,sha256=_YxDn2jyDjfk8ttqsTPrZvD-c6OStmERjz_P98ihS3g,21444
27
+ splat/segtypes/common/c.py,sha256=uKc8V4VSz2bRkt2g7CVbKibRdp2R1Ro9UAxBVLyB8M8,21494
28
28
  splat/segtypes/common/code.py,sha256=FfBuy6x8fQXoJYX2Ul80uOs-CzlVslUaveD-hBmvKXM,10693
29
29
  splat/segtypes/common/codesubsegment.py,sha256=a9AGNfWrQRfMVKP84_a_2BBfQSwOVviAltp8ZjOivT0,11140
30
30
  splat/segtypes/common/cpp.py,sha256=p-duowCt4czrmaWgj1LuFw5LGfdyB_uaeLTKVmH80Cg,180
@@ -82,7 +82,7 @@ splat/util/compiler.py,sha256=n5hlxazUy8rq-t7DADBbC0n6ZGuLDzp3ma3883oHQ3E,1848
82
82
  splat/util/conf.py,sha256=0Qcv5d5XAvdVzqF4i4L_lJuC5hI0PEufeob58YaN-xs,3230
83
83
  splat/util/file_presets.py,sha256=4UsDMqQy5ZACSafjCba1pwwTQKK_2leOycwTFkNG4Ac,18985
84
84
  splat/util/log.py,sha256=aJA1rg8IirJu1wGzjNuATHvepYvD3k5CtEyMasyJWxI,1193
85
- splat/util/options.py,sha256=Lq1noBa-2f_j7lSvFfOyq4b2rFhBuCvB-M_s1Czu1Pg,31931
85
+ splat/util/options.py,sha256=wPW2T1jZ3m6fAgVnVfsnvAH0ZK4pF_-mlVP967RdtNU,32287
86
86
  splat/util/palettes.py,sha256=d3KoZnwt-zunI9eNwb3txysXg4DY3xnF0O5aQRxM4so,2920
87
87
  splat/util/progress_bar.py,sha256=41VehpIFK1cphENaXV_Aq6_3mFx25eQ8V7Z51SKFPeM,166
88
88
  splat/util/relocs.py,sha256=j8fzM9u0ZQwZa1b1Dd26ho9GwIooBXt8mE0jAN7wqV0,4153
@@ -92,13 +92,13 @@ splat/util/utils.py,sha256=kAC0DAjaGa0Kq2k86RfQtFkheV87XuyeOgZIVIk3CHE,208
92
92
  splat/util/vram_classes.py,sha256=UH4rkugEwoec_-alJ4smNOcnU51G-V5OG7Pfsj47eaM,3491
93
93
  splat/util/n64/__init__.py,sha256=hsBkPh6NUz-bW7HVspcNZ0mCxBhdfcPC07_7gbga95o,84
94
94
  splat/util/n64/find_code_length.py,sha256=uUoPoUORAjsAvH8oGqwnGvw6j8I_NnSrZtA-x9h9h7E,1414
95
- splat/util/n64/rominfo.py,sha256=Ms9P-MuuOSyEF4QNB3wBuC-U9OdrfByqR0VPAHqTBQI,16976
95
+ splat/util/n64/rominfo.py,sha256=AlfVZO6T1Nguw_4nY5wqoAf7MKgvSZnIqcDcSWFaa48,17854
96
96
  splat/util/ps2/__init__.py,sha256=hAU12HtQLOc_sxc8_rzxOERBT5wjg3DhQ8XRN9hBXfA,39
97
97
  splat/util/ps2/ps2elfinfo.py,sha256=v6nTPabzXpG1N-MhhkbVeLoGjgsjBEpHL1DoPv26-Hs,7982
98
98
  splat/util/psx/__init__.py,sha256=kCCaR-KB1mNlIcXB4OuuSQ2zVLbWg_SnIZIUeyjeBBI,39
99
99
  splat/util/psx/psxexeinfo.py,sha256=Oxd5Lt8HTNHuUzw3cVujdyJRqEG-yo0XT78wEISUXms,5705
100
- splat64-0.37.1.dist-info/METADATA,sha256=3QvXBmz8S6MtVua2En81GEBd_2Q2CSy6SWS7k0gA9UQ,3879
101
- splat64-0.37.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
102
- splat64-0.37.1.dist-info/entry_points.txt,sha256=O7Xy-qNOHcI87-OQrWJ-OhRDws74SuwVb_4rtnp0eLo,52
103
- splat64-0.37.1.dist-info/licenses/LICENSE,sha256=97VMVzjG8yQvsf8NG2M9IFSbh7R8cifJnc6QK1cZqj8,1070
104
- splat64-0.37.1.dist-info/RECORD,,
100
+ splat64-0.37.3.dist-info/METADATA,sha256=FKccQgwEm4dceHwvkEoV5zXoP5nvmmROmbElBncwa1Q,3879
101
+ splat64-0.37.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
102
+ splat64-0.37.3.dist-info/entry_points.txt,sha256=O7Xy-qNOHcI87-OQrWJ-OhRDws74SuwVb_4rtnp0eLo,52
103
+ splat64-0.37.3.dist-info/licenses/LICENSE,sha256=97VMVzjG8yQvsf8NG2M9IFSbh7R8cifJnc6QK1cZqj8,1070
104
+ splat64-0.37.3.dist-info/RECORD,,