Open main menu

Difference between revisions of "SCUMM/Technical Reference/Room resources"

Supplement and add critical missing info
(Supplement and add critical missing info)
Line 11: Line 11:
<tr><td>0x00</td><td>UInt16LE</td><td>Size of the room resource chunk</td></tr>
<tr><td>0x00</td><td>UInt16LE</td><td>Size of the room resource chunk</td></tr>
<tr><td>0x02</td><td>???</td><td>Unknown</td></tr>
<tr><td>0x02</td><td>???</td><td>Unknown</td></tr>
<tr><td>0x04</td><td>UInt16LE</td><td>Room width in tiles</td></tr>
<tr><td>0x04</td><td>UInt16LE</td><td>Room width in tiles (always <code>0x1C</code> or <code>0x3C</code>)</td></tr>
<tr><td>0x06</td><td>UInt16LE</td><td>Room height in tiles</td></tr>
<tr><td>0x06</td><td>UInt16LE</td><td>Room height in tiles (always <code>0x10</code> on NES)</td></tr>
<tr><td>0x08</td><td>???</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x08</td><td>???</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x0a</td><td>UInt16LE</td><td>Offset to tileset index, palette and gfx nametable location (NES)</td></tr>
<tr><td>0x0a</td><td>UInt16LE</td><td>Offset to 1 byte for the tileset index, followed by 16 bytes of palette data, and then the RLE encoded nametable data (NES)</td></tr>
<tr><td>0x0c</td><td>UInt16LE</td><td>Offset to gfx attrtable location (NES)</td></tr>
<tr><td>0x0c</td><td>UInt16LE</td><td>Offset to the RLE encoded attribute table (NES)</td></tr>
<tr><td>0x0e</td><td>UInt16LE</td><td>Offset to mask flag value location</td></tr>
<tr><td>0x0e</td><td>UInt16LE</td><td>Offset to the mask flag location, which if 1 is followed by the RLE encoded mask data</td></tr>
<tr><td>0x10</td><td>???</td><td>Unknown (On NES both these values are equal)</td></tr>
<tr><td>0x10</td><td>???</td><td>Unknown (On NES both these values are equal)</td></tr>
<tr><td>0x12</td><td>???</td><td>Unknown</td></tr>
<tr><td>0x12</td><td>???</td><td>Unknown</td></tr>
Line 41: Line 41:
=== Object image offsets ===
=== Object image offsets ===


From byte 0x1c starts the offsets to object images:
From byte <code>0x1c</code> starts the offsets to object images, 2 bytes per object:
<table border="2" cellspacing="0" cellpadding="4">
<table border="2" cellspacing="0" cellpadding="4">
<tr><th>Location</th><th>Format</th><th>Data</th></tr>
<tr><th>Location</th><th>Format</th><th>Data</th></tr>
Line 48: Line 48:
<tr><td>...</td><td>UInt16LE</td><td>Repeated <code>objNum</code> times</td></tr>
<tr><td>...</td><td>UInt16LE</td><td>Repeated <code>objNum</code> times</td></tr>
</table>
</table>
On the NES this is RLE encoded nametable and attribute table data based on the height and width of the object. The nametable data is encoded per row. These graphical updates are triggered in SCUMM by <code>setState08</code> and <code>clearState08</code>. Objects that have no state changes must still have an entry and can point arbitrarily.


=== Object code offsets ===
=== Object code offsets ===


From there, there is the list of offsets to each object code:
Next starts the list of offsets to each object's data:
<table border="2" cellspacing="0" cellpadding="4">
<table border="2" cellspacing="0" cellpadding="4">
<tr><th>Location</th><th>Format</th><th>Data</th></tr>
<tr><th>Location</th><th>Format</th><th>Data</th></tr>
<tr><td>0x1c + objNum * 2</td><td>UInt16LE</td><td>Offset to object 1 content location</td></tr>
<tr><td>0x1c + objNum * 2</td><td>UInt16LE</td><td>Offset to object 1 content location</td></tr>
<tr><td>0x1e + objNum * 2</td><td>UInt16LE</td><td>Offset to object 2 content location</td></tr>
<tr><td>0x1c + objNum * 2</td><td>UInt16LE</td><td>Offset to object 2 content location</td></tr>
<tr><td>...</td><td>UInt16LE</td><td>Repeated <code>objNum</code> times</td></tr>
<tr><td>...</td><td>UInt16LE</td><td>Repeated <code>objNum</code> times</td></tr>
</table>
</table>
See table below for a description of the object data.
=== Unknown data ===
At this point there are 4 bytes of unknown data on the NES.


=== Number of boxes ===
=== Number of boxes ===


The boxes number in the room is located at the boxes number offset specified in the header.
The number of boxes (<code>boxNum</code>) in the room is located at the offset specified in the header.


<table border="2" cellspacing="0" cellpadding="4">
<table border="2" cellspacing="0" cellpadding="4">
Line 68: Line 76:
</table>
</table>


'''Note:''' Some LFL files have unknown data between the last object code offset and the boxes number offset location. For most file however, both data are contiguous.
'''Note:''' Some LFL files have unknown data between the last object code offset and the boxes number offset location. For most files however, both data are contiguous.


=== Boxes ===
=== Boxes ===


2 bytes after the offset to number of boxes starts the payload for the boxes. Each box takes 8 bytes described as follow:
If the number of boxes is greater then 0 then 1 byte later starts the boxes payload. Each box has 8 bytes described as follows:


<table border="2" cellspacing="0" cellpadding="4">
<table border="2" cellspacing="0" cellpadding="4">
Line 85: Line 93:
<tr><td>0x07</td><td>UInt8</td><td>flags</td></tr>
<tr><td>0x07</td><td>UInt8</td><td>flags</td></tr>
</table>
</table>
The flags byte has various uses including:
<table border="2" cellspacing="0" cellpadding="4">
<tr><th>Flag Value</th><th>Meaning</th></tr>
<tr><td>0x08</td><td>X flip</td></tr>
<tr><td>0x10</td><td>Y flip</td></tr>
<tr><td>0x20</td><td>Unused in V1</td></tr>
<tr><td>0x40</td><td>Locked</td></tr>
<tr><td>0x80</td><td>Invisible</td></tr>
</table>
=== Boxes matrix table ===
The box matrix payload starts with an offset table that points to the beginning of each box's data. It is <code>boxNum</code> wide.


=== Box matrix ===
=== Box matrix ===


Immediately following the last box payload is the box matrix.
Immediately following the last box's data is the box matrix which determines how the boxes are connected.
 
There are a minimum of <code>boxNum * boxNum</code> entries read as UInt8. Each entry has at least 2 pairs of 3 byte sets which determine connecting boxes. The table allows more values to be used in each entry if necessary. Example pseudo code:
 
<pre>boxMatrix = [
[1, 1, 1,  1, 1, 0],
[1, 2, 2,  2, 2, 1],
[1, 2, 3,  3, 3, 2],
[2, 2, 3,  4, 4, 3],
[3, 3, 3,  4, 5, 4],
[4, 4, 4,  4, 5, 5]
];
 
function nextBox (from, to)
{
  var boxm = boxMatrix[from];
  if (boxm[0] <= to && to <= boxm[1])
    return boxm[2];
  if (boxm[3] <= to && to <= boxm[4])
    return boxm[5];
  return -1;
}</pre>
 
=== Tileset index ===
 
The two bytes at <code>0x0a</code> of the room data point to the tileset index which is used to look up the tileset in separate table. The tileset table provides the bank and memory offset for the given tileset.
 
=== Palettes ===
 
One byte later starts the background palettes for a total of 16 bytes.
 
=== Nametable data ===
 
Next starts the RLE encoded nametable data (NES). The room height and width at bytes <code>0x06</code> and <code>0x04</code> of the room data are used to determine how much data is decoded.
 
=== Attribute table data ===
 
The two bytes at <code>0x0c</code> of the room data point to the RLE encoded attribute table (NES). The amount of data is decoded according to the rom height and width.
 
=== Mask Flag ===
 
Byte <code>0x0e</code> of the room data points to this mask flag. It has a value of <code>0x00</code> or <code>0x01</code>.
 
=== Mask data ===


There are <code>boxNum * boxNum</code> number of them read as UInt8.
If the mask flag is <code>0x01</code> then the RLE encoded mask data follows. This is bitmap data where each bit corresponds to a single tile, so a single byte covers a row of 8 tiles. A bit value of 1 = masked (background priority), and 0 = unmasked (sprite priority). The bits in each byte are reversed (or read left to right) before they are applied.
 
=== Object image data ===
 
Next starts the data that is pointed to by the object image offsets beginning at byte 0x1c in the room data. This is RLE encoded nametable and attribute table data for each. The amount of data for each object is based on the height and width of the object. The data is RLE encoded per row. These graphical updates are triggered in SCUMM by `setState08` and `clearState08`.


=== Object code ===
=== Object code ===
Line 102: Line 172:
<tr><td>0x02</td><td>???</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x02</td><td>???</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x03</td><td>???</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x03</td><td>???</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x04</td><td>UInt16LE</td><td>The object number (used to reference object in scripts)</td></tr>
<tr><td>0x04</td><td>UInt16LE</td><td>The object number (used to reference object in scripts). The object number must be 255 (0xFF) or less to be able to be picked up.</td></tr>
<tr><td>0x06</td><td>??</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x06</td><td>??</td><td>Unknown (Always 0 on NES)</td></tr>
<tr><td>0x07</td><td>UInt8</td><td>X position of object</td></tr>
<tr><td>0x07</td><td>UInt8</td><td>X position of object</td></tr>
<tr><td>0x08</td><td>UInt8</td><td>Y position of object + Object parent state</td></tr>
<tr><td>0x08</td><td>UInt8</td><td>Object parent state + Y position of object (<code>0xSSSYYYYY</code>). Parent state is always <code>0x04</code> if present and is used in combination with the Object parent value at <code>0x0a</code> to hide objects behind doors (think the refrigerator).</td></tr>
<tr><td>0x09</td><td>UInt8</td><td>Object width in tiles</td></tr>
<tr><td>0x09</td><td>UInt8</td><td>Object width in tiles</td></tr>
<tr><td>0x0a</td><td>UInt8</td><td>Object parent</td></tr>
<tr><td>0x0a</td><td>UInt8</td><td>Object parent</td></tr>
<tr><td>0x0b</td><td>UInt8</td><td>Walk to X position</td></tr>
<tr><td>0x0b</td><td>UInt8</td><td>Walk to X position</td></tr>
<tr><td>0x0c</td><td>UInt8</td><td>Walk to Y position</td></tr>
<tr><td>0x0c</td><td>UInt8</td><td>Preposition + Walk to Y position (<code>0xPPPYYYYY</code>). Possible preposition values are: <code>0x001</code> = in, <code>0x010</code> = with, <code>0x011</code> = on.</td></tr>
<tr><td>0x0d</td><td>UInt8</td><td>Actor facing direction + Object height in tiles</td></tr>
<tr><td>0x0d</td><td>UInt8</td><td>Object height in tiles + Actor facing direction (<code>0xHHHHHDDD</code>). Possible facing values are: <code>0x100</code> = left, <code>0x101</code> = right, <code>0x110</code> = front (facing player), <code>0x111</code> = back (facing away from player).</td></tr>
<tr><td>0x0e</td><td>UInt8</td><td>Offset to object name</td></tr>
<tr><td>0x0e</td><td>UInt8</td><td>Offset to object name</td></tr>
</table>
</table>


'''Note:''' The object code data starts at byte 15, the verb-script pairs part ends with a 0x00 byte. This means that the object name offset value is always an even number between 16 and 254.
'''Note:''' The object code data starts at byte 15. The verb-script pairs terminate with a null byte (0x00). This means that the object name offset value is always an even number 16 or above.


==== Object code content ====
==== Object code content ====


Object code content contains 3 optional sections, in this order:
Object code content contains 3 optional sections, in this order:
* 0 or more pairs of verb ids and object script offsets
* 0 or more pairs of verb id and script offsets
* The name of the object
* The name of the object
* 0 or more object scripts
* 0 or more object scripts consisting of SCUMM bytecode


===== Object script offsets =====
===== Object script offsets =====


From byte 15, 2 UInt8 are read 2 by 2.
Starting at byte 15, two UInt8 values are read at a time.


The first one is a reference to the verb ID and the second is an offset to the beginning of the object script.
The first value is a the verb ID and the second value is an offset to the beginning of the object script. Multiple verbs can have the same offset. The verb IDs are as follows:


Multiple verbs can refer to the same offset.
<table border="2" cellspacing="0" cellpadding="4">
<tr><th>Verb ID</th><th>Meaning</th></tr>
<tr><td>0x01</td><td>Open</td></tr>
<tr><td>0x02</td><td>Close</td></tr>
<tr><td>0x03</td><td>Give</td></tr>
<tr><td>0x04</td><td>Turn On</td></tr>
<tr><td>0x05</td><td>Turn Off</td></tr>
<tr><td>0x06</td><td>Fix (Unused)</td></tr>
<tr><td>0x07</td><td>New Kid (Unused)</td></tr>
<tr><td>0x08</td><td>Unlock</td></tr>
<tr><td>0x09</td><td>Push</td></tr>
<tr><td>0x0A</td><td>Pull</td></tr>
<tr><td>0x0B</td><td>Use</td></tr>
<tr><td>0x0C</td><td>Read</td></tr>
<tr><td>0x0D</td><td>Walk To</td></tr>
<tr><td>0x0E</td><td>Get</td></tr>
<tr><td>0x0F</td><td>What is (Unused)</td></tr>
<tr><td>0xFF</td><td>All</td></tr>
</table>


This part ends with a 0x00.
This part ends with <code>0x00</code>.


===== Object name =====
===== Object name =====


The object name starts at specified offset. Each character is read until a 0x00 value is found.
The object name starts at the offset specified in byte <code>0x0e</code> of the object data. Each character is read until a <code>0x00</code> value is found.


===== Object script =====
===== Object scripts =====


Each object script ends with 0x00 (Opcode for <code>stopObjectCode()</code>).
The object scripts are composed of SCUMM bytecode. Each object script ends with <code>0x00</code> (SCUMM opcode for <code>stopObjectCode()</code>).
3

edits