Each effect is a LIST chunk of type efct. What is inside is a
DirectInput DIEFFECT structure written out with its pointers turned into
flags, so that the arrays they pointed to could follow as data chunks. The
offsets below count from the first byte of the LIST chunk. Numbers are
little-endian.
Layout
+0x00List headerLIST, the size,efct12+0x0CName64+0x4CDIEFFECT56+0x84Effect type GUID16+0x94A number4+0x98Data chunks axes, direction, envelope, parametersto the end of the list
| Offset | Size | Name | Description |
|---|---|---|---|
+0x00 | 4 | List ID | LIST. |
+0x04 | 4 | List size | The bytes after this field: the rest of the effect. In every file seen the lists of a file add up exactly to the end of the file. |
+0x08 | 4 | List type | efct. |
+0x0C | 64 | Name | The effect’s name as a zero-ended string in a 64-byte buffer, such as ConstantForce10 or Sine6. Force Editor does not clear the buffer, so what follows the zero is left over from whatever was in it before: in shotgun1.ffe the second effect’s buffer reads Sine85, a zero, then tForce10, the tail of the ConstantForce10 that was there before, and after that come bytes that look like leftover memory. All 262 names have such bytes after the zero; ignore them. |
+0x4C | 56 | DIEFFECT | See below. |
+0x84 | 16 | Effect type | The GUID of the effect type, for example GUID_Sine. The standard ones are on the effect types page. This is the GuidEffect that DIFILEEFFECT hands to a program. |
+0x94 | 4 | A number | 1 in all 262 effects seen. What it means is not known: it could be a version or a count. |
+0x98 | Data chunks | Up to four data chunks, in the order below. |
The DIEFFECT
The 14 DWORDs of the DIEFFECT structure of DirectInput 7 (dinput.h),
in order. The four fields that were pointers in memory hold, in the file, the
DIEP_* flag of the data chunk that holds what they pointed to, or 0 when
there is none.
| Offset | Field | Description |
|---|---|---|
+0x4C | dwSize | 56, the size of DIEFFECT. |
+0x50 | dwFlags | DIEFF_* flags: how the axes and direction are given. 0x01 DIEFF_OBJECTIDS (axes are object IDs), 0x02 DIEFF_OBJECTOFFSETS (axes are offsets in the data format), 0x10 DIEFF_CARTESIAN, 0x20 DIEFF_POLAR, 0x40 DIEFF_SPHERICAL. All 262 effects have 0x22: offsets and polar. |
+0x54 | dwDuration | How long the effect lasts, in microseconds, or 0xFFFFFFFF (INFINITE) to play until stopped. 5 effects are infinite. |
+0x58 | dwSamplePeriod | The time between the device’s updates of the effect, in microseconds. 0 (the default) in every effect. |
+0x5C | dwGain | The effect’s gain, up to 10000 (full strength). 10000 in every effect. |
+0x60 | dwTriggerButton | The button that plays the effect, or 0xFFFFFFFF (DIEB_NOTRIGGER) for none. With DIEFF_OBJECTOFFSETS it is the button’s offset in the data format: 0x30 is DIJOFS_BUTTON0, which 23 effects have. |
+0x64 | dwTriggerRepeatInterval | The time between repeats while the trigger is held, in microseconds. 0xFFFFFFFF in every effect. |
+0x68 | cAxes | The number of axes: 2 in every effect. |
+0x6C | rgdwAxes | 0x20 (DIEP_AXES): an axes chunk follows. The explorer accepts 0 (none) or 0x20. |
+0x70 | rglDirection | 0x40 (DIEP_DIRECTION): a direction chunk follows. |
+0x74 | lpEnvelope | 0x80 (DIEP_ENVELOPE) when there is an envelope chunk, 0 for none. 75 effects have one. |
+0x78 | cbTypeSpecificParams | The size in bytes of the effect type’s parameters: 4 for a constant force, 8 for a ramp, 16 for a wave, 24 for each axis of a condition. |
+0x7C | lpvTypeSpecificParams | 0x100 (DIEP_TYPESPECIFICPARAMS): a parameters chunk follows. |
+0x80 | dwStartDelay | The time between the effect being started and its force beginning, in microseconds. Added in DirectInput 6, so the structure is 56 bytes and not the 52 of DIEFFECT_DX5. 131 effects have a delay. |
The data chunks
Each is an ordinary chunk: data, a 32-bit size, and the bytes. A chunk is
there when the matching flag above is not 0, and they follow in this order.
| Chunk | When | Size | Contents |
|---|---|---|---|
| Axes | rgdwAxes is not 0 | 4 × cAxes | One DWORD for each axis. With DIEFF_OBJECTOFFSETS they are offsets in the device’s data format: 0 X, 4 Y, 8 Z, 12 X rotation, 16 Y rotation, 20 Z rotation, 24 and 28 the sliders. The files all have 0 and 4. |
| Direction | rglDirection is not 0 | 4 × cAxes | One signed number for each axis. In polar coordinates the first is the angle in hundredths of a degree (0 north, 9000 east, 18000 south) and the second is 0; in Cartesian they are the components of a vector. |
| Envelope | lpEnvelope is not 0 | 20 | A DIENVELOPE: dwSize (20), dwAttackLevel, dwAttackTime, dwFadeLevel, dwFadeTime. The levels are 0 to 10000, the times microseconds. |
| Parameters | cbTypeSpecificParams is not 0 | cbTypeSpecificParams | The effect type’s own structure: see effect types. |
An envelope changes the strength of the effect over time: it starts at the attack level, rises to the effect’s full strength over the attack time, and over the last fade time falls to the fade level. Force curves shows how the explorer draws it.
Worked example
chainb.ffe from the DirectX 7 SDK has one effect, a sine wave named
Sine6 with an envelope. The file is 264 bytes: 28 for the header and
236 for the list.
| Offset | Bytes | Meaning |
|---|---|---|
0x00 | 52 49 46 46 | RIFF |
0x04 | 00 00 00 00 | RIFF size, 0 |
0x08 | 46 4F 52 43 | FORC |
0x0C | 5C 77 7E 19 BA 34 D3 11 AB D5 00 C0 4F 8E C6 27 | File GUID {197E775C-34BA-11D3-ABD5-00C04F8EC627} |
0x1C | 4C 49 53 54 | LIST |
0x20 | E4 00 00 00 | List size 228 (0x1C + 8 + 228 = 264) |
0x24 | 65 66 63 74 | efct |
0x28 | 53 69 6E 65 36 00 and 58 more bytes | Name “Sine6”, then left-over bytes to the end of the 64 |
0x68 | 38 00 00 00 | dwSize 56 |
0x6C | 22 00 00 00 | dwFlags 0x22: offsets and polar |
0x70 | 88 6C 06 00 | dwDuration 0x66C88 = 421000 µs |
0x74 | 00 00 00 00 | dwSamplePeriod 0 |
0x78 | 10 27 00 00 | dwGain 10000 |
0x7C | FF FF FF FF | dwTriggerButton none |
0x80 | FF FF FF FF | dwTriggerRepeatInterval |
0x84 | 02 00 00 00 | cAxes 2 |
0x88 | 20 00 00 00 | rgdwAxes: DIEP_AXES |
0x8C | 40 00 00 00 | rglDirection: DIEP_DIRECTION |
0x90 | 80 00 00 00 | lpEnvelope: DIEP_ENVELOPE |
0x94 | 10 00 00 00 | cbTypeSpecificParams 16 |
0x98 | 00 01 00 00 | lpvTypeSpecificParams: DIEP_TYPESPECIFICPARAMS |
0x9C | 38 C7 00 00 | dwStartDelay 51000 µs |
0xA0 | 23 1C 54 13 33 8E D0 11 9A D0 00 A0 C9 A0 6E 35 | Effect type {13541C23-8E33-11D0-9AD0-00A0C9A06E35}, GUID_Sine |
0xB0 | 01 00 00 00 | The number, 1 |
0xB4 | 64 61 74 61 08 00 00 00 00 00 00 00 04 00 00 00 | data, 8 bytes: axes 0 (X) and 4 (Y) |
0xC4 | 64 61 74 61 08 00 00 00 00 00 00 00 00 00 00 00 | data, 8 bytes: direction 0 and 0 (north) |
0xD4 | 64 61 74 61 14 00 00 00 14 00 00 00 40 1F 00 00 74 A4 00 00 88 13 00 00 D0 91 02 00 | data, 20 bytes: envelope with size 20, attack level 8000, attack time 42100 µs, fade level 5000, fade time 168400 µs |
0xF0 | 64 61 74 61 10 00 00 00 88 13 00 00 00 00 00 00 00 00 00 00 02 7E 00 00 | data, 16 bytes: a DIPERIODIC with magnitude 5000, offset 0, phase 0 and period 32258 µs |
That is a 31 Hz sine wave of half strength, played for 0.42 seconds after a 51
millisecond delay. The envelope starts it at a level of 8000 and brings it down to its
magnitude of 5000 over 42 milliseconds; the fade level equals the magnitude, so the end
of the effect does not fade. The list ends at byte 0x108, the end of the
file.