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/__init__.py
CHANGED
splat/disassembler/__init__.py
CHANGED
|
@@ -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,
|
|
10
|
+
SPIMDISASM_MIN = (1, 38, 0)
|
|
11
11
|
|
|
12
12
|
def configure(self):
|
|
13
13
|
# Configure spimdisasm
|
splat/platforms/ps2.py
CHANGED
splat/scripts/create_config.py
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#! /usr/bin/env python3
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
-
import
|
|
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
|
|
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(
|
|
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.
|
|
143
|
-
)
|
|
144
|
-
assert (
|
|
145
|
-
other_seg.
|
|
146
|
-
)
|
|
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
|
-
|
|
221
|
-
|
|
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
|
|
splat/segtypes/common/asm.py
CHANGED
splat/segtypes/common/bin.py
CHANGED
splat/segtypes/common/bss.py
CHANGED
|
@@ -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:
|
splat/segtypes/common/c.py
CHANGED
|
@@ -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,
|
|
182
|
-
)
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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")
|
splat/segtypes/common/code.py
CHANGED
|
@@ -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 (
|
splat/segtypes/common/data.py
CHANGED
|
@@ -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)
|
splat/segtypes/common/databin.py
CHANGED
splat/segtypes/common/rodata.py
CHANGED
|
@@ -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.
|
|
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
|
|
splat/segtypes/common/segment.py
CHANGED
splat/segtypes/linker_entry.py
CHANGED
|
@@ -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.
|
|
167
|
-
)
|
|
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
|
|
241
|
-
if
|
|
242
|
-
section_entries[
|
|
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
|
-
|
|
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
|
|
393
|
-
if
|
|
392
|
+
for section_name in segment.section_order:
|
|
393
|
+
if section_name not in options.opts.section_order:
|
|
394
394
|
continue
|
|
395
|
-
if
|
|
395
|
+
if section_name == ".bss":
|
|
396
396
|
continue
|
|
397
397
|
|
|
398
398
|
entry = LinkerEntry(
|
|
399
|
-
segment,
|
|
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
|
|
443
|
-
if
|
|
444
|
-
section_entries[
|
|
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
|