Jazz2::Compatibility::JJ2Anims class

Parses original .j2a animation files.

Reads the original game's combined animation archive (Anims.j2a), decoding its animation sets, frames and embedded audio samples, and writes the converted sprites and sounds into the engine's .pak format. Asset naming and palette handling are driven by AnimSetMapping.

Public types

class ImageContentDecoder
Incremental decoder for the image content written by WriteImageContent.
struct PackedFrame
Where one frame ends up in a tightly packed sheet.
struct PackedSheet
Describes a tightly packed sheet, or nothing when the frames form a regular grid.
enum class ImageCompression { Default, Lz4 }
Compression of the image content of the sprite sheets and tilesets a conversion writes.

Public static variables

static std::int32_t MaxSheetSize constexpr
Largest sheet PackRectangles() lays out, per axis.
static std::int32_t SheetPageSize constexpr
Size of the pages a sheet may be split into, per axis - nothing is placed across one.
static ImageCompression PreferredImageCompression
Image compression that sprite sheets and tilesets are converted with, see ImageCompression.
static std::uint8_t ImageContentLz4Flag constexpr
Header flag of a sprite sheet (.aura) or a tileset (.j2t) whose image content is LZ4.
static std::int32_t TilesetLz4BandRows constexpr
Rows of a tileset that go into one LZ4 block, one band of tiles - see WriteImageContentLz4.

Public static functions

static auto Convert(StringView path, PakWriter& pakWriter, bool isPlus = false, ConversionProgress progress = {}) -> JJ2Version
Converts the specified animation file and writes the result to a .pak file.
static void WriteImageContent(Stream& so, const std::uint8_t* data, std::int32_t width, std::int32_t height, std::int32_t channelCount)
Writes raw image content to the specified stream.
static void ReadImageContent(Stream& s, std::uint8_t* data, std::int32_t width, std::int32_t height, std::int32_t channelCount)
Reads raw image content from the specified stream.
static void WriteImageContentLz4(Stream& so, const std::uint8_t* data, std::int32_t width, std::int32_t height, std::int32_t channelCount, std::int32_t bandRows)
Writes image content as LZ4 blocks of bandRows rows each.
static auto DecodeImageContentLz4(const std::uint8_t*& src, const std::uint8_t* end, std::uint8_t* data, std::int32_t byteCount) -> bool
Decodes the next LZ4 block of image content from memory, advancing src past it.
static auto ReadImageContentLz4(Stream& s, std::uint8_t* data, std::int32_t byteCount) -> bool
Reads and decodes the next LZ4 block of image content from a stream, see DecodeImageContentLz4.
static auto PackRectangles(SmallVectorImpl<PackedFrame>& rects, std::int32_t spacing, std::int32_t& sheetWidth, std::int32_t& sheetHeight) -> bool
Lays rectangles out in as small a sheet as possible.
static auto LiesAcrossPageLine(const PackedFrame& rect) -> bool constexpr
Returns true if a rectangle lies across a multiple of SheetPageSize, where a split texture is cut.

Enum documentation

enum class Jazz2::Compatibility::JJ2Anims::ImageCompression

Compression of the image content of the sprite sheets and tilesets a conversion writes.

Every platform reads the game's own format (WriteImageContent). A tree prepared for one of the consoles that cannot convert on the device - and so has to carry its content ready-made anyway - can carry LZ4 instead (WriteImageContentLz4), which decodes faster (a tileset 1.6 times as fast on the Nintendo 64, measured) and takes less than half the space. Only the asset packer changes this, for those targets; a build without LZ4 support refuses such a file rather than misreading it.

Enumerators
Default

The game's own format, see WriteImageContent

Lz4

LZ4 blocks of the raw pixels, see WriteImageContentLz4

Function documentation

static JJ2Version Jazz2::Compatibility::JJ2Anims::Convert(StringView path, PakWriter& pakWriter, bool isPlus = false, ConversionProgress progress = {})

Converts the specified animation file and writes the result to a .pak file.

Parameters
path
pakWriter
isPlus
progress Reports how far the conversion has got, see ConversionProgress

static void Jazz2::Compatibility::JJ2Anims::ReadImageContent(Stream& s, std::uint8_t* data, std::int32_t width, std::int32_t height, std::int32_t channelCount)

Reads raw image content from the specified stream.

The counterpart of WriteImageContent, and the only decoder for it - the game reads its sprites, tilesets and fonts through this. data is written channelCount bytes per pixel, but each pixel is stored with a single four byte write, so the buffer needs three bytes of slack past the last pixel when fewer channels are read.

static void Jazz2::Compatibility::JJ2Anims::WriteImageContentLz4(Stream& so, const std::uint8_t* data, std::int32_t width, std::int32_t height, std::int32_t channelCount, std::int32_t bandRows)

Writes image content as LZ4 blocks of bandRows rows each.

Every block is the raw pixels of its rows (channelCount bytes each), compressed at the highest ratio - the cost is paid once, by the converter, and decoding does not get slower for it - and preceded by its compressed size (32-bit little-endian). The blocks are independent, so a reader can decode them one at a time straight into a buffer of one band, which is how a tileset is read (see TilesetLz4BandRows); a sprite sheet is one block.

static bool Jazz2::Compatibility::JJ2Anims::DecodeImageContentLz4(const std::uint8_t*& src, const std::uint8_t* end, std::uint8_t* data, std::int32_t byteCount)

Decodes the next LZ4 block of image content from memory, advancing src past it.

byteCount is the exact size of the block's pixels. Returns false if the block is damaged or does not have exactly that many bytes; data is then left undefined.

static bool Jazz2::Compatibility::JJ2Anims::PackRectangles(SmallVectorImpl<PackedFrame>& rects, std::int32_t spacing, std::int32_t& sheetWidth, std::int32_t& sheetHeight)

Lays rectangles out in as small a sheet as possible.

Takes PackedFrame::W and PackedFrame::H of every rectangle and fills in PackedFrame::X and PackedFrame::Y, keeping spacing pixels between any two rectangles. The sheet is chosen for the least memory once its dimensions are rounded up to powers of two, which is what the hardware that cannot sample anything else pays for it; among layouts that round up the same, one that fits into a single SheetPageSize page wins, and then the smallest exact area, which is what every other platform pays. The sheet is reported at its exact size rather than rounded up, the backends that need a power of two pad the texture themselves.

No rectangle is placed across a multiple of SheetPageSize on either axis (see there), unless there is no other way to fit them into MaxSheetSize x MaxSheetSize - a rectangle larger than a page, or so many that the gaps the page lines leave cannot be afforded; LiesAcrossPageLine() tells when that happened. Returns false when the rectangles do not fit into MaxSheetSize x MaxSheetSize at all.

Variable documentation

static std::int32_t Jazz2::Compatibility::JJ2Anims::MaxSheetSize constexpr

Largest sheet PackRectangles() lays out, per axis.

The smallest texture limit among the supported platforms decides it, so a sheet that fits here needs no per-platform variant of the converted assets.

static std::int32_t Jazz2::Compatibility::JJ2Anims::SheetPageSize constexpr

Size of the pages a sheet may be split into, per axis - nothing is placed across one.

The PSP's GE cannot address more than 512 texels per axis, so its backend splits a larger texture into pages of this size and draws every primitive from the page its texture rectangle starts in (the legacy GL backend does the same on a device limited to 512). A frame lying across a page line would be cut off at it, so PackRectangles() never places one there.

static std::uint8_t Jazz2::Compatibility::JJ2Anims::ImageContentLz4Flag constexpr

Header flag of a sprite sheet (.aura) or a tileset (.j2t) whose image content is LZ4.

The flags byte follows the version in both headers, and this bit is free in both.