Riven SFXE resources

From A look inside The Link @ wiki
Jump to: navigation, search
Mohawk Overview
Scripts Variables
External commands

What is an SFXE resource?

SFXE data has to do with the water special effect, which works by taking pixels from the rendered card picture and slightly moving them in periodic motion according to precomputed animation frames. Each frame consists of "displacement commands" that take pixel rows from the original picture and copy them at specified positions over the front viewport. Displacement commands in each frame are arranged in a way that already takes into account periodic motion: the rendering code just loops interpreting frame after frame, blindly copying pixel rows from the offscreen, pre-composited card picture to the visible screen. It is a reasonably simple algorithm whith a low number of pixels copied each second, attaining good performance even in the slow systems targeted by the original engine.

SFXE data is divided in 4 sections: the header, an unknown and apparently unused section, an offset table and a frame section. The offset table points to frames in the frame section. Everything is in big-endian byte order.

The header

First of all comes a 52-byte header:

int8_t magic[2]
uint16_t frame_count
uint32_t offset_table_position
uint16_t left, top, right, bottom
uint16_t effect_speed
uint16_t u0
uint16_t alt_top, alt_left, alt_bottom, alt_right
uint16_t u1
uint16_t alt_frame_count
uint32_t u2
uint32_t u3
uint32_t u4
uint32_t u5
uint32_t u6
  • magic is the string "SL". Maybe it identifies a known format, but I've found no info;
  • frame_count is the number of animation frames;
  • offset_table_position seems always 164. Probably it is the position of the offset table relative to the beginning of the resource, which is exactly 52+112=164. Altering this field makes Riven crash.
  • left, top, right, bottom define the global rectangle of the effect area within the 608x392 game window. Note that right and bottom exceed by 1 pixel (that is, fullscreen SFXE resources have right = 608 and bottom = 392, not 607 and 391);
  • effect_speed seems always 15. It controls the speed of the water effect, and seems in units of frames per second;
  • alt_top, alt_left, alt_bottom, alt_right is a copy of the previous rectangle. It seems ignored by the engine, changing it doesn't produce effects;
  • u1 seems always 0;
  • alt_frame_count is a copy of frame_count;
  • u2, u3, u4, u5 seem always 4, 0, 0x19 and 1 respectively;
  • u6 seems always 0 or 0x10000000;

Note that the effect rectangle is not really necessary to render the effect: the only important value is top.

Altering any of the unknown fields u0...u6 produces no apparent change at all, nor makes the engine crash. They look really like a waste of space.

The unknown section

This is a 112-byte block of unknown purpose. It seems divided into 8-byte rows, and looks like an encoded mask or a tBMP command stream. The values don't change very much between resources and show the same structure. Incredibly enough, altering any of these values doesn't produce observable effects! Well, I've tried only on a few resources, but this behavior is quite disappointing. This data looks like a structure that was discarded in the final version of the engine, but was left in SFXE resources.

The offset table

This is just an array of frame_count unsigned long offsets, relative to the beginning of the resource. Each offset points to a frame in the frame section.

The frame section

This section contains frame_count data blocks, each representing an animation frame. Each block is made of a variable number of commands. Each command is an unsigned short opcode optionally followed by arguments. 3 opcodes have been observed:

  • Command 1 has no arguments. It increments current_row by one pixel; current_row is a variable that scans the effect area row by row, starting from top (defined in the header). It is used by command 3 to help copying pixel rows. The number of "1" commands inside a frame is equal to the effect area height in pixels, that is, the number of pixel rows affected.
  • Command 3 has 4 unsigned short arguments, dst_left src_left src_top row_width. It defines the displacement: take a row of row_width pixels from the original picture at (src_left,src_top) and copy it at (dst_left,current_row) on the viewport.
  • Command 4 is the last command and ends the frame.