splat64 0.36.3__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/scripts/create_config.py +206 -4
- splat/segtypes/common/bss.py +1 -1
- splat/segtypes/common/c.py +17 -0
- splat/segtypes/common/codesubsegment.py +3 -3
- splat/segtypes/common/data.py +2 -2
- splat/segtypes/common/rodata.py +2 -2
- splat/segtypes/n64/ci.py +2 -0
- splat/segtypes/n64/img.py +4 -0
- splat/segtypes/n64/palette.py +21 -2
- splat/segtypes/segment.py +1 -2
- splat/util/__init__.py +1 -0
- splat/util/ps2/__init__.py +1 -0
- splat/util/ps2/ps2elfinfo.py +249 -0
- splat/util/symbols.py +7 -2
- {splat64-0.36.3.dist-info → splat64-0.36.4.dist-info}/METADATA +9 -9
- {splat64-0.36.3.dist-info → splat64-0.36.4.dist-info}/RECORD +20 -18
- {splat64-0.36.3.dist-info → splat64-0.36.4.dist-info}/WHEEL +1 -1
- {splat64-0.36.3.dist-info → splat64-0.36.4.dist-info}/entry_points.txt +0 -0
- {splat64-0.36.3.dist-info → splat64-0.36.4.dist-info}/licenses/LICENSE +0 -0
splat/__init__.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
|
|
|
@@ -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/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
|
@@ -283,7 +283,24 @@ class CommonSegC(CommonSegCodeSubsegment):
|
|
|
283
283
|
section = self.spim_section.get_section()
|
|
284
284
|
old_value = section.getGpRelHack()
|
|
285
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
|
+
|
|
286
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
|
+
|
|
287
304
|
section.setGpRelHack(old_value)
|
|
288
305
|
|
|
289
306
|
def get_c_preamble(self):
|
|
@@ -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):
|
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/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)
|
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/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
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
|
|
@@ -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/symbols.py
CHANGED
|
@@ -600,8 +600,13 @@ def add_symbol_to_spim_section(
|
|
|
600
600
|
return context_sym
|
|
601
601
|
|
|
602
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.
|
|
603
605
|
def create_symbol_from_spim_symbol(
|
|
604
|
-
segment: "Segment",
|
|
606
|
+
segment: "Segment",
|
|
607
|
+
context_sym: spimdisasm.common.ContextSymbol,
|
|
608
|
+
*,
|
|
609
|
+
force_in_segment: bool,
|
|
605
610
|
) -> "Symbol":
|
|
606
611
|
in_segment = False
|
|
607
612
|
|
|
@@ -631,7 +636,7 @@ def create_symbol_from_spim_symbol(
|
|
|
631
636
|
in_segment = segment.contains_vram(context_sym.vram)
|
|
632
637
|
|
|
633
638
|
sym = segment.create_symbol(
|
|
634
|
-
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
|
|
635
640
|
)
|
|
636
641
|
|
|
637
642
|
if sym.given_name is None and context_sym.name is not None:
|
|
@@ -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,16 +30,16 @@ 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
39
|
Requires-Dist: crunch64<1.0.0,>=0.5.1; extra == 'dev'
|
|
40
40
|
Requires-Dist: mypy; extra == 'dev'
|
|
41
41
|
Requires-Dist: n64img>=0.3.3; extra == 'dev'
|
|
42
|
-
Requires-Dist: pygfxd; extra == 'dev'
|
|
42
|
+
Requires-Dist: pygfxd>=1.0.5; extra == 'dev'
|
|
43
43
|
Requires-Dist: rabbitizer<2.0.0,>=1.12.0; extra == 'dev'
|
|
44
44
|
Requires-Dist: ruff; extra == 'dev'
|
|
45
45
|
Requires-Dist: spimdisasm<2.0.0,>=1.38.0; extra == 'dev'
|
|
@@ -48,7 +48,7 @@ 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
53
|
Requires-Dist: spimdisasm<2.0.0,>=1.38.0; extra == 'mips'
|
|
54
54
|
Description-Content-Type: text/markdown
|
|
@@ -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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
splat/__init__.py,sha256=
|
|
1
|
+
splat/__init__.py,sha256=K7V_4INbs5mtgBs_XECCUNEhSPHURCOviws3SKjutbc,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,21 +14,21 @@ 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=
|
|
17
|
+
splat/scripts/create_config.py,sha256=Xt0BgS9ql5GfLrNKWQRMuKWea0sXUygRV1_wCYGrrPE,17949
|
|
18
18
|
splat/scripts/split.py,sha256=wWib2lDdedzjcNLH-Ay48gM2XGZ9JCUsrriamuHzkOU,21935
|
|
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
|
-
splat/segtypes/segment.py,sha256=
|
|
21
|
+
splat/segtypes/segment.py,sha256=IcmF7FxDrJ1ZP5AKFO-skNrGfYhGIYwawyepKzeTF3A,30619
|
|
22
22
|
splat/segtypes/common/__init__.py,sha256=mnq0acScilSCCo6q2PvkDk0Or3V8qitA7I8QMVw8haI,631
|
|
23
23
|
splat/segtypes/common/asm.py,sha256=gpR4uZ57l4Q9G0XTOeO1JIk9P5d8xXBxRfkrPIN-M1A,644
|
|
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
|
-
splat/segtypes/common/bss.py,sha256=
|
|
27
|
-
splat/segtypes/common/c.py,sha256=
|
|
26
|
+
splat/segtypes/common/bss.py,sha256=cX0RO7LRcBRO6t463AUSSV8sGdlxZTcd5_McZjjqfFo,3107
|
|
27
|
+
splat/segtypes/common/c.py,sha256=RkevmqKXK9RKQ_yPClFFur1Uq4jGGdVtL1BnScbHhok,21327
|
|
28
28
|
splat/segtypes/common/code.py,sha256=FfBuy6x8fQXoJYX2Ul80uOs-CzlVslUaveD-hBmvKXM,10693
|
|
29
|
-
splat/segtypes/common/codesubsegment.py,sha256=
|
|
29
|
+
splat/segtypes/common/codesubsegment.py,sha256=XpPzNwGSNL4HE7Qgi7-tSVXlYtfdXug9PrzQIJu3yDI,10720
|
|
30
30
|
splat/segtypes/common/cpp.py,sha256=p-duowCt4czrmaWgj1LuFw5LGfdyB_uaeLTKVmH80Cg,180
|
|
31
|
-
splat/segtypes/common/data.py,sha256=
|
|
31
|
+
splat/segtypes/common/data.py,sha256=l3CCa95vHH8XwiksamaxuZuSF_rOGqcozGlZj0bt9Yw,5712
|
|
32
32
|
splat/segtypes/common/databin.py,sha256=nDI1ciAv0QLt2xDYjEtGJHSoONZzJZKyST-BuSKML2Q,1169
|
|
33
33
|
splat/segtypes/common/eh_frame.py,sha256=MzL373ej2E38leM47py3T5beLq2IolsYszLZM8PYS2Y,913
|
|
34
34
|
splat/segtypes/common/gcc_except_table.py,sha256=-ZrQL5dCcRu7EPFFPgL8lIJwWL5FsEeeQoSUVfTkyIg,2112
|
|
@@ -39,14 +39,14 @@ splat/segtypes/common/lib.py,sha256=WaJH0DH0B8iJvhYQWYBjThbNeEXz3Yh7E8ZyONe5JtQ,
|
|
|
39
39
|
splat/segtypes/common/linker_offset.py,sha256=IaEXMXq62BS2g95I4lS0wax5Ehjfi3O3C-uSDUbMjvI,767
|
|
40
40
|
splat/segtypes/common/pad.py,sha256=-7SaRgXrmGeOINKVV9v1c3e9jn8aVQknD8EVaEPcPrw,758
|
|
41
41
|
splat/segtypes/common/rdata.py,sha256=ab2MFZVjlnD2p5nDVNroo3W9blaVJND2jN2PJPy6a_Y,142
|
|
42
|
-
splat/segtypes/common/rodata.py,sha256=
|
|
42
|
+
splat/segtypes/common/rodata.py,sha256=ZUqXPw119EewNr9_pUL9QeoVML5XwPTo1THz9OjB86k,6238
|
|
43
43
|
splat/segtypes/common/rodatabin.py,sha256=RsvhGxY9iX1QHSyMfuWLaFrqdBvDEtL4j13-jPpvyvk,1174
|
|
44
44
|
splat/segtypes/common/sbss.py,sha256=blIEuVYie4qNEOYMmlSdltn5f4MvgJu3AV8vqVD8Nh4,131
|
|
45
45
|
splat/segtypes/common/sdata.py,sha256=dnLzNSNtSyclLZiNUmFTv0e0BWN8glxB1km1MSRq6xY,136
|
|
46
46
|
splat/segtypes/common/segment.py,sha256=gFPzEi0Jq0NGtL4xlblIlNZus7dHkxebLKkr6jhOkxs,81
|
|
47
47
|
splat/segtypes/common/textbin.py,sha256=PXEb1JQB2NEqVDLg5KW2vI0NLSqrROJvCOifxB0NUms,5994
|
|
48
48
|
splat/segtypes/n64/__init__.py,sha256=tf2yWlijeKmOp1OhEEL-aW88mU-mWQRC2lSjQ5Ww1eI,569
|
|
49
|
-
splat/segtypes/n64/ci.py,sha256=
|
|
49
|
+
splat/segtypes/n64/ci.py,sha256=i2axvnNtteezMg18gLcgggaI14Ez0I5DuGuNah9z4DU,2426
|
|
50
50
|
splat/segtypes/n64/ci4.py,sha256=ZP8e12CpV8U7r8oLnb9uHc-MkmBbxjXBbJxROFvOjiM,184
|
|
51
51
|
splat/segtypes/n64/ci8.py,sha256=tS4USmp0LuJnP-UlgaOYoxvYxBKgE67wYCz-KeRLlFk,184
|
|
52
52
|
splat/segtypes/n64/decompressor.py,sha256=YuKo-PS_S2-Ow1Zw8QTO70NWlSYTY79JZN0kUQB7T0U,1779
|
|
@@ -58,10 +58,10 @@ splat/segtypes/n64/i8.py,sha256=5zqYmpmuXprEzwidVQTknSKqMuu0C1Izk4t5ILoTPf4,204
|
|
|
58
58
|
splat/segtypes/n64/ia16.py,sha256=y1uLVuh72HTmyAEK90R67vftp-X4LDgRxgJyU8EAnH0,208
|
|
59
59
|
splat/segtypes/n64/ia4.py,sha256=YdBtqx1LUHFXCPOUJUqutSHnD3YTIov6JKSZjXNKuVY,206
|
|
60
60
|
splat/segtypes/n64/ia8.py,sha256=TxTqD-4mx7eF81IxThkPn54aJkEtGALGvGudmejBM0w,206
|
|
61
|
-
splat/segtypes/n64/img.py,sha256=
|
|
61
|
+
splat/segtypes/n64/img.py,sha256=xbpgJlg8tiok4CaFvkWTXRcxvNZGK_i3GAnN5jtY8kA,3283
|
|
62
62
|
splat/segtypes/n64/ipl3.py,sha256=HKPXIaKr6XAQGwm3YKQedmijGqXNebzIS9LjiPxl2Q0,200
|
|
63
63
|
splat/segtypes/n64/mio0.py,sha256=jYTq0Iz1NkWSFLDnFPdLYu3HG2adyRZEVlzevFNdn0U,379
|
|
64
|
-
splat/segtypes/n64/palette.py,sha256=
|
|
64
|
+
splat/segtypes/n64/palette.py,sha256=y--D6EfBPvzahfcLA_ahFR9MHslcPpu2JLNBuNpK-B4,4264
|
|
65
65
|
splat/segtypes/n64/rgba16.py,sha256=ddWJARv5SKFFuCUIFeI-MQFqa52fa3d0srZMVbCXOSw,212
|
|
66
66
|
splat/segtypes/n64/rgba32.py,sha256=LbYDZ4b95axIYKc06HDyAYMUm-cTJLOsRCKGlE_Hlmw,227
|
|
67
67
|
splat/segtypes/n64/rsp.py,sha256=8t9ZnhuF2KrxG1FTRC7R0zgciu-4C9SMcFcFzBP4rfI,236
|
|
@@ -75,7 +75,7 @@ splat/segtypes/ps2/vtables.py,sha256=c8weQcbQ0-KkjkLHUXViTWkEaJ6LNR8jBUMV-0t0m4o
|
|
|
75
75
|
splat/segtypes/psp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
76
|
splat/segtypes/psx/__init__.py,sha256=tn0oDYUPtF0PfC8I4G23nPvH_JAcuLGxOCvYN1SE3Dc,31
|
|
77
77
|
splat/segtypes/psx/header.py,sha256=2S8XG_6zfLGzmEA0XpFqs0-4nf52YD9Erk6SbMUlVzo,2639
|
|
78
|
-
splat/util/__init__.py,sha256=
|
|
78
|
+
splat/util/__init__.py,sha256=vejj8R_nldFOlIOEoG492Lycg1zUCTQewoPF9c3P6fw,538
|
|
79
79
|
splat/util/cache_handler.py,sha256=BrTWo8U4bj8TBcZfIRwiAYmogc3YUlBz9P-34Y0aIrg,1851
|
|
80
80
|
splat/util/color.py,sha256=FSmy0dAQJ9FzRBc99Yt4kBEyB62MC_YiVkqoWgPMsRU,371
|
|
81
81
|
splat/util/compiler.py,sha256=uXShMm49380ENecSFlsi75LWI45yakWkExZX8NT5pOU,1778
|
|
@@ -87,16 +87,18 @@ 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
|
|
89
89
|
splat/util/statistics.py,sha256=8C88vLW8q4Xd4i1Crz8X24NLoraLyKd0lw3ebbeonSo,1906
|
|
90
|
-
splat/util/symbols.py,sha256=
|
|
90
|
+
splat/util/symbols.py,sha256=wqznzi0-ZQ0pID1qWLShSPHL3eD7M5jjMwmO4LyWxIc,31519
|
|
91
91
|
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
95
|
splat/util/n64/rominfo.py,sha256=Ms9P-MuuOSyEF4QNB3wBuC-U9OdrfByqR0VPAHqTBQI,16976
|
|
96
|
+
splat/util/ps2/__init__.py,sha256=hAU12HtQLOc_sxc8_rzxOERBT5wjg3DhQ8XRN9hBXfA,39
|
|
97
|
+
splat/util/ps2/ps2elfinfo.py,sha256=v6nTPabzXpG1N-MhhkbVeLoGjgsjBEpHL1DoPv26-Hs,7982
|
|
96
98
|
splat/util/psx/__init__.py,sha256=kCCaR-KB1mNlIcXB4OuuSQ2zVLbWg_SnIZIUeyjeBBI,39
|
|
97
99
|
splat/util/psx/psxexeinfo.py,sha256=Oxd5Lt8HTNHuUzw3cVujdyJRqEG-yo0XT78wEISUXms,5705
|
|
98
|
-
splat64-0.36.
|
|
99
|
-
splat64-0.36.
|
|
100
|
-
splat64-0.36.
|
|
101
|
-
splat64-0.36.
|
|
102
|
-
splat64-0.36.
|
|
100
|
+
splat64-0.36.4.dist-info/METADATA,sha256=sQtM6Sc89JJYxes3KqZRDfIKyDRLqzFuXUbiD82IXR4,3879
|
|
101
|
+
splat64-0.36.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
102
|
+
splat64-0.36.4.dist-info/entry_points.txt,sha256=O7Xy-qNOHcI87-OQrWJ-OhRDws74SuwVb_4rtnp0eLo,52
|
|
103
|
+
splat64-0.36.4.dist-info/licenses/LICENSE,sha256=97VMVzjG8yQvsf8NG2M9IFSbh7R8cifJnc6QK1cZqj8,1070
|
|
104
|
+
splat64-0.36.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|