Skip to content

Support word_swap parameter on image segments (TMEM odd-row interleaving) #534

Description

@marijnvdwerf

Problem

Some N64 games store textures with TMEM interleaving applied: on odd rows, 32-bit words within each 64-bit TMEM word are swapped. This is a well-known N64 hardware behavior used by the SGI sprite tools (mksprite, mkisprite, etc.) and affects textures loaded via SP_TEXSHUF / sprite library routines.

When extracting these textures to PNG, splat produces garbled images because it doesn't un-swap the odd rows. There is currently no way to tell splat that an image segment needs word-swapping.

This affects standalone image segments like rgba16, ia16, ci8, i8, etc. — any format that may have been processed by the N64 sprite tools.

Context

  • pigment64 already supports --word-swap on both to-png (un-swap during extraction) and to-bin (re-swap during rebuild). See decompals/pigment64#22 / PR #23.
  • n64img (which splat uses for image extraction) does not support word-swap. PR #8 has been open as a draft since 2022 and was never merged.
  • The Pokemon Snap decomp project works around this with a custom snap_sprite segment type that manually un-swaps odd rows in Python after calling n64img's parse(). See ethteck/pokemonsnap PR #33.

Desired YAML syntax

Following the existing flip_x / flip_y pattern:

- {start: 0x8564E0, type: rgba16, width: 92, height: 21, word_swap: true}

What would need to change

Extraction (N64SegImg.split in src/splat/segtypes/n64/img.py):

Since n64img doesn't support word-swap, splat would need to un-swap the raw ROM bytes before passing them to n64img. The un-swap operation is: on odd rows, swap the two 4-byte halves of each 8-byte chunk. This is a pre-processing step on the binary data, independent of pixel format.

Alternatively, splat could invoke pigment64 to-png --word-swap instead of / in addition to n64img, but that would be a larger architectural change.

__init__ (N64SegImg.__init__):

Parse word_swap from YAML (like flip_x / flip_y) and store it on the segment object.

Build system integration:

The word_swap flag should be accessible on the segment object so that project build systems can pass --word-swap to pigment64 to-bin during rebuild. Currently, projects iterate over splat's linker entries and read seg.n64img.flip_h / seg.n64img.flip_v to build the pigment64 command line. A seg.word_swap field would allow the same pattern for word-swap.

Affected formats

The word-swap applies at the byte level (swap 4-byte halves of 8-byte chunks on odd rows), so it is format-independent. It could apply to rgba16, ia16, ci8, i8, ia8, ci4, i4, etc. Note that for RGBA32, the swap unit should be 8-byte halves of 16-byte chunks — see decompals/pigment64#30.

Real-world example

Pokemon Snap has ~33 standalone rgba16 textures (92×21 pixels each) that are word-swapped in ROM. Without splat support, these must either remain as raw bin segments or use a custom segment type that manually implements the un-swap.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions