More image splits (#272)

* A number of new image splits

* Address comments.

* git subrepo pull --force --branch=imgflip tools/splat

subrepo:
  subdir:   "tools/splat"
  merged:   "9caaa45df9"
upstream:
  origin:   "https://github.com/ethteck/splat.git"
  branch:   "imgflip"
  commit:   "9caaa45df9"
git-subrepo:
  version:  "0.4.3"
  origin:   "???"
  commit:   "???"

* use flip_y over flip

* git subrepo pull --force --branch=imgflip tools/splat

subrepo:
  subdir:   "tools/splat"
  merged:   "ef663ec0d5"
upstream:
  origin:   "https://github.com/ethteck/splat.git"
  branch:   "imgflip"
  commit:   "ef663ec0d5"
git-subrepo:
  version:  "0.4.3"
  origin:   "???"
  commit:   "???"

* use flip_y

* git subrepo pull --force --branch=imgflip tools/splat

subrepo:
  subdir:   "tools/splat"
  merged:   "3144dc17f6"
upstream:
  origin:   "https://github.com/ethteck/splat.git"
  branch:   "imgflip"
  commit:   "3144dc17f6"
git-subrepo:
  version:  "0.4.3"
  origin:   "???"
  commit:   "???"

Co-authored-by: JoshDuMan <Joshua.Shoup.1996@gmail.com>
This commit is contained in:
alex 2021-04-21 17:01:00 +01:00 committed by GitHub
parent a3e4140101
commit 9900e9a2b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 245 additions and 84 deletions

View File

@ -50,8 +50,14 @@ class Converter():
self.mode = mode
self.infile = infile
self.outfile = outfile
self.flip_x = "--flip-x" in argv
self.flip_y = "--flip-y" in argv
if self.flip_y:
print(self.infile)
assert self.flip_x == False, "flip_x is not supported"
self.warned = False
def warn(self, msg):
@ -250,7 +256,7 @@ class Converter():
if __name__ == "__main__":
if len(argv) < 4:
print("usage: build.py MODE INFILE OUTFILE [--flip-y]")
print("usage: build.py MODE INFILE OUTFILE [--flip-x] [--flip-y]")
exit(1)
Converter(*argv[1:]).convert()

View File

@ -5,8 +5,8 @@
;
[subrepo]
remote = https://github.com/ethteck/splat.git
branch = master
commit = 5e36c4555865e8192653217ca75afe0da661651a
parent = e7de26b831ba89b66285a4847c8c28e562f19ca2
branch = imgflip
commit = 3144dc17f6fd77211e016cfbbc6a705f59b838e0
parent = 70e14bea15b1374095da723425d9b59a2ffb5915
method = merge
cmdver = 0.4.3

View File

@ -1,6 +1,14 @@
# splat Release Notes
## 0.7: The Path Update
## 0.7.1
* Image segment changes:
* Added `flip_x` and `flip_y` boolean parameters to replace `flip`.
* `flip` is deprecated and will produce a warning when used.
* Fixed flipping of `ci4` and `ci8` images.
* Fixed `extract: false` (and `start: auto`) behaviour.
## 0.7.0: The Path Update
* Significantly better performance, especially when using the cache feature (`--use-cache` CLI arg).
* BREAKING: Some cli args for splat have been renamed. Please consult the usage output (-h or no args) for more information.

View File

@ -35,11 +35,8 @@ class LinkerEntry:
def __init__(self, segment: Segment, src_paths: List[Path], object_path: Path, section: str):
self.segment = segment
self.src_paths = [clean_up_path(p) for p in src_paths]
self.object_path = path_to_object_path(object_path)
self.section = section
if self.section == "linker":
self.object_path = None
else:
self.object_path = path_to_object_path(object_path)
class LinkerWriter():
def __init__(self):
@ -65,7 +62,6 @@ class LinkerWriter():
if entry.section == "linker": # TODO: isinstance is preferable
self._end_block()
self._begin_segment(entry.segment)
continue
start = entry.segment.rom_start
if isinstance(start, int):

View File

@ -27,7 +27,7 @@ class N64SegAsm(N64SegCodeSubsegment):
def scan(self, rom_bytes: bytes):
if self.rom_start != "auto" and self.rom_end != "auto" and self.rom_start != self.rom_end:
self.funcs_text = self.disassemble_code(rom_bytes, options.get("asm_endlabels", False))
self.funcs_text = self.disassemble_code(rom_bytes)
def split(self, rom_bytes: bytes):
if not self.rom_start == self.rom_end:

View File

@ -10,7 +10,7 @@ from util import options
class N64SegC(N64SegCodeSubsegment):
defined_funcs: Set[str] = set()
STRIP_C_COMMENTS_RE = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
@ -135,7 +135,7 @@ class N64SegC(N64SegCodeSubsegment):
c_lines = self.get_c_preamble()
for func in funcs_text:
func_name = self.parent.get_symbol(func, type="func", local_only=True).name
func_name = self.get_symbol(func, type="func", local_only=True).name
if options.get_compiler() == "GCC":
c_lines.append("INCLUDE_ASM(s32, \"{}\", {});".format(self.name, func_name))
else:

View File

@ -1,11 +1,12 @@
from segtypes.n64.ci8 import N64SegCi8
from util import iter
class N64SegCi4(N64SegCi8):
@staticmethod
def parse_image(data, width, height, flip_h=False, flip_v=False):
img_data = bytearray()
for i in range(width * height // 2):
for x, y, i in iter.iter_image_indexes(width, height, 0.5, 1, flip_h, flip_v):
img_data.append(data[i] >> 4)
img_data.append(data[i] & 0xF)

View File

@ -3,6 +3,7 @@ from segtypes.n64.rgba16 import N64SegRgba16
import png
from util import log
from util import options
from util import iter
if TYPE_CHECKING:
from segtypes.n64.palette import N64SegPalette as Palette
@ -35,7 +36,19 @@ class N64SegCi8(N64SegRgba16):
@staticmethod
def parse_image(data, width, height, flip_h=False, flip_v=False):
return data
# hot path
if not flip_h and not flip_v:
return data
flipped_data = bytearray()
for x, y, i in iter.iter_image_indexes(width, height, 1, 1, flip_h, flip_v):
flipped_data.append(data[i])
return flipped_data
def max_length(self):
return self.width * self.height
def cache(self):
return (self.config, self.rom_end, 1)

View File

@ -39,7 +39,7 @@ class N64SegCodeSubsegment(Segment):
def is_branch_insn(mnemonic):
return (mnemonic.startswith("b") and not mnemonic.startswith("binsl") and not mnemonic == "break") or mnemonic == "j"
def disassemble_code(self, rom_bytes, addsuffix=False):
def disassemble_code(self, rom_bytes):
insns = [insn for insn in N64SegCodeSubsegment.md.disasm(rom_bytes[self.rom_start : self.rom_end], self.vram_start)]
funcs = self.process_insns(insns, self.rom_start)
@ -50,7 +50,7 @@ class N64SegCodeSubsegment(Segment):
funcs = self.determine_symbols(funcs)
self.gather_jumptable_labels(rom_bytes)
return self.add_labels(funcs, addsuffix)
return self.add_labels(funcs)
def process_insns(self, insns, rom_addr):
assert(isinstance(self.parent, N64SegCode))
@ -243,6 +243,7 @@ class N64SegCodeSubsegment(Segment):
if offset != 0:
offset_str = f"+0x{offset:X}"
if self.parent:
self.parent.check_rodata_sym(func_addr, sym)
@ -256,7 +257,7 @@ class N64SegCodeSubsegment(Segment):
ret[func_addr] = func
return ret
def add_labels(self, funcs, addsuffix):
def add_labels(self, funcs):
ret = {}
for func in funcs:
@ -310,9 +311,6 @@ class N64SegCodeSubsegment(Segment):
if insn[0].mnemonic != "branch" and insn[0].mnemonic.startswith("b") or insn[0].mnemonic.startswith("j"):
indent_next = True
if addsuffix:
func_text.append(f"endlabel {sym.name}")
ret[func] = (func_text, rom_addr)
@ -360,7 +358,7 @@ class N64SegCodeSubsegment(Segment):
rom_offset += 4
def should_scan(self) -> bool:
return options.mode_active("code")
return self.should_split()
def should_split(self) -> bool:
return self.extract and options.mode_active("code")

View File

@ -33,6 +33,13 @@ class N64SegData(N64SegCodeSubsegment):
def get_linker_section(self) -> str:
return ".data"
def get_linker_entries(self):
from segtypes.linker_entry import LinkerEntry
path = self.out_path()
return [LinkerEntry(self, [path], path, self.get_linker_section())]
def get_symbols(self):
ret = []
@ -141,7 +148,7 @@ class N64SegData(N64SegCodeSubsegment):
return sym_str
def disassemble_data(self, rom_bytes):
rodata_encountered = "rodata" in self.type
rodata_encountered = self.type == "rodata"
ret = ".include \"macro.inc\"\n\n"
ret += f'.section {self.get_linker_section()}'
@ -154,6 +161,9 @@ class N64SegData(N64SegCodeSubsegment):
mnemonic = syms[i].access_mnemonic
sym = self.parent.get_symbol(syms[i].vram_start, create=True, define=True, local_only=True)
if sym.vram_start == 0x80097D90:
dog = 5
sym_str = f"\n\nglabel {sym.name}\n"
dis_start = self.parent.ram_to_rom(syms[i].vram_start)
dis_end = self.parent.ram_to_rom(syms[i + 1].vram_start)

View File

@ -57,6 +57,7 @@ class N64SegGroup(N64Segment):
segment.parent = self
if segment.rom_start != "auto":
assert isinstance(segment.rom_start, int)
segment.vram_start = self.rom_to_ram(segment.rom_start)
# TODO: assumes section order - generalize and stuff

View File

@ -2,10 +2,47 @@ from pathlib import Path
from typing import Optional
from segtypes.n64.segment import N64Segment
from util import options
from util import log
class N64SegImg(N64Segment):
def __init__(self, segment, rom_start, rom_end):
super().__init__(segment, rom_start, rom_end)
if type(segment) is dict:
if self.extract:
self.width = segment["width"]
self.height = segment["height"]
self.flip_horizontal = bool(segment.get("flip_x", False))
self.flip_vertical = bool(segment.get("flip_y", False))
if segment.get("flip"):
self.warn(f"'flip' parameter for img segments is deprecated; use flip_x and flip_y instead")
flip = segment.get("flip")
self.flip_vertical = flip == "both" or flip.startswith("v") or flip == "y"
self.flip_horizontal = flip == "both" or flip.startswith("h") or flip == "x"
else:
if self.extract:
if len(segment) < 5:
log.error(f"Error: {self.name} is missing width and height parameters")
self.width = segment[3]
self.height = segment[4]
self.flip_horizontal = False
self.flip_vertical = False
if self.extract and self.max_length() is not None:
expected_len = int(self.max_length())
actual_len = self.rom_end - self.rom_start
if actual_len > expected_len and actual_len - expected_len > self.subalign:
log.error(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)")
def out_path(self) -> Optional[Path]:
return options.get_asset_path() / self.dir / f"{self.name}.png"
def should_split(self) -> bool:
return self.extract and options.mode_active("img")
def max_length(self) -> Optional[int]:
return None

View File

@ -27,18 +27,19 @@ class N64SegPalette(N64Segment):
self.name.split(".")[0]
) if type(segment) is dict else self.name.split(".")[0]
if self.rom_end == "auto":
log.error(f"segment {self.name} needs to know where it ends; add a position marker [0xDEADBEEF] after it")
if self.extract:
if self.rom_end == "auto":
log.error(f"segment {self.name} needs to know where it ends; add a position marker [0xDEADBEEF] after it")
if self.max_length() and isinstance(self.rom_end, int):
expected_len = int(self.max_length())
actual_len = self.rom_end - self.rom_start
if actual_len > expected_len and actual_len - expected_len > self.subalign:
log.error(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)")
if self.max_length() and isinstance(self.rom_end, int):
expected_len = int(self.max_length())
actual_len = self.rom_end - self.rom_start
if actual_len > expected_len and actual_len - expected_len > self.subalign:
log.error(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)")
def should_split(self):
return super().should_split() or options.mode_active("img")
return self.extract and (super().should_split() or options.mode_active("img"))
def out_path(self) -> Optional[Path]:
return options.get_asset_path() / self.dir / f"{self.name}.png"

View File

@ -7,37 +7,6 @@ from util.color import unpack_color
# TODO: move common behaviour to N64ImgSegment and have all image segments extend that instead
class N64SegRgba16(N64SegImg):
def __init__(self, segment, rom_start, rom_end):
super().__init__(segment, rom_start, rom_end)
if type(segment) is dict:
self.width = segment["width"]
self.height = segment["height"]
self.flip = segment.get("flip", "noflip")
elif len(segment) < 5:
log.error("missing parameters")
else:
self.width = segment[3]
self.height = segment[4]
self.flip = "noflip"
if self.max_length():
expected_len = int(self.max_length())
actual_len = self.rom_end - self.rom_start
if actual_len > expected_len and actual_len - expected_len > self.subalign:
log.error(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)")
@property
def flip_vertical(self):
return self.flip == "both" or self.flip.startswith("v") or self.flip == "y"
@property
def flip_horizontal(self):
return self.flip == "both" or self.flip.startswith("h") or self.flip == "x"
def should_split(self):
return super().should_split() or options.mode_active("img")
def split(self, rom_bytes):
path = self.out_path()
path.parent.mkdir(parents=True, exist_ok=True)

View File

@ -6,12 +6,12 @@ def iter_in_groups(iterable, n, fillvalue=None):
return zip_longest(*args, fillvalue=fillvalue)
def iter_image_indexes(width, height, bytes_per_x=1, bytes_per_y=1, flip_h=False, flip_v=False):
w = int(width * bytes_per_x)
h = int(height * bytes_per_y)
w = int(width * bytes_per_x)
h = int(height * bytes_per_y)
xrange = range(w - ceil(bytes_per_x), -1, -ceil(bytes_per_x)) if flip_h else range(0, w, ceil(bytes_per_x))
yrange = range(h - ceil(bytes_per_y), -1, -ceil(bytes_per_y)) if flip_v else range(0, h, ceil(bytes_per_y))
xrange = range(w - ceil(bytes_per_x), -1, -ceil(bytes_per_x)) if flip_h else range(0, w, ceil(bytes_per_x))
yrange = range(h - ceil(bytes_per_y), -1, -ceil(bytes_per_y)) if flip_v else range(0, h, ceil(bytes_per_y))
for y in yrange:
for x in xrange:
yield x, y, (y * w) + x
for y in yrange:
for x in xrange:
yield x, y, (y * w) + x

View File

@ -1,12 +1,12 @@
import sys
from colorama import init, Fore, Style
from typing import Union
from typing import Optional
init(autoreset=True)
newline = True
Status = Union[None, str]
Status = Optional[str]
def write(*args, status=None, **kwargs):
global newline

View File

@ -22,12 +22,12 @@ def initialize(config: Dict, config_path: str, base_path=None, target_path=None)
def set(opt, val):
opts[opt] = val
def get(opt, default=None):
return opts.get(opt, default)
def get_platform() -> str:
return opts.get("platform", "n64")
return opts.get("platform", "N64")
def get_compiler() -> str:
return opts.get("compiler", "IDO")

View File

@ -2172,14 +2172,12 @@ segments:
subsegments:
- [0x38F900, c]
- [0x390340, bin]
- [0x3903D0, bin]
- start: 0x390810
type: ia4
name: battle/text_action_command_ratings
width: 64
height: 125
flip: vertical
- [0x3917B0, bin]
- [0x3903D0, ia4, battle/lucky, 64, 32]
- [0x3907D0, ia4, battle/miss, 64, 32]
- [0x390BD0, ia4, battle/good, 64, 32]
- [0x390FD0, ia4, battle/nice, 64, 32]
- [0x3913D0, ia4, battle/super, 64, 32]
- [0x3917D0, bin]
- type: code
start: 0x391D30
vram: 0xE0092000
@ -2471,20 +2469,114 @@ segments:
subsegments:
- [0x3EB4E0, c]
- [0x3EBC30, bin]
- [0x3ED4E0, ia8, world/text_chapter, 128, 38]
- [0x3EE7E0, bin]
- [0x3EBE60, ia8, world/ch0, 144, 40]
- [0x3ED4E0, ia8, world/text_chapter, 128, 40]
- [0x3EE8E0, ia8, world/text_end_of, 104, 40]
- [0x3EF920, ia8, world/exclamation_point, 16, 40]
- [0x3EFBA0, ia8, world/ch1, 64, 64]
- [0x3F0BA0, ia8, world/ch2, 64, 64]
- [0x3F1BA0, ia8, world/ch3, 64, 64]
- [0x3F2BA0, ia8, world/ch4, 64, 64]
- [0x3F3BA0, ia8, world/ch5, 64, 64]
- [0x3F4BA0, ia8, world/ch6, 64, 64]
- [0x3F5BA0, ia8, world/ch7, 64, 64]
- [0x3F6BA0, ia8, world/ch8, 64, 64]
- [0x3F7BA0, rgba16, world/chapter_rainbow, 8, 16]
- [0x3F7CA0, bin]
- type: code
start: 0x3F83F0
vram: 0xE0110000
subsegments:
- [0x3F83F0, c]
- [0x3F8CA0, bin]
- [0x3F8CC0, i4, ice_smash, 64, 128]
- [0x3F9CC0, bin]
- type: code
start: 0x3F9E50
vram: 0xE0112000
subsegments:
- [0x3F9E50, c]
- [0x3FA480, bin]
- start: 0x3FA4B0
type: ci4
name: star_cards/card_front
flip_y: true
width: 32
height: 32
- [0x3FA6B0, palette, star_cards/card_front]
- [0x3FA6D0, bin] # PAD
- start: 0x3FA8B0
type: ci4
name: star_cards/card_back
flip_y: true
width: 32
height: 64
- [0x3FACB0, palette, star_cards/card_back]
- [0x3FACD0, bin] # PAD
- start: 0x3FAEB0
type: ci4
name: star_cards/wave
flip_y: true
width: 32
height: 32
- [0x3FB0B0, palette, star_cards/wave]
- [0x3FB0D0, bin] # PAD
- start: 0x3FB2B0
type: ci4
name: star_cards/squares
flip_y: true
width: 16
height: 16
- [0x3FB330, palette, star_cards/squares]
- [0x3FB350, bin] # PAD
- start: 0x3FB530
type: ci4
name: star_cards/eldstar
flip_y: true
width: 48
height: 48
- [0x3FB9B0, palette, star_cards/eldstar]
- [0x3FB9D0, bin] # PAD
- start: 0x3FBBB0
type: ci4
name: star_cards/mamar
flip_y: true
width: 48
height: 48
- [0x3FC030, palette, star_cards/mamar]
- [0x3FC050, bin] # PAD
- start: 0x3FC230
type: ci4
name: star_cards/skolar
flip_y: true
width: 48
height: 48
- [0x3FC6B0, palette, star_cards/skolar]
- [0x3FC6D0, bin] # PAD
- start: 0x3FC8B0
type: ci4
name: star_cards/muskular
flip_y: true
width: 48
height: 48
- [0x3FCD30, palette, star_cards/muskular]
- [0x3FCD50, bin] # PAD
- start: 0x3FCF30
type: ci4
name: star_cards/misstar
flip_y: true
width: 48
height: 48
- [0x3FD3B0, palette, star_cards/misstar]
- [0x3FD3D0, bin] # PAD
- start: 0x3FDC30
type: ci4
name: star_cards/klevar
flip_y: true
width: 48
height: 48
- [0x3FE0B0, palette, star_cards/klevar]
- [0x3FE0D0, bin]
- type: code
start: 0x3FEAE0
vram: 0xE0114000
@ -2509,6 +2601,35 @@ segments:
subsegments:
- [0x404220, c]
- [0x404E40, bin]
- start: 0x404F40
type: ci4
name: world/cloud
flip_y: true
width: 32
height: 32
- [0x405140, palette, world/cloud]
- start: 0x405340
type: ci4
name: world/waterblock
flip_y: true
width: 32
height: 32
- [0x405540, palette, world/waterblock]
- start: 0x405740
type: ci4
name: world/yellow_carpet
flip_y: true
width: 32
height: 32
- [0x405940, palette, world/yellow_carpet]
- start: 0x405B40
type: ci4
name: world/numbers
flip_y: true
width: 32
height: 160
- [0x406540, palette, world/numbers]
- [0x406740, bin]
- type: code
start: 0x406B40
vram: 0xE011C000