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.mode = mode
self.infile = infile self.infile = infile
self.outfile = outfile self.outfile = outfile
self.flip_x = "--flip-x" in argv
self.flip_y = "--flip-y" 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 self.warned = False
def warn(self, msg): def warn(self, msg):
@ -250,7 +256,7 @@ class Converter():
if __name__ == "__main__": if __name__ == "__main__":
if len(argv) < 4: 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) exit(1)
Converter(*argv[1:]).convert() Converter(*argv[1:]).convert()

View File

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

View File

@ -1,6 +1,14 @@
# splat Release Notes # 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). * 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. * 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): def __init__(self, segment: Segment, src_paths: List[Path], object_path: Path, section: str):
self.segment = segment self.segment = segment
self.src_paths = [clean_up_path(p) for p in src_paths] self.src_paths = [clean_up_path(p) for p in src_paths]
self.object_path = path_to_object_path(object_path)
self.section = section self.section = section
if self.section == "linker":
self.object_path = None
else:
self.object_path = path_to_object_path(object_path)
class LinkerWriter(): class LinkerWriter():
def __init__(self): def __init__(self):
@ -65,7 +62,6 @@ class LinkerWriter():
if entry.section == "linker": # TODO: isinstance is preferable if entry.section == "linker": # TODO: isinstance is preferable
self._end_block() self._end_block()
self._begin_segment(entry.segment) self._begin_segment(entry.segment)
continue
start = entry.segment.rom_start start = entry.segment.rom_start
if isinstance(start, int): if isinstance(start, int):

View File

@ -27,7 +27,7 @@ class N64SegAsm(N64SegCodeSubsegment):
def scan(self, rom_bytes: bytes): def scan(self, rom_bytes: bytes):
if self.rom_start != "auto" and self.rom_end != "auto" and self.rom_start != self.rom_end: 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): def split(self, rom_bytes: bytes):
if not self.rom_start == self.rom_end: if not self.rom_start == self.rom_end:

View File

@ -135,7 +135,7 @@ class N64SegC(N64SegCodeSubsegment):
c_lines = self.get_c_preamble() c_lines = self.get_c_preamble()
for func in funcs_text: 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": if options.get_compiler() == "GCC":
c_lines.append("INCLUDE_ASM(s32, \"{}\", {});".format(self.name, func_name)) c_lines.append("INCLUDE_ASM(s32, \"{}\", {});".format(self.name, func_name))
else: else:

View File

@ -1,11 +1,12 @@
from segtypes.n64.ci8 import N64SegCi8 from segtypes.n64.ci8 import N64SegCi8
from util import iter
class N64SegCi4(N64SegCi8): class N64SegCi4(N64SegCi8):
@staticmethod @staticmethod
def parse_image(data, width, height, flip_h=False, flip_v=False): def parse_image(data, width, height, flip_h=False, flip_v=False):
img_data = bytearray() 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] >> 4)
img_data.append(data[i] & 0xF) img_data.append(data[i] & 0xF)

View File

@ -3,6 +3,7 @@ from segtypes.n64.rgba16 import N64SegRgba16
import png import png
from util import log from util import log
from util import options from util import options
from util import iter
if TYPE_CHECKING: if TYPE_CHECKING:
from segtypes.n64.palette import N64SegPalette as Palette from segtypes.n64.palette import N64SegPalette as Palette
@ -35,7 +36,19 @@ class N64SegCi8(N64SegRgba16):
@staticmethod @staticmethod
def parse_image(data, width, height, flip_h=False, flip_v=False): 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): def max_length(self):
return self.width * self.height 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): def is_branch_insn(mnemonic):
return (mnemonic.startswith("b") and not mnemonic.startswith("binsl") and not mnemonic == "break") or mnemonic == "j" 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)] 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) funcs = self.process_insns(insns, self.rom_start)
@ -50,7 +50,7 @@ class N64SegCodeSubsegment(Segment):
funcs = self.determine_symbols(funcs) funcs = self.determine_symbols(funcs)
self.gather_jumptable_labels(rom_bytes) self.gather_jumptable_labels(rom_bytes)
return self.add_labels(funcs, addsuffix) return self.add_labels(funcs)
def process_insns(self, insns, rom_addr): def process_insns(self, insns, rom_addr):
assert(isinstance(self.parent, N64SegCode)) assert(isinstance(self.parent, N64SegCode))
@ -243,6 +243,7 @@ class N64SegCodeSubsegment(Segment):
if offset != 0: if offset != 0:
offset_str = f"+0x{offset:X}" offset_str = f"+0x{offset:X}"
if self.parent: if self.parent:
self.parent.check_rodata_sym(func_addr, sym) self.parent.check_rodata_sym(func_addr, sym)
@ -256,7 +257,7 @@ class N64SegCodeSubsegment(Segment):
ret[func_addr] = func ret[func_addr] = func
return ret return ret
def add_labels(self, funcs, addsuffix): def add_labels(self, funcs):
ret = {} ret = {}
for func in funcs: for func in funcs:
@ -311,9 +312,6 @@ class N64SegCodeSubsegment(Segment):
if insn[0].mnemonic != "branch" and insn[0].mnemonic.startswith("b") or insn[0].mnemonic.startswith("j"): if insn[0].mnemonic != "branch" and insn[0].mnemonic.startswith("b") or insn[0].mnemonic.startswith("j"):
indent_next = True indent_next = True
if addsuffix:
func_text.append(f"endlabel {sym.name}")
ret[func] = (func_text, rom_addr) ret[func] = (func_text, rom_addr)
if options.get("find_file_boundaries"): if options.get("find_file_boundaries"):
@ -360,7 +358,7 @@ class N64SegCodeSubsegment(Segment):
rom_offset += 4 rom_offset += 4
def should_scan(self) -> bool: def should_scan(self) -> bool:
return options.mode_active("code") return self.should_split()
def should_split(self) -> bool: def should_split(self) -> bool:
return self.extract and options.mode_active("code") return self.extract and options.mode_active("code")

View File

@ -33,6 +33,13 @@ class N64SegData(N64SegCodeSubsegment):
def get_linker_section(self) -> str: def get_linker_section(self) -> str:
return ".data" 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): def get_symbols(self):
ret = [] ret = []
@ -141,7 +148,7 @@ class N64SegData(N64SegCodeSubsegment):
return sym_str return sym_str
def disassemble_data(self, rom_bytes): def disassemble_data(self, rom_bytes):
rodata_encountered = "rodata" in self.type rodata_encountered = self.type == "rodata"
ret = ".include \"macro.inc\"\n\n" ret = ".include \"macro.inc\"\n\n"
ret += f'.section {self.get_linker_section()}' ret += f'.section {self.get_linker_section()}'
@ -154,6 +161,9 @@ class N64SegData(N64SegCodeSubsegment):
mnemonic = syms[i].access_mnemonic mnemonic = syms[i].access_mnemonic
sym = self.parent.get_symbol(syms[i].vram_start, create=True, define=True, local_only=True) 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" sym_str = f"\n\nglabel {sym.name}\n"
dis_start = self.parent.ram_to_rom(syms[i].vram_start) dis_start = self.parent.ram_to_rom(syms[i].vram_start)
dis_end = self.parent.ram_to_rom(syms[i + 1].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 segment.parent = self
if segment.rom_start != "auto": if segment.rom_start != "auto":
assert isinstance(segment.rom_start, int)
segment.vram_start = self.rom_to_ram(segment.rom_start) segment.vram_start = self.rom_to_ram(segment.rom_start)
# TODO: assumes section order - generalize and stuff # TODO: assumes section order - generalize and stuff

View File

@ -2,10 +2,47 @@ from pathlib import Path
from typing import Optional from typing import Optional
from segtypes.n64.segment import N64Segment from segtypes.n64.segment import N64Segment
from util import options from util import options
from util import log
class N64SegImg(N64Segment): 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]: def out_path(self) -> Optional[Path]:
return options.get_asset_path() / self.dir / f"{self.name}.png" return options.get_asset_path() / self.dir / f"{self.name}.png"
def should_split(self) -> bool: def should_split(self) -> bool:
return self.extract and options.mode_active("img") return self.extract and options.mode_active("img")
def max_length(self) -> Optional[int]:
return None

View File

@ -27,17 +27,18 @@ class N64SegPalette(N64Segment):
self.name.split(".")[0] self.name.split(".")[0]
) if type(segment) is dict else self.name.split(".")[0] ) if type(segment) is dict else self.name.split(".")[0]
if self.rom_end == "auto": if self.extract:
log.error(f"segment {self.name} needs to know where it ends; add a position marker [0xDEADBEEF] after it") 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): if self.max_length() and isinstance(self.rom_end, int):
expected_len = int(self.max_length()) expected_len = int(self.max_length())
actual_len = self.rom_end - self.rom_start actual_len = self.rom_end - self.rom_start
if actual_len > expected_len and actual_len - expected_len > self.subalign: 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)") 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): 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]: def out_path(self) -> Optional[Path]:
return options.get_asset_path() / self.dir / f"{self.name}.png" 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 # TODO: move common behaviour to N64ImgSegment and have all image segments extend that instead
class N64SegRgba16(N64SegImg): 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): def split(self, rom_bytes):
path = self.out_path() path = self.out_path()
path.parent.mkdir(parents=True, exist_ok=True) 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) 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): 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) w = int(width * bytes_per_x)
h = int(height * bytes_per_y) 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)) 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)) 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 y in yrange:
for x in xrange: for x in xrange:
yield x, y, (y * w) + x yield x, y, (y * w) + x

View File

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

View File

@ -27,7 +27,7 @@ def get(opt, default=None):
return opts.get(opt, default) return opts.get(opt, default)
def get_platform() -> str: def get_platform() -> str:
return opts.get("platform", "n64") return opts.get("platform", "N64")
def get_compiler() -> str: def get_compiler() -> str:
return opts.get("compiler", "IDO") return opts.get("compiler", "IDO")

View File

@ -2172,14 +2172,12 @@ segments:
subsegments: subsegments:
- [0x38F900, c] - [0x38F900, c]
- [0x390340, bin] - [0x390340, bin]
- [0x3903D0, bin] - [0x3903D0, ia4, battle/lucky, 64, 32]
- start: 0x390810 - [0x3907D0, ia4, battle/miss, 64, 32]
type: ia4 - [0x390BD0, ia4, battle/good, 64, 32]
name: battle/text_action_command_ratings - [0x390FD0, ia4, battle/nice, 64, 32]
width: 64 - [0x3913D0, ia4, battle/super, 64, 32]
height: 125 - [0x3917D0, bin]
flip: vertical
- [0x3917B0, bin]
- type: code - type: code
start: 0x391D30 start: 0x391D30
vram: 0xE0092000 vram: 0xE0092000
@ -2471,20 +2469,114 @@ segments:
subsegments: subsegments:
- [0x3EB4E0, c] - [0x3EB4E0, c]
- [0x3EBC30, bin] - [0x3EBC30, bin]
- [0x3ED4E0, ia8, world/text_chapter, 128, 38] - [0x3EBE60, ia8, world/ch0, 144, 40]
- [0x3EE7E0, bin] - [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 - type: code
start: 0x3F83F0 start: 0x3F83F0
vram: 0xE0110000 vram: 0xE0110000
subsegments: subsegments:
- [0x3F83F0, c] - [0x3F83F0, c]
- [0x3F8CA0, bin] - [0x3F8CA0, bin]
- [0x3F8CC0, i4, ice_smash, 64, 128]
- [0x3F9CC0, bin]
- type: code - type: code
start: 0x3F9E50 start: 0x3F9E50
vram: 0xE0112000 vram: 0xE0112000
subsegments: subsegments:
- [0x3F9E50, c] - [0x3F9E50, c]
- [0x3FA480, bin] - [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 - type: code
start: 0x3FEAE0 start: 0x3FEAE0
vram: 0xE0114000 vram: 0xE0114000
@ -2509,6 +2601,35 @@ segments:
subsegments: subsegments:
- [0x404220, c] - [0x404220, c]
- [0x404E40, bin] - [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 - type: code
start: 0x406B40 start: 0x406B40
vram: 0xE011C000 vram: 0xE011C000