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.
Files changed (39) hide show
  1. splat/__init__.py +1 -1
  2. splat/disassembler/__init__.py +0 -1
  3. splat/disassembler/null_disassembler.py +0 -1
  4. splat/disassembler/spimdisasm_disassembler.py +1 -1
  5. splat/platforms/ps2.py +0 -2
  6. splat/scripts/create_config.py +207 -5
  7. splat/scripts/split.py +9 -9
  8. splat/segtypes/common/asm.py +1 -3
  9. splat/segtypes/common/bin.py +2 -1
  10. splat/segtypes/common/bss.py +1 -1
  11. splat/segtypes/common/c.py +29 -10
  12. splat/segtypes/common/code.py +1 -1
  13. splat/segtypes/common/codesubsegment.py +4 -4
  14. splat/segtypes/common/data.py +2 -2
  15. splat/segtypes/common/databin.py +0 -1
  16. splat/segtypes/common/rodata.py +3 -3
  17. splat/segtypes/common/rodatabin.py +0 -1
  18. splat/segtypes/common/segment.py +1 -1
  19. splat/segtypes/linker_entry.py +19 -14
  20. splat/segtypes/n64/ci.py +2 -0
  21. splat/segtypes/n64/gfx.py +0 -1
  22. splat/segtypes/n64/img.py +4 -0
  23. splat/segtypes/n64/palette.py +21 -2
  24. splat/segtypes/segment.py +7 -8
  25. splat/util/__init__.py +1 -0
  26. splat/util/cache_handler.py +1 -1
  27. splat/util/conf.py +5 -5
  28. splat/util/n64/rominfo.py +1 -1
  29. splat/util/ps2/__init__.py +1 -0
  30. splat/util/ps2/ps2elfinfo.py +249 -0
  31. splat/util/psx/psxexeinfo.py +2 -3
  32. splat/util/relocs.py +3 -3
  33. splat/util/symbols.py +17 -10
  34. splat/util/utils.py +3 -3
  35. {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/METADATA +12 -12
  36. {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/RECORD +39 -37
  37. {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/WHEEL +1 -1
  38. {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/entry_points.txt +0 -0
  39. {splat64-0.36.2.dist-info → splat64-0.36.4.dist-info}/licenses/LICENSE +0 -0
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.36.2"
4
+ __version__ = "0.36.4"
5
5
  __author__ = "ethteck"
6
6
 
7
7
  from . import util as util
@@ -1,4 +1,3 @@
1
1
  from . import disassembler as disassembler
2
2
  from . import spimdisasm_disassembler as spimdisasm_disassembler
3
3
  from . import disassembler_section as disassembler_section
4
- from . import disassembler_section as disassembler_section
@@ -1,5 +1,4 @@
1
1
  from . import disassembler
2
- from ..util.options import SplatOpts
3
2
  from typing import Set
4
3
 
5
4
 
@@ -7,7 +7,7 @@ from typing import Set
7
7
 
8
8
  class SpimdisasmDisassembler(disassembler.Disassembler):
9
9
  # This value should be kept in sync with the version listed on requirements.txt and pyproject.toml
10
- SPIMDISASM_MIN = (1, 36, 1)
10
+ SPIMDISASM_MIN = (1, 38, 0)
11
11
 
12
12
  def configure(self):
13
13
  # Configure spimdisasm
splat/platforms/ps2.py CHANGED
@@ -1,8 +1,6 @@
1
1
  import spimdisasm
2
2
  import rabbitizer
3
3
 
4
- from ..util import compiler, options
5
-
6
4
 
7
5
  def init(target_bytes: bytes):
8
6
  rabbitizer.config.toolchainTweaks_treatJAsUnconditionalBranch = False
@@ -1,15 +1,19 @@
1
1
  #! /usr/bin/env python3
2
2
 
3
3
  import argparse
4
- import sys
4
+ import hashlib
5
5
  from pathlib import Path
6
+ import subprocess
7
+ import sys
8
+ from typing import Optional
6
9
 
7
10
  from ..util.n64 import find_code_length, rominfo
8
11
  from ..util.psx import psxexeinfo
12
+ from ..util.ps2 import ps2elfinfo
9
13
  from ..util import log, file_presets, conf
10
14
 
11
15
 
12
- def main(file_path: Path):
16
+ def main(file_path: Path, objcopy: Optional[str]):
13
17
  if not file_path.exists():
14
18
  sys.exit(f"File {file_path} does not exist ({file_path.absolute()})")
15
19
  if file_path.is_dir():
@@ -27,6 +31,11 @@ def main(file_path: Path):
27
31
  create_psx_config(file_path, file_bytes)
28
32
  return
29
33
 
34
+ # Check for ELFs
35
+ if file_bytes[0:4] == b"\x7fELF":
36
+ do_elf(file_path, file_bytes, objcopy)
37
+ return
38
+
30
39
  log.error(f"create_config does not support the file format of '{file_path}'")
31
40
 
32
41
 
@@ -223,7 +232,7 @@ segments:
223
232
  f"// The address of the end of the stack is 0x{rom.entrypoint_info.stack_top.value:08X}."
224
233
  )
225
234
  reloc_addrs.append(
226
- f"// A common size for this stack is 0x2000, so try checking for the address 0x{rom.entrypoint_info.stack_top.value-0x2000:08X}. Note the stack may have a different size."
235
+ f"// A common size for this stack is 0x2000, so try checking for the address 0x{rom.entrypoint_info.stack_top.value - 0x2000:08X}. Note the stack may have a different size."
227
236
  )
228
237
  reloc_addrs.append(
229
238
  f"// rom:0x{rom.entrypoint_info.stack_top.rom_hi:06X} reloc:MIPS_HI16 symbol:main_stack addend:0xXXXX"
@@ -360,15 +369,208 @@ segments:
360
369
  file_presets.write_all_files()
361
370
 
362
371
 
372
+ def do_elf(elf_path: Path, elf_bytes: bytes, objcopy: Optional[str]):
373
+ elf = ps2elfinfo.Ps2Elf.get_info(elf_path, elf_bytes)
374
+ if elf is None:
375
+ log.error(f"Unsupported elf file '{elf_path}'")
376
+
377
+ basename = elf_path.name.replace(" ", "")
378
+ cleaned_basename = remove_invalid_path_characters(basename)
379
+
380
+ rom_name = Path(f"{cleaned_basename}.rom")
381
+ # Prefer the user objcopy
382
+ if objcopy is None:
383
+ objcopy = find_objcopy()
384
+ objcopy_cmd = run_objcopy(objcopy, str(elf_path), str(rom_name))
385
+
386
+ sha1 = hashlib.sha1(rom_name.read_bytes()).hexdigest()
387
+
388
+ header = f"""\
389
+ # name: Your game name here!
390
+ sha1: {sha1}
391
+ options:
392
+ basename: {basename}
393
+ target_path: {rom_name}
394
+ elf_path: build/{cleaned_basename}.elf
395
+ base_path: .
396
+ platform: ps2
397
+ compiler: {elf.compiler}
398
+ """
399
+
400
+ if elf.gp is not None:
401
+ header += f"""
402
+ gp_value: 0x{elf.gp:08X}
403
+ """
404
+ if elf.ld_gp_expression is not None:
405
+ header += f" ld_gp_expression: {elf.ld_gp_expression}\n"
406
+ else:
407
+ header += " # ld_gp_expression:\n"
408
+
409
+ header += f"""
410
+ # asm_path: asm
411
+ # src_path: src
412
+ # build_path: build
413
+
414
+ ld_script_path: {cleaned_basename}.ld
415
+ ld_dependencies: True
416
+ ld_wildcard_sections: True
417
+ ld_bss_contains_common: True
418
+
419
+ create_asm_dependencies: True
420
+
421
+ find_file_boundaries: False
422
+
423
+ o_as_suffix: True
424
+
425
+ symbol_addrs_path:
426
+ - symbol_addrs.txt
427
+ reloc_addrs_path:
428
+ - reloc_addrs.txt
429
+
430
+ # undefined_funcs_auto_path: undefined_funcs_auto.txt
431
+ # undefined_syms_auto_path: undefined_syms_auto.txt
432
+
433
+ extensions_path: tools/splat_ext
434
+
435
+ string_encoding: ASCII
436
+ data_string_encoding: ASCII
437
+ rodata_string_guesser_level: 2
438
+ data_string_guesser_level: 2
439
+
440
+ named_regs_for_c_funcs: False
441
+ """
442
+
443
+ header += "\n section_order:\n"
444
+ for sect, is_valid in elf.elf_section_names:
445
+ comment = "" if is_valid else "# "
446
+ header += f" {comment}- {sect}\n"
447
+
448
+ header += "\n auto_link_sections:\n"
449
+ for sect, is_valid in elf.elf_section_names:
450
+ comment = "" if is_valid else "# "
451
+ if sect != ".text" and sect != ".vutext":
452
+ header += f" {comment}- {sect}\n"
453
+
454
+ segments = "\nsegments:"
455
+ for seg in elf.segs:
456
+ segments += f"""
457
+ - name: {seg.name}
458
+ type: code
459
+ start: 0x{seg.start:06X}
460
+ vram: 0x{seg.vram:08X}
461
+ bss_size: 0x{seg.bss_size:X}
462
+ subalign: null
463
+ subsegments:
464
+ """
465
+ for section in seg.sections:
466
+ if section.is_nobits:
467
+ segments += f" - {{ type: {section.splat_segment_type}, vram: 0x{section.vram:08X}, name: {seg.name}/{section.vram:08X} }} # {section.name}\n"
468
+ else:
469
+ segments += f" - [0x{section.start:06X}, {section.splat_segment_type}, {seg.name}/{section.start:06X}] # {section.name}\n"
470
+
471
+ segments += f"""\
472
+ - [0x{elf.size:X}]
473
+ """
474
+
475
+ out_file = Path(f"{cleaned_basename}.yaml")
476
+ with out_file.open("w", newline="\n") as f:
477
+ print(f"Writing config to {out_file}")
478
+ f.write(header)
479
+ f.write(segments)
480
+
481
+ # `file_presets` requires an initialized `opts`.
482
+ # A simple way to do that is to simply load the yaml we just generated.
483
+ conf.load([out_file])
484
+ file_presets.write_all_files()
485
+
486
+ # Write symbol_addrs.txt file
487
+ symbol_addrs = []
488
+ symbol_addrs.append(f"_start = 0x{elf.entrypoint:08X}; // type:func")
489
+ if symbol_addrs:
490
+ symbol_addrs.append("")
491
+ with Path("symbol_addrs.txt").open("w", newline="\n") as f:
492
+ print("Writing symbol_addrs.txt")
493
+ f.write(
494
+ "// Visit https://github.com/ethteck/splat/wiki/Adding-Symbols for documentation about this file\n"
495
+ )
496
+ contents = "\n".join(symbol_addrs)
497
+ f.write(contents)
498
+
499
+ # Write other linker script
500
+ linker_script = []
501
+ linker_script.append("ENTRY(_start);")
502
+ if linker_script:
503
+ linker_script.append("")
504
+ with Path("linker_script_extra.ld").open("w", newline="\n") as f:
505
+ print("Writing linker_script_extra.ld")
506
+ f.write(
507
+ "/* Pass this file to the linker with the `-T linker_script_extra.ld` flag */\n"
508
+ )
509
+ contents = "\n".join(linker_script)
510
+ f.write(contents)
511
+
512
+ print()
513
+ print(
514
+ "The generated yaml does not use the actual ELF file as input, but instead it"
515
+ )
516
+ print(
517
+ 'uses a "rom" generated from said ELF, which contains the game code without any'
518
+ )
519
+ print("of the elf metadata.")
520
+ print(
521
+ 'Use the following command to generate this "rom". It is recommended to include'
522
+ )
523
+ print("this command into your setup/configure script.")
524
+ print("```")
525
+ print(" ".join(objcopy_cmd))
526
+ print("```")
527
+
528
+
529
+ def find_objcopy() -> str:
530
+ # First we try to figure out if the user has objcopy on their pc, and under
531
+ # which name.
532
+ # We just try a bunch and hope for the best
533
+ options = [
534
+ "mips-linux-gnu-objcopy",
535
+ "mipsel-linux-gnu-objcopy",
536
+ ]
537
+
538
+ for name in options:
539
+ sub = subprocess.run([name, "--version"], capture_output=True)
540
+ if sub.returncode == 0:
541
+ return name
542
+
543
+ msg = "Unable to find objcopy.\nI tried the following list of names:\n"
544
+ for name in options:
545
+ msg += f" - {name}\n"
546
+ msg += "\nTry to install one of those or use the `--objcopy` flag to pass the name to your own objcopy to me."
547
+ log.error(msg)
548
+
549
+
550
+ def run_objcopy(objcopy_name: str, elf_path: str, rom: str) -> list[str]:
551
+ cmd = [objcopy_name, "-O", "binary", "--gap-fill=0x00", elf_path, rom]
552
+ print("Running:", " ".join(cmd))
553
+ sub = subprocess.run(cmd)
554
+ if sub.returncode != 0:
555
+ log.error("Failed to run objcopy")
556
+ return cmd
557
+
558
+
363
559
  def add_arguments_to_parser(parser: argparse.ArgumentParser):
364
560
  parser.add_argument(
365
561
  "file",
366
- help="Path to a .z64/.n64 ROM or PSX executable",
562
+ help="Path to a .z64/.n64 ROM, PSX executable or PS2 ELF",
563
+ type=Path,
564
+ )
565
+ parser.add_argument(
566
+ "--objcopy",
567
+ help="Path to an user-provided objcopy program. Only used when processing ELF files",
568
+ type=str,
367
569
  )
368
570
 
369
571
 
370
572
  def process_arguments(args: argparse.Namespace):
371
- main(Path(args.file))
573
+ main(args.file, args.objcopy)
372
574
 
373
575
 
374
576
  script_description = "Create a splat config from an N64 ROM or PSX executable."
splat/scripts/split.py CHANGED
@@ -138,12 +138,12 @@ def initialize_segments(config_segments: Union[dict, list]) -> List[Segment]:
138
138
  )
139
139
 
140
140
  # Not user error, hopefully...
141
- assert (
142
- seg.paired_segment is None
143
- ), f"Somehow '{seg.name}' was already paired so something else? It is paired to '{seg.paired_segment.name}' instead of {other_seg.name}"
144
- assert (
145
- other_seg.paired_segment is None
146
- ), f"Somehow '{other_seg.name}' was already paired so something else? It is paired to '{other_seg.paired_segment.name}' instead of {seg.name}"
141
+ assert seg.paired_segment is None, (
142
+ f"Somehow '{seg.name}' was already paired so something else? It is paired to '{seg.paired_segment.name}' instead of {other_seg.name}"
143
+ )
144
+ assert other_seg.paired_segment is None, (
145
+ f"Somehow '{other_seg.name}' was already paired so something else? It is paired to '{other_seg.paired_segment.name}' instead of {seg.name}"
146
+ )
147
147
 
148
148
  found = True
149
149
  # Pair them
@@ -216,9 +216,9 @@ def calc_segment_dependences(
216
216
 
217
217
  for follows_class in vram_class.follows_classes:
218
218
  if follows_class in vram_class_to_segments:
219
- vram_class_to_follows_segments[
220
- vram_class
221
- ] += vram_class_to_segments[follows_class]
219
+ vram_class_to_follows_segments[vram_class] += (
220
+ vram_class_to_segments[follows_class]
221
+ )
222
222
  return vram_class_to_follows_segments
223
223
 
224
224
 
@@ -1,7 +1,5 @@
1
- from pathlib import Path
2
- from typing import Optional, List
1
+ from typing import Optional
3
2
 
4
- from ...util import options
5
3
 
6
4
  from .codesubsegment import CommonSegCodeSubsegment
7
5
 
@@ -3,7 +3,8 @@ from typing import Optional
3
3
 
4
4
  from ...util import log, options
5
5
 
6
- from .segment import CommonSegment, SegmentType
6
+ from .segment import CommonSegment
7
+ from ..segment import SegmentType
7
8
 
8
9
 
9
10
  class CommonSegBin(CommonSegment):
@@ -88,7 +88,7 @@ class CommonSegBss(CommonSegData):
88
88
 
89
89
  for spim_sym in self.spim_section.get_section().symbolList:
90
90
  symbols.create_symbol_from_spim_symbol(
91
- self.get_most_parent(), spim_sym.contextSym
91
+ self.get_most_parent(), spim_sym.contextSym, force_in_segment=True
92
92
  )
93
93
 
94
94
  def should_scan(self) -> bool:
@@ -177,9 +177,9 @@ class CommonSegC(CommonSegCodeSubsegment):
177
177
  if rodata_sibling.is_generated:
178
178
  continue
179
179
 
180
- assert isinstance(
181
- rodata_sibling, CommonSegRodata
182
- ), f"{rodata_sibling}, {rodata_sibling.type}"
180
+ assert isinstance(rodata_sibling, CommonSegRodata), (
181
+ f"{rodata_sibling}, {rodata_sibling.type}"
182
+ )
183
183
 
184
184
  if not rodata_sibling.type.startswith("."):
185
185
  # Emit an error if we try to migrate the rodata symbols to functions if the rodata section is not prefixed with a dot
@@ -237,7 +237,7 @@ class CommonSegC(CommonSegCodeSubsegment):
237
237
  entry.sectionRodata = rodata_section_type
238
238
  if entry.function is not None:
239
239
  if (
240
- entry.function.getName() in self.global_asm_funcs
240
+ entry.function.getNameUnquoted() in self.global_asm_funcs
241
241
  or is_new_c_file
242
242
  or options.opts.disassemble_all
243
243
  ):
@@ -250,7 +250,8 @@ class CommonSegC(CommonSegCodeSubsegment):
250
250
  assert func_sym is not None
251
251
 
252
252
  if (
253
- entry.function.getName() not in self.global_asm_funcs
253
+ entry.function.getNameUnquoted()
254
+ not in self.global_asm_funcs
254
255
  and options.opts.disassemble_all
255
256
  and not is_new_c_file
256
257
  ):
@@ -263,7 +264,8 @@ class CommonSegC(CommonSegCodeSubsegment):
263
264
  else:
264
265
  for spim_rodata_sym in entry.rodataSyms:
265
266
  if (
266
- spim_rodata_sym.getName() in self.global_asm_rodata_syms
267
+ spim_rodata_sym.getNameUnquoted()
268
+ in self.global_asm_rodata_syms
267
269
  or is_new_c_file
268
270
  or options.opts.disassemble_all
269
271
  ):
@@ -281,7 +283,24 @@ class CommonSegC(CommonSegCodeSubsegment):
281
283
  section = self.spim_section.get_section()
282
284
  old_value = section.getGpRelHack()
283
285
  section.setGpRelHack(False)
286
+
287
+ if options.opts.platform == "ps2":
288
+ # Modern gas requires `$` on the special r5900 registers.
289
+ from rabbitizer import TrinaryValue
290
+
291
+ for func in section.symbolList:
292
+ assert isinstance(func, spimdisasm.mips.symbols.SymbolFunction)
293
+ for inst in func.instructions:
294
+ inst.flag_r5900UseDollar = TrinaryValue.TRUE
295
+
284
296
  self.split_as_asmtu_file(self.asm_out_path())
297
+
298
+ if options.opts.platform == "ps2":
299
+ for func in section.symbolList:
300
+ assert isinstance(func, spimdisasm.mips.symbols.SymbolFunction)
301
+ for inst in func.instructions:
302
+ inst.flag_r5900UseDollar = TrinaryValue.FALSE
303
+
285
304
  section.setGpRelHack(old_value)
286
305
 
287
306
  def get_c_preamble(self):
@@ -307,7 +326,7 @@ class CommonSegC(CommonSegCodeSubsegment):
307
326
  "\nA gap was detected in migrated rodata symbols!", status="warn"
308
327
  )
309
328
  log.write(
310
- f"\t In function '{func.getName()}' (0x{func.vram:08X}), gap detected between '{rodata_sym.getName()}' (0x{rodata_sym.vram:08X}) and '{next_rodata_sym.getName()}' (0x{next_rodata_sym.vram:08X})"
329
+ f"\t In function '{func.getNameUnquoted()}' (0x{func.vram:08X}), gap detected between '{rodata_sym.getNameUnquoted()}' (0x{rodata_sym.vram:08X}) and '{next_rodata_sym.getNameUnquoted()}' (0x{next_rodata_sym.vram:08X})"
311
330
  )
312
331
  log.write(
313
332
  f"\t The address of the missing rodata symbol is 0x{rodata_sym.vramEnd:08X}"
@@ -414,7 +433,7 @@ class CommonSegC(CommonSegCodeSubsegment):
414
433
  and spim_sym.instructions[0].isReturn()
415
434
  and spim_sym.instructions[1].isNop()
416
435
  ):
417
- c_lines.append(f"void {spim_sym.getName()}(void) {{")
436
+ c_lines.append(f"void {spim_sym.getNameUnquoted()}(void) {{")
418
437
  c_lines.append("}")
419
438
  else:
420
439
  c_lines.append(
@@ -492,7 +511,7 @@ class CommonSegC(CommonSegCodeSubsegment):
492
511
  depend_list = []
493
512
  for entry in symbols_entries:
494
513
  if entry.function is not None:
495
- func_name = entry.function.getName()
514
+ func_name = entry.function.getNameUnquoted()
496
515
 
497
516
  if func_name in self.global_asm_funcs or is_new_c_file:
498
517
  outpath = asm_out_dir / self.name / (func_name + ".s")
@@ -502,7 +521,7 @@ class CommonSegC(CommonSegCodeSubsegment):
502
521
  f.write(f" \\\n {outpath.as_posix()}")
503
522
  else:
504
523
  for rodata_sym in entry.rodataSyms:
505
- rodata_name = rodata_sym.getName()
524
+ rodata_name = rodata_sym.getNameUnquoted()
506
525
 
507
526
  if rodata_name in self.global_asm_rodata_syms or is_new_c_file:
508
527
  outpath = asm_out_dir / self.name / (rodata_name + ".s")
@@ -1,5 +1,5 @@
1
1
  from collections import OrderedDict
2
- from typing import OrderedDict, List, Optional, Type, Tuple
2
+ from typing import List, Optional, Type, Tuple
3
3
 
4
4
  from ...util import log, options, utils
5
5
 
@@ -119,7 +119,7 @@ class CommonSegCodeSubsegment(Segment):
119
119
  self.parent: CommonSegCode = self.parent
120
120
 
121
121
  symbols.create_symbol_from_spim_symbol(
122
- self.get_most_parent(), func_spim.contextSym
122
+ self.get_most_parent(), func_spim.contextSym, force_in_segment=False
123
123
  )
124
124
 
125
125
  # Gather symbols found by spimdisasm and create those symbols in splat's side
@@ -129,7 +129,7 @@ class CommonSegCodeSubsegment(Segment):
129
129
  )
130
130
  if context_sym is not None:
131
131
  symbols.create_symbol_from_spim_symbol(
132
- self.get_most_parent(), context_sym
132
+ self.get_most_parent(), context_sym, force_in_segment=False
133
133
  )
134
134
 
135
135
  # Main loop
@@ -153,7 +153,7 @@ class CommonSegCodeSubsegment(Segment):
153
153
  context_sym = self.spim_section.get_section().getSymbol(sym_address)
154
154
  if context_sym is not None:
155
155
  symbols.create_symbol_from_spim_symbol(
156
- self.get_most_parent(), context_sym
156
+ self.get_most_parent(), context_sym, force_in_segment=False
157
157
  )
158
158
 
159
159
  def print_file_boundaries(self):
@@ -182,7 +182,7 @@ class CommonSegCodeSubsegment(Segment):
182
182
  print(
183
183
  "File split suggestions for this segment will follow in config yaml format:"
184
184
  )
185
- print(f" - [0x{self.rom_start+in_file_offset:X}, {self.type}]")
185
+ print(f" - [0x{self.rom_start + in_file_offset:X}, {self.type}]")
186
186
 
187
187
  def should_scan(self) -> bool:
188
188
  return (
@@ -130,7 +130,7 @@ class CommonSegData(CommonSegCodeSubsegment, CommonSegGroup):
130
130
 
131
131
  for symbol in self.spim_section.get_section().symbolList:
132
132
  symbols.create_symbol_from_spim_symbol(
133
- self.get_most_parent(), symbol.contextSym
133
+ self.get_most_parent(), symbol.contextSym, force_in_segment=True
134
134
  )
135
135
 
136
136
  # Gather symbols found by spimdisasm and create those symbols in splat's side
@@ -140,7 +140,7 @@ class CommonSegData(CommonSegCodeSubsegment, CommonSegGroup):
140
140
  )
141
141
  if context_sym is not None:
142
142
  symbols.create_symbol_from_spim_symbol(
143
- self.get_most_parent(), context_sym
143
+ self.get_most_parent(), context_sym, force_in_segment=False
144
144
  )
145
145
 
146
146
  # Hint to the user that we are now in the .rodata section and no longer in the .data section (assuming rodata follows data)
@@ -1,4 +1,3 @@
1
- from pathlib import Path
2
1
  from typing import Optional
3
2
 
4
3
  from ...util import log, options
@@ -108,7 +108,7 @@ class CommonSegRodata(CommonSegData):
108
108
 
109
109
  for symbol in self.spim_section.get_section().symbolList:
110
110
  generated_symbol = symbols.create_symbol_from_spim_symbol(
111
- self.get_most_parent(), symbol.contextSym
111
+ self.get_most_parent(), symbol.contextSym, force_in_segment=True
112
112
  )
113
113
  generated_symbol.linker_section = self.get_linker_section_linksection()
114
114
 
@@ -119,7 +119,7 @@ class CommonSegRodata(CommonSegData):
119
119
  )
120
120
  if context_sym is not None:
121
121
  symbols.create_symbol_from_spim_symbol(
122
- self.get_most_parent(), context_sym
122
+ self.get_most_parent(), context_sym, force_in_segment=False
123
123
  )
124
124
 
125
125
  possible_text = self.get_possible_text_subsegment_for_symbol(symbol)
@@ -130,7 +130,7 @@ class CommonSegRodata(CommonSegData):
130
130
  f"\nRodata segment '{self.name}' may belong to the text segment '{text_segment.name}'"
131
131
  )
132
132
  print(
133
- f" Based on the usage from the function {refenceeFunction.getName()} to the symbol {symbol.getName()}"
133
+ f" Based on the usage from the function {refenceeFunction.getNameUnquoted()} to the symbol {symbol.getNameUnquoted()}"
134
134
  )
135
135
  possible_text_segments.add(text_segment)
136
136
 
@@ -1,4 +1,3 @@
1
- from pathlib import Path
2
1
  from typing import Optional
3
2
 
4
3
  from ...util import log, options
@@ -1,4 +1,4 @@
1
- from ...segtypes.segment import Segment, SegmentType
1
+ from ...segtypes.segment import Segment
2
2
 
3
3
 
4
4
  class CommonSegment(Segment):
@@ -162,9 +162,9 @@ class LinkerEntry:
162
162
  linker_writer._write_symbol(path_cname, ".")
163
163
 
164
164
  def emit_path(self, linker_writer: "LinkerWriter"):
165
- assert (
166
- self.object_path is not None
167
- ), f"{self.segment.name}, {self.segment.rom_start}"
165
+ assert self.object_path is not None, (
166
+ f"{self.segment.name}, {self.segment.rom_start}"
167
+ )
168
168
 
169
169
  if self.noload and self.bss_contains_common:
170
170
  linker_writer._write_object_path_section(
@@ -237,9 +237,9 @@ class LinkerWriter:
237
237
  return
238
238
 
239
239
  section_entries: OrderedDict[str, List[LinkerEntry]] = OrderedDict()
240
- for l in segment.section_order:
241
- if l in options.opts.section_order:
242
- section_entries[l] = []
240
+ for section_name in segment.section_order:
241
+ if section_name in options.opts.section_order:
242
+ section_entries[section_name] = []
243
243
 
244
244
  # Add all entries to section_entries
245
245
  prev_entry = None
@@ -301,7 +301,7 @@ class LinkerWriter:
301
301
 
302
302
  # To keep track which sections has been started
303
303
  started_sections: Dict[str, bool] = {
304
- l: False for l in options.opts.section_order
304
+ section_name: False for section_name in options.opts.section_order
305
305
  }
306
306
 
307
307
  # Find where sections are last seen
@@ -389,14 +389,19 @@ class LinkerWriter:
389
389
 
390
390
  self._begin_segment(segment, seg_name, noload=False, is_first=is_first)
391
391
 
392
- for l in segment.section_order:
393
- if l not in options.opts.section_order:
392
+ for section_name in segment.section_order:
393
+ if section_name not in options.opts.section_order:
394
394
  continue
395
- if l == ".bss":
395
+ if section_name == ".bss":
396
396
  continue
397
397
 
398
398
  entry = LinkerEntry(
399
- segment, [], segments_path / f"{seg_name}.o", l, l, noload=False
399
+ segment,
400
+ [],
401
+ segments_path / f"{seg_name}.o",
402
+ section_name,
403
+ section_name,
404
+ noload=False,
400
405
  )
401
406
  self.dependencies_entries.append(entry)
402
407
  entry.emit_entry(self)
@@ -439,9 +444,9 @@ class LinkerWriter:
439
444
  seg_name = segment.get_cname()
440
445
 
441
446
  section_entries: OrderedDict[str, List[LinkerEntry]] = OrderedDict()
442
- for l in segment.section_order:
443
- if l in options.opts.section_order:
444
- section_entries[l] = []
447
+ for section_name in segment.section_order:
448
+ if section_name in options.opts.section_order:
449
+ section_entries[section_name] = []
445
450
 
446
451
  # Add all entries to section_entries
447
452
  prev_entry = None
splat/segtypes/n64/ci.py CHANGED
@@ -48,6 +48,8 @@ class N64SegCi(N64SegImg):
48
48
  return options.opts.asset_path / self.dir / f"{out_name}{type_extension}.png"
49
49
 
50
50
  def split(self, rom_bytes):
51
+ self.check_len()
52
+
51
53
  assert self.palettes is not None
52
54
  if len(self.palettes) == 0:
53
55
  # TODO: output with blank palette
splat/segtypes/n64/gfx.py CHANGED
@@ -36,7 +36,6 @@ from pygfxd import (
36
36
  gfxd_f3dexb,
37
37
  gfxd_f3dex2,
38
38
  )
39
- from ..segment import Segment
40
39
 
41
40
  from ...util import log, options
42
41
  from ...util.log import error