Adding modding folder with a hacked build.

This commit is contained in:
ml
2026-09-26 08:53:08 +02:00
parent cdb960dc71
commit c91080aba7
755 changed files with 50424 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
## Contributing Guidelines
Thank you for considering to contribute to this documentation!
Such a contribution can be useful for modders, tool-, and engine developers.
The following text lists guidelines for contributing to this documentation.
These guidelines don't have legal status, so use them as a reference and common sense - and feel free to update them as well!
### "I just want to know..."
For questions, or general meta-information about the engine or the data files, please refer to the [systemshock.org](https://www.systemshock.org) forums.
The following sub-sections will most likely be applicable for your question:
* [Engineering](https://www.systemshock.org/index.php?board=36.0) - for modding the game or creating fan-missions in general
* [Deck 13](https://www.systemshock.org/index.php?board=13.0) - for engine source-code, ports, and tool-development related topics
* [System Shock source code](https://github.com/nothings/shockmac) - source code of System Shock for Mac OS with notes from Sean Barrett
### Scope
This documentation is meant to describe the "meaningful" resource files of the original System Shock (1994) and related engine behaviour.
It is meant to give modders and tool-developers (possibly engine developers) a reference on what data to expect.
What does "meaningful" mean? Data that has effect on the players experience. As a rule of thumb, there are several fields in various structures that are unused. Even with the source code of the engine available, it may be academic to try to document fields and their fictionial behaviour just by their name alone. If a field is never used, it may simply be stated as such and nothing more.
Exceptions are listed on the main readme - for instance, not covering music or Mac-specific files.
What does "original" mean? The game System Shock from 1994 and its direct derivatives, such as the "Enhanced Edition" or possible (future) source ports that claim to be compatible with the data files. It may be that future source ports extend the behaviour of the original engine. If this is the case, this documentation could be extended alongside, stating the compatibilities.
This documentation is NOT a documentation on the engine itself. There may be some gray areas if related to behaviour, yet you won't find a documentation on how the renderer is working for instance.
### Conventions
This documentation is meant to be read by a Markdown-compatible viewer, though for the most part, this will remain GitHub itself.
Refer to the [conventions](conventions.md) document for formatting and content.
To reiterate from this document: Please avoid adding information "on a hunch" to the documentation. Meanwhile the source code has already been released, so if there isn't a test, or a successful tool showcasing information, the documentation should at least hold up to the original source code.
It is acceptable to change field/type names if they are "near" synonyms (or simply words written in full). The documentation should aim to stay close to the original source, not a particular fan-project (not even tools from InkyBlackness - their code should follow as well.)
### Information
As a rule of thumb, this documentation describes types (data structures) with their fields, names, and related behaviour of the engine.
This is typically a combination of a dry listing of a data structure, as described in the conventions, and then some paragraphs of prose text.
It should not contain any source code - especially copied from the original source, which uses a different license. The only algorithms explained in this documentation are that of compression, and even these could be covered in prose.
### Adding/Changing the documentation
If you find typos, new information, better (more expressive) descriptions that you would like to see handled, feel free to create an issue, or possibly even a pull-request about this.
+1
View File
@@ -0,0 +1 @@
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
+32
View File
@@ -0,0 +1,32 @@
# THE UNOFFICIAL SYSTEM SHOCK 1 SPECIFICATIONS
This is a sub-project as part of the [InkyBlackness](https://inkyblackness.github.io) project. It aims to document the data (resource) files of the video game [System Shock](http://en.wikipedia.org/wiki/System_Shock). It is the continuation of the previous work performed as part of "The System Shock Hack Project" ([TSSHP](http://tsshp.sourceforge.net/)).
The goal is to document all meaningful bits and bytes of the original data files.
Start reading with the [main page](index.md) (the index).
If you would like to contribute to the documentation, please consider [the guidelines](CONTRIBUTING.md).
## Status
The content of this documentation supersedes the information available from TSSHP (the [ss-specs.txt](http://tsshp.cvs.sourceforge.net/viewvc/tsshp/tsshp/doc/ss-specs.txt?view=markup) file). In some instances, this documentation even provides more accurate descriptions.
The format of all raw media content is described, with the exception of a few unknown fields. The same counts for the game objects.
See the [issue list](https://github.com/inkyblackness/ss-specs/issues) for an overview about what is still actively analyzed.
With the release of the original source (see credits below), many so far unknown things can be clarified.
Currently not in scope of this documentation project are music files (See [here for details](other/Music.md)) and the Macintosh release.
## Credits
* This project, as well as InkyBlackness itself, wouldn't be possible without the work of TSSHP and the effort of the people before that.
* [Source release](https://github.com/NightDiveStudios/shockmac) of the Mac version, which finally allowed to figure out unknown details, and set some guesswork naming right.
## License
[![License][license-image]][license-url]
This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0/).
[license-url]: http://creativecommons.org/licenses/by-nc-sa/4.0/
[license-image]: https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png
+336
View File
@@ -0,0 +1,336 @@
## Game state and hacker information, resource 0x0FA1 (`Player` struct)
This resource contains everything about the hacker, together with general game-state information.
0000 20xbyte Hacker name (incl. 0x00 termination char). Game only allows 12 characters for input at start.
0014 byte Realspace Level; The level to return to after exiting cyberspace.
0015 4xbyte Difficulties (Game rating): combat, mission, puzzle, cyber. All 0..3
0019 3xbyte Unused.
001C uint32 Game time
0020 uint32 Last second update
0024 uint32 Last drug update
0028 uint32 Last ware update
002C uint32 Last animation check
0030 int32 Queue Time
0034 int32 Delta Time
0038 byte Detail Level
0039 byte Current level identifier
003A 22xint16 Initial SHODAN levels
0066 6xbyte Controls
006C int16 Player Object ID
006E [8]byte Realspace location; The point to return to after exiting cyberspace.
0076 int32 Version number of the data structure; Value: 6
007A [14]int16 General inventory references, Object IDs
0096 byte Posture
0097 byte Foot planted; 1: on the floor
0098 byte Lean X
0099 byte Lean Y
009A int16 Unused
009C byte Health; 0x00..0xFF
009D byte Cyberspace Integrity
009E uint16 Hitpoint regeneration per minute
00A0 8xbyte Damage rate per minute [explosion, energy, magnetic, radiation, gas, tranq, needle, bio]
00A8 uint16 Bio post expose
00AA uint16 Rad post expose
00AC byte Power 0x00..0xFF
00AD byte Power usage in JPM
00AE byte Power regeneration
00AF byte Energy out; 1: is out of energy
00B0 int16 Cyberspace trips
00B2 int32 Cyberspace time base
00B6 [64]uint8 512 Boolean game variables; Bytes are counted up, bits within byte counted from LSB; Example: bit[10] = 0x00 0x04 0x..
00F6 [64]int16 64 Integer game variables
0176 uint32 HUD modes; Bitfield as per hud.h
017A byte Unused
017B sint32 Fatigue
017F uint16 Fatigue spending; Current rate of fatigue expenditure in pts/sec
0181 uint16 Fatigue regeneration
0183 uint16 Fatigue regeneration base
0185 uint16 Fatigue regeneration maximum
0187 sint8 Accuracy
0188 byte Shield absorb rate, in percent
0189 byte Shield threshold
018A byte Light value; setting of lamp
Multi-Function-Display (MFD) state
018B [10]byte 2x5 MFD slot indices
0195 [7]byte MFD status
019C [7]byte MFD function IDs
01A3 [32]byte MFD function flags
01C3 [256]byte MFD function data
02C3 [2]byte current MFDs
02C5 [2]byte MFD empty functions
02C7 [64]byte MFD access puzzles
0307 [2]byte MFD save slots
Installed Hardware (0: not installed):
0309 byte version for infrared night vision
030A byte version for target info
030B byte version for Sensaround
030C byte version for "Aim Enhancement Hardware"
030D byte version for HUD (unknown function)
030E byte version for bioscan
030F byte version for navigation unit
0310 byte version for shield
0311 byte version for data reader
0312 byte version for lantern
0313 byte version for view control
0314 byte version for enviro suit
0315 byte version for booster
0316 byte version for jump jet
0317 byte version for system status
Installed Software(0: not installed):
0318 [7]byte offensive software
031F [3]byte defensive software
0322 [9]byte misc software
Full clips of ammunition
032B byte ML STANDARD ROUNDS
032C byte ML TEFLON COATED ROUNDS
032D byte SV NEEDLE DARTS
032E byte SV TRANQ DARTS
032F byte HOLLOW-TIP 2100 CLIP
0330 byte HEAVY SLUG 2100 CLIP
0331 byte DC RUBBER SLUGS
0332 byte MARK3 MAGNESIUM-TIP SHELLS
0333 byte MARK3 PENETRATOR SHELLS
0334 byte AM HORNET CLIP
0335 byte AM SPLINTER CLIP
0336 byte RF SLAG CLIP
0337 byte RF LARGE SLAG CLIP
0338 byte SG MAG PULSE CART
0339 byte MM RAIL CLIP
Ammunition not in full clips
033A byte ML STANDARD ROUNDS
033B byte ML TEFLON COATED ROUNDS
033C byte SV NEEDLE DARTS
033D byte SV TRANQ DARTS
033E byte HOLLOW-TIP 2100 CLIP
033F byte HEAVY SLUG 2100 CLIP
0340 byte DC RUBBER SLUGS
0341 byte MARK3 MAGNESIUM-TIP SHELLS
0342 byte MARK3 PENETRATOR SHELLS
0343 byte AM HORNET CLIP
0344 byte AM SPLINTER CLIP
0345 byte RF SLAG CLIP
0346 byte RF LARGE SLAG CLIP
0347 byte SG MAG PULSE CART
0348 byte MM RAIL CLIP
Patches
0349 byte STAMINUP STIMULANT
034A byte SIGHT VISION ENCHANCEMENT
034B byte BERSERK COMBAT BOOSTER
034C byte MEDIPATCH HEALING AGENT
034D byte REFLEX REACTION AID
034E byte GENIUS MIND-ENCHANCER
034F byte DETOX UNIVERSAL ANTIDOTE
Explosives
0350 byte FRAGMENTATION GRENADE
0351 byte EMP GRENADE
0352 byte GAS GRENADE
0353 byte CONCUSSION BOMB
0354 byte LAND MINE
0355 byte NITROPACK
0356 byte EARTH SHAKER EXPLOSIVE
0357 [294]byte Message status bitfield; 47 emails + 23 data fragments + (16 logs * 14 levels)
047D [14]byte Level log marker; On which level logs are in inventory
048B [5]byte First weapon
+0 byte Subclass (0xFF means no weapon)
+1 byte Type
+2 byte Rounds / Heat
+3 byte Ammunition type / Energy setting (0x80 bit is overload)
+4 byte Unused
0490 [5]byte Second weapon
0495 [5]byte Third weapon
049A [5]byte Fourth weapon
049F [5]byte Fifth weapon
04A4 [5]byte Sixth weapon
04A9 [5]byte Seventh weapon
04AE [15]byte Hardware on/off
04BD [19]byte Software status on/off
04D0 byte Jumpjet energy fraction
04D1 [32]byte email sender count
04F1 [7]byte Patches time remainder
04F8 [7]byte Patches intensity
04FF [7]uint16 Timer settings of explosives, in 0.1 seconds; Default: 0x46 = 7 seconds
050D uint16 Unused
050F uint16 Time to completion of current program (seconds). Essentially unused, although used to print some strings
0511 uint16 ObjectID of current target
0513 uint32 Last weapon fire time
0517 uint16 Weapon fire rate; Time needed before next fire.
0519 [10]byte HUD actives: weapon, grenade, drug, cart, hardware, combat soft, defense soft, misc soft, general, email
0523 uint16 Object under cursor before cyberspace
0525 uint16 Last panel reference
0527 sint32 Kill count
052B sint32 Time in cyberspace
052F sint32 Number of shots fired
0533 sint32 Number of hits
0537 sint32 Death count; How often hacker was resurrected.
053B int32 Tilt (eye position)
053F [12]fix Physics state (EDMS) - see below
056F byte Currently active inventory category
0570 byte Active bio tracks
0571 int16 current email
0573 [6]byte version
0579 byte dead flag
057A uint16 lean filter state
057C [2]byte unused
057E byte MFD save field
057F uint32 Auto fire click
0583 uint32 Posture state
0587 byte Option: Text Length (0: normal, 1: terse)
0588 uint32 Last head bob
058C [9]byte Padding
> In archive.dat, this table is (nearly) all 0x00, and also slightly smaller than necessary.
### Physics state (EDMS `State` structure, 48 bytes)
0000 fix X Coordinate (East -> West)
0004 fix Y Coordinate (North -> South)
0008 fix Z Coordinate (above ground)
000C fix Yaw; 0 is facing East, going counter-clockwise
0010 fix Upper body back force (?)
0014 fix Upper body left force (?)
0018 fix Forward force (?) -- X dot product
001C fix Left force (?) -- Y dot product
0020 fix Unknown -- Z dot product
0024 fix Unknown
0028 fix Unknown
002C fix Unknown
Rotation values are in range 0..2PI.
### Message status enumeration (1 byte)
0x80 Message received
0x40 Message read
0x3F Unused
### Game variables
Game variables are described on a [separate page](gameVariables.md)
### Initial game state
The original engine does not allow for having `archive.dat` specify the initial state for both the game and hacker.
The `System Shock Enhanced Edition Source Port` introduced a modified start behaviour,
which allows for (almost) full control on how and where the protagonist starts:
When the first field of the `physics state` (the `X` coordinate of EDMS state) is not zero,
then most default settings are skipped, and data is taken from `archive.dat`.
Still, some minimal setup is performed either way, in order to have a stable game.
> This behaviour exists already in the original engine, but it only considers the physics structure itself,
> and no further information, such as the current level or any game variable.
#### Mission-independent hardcoded defaults
The following defaults are being applied regardless of existing data in `archive.dat`.
##### Game state fields
| Offset | Name | Initial value |
|--------|----------------------------|-----------------------------------------------------|
| 0000 | Hacker name | as per start menu |
| 0015 | Difficulties | as per start menu |
| 0038 | Detail Level | as per configuration |
| 003A | Initial SHODAN levels | all to `-1` |
| 006C | Player Object ID | `0` |
| 00A0 | Damage rate per minute | all to `0` |
| 0511 | ObjectID of current target | `0` |
| 0513 | Last weapon fire time | `0` |
| ** | Various MFD fields | To a consistent (empty) MFD display |
| 0309 | Installed Hardware[10] | `1` to hardcode "fullscreen hardware" |
| 0570 | Active bio tracks | `0xFF` |
| 00F6 | Integer game variables[48] | Language, as per configuration |
The two "random" game variables (index 31 and 32) are randomized only if they are equal.
#### Mission-dependent defaults
The following defaults are being applied *only* if `physics state` field `X` is zero.
##### Game state fields
| Offset | Name | Initial value |
|--------|----------------------------|-----------------------------------------------------|
| 0039 | Current level identifier | `1` |
| 007A | General Inventory | all to `0` |
| 009C | Health | `212` |
| 009D | Cyberspace Integrity | `255` |
| 009E | Hitpoint regeneration | `0` |
| 00AC | Power | `255` |
| 00B2 | Cyberspace time base | `504000` (30 minutes in 280 ticks-per-second) |
| 0181 | Fatigue regeneration | `0` |
| 0183 | Fatigue regeneration base | `100` |
| 0183 | Fatigue regeneration max | `400` |
| 0187 | Accuracy | `100` |
| 0188 | Shield absorb rate | `0` |
| 0357 | Message status[26] | `0x80`, to mark email from Rebecca received |
| 048B | Weapons[*].type | `0xFF`, to mark no weapon in slot |
| 04FF | Explosives timer[*] | `70` (= 7 seconds) |
| 0519 | HUD actives[9] | `0xFF`, to mark email being active |
| 053F | Physics state (EDMS) | X=30.5, Y=22.5, Z=planted at floor, Alpha=West |
##### Variables
The following integer variables are set:
| Integer | Value |
|---------|--------|
| 3 | 2 |
| 12 | 3 |
| 51 | 256 |
And the following boolean variables are set to `true`:
```
0x001, 0x002, 0x003, 0x010, 0x012, 0x015, 0x016, 0x017, 0x018, 0x019, 0x01A,
0x020, 0x021, 0x024, 0x025,
0x04B, 0x04C, 0x04D, 0x04E, 0x04F,
0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058, 0x059, 0x05A, 0x05B, 0x05C, 0x05D, 0x05E, 0x05F,
0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07A, 0x07B, 0x07C, 0x07D, 0x07E, 0x07F,
0x0A0, 0x0A1, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9,
0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7, 0x0C8, 0x0C9, 0x0CA, 0x0CB, 0x0CC, 0x0CD, 0x0CE, 0x0CF,
0x0E1, 0x0E3, 0x0E5, 0x0E7, 0x0E9, 0x0EB, 0x0ED, 0x0EF,
0x0F1, 0x0F3, 0x0F5, 0x0F7, 0x0F9, 0x0FB, 0x0FD, 0x0FF,
0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F,
0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F,
0x121, 0x123, 0x125, 0x127, 0x129, 0x12B,
```
+208
View File
@@ -0,0 +1,208 @@
## Game variables
This page lists all game variables with their index, their initital value, and their usage in the original game.
> At the moment not all variables have been catalogued. Only the initial values have been determined.
### Boolean variables
| Index | Initial Value | Usage |
|------:|--------------:|----------------------------------------------------------------------------------------------|
| 0 | 0 | Must always be 0, otherwise default conditions may break |
| 1 | 1 | Must always be 1, otherwise some locks may fail |
| 2 | 1 | Unknown |
| 3 | 1 | Unknown |
| 6 | 0 | Shield status (+ System Analyzer) |
| 8 | 0 | Laser status (+ System Analyzer); 0: charging, 1: destroyed |
| 10 | 0 | Delta grove launch enable (+ System Analyzer) |
| 11 | 0 | Alpha grove launch enable (+ System Analyzer) |
| 12 | 0 | Beta grove launch enable (+ System Analyzer) |
| 13 | 0 | Relay 428 report status; 0: functioning, 1: faulty; Set by main switch, checked by diag. |
| 14 | 0 | Relay 428 repair status; 0: faulty, 1: fixed; Set by panel, checked by main switch. |
| 15 | 0 | Beta grove jettisoned (+ System Analyzer) |
| 16 | 1 | Unknown |
| 18 | 1 | Unknown |
| 20 | 0 | Reactor on destruct, pods enabled; Quakes (+ System Analyzer) |
| 21 | 1 | Unknown |
| 22 | 1 | Unknown |
| 23 | 1 | Unknown |
| 24 | 1 | Unknown |
| 25 | 1 | Unknown |
| 26 | 1 | Medical armory lock on doors in level 1 |
| 30 | 0 | Switch to open armory doors on level 1 via cyberspace level 14 |
| 32 | 1 | Unknown |
| 33 | 1 | Unknown |
| 36 | 1 | Unknown |
| 37 | 1 | Unknown |
| 75 | 1 | Unknown |
| 76 | 1 | Unknown |
| 77 | 1 | Unknown |
| 78 | 1 | Unknown |
| 79 | 1 | Unknown |
| 80 | 1 | Unknown |
| 81 | 1 | Unknown |
| 82 | 1 | Unknown |
| 83 | 1 | Unknown |
| 84 | 1 | Unknown |
| 85 | 1 | Unknown |
| 86 | 1 | Unknown |
| 87 | 1 | Unknown |
| 88 | 1 | Unknown |
| 89 | 1 | Unknown |
| 90 | 1 | Unknown |
| 91 | 1 | Unknown |
| 92 | 1 | Unknown |
| 93 | 1 | Unknown |
| 94 | 1 | Unknown |
| 95 | 1 | Unknown |
| 112 | 1 | Unknown |
| 113 | 1 | Unknown |
| 114 | 1 | Unknown |
| 115 | 1 | Unknown |
| 116 | 1 | Unknown |
| 117 | 1 | Unknown |
| 118 | 1 | Unknown |
| 119 | 1 | Unknown |
| 120 | 1 | Unknown |
| 121 | 1 | Unknown |
| 122 | 1 | Unknown |
| 123 | 1 | Unknown |
| 124 | 1 | Unknown |
| 125 | 1 | Unknown |
| 126 | 1 | Unknown |
| 127 | 1 | Unknown |
| 145 | 0 | "Online" help (tooltips); 0: show, 1: off |
| 160 | 1 | Unknown |
| 161 | 1 | Unknown |
| 162 | 1 | Unknown |
| 163 | 1 | Unknown |
| 164 | 1 | Unknown |
| 165 | 1 | Unknown |
| 166 | 1 | Unknown |
| 167 | 1 | Unknown |
| 168 | 1 | Unknown |
| 169 | 1 | Unknown |
| 192 | 1 | Unknown |
| 193 | 1 | Unknown |
| 194 | 1 | Unknown |
| 195 | 1 | Unknown |
| 196 | 1 | Unknown |
| 197 | 1 | Unknown |
| 198 | 1 | Unknown |
| 199 | 1 | Unknown |
| 200 | 1 | Unknown |
| 201 | 1 | Unknown |
| 202 | 1 | Unknown |
| 203 | 1 | Unknown |
| 204 | 1 | Unknown |
| 205 | 1 | Unknown |
| 206 | 1 | Unknown |
| 207 | 1 | Unknown |
| 225 | 1 | Unknown |
| 227 | 1 | Unknown |
| 229 | 1 | Unknown |
| 231 | 1 | Unknown |
| 233 | 1 | Unknown |
| 235 | 1 | Unknown |
| 237 | 1 | Unknown |
| 239 | 1 | Unknown |
| 241 | 1 | Unknown |
| 243 | 1 | Unknown |
| 245 | 1 | Unknown |
| 247 | 1 | Unknown |
| 249 | 1 | Unknown |
| 251 | 1 | Unknown |
| 253 | 1 | Unknown |
| 255 | 1 | Unknown |
| 257 | 1 | Unknown |
| 259 | 1 | Unknown |
| 261 | 1 | Unknown |
| 263 | 1 | Unknown |
| 265 | 1 | Unknown |
| 267 | 1 | Unknown |
| 269 | 1 | Unknown |
| 271 | 1 | Unknown |
| 273 | 1 | Unknown |
| 275 | 1 | Unknown |
| 277 | 1 | Unknown |
| 279 | 1 | Unknown |
| 281 | 1 | Unknown |
| 283 | 1 | Unknown |
| 285 | 1 | Unknown |
| 287 | 1 | Unknown |
| 289 | 1 | Unknown |
| 291 | 1 | Unknown |
| 293 | 1 | Unknown |
| 295 | 1 | Unknown |
| 297 | 1 | Unknown |
| 299 | 1 | Unknown |
| 300 | 0 | Set to 1 while the data reader icon is flashing & beeping |
### Integer variables
| Index | Initial Value | Usage |
|------:|--------------:|---------------------------------------------------------|
| 1 | 0 | Incremented for each destroyed computer node lvls 1-7 |
| 3 | 2 | Engine state |
| 9 | 0 | Incremented by 1 for laser, beta grove, reactor |
| 12 | 3 | Number of groves |
| 13 | Mission value | Unknown |
| 14 | Cyber value | Unknown |
| 15 | Combat value | Unknown |
| 16 | L00 security | Security value of level |
| 17 | L01 security | Security value of level |
| 18 | L02 security | Security value of level |
| 19 | L03 security | Security value of level |
| 20 | L04 security | Security value of level |
| 21 | L05 security | Security value of level |
| 22 | L06 security | Security value of level |
| 23 | L07 security | Security value of level |
| 24 | L08 security | Security value of level |
| 25 | L09 security | Security value of level |
| 26 | L10 security | Security value of level |
| 27 | L11 security | Security value of level |
| 28 | L12 security | Security value of level |
| 29 | L13 security | Security value of level |
| 30 | Puzzle value | Unknown |
| 31 | random | Reactor code 1 (3-digit BCD) |
| 32 | random | Reactor code 2 (3-digit BCD) |
| 33 | 0 | Destroyed comp. nodes on lvl1 before showing digit |
| 34 | 0 | Destroyed comp. nodes on lvl2 before showing digit |
| 35 | 0 | Destroyed comp. nodes on lvl3 before showing digit |
| 36 | 0 | Destroyed comp. nodes on lvl4 before showing digit |
| 37 | 0 | Destroyed comp. nodes on lvl5 before showing digit |
| 38 | 0 | Destroyed comp. nodes on lvl6 before showing digit |
| 39 | 0 | Destroyed cyberguards in large cspace / open barrier |
| 40 | | Unknown |
| 41 | as per config | Audio music volume (0x00 .. 0x64) |
| 42 | 0x4A3D | Video gamma |
| 43 | as per config | Audio digital FX volume (0x00 .. 0x64) |
| 44 | as per config | Mouse handedness (Right: 0, Left: 1) |
| 45 | | Unknown |
| 46 | | Unknown |
| 47 | 0x5555 | Double click time |
| 48 | as per config | Selected language (0: ENG, 1: FRA, 2: GER) |
| 49 | as per config | Audio message volume (0x00 .. 0x64) |
| 50 | as per config | Video resolution |
| 51 | 256 | Joystick sensitivity |
| 52 | | Unknown |
| 53 | as per config | Audio messages (0: Text, 1: Speech, 2: Both) |
| 54 | | Unknown |
| 55 | | Unknown |
| 56 | | Unknown |
| 57 | | Unknown |
| 58 | as per config | Audio channels (0: 2ch, 1: 4ch, 2: 8ch) |
| 59 | | Unknown |
| 60 | | Unknown |
| 61 | | Unknown |
| 62 | | Unknown |
| 63 | | Unknown |
The two random codes (index 31, 32) will be randomized if they are equal.
> The two random codes are stored in [Binary-Coded-Decimal](https://en.wikipedia.org/wiki/Binary-coded_decimal).
> The integer values from index 40 on seem to be all about in-game options.
> This may be the reason why it was earlier thought that there are only 40 game variables.
+80
View File
@@ -0,0 +1,80 @@
## Archives
This section covers the archive files. There is one template archive file, called ```archive.dat```, and a number of save-game files, called ```savgamXX.dat```. All of these files are regular resource files.
Any new game is based on the archive.dat file. All these archive files contain the complete game state, including the level layout. In other words: Citadel itself, with all its content, is represented through such a file; Properties and media for the content is in the other resource files.
## Map specific data
Each level has 98 resources reserved, but only 51 in use. The level-specific resources are grouped according to the level identifier. For example, on Citadel level R, the resources start with ID ```4002``` (decimal) up to ```4053```. Level 1 uses ```4102``` to ```4153``` and so forth.
On the following pages, resources identified as ```LXX``` refer to level-specific resource ```XX``` for any level. These numbers will always be given in decimal.
## Extra Resources
### Name of archive, resource 0x0FA0 (4000)
This resource contains the name of the archive (title of the save-game). It is a 0x00 terminated string, with a dynamic length of up to 32 bytes (including the termination character).
> The archive.dat file contains extra (unknown) data after the name in this resource, up to a complete length of 128 bytes. A save-game file has only the name in this resource. The name of the template archive is "Starting Game".
### [Game state resource 0x0FA1 (4001)](gameState.md)
This resource contains information about the game and the hacker.
It also contains the [level variables](levelVariables.md).
### Additional Resources
Save-game files contain 4 further resources that are not present in archive.dat :
* 0x0000 Unknown int32. ```0x00000000``` at first investigation
* 0x024E Unknown.
* 0x024F Unknown.
* 0x0F9F Unknown int32. ```0x00004444``` at first investigation
## Index
### By topic
* [Map Information](mapInformation.md)
* [Level Objects](levelObjects.md)
* [Surveillance Cameras](surveillanceCameras.md)
* [Loop Configuration](loopConfiguration.md)
### By level-specific resource ID
* ```L02``` Map version number. int32 with value ```0x0000000B``` for all maps.
* ```L03``` Object version number. int32 with value ```0x0000001B``` for all maps.
* ```L04``` [Basic level information](mapInformation.md)
* ```L05``` [Map Layout](mapInformation.md)
* ```L06``` [Level schedules](levelSchedules.md)
* ```L07``` [Level texture map](mapInformation.md)
* ```L08``` [Main object table](levelObjects.md#level-object-table)
* ```L09``` [Level object cross-reference table](levelObjects.md#level-object-cross-reference-table)
* ```L10``` to ```L24``` 15 object [class specific tables](levelObjects.md#class-tables-and-entries)
* ```L25``` to ```L39``` 15 object [class default info](levelObjects.md#class-default-information)
* ```L40``` Miscellaneous savefile version number. int32. For all maps: ```CD-Release```: ```0x0000000D```. All other: ```0x0000000B```
* ```L41``` Unused entry. byte with value ```0x00``` for all maps.
* ```L42``` [Texture animation](textureAnimation.md)
* ```L43``` [Surveillance Sources](surveillanceCameras.md)
* ```L44``` [Surveillance Surrogates](surveillanceCameras.md)
* ```L45``` [Level variables](levelVariables.md)
* ```L46``` [Map notes](mapInformation.md#map-notes)
* ```L47``` [Map notes pointer](mapInformation.md#map-notes)
* ```L48``` Unknown. 0x30 byte length.
* ```L49``` Unknown. 0x1C0 byte length.
* ```L50``` Unknown int16. ```0x0000``` for all maps.
* ```L51``` [Loop Configuration](loopConfiguration.md)
* ```L52``` Unknown int16. (Only present in ```CD-Release```)
* ```L53``` Unknown. 0x40 byte length, all zeroes. (Only present in ```CD-Release```)
### Levels of Citadel
This is the level list of the base game:
* Level 00: Reactor level
* Level 01-09: Levels 1-9
* Level 10: SHODAN cyberspace
* Level 11: Delta grove
* Level 12: Alpha grove
* Level 13: Beta grove
* Level 14: Cyberspace for levels 1 and 2
* Level 15: Cyberspace
+175
View File
@@ -0,0 +1,175 @@
## Level Objects
Instance data of [Level Objects](../levelObjects/index.md) is stored as part of an archive. Two resources describe general information, then further resources are object class specific.
### General Information
#### Level Object Table
```L08```, a compressed resource, contains a table with general entries about the objects in the current level.
This table contains 872 entries, which houses two lists.
**Level Object Entry** (`Obj` struct) (27 bytes)
0000 byte In use flag. 0: free, 1: in use
0001 byte Object class
0002 byte Object subclass
0003 int16 Class table index
0005 int16 Cross-Reference table index (also: used list head)
0007 int16 Next
0009 int16 Previous
000B uint16 X Coordinate
000D uint16 Y Coordinate
000F byte Z Coordinate (height)
0010 byte Pitch rotation (around X axis), increasing values turn upwards
0011 byte Yaw/Heading rotation (around Z axis), increasing values turn rightwards
0012 byte Roll/Bank rotation (around Y axis), increasing values turn rightwards
0013 byte Physics (0xFF for unaffected, the default)
0014 byte Object type
0015 sint16 Hitpoints
0017 [4]byte Extra
The starting point for both lists is the first entry (index zero). This entry is not in use (```In use flag``` is zero).
The `Cross-Reference table index` field of the starting point refers to the first object of a list of objects in use. For all object entries in use,
their ```Previous``` and ```Next``` fields form a double-linked list, with the exception of the starting point:
The last used object entry refers to the starting point with its ```Next``` value without being referred-to from the starting point.
The `Next` index of the starting point refers to the first object of a list of objects not in use.
For all object entries not in use, their `Next` field forms a single-linked list.
The `Previous` field of unused objects is not defined and contains arbitrary data.
The last unused object entry refers to the starting point with its ```Next``` value.
If an object is placed in the world, it has the X, Y and Z coordinates set, as well as the ```Cross-Reference table index``` pointing to a corresponding entry in the cross-reference table.
If an object was spawned by the engine without a location in the world, such as loot from corpses,
then the ```X``` field is ```0xFFFF```, ```Y``` and ```Z``` fields are zero, and the ```Cross-Reference table index``` is zero as well.
The ```extra``` field is a multi-purpose field, which may hold further properties and/or state. The exact features are environment specific and may also be dependent on the classification of the object.
##### Global extra
For some objects, the ```extra``` field is dependent on the object type or class, and the field is applicable to both real world and cyberspace.
**Basic Extra** (4 bytes)
0000 byte Make Info - a general index
0001 byte Current Frame - for animations
0002 byte Time Remainder - for animations
0003 byte Instance Flags - for various purposes
This is the layout as defined by the engine. Yet, these values may be interpreted/used specifically according to type and/or environment.
**Panel Extra** (4 bytes)
0000 byte Panel name index; Index into cybstrng 0x0869
0001 [3]byte Other
If the ```Panel name``` resolves to an unknown block or empty string, then the regular object name is displayed.
**Surfaces Extra* (4 bytes)
0000 byte Unknown
0001 byte Bitmap Index
0002 [2]byte Unknown
This is used for simple surfaces, such as icons (`7/2/1`) and paintings (`7/2/4`).
##### Cyberspace extra
In case the level is cyberspace, the ```extra``` field contains the following properties.
**Cyberspace Level Object Extra** (4 bytes)
0000 byte Other
0001 byte ICE presence
0002 byte Unknown
0003 byte ICE level
Level objects in cyberspace can be locked by ICE (Intrusion Countermeasures Electronics). ICE need to be defeated using the drill tool before the locked object can be acquired/used. The ```ICE presence``` field determines whether ICE is present. Any value != ```0x00``` has ICE present.
If present, the health of the ICE is determined by the health of the object.
The ```ICE level``` then determines how aggressive and powerful the ICE is. ```0x00``` is the weakest, ```0xFF``` is the highest.
> Although the vanilla game uses ICE only on software objects, the engine also supports ICE on other objects, such as cyberspace exits.
#### Level Object Cross-Reference Table
```L09```, a compressed resource, is a table that links level objects and the tiles they are in. The index field in the [Tile Map Entry](mapInformation.md) refers to this table, as well as the table index field in the **Level Object Entry**. This table contains 1600 entries.
**Level Object Cross-Reference Entry** (10 bytes)
0000 int16 Tile X
0002 int16 Tile Y
0004 int16 Index into level object entry table
0006 int16 Index for next object
0008 int16 Index for next tile of same object
Objects can extend over several tiles. The last field points to the next reference entry about the same object, with different tile coordinates.
This field creates a single-linked list, with the last entry pointing to the first one.
If the object is contained within one tile only, this field points to its own entry.
The major coordinates of the main object entry match those of the last entry of the cross-reference list. The cross-reference list only contains the X/Y values of the tiles - not sub-tile coordinates.
The ```Index for next object``` field creates a single-linked list, terminating with a zero value.
This list either refers to all objects within the same tile (if referred to from a tile), or creates a pool of unused cross-reference entries. The start of this pool is the first entry (index zero).
Unused cross-reference entries contain arbitrary data and only the next object index field is valid.
### Class Specific Information
In the following resources, all tables have a static count of entries. Each entry has a common prefix.
**Level Object Prefix** (6 bytes)
0000 int16 Index into level object entry table
0002 int16 Previous
0004 int16 Next
The tables contain two lists each, similar to the main object table:
The starting point is the first entry (index zero). The starting point refers to a double-linked list of used entries (via ```Next```), and a single-linked list of unused entries (via ```Previous```). Both lists are terminated by referring back to index zero.
The ```Index``` field of the starting point does not refer to a level object, it is the index within the same table of the last used entry. (The actual "previous" link to the double-linked list of used objects.)
> The sum of all possible entries is slightly higher than the total count of possible **Level Object Entries**.
#### Class Tables and Entries
The following table lists the the level resources per class type. It also lists how many entries each table contains and how big one entry is.
The name column links to the corresponding details.
| Class | Level Res | Name | Entry Count | Entry Size (incl. prefix) |
|:-----:|-------------|-------------------------------------------|-------------|---------------------------|
| 0 | ```L10``` | [Gun][L10] | 16 | 8 |
| 1 | ```L11``` | [Ammo][L11] | 32 | 6 |
| 2 | ```L12``` | Physics | 32 | 40 |
| 3 | ```L13``` | [Grenade][L13] | 32 | 12 |
| 4 | ```L14``` | [Drug][L14] | 32 | 6 |
| 5 | ```L15``` | [Hardware][L15] | 8 | 7 |
| 6 | ```L16``` | [Software][L16] | 16 | 9 |
| 7 | ```L17``` | [Big Stuff][L17] | 176 | 16 |
| 8 | ```L18``` | [Small Stuff][L18] | 128 | 16 |
| 9 | ```L19``` | [Fixture][L19] | 64 | 30 |
| 10 | ```L20``` | [Door][L20] | 64 | 14 |
| 11 | ```L21``` | Animating | 32 | 10 |
| 12 | ```L22``` | [Trap][L22] | 160 | 28 |
| 13 | ```L23``` | [Container][L23] | 64 | 21 |
| 14 | ```L24``` | [Critter][L24] | 64 | 46 |
[L10]: ../levelObjects/00_Gun/levelGunEntry.md
[L11]: ../levelObjects/01_Ammo/levelAmmoEntry.md
[L13]: ../levelObjects/03_Grenade/levelGrenadeEntry.md
[L14]: ../levelObjects/04_Drug/levelDrugEntry.md
[L15]: ../levelObjects/05_Hardware/levelHardwareEntry.md
[L16]: ../levelObjects/06_Software/levelSoftwareEntry.md
[L17]: ../levelObjects/07_BigStuff/levelBigStuffEntry.md
[L18]: ../levelObjects/08_SmallStuff/levelSmallStuffEntry.md
[L19]: ../levelObjects/09_Fixture/levelFixtureEntry.md
[L20]: ../levelObjects/10_Door/levelDoorEntry.md
[L22]: ../levelObjects/12_Trap/levelTrapEntry.md
[L23]: ../levelObjects/13_Container/levelContainerEntry.md
[L24]: ../levelObjects/14_Critter/levelCritterEntry.md
### Class Default Information
Each level has 15 further resources, ```L25``` to ```L39```, one for each object class. The length of these resources is the same as for one class entry of the corresponding specific entries.
This information is used as default for creating new objects. Most of the time, all their bytes are 0x00.
+119
View File
@@ -0,0 +1,119 @@
## Level Schedules
```L06``` is a dynamically sized list of currently active schedules. A schedule performs some delayed action at a specific time in the future.
It contains a number of live entries equal to the ```timer count``` field from ```L04``` [(Level Information)](mapInformation.md#Level-information).
Unless the list is full, this list always has one extra "terminator" entry at the end.
**Level Schedule Entry** (`SchedEvent` struct) (8 bytes)
0000 int16 Timestamp, with a scale of 0x100 = 10 seconds
0002 int16 Type
0004 byte[4] Data
```Time``` specifies the timestamp accoring to in-game time. Should the level become activated (either through loading of savegame load or level transition) and the current time is beyond the trigger time, the object is immediately triggered.
```Type``` specifies how the schedule should be handled on timeout. ```Data``` is then type-specific.
> The one terminator entry is required to exist, and does not have to contain any useful information at the beginning (can be all zero).
> The engine sorts the schedule entries according to their ```trigger time```.
For the schedule data, any object index refers to the main object table in the level.
### Schedule Type 0: Null Schedule Event
This event does nothing.
### Schedule Type 1: Grenade Schedule Event (`GrenSchedEvent` struct)
**Grenade Schedule Event Data** (4 bytes)
0000 int16 Grenade object index
0002 byte Unique identifier
0003 byte Unused
The ```unique identifier``` must match the value from the (live) grenade object in the level to be handled.
This event removes the grenade object on timeout.
### Schedule Type 2: Explosion Schedule Event
This event does nothing.
### Schedule Type 3: Door Schedule Event (`DoorSchedEvent` struct)
**Door Schedule Event Data** (4 bytes)
0000 int16 Object index
0002 uint16 Unique identifier
If the ```object index``` references an open door, then this door will be triggered to close.
If the ```object index``` references a "plastiqued antenna relay", then this event causes the explosion and the special handling of these fixtures.
### Schedule Type 4: Trap Schedule Event (`TrapSchedEvent` struct)
**Trap Schedule Event Data** (4 bytes)
0000 int16 Target object index
0002 int16 Source object index
Triggers the ```target object``` first (without condition check).
If ```source object``` is not -1, it is attempted next, including condition checks.
### Schedule Type 5: Exposure Schedule Event
**Exposure Schedule Event Data** (`SchedExposeData` struct) (4 bytes)
0000 sint8 Damage
0001 uint8 Type
0002 uint8 TSecs
0003 uint8 Count -- unused
This event exposes ```type``` to the hacker for half of the amount given in ```damage```. Damage values are rounded towards the greater number.
If the calculated half is not equal to the ```damage``` value, then another event is re-scheduled with an offset of ```tSecs```.
> An earlier version of the engine appeared to use ```count``` as a counter towards zero, though this was replaced with the damage-halving algorithm.
### Schedule Types 6, 7: Floor, Ceiling Schedule Events (`HeightSchedEvent` struct)
These events share the same event data and algorithm.
**Height Schedule Event Data** (4 bytes)
0000 sint8 Semaphor
0001 sint8 Key
0002 sint8 Steps Remaining
0003 sint8 SFX Code -- unused
This event references the height transition referenced by ```semaphor``` (stored in ```L53```) which has to have given ```key``` to be valid.
```Steps remaining``` indicates both the direction, as well as how many further steps to take. When this counter reaches zero, movement stops.
### Schedule Type 8: Light Schedule Event
This event has no specific data.
It turns off any muzzle light source and restores the state of the lamp hardware.
### Schedule Type 9: Bark Schedule Event (`BarkSchedEvent` struct)
This event has no specific data.
It is scheduled by specific ```trap message``` actions (action type 22) to automatically remove the side-image after a certain time from the side-MFD.
### Schedule Type 10: EMail Schedule Event (`EmailSchedEvent` struct)
**EMail Schedule Event Data** (4 bytes)
0000 sint16 Data Munge (EMail index)
0002 byte[2] Unused
This event is the delayed action of ```receive email``` actions (action type 15). This event is used if the action has a delay higher than zero.
@@ -0,0 +1,36 @@
## Level Variables
```L45``` contains level variables, including environmental settings.
**Level Variables** (94 bytes)
0000 int16 Data Length. = 94
0002 byte Mist; Unused (0x00)
0003 byte Gravity; Unused (0x00)
0004 byte Radiation poisoning level, in 0.5 LBP
0005 byte Gravity, in 25% OR bio contamination level, in 0.5 LBP
0006 byte Gravity switch. If 0, ```0005``` is bio contamination. Gravity mod otherwise.
0007 byte Bio contamination register. Must be >1 to have contamination to register
0008 byte Radiation register. Must be >1 to have radiation to register
0009 uint32 Exit Time; Unused in savegame
000D [3*27]byte 3 Automap Variables entries, for first, second, full-screen (see below)
> Hazards are notified from value 0x01, but affect only starting from value 0x02 (1 LBP).
**Automap Variables** (27 bytes)
0000 byte Initialized.
0001 byte Zoom; 1 (farthest) .. 5 (nearest)
0002 sint32 X-coordinate; (as fix32 value)
0006 sint32 Y-coordinate; (as fix32 value)
000A uint16 Unknown
000C uint16 Unknown
000E uint16 Follow object; The object to focus on; Always 0x0001 (hacker)
0010 uint16 Sensor object
0012 uint16 Note object
0014 uint16 Flags
0016 uint16 Available flags
0018 byte Version ID
0019 uint16 Sensor radius; Scan radius, in tiles and sub-tiles
@@ -0,0 +1,24 @@
## Loop Configuration
```L51``` is a table containing loop configuration. 64 entries are stored in this table, with unused entries set to 0x00.
> This table appears to be automatically filled by displaying objects, such as screens and the like.
> Although ```archive.dat``` contains valid information in this table, it is rewritten by the information from the level object properties.
**LoopConfigEntry** (15 bytes)
0000 int16 Object Table Index
0002 byte Loop type
0003 [10]byte Unused
000D int16 Frame time -- 0x0100 is 0.9 seconds; Default: 0x0080
**Loop Type**
0 Don't loop, stick with first frame
1 Loop forward
2 Play once (on finish, stick with last frame)
3 Loop backward
4 Loop back and forth
5 Loop back and forth
6 Loop back and forth
7 Loop back and forth
+252
View File
@@ -0,0 +1,252 @@
## Map Information
### Map layout
#### Level information
```L04```, a compressed resource, contains the basic information about a level (a map).
> In the original source, this structure is called ```FullMap```.
**Level Information** (58 bytes)
0000 sint32 X Size - always 64 in the archives.
0004 sint32 Y Size - always 64 in the archives.
0008 sint32 X Shift - always 6 in the archives.
000C sint32 Y Shift - always 6 in the archives.
0010 sint32 Z Shift
0014 byte[4] Placeholder - ignore
0018 byte Cyberspace flag. 0: real life, 1: cyberspace
0019 byte[12] Unused
0025 byte[21] Scheduler Info
> The engine doesn't support maps with more than 4096 tiles. By default, this is a map of 64x64 tiles.
> While other combinations of X/Y sizes are feasible, as long as the respective shift parameter matches, there is
> hardly a point to try something different than 64x64.
The ```z shift``` determines the height of the level as well as the size of one height unit for tiles.
The level is one tile width high (perfect cube) if the shift value is 5.
A shift value of 0 gives 32 tiles of height, a value of 7 one fourth (1/4) of a tile. So it's 32, 16, 8, 4, 2, 1, 1/2, 1/4 for 0..7.
> Most levels have a shift value of 3, giving 4 tile widths of height. Security (Level 8) has a shift value of 1 - 16 tiles.
One height unit is the 32th of the level height. The higher the level is, the larger are the visible jumps of any height value.
Although any level can be marked as cyberspace - even the starting level - only levels 10, 14 and 15 properly work.
This is due to the engine considering 10 and everything above 13 to be cyberspace.
Cyberspace items in other levels don't work as expected, especially the exit port either crashes the game or puts the player at the start of the cyberspace level again.
As a further hardcoded limitation, only cyberspace levels 14 and 15 have a time limit. Level 10 is without limit.
The ```placeholder``` has no use in the archives. In the engine, this field is used as a placeholder for a pointer within the game logic.
The ```schedule``` information is the primary entry for level-specific timers.
**Level Scheduler Information** (21 bytes)
0000 sint32 Size - always 64 in the archives
0004 sint32 Schedule count
0008 sint32 Element size - always 8 in the archives
000C byte Grow - always 0 in the archives
000D byte[4] Pointer1
0011 byte[4] Pointer2
```Size``` and ```element size``` require to be set to the values ```0x40``` and ```0x08``` respectively. Otherwise the level timers (stored in ```L06```) will not work properly.
The ```element size``` of 8 comes from the size of scheduler entries.
```Grow```, a boolean, would indicate to the scheduler system to allow reallocating the timer list to contain more than ```size``` amount of entries. Yet, the engine always initializes this with 0 (= don't grow).
The two pointer fields are used engine-internally.
#### Tile map
The tiles are described in the compressed resource ```L05```, a table of tile map entries. The first entry describes the lower left tile and the next entries go east first, then north (first columns, then rows). All levels have a size of 64x64 tiles as the engine supports (and expects) only this layout.
The outermost tiles of the level (the 'border') should always be solid tiles. If these tiles are non-solid, the graphics engine may have rendering issues (walls not shown, just black) and entering these tiles freezes the game. This gives an effective available space of 62x62 tiles.
**Tile Map Entry** (16 bytes)
0000 byte Type
0001 byte Floor Info
0002 byte Ceiling Info
0003 byte Slope height. Valid for any non-flat tile.
0004 sint16 Index to first object in cross-reference table.
0006 uint16 Texture Info
0008 uint32 Flags
000C byte Sub Clip -- always 0xFF
000D byte Clear Solid -- unknown
000E byte Unused
000F byte Light delta
**Type** (1 byte)
The type is an enumeration with the following constants:
0x00 solid
0x01 open
0x02 diagonal open s/e (nw edge closed off)
0x03 diagonal open s/w (ne edge closed off)
0x04 diagonal open n/w (se edge closed off)
0x05 diagonal open n/e (sw edge closed off)
0x06 slope s->n
0x07 slope w->e
0x08 slope n->s
0x09 slope e->w
0x0A valley se->nw
0x0B valley sw->ne
0x0C valley nw->se
0x0D valley ne->sw
0x0E ridge nw->se
0x0F ridge ne->sw
0x10 ridge se->nw
0x11 ridge sw->ne
All non-open tiles are given as low -> high. Valleys have 3 vertexes higher and one lower (at floor level). Ridges have one vertex higher and the remaining three at floor level. The given direction is that between the one vertex and its opposite one.
**Floor/Ceiling Info** (1 byte)
bits 0-4 Height (ceiling is units down from 0x20)
bits 5-6 Texture rotation. When viewed from above: 1: 90°, 2: 180°, 3: 270° - always clockwise
bit 7 Hazard flag. Floor has biohazard, ceiling has radiation hazard.
The exact behaviour for the hazard flags is determined by the [level variables](levelVariables.md).
**Texture Info** (2 bytes)
The texture info field contains information for the tile textures of the level.
For real world, it contains index values into the texture map (see next resource below).
Cyberspace levels specify the palette color index for floor and ceiling of the South-Western corner.
Real World:
bits 0-5 Wall texture
bits 6-10 Ceiling texture
bits 11-15 Floor texture
Cyberspace:
bits 0-7 Floor palette index
bits 8-15 Ceiling palette index
**Flags** (4 bytes)
Real World:
0x80000000 Tile visited (automapper)
0x70000000 Unused
0x0F000000 Ceiling shadow
0x00F00000 Unused
0x000F0000 Floor shadow
0x0000E000 Music index
0x00001000 Music "peril"
0x00000C00 Slope control. See below.
0x00000200 Deconstructed flag ("spooky" music)
0x00000100 Use wall texture from adjacent tile
0x00000080 Unused -- only set for computer room on level 7
0x00000040 Flip wall texture horizontally alternating
0x00000020 Flip wall texture horizontally
0x0000001F Offset for wall textures
Cyberspace:
0x01000000 Flight pull: Strong pull towards floor
0x000F0000 Flight pull
0x0000E000 Music index
0x00001000 Music "peril"
0x00000C00 Slope control. See below.
0x00000060 Game-Of-Life flags (either needs to be set)
```Ceiling shadow``` and ```Floor shadow``` specify the darkness intensity for the south-western corner of the tile.
The ceiling value darkens all four surrounding ceilings.
The walls made by surrounding top-pillars are darkened starting from the ceiling.
The walls made by surrounding bottom-pillars are darkened starting from their floor.
The floor value darkens all four surrounding floors.
The walls made by surrounding bottom-pillars are darkened starting from the floor.
The walls made by surrounding top-pillars are darkened starting from their ceiling.
> The actions to modify the light values can only give tiles more light, or remove previously added light.
> Thus, with shadow values of 0, these tiles are as bright as possible, without any chance of making them darker.
```Slope control``` specifies for the floor and ceiling how slopes (and valleys and ridges) should look like.
0: Ceiling is inverted to floor; A slope height of 0x1F will have ceiling and floor seal of the tile.
1: Ceiling is mirroring the floor; A slope height of 0x10 will have their most extent vertexes touch each other.
2: Ceiling is flat.
3: Floor is flat.
```Flip wall texture horizontally```
Wall textures can be flipped horizontally.
In the ```alternating``` case, flipping is based on the parities of the tile's two coordinates (even/odd status of the X and Y values).
Furthermore, North/South wall textures are flipped separately to East/West wall textures; If the North/South walls are flipped for a certain tile, the corresponding East/West walls will not be flipped. If the North/South walls are not flipped, then the East/West walls will be flipped.
> This ensures mirrored patterns are kept across corners without the need to change the flags for every other tile. This is heavily used on level 9 for the infested walls.
The North/South texture is to be flipped if the parities of the tile's two coordinates are different - East/West would be normal in this case.
The North/Shouth texture is normal if the parities of the tile's two coordinates match - East/West would be flipped in this case.
This alternation is inverted if additionally bit ```0x00000020```` is set.
```Flight pull``` specifies what default translation/rotation forces are in play
0: None
1: Weak pull towards East
2: Weak pull towards West
3: Weak pull towards North
4: Weak pull towards South
5: Medium pull towards East
6: Medium pull towards West
7: Medium pull towards North
8: Medium pull towards South
9: Strong pull towards East
10: Strong pull towards West
11: Strong pull towards North
12: Strong pull towards South
13: Medium pull towards ceiling
14: Medium pull towards floor
15: Strong pull towards ceiling
16: Strong pull towards floor (requires separate extra bit)
> The engine applies the force to the 'nose' of the hacker, i.e. it will pull the heading until the hacker looks straight
> at the origin of the force. This causes the implicit rotation. Only if the heading is exactly opposite to the origin,
> then no rotation happens (i.e., pulled backwards).
> All but the 'Strong' pulls can be countered.
**Light delta** (1 byte)
The high nibble specifies the light delta to the ceiling shadow. The low nibble specifies the light delta to the floor shadow.
The [Change lighting action](../levelObjects/Actions.md#action-type-7-change-lightning) modifies these values.
### Texture map
This resource is a list of int16 texture identifier in resource ```L07``` with a fixed length of 108 bytes.
The ```Texture Info``` fields in the tile map refer to these identifier.
Although the field for wall textures would allow for 64 textures, the resource can only hold up to 54 textures.
The first 32 textures can be used for floor/ceiling as well.
> The engine caches these textures when reloading a savegame with the same level.
> In the original source this list is also referred to as ```loaded textures``` or ```loved_textures```.
> The 'texture' index for space is 0x00CD, the observation grating has 0x00B1 (ceiling on groves and bridge)
See [Level Textures](../content/LevelTextures.md) for further information.
### Map notes
#### Text Data
Text for the map notes is stored in the level-specific resource ```L46```. The notes are stored sequentially, with the 0x00 character terminating a note.
The maximum length of one note is 40 bytes (including the termination character). The resource itself is pre-initialized with 0x00 bytes to a total length of 0x800 bytes.
Data in this resource is always defragmented. If a note is modified or removed, the notes are rearranged.
#### Next available space
Resource ```L47``` is a single uint32 offset, pointing to the first available byte in the text data resource.
@@ -0,0 +1,28 @@
## Surveillance Cameras
The engine supports up to 8 surveillance cameras per level.
Such a camera can be any object in the level, and an additional surrogate can determine when a linked screen should show static although the actual source would still be available.
### Surveillance Sources
The sources of surveillance screens are defined with ```L43```. This resource contains a list of up to 8 object table index values.
These referred objects provide the location and orientation for the video source of a screen. A screen then refers to one of these entries by using one of the corresponding [special values as 'picture source'](../levelObjects/07_BigStuff/levelBigStuffEntry.md).
**Surveillance Sources Table** (16 bytes)
0000 [8]int16 Source object table index
> For a source that is not an actual camera object, it is typical to find 'null' trigger objects to be the reference.
> Unused surveillance sources refer to the unused object at index 0.
### Surveillance Surrogates
Screens may be bound to a destructible object. If this surrogate is destroyed, the corresponding screen shows static. These associations are stored in ```L44```, a resource with a list of up to 8 object table index values.
**Surveillance Surrogates Table** (16 bytes)
0000 [8]int16 Deathwatch object table index
> For camera sources, it is typical to refer to the actual camera in this table.
@@ -0,0 +1,30 @@
## Texture Animation
```L42``` is a table containing animation information for textures. 4 entries are stored in this table.
The ```Animation Group``` field of the [Texture Properties](../fileFormat/PropertyFiles#properties-entry) specifies
the index into this table. A wall that has such a texture set will use further textures. According to the
```Number of frames``` field of this table entry, more textures are used from the [Texture Map](mapInformation.md#texture-map).
Meaning, if the level uses texture X which has an animation group set, the corresponding animation entry specifies
how many further textures are used for this animation. These then have all to be next to each other in the texture map.
The properties of the first texture determine how the remaining will behave (climbable, ...)
> Since most textures have their animation group set to 0x00 and have no animation, the first entry in this
> table is unused and the bytes always 0.
**Animation Entry** (7 bytes)
0000 int16 Frame time (in milliseconds)
0002 int16 Current frame time
0004 byte Current frame index
0005 byte Number of frames for loop
0006 byte Loop type/Loop state
**Loop Type** (1 byte)
0x00 Loop forward
0x01 Loop forth-and-back (forward state)
0x81 Loop forth-and-back (backward state)
+9
View File
@@ -0,0 +1,9 @@
## Access Levels
Doors can be restricted to certain access levels. The engine knows about 32 access levels (0..31).
The names of the access levels are stored as [texts](../media/Texts.md) in resource ```0x0879```. Per access level there
are two text strings, so 64 in total. The first string is the short name, for example ```STD``` and the second the long
name, ```Standard```.
The first level (0) with name ```None``` is not checked. Doors with level 0 immediately open.
@@ -0,0 +1,57 @@
## Electronic Messages
This chapter documents the electronic messages that the hacker receives either through triggers or collecting them.
This means e-mails (including video mails), logs and data fragments - everything the "Multimedia data reader" can show.
### Text Format
The [text resources](../media/Texts.md#electronic-messages) for the messages contain both the content, as well as the
meta information, encoded in text form. Any such message resource has the following string blocks:
1 line Meta info
1 line Title
1 line Sender
1 line Subject
N lines Verbose text
1 line Empty
M lines Terse text
1 line Empty
The ```Meta info``` line describes what to show additionally. This text has the following format:
meta-info := [event " "] [color " "] [left-mfd] ["," [" "] right-mfd]
event := ("i" hex2) | "t"
color := "c" hex2
left-mfd := dec
right-mfd := dec
Examples:
"13, 18"
"i17 8"
"t cD1 23,23"
"cD1 23,6"
For most of the messages, at least ```left-mfd``` is specified. ```left-mfd``` and ```right-mfd``` are decimal offset
numbers that refer to the image to show in the respective MFD. The offset is based on resource ```0x0028```.
If ```left-mfd``` is greater or equal 256, then the message refers to a video mail. Video mails don't have an MFD image.
If specified, the ```color``` field identifies a hexadecimal colour palette index. This colour is used to draw the sender and subject lines.
The ```event``` field chains messages together. "i" events identify the message that must immediately follow the current one;
The hexadecimal number is the offset for the next message (based on resource ```0x0989```). Such interrupt messages then have "t" as event.
> The vanilla resource files contain a few messages with errors in them:
> There are some "stub email" messages with empty header lines, the text "stub email" twice and only one terminating empty line.
> Furthermore, there is one message in ```gerstrng.res``` which misses the terminating empty line in the terse text.
### Text Content
Within the texts, the substring ```$N``` is the placeholder for the name of the hacker.
### Audio
Corresponding audio resources are based on ID ```0x0AB5```. If an electronic message with ID X has audio, the audio will have resource ID ```0x0AB5``` + X. (Also: resource ID of text resource + 300)
These audio resources are [MOVI resources](../media/moviResources.md) with only the audio component. The audio recordings are stored in the files
```citalog.res``` (English), ```geralog.res``` (German) and ```frnalog.res``` (French).
+29
View File
@@ -0,0 +1,29 @@
## Level Textures
A level texture is a square bitmap used for level paneling - floors, ceiling and walls. The resources contain 273 textures,
for which there are four different resolutions available.
### Bitmaps
The bitmaps are stored uncompressed in ```texture.res``` in the following resources:
0x004C 16x16 bitmaps, one per block (a compound resource)
0x004D 32x32 bitmaps, one per block (a compound resource)
0x02C3 - 0x03D3 64x64 bitmaps, one per resource
0x03E8 - 0x04F8 128x128 bitmaps, one per resource
### Texts
Each texture comes with a description (name) and a "can't be used" message. [These texts](../media/Texts.md)
are stored in resources ```0x086A``` and ```0x086B``` respectively.
> Note: The text resources reserve 512 blocks, but only 273 are used.
### Properties
The [texture properties](../fileFormat/PropertyFiles#texture-Properties-textprop.dat) give further information on textures.
### Animation
Some textures, according to their properties, are in animation groups. These groups are controlled [per level](../archives/textureAnimation.md).
+72
View File
@@ -0,0 +1,72 @@
## Conventions
### Content
Information in these documents MUST be verifiable, or have been verified (e.g. through testing or cross-reference to original code) to be included. Assumptions, contradictions, discussions and the like MUST be handled through the [issue](https://github.com/inkyblackness/ss-specs/issues) system.
In general, changes to the documents SHOULD be introduced by linking commits to corresponding issues. This way, issues can be used as reference material for verification.
### Language
The human language of this documentation is English. Names of types/fields SHOULD follow that of the source (which is presumably en-us), while prose text SHOULD follow en-uk .
This is primarily to uphold the legacy of this documentation. The original ss-specs was maintained within the context of TSSHP, which came from Britain.
When wording actions, consider differenciating between the character in the game-world ("the hacker"), and the player. ("The hacker has a rotation angle of xyz, so the player sees...").
Sentences referring to individuals SHALL be phrased with inclusive, gender neutral language. The use of [singular they](https://en.wikipedia.org/wiki/Singular_they) is to be used if no other option is available.
> While the hacker is represented with a male body and voice in the original game, a fan-mod could change this. (see [here](https://www.moddb.com/mods/system-shock-female-hacker))
> This documentation states raw engine data, which doesn't care about humans, even if considered to be insects.
More generally, consider online guides about inclusive language, such as [this one from Google](https://developers.google.com/style/inclusive-documentation).
### Formatting
Vanilla [Markdown](http://daringfireball.net/projects/markdown/syntax) SHOULD be used for all pages.
The biggest header available is the H2 header (""## Title"). H1 header are reserved for the main title. Each page SHOULD start with an H2 header, which is also the link in the [index](index.md).
### Data
In general, this documentation describes (serialized) data structures. Such data structures SHOULD be documented using code blocks and follow the following format:
offset type description
#### Example
0000 int32 some value
0004 [14]byte unknown, not used (0x00)
0012 int16 other value
Offset values MUST start at 0 and be given in hexadecimal (or placeholder). They SHOULD have a width of 4 (or more) digits.
Types are field specific.
Description is a short human-readable text describing the field.
#### Integers
For integer types, they MUST be given in the following format:
(s|u)?int(8|16|24|32)
's' and 'u' prefixes specify 'signed' and 'unsigned integer' respectively. If no sign-prefix is given, it has not been verified whether this field allows negative values or allows greater positive values.
Implementors MAY treat fields without known sign to be unsigned - this can be the case for length fields for instance. It is unknown whether the original executable would handle them correctly.
Unless otherwise noted, integer values are stored in [little-endian](http://en.wikipedia.org/wiki/Endianness#Little-endian) format.
#### Fixed point numbers
Due hardware limitations and slow perfomance of float point operations, System Shock uses fixed point number represenation of real data type.
Usually engine uses [Q15.16](https://en.wikipedia.org/wiki/Q_(number_format)) format (1 bit for sign, 15 bits for integer part, 16 bits for fractional):
0x 0000 0000
| |
| fractional part
integer part with sign (first bit, 0 means positive, 1 - negative)
This format is refered as `fix` (int32). For example, (2 * Pi) number is `0x0006487f` (6 + 18559/65536 = 6.28319)
Internally engine supports Q23.8 numbers (`fix24`) or even Q2.28 (for some 2D- and 3D-computations).
@@ -0,0 +1,196 @@
## Property Files
A few files don't have an archive format, and instead are flat files consisting directly serialized tables.
### Texture Properties (textprop.dat)
This file contains a single table of texture property entries, after a header.
> The table appears to be truncated. Since the game only uses 273 textures, this may have never been detected.
> The file has 4004 bytes on disk, meaning 4000 bytes are available for the table.
> In a test, the original engine appeared to be fine to read a smaller file with exactly 273 entries (3007 bytes) -
> which had the unknown fields set to zero.
> The original source explains the difference: The texture property system would support up to 400 textures.
> It also **assumed** a property structure size of 10 bytes (hardcoded value), while the actual structure is 11 bytes long.
> In other words, one could feed the engine more than 273 textures, yet no more than 363. More than 363 would simply be ignored and the extra textures would just have their default properties.
> This also explains why the Mac port reads exactly 363 textures, as reading more would exhaust the file.
#### Header
The file starts with a 4-byte header.
**Texture Properties File Header** (4 bytes)
0000 int32 file version number, value 0x09
The engine rejects a properties file with a different version number.
#### Properties Entry
**Texture Property Entry** (11 bytes)
0000 byte family (unused)
0001 byte target (unused)
0002 sint16 resilience (unused); Default: 10
0004 sint16 distance modifier; Default: 0
0006 byte Climbable. 0x00: not climbable; != 0x00: wall can be climbed (original name: friction)
0007 byte friction walk (unused); Always 0x00
0008 byte Transparency control (original name: force_dir)
0009 byte Animation group
000A byte Animation index - if texture is animated
The two bytes at the beginning (`family` and `target`) almost always are each set to the low-byte of the index. A few exceptions have both bytes 0x00. Since they are unused, it is unclear what they mean.
The `resilience` is nearly always `0x0A`; The one existing exception has it 0. Since it is unused, it is unclear what it means.
The `distance modifier` always zero. It is used in the renderer to determine which texture resolution to use, based on distance from the camera. A higher modifier causes the smaller resolutions to be chosen at larger distances; A negative value causes an earlier switch.
See [Texture Animation](../archives/textureAnimation.md) for details on animated textures.
**Transparency Control**
0x00 Regular texture; transparent pixel (palette index 0x00) are drawn black
0x01 Space texture (pixel data of image is ignored)
0x02 Texture with space background for transparent pixels
### Object Properties (objprop.dat)
The object properties file contains several tables about properties of [level objects](../levelObjects/index.md). It has the following format:
| Header | Class 0 Tables | Class 1 Tables | ... | Common Table |
#### Header
The file starts with a 4-byte header containing the version number.
**Object Properties File Header** (4 bytes)
0000 int32 file version number, value 0x2D
The engine rejects a properties file with a different version number.
#### Class Tables
For each object class, one entry exists with a table of class-generic properties and a further table, subclass-specific:
| Class N Tables |
| Generic | Subclass 0 | Subclass 1 | ... | Subclass N |
The sizes of the generic and specific tables are dependent on the object class, subclass and type amount.
The generic table contains one entry per object type of this class. The specific tables contain one entry per object type of the subclass.
See [Level Objects](../levelObjects/index.md) for an overview of the amount of types and their entry sizes.
#### Common Table
At the end of the file is the table with common properties. For each object type one entry.
**Common Object Properties** (27 bytes)
0000 int32 Mass
0004 int16 Default hitpoints
0006 uint8 Armour
0007 byte Render type
0008 byte Physics type; 0x00: none (insubstantial), 0x01: regular, 0x02: special
0009 byte Hardness; 0..253
000A byte Unused; Internally called "pep", this value appears to not be used by the engine.
000B byte Physics XR; 0..253
000C byte Unused; Internally called "physics_y". These values are set (almost) identical to 000B and have no effect.
000D byte Physics Z; Set to 0x30 for all barriers (10/x/x).
000E byte Vulnerabilities
000F byte Special vulnerabilities
0010 [2]byte Unused
0012 byte Defence value
0013 byte Toughness; 0x03: Invincible
0014 int16 Flags
0016 int16 MFD/Mesh ID
0018 [2]byte Bitmap 3D field model index
001A byte Destruction effect
`Physics type` governs how collisions shall be handled. `0x00` makes the object insubstantial, ignoring forces applied to it. For instance, most scenery stays where it was put. `0x01` is for regular objects meant to react "normal" in a physical world. `0x02` behaves strange is set for exactly one object (`14/0/6`, "SONIC THE HEDGEHOG", an unused critter). Objects with a physics type of `0x02` aren't affected by gravity for instance, but are repelled if hacker runs into them.
The `hardness` acts as repulsion force on the object if it collides with the ground or walls - or on the hacker if hacker runs into it.
Hardness depends on the `mass`; Objects with more mass bounce around less.
The `physics xr` value is applied to vertically center (animated) sprites on their position. If `0x00`, the sprites are rendered with their bottom anchored at the object's position. Higher values will move the sprites downwards.
Related, the `physics z` value is inconsistently used as a height value. Only if this value is `0x00` (most of the time), then `physics xr` is used for height calculation. Still, the XR value is used far more often than the Z value.
The `vulnerabilities` and `special vulnerabilities` match the corresponding damage type fields from [Generic Weapon Info](../levelObjects/GenericWeaponInfo.md).
> The engine, internally, calls this field `resistances`, yet the code does the opposite. Furthermore, this `resistances` field covers the four bytes from `000E`.
The field of `special vulnerabilities` is split up into `0x0F` primary vulnerabilities (double damage) and `0xF0` super vulnerabilities (quad damage). Damage is multiplied only if the originating special force equals the vulnerabilities in these fields.
> Quad damage is reduced to triple damage on difficulty level 3 (either for real-world, or cyber, wherever it is calculated).
The `defence value` is compared to the `offence value` of a weapon or projectile to determine a "critical hit" (or critical reduction). Higher defence values will decrease the damage, higher offence values will increase the damage. If it is `0xFF`, then the target is invulnerable to critical hits.
The `toughness` determines how much damage, after most of the calculation, goes through - this still requires the object to be solid to register damage. The value of `0x03` reduces the damage to zero. Otherwise, this value is considered to be a shift factor, so a value of `0x01` halves damage, `0x02` quarters it.
> This value is set to `0x04` only for the non-used SHODAN critter `14/3/5`. This one would have been affected by only the strongest weapons.
> Probably this value was intended to be in range of 0..3 only.
`MFD/Mesh ID` identifies the MFD to open for this type of object, if selected from inventory. For 3D objects in the world, this value identifies the 3D mesh to render, based on resource `0x08FC`.
**Render Type Enumeration** (1 byte)
0x01 3D object
0x02 Sprite
0x03 Screen
0x04 Critter
0x06 Fragments (e.g. the Cyberdog)
0x07 Not drawn
0x08 Oriented surface (door, wall decoration)
0x0B Special
0x0C Force door
**Common Object Flags** (2 byte)
0x0001 Useful inventory object (green font)
0x0002 Solid - can't be passed through
0x000C Use mode (see below)
0x0010 Usable inventory object - object is triggered on double-click in inventory list
0x0020 Blocks 3D rendering if closed (set for opaque doors)
0x00C0 Light type (see below)
0x0100 Solid if closed (set for barriers and repulsor object)
0x0200 Flat solid (objects can be placed on it)
0x0400 Doubled bitmap size (set for various explosion animations)
0x0800 Destroy on contact
0x7000 Class-specific flags
0x8000 Useless object. Unless specifically marked useful otherwise, object can be removed to make room
**Use Mode Enumeration** (2 bit)
0x00 Pickup - items can be taken
0x01 Use - items are used, not taken
0x02 undefined
0x03 "Null" - can't be used
**Light Type Enumeration** (2 bit)
0x00 Normal, simple lighting
0x01 Complex lighting
0x02 Ignore darkness
0x03 Deferred light type (instance-specific)
**Bitmap 3D Bitfield** (2 byte)
0x03FF Bitmap number low mask
0x8000 Bitmap number high mask
0x0400 Animation
0x0800 Repeat
0x7000 Frame number mask
**Object Destruction Effect** (1 byte)
This byte is a bitmask field with the following layout:
0x1F Animation frame index -- refers to different types of animations
0x20 Play destruction sound
0x40 Play destruction sound (not used in main game)
0x80 Actual explosion (with damage) -- requires Animation frame index to be 0x0F
> Some objects have their "private" destruction sounds and have none of the sound bits set.
> The `actual explosion`, set for larger electrical appliances, works only if the full value is `0x8F`.
@@ -0,0 +1,126 @@
## Resource Files
Resource files are structured archives. These files contain one or more "resource" entries. Resources are identified and contain one or more data blocks. A resource with more than one data block is called a "compound resource".
### Identifier
A resource identifier is a ```uint16``` value, and a compound resource uses a ```uint16``` value as index.
Resource identifier are "valid" from value ```0x0003``` and above.
```0x0000``` is the NULL identifier (this was quick approach to erase resources from file for developers), and ```0x0001``` and ```0x0002``` are used engine-internally (first used to point to head ptr in
[LRU](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)) cache, second - to tail ptr in LRU).
### General Format
A resource file consists of a header, resources and a file directory. The header points to the file offset of the file directory and the file directory points to the file offset of the first resource.
| /--------------------------------------v |
| Header.dirOffset | Res0 | Res1 | ... | Res N | Directory.dataOffset |
| ^----------------------------------------/ |
All resources, as well as the file directory, start at a 4-byte boundary.
To calculate the file offset of a resource, the file directory has to be read first to determine the offsets (and lengths) of the previous resources.
> Although the format would allow for a different order, all resource files have the directory at the end of the file.
### File Header
The file header has a length of 128 bytes and has the following format:
**File Header** (`ResFileHeader` struct, 128 bytes)
0000 [16]byte signature signature "LG Res File v2\r\n"
0010 [96]byte comment comment, terminated with "\x1A" (remainder 0x00)
0070 [12]byte reserved reserved, set to 0x00
007C sint32 dirOffset file offset to directory
> No resource file has been found containing a comment. It always only contains the terminator byte 0x1A.
> The comment is terminated with 0x1A as this represents the ```Ctrl+Z``` code. This was a little trick to enable people to ```type``` the file on the command line and not be spammed with binary garbage.
### File Directory
The file directory consists of a 6 byte header followed directly by its directory entries:
**Resource file directory header** (`ResDirHeader` struct, 6 bytes)
0000 uint16 numEntries number of resources in the file
0002 sint32 dataOffset file offset to beginning of first resource
**Resource file directory entry** (`ResDirEntry` struct, 10 bytes)
0000 uint16 id resource ID
0002 sint24 size resource length (unpacked)
0005 sint8 flags resource flags (see below)
0006 sint24 csize resource length (packed in file)
0009 sint8 type resource type (see below)
### Resource Flags
The resource flag marks extra information about the resource.
**Resource Flag** (1 byte)
0x01 RDF_LZW compressed
0x02 RDF_COMPOUND compound
0x04 RDF_RESERVED reserved (unused)
0x08 RDF_LOADONOPEN load on open (unused)
> The original source lists several more resource flags, which are all unused in System Shock.
> Presumably this is the result of re-used code.
"Flat" resources contain only one data block, which is the complete resource data itself. Compound resources contain 0, one, or more blocks.
### Resource Types
The following resource types appears in System Shock:
0x00 UNKNOWN Palette (although originally called "unknown", it is used for palettes exclusively)
0x01 STRING String
0x02 IMAGE Image
0x03 FONT Font
0x04 ANIM Animation Script (Video clip)
0x07 VOC Sound effects and voices (.voc)
0x0F OBJ3D 3D model geometry
0x11 MOVIE Movie
0x30 APP1 Application specific resource (used in archive.dat for storing maps)
> The original source lists several more resource types, which are all unused in System Shock.
> Presumably this is the result of re-used code.
>
> There exists 0x05 (PALL) resource type specially for palettes, but never used in System Shock.
### Compound Resources
If a resource is flagged to be compound, the resource data starts with a directory. This directory contains relative offsets to the first bytes of the contained blocks, following the directory.
**Resource directory** (2 bytes + 4 bytes per block + 4 bytes)
0000 uint16 number of blocks in resource
0002 sint32 resource offset to first block
....
nnnn sint32 resource offset to last block
nnnn+4 sint32 total length of resource
To calculate the length of a block N, take the start offset of N+1 and subtract the start offset of N. This is why there is one extra offset entry in the directory.
If a compound resource is compressed, the directory itself is not compressed. Resource offsets always point into the uncompressed data.
> Some compound resources have two zero bytes between the directory and the first block.
### Resource Compression
Resource compression is based on the [LZW algorithm](http://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch). It uses a fixed word-size of 14 bits. Two words are reserved for control.
The words are serialized sequentially big-endian as a continuous stream.
For example, if the first 7 bytes in the resource are
aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg
then the corresponding words are
aaaaaaaabbbbbb bbccccccccdddd ddddeeeeeeeeff ffffffgggggggg
with the high bit to the left in each case.
The first 256 words of the dictionary are initialized with length 1 for the corresponding byte values. The reserved words are:
* 0x3FFE Reset dictionary to initial state
* 0x3FFF End-Of-Stream
The End-Of-Stream marker is always issued. The serialized byte stream always contains a trailing 0x00 byte after the End-Of-Stream word.
The compressor issues a dictionary reset when it is exhausting the dictionary and can't add a new word to it for the 1001st time.
+188
View File
@@ -0,0 +1,188 @@
## File Overview
### Variants
Not considering any re-releases, System Shock 1 was available in the following variants:
* For DOS
* Demo: HD and CD
* Release: HD and CD
* For MAC
* Release: CD
> Unless otherwise noted, this documentation considers only the DOS CD release.
The main difference between HD and CD releases is the CD releases contain audio files for the voice logs and provide a high resolution option.
### Locations
Data files described in this documentation are available at the following locations:
* CD-ROM (System Shock 1 CD)
* /cdrom/data
* /hd/data (will be copied to HD)
* HD (System Shock installation directory)
* /data
### File Types
Data files come in the following variants:
* [Resource files](ResourceFiles.md), which are structured archives
* [Property files](PropertyFiles.md), containing one or more serialized tables
Resource files are the majority. All files with filename suffix `.res` are resource files. Savegame files have the same format, including the "starter" savegame file `archive.dat`. These files contain one or more "resource" entries. Resources are identified and contain one or more data blocks. A resource with more than one data block is called a "compound resource".
The property files are "flat" files containing one or more serialized tables.
### List of Files
The following table shows an overview of the known files, across the different releases.
For resource files, resources may share the same identifier. In this case they represent the alternatives available through configuration. The options are:
* Language. Available are: "English", "French" and "German"
* Cutscene resolution (MOVI format). Available are: "low" (320x150) and "svga" (600x300)
> The "bitmap" based cutscenes have a resolution of 320x200 and have their own identifier.
<table style="width:100%;">
<tr>
<th rowspan=2>Content</th>
<th colspan=3>Languages</th><th colspan=2>Location HD Release</th><th colspan=2>Location CD Release</th>
</tr>
<tr>
<th>ENG</th><th>FRN</th><th>GER</th>
<th>Demo</th><th>Release</th><th>Demo</th><th>Release</th>
</tr>
<tr>
<th colspan="8">Startup and Cutscenes</th>
</tr>
<tr>
<td>Splash screens</td><td colspan=3>```splash.res```</td>
<td>HD</td><td>HD</td><td>CD</td><td>CD</td>
</tr>
<tr>
<td>Splash screen palette</td><td colspan=3>```splshpal.res```</td>
<td>--</td><td>HD</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Palette for low-res bitmap cutscenes</td><td colspan=3>```cutspal.res```</td>
<td>--</td><td>HD</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Low-Res bitmap intro cutscene</td><td colspan=3>```start1.res```</td>
<td>--</td><td>HD</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Low-Res MOVI intro cutscene</td><td>```lowintr.res```</td><td>```lofrintr.res```</td><td>```logeintr.res```</td>
<td>--</td><td>--</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>High-Res MOVI intro cutscene</td><td>```svgaintr.res```</td><td>```svfrintr.res```</td><td>```svgeintr.res```</td>
<td>--</td><td>--</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Low-Res bitmap end cutscene</td><td colspan=3>```win1.res```</td>
<td>--</td><td>HD</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Low-Res MOVI end cutscene</td><td colspan="3">```lowend.res```</td>
<td>--</td><td>--</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>High-Res MOVI end cutscene</td><td colspan="3">```svgaend.res```</td>
<td>--</td><td>--</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Low-Res bitmap death cutscene</td><td colspan=3>```death.res```</td>
<td>--</td><td>HD</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Low-Res MOVI death cutscene</td><td colspan="3">```lowdeth.res```</td>
<td>--</td><td>--</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>High-Res MOVI death cutscene</td><td colspan="3">```svgadeth.res```</td>
<td>--</td><td>--</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>Main menu screens</td><td colspan=3>```intro.res```</td>
<td>HD</td><td>HD</td><td>CD</td><td>HD & CD</td>
</tr>
<tr>
<th colspan="8">In-game Resources</th>
</tr>
<tr>
<td>New Game Template</td><td colspan=3>```archive.dat```</td>
<td>HD</td><td>HD</td><td>CD</td><td>CD</td>
</tr>
<tr>
<td>In-game palette</td><td colspan=3>```gamepal.res```</td>
<td>HD</td><td>HD</td><td>CD</td><td>CD</td>
</tr>
<tr>
<td>Texts</td><td>```cybstrng.res```</td><td>```frnstrng.res```</td><td>```gerstrng.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>HUD resources</td><td>```mfdart.res```</td><td>```mfdfrn.res```</td><td>```mfdger.res```</td>
<td>HD 1)</td><td>HD</td><td>HD 1)</td><td>HD</td>
</tr>
<tr>
<td>Trigger audio</td><td>```citbark.res```</td><td>```frnbark.res```</td><td>```gerbark.res```</td>
<td>--</td><td>--</td><td>CD 1)</td><td>CD</td>
</tr>
<tr>
<td>Log Audio</td><td>```citalog.res```</td><td>```frnalog.res```</td><td>```geralog.res```</td>
<td>--</td><td>--</td><td>CD 1)</td><td>CD</td>
</tr>
<tr>
<td>Video Mails</td><td colspan=3>```vidmail.res```</td>
<td>--</td><td>HD</td><td>--</td><td>CD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```objart.res```</td>
<td>HD</td><td>HD</td><td>CD</td><td>CD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```objart2.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```sideart.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>Level textures</td><td colspan=3>```texture.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```citmat.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>Sound Effects</td><td colspan=3>```digifx.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```gamescr.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```handart.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```obj3d.res```</td>
<td>HD</td><td>HD</td><td>HD</td><td>HD</td>
</tr>
<tr>
<td>???</td><td colspan=3>```objart3.res```</td>
<td>HD</td><td>HD</td><td>CD</td><td>HD</td>
</tr>
<tr>
<th colspan="8">Property Files</th>
</tr>
<tr>
<td>Object properties</td><td colspan=3>```objprop.dat```</td>
<td>HD</td><td>HD</td><td>CD</td><td>HD</td>
</tr>
<tr>
<td>Texture properties</td><td colspan=3>```textprop.dat```</td>
<td>HD</td><td>HD</td><td>CD</td><td>CD</td>
</tr>
</table>
1) Only the English files available
+29
View File
@@ -0,0 +1,29 @@
## Index
* [Conventions](conventions.md)
* General File Format
* [File Overview](fileFormat/index.md)
* [Resource Files](fileFormat/ResourceFiles.md)
* [Property Files](fileFormat/PropertyFiles.md)
* Archives
* [Archive Overview](archives/index.md)
* Raw Media
* [Bitmaps](media/Bitmaps.md)
* [Palettes](media/Palettes.md)
* [Sound Effects](media/SoundEffects.md)
* [MOVI Resources](media/moviResources.md)
* [Video Clips](media/VideoClips.md)
* [Texts](media/Texts.md)
* [Geometry](media/Geometry.md)
* Content
* [Access Levels](content/AccessLevels.md)
* [Electronic Messages](content/ElectronicMessages.md)
* [Level Objects](levelObjects/index.md)
* [Level Textures](content/LevelTextures.md)
* Other Files
* [Music](other/Music.md)
@@ -0,0 +1,79 @@
### Gun Properties
#### Generic Gun Properties
**Generic Gun Propertes** (`GunProp` struct) (2 bytes)
0000 uint8 Fire rate
0001 byte Ammo type
For non-automatic guns, the `fire rate` specifies when the next bullet can be shot. For melee weapons it is the time until the next swing can be made.
It is measured in game time, with a unit for this value of about 36 milliseconds. A value of ```0x00``` means 'as fast as you can press fire'.
> Changing this value does not have any detectable effect for automatic guns (```0/1/x```).
**Ammo type** (1 byte)
0xF0 Ammo subclass identifier
0x0F Bitmask, which ammo types within the ammo subclass are compatible (0..3)
> Ammo type is ignored for beam and melee weapons.
#### Specific 2 Properties
This subclass describes special projectile weapons.
**Specific 2 Properties** (`SpecialGunProp` struct) (16 bytes)
0000 [8]byte [Generic Weapon Info](../GenericWeaponInfo.md)
0008 uint8 Projectile travel speed
0009 int32 Projectile triple (0x00CCSSTT)
000D uint8 Attack mass
000E sint16 Attack speed (kickback)
#### Specific 3 Properties
This subclass describes melee weapons.
**Specific 3 Properties** (`HandtohandGunProp` struct) (13 bytes)
0000 [8]byte [Generic Weapon Info](../GenericWeaponInfo.md)
0008 uint8 Energy usage
0009 uint8 Attack mass (impact force - pushback on hit)
000A uint8 Range
000B sint16 Attack speed (kickback; only applied if hitting something)
#### Specific 4 Properties
This subclass describes energy beam weapons.
**Specific 4 Properties** (`BeamGunProp` struct) (13 bytes)
0000 [8]byte [Generic Weapon Info](../GenericWeaponInfo.md)
0008 uint8 Max charge
0009 uint8 Attack mass (impact force - pushback on hit)
000A uint8 Range
000B sint16 Attack speed (kickback)
#### Specific 5 Properties
This subclass describes energy projectile weapons.
**Specific 5 Properties** (`BeamprojGunProp` struct) (18 bytes)
0000 [8]byte [Generic Weapon Info](../GenericWeaponInfo.md)
0008 uint8 Max charge
0009 byte Attack mass
000A sint16 Attack speed (kickback)
000C uint8 Projectile travel speed
000D int32 Projectile triple (0x00CCSSTT)
0011 byte Flags
**Energy Projectile Weapon Flags** (1 byte)
0x02 Stun effect
@@ -0,0 +1,9 @@
### Level Gun Table (Class 0)
```L10``` is a table describing weapons and entries extend [the prefix](../../archives/levelObjects.md#class-specific-information) with their current ammo or charge.
**Level Gun Entry** (8 bytes)
0000 [6]byte Level object prefix
0006 byte Ammo type / Charge
0007 byte Ammo count / Temperature
@@ -0,0 +1,12 @@
### Ammo Properties
#### Generic Ammo Properties
**Generic Ammo Propertes** (`AmmoProp` struct) (14 bytes)
0000 [8]byte [Generic Weapon Info](../GenericWeaponInfo.md)
0008 uint8 Cartridge size
0009 uint8 Bullet mass (impact force - pushback on hit)
000A sint16 Bullet speed (kickback)
000C uint8 Range
000D uint8 Recoil force
@@ -0,0 +1,7 @@
### Level Ammo Table (Class 1)
```L11``` is a table for ammo clips. Since clips are always full, entries have no further fields.
**Level Ammo Entry** (6 bytes)
0000 [6]byte Level object prefix
@@ -0,0 +1,42 @@
### Physics Properties
#### Generic Properties
**Physics Generic Properties** (`PhysicsProp` struct) (1 byte)
0000 byte Physics flags
**Physics Flags** (1 byte)
0x01 Emit light
0x02 Bounce off walls
0x04 Bounce off objects (real world), Pass through objects (cyberspace)
0x08 Bounce off projectiles; set for all cyberspace projectiles
> Cyberspace projectiles can't emit light, though they can bounce off walls.
#### Specific 0 Properties
"Tracer" objects.
> These are not used in the engine.
**Physics Specific 0 Properties** (`TracerPhysicsProp` struct) (20 bytes)
0000 [4]int16 X coordinates
0008 [4]int16 Y coordinates
0010 [4]uint8 Z coordinates
#### Specific 1 Properties
These properties are set for some `2/1/x` objects. More specifically: For cyberspace projectiles, also called "slow physics". The other projectiles of this subclass have their specific bytes zero.
**Physics Specific 1 Properties** (`SlowPhysicsProp` struct) (6 bytes)
0000 [6]byte Colour scheme
`Colour scheme` contains 6 palette indices from the game palette. The index into this list comes from the geometry properties, specifically from the [Set Colour and Shade](../../media/Geometry#set-colour-and-shade) command.
@@ -0,0 +1,50 @@
### Grenade Properties
#### Generic Grenade Properties
**Generic Grenade Propertes** (`GrenadeProp` struct) (15 bytes)
0000 [8]byte [Generic Weapon Info](../GenericWeaponInfo.md)
0008 byte Touchiness
0009 byte Blast radius
000A byte Blast core range
000B byte Blast damage
000C uint8 Attack mass (impact force - pushback on hit)
000D int16 Grenade Flags
`Touchiness` defines how likely a grenade is going to chain-explode if another was exploding nearby.
> This value influences a calculation, based on a random value, as well as damage percentage. If the resulting number is higher than 7, then a chain-reaction is caused.
> In the main game, this value is always `0x00`.
`Blast radius` specifies in tiles how far the explosion reaches, including visual effects (EMP scroll, explosion flash).
The main damage (from the `Generic Weapon Info`) is applied within the `Blast core range` of the explosion. This damage falls off from the center to the edge of the core range.
The `blast damage` is fully applied within the `blast core range`, additionally to the main damage. This damage falls off from the edge of the core range to the edge of the blast radius.
**Grenade Flags** (2 bytes)
0x01 Explode on contact
0x02 unused
0x04 Explode by timer
0x08 Explode by vicinity (mine)
> The behaviour of grenades is not exclusively governed by the flags field. Some seems to be hardcoded.
> For instance, regular grenades marked with "timer" don't offer a time setting in the HUD and explode right after being thrown.
> Regular timed grenades marked as "contact" ignore their timer setting, explode on object-contact, but not on floor/wall contact.
#### Specific 1 Properties
Properties for timed grenades.
**Specific 1 Properties** (`TimedGrenadeProp` struct) (3 bytes)
0000 uint8 Minimum time
0001 uint8 Maximum time
0002 uint8 Timing deviation -- always 0x00
`Minimum time` and `maximum time` specify the range, in seconds, timed grenades can be set to.
> `Timing deviation` is always set to 0x00. Since it may also let the grenade go off earlier than expected, this may happen while still in the hand - which is presumably why it is unused in the main game.
@@ -0,0 +1,44 @@
## Level Explosive Table (Class 3)
```L13``` is a table describing explosives.
**Level Explosive Entry** (12 bytes)
0000 [6]byte Level object prefix
0006 [6]byte Explosive data
**Explosive Data** (6 bytes)
0000 int16 Unknown; Only set for live explosives
0002 int16 State
0004 int16 Timer time
**State** (2 bytes)
0x0000 Inert
0x0001 Thrown live grenade
0x0005 Landed live grenade (land mines only, can't be picked up)
The timer settings of explosives are stored in the [game state](../../archives/gameState.md).
The ```Timer time``` field is set to the same value as in the [level schedules](../../archives/levelSchedules.md), though modifying this value doesn't have an effect. The level timer is the authorative entry when to trigger the explosive.
### Contact explosives 3/0/x
Contact explosives start a timer, though its expiry does nothing with the grenade.
Land mines can be catched mid-air and re-thrown, others explode. Land mines enter state ```0x0005``` when touching the ground, after which they can't be picked up safely anymore.
> Although the game state also keeps time settings for these explosives, they can't be changed. Their timer don't have an effect anyway.
### Timed explosives 3/1/0, 3/1/1
These explosives keep the state at ```0x0001```, even when landed. Live explosives can be picked up and re-thrown, until their timer sets them off.
### Object explosion 3/1/2
This item has not been encountered.
@@ -0,0 +1,9 @@
### Level Drug Table (Class 4)
```L14``` is a table describing Drugs.
**Level Drug Entry** (6 bytes)
0000 [6]byte Level object prefix
This entry does not contain any more information.
@@ -0,0 +1,9 @@
### Level Hardware Table (Class 5)
```L15``` is a table describing hardware.
**Level Hardware Entry** (7 bytes)
0000 [6]byte Level object prefix
0006 byte Hardware version
@@ -0,0 +1,92 @@
## Level Software Table (Class 6)
```L16``` is a table describing software - programs, logs, data...
**Level Software Entry** (9 bytes)
0000 [6]byte Level object prefix
0006 [3]byte Software data
### Cyberspace programs 6/0/x, 6/1/x, 6/2/x
**Cyberspace program data** (3 bytes)
0000 byte Version
0001 [2]byte Unused
> The three subclasses can be considered "aggressive programs", "defensive programs" and "boosts". Boosts ignore the version.
> Although several programs are defined, only a few are used.
> Different version numbers also change the color of the cube.
### Fun pack modules 6/3/0
**Fun pack module data** (3 bytes)
0000 byte Game mask
0001 [2]byte Unused
**Game mask** (1 byte)
0x01 Ping
0x02 Eel Zapper
0x04 Road
0x08 Botbounce
0x10 15
0x20 TriopToe
0x40 Gameb (Not working, gives "not installed" message)
0x80 Wing 0
### Multimedia data 6/4/0, 6/4/1
**Multimedia software data** (3 bytes)
0000 byte Unused
0001 byte Index
0002 byte Type
**Multimedia type** (1 byte)
0x00 E-Mail
0x01 Log
0x02 Data
> In the game, e-mails are received through triggers, logs are only in real-world levels, and data only in cyberspace levels. The engine supports placing any of them in any level type.
#### Logs
For logs, the ```Log resource offset``` is based on the resource ID ```0x09B8```. The high nibble is used as the level identifier - into
which category the log should be put.
See [Electronic Messages](../../content/ElectronicMessages.md) for details on messages.
> 6/4/0 are called "TEXT", behave like 6/4/1 "EMAIL", and are only used as logs.
## Cyberspace "Scenery" Software (Class 7, in cyberspace)
In cyberspace levels, ```L17``` is a table for additional software. It acts as an overflow for software if the first table is full.
**Scenery Software Entry** (16 bytes)
0000 [6]byte Level object prefix
0006 [10]byte Scenery software data
### Scenery software 7/3/2 ("GLOW BULB"), 7/2/0 ("SIGN")
**Scenery software data** (10 bytes)
0000 int16 Parameter
0002 int32 Subclass
0006 int32 Type
For entries that specify a program, ```Parameter``` specifies the version. For fun packs, it is the game mask.
> This type is only used for software with a single parameter; Found only for programs and fun packs.
@@ -0,0 +1,220 @@
## Level BigStuff Table (Class 7)
```L17``` is a table describing big items in real-world levels.
Cyberspace levels may re-use "big stuff" entries for further software entries. These entries need to be
interpreted differently. See [Software](../06_Software/levelSoftwareEntry.md).
**Level BigStuff Entry** (16 bytes)
0000 [6]byte Level object prefix
0006 [10]byte BigStuff Info
### Displays
There are several big items that are capable of displaying (animated) content. They reuse the following common types:
**Display Info** (10 bytes)
0000 int16 Frame count
0002 int16 Loop type
0004 int16 Alternation type
0006 int16 Picture source
0008 int16 Alternate picture source
Animated screens are also controlled by the [Loop Configuration](../../archives/loopConfiguration.md). If not, they use their own loop type.
**Loop Type** (1 byte)
0x00 Forward
0x01 Forward/Backward
0x02 Backward
0x03 Forward/Backward
> Other values have been found as well, without effect; The engine defaults to forward looping.
** Alternation Type** (1 byte)
0x00 Don't alternate
0x03 Alternate randomly
> Not all alternate source combinations work properly. Commonly it is used to alternate to a text.
**Picture Source** (2 bytes)
This field specifies what shall be shown. It has the following cases:
0x0000 .. 0x00F5 Image frames, offset from resource 0x0141
0x00F6 Static fading into SHODAN's face
0x00F7 Unknown (only found to display static)
0x00F8 .. 0x00FF Surveillance screens 0 .. 7
0x0100 .. 0x017E Text message from resource 0x0877
0x017F Random number for CPU rooms, level 1-6 before destroying nodes.
0x0180 .. 0x01FF Text message, scrolling vertically
For surveillance screens, the sources of screens 0-7 are specified in the [Surveillance Sources Table](../../archives/surveillanceCameras.md).
The following big items support displays:
7/0/6 (TV), 7/0/7 (COMPUTER MONITOR),
7/2/6 (SCREEN, small), 7/2/8 (SCREEN, large), 7/2/9 (SCREEN, medium),
with variation: 7/5/5 (CONTROL PANEL), 7/5/6 (CONTROL PEDESTAL) (see below)
### Electronics 7/0/x
> Apart from some displays, nothing special
### Furniture 7/1/x
#### Cabinets 7/1/2
**Cabinet Data** (10 bytes)
0000 [2]byte Unused
0002 [2]int16 Object index
0006 [4]byte Unused
Cabinets can contain up to two objects.
#### Texturable furniture 7/1/5, 7/1/7, 7/1/8 (chairs and couches)
**Texturable Furniture Data** (10 bytes)
0000 [6]byte Unused
0006 int16 Texture index
0008 [2]byte Unused
If ```Texture index``` is not zero, it specifies the alternate texture to use for the "main" faces of the furniture.
### Surfaces 7/2/x
These are big objects that are flat and should be placed at another surface.
#### Words 7/2/3
**Words BigStuff Data** (10 bytes)
0000 int16 Text index (within resource 0x0868)
0002 int16 Font and size (bits 0-3 are font, 4-7 are size)
0004 int16 Colour (palette index, 0 defaults to red)
0006 [4]byte Unused
#### Texture Maps 7/2/7
**Texture map big stuff** (10 bytes)
0000 [6]byte Unused
0006 int16 Texture index
0008 [2]byte Unused
Texture maps allow to place one tile from the level texture list at any arbitrary position.
### Lighting 7/3/x
### Medical equipment 7/4/x
#### Control Pedestals (buttons only) 7/4/0
These can trigger an object if used.
**Button Control Pedestal Info** (10 bytes)
0000 [2]byte Unused
0002 int16 Trigger object index
0004 [6]byte Unused
#### Surgery Machines 7/4/3
**Surgery Machine Info** (10 bytes)
0000 [2]byte Unused
0002 byte Broken state; 0x00 = OK, 0xE7 = broken
0003 [2]byte Unused
0005 byte Broken message index; Broken default: 0x61
0006 [4]byte Unused
With all bytes zero, the surgery machine works normally.
When the ```Broken state``` field is set to a certain value (the game has ```0xE7``` for the one on level 2, though some other work, too), then the machine wouldn't work. When not working, the message identified with ```Broken message index``` is displayed in the HUD. (```0x61``` = "This machine is broken").
#### Textureable Furniture 7/4/5 (chairs)
These have the same layout as the general textureable furniture (see above).
### Science and security equipment 7/5/x
#### Security Cameras 7/5/4
**Security Camera Info** (10 bytes)
0000 [2]byte Unused
0002 byte Panning switch; 0x00: stationary, 0x01: panning
0003 [7]byte Unused
#### Control Pedestals (with display) 7/5/5, 7/5/6
Control pedestals are similar to displays regarding the source of the display.
Instead of having different loop options, they trigger an object if used.
**Display Control Pedestal Info** (10 bytes)
0000 int16 Frame count
0002 int16 Trigger object index
0004 int16 Alternation type
0006 int16 Picture source
0008 int16 Alternate picture source
### Garden big stuff 7/6/x
### Bridges 7/7/x
#### Solid Bridges 7/7/0, 7/7/1
**Solid Bridge Info** (10 bytes)
0000 [2]byte Unused
0002 byte Size. Bits 0-3: X, bits 4-7: Y
0003 byte Height. 0 is default, level height units otherwise.
0004 byte Top/Bottom texture
0005 byte Side texture
0006 [4]byte Unused
The size fields are defined as 0: 3D model default, 4: tile width.
The texture fields have bit 7 set to indicate texture from level texture list. If 0, the other 7 bits index
into ```citmat.res```.
> If either textures are to be taken from the level list, the object description will be that of the textures.
> If a texture is to be taken from citmat.res, the texture index must be > 0, otherwise it will still be the first
> level texture, even if bit 7 is 0.
#### Force Bridges 7/7/7, 7/7/8, 7/7/9
**Force Bridge Info** (10 bytes)
0000 [2]byte Unused
0002 byte Size. Bits 0-3: X, bits 4-7: Y -- see above
0003 byte Height. 0 is default, level height units otherwise.
0004 [2]byte Unused
0006 byte Colour
0007 [3]byte Unused
Object 7/7/8 is a placeholder for force bridges that have not been activated yet. These objects are invisible and are made to force bridges with the use of trigger action 24.
Force bridges of type 7/7/9 differ from the typical ones (7/7/7) in having a default height of zero and no animation when switched from type 8. They furthermore need a size explicitly set.
The engine supports several different values for ```Colour```. Used values are ```0x00``` for red and ```0xFE``` for green.
> Not all 256 colour possibilities have been tested, many resulted in dark red tones or solid colours. Usable variants were found from ```0xF7``` and above.
@@ -0,0 +1,186 @@
## Level SmallStuff Table (Class 8)
```L18``` is a table describing general small stuff.
**Level SmallStuff Entry** (16 bytes)
0000 [6]byte Level object prefix
0006 [10]byte SmallStuff data
### Junk 8/0/x
#### Papers 8/0/2
**Paper** (10 bytes)
0000 [2]byte Unused
0002 int16 Paper ID
0004 [6]byte Unused
The ```Paper ID``` refers to a paper text entry and is the offset from resource ID ```0x003C```.
The referred text entries have multiple blocks and the engine simply concatenates all these strings to one text.
#### Briefcases 8/0/7
**Briefcase** (10 bytes)
0000 [2]byte Unused
0002 [4]int16 Object index
### Broken Stuff 8/1/x
Nothing special.
### Dead Bodies 8/2/x
#### Corpses 8/2/0 - 8/2/7
**Corpse** (10 bytes)
0000 byte Treasure type
0001 byte Unused
0002 [4]int16 Object index
The four ```Object index``` values determine the corpse's loot. More random loot can be added to empty slots based on the ```Treasure type```.
**Treasure Type Enumeration** (1 byte)
0xF5 No treasure
0xF6 Humanoid
0xF7 Drone
0xF8 Assassin
0xF9 Warrior cyborg
0xFA Flier bot
0xFB Security 1 bot
0xFC Exec bot
0xFD Cyborg enforcer
0xFE Security 2 bot
0xFF Elite cyborg
0x00 Standard corpse
0x01 Loot-oriented corpse
0x02 Repairbot
0x03 Serv-bot
> The treasure type values for corpses are the same as for [critter properties](../14_Critter/critterProperties.md), except offset by 11.
#### Severed Heads 8/2/13, 8/2/14
**Severed Head** (10 bytes)
0000 [2]byte Unused
0002 byte Image index
0003 [7]byte Unused
If ```Image index``` is zero, the generic mutilated head is shown. Otherwise, it is the index for a picture of a person, using that of e-mails.
### Consumables 8/3/x
Nothing special.
### Access Cards 8/4/x
**Access Card** (10 bytes)
0000 int16 Unknown
0002 sint32 Access mask
0006 [4]byte Unused
All access cards work the same way: When consumed, they add the given ```Access mask``` to the current priviledges and give a message whether and which new access was gained.
**Access mask** (sint32)
0x00000001 "None" - This priviledge is none and will not be added. Should not be used.
0x000000FE Various generic priviledges
0x00FFFF00 Used for "group" priviledges; Typically used with 8/4/10 (Group access card)
0x7F000000 Personal priviledges; 0x01: D'Arcy, 0x10: Diego
8/4/11 items (Personal access cards) have a special handling if personal priviledges are set; When identifying the object, the name of the person is appended to the title.
> 8/4/0 (Current access levels) works like any other access card, though it doesn't have a proper icon (only an 'X'). In the game it is used only on level 1 for a few cases, purpose unknown.
### Cyberspace Items 8/5/x
#### Integrity Restorative 8/5/1
**Integrity Restorative** (10 bytes)
0000 [2]byte Unused
0002 byte Restoration amount
0003 [7]byte Unused
Per default (```Restoration amount``` = 0), the item restores about 3/4 of integrity.
#### Security Defense Mine 8/5/2
**Security Defense Mine** (10 bytes)
0000 [2]byte Unused
0002 byte Damage amount
0003 [7]byte Unused
Per default (```Damage amount``` = 0), the item removes little amount (about one chevron).
#### Security ID Module 8/5/3
**Security ID Module** (10 bytes)
0000 int16 Unknown
0002 sint32 Access mask
0006 [4]byte Unused
Works like access cards - see above.
#### Cyber Info Nodes 8/5/6, 8/5/8
**Cyber Info Node** (10 bytes)
0000 [2]byte Unused
0002 int16 Text index
0004 [6]byte Unused
For 8/5/6 (information nodes), the text index is for text resource ```0x0878```.
For 8/5/8 (data fragments), the text index is for text resource ```0x087A```.
#### Cyberspace Barricades 8/5/9
**Cyberspace Barricade** (10 bytes)
0000 [2]byte Unused
0002 byte Size. Bits 0-3: X, bits 4-7: Y
0003 byte Height. 0 is default, level height units otherwise.
0004 [2]byte Unused
0006 byte Colour
0007 [3]byte Unused
Layout and properties are the same as for force bridges (7/7/7).
#### Cyberspace Decoys 8/5/10
**Cyberspace Decoy** (10 bytes)
0000 [2]byte Unused
0002 int16 Destroy object index
0004 [6]byte Unused
Normally, these cannot be destroyed in cyberspace. However, if placed in a map and made vulnerable to damage, decoys can be made to destroy another object when destroyed.
### Wear and Tear Stuff 8/6/x
Nothing special.
### Quest Items 8/7/x
Nothing special.
@@ -0,0 +1,11 @@
### Small Stuff Properties
#### Specific 5 Properties
These items (```8/5/x```) are cyberspace items.
**Cyberspace Items Specific Properties** (`CyberSmallstuffProp` struct) (6 bytes)
0000 [6]byte Colour scheme
```Colour scheme``` contains 6 palette indices from the game palette. The index into this list comes from the geometry properties, specifically from the [Set Colour and Shade](../../media/Geometry#set-colour-and-shade) command.
@@ -0,0 +1,266 @@
## Level Fixture Table (Class 9)
```L19``` is a table describing fixtures and electrical stuff.
**Level Fixture Entry** (30 bytes)
0000 [6]byte Level object prefix
0008 [24]byte Fixture Specific Info
### Buttons 9/0/x
Buttons come in various shapes and forms. All of them are triggers for actions and the entry data that of [actions](../Actions.md).
**Button Fixture Specific Info** (24 bytes)
0000 [22]byte Action data
0016 uint16 Access mask
The conditions for buttons are based on [game variables](../Conditions.md#game-variable-conditions).
```Access mask``` is an additional condition for the button. If not zero, the lower five bits can specify one required access level the hacker must have. The remaining 7 bits don't work properly or resolve to the "None" level.
### Receptacles 9/1/x, 9/4/x
Most receptacles work in conjunction with the quest items (8/7/x). With the exception of antenna relay panels, they contain regular actions as data.
Unless noted otherwise below, the conditions for receptacles are based on object types, with an optional trap message if the wrong item is used.
**Receptacle Fixture Specific Info** (24 bytes)
0000 [22]byte Action data
0016 [2]byte Unused
**Receptacle Fixture Condition** (4 bytes)
0000 byte Object type
0001 byte Object subclass
0002 byte Object class
0003 byte Trap message (optional)
#### Antenna Relay Panel 9/1/3
**Antenna Relay Panel Specific Info** (24 bytes)
0000 [6]byte Unused
0006 int16 Trigger object index 1
000A int16 Trigger object index 2
000E int16 Destroy object index
The two ```trigger object index``` values work only with trigger objects, they can't trigger arbitrary objects.
> Behaviour of relays is hardcoded: After accepting the plastique, their type changes to 9/1/4 and a level timer is started. When the timer expires, the panel explodes, the type changes again to 9/1/5, the two objects are triggered and the object-to-destroy is removed.
#### Retinal ID Scanner 9/1/6
When using a head item on the retinal scanner, the condition field is compared against the head's ```Current Frame``` value (for object 8/12/13), or ```Current Frame + 11``` (for object 8/12/14). Note that this is different from the ```Image index``` value which determines how the head appears in the MFD.
### Stations 9/2/x
The conditions for stations are based on [game variables](../Conditions.md#game-variable-conditions).
#### Cyberspace Terminals 9/2/0
**Cyberspace Terminal Specific Info** (24 bytes)
0000 byte State
0001 byte Unused
0002 [4]byte Condition
0006 int32 Target X
000A int32 Target Y
000E int32 Target Z
0012 int32 Target Level
0016 [2]byte Unused
The orientation of the player in the target cyberspace level can not be specified. The orientation will be taken over from the real world.
**State** (1 byte enumeration)
0: Off
1: Active (Normal state)
2: Locked. Trying to activate it gives an electrical shock.
#### Energy Charge Stations 9/2/1
**Energy Charge Station Specific Info** (24 bytes)
0000 [2]byte Unused
0002 [4]byte Condition
0006 int32 Power amount
000A int32 Recharge time in seconds
000E int32 Object index
0012 int32 Recharged timestamp
0016 [2]byte Unused
The object identified by ```Object index``` is triggered every time when energy is drawn from the station.
The ```Recharged timestamp``` will be set to the game time when the station is usable again. There is no dedicated timer running.
### Input Panels 9/3/x
The conditions for input panels are based on [game variables](../Conditions.md#game-variable-conditions).
**Input Panel Info** (24 bytes)
0000 [2]byte Unknown
0002 [4]byte Condition
0006 [18]byte Input panel info
#### Access Panels (Puzzles) 9/3/0 to 9/3/3, 9/3/9, 9/3/10
Puzzles are either "wire" or "block" puzzles. The type of the puzzle is determined by a bit within the info structure.
##### Wire Puzzles
**Wire Puzzle Input Panel Info** (18 bytes)
0000 int32 Target object index (object to toggle on success)
0004 byte Puzzle layout
0005 uint8 Target power level out of 0xFF
0006 uint8 Wire properties mask
0007 int8 Puzzle type - must be 0x00 for wire puzzle
0008 uint32 Target state of wires
000C uint32 Current state of wires
0010 [2]byte Unused
**Puzzle Layout** (1 byte bitmask)
0x0F Wire count; 0 default to 4 wires
0xF0 Connector count per size; 0 defaults to 6 connectors
**Wire properties mask** (1 byte bitmask)
0x0F Color wires; All pink if not set
0xF0 Solved state
> For the properties mask, any of the respective four bits needs to be set to count for the whole nibble.
> ```Solved state``` is always ```0xF0```, whereas ```Color wires``` is only set as ```0x01```.
Wire states are stored as three-bit pairs, ordered from LSB to MSB (right to left). The first pair describes the first
wire and the lower triple specifies the left connector.
This allows for puzzles up to 5 wires and 8 connectors, although only up to 6 connectors are visible.
##### Block Puzzles
**Block Puzzle Input Panel Info** (18 bytes)
0000 int32 Target object index (object to toggle on success)
0004 int16 State store object index (refers to a null trigger)
0006 byte Unused - 0x00
0007 int8 Puzzle type - must be 0x10 for block puzzle
0008 int32 Puzzle layout
000C [6]byte Unused
**Puzzle Layout** (4 byte bitmask)
0x00000001 Puzzle solved flag ("Circuit Activated")
0x0000000E Unknown
0x00000070 Coordinate of source connector
0x00000180 Source location
0x00000E00 Unknown
0x00007000 Coordinate of destination connector
0x00018000 Destination location
0x00700000 Width
0x07000000 Height
0x70000000 Side effect type
The location values are 0: top, 1: bottom, 2: left, 3: right
Side effects are:
0: Toggle all blocks horizontally/vertically from the changed one (across the board)
1: Toggle all surrounding 8 blocks
2: Toggle all blocks diagonally from the changed one (across the board)
3: Toggle all blocks horizontally, vertically and diagonally across the board
4: None
The state of the puzzle is stored in the referred ```State store object```, a trigger with no action.
The trigger info block is repurposed and at its offset ```0006``` there are 4 uint32 fields, concatenated to a 128 bit array.
Starting with the LSB from this bit array, 3 bits are repeatedly taken to fill the blocks from left to right, top to bottom.
This allows a maximum puzzle size of 42 blocks (6x7 or 7x6).
The blocks are:
0: Nothing (empty)
1: Inactive (x)
2: Active (+)
3: Active (+) - not used
4: Full node
5: Full node - not used
6: Hollow node
7: Hollow node - not used
```Full nodes``` require only one incoming connection to forward power to the other three neighbours.
```Hollow nodes``` require two incoming connections to forward power to the other two neighbours.
#### Elevator Panels 9/3/4, 9/3/5, 9/3/6
**Elevator Input Panel Info** (18 bytes)
0000 int16 Destination object index 2
0002 int16 Destination object index 1
0004 int16 Destination object index 4
0006 int16 Destination object index 3
0008 int16 Destination object index 6
000A int16 Destination object index 5
000C int16 Accessible bitmask (which floors are accessible)
000E int16 Elevator shaft bitmask (which floors does the shaft connect)
0010 [2]byte Unused
If a floor bit is set in the ```Elevator shaft bitmask``` field but not in the ```Accessible bitmask``` field, using
that button gives the "Shaft damage" message. The MFD is capable of displaying up to 12 floors.
The ```Destination object index``` values refer to the object index of the elevator panels in the other levels. Note the swapped order of the index values. Presumably the six values are loaded as three int32 values first, and then the index values are extracted. The Nth bit from the accessible bitmask, counted from LSB, references the Nth destination object index.
For an elevator panel to work properly (and not crash the game), the following conditions must be met:
* The panel must be in a tile which has the elevator music set. (Music index ```0xF``` in tile flags.)
* The panels envolved in a level transition must reference each other. In other words, all panels of one connected elevator must have the same configuration.
> Referencing an object from another level requires an editor to keep track of inter-level references. Or burden the user.
> The game uses only up to three active floors.
> The elevator panel on level 1 has the first byte of the input panel structure set to ```0x01```. Purpose and effect unknown.
#### Number Pads 9/3/7, 9/3/8
**Number Pad Input Panel Info** (18 bytes)
0000 int16 3-digit combination 1
0002 int16 Trigger object index 1
0004 int16 3-digit combination 2
0006 int16 Trigger object index 2
0008 int16 3-digit combination 3
000A int16 Trigger object index 3
000C int16 Fail object index
000E [4]byte Unknown
The 3-digit combinations are stored in [Binary-Coded-Decimal](https://en.wikipedia.org/wiki/Binary-coded_decimal) right-bound,
so the combination ```451``` is stored as ```0x0451```.
A combination must be something else than "000" to be active ("000" is always wrong).
Combinations must not be entered in sequence, any of the three combinations is valid at any time.
If the ```Fail object index``` refers to a valid object, this one is triggered when a wrong combination was entered.
> One number pad on level 0 has the first byte of the input panel structure set to ```0x06```. Purpose and effect unknown.
### Cyberspace Switches 9/5/x
Cyberspace switches are triggers for actions and the entry data that of [actions](../Actions.md).
0000 [22]byte Action data
0016 [2]byte Unused
The conditions for cyberspace switches are based on [game variables](../Conditions.md#game-variable-conditions).
@@ -0,0 +1,29 @@
## Level Door Table (Class 10)
```L20``` is a table describing doors and barriers.
**Level Door Entry** (14 bytes)
0000 [6]byte Level object prefix
0006 [8]bytes Door Data
**Door Data** (8 bytes)
0000 int16 Lock variable index
0002 byte Lock message
0003 byte Force door colour index
0004 byte Required access level
0005 byte Auto-close time; in units of approximately 0.5 seconds - common default is 0x0C
0006 int16 Second door object index
The ```Lock variable index``` determines whether a door is locked. If the referenced boolean variable is 1, then the door won't open when used. If the door is triggered by other means, the door is unlocked and the index is reset to zero.
Using the door while being locked, the message according to ```Lock message``` is shown. The message index is into the text resource ```0x0871```, with a block offset of ```7```. (```0x00``` gives the default "The door is locked" at index ```7```).
The ```Required access level``` indicates which [access level](../content/AccessLevels.md) the hacker needs to activate
(open) the door. If zero, no special access is required. This values is ```0xFF``` for doors "locked by SHODAN level security".
Such doors have a separate trigger referring these doors.
The ```Second door object index``` refers to a second door to trigger when activating this one.
Bulkhead doors for instance reference each other this way.
@@ -0,0 +1,27 @@
### Animating Properties
#### Generic Animating Properties
**Generic Animating Propertes** (`AnimatingProp` struct) (2 bytes)
0000 byte Frame time
0001 byte Flags
The `frame time` specifies the time of a single frame of the animation. A value of `0xFF` means a time of approximately 0.9 seconds. All animations have this value set to `0x1E` (30).
**Animating Flags** (1 byte)
0x01 Emit light; The animation lights up the area with one tile radius for its duration.
> Light is only emitted by objects placed by the engine. This flag is only set for such temporary animations, such as explosions (`11/2/x`), but works for impacts as well (`11/1/x`).
#### Specific 2 Properties
"Explosion" animations.
**Specific 2 Properties** (1 byte)
0000 byte Frame explode
@@ -0,0 +1,136 @@
## Level Trap Table (Class 12)
```L22``` is a table describing trap items and general marker.
**Level Trap Entry** (28 bytes)
0000 [6]byte Level object prefix
0006 [22]byte Trap data
### Infrastructure 12/0/x
#### Critter AI Hints 12/0/7
**Critter AI Hint Data** (22 byte)
0000 [6]byte Unused; A few have the first byte 0x06
0006 int16 Next object index
0008 [10]byte Unused
0012 int16 Trigger object flag -- 0: off, 1: on
0014 int16 Trigger object index
AI hint marker are arranged to form a loop with their ```next object index``` fields. They describe a patrolling path for critters.
If the ```trigger object flag``` is set to ```0x01```, the object identified by ```trigger object index``` will be triggered approximately 1.5 seconds after a critter entered the tile this marker is in.
> These patrolling paths appear to work only under special circumstances. They are rarely used in the main game and so far have been found to properly work only on level 9 for a mutated cyborg.
> The main reason for these hints not properly working is that roaming critters only walk when the hacker is within their vicinity - which often already makes them hostile. The cyborg on level 9 initially is docile, thus not concerned about a (passive) hacker. Its 4 state bytes are ```00 00 04 00``` and requires the second loot object to point to the first hint.
> Critters take these hints as such - they first walk into their direction, and continue to walk after passing them. Without a new hint, they will wander off in random directions. The cyborg on level 9 properly walks back and forth because its linear back-and-forth path has a hint for ever tile, and the two edges of the path are in coves, within which the cyborg bumps against the walls until properly turned around.
#### Repulsors 12/0/10
**Repulsor Data** (22 byte)
0000 [10]byte Unused; A few have the first byte 0x06 without effect
000A int32 Start height
000E int32 End height
0012 int32 Repulsion flags; 0x00000001: disabled (float down); 0x00000008: strong repulsor
The ```start height``` and ```end height``` values have a unit of ```0x00010000``` for one tile height. An ```end height``` of zero means endless.
Both values are measured from the floor. Repulsion only happens if the hacker is within these limits.
#### Triggers 12/0/x (with exceptions)
The type of the trigger object determines its cause. The trap data is then that of [actions](../Actions.md).
Trigger Types:
0: Entry; Hacker enters tile (mid-air or per foot)
1: Null; Must be set off externally. Also used as data storage.
2: Floor; Hacker touches floor of this tile (per foot exclusively)
3: Player Death; Used to resurrect Hacker
4: Death Watch; When certain objects (or types of objects) are destroyed. Common for CPU nodes.
5: Area Enter; Hacker enters (or leaves) a given radius
6: Area Continuous; Hacker enters any tile inside (or outside) a given radius
7: This is not a trigger! See AI hints above.
8: Level Entry; Used for instance to initialize starting health
9: Continuous; Unused
10: This is not a trigger! See repulsors above.
11: Ecology; Certain objects go extinct
12: SHODAN; Triggered when level security changes
##### Conditions
Nearly all triggers have conditions based on [game variables](../Conditions.md#game-variable-conditions).
The following exceptions exist:
* 4 (Death Watch Trigger): A union of [object type][obj-type-cond] and [object index conditions][obj-index-cond]
* 5, 6 (Area Trigger): A radius around the trigger. See below.
* 11 (Ecology Trigger): A combination of [object type][obj-type-cond] and a limit value. See below.
[obj-type-cond]: ./Conditions.md#object-type-conditions
[obj-index-cond]: ./Conditions.md#object-index-conditions
##### Area Triggers
These triggers activate when the hacker enters or leaves a circular area around the trigger. The radius of this area is determined by the condition.
**Area Trigger Condition** (4 bytes)
0000 sint32 Radius (in tiles)
If the ```radius``` is between 1 and 4095, the trigger activates when the hacker is within a circular area of that size around the trigger's tile. A negative radius will activate the trigger when the hacker is outside of the circle instead (e.g. if the radius is -5, the trigger activates when the hacker is at least 5 tiles away from the trigger).
Trigger type 5 ("Area Enter") activates once the hacker either enters (positive radius) or leaves (negative radius) the area. Type 6 ("Area Continuous") activates when the hacker enters any tile that is either inside (positive radius) or outside (negative radius) of the area.
##### Ecology Triggers
These triggers watch a certain type of objects as their condition, together with a limit value. Whenever the current count of these objects is changed to a number below the limit value, the action is executed.
**Ecology Trigger Condition** (4 bytes)
0000 byte Type
0001 byte Subclass
0002 byte Class
0003 byte Limit
Note: The action is executed more than once. How often the action is executed is dependent on the delta. If the new object count equals to (limit-1), then the action is executed twice. If the new object count is less than that, the action is executed three times.
> The layout for the C/S/T is the common one in an int32 value, with the high-byte representing the limit value.
> The game watches only critters, typically to spawn new ones. The trigger also works with other classes.
### Trip Beams 12/1/x
These are not used in the game.
### Map Marker 12/2/x
Only two types are used in the game.
#### Map Notes 12/2/3
**Map Note Marker Data** (22 bytes)
0000 [18]byte Unused
0012 int32 Entry offset
The ```Entry offset``` points into ```L46``` ([map notes](mapInformation.md#map-notes)) for the text entry. When a map note is removed and the whole table is defragmented, all the entry offsets are changed accordingly.
#### Music Voodoo Marker 12/2/4
**Music Voodoo Marker Data** (22 byte)
0000 [6]byte Unused
0006 byte Music flavour
0007 [15]byte Unused
The ```Music flavour``` is an index, with a range of ```0x00```..```0x04```, referencing patches of "music" resembling industrial noise.
They have a rectangular area of effect which covers 14x14 tiles, centered on the tile of the trap.
The music is only played if the current tile has a music index of zero.
> This is used extensively on maintenance level, the 4 flight decks, as well as for the maintenance tunnels on the groves.
@@ -0,0 +1,25 @@
## Level Container Table (Class 13)
```L23``` is a table describing containers.
**Level Container Entry** (21 bytes)
0000 [6]byte Level object prefix
0006 [4]int16 Contained object indices
for crates (13/0/x):
000E byte Width
000F byte Depth
0010 byte Height
0011 byte Top/Bottom texture number
0012 byte Side texture number
For the crate-specific values (dimensions and textures), if a value is 0, the default value from the 3D model is used.
The textures are taken from the resources in ```citmat.res```, based on ```0x0884```.
> Texture numbers from ```0x80``` have been found to take the textures from the level texture list.
> It is unclear if this is intentional or a side-effect of how data is arranged in the engine.
> Barrels (13/1/x) can not be inspected and may still contain items. Destroying a barrel drops its contents.
> There is one radioactive waste barrel (13/1/0) on level 4 which "contains" a hornet clip, which is lying right next to it.
> This is most likely the result of a mistake.
@@ -0,0 +1,113 @@
### Critter Properties
#### Generic Critter Properties
**Generic Critter Propertes** (`CritterProp` struct) (75 bytes)
0000 byte Intelligence; unused
0001 [21]byte Primary attack info
0016 [21]byte Secondary attack info
002B uint8 Perception
002C uint8 Defence
002D sint8 Projectile source height offset
002E int32 Flags
0032 byte Mirrored views; unused
0033 [8]uint8 Frame numbers; Amount for each posture
003B uint8 Animation speed
003C byte Attack sound index
003D byte Near sound index
003E byte Hurt sound index
003F byte Death sound index
0040 byte Notice sound index
0041 int32 Corpse triple - 0x00CCSSTT; zero for "none"
0045 byte View count; for multi-view postures
0046 byte Secondary attack probability
0047 byte Disrupt probability
0048 byte Treasure type
0049 byte Hit effect -- 0x00: meat, 0x01: plant, 0x02: robot, 0x03: cyborg, 0x04: generic
004A byte Attack key frame
`Perception` is a probability whether an AI detects hacker when visible during an AI interval.
The `projectile source height offset` is a vertical offset specifying from where projectiles should be launched.
> This is set to some positive values for the Elite Guard and Mutated Cyborg, so that their projectils appear from their heads.
The `animation speed` specifies the time of a single frame of an animation. A value of `0xFF` means a time of approximately 0.9 seconds.
For all `sound indices`, a value of `0xFF` indicates "no sound".
> No enemy has a sound set for being hurt in the main game.
`View count` determines how many frames are available per viewing direction. Cyberspace entities, as well as unused critters, have this set to `0x01`, all others have `0x08`. Changing these values messes up rendering of the critter, and/or all subsequent critters. The sequence of the corresponding images will have to be adjusted accordingly.
The `secondary attack probability` determines how likely the critter will use its secondary attack when close to the hacker. `0xFF` means always, `0x00` will have the critter use the secondary attack only when farther away.
The `disrupt probability` determines how likely the critter will be interrupted when being hit. `0xFF` means always, `0x00` will have the critter execute its attacks and movement without interruptions.
`Treasure type` is an enumeration value (ranging from `0x00` to `0x0E`), pointing into a hardcoded pool of possible loot. See below for the possible constants. The concrete loot is randomized accordingly.
The `attack key frame` value is an index into the attack animation, when the corresponding attack should take effect.
**Critter Attack Info** (`CritterAttack` struct) (21 bytes)
0000 uint8 Damage type
0001 uint8 Special damage type
0002 [2]byte Unused
0004 int16 Damage modifier
0006 uint8 Offence value
0007 uint8 Penetration
0008 uint8 Attack mass
0009 int16 Attack velocity
000B uint8 Accuracy
000C uint8 Attack range
000D int32 Reload time, with 0x100 being approximately 0.9 seconds
0011 int32 Projectile triple -- 0x00CCSSTT; zero for "none"
The `damage type` seems to be compatible with the damage type of [generic weapon info](../GenericWeaponInfo.md). Yet all damage types that affect hacker are handled equally; The two damage types `0x04` and `0x08` have no effect at all.
> The main game only uses damage types `0x01` and `0x02` (and some combinations). `0x04` is also set for Diego as well as SHODAN, though this has no further effect.
The `offence value` is compared to the defence value of the target object. See [common properties](../../fileFormat/PropertyFiles.md#common-table) for details.
An `accuracy` of `0x00` will still have the critter hit, though not that often and with less damage.
The critter will want to get within the `attack range` to perform its attack.
**Critter Properties Flags** (4 bytes)
0x00000001 Flying
> There are further constants defined, though not in use by the engine.
> The properties file has some critters with flag `0x00000004` set, which indicated "small" critters.
**Treasure Type Enumeration** (1 byte)
0x00 No treasure
0x01 Humanoid
0x02 Drone
0x03 Assassin
0x04 Warrior cyborg
0x05 Flier bot
0x06 Security 1 bot
0x07 Exec bot
0x08 Cyborg enforcer
0x09 Security 2 bot
0x0A Elite cyborg
0x0B Standard corpse
0x0C Loot-oriented corpse
0x0D Repairbot
0x0E Serv-bot
#### Specific 3 Properties
These critters (`14/3/x`) are for cyberspace.
**Cyberspace Critters Specific Properties** (`CyberCritterProp` struct) (6 bytes)
0000 [3]byte Colour scheme
0000 [3]byte Alternative colour scheme
The `colour scheme`s contain 3 palette indices from the game palette. The 'alternative' colours are used for critters attacking or being hostile.
The index into this list comes from the geometry properties, specifically from the [Set Colour and Shade](../../media/Geometry#set-colour-and-shade) command.
@@ -0,0 +1,89 @@
## Level Critter Table (Class 14)
```L24``` is a table describing critters.
**Level Critter Entry** (46 bytes)
0000 [6]byte Level object prefix
0006 [40]byte Level critter data
**Level Critter Data** (40 bytes)
0000 int32 Pending Z-rotation. Values up to 0x608000 seen.
0004 int32 Fixed forward velocity. Rare values of 0x0D000 and 0x090000 seen.
0008 [2]byte Unknown, Unused
000A int16 Hastiness. Values of 0x0050 and 0x0030 seen.
000C int16 State timeout
000E int16 Unknown - set to 0x0002 for a few critters.
0010 int16 Unknown - set to arbitrary values.
0012 [2]byte Unused
0014 byte Roaming critter state
0015 byte Primary critter state
0016 byte Secondary critter state
0017 byte Tertiary critter state
0018 [6]byte AI coordinates A
001E byte Always 0xFF
001F byte AI coordinates B
0020 [2]int16 Loot object indices
0024 [2]byte Unknown, Unused
0026 [2]byte Unknown, set only for a few roaming critters.
```Pending Z-rotation``` is a rotation value that the critter wants to perform next. Only when this value is zero, it continues with its regular state.
```Fixed forward veloctiy``` is used for a few critters to give them a fixed forward speed. This speed can be different than their normal speed, and even make them slower than regular.
```Hastiness``` makes a few critter appear as if they were under the effect of a speedup-stimulant. Their actions, including their animations, are quicker.
```State timeout``` is an indicator on how long the critter will stay in this state. Observed only in "cautious" state.
This time is reduced as long as the hacker is in the vicinity. When the time reaches zero, the critter becomes hostile.
The time value is typically quite low. A few high values like 0x0062 have been observed.
```AI coordinates A``` and ```B``` appear to be some sort of coordinates. Changing them has no visible effect, so they may be scratch values for the AI, determining where to go next. They are used only for roaming critters.
```Always 0xFF``` must always be set to ```0xFF```, otherwise the critter is immediately hostile, regardless of where it is on the map. All critters in the main game have it set to ```0xFF```.
### Critter states
The state a critter is in is goverend by four bytes, which have meanings on their own, as well as combined effects. For simple cases, only the ```Primary critter state``` is used. This value is also the one modified by the "Set critter state" action.
A roaming critter has the ```Roaming critter state``` value set, and also needs other states to be set. Working combinations found so far are:
* ```03 01 01 00```
* ```02 01 01 00``` (not found in archive.dat)
A critter only roams on its own if the hacker is within a radius of about 6 tiles.
A patrolling critter follows a path laid out by ```Critter AI Hint``` marker (See [traps](../12_Trap/levelTrapEntry.md#critter-ai-hints-1207)). The first marker of such a path must be referenced in the second ```loot object index```.
> The only working example of such a critter is initially docile, and has a state combination of ```00 00 04 00```.
The ```Primary critter state``` holds the state the critter is currently behaving in. This state is also reported by the targeting tool.
**Primary Critter State** (1 byte)
0x00 docile
0x01 cautious
0x02 hostile
0x03 cautious (?)
0x04 attacking
0x05 sleeping
0x06 tranquilized
0x07 confused
> Various combinations of the state values can be found in the main game. While they have different effects, their pattern is not clear.
**Critter Mood Enumeration**
0 Friendly
1 Neutral
2 Hostile
3 Isolation
4 Attacking
**Critter Orders Enumeration**
0 Guard
1 Roam
2 Sleep
3 Patrol between two points
4 Highway - Follow path
5 No move (guarding without moving)
+786
View File
@@ -0,0 +1,786 @@
## Actions
Level traps and various fixtures (buttons and the like) make use of actions that perform some state change and/or give some indication to the player. They are essentially what make a level (and the game in total) come to life.
Actions can be triggered by various sources. They may have conditions that first have to be met before the action is actually performed. The type of the action determines how to interpret the details (parameters) of the action.
### Quest Value Key
Some actions make use of quest value keys, which are used to either resolve a raw value, or a value from the game variables ("quest values").
* If bit ```0x1000``` is set in the key, the current integer game variable value of index ```key & 0x0FFF``` is referenced.
* If bit ```0x2000``` is set in the key, the current boolean game variable value of index ```key & 0x0FFF``` is referenced.
* For resolving values, if the key is less than 0x1000, then the resulting value is the value key itself.
### Action Data
**Action Data** (22 bytes)
0000 byte Action type
0001 byte Use quota
0002 [4]byte Condition
0006 [16]byte Action details
The ```Use quota``` indicates how often more this action can be executed. ```0``` means endless.
The value is only decremented if it is not zero and the condition was met.
If the value is decremented to zero after the action was executed, the object where the action is in is deleted.
Internally the ```action details``` are processed as four sint32 values. This documentation dissects them further where appropriate.
### Action Type 1: Transport hacker
**Transport Hacker Action Details** (16 byte)
0000 int32 Target X
0004 int32 Target Y
0008 int32 Target Z
000C int32 Target Level
For all three target axis values, if they are ```0x4000``` or greater, they are ignored and the respective axis is not changed in the transport.
The ```X``` and ```Y``` axes specify tile numbers, the ```Z``` axis is interpreted in level units.
> In the main game, this axis-preservation is used only on level 2 for the Z axis on the experimental transporter,
> which incidentally has both platforms at the same height.
If to be considered, the target values are then used as quest value keys to resolve the actual location for the respective axis.
> The main game does not make use of this quest value lookup feature. All encounterd values are direct values.
The ```target level``` specifies a cross-level transfer to the identified level if the value is smaller than ```0x1000```.
If it is ```0x1000``` or greater, transportation is only done within the same level, regardless of the value.
> Cross-level transport is used only for a same-level transfer on level 4 (restoration chamber), and twice for two groves, to
> transport the hacker to the restoration chamber on level 6. Other instances are set to ```0x2222```, which are ignored.
### Action Type 2: Change Health
This action is also called "damage", probably based on the first parameter.
**Change Health Action Details** (16 byte)
0000 int32 Damage delta
0004 int16 Health delta
0006 byte Health change flag; 0: remove delta, 1: add delta
0007 byte Damage type -- used if removing health
0008 int16 Power delta
000A int16 Power change flag; 0: remove delta, 1: add delta
000C int16 Fatigue delta
000E int16 Fatigue change flag; 0: add delta, 1: remove delta
All four ```delta``` values are used as quest value keys to resolve the actual delta value.
> The ```damage delta``` is not used in the main game.
> Although intended to cause explosion damage, due to mixed up parameters in the code, a fixed amount of damage is done,
> with the delta value being interpreted as damage type.
> Reducing health to/below 0 causes death. Reducing power below 0 causes an underflow and hacker is fully powered again.
### Action Type 3: Clone/Move Object
> This action is internally called "Create Object". Name not kept to describe actual functionality better.
**Clone/Move Action Details** (16 byte)
0000 int16 Source object index -- quest value key
0002 int16 Move flag
0004 int32 Target X tile
0008 int32 Target Y tile
000C int32 Target Z
The ```move flag``` has a dual purpose. First, it is used to determine whether the object shall be moved or cloned.
If it is zero, then the object is cloned; If it has bit ```0x1000``` set, then the object is cloned as well; All other values cause the source object to be moved.
If the object is cloned, then the ```move flag``` parameter will be used as quest value key to store the object index of the newly created object.
> The game archive has various values for the ```move flag```.
For all three target axis values, if they are ```0x4000``` or greater, they are ignored and the respective axis is not changed.
The ```X``` and ```Y``` axes specify tile numbers, the ```Z``` axis is interpreted in level units.
> In the main game, this axis-preservation is used with various values, including ```0x4400```.
If to be considered, the target values are then used as quest value keys to resolve the actual location for the respective axis.
The orientation and in-tile position of the source object will be kept.
### Action Type 4: Set Game Variable
> This action is internally called "Quest Bit". Name not kept to describe actual functionality better.
**Set Game Variable Action Details** (16 byte)
0000 int32 Quest value key
0004 int16 Value
0006 int16 Operation
0008 int32 Message 1 (within resource 0x0867)
000C int32 Message 2 (within resource 0x0867)
Depending on the ```quest value key```, different sets of variables are modified.
The variables are stored in the [Game State Structure](../../archives/gameState.md).
If no bit in the nibble of ```0xF000``` is set, the quest value key defaults to boolean variables.
> For the most part in the main game, the boolean variable selector ```0x2000``` is not set and thus relies on this default handling.
> There are a few cases where this boolean variable selector is set.
For the int16 variables, ```value``` must be in the range ```0..0x0FFF```. ```Operation``` can be one of the following:
0: Set value
1: Add value
2: Subtract value
3: Multiply with value
4: Divide by value (Resulting fractions are cut, divide by 0 is ignored)
5: Modulo by value
For the boolean variables, ```operation``` is ignored and always ```0```.
The values are either ```0``` or ```1``` to set that value, or ```2``` to toggle the current value.
If the resulting value is not zero, the (optional) ```message 1``` is played/shown. If the resulting value is zero, the (optional) ```message 2``` is played/shown. Both message parameters are quest value keys.
> Operations other than Set or Add are not used in the game for the int16 values.
### Action Type 5: Cutscene
**Cutscene Action Details** (16 byte)
0000 int32 Cutscene index; 0: Death, 1: Intro, 2: Ending
0004 int32 End game flag
0008 [8]byte Unused
```End game flag``` must be ```1```. Setting it to ```0``` leaves the game in an unplayable state after the cutscene.
What happens after the cutscene is dependent on the cutscene itself: The death and intro cutscenes return to the main menu,
the ending cutscene ends the game (showing score and credits).
> In the regular game, this action is used only once when defeating SHODAN.
### Action Type 6: Trigger other objects
**Trigger Objects Action Details** (16 byte)
0000 int16 Object 1 index
0002 int16 Object 1 delay
...
000C int16 Object 4 index
000E int16 Object 4 delay
The delays are all relative to the execution start of the action. Their unit is 0.1 of a second.
All four object indices are quest value keys. They are resolved when this action is executed, not after the delay.
### Action Type 7: Change lighting
**Change Lighting Action Details** (16 byte)
0000 int16 Light type extent - quest value key
0002 int16 Reference object index - quest value key
0004 int16 Transition type. 0x0000: immediate, 0x0001: fade, 0x0100: flicker
0006 int16 Transition state
0008 byte Light type
0009 byte Unused
000A int16 Light surface. 0x0000: floor, 0x0001: ceiling, 0x0002: floor and ceiling
000C [4]byte Light type parameter
These actions modify the light deltas of the affected tiles. Such actions can not change the base darkness
values of a tile. If a tile is fully bright by default, nothing can change that.
The configured light parameters are absolute values.
The ```transition state``` is a bitmask storing the state for a change.
**Transition state** (2 bytes)
0x0FFF Step counter
0x1000 0: light on, 1: light off
**Light type** (1 byte)
0x00 Rectangular
0x01 Linear gradient (East-West smooth)
0x02 Linear gradient (North-South smooth)
0x03 Radial gradient
**Gradient light type parameter** (4 bytes)
0000 byte Off light begin intensity
0001 byte Off light end intensity
0002 byte On light begin intensity
0003 byte On light end intensity
#### Rectangular light (type 0x00)
For this light type, the ```light type extent``` specifies a second object index. All tiles within this rectangle are affected.
**Light type parameter** (4 bytes)
0000 byte Off light value 0x00..0x0F
0001 byte On light value 0x00..0x0F
0002 [2]byte Unused
#### Linear gradient light (types 0x01, 0x02)
For this light type, the ```light type extent``` specifies a second object index to define the affected area.
For these types, the ```gradient light type parameter``` intensity values have a range of ```0x00```..```0x0F```.
> These light types are not used in the regular game.
#### Radial gradient light (type 0x03)
For this light type, the ```light type extent``` specifies a radius, in tiles, of affected tiles. The begin of the gradient is always the
tile of the triggering object (```reference object index``` is ignored).
The intensity values of the ```gradient light type parameter``` have a range of ```0x00```..```0x7F```.
A value of around ```0x14``` lights a tile as if it were fully lit.
Values above will spill over to the surrounding tiles.
### Action Type 8: Effect
**Effect Action Details** (16 byte)
0000 int16 Sound index, based on 0x00C9; 0: Play nothing
0002 int16 Sound play count; 0: endless
0004 int16 Visual effect
0006 [2]byte Unused
0008 int16 Additional visual effect
000A [2]byte Unused
000C int16 Additional visual effect time; 0: endless
000E [2]byte Unused
All parameters are used as quest value keys.
Visual Effects:
0: None
1: Power on: Dim level lights and fade back to normal; Plays generator sound as well
2: Quake: Shake cam and play rumble sound
3: Escape pod: Shows launch sequence, cam shake and abort; Plays SHODAN message afterwards
4: Red static (fullscreen)
5: Red static (interference)
Additional Visual Effects:
0: None
1: White flash
2: Pink flash
3: Gray static (fullscreen)
4: Vertical panning (including HUD)
### Action Type 9: Change tile heights
0000 int32 Tile X
0004 int32 Tile Y
0008 int16 Target floor height; 0..31, measured from floor absolute zero
000A int16 Target ceiling height; 1..31, measured from floor absolute zero
000C [2]byte Unused
000E int16 Silent flag; 0: play sound, 1: no sound
Tile coordinates, as well as the heights, are used as quest value keys.
For the height fields a value equal to, or greater than ```0x0100``` indicates "don't move".
> The ceiling height has also been encountered to be ```0x0020```, also not moving.
The ```silent flag``` only works for floor changes. If the ceiling changes height, it makes sound regardless of this parameter.
### Action Type 10: Change Terrain
0000 int32 Tile X
0004 int32 Tile Y
0008 int32 Tile type
000C int32 Terrain parameter (slope height)
All four parameters are used as quest value keys.
If ```tile type``` or ```terrain parameter``` has the bit ```0x4000``` set, the respective property will not be changed.
> This action is not used in the main game.
### Action Type 11: Scheduled Trap
**Scheduled Trap Action Details** (16 byte)
0000 int32 Object index
0004 int32 Time interval
0008 int32 Activation value
000C int16 Variance
000E [2]byte Unused
Parameters ```object index```, ```time interval```, and ```variance``` (if set != 0) are used as quest value keys.
The ```time interval``` specifies in 0.1 seconds units within which time span the identified object shall be triggered regularly.
The ```variance``` adds some randomness to the interval. Usually low numbers are encountered in the main game.
> The unit of the variance is that of the engine-internal ticks.
The ```activation value``` has three different uses:
* greater than or equal to ```0xFFFF```: endless scheduling.
* bit ```0x1000``` set AND boolean game variable identified by this value key is true: schedule trigger
* value between ```0x0000``` (exclusive) and ```0x1000``` (exclusive): remaining schedule amount, down to zero
> The game has the ```Activation value``` typically set to ```0xFFFF```, ```0x10000``` or ```0x11111``` in the main game.
### Action Type 12: Cycle Objects
> Internally, this action is called "Alternating splitter"
**Cycle Objects Action Details** (16 byte)
0000 [3]int32 Object indices
000C int32 Next object
All three ```object indices``` are interpreted as quest value keys.
This action triggers objects from the list of ```object indices```. The ```next object``` field indicates which entry to trigger next.
Afterwards, the ```next object``` field is incremented. It is reset to ```0``` if it is either greater than ```2```, or it is ```2``` and the third object index is ```0```.
### Action Type 13: Destroy Object
**Destroy Object Action Details** (16 byte)
0000 int16 Object 1 index
0002 int16 Unused
0004 int16 Object 2 index
0006 int16 Unused
0008 int16 Object 3 index
000A int16 Unused
000C int32 Message index (within resource 0x0867); 0: no message
All three ```object indices``` are interpreted as quest value keys.
### Action Type 14: Unused
> This action is not used and does nothing.
### Action Type 15: Receive EMail
**Receive EMail Action Details** (16 byte)
0000 int16 EMail index, based on 0x0989
0002 [2]byte Unused
0004 int16 Delay
0008 [10]byte Unused
The ```Delay``` is roughly counted in seconds. Tests showed their actual unit seems to be somewhere at 0.9 seconds.
### Action Type 16: Expose
**Change Effect Action Details** (16 byte)
0000 int16 Delta value
0002 int16 Effect change flag; 0: add delta, 1: remove delta
0004 int32 Effect type
0008 [8]byte Unused
This action exposes the hacker to some typed hazard or treatment.
Effect types are:
4: Radiation poisoning
8: Bio contamination
> The main game uses this only once to remove the radiation in level R - treatment area.
### Action Type 17: Set Object Parameter
**Set Object Parameter Action Details** (16 byte)
0000 int16 Object index 0
0002 int16 Object index 2
0004 int32 Value 1
0008 int32 Value 2
000C int32 Value 3
Both object indices are treated as quest value keys.
This action sets parameters of target objects. Which value is applied to which parameter is dependent on the target.
The following table lists the mappings.
| Target Class | Value 1 | Value 2 | Value 3 |
|:-------------:|-----------------|----------------------------------|-----------------|
| Big Stuff | Cosmetic value | Data1 | Data2 |
| Door | Locked | ```0xFF00```: Message index, ```0x00FF```: Cosmetic value | ```0xFF00```: Access level, ```0x00FF```: Auto-close time |
| Fixture, Trap | Parameter Number (1..4) | Parameter Value | Unused |
For ```big stuff``` and ```door``` classes, if a value is ```-1``` it doesn't modify the respective target parameter.
> Which parameters are specifially affected for fixtures and traps depends on their respective interpretation of the 16 bytes.
### Action Type 18: Set Screen Picture
> Internally, this action is called "animate".
**Set Screen Picture Action Details** (16 byte)
0000 int16 Screen 1 object index
0002 int16 Screen 2 object index
0004 int32 Single sequence source
0008 int32 Loop sequence source
000C int32 Unused
This action sets the picture source for the given screen object(s).
If provided, the ```Single sequence``` source is played first. If provided, the ```Loop sequence``` is played
next and looped. If no loop is provided, the last frame of the single sequence will be kept.
### Action Type 19: Hack
This action is a more generic one with several different interpretations, depending on the first field.
**Hack Action Details** (16 byte)
0000 int32 Hack type
0004 [12]byte Hack parameter
#### Hack Action Type 1: Toggle repulsor
**Toggle Repulsor Details** (12 byte)
0000 int32 Repulsor object index
0004 byte "Off" texture index (into level texture list)
0005 byte "On" texture index (into level texture list)
0006 [2]byte Unused
0008 byte Toggle type
0009 [3]byte Unused
**Toggle type** (1 byte)
0x00 Toggle On/Off
0x01 Toggle On, stay on
0x02 Toggle Off, stay off
#### Hack Action Type 2: Show Game Code Digit
**Show Game Code Digit Details** (12 byte)
0000 int32 Screen object index
0004 int32 Digit number 1..6 (= level number)
0008 [4]byte Unused
#### Hack Action Type 3: Set Fixture Parameter from Variable
**Set Fixture Parameter From Variable Details** (12 byte)
0000 int32 Fixture object index
0004 int32 Parameter number (1..4)
0008 int32 Game integer variable index
> This hack is used only twice, exclusively to set the codes for the reactor in the base game.
> Similar to the "Set Object Parameter" action, the interpretation of the modified parameter is depending on the action in the target fixture.
#### Hack Action Type 4: Set Frame State
> Although this hack is called "fixture frame", it doesn't check for the type of the target object and allows to modify any object's frame state.
**Set Frame State Details** (12 byte)
0000 int32 Button object index
0004 int32 New frame state; 0: off, 1: on
0008 int32 MFD update flag; 0: ignored, != 0: update MFD
#### Hack Action Type 5: Door Control
**Door Control State Details** (12 byte)
0000 int32 Door object index
0004 int32 Control value
0008 int32 Unused
Control values:
1: open door
2: close door
3: toggle door
4: disable auto-close
#### Hack Action Type 6: Return to main menu
This hack has no parameters (all 12 bytes 0x00) and directly returns to the main menu.
#### Hack Action Type 7: Rotate Object
**Rotate Object State Details** (12 byte)
0000 int32 Object index
0004 byte Amount
0005 byte Type -- 0x00: endless, != 0x00: back/forth
0006 byte Direction -- 0x00: forward, 0x01 when running backwards
0007 byte Axis -- 0x00: Z (yaw), 0x01: X (pitch), 0x02: Y (roll)
0008 byte Forward limit
0009 byte Backward limit
000A [2]byte Unused
This hack rotates an object along one axis. This rotation can be endless in one direction, or a back and forth rotation,
depending on the ```Type``` field.
If the rotation type is back and forth, then the object is rotated between the two limits. Should the object start outside of these
limits, then rotation continues normally until it is within the limits.
If the rotation type is endless, whenever one limit is reached, the object immediately jumps to an orientation as per the other limit.
#### Hack Action Type 8: Remove Critters
> This hack is called "armageddon" internally, most likely in a reference to a similar action in the game Lemmings.
**Remove Critters State Details** (12 byte)
0000 int32 Object type - 0x00CCSSTT
0004 int32 Radius
0008 int32 Unused
This hack removes critters of the specified object type from the level.
The ```radius``` specifies how many shall be removed around the tile of the action's object, in tiles.
> Avoid using high amounts. The engine can remove only a certain amount of objects in one tick (100) and has no guard if more are removed.
> A memory overrun occurs in this case.
#### Hack Action Type 9: SHODAN pixelation
This hack has no parameters (all 12 bytes 0x00) and fills the screen with an image. The used image is hardcoded.
The game returns to the main menu after the screen is filled.
It should be used only in cyberspace; Using it in real world messes up tile textures.
#### Hack Action Type 10: Set Condition
**Set Condition Details** (12 byte)
0000 int32 Object index
0004 [4]byte Condition
0008 [4]byte Unused
Sets the condition values of specified object (fixture or trap).
#### Hack Action Type 11: Show System Analyzer
0000 int32 Page index (0..2)
0004 [8]byte Unused
#### Hack Action Type 12: Make Area Radioactive
> Internally this hack is called "spew". Yet it only supports spillage of radiation.
**Make Area Radioactive Details** (12 byte)
0000 int32 Reference object index
0004 int16 Watched object index
0006 int16 Watched object trigger state
0008 int32 Spew type. Only value of 0 supported (= radiation).
The rectangular area defined by ```reference object``` and the object of the action emits radiation while ```watched object``` is in given state (1 = active/open).
> This is used only once, on level 2 for the small room in Beta quadrant. The door is both the reference as well as the watched object.
#### Hack Action Type 13: Oriented Trigger Object
> This hack is exclusively used on level 8 to trigger three different taunts from Diego.
> Hence it is also called "Diego Taunt" in the source.
**Oriented Trigger Object Details** (12 byte)
0000 uint16 Horizontal direction
0002 [2]byte Unused
0004 int32 Object index
0008 [4]byte Unused
This hack triggers the given object only if the player looks to the given ```Horizontal direction``` (+/- 45°).
Value ```0x0000``` specifies north, going clockwise with increasing numbers, meaning:
0x0000 North (matches between NW and NE)
0x4000 East (matches between NE and SE)
0x8000 South (matches between SE and SW)
0xC000 West (matches between SW and NW)
#### Hack Action Type 14: Close Data MFD
**Close Data MFD Details** (12 byte)
0000 int32 Displayed object index
0004 [8]byte Unused
If the specified object is currently displayed in the "Data" MFD, close it and return to previous display.
> This hack is used only once to close the keypad panel from the reactor if an invalid combination is entered.
#### Hack Action Type 15: Earth destruction by laser
This hack has no parameters (all 12 bytes 0x00) and lets the player receive the message about the fired laser.
The game continues after the message has played. Images and text are hardcoded.
#### Hack Action Type 16: Change Critter Type Global
**Change Critter Type Global Action Details** (16 byte)
0000 int32 Critter type - 0x00CCSSTT
0004 byte New type
0005 [7]byte Unused
This hack morphs all critters of given object type in the level and changes their type to the given one.
Class and subclass stay the same. Stats, such as healthpoints, are maintained as well.
If ```new type``` specifies a value greater than ```0x0F```, the object will be destroyed.
### Action Type 20: Set Tile Textures
> This action is not used in the main game.
**Set Tile Textures Action Details** (16 byte)
0000 int32 Area
0004 int32 Floor Operation
0008 int32 Wall Operation
000C int32 Ceiling Operation
The ```area``` defines a rectangular area of affected tiles.
If ```area``` is less than ```0x10000```, then only one tile is affected, identified by ```0xXXYY```.
Otherwise, ```area``` is interpreted as two object indices (interpreted as quest value keys), that define the area by their respective location.
The three ```operation``` fields identify, per texture, what to do.
**Texture Operation** (4 byte)
0000 int16 Source filter
0002 int16 Destination texture
If ```source filter``` is either equal to or greater than ```0x1000```, or the tile uses the texture identified by this filter, then set the texture according to ```destination texture``` if this value is less than ```0x1000```.
Said differently, try to set ```destination texture``` only if the value is less than ```0x1000```, and consider only tiles if set as a flood-fill, or if the source matches.
### Action Type 21: Set Critter State
**Set Critter State Action Details** (16 byte)
0000 int16 Filter parameter
0002 int16 Filter type
0004 int32 Area
0008 int32 New critter mood -- ignored if value >= 0x1000
000C int32 New critter orders -- ignored if value >= 0x1000
Sets the state of critters.
See [Critters](14_Critter/levelCritterEntry.md) for the enumeration values of critter states.
The ```filter type``` parameter defines via bits which critters should be considered and how the ```filter parameter``` is interpreted.
**Filter Type Bitfield** (2 byte)
0x0001 Consider loner critters only. If not set, only consider pack critters.
0x0002 Ignore loner/pack property of critter.
0x0004 If set, ```filter parameter``` identifies one specific critter to affect (regardless of remaining bits). Use area otherwise.
0x0008 Affect every critter in the area, regardless of subtype/type. (Note: only works for small areas)
If neither bits ```0x0004```, nor ```0x0008``` are set, then ```filter parameter``` specifies the subclass and type of affected critters (```0xSSTT```).
The ```area``` parameter defines the affected tiles in the following ways:
* All zero: affect entire level
* Lower 16 bits zero: upper 16 bits define half of the side-length of a square, with the tile of the action object at its center.
* Two 16 bit values identifying reference objects that define a rectangular area.
Unless the ```new critter mood``` is ```friendly```, mood is ignored on combat level 0.
### Action Type 22: Trap Message
> Internally, such messages are called "barks".
**Trap Message Action Details** (16 byte)
0000 sint32 Background image
0004 int32 Message index (within resource 0x0867)
0008 int32 Text colour
000C int32 MFD suppression flag; 0: Show in MFD, 1: Show only in HUD
The ```Background image``` and ```Text colour``` are only considered for display in MFD. The user needs to have messages set to "text" or "both" to see them.
For the ```Background image```, the following values are possible:
-1 SHODAN (black and white)
-2 Diego
> The game uses other image references as well, yet they don't work. This is due to the code being modified to ignore positive background image values.
### Action Type 23: Spawn Objects
**Spawn Objects Action Details** (16 byte)
0000 int32 Object type; Format 0x00CCSSTT
0004 int32 Area
0008 int32 Quantity - quest value key
000C int32 Flags
This action spawns a random number of objects, randomly distributed within an area.
The spawned objects are placed at the floor in the center of the tile.
The ```area``` field specifies a rectangular area, either based on two objects, or a square.
If ```area``` is less than ```0x10000```, then it specifies half of the final side-length of the resulting square, and the trap object's location is at the lower-right corner of this square. Otherwise, ```area``` specifies two object indices that define the rectangle with their locations.
Objects are only spawned if the ```Combat``` value is 1 or higher. If it is 3, then one more object than requested is spawned.
Not all classes can be spawned.
> Although the game uses enemies exclusively, ammo, explosives, and patches can be spawned as well.
**Spawn Objects Flags** (4 byte)
0x00000001 Avoid spawn if tile is within a distance of 7 based on the sum of X and Y deltas.
0x00000002 Avoid spawn if hacker looks at the tile within a distance of 14 based on the sum of X and Y deltas.
0x00000004 Allow spawn if tile is an elevator tile (identified by elevator music).
0x00000008 Force spawn, even if the ceiling is too low, or the tile contains already a critter.
### Action Type 24: Change Object Type
> Internally, this action is called "transmogrify".
**Change Object Type Action Details** (16 byte)
0000 int32 Object index
0004 int16 New type
0006 int16 Reset mask
0008 [8]byte Unused
This action sets, or toggles, the type of an object. It is primarily used to extend and retract force bridges.
> For bridges in their retracted state, the game uses "Elephant, Jorp" (```7/7/8```), an invisible placeholder type.
The ```Reset mask``` is used when the specified object is already at given type. If the specified object is already at given type,
then the new type is calculated as ```New type``` XOR ```Reset mask```.
> So, to toggle between ```0x08``` and ```0x09```, ```New type``` should be ```0x08``` and ```Reset mask``` must be set to ```0x01```.
> To toggle between ```0x08``` and ```0x07```, ```New type``` should be ```0x07``` and ```Reset mask``` must be set to ```0x0F```.
> The first byte of unused field at ```0008``` is set to ```1``` for only one force bridge in the game (on level 2) and is ignored.
> It appears that an earlier version of the code supported suppressing of animations.
@@ -0,0 +1,66 @@
## Conditions
Conditions are filters for [actions](Actions.md) and for some [fixtures](09_Fixture/levelFixtureEntry.md) that don't use actions.
Conditions can examine different aspects of the state of the game or (current) level.
### Game Variable Conditions
These conditions check the value of a game variable (also called "quest value").
**Game Variable Condition** (4 bytes)
0000 uint16 Variable key
0002 byte Value
0003 byte Failure message
The ```Failure message``` is a message (or indication) the player shall receive if the condition was not met.
```0x00``` means no indication, ```0xFF``` is used for security warnings (blocked by SHODAN security);
All other values are interpreted as text/audio message index.
**Variable key** (2 bytes)
0x03FF Variable index
0x0C00 Unused
0x1000 Variable type; 0: boolean variable, 1: int16 variable
0xE000 Comparison
**Comparison**
0: Variable must be equal to value
1: Variable must be less than value
2: Variable must be less than, or equal to, value
3: Variable must be greater than value
4: Variable must be greater than, or equal to, value
5: Variable must be not equal to value
6: Value must be greater than a random value of range 0..254 (variable value is ignored)
### Object Type Conditions
These conditions check for a specific object type.
**Object Type Condition** (4 bytes)
0000 int32 Class/Subclass/Type encoded as 0x00CCSSTT
Note that the most significant byte needs to be ```0x00```.
> These conditions are used for the quest items and for death watch trigger.
### Object Index Conditions
These conditions are level-specific and only used for death watch trigger.
**Object Index Condition** (4 bytes)
0000 int16 Object index
0002 byte Unused
0003 byte Condition marker = 0x01
As these conditions are used for the same type of triggers as the object type conditions, the ```condition marker``` identifies the class of condition.
@@ -0,0 +1,33 @@
### Generic Weapon Info
This structure is reused for various offensive items.
**Generic Weapon Info** (`COMBAT_DATA_FIELDS` macro) (8 bytes)
0000 sint16 Damage
0002 byte Offence value
0003 byte Damage type
0004 byte Special damage type
0005 [2]byte Unused
0007 uint8 Armour penetration
The `offence value` is compared to the defence value of the target object. See [Common Object Properties](../fileFormat/PropertyFiles.md#common-table) for details.
If `Armour penetration` is equal or higher than the `Armour` value of the object being hit, the full `Damage` is applied. Otherwise, the difference is subtracted from `Damage`. If the resulting value reaches 0 or lower, "No Damage" is displayed.
`Special damage type` matches the `Special vulnerabilities` sub-fields from [Common Object Properties](../fileFormat/PropertyFiles.md#common-table);
It is split up into `0x0F` primary damage (double damage) and `0xF0` super damage (quad damage).
> Similar to the vulnerabilities in the common object properties, the damage type is a single 32bit value from `0003` in the source.
**Damage Type Bitfield** (1 byte)
0x01 Explosion (impact)
0x02 Energy
0x04 Magnetic (EMP)
0x08 Radiation
0x10 Gas
0x20 Tranquilizer
0x40 Needle
0x80 Bio
+99
View File
@@ -0,0 +1,99 @@
## Level Objects
A level object is anything that is not part of the basic level layout (the tile map). The game knows about 476 different objects.
Objects are identified using three values. They are divided by class, subclass and type. In this documentation such an identifier will be written as ```c/s/t```. For example, the energy charge station is ```9/2/1```.
> In the original source, such an identifier is called a ```TRIPLE```.
Information about level objects exists in two places:
* Static properties, describing an object in general
* Instance data, describing the state of a specific object instance in the game
The static properties are stored in a separate file. See [Property Files](../fileFormat/PropertyFiles.md) for basic information.
Instance data is stored as part of an archive. See [Level Objects](../archives/levelObjects.md) for basic information.
### Known Classes, Subclasses and Types
The following table lists the object classes together with the available types per subclass:
| Class | Name | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|:-----:|------------------------------------|----|----|----|----|----|----|----|----|
| 0 | Gun | 5 | 2 | 2 | 2 | 3 | 2 | - | - |
| 1 | Ammo | 2 | 2 | 3 | 2 | 2 | 2 | 2 | - |
| 2 | Physics | 6 | 16 | 2 | - | - | - | - | - |
| 3 | Grenade | 5 | 3 | - | - | - | - | - | - |
| 4 | Drug | 7 | - | - | - | - | - | - | - |
| 5 | Hardware | 5 | 10 | - | - | - | - | - | - |
| 6 | Software | 7 | 3 | 4 | 5 | 3 | - | - | - |
| 7 | Big Stuff | 9 | 10 | 11 | 4 | 9 | 8 | 16 | 10 |
| 8 | Small Stuff | 8 | 10 | 15 | 6 | 12 | 12 | 9 | 8 |
| 9 | Fixtures | 9 | 7 | 3 | 11 | 2 | 3 | - | - |
| 10 | Door | 10 | 9 | 7 | 5 | 10 | - | - | - |
| 11 | Animating | 9 | 11 | 14 | - | - | - | - | - |
| 12 | Trap | 13 | 1 | 5 | - | - | - | - | - |
| 13 | Container | 3 | 3 | 4 | 8 | 13 | 7 | 8 | - |
| 14 | Critter | 9 | 12 | 7 | 7 | 2 | - | - | - |
> This information is not stored within the resource files, it is hardcoded in the engine.
### Static Properties
The following table lists the byte sizes of the generic and specific property structures per class.
Many of these bytes are all zero in ```objprop.dat```; In these cases no detailed data definition will be linked.
| Class | Generic | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|:-----:|---------------------------------------------|-----------|-----------|-----------|-----------|-----------|-----------|----|----|
| 0 | [2](00_Gun/gunProperties.md) | 1 | 1 | [16][0/2] | [13][0/3] | [13][0/4] | [18][0/5] | - | - |
| 1 | [14](01_Ammo/ammoProperties.md) | 1 | 1 | 1 | 1 | 1 | 1 | 1 | - |
| 2 | [1](02_Physics/physicsProperties.md) | [20][2/0] | [6][2/1] | 1 | - | - | - | - | - |
| 3 | [15](03_Grenade/grenadeProperties.md) | 1 | [3][3/1] | - | - | - | - | - | - |
| 4 | 17 | 7 | - | - | - | - | - | - | - |
| 5 | 2 | 1 | 2 | - | - | - | - | - | - |
| 6 | 2 | 1 | 1 | 1 | 1 | 1 | - | - | - |
| 7 | 4 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 8 | 2 | 1 | 1 | 1 | 1 | 1 | [6][8/5] | 1 | 2 |
| 9 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | - | - |
| 10 | 1 | 1 | 1 | 1 | 1 | 1 | - | - | - |
| 11 | [2](11_Animating/animatingProperties.md) | 1 | 1 | [1][11/2] | - | - | - | - | - |
| 12 | 1 | 1 | 1 | 1 | - | - | - | - | - |
| 13 | 3 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | - |
| 14 | [75](14_Critter/critterProperties.md) | 1 | 2 | 2 | [6][14/3] | 1 | - | - | - |
> There is also the case of objects with a specific entry length of 0. This is the case for object types that didn't make it into the game (vending machines).
> Though, this is only a guess. Yet, the serialization code of the source-port (based on the mac-port) shows a hack by shifting the offset back by 2 bytes after serializing the doors. So, something must have been done in the original engine.
[0/2]: 00_Gun/gunProperties.md#specific-2-properties
[0/3]: 00_Gun/gunProperties.md#specific-3-properties
[0/4]: 00_Gun/gunProperties.md#specific-4-properties
[0/5]: 00_Gun/gunProperties.md#specific-5-properties
[2/0]: 02_Physics/physicsProperties.md#specific-0-properties
[2/1]: 02_Physics/physicsProperties.md#specific-1-properties
[3/1]: 03_Grenade/grenadeProperties.md#specific-1-properties
[8/5]: 08_SmallStuff/smallStuffProperties.md#specific-5-properties
[11/2]: 11_Animating/animatingProperties.md#specific-2-properties
[14/3]: 14_Critter/critterProperties.md#specific-3-properties
### Object Names
The names are localized and [stored as texts](../media/Texts.md). The short names are stored in resource ```0x086D```, the long
names are in resource ```0x0024```.
### Object Bitmaps
Every object has bitmaps associated with it. The bitmaps are all stored in resource ```0x0546``` in ```objart.res```, starting with block 1 for the first object. Each object has at least 3 bitmaps associated, with additional in-world bitmaps specified as per ```Extra``` field of the [Common Object Properties](../fileFormat/PropertyFiles.md#common-table).
As a result, to determine the proper block number for a given object, the total number of previous (extra) bitmaps needs to be known.
The first image is the standard image, the second (and additional) bitmaps are in-world bitmaps, and the last one is a micro-image.
> The micro-images are sometimes fully transparent. This has been found for marker objects for instance.
> Maybe a (future) mapping unit would have been able to show objects, except those that weren't meant to be seen by the player.
> For such transparent bitmaps, the first bitmap shows some abstract presentation. For example, the "continuous trigger" has the infinity symbol as first image.
> On the contrary, using the first bitmap exclusively for editors might not be usable either as they sometimes have the same image for different types (software instance).
Objects of render type ```Fragment``` have one extra bitmap. The first one contains the colour information and the extra bitmap the depth information; It is a "gray-scale" bitmap with ```0xD0``` as the center.
+97
View File
@@ -0,0 +1,97 @@
## Bitmaps
Bitmaps are images using a palette of colours. System Shock uses bitmaps exclusively for its graphics and uses one byte
as index per pixel - thus 256 colours possible. The resources store such bitmaps with content type ```0x02``` and they are always compound, even if only one bitmap is stored.
Bitmaps are stored with a header, the pixel data (sometimes compressed), and an optional private palette.
### Header
**Bitmap Header** (`FrameDesc` struct) (28 bytes)
0000 [4]byte Unused. Always zero. (used as pointer in the engine)
0004 byte Type -- 0x02: Uncompressed; 0x04 Compressed
0005 byte Align -- Unused
0006 uint16 Flags
0008 sint16 Width
000A sint16 Height
000C uint16 Stride
000E byte WidthFactor
000F byte HeightFactor
0010 [4]int16 HotspotBox: Left, Top, Right, Bottom
0018 sint32 Private palette offset
The ```Flags``` is set to value ```0x0001``` then palette index ```0x00``` should be treated as a transparent pixel instead of painting it black.
> Compressed images appear to default to transparency if the resolution of the game is 320x200. Higher resolutions require the flag set to behave identical.
>
> Other factors may also govern whether the bitmap should be treated with transparency - videos and level textures have their special handling in this regard.
The ```WidthFactor``` and ```HeightFactor``` specify the index of the highest set bit of ```Width``` and ```Height```
respectively. For example, if Width is 320 (0x140), then the WidthFactor is 8 (1<<8 == 0x100).
`HotspotBox` is a placeholder for different applications. It can define a rectangle, or it only defines a point (using the first two values) for anchoring.
> For the existing images in the original resource files, all images have a ```Stride``` equal to ```Width```.
### Pixel Data
After the header, the pixel data is serialized. If the image is uncompressed (Type ```0x02```),
```Height``` x ```Stride``` bytes make up the image. Compressed images (Type ```0x04```) use a simple run-length-encoding (RLE).
#### Pixel Compression
The compressed byte-stream contains the following byte sequences:
00 nn zz write nn bytes of value zz
nn .. 0 < nn < 0x80: copy the following nn bytes
80 lo hi lo hi are a little-endian uint16 nnnn, to be handled as follows:
80 00 00 End of compressed data (Skip to end)
80 lo hi 0 < hi < 0x80: skip (nnnn & 0x7FFF) output pixel
80 lo hi .. 0x80 <= hi < 0xC0: copy the following (nnnn & 0x3FFF) bytes
80 lo C0 hi == 0xC0: undefined (not encountered)
80 lo hi zz 0xC0 < hi: write (nnnn & 0x3FFF) bytes of value zz
nn 0x80 < nn: skip (nn & 0x7F) output pixel
Three cases exist to compress pixel data: skip a list of pixel, compress a list of a constant
value and copy an arbitrary array of bytes. For each of the three, two encoding variants are used: a short and a long form.
If a count is to be serialized that is higher than the long-form can store, the encoder uses the long-form to reduce this number to finalize with the short-form.
> Skipping output pixel is relevant for compressing video clips. Skipped pixel will retain the colour value from the previous frame.
> For still images, initialize the output buffer with 0x00.
>
> To implement a compressor that creates output closest to the original content, use the following sequence. The term "reference" means either a previous frame bitmap, or a fictual frame with all bytes 0x00.
> * From an array of bytes with length X, calculate the count Z of bytes at the end that are identical to reference. Compress only N bytes, where N = X - (Z % 0x7FFF).
> The final sequence ```80 00 00``` implies to skip to the end. The encoder
> seems to have had a limit of counting only up to 0x7FFF, so there may be further (long) skip entries before.
> * Iterate through the pixel data from start to N:
> * Count the extent of static value bytes
> * Count the number of bytes identical to reference
> * Determine encoding method
> * If the pixel value shall be retained from the reference, skip the count of bytes
> * Otherwise, if the count is more than three, encode the count and the constant value
> * Otherwise, count the extent of an arbitrary array; Abort when more than three static or identical values are found in sequence; Encode this array.
> * Skip the input bytes that have been serialized and continue with the loop
> * Encode end of compressed data sequence.
### Private Palette
The format also allows for an optional palette, which is specific for the bitmap. If an image has no private palette, then this
entry is not present and the resource data ends directly after the pixel data.
Without a private palette, the colours of the bitmap are context specific. See [Palettes](Palettes.md) for more information.
Private palettes are used for the splash screens at the beginning of the game, where nothing else is shown.
The ```Private palette offset``` in the bitmap header specifies the absolute offset *from the beginning* of the resource to the private palette entry. This means in case of a compound resource, this offset measures across the previous resource blocks as well.
**Private Palette** (772 bytes)
0000 [4]byte Unknown. Always 0x00, 0x00, 0x00, 0x01
0004 [256*3]byte RGB values
> The only working examples for this can be found in ```splash.res```.
>
> The resource file ```gamescr.res``` also contains three bitmaps with a private palette, but these palettes are not used and are ignored by the engine.
> The three bitmaps in question are the help overlay (```ALT+o```), and are shown with the game world in the background. Since in these
> cases the world can't change its colour, the screen can't use a private palette. By extension, the side-effects of the "Berserk" patch
> also affect the colours of the help overlay.
> Furthermore, for these three bitmaps, the values of offset fields are wrong as well.
+33
View File
@@ -0,0 +1,33 @@
## Fonts
This chapter documents the data format of the fonts. The game uses [bitmap fonts](https://en.wikipedia.org/wiki/Computer_font#Bitmap_fonts) which describe the characters.
The fonts are stored in single blocks within resources of type ```0x03```. They can be found in the file ```gamescr.res```.
All glyphs in the bitmap are lined up in a single line. There are no Y offsets per glyph, and the height is always equal to the height of the bitmap. An array of ```X-Offsets``` points to the start of a glyph and the width is equal the difference to the next offset.
### Binary Format
**Font Header** (84 bytes)
0000 uint16 Font Type -- 0x0000: Monochrome, 0xCCCC: Color
0002 [34]byte Unknown -- always 0x00
0024 int16 First character index (inclusive)
0026 int16 Last character index (inclusive)
0028 [32]byte Unknown -- always 0x00
0048 int32 Offset to X-Offsets, from beginning of data
004C int32 Offset to bitmap, from beginning of data
0050 int16 Width of bitmap in bytes
0052 int16 Height of bitmap in bytes
**X-Offsets** (2*N bytes)
The X-Offsets is an array of ```int16``` values. The length of the array is equal to the number of characters the font covers, plus one. The width of one glyph is always equal the difference to the next offset, even for the last one.
For a monochrome font, the offset is counted in bits, always beginning with the MSB.
**Bitmap** (W*H bytes)
Monochrome fonts have the glyphs packed as bits within the bytes, color fonts use the bytes as palette indices.
> Fonts that have an outline, primarily seen with the HUD fonts, don't store the outline information in the font bitmaps. The outlines are rendered at runtime.
+182
View File
@@ -0,0 +1,182 @@
## 3D Object Geometry
This chapter documents the data format of the three dimensional objects encountered in the game (tables, ICE in cyberspace, crates...).
All the geometry is stored in the file ```obj3d.res``` with content type ```0x0F```. Each resource in this file is compound with one data block. There is one object per resource (and data block).
Each three dimensional ```model``` consists of a list vertex definitions and a structure of ```nodes```, with the model itself being the root node. A node contains a list of ```anchors```, with each one having a normal vector and a reference point.
Up to exactly one anchor per node may branch out to two further nodes (creating a binary tree). Further anchors contain lists of rendered ```faces```. Faces are either texture mapped or filled with a single colour.
### Binary Format
The data starts with 8 bytes of header information, followed by a list of "commands" which describe the structure.
**Geometry Header** (8 bytes)
0000 int16 Unknown -- always 0x0027
0002 int16 Unknown -- always 0x0008
0004 int16 Unknown -- always 0x0002
0006 int16 Total number of Faces
> The ```Total number of Faces``` value does not seem to be authorative. There are a few entries in the resouce file which state a higher count than actually in the model.
**Model Command** (2+N bytes)
0000 int16 Model Command Identifier
0002 N Command specific content
> There is no framing structure which would allow skipping command entries, so an implementation has to know of all commands beforehand.
**fixed32/16** (4 bytes)
In the following, coordinates (and their offsets) are serialized as fixed floating point values in a 32/16 format.
0000 sint16 Integer value
0002 uint16 Fraction (in 1/65536 units)
> For a simple implementation, it is sufficient to read the value as a ```sint32```, convert it to floating point and divide by 65536.0 .
**Vector** (12 bytes)
A vector is serialized with three coordinates:
0000 fixed32/16 X
0004 fixed32/16 Y
0008 fixed32/16 Z
### Geometry Commands
#### Vertex Definitions
Vertices are always (and only) defined at the beginning of the model (the root node). Three ways exist: define a single vertex, multiple vertices or references to previous ones with offsets.
##### Define Single Vertex
0000 int16 Model Command Identifier -- 0x0015
0002 int16 Unknown -- always 0x0000
0004 Vector Vertex coordinates
##### Define Multiple Vertices
0000 int16 Model Command Identifier -- 0x0003
0002 int16 Number of vertices
0004 int16 Unknown -- always 0x0000
0006 [N]Vector Coordinates of vertices
##### Define Vertex (One Offset)
0000 int16 Model Command Identifier -- X: 0x000A, Y: 0x000B, Z: 0x000C
0002 int16 New vertex index
0004 int16 Reference vertex index
0006 fixed32/16 Offset value to coordinate as per command identifier
> In the existing files, the ```New vertex index``` has always been found to equal the current amount of known vertices, appending at the end of the list.
##### Define Vertex (Two Offsets)
0000 int16 Model Command Identifier -- XY: 0x000D, XZ: 0x000E, YZ: 0x000F
0002 int16 New vertex index
0004 int16 Reference vertex index
0006 fixed32/16 Offset value to first coordinate as per command identifier
0010 fixed32/16 Offset value to second coordinate as per command identifier
> In the existing files, the ```New vertex index``` has always been found to equal the current amount of known vertices, appending at the end of the list.
> To generate a list of vertex definitions as close as possible to the original files, follow these steps:
> * Determine count of dissimilar vertices:
> * From the list of vertices, iterate reverse from the tail.
> * For each vertex, search the list below the current index if there is another vertex which has at least one identical coordinate.
> * Abort this loop if the current vertex has no similar other. Vertices from beginning to the current index are all dissimilar.
> * Depending on the count of dissimilar vertices, define them with either the single definition or the multiple definition.
> * Now iterate forward through the list (until end), beginning with the first vertex after the dissimilar ones.
> * For each vertex, again find another most similar from those coming before it (including those already handled in this loop).
> * Compare the coordinates again.
> * If two are identical, write a single offset definition (for the one coordinate not equal).
> * If one is identical, write a double offset definition (for the two coordinates not equal).
>
> This list assumes the vertices are all unique and they were sorted to have the dissimilar ones come first. This must be done in
> combination with modifying the face definitions as they refer to the vertices via indices.
> Following this sequence will create a vertex definition with equal byte length as in the original. The differences to the original files are in which
> reference vertices were taken.
#### Node Control
##### End Of Node
0000 int16 Model Command Identifier -- 0x0000
Every node is finished with this command.
##### Define Node Anchor
0000 int16 Model Command Identifier -- 0x0006
0002 Vector Normal vector
000E Vector Reference point
001A int16 Byte offset to 'left' branch
001C int16 Byte offset to 'right' branch
The offset values are based on the start of the command. Branch data comes only after the current node has ben finished; There is always only one ```End Of Node``` command pending. Branches are serialized depth-first.
> In the existing files always the 'right' branch has been found to be serialized first. Although only a few cases, several layers can be found within one branch. If a branch is not needed, it contains only the ```End Of Node``` command.
#### Face Control
##### Define Face Anchor
0000 int16 Model Command Identifier -- 0x0001
0002 int16 Anchor byte length (measured from start of this command)
0004 Vector Normal vector
0010 Vector Reference point
001C N Face drawing commands
Faces within one ```Define Face Anchor``` command are either painted in a single colour (with an optional shade) or texture mapped.
##### Coloured Face
0000 int16 Model Command Identifier -- 0x0004
0002 int16 Vertex count
0004 [N]int16 Vertex indices
This command always has either a ```Set Colour``` or a ```Set Colour And Shade``` command before. It may be that several ```Coloured Face``` commands refer to the same setting.
##### Set Colour
0000 int16 Model Command Identifier -- 0x0005
0002 int16 Colour palette index
##### Set Colour And Shade
0000 int16 Model Command Identifier -- 0x001C
0002 int16 Colour palette cross index
0004 int16 Shade value (0..3)
The ```Colour palette cross index``` refers to a small lookup table from the corresponding static object properties. This lookup table is a list of always 6 palette indices.
> The data files contain cross indices of 0..6 . TSSHP skips those with value 0 and subtracts 1 from the remaining 6 values.
##### Texture Mapping
0000 int16 Model Command Identifier -- 0x0025
0002 int16 Number of entries
0004 [N]Entry Mapping entries
**Mapping Entry (10 bytes)**
0000 int16 Vertex index
0002 fixed32/16 texture u coordinate
0006 fixed32/16 texture v coordinate
##### Textured Face
0000 int16 Model Command Identifier -- 0x0026
0002 int16 Texture identifier
0004 int16 Vertex count
0006 [N]int16 Vertex indices
This command always has a ```Texture Mapping``` command before.
The ```Texture identifier``` references a texture with a resource identifier base of ```0x01DB```, found in ```citmat.res```.
The value of 0 specifies special, object specific handling.
+40
View File
@@ -0,0 +1,40 @@
## Palettes
The engine uses a graphic mode with 256 colors per pixel. The graphic resources (images and videos) use bitmaps together
with palette information to resolve a pixel value (1 byte) to the corresponding RGB value (3x1 byte).
Depending on the game-state, different palettes are in use. The splash screens at the beginning are created using a
```Private Palette``` (see the [bitmap](Bitmaps.md) format). Cutscenes have their own palette(s) and during the game,
one specific palette is used throughout. This game palette is stored in ```gamepal.res```.
### Format
Palettes always have a size of 768 bytes, for 256 colors with 1 byte for each of the three colour channels.
**Palette** (768 bytes)
0000 [256*3]byte RGB values
### Transparency
Palette index 0 is used as a marker for fully transparent pixel. For instance, various decoration or critter sprites need
to have transparent areas in order to lose the rectangular shape.
### Pixel Animation / Palette Looping
During the game various textures can be observed with small animations. Most typically seen with LED-like blinking effects.
Instead of implementing this with multiple frames, the engine simply loops through the colors within a few groups.
The following groups (palette index values) are looped:
0x03 - 0x07: Changed every ~1200 msec
0x0B - 0x0F: Changed every ~700 msec
0x10 - 0x14: Changed every ~360 msec
0x15 - 0x17: Changed every ~1800 msec
0x18 - 0x1A: Changed every ~1430 msec
0x1B - 0x1F: Changed every ~1080 msec
> These groups are looped even while the game is paused.
>
> Above switching times have been determined by counting frames of a video recording from dosbox, running within a VM.
> Exact values may be different.
+45
View File
@@ -0,0 +1,45 @@
## Sound Effects
Sound effects are stored in ```digifx.res```, which contains resources of data type ```0x07```.
The resources have IDs in the range from ```0x00C9``` to ```0x013B```, are uncompressed and have one effect per resource.
Each resource contains one sound effect, which is stored in the
[Creative Voice File](http://wiki.multimedia.cx/index.php?title=Creative_Voice) format.
Although the format would allow for different sample formats, only unsigned 8-bit PCM audio (MIME type ```audio/L8```)
is supported and expected. The sound effects don't use any special block types of the file format,
only one ```Sound data``` block is present, followed by a ```Terminator``` block identifier. All effects have one channel (mono).
### Format
The following is an extract of the above linked page, specialized to the use by the engine.
***Sound Effect***
0000 [19]byte Identifier text "Creative Voice File"
0013 byte Identifier terminator: 0x1A
0014 int16 Length of header (commonly 0x1A)
0016 uint16 Version number (0x010A)
0018 uint16 Version number validation
001A byte Sound data block type: 0x01
001B [3]byte Length of block data (sample count + 2)
001E byte Frequency divisor
001F byte Codec ID (audio/L8: 0x00)
0020 []byte Samples
NNNN byte Terminator block type: 0x00
***Version Number***
The version number is encoded as a major and minor number, in the high and low bytes respectively.
The validation value is the result of building the binary complement of the version number and adding the constant ```0x1234```.
***Frequency Divisor***
The frequency divisor is used to calculate the sample rate with the following formula:
```
sampleRate := 1000000 / (256 - frequencyDivisor)
```
+67
View File
@@ -0,0 +1,67 @@
## Texts
All texts are stored in the language specific files ```cybstrng.res```, ```frnstrng.res``` and ```gerstrng.res```.
These files contain 188 resources of type ```0x01```. They are compound with several blocks. Each block contains
one string, which is always 0x00 terminated.
While the English strings may be readable according to standard ASCII, with the French and German options, texts
need to be interpreted according to the DOS [code page 437](http://en.wikipedia.org/wiki/Code_page_437).
> Since texts are always presented through the game fonts, any one-byte codepage can be used - As long as the
> corresponding font bitmaps match for the requested characters (bytes).
### Control Characters
For texts with multiple lines, ```0x0A``` acts as a newline marker.
```0x02``` is a soft-hyphen between syllables. Not all texts contain this character.
### Resource Overview
* 0x0867 Trap messages, one per block
* 0x0868 Wall texts
* 0x0869 Switch/Panel descriptions
* 0x086A Texture descriptions (Note: From block 273 onwards of the 512 blocks, the text is always "bug")
* 0x086B "Can't use X" texts for textures (Note: bug texts are simply "can't be used")
* 0x086C HUD texts/messages
* 0x086D Object short names
* 0x086E Messages of the targeting unit
* 0x086F HUD messages (top)
* 0x0870 Log category titles ("Level 1 Logs"...)
* 0x0871 Action messages
* 0x0872 Intro cutscene subtitles
* 0x0873 Death cutscene subtitles
* 0x0874 Win cutscene subtitles
* 0x0875 System scanner label texts
* 0x0876 Sign/Logo names
* 0x0877 Monitor/Screen texts
* 0x0878 Cyberspace toggle texts
* 0x0879 Access card names
* 0x087A Datalet messages (cyberspace nodes)
* 0x087B Menu texts
* 0x087C Credits
* 0x087D Keyboard help
* 0x087E Various HUD messages
* 0x087F Tooltip texts
* 0x0880 Main menu texts
* 0x0881 Endgame statistics texts
* 0x0882 Inventory description/version texts
* 0x0883 Texts for MFD games
* 0x0024 Object names (readable)
* 0x003C - 0x0046 Paper text messages
#### [Electronic Messages](../content/ElectronicMessages.md)
* 0x0989 - 0x09B6 E-Mails
* 0x09B8 - 0x09BD Level R logs
* 0x09C8 - 0x09D7 Level 1 Logs
* 0x09D8 - 0x09E4 ...
* 0x09E8 - 0x09F0
* 0x09F8 - 0x09FE
* 0x0A08 - 0x0A13
* 0x0A18 - 0x0A21
* 0x0A28 - 0x0A30
* 0x0A38 - 0x0A3D Level 8 logs
* 0x0A98 - 0x0AA4 Data fragments
+57
View File
@@ -0,0 +1,57 @@
## Video Clips
Video clips are made out of a sequence of standard [bitmaps](Bitmaps.md). The meta-information about a video clip
is stored in resources of type ```0x04```. This information block refers to the resource that contains the frames, which
is stored in the same resource file.
Contrary to [MOVI Resources](moviResources.md), video clips contain only the frame information - nothing else.
### Format
**Video Clip Sequence Description** (16+5*N bytes)
0000 int16 Width of video.
0002 int16 Height of video.
0004 int16 Resource ID of frames, referring to a compound resource
0006 [6]byte Unknown. Always 0x00
000C int16 Unknown. TriOp intro: 0x0001, 0x0000 otherwise
000E N*[5]byte Sub-table of N sequence entries
MMMM int16 Unknown. Always 0x010C
**Video Clip Sequence Entry** (5 bytes)
0000 byte Unknown. Always 0x04.
0001 byte First frame
0002 byte Last frame
0003 int16 Frame time (in milliseconds)
### Video Mails
Video mails are small animated movies sent as mail during the game, all stored in ```vidmail.res```.
A single video mail is split up in several sequences. Most notably, the TriOp intro is a reused sequence for all mails.
The resource file contains 12 such sequences: Per sequence there is one resource of type ```0x04```
and a corresponding bitmap resource (containing the frames in blocks).
Video mails have a size of 200x100 pixel and are the same for the HD and CD releases. They share the palette of the
game (```gamepal.res```).
#### Hardcoded constants
The classic engine has the following hardcoded constants:
* There are 6 video mails, ordered as: "shield", "grove", "bridge", "laser", "explode", and "status"
* "laser" has two sequences, "explode" has five sequences. The twelth sequence is the intro, having index 10, right before the status.
* The first ID for the sequence descriptions is `0x0A4C`
* The first ID for the sequence bitmaps is `0x0A40`
> The engine always plays the intro first, then the requested video mail. The bundling of sequences (like for "explode") is hardcoded,
> so if another video mail could either split itself up, or simply have only one frame (or none at all) at the end.
### Cutscenes
The files ```start1.res```, ```death.res``` and ```win1.res``` contain low-res variants of the corresponding
cutscenes.
The palettes for these cutscenes are stored in ```cutspal.res```.
+258
View File
@@ -0,0 +1,258 @@
## MOVI Resources
Resources with content type `0x11` are a media container themselves, similar to audio/video container formats.
These resources are called "MOVI Resources" since the initial tag is the four letter code `MOVI`.
### Format
Such a resource contains the following components:
* a header, describing meta information about the media,
* a palette, with initial colours,
* an index, describing the following data entries, and
* the data entries.
**MOVI Header** (`MovieHeader` struct) (1024 bytes)
0000 uint32 magicId Container tag 0x49564F4D ("MOVI")
0004 int32 numChunks Number of chunk entries in file
0008 int32 sizeChunks Size of index table
000C int32 sizeData Size of content (all data entries)
0010 fix totalTime Length of media (in seconds)
0014 fix frameRate Frame rate (informational only)
0018 int16 frameWidht Width of video
001A int16 frameHeight Height of video
001C int16 gfxNumBits For videos may be 0x0008, 0x000F or 0x0018, 0x0000 otherwise
001E int16 isPalette Palette presence in file. For videos 0x0001, 0x0000 otherwise
0020 int16 audioNumChans Numbers of audiochannels 0x0000 - no audio, 0x0001 - mono, 0x0002 stereo
0022 int16 audioSampleSize Audio sample size, 0x0001 - 8-bit, 0x0002 - 16-bit
0026 fix audioSampleRate Sample rate of audio, in Hz (0x2B770000 - 11127 Hz, 0x56EE0000 - 22254 Hz)
0030 uint8[216] reserved Reserved
0108 uint8[768] palette Palette
If the index table size is greater than the total of index entry number times one entry size, the remaining bytes are zero.
> The index table sizes come in multiples of 0x0400. The rough rule for recreating the existing index sizes is:
> Required size less than 0x0400 -> Use 0x0400. Otherwise: Round up to next 0x0400 border and add another 0x0400.
**Palette** (256 * 3 bytes)
The palette is always present, even if the MOVI contains only audio. In such cases all the colour channels are 0x00.
**Movie chunk Entry** (`MovieChunk` struct) (8 bytes)
0000 uint32 Chunk info (bitfield)
0004 uint32 offset Offset to data (from beginning)
First 4 bytes are uint32 bit field which follow structure:
bits 00-23 24 time Fixed point timestamp from movie start (in seconds)
bits 24-24 1 played Has this chunk been clocked out? (Used by engine internally)
bits 25-28 4 flags Chunk type specific flags
bits 39-31 3 chunkType Chunk type (see below)
**Chunk type specific flags** may be:
For Video chunk type (0x01 MOVIE_CHUNK_VIDEO)
0x0F MOVIE_FVIDEO_BMTMASK 4 bits of flags is bmtype
0x0F MOVIE_FVIDEO_BMF_4X4 4x4 movie format
> Not clear, why those flags uses same code, this may be bug or leftovers from reusing library component
For Palette chunk type (0x04 MOVIE_CHUNK_PALETTE)
0x00 MOVIE_FPAL_SET Set palette from data
0x01 MOVIE_FPAL_BLACK Set palette to black
0x07 MOVIE_FPAL_EFFECTMASK 4 bits of flags is effect
0x08 MOVIE_FPAL_CLEAR If bit set, clear screen
For Table chunk type (0x05 MOVIE_CHUNK_TABLE)
0x01 MOVIE_FTABLE_COLORSET Table is color set
0x02 MOVIE_FTABLE_HUFFTAB Huffman table (compressed)
### Chunk Types
#### 0 End of chunk (MOVIE_CHUNK_END)
This type is always present as the last entry in the index. It is used to calculate the length of the previous entry as
the table does not contain a length field for entries.
The timestamp of this entry should match the media length from the header.
#### 1 Video (MOVIE_CHUNK_VIDEO)
Video frames are compressed in two variants, for low and high resolution.
Frames are either "full" frames, which set all, or nearly all, pixels in the buffer, typically at a scene change. Other frames are "delta" frames, which re-use (most of) the previous pixel in the buffer. This re-use is done by writing/using the palete index 0x00, which is meant to be transparent.
##### Low-resolution Video
Low-resolution video is using type `0x21`. Data contains a bounding box and then the compressed frame using the [compression algorithm](./Bitmaps.md#pixel-compression) for standard bitmaps.
**Low Resolution Video Data** (8+N bytes)
0000 [4]int16 Bounding Box
0008 []byte Compressed pixel data
> In the existing videos, the bounding box is always `0, 0, 320, 150`, specifying the same size as the video itself.
##### High-resolution Video
High-resolution video is using type `0x79` with a more complex compression algorithm. This algorithm works in conjunction of the `Table` types. See [below](#high-resolution-video-compression) for details on the algorithm.
**High Resolution Video Data** (2+N bytes)
0000 int16 Offset to Mask-stream in bytes, from beginning (XXXX)
0002 []byte Bitstream
XXXX []byte Mask-stream
The `Bitstream` is a stream of serialized big-endian integers. The `Mask-stream` is a stream of serialized little-endian integers of different lengths (2, 4, 6, and 8 bytes).
The order and layout of the values in the streams is determined by the algorithm. The decoder will want to read past the end of the streams in a few cases. For these cases the missing bits/bytes are to be set to zero. The encoder removed any trailing 0x00 bytes from these streams in order to save further space.
#### 2 Audio (MOVIE_CHUNK_AUDIO)
The data entry contains unsigned 8-bit PCM samples.
> Audio is chunked into entries of 8192 bytes, even if the MOVI contains only audio.
#### 3 Text (MOVIE_CHUNK_TEXT)
**Subtitle Entry** (`MovieTextItem` struct) (16+N bytes)
0000 uint32 tag Subtitle Control
0004 uint32 offset Offset to text string, always 0x10
0008 [8]byte Unused -- always 0x00, as they are skipped
0010 []byte Null terminated string
Subtitle text will be shown with the color from palette index 255 (last entry).
##### Subtitle Controls
`'AREA'` (uint32 `0x41455241`) is for setting up the subtitle space. It defines the rectangle, in pixel (left, top, right, bottom), where the text is to be displayed. Example:
```
20 365 620 395 CLR
```
`'STD '` (uint32 `0x20445453`), `'FRN '` (uint32 `0x204E5246`) and `'GER '` (uint32 `0x20524547`) specify the actual subtitle text in the respective language, with `'STD '` being the default language (English).
#### 4 Palette (MOVIE_CHUNK_PALETTE)
There are two palette entry types: `0x4C` and `0x04`. For any change in the palette, two entries are in the container. Both entries always come in sequence (first `0x4C`, then `0x04`) and both have identical timestamp values.
The entry with type `0x4C` will have a length of 0 and the same data offset as the next entry, `0x04`.
The entry with type `0x04` points to data of 0x300 bytes (= 256*3) which describes the new palette for upcoming frames.
> At least for high-resolution videos, the next frame after a palette change does not always entirely set the complete pixel buffer for the next frame.
> In the intro video for instance, SHODAN's gaining of conscience scene has a few pixel left over from the previous scene.
>
> As a recommendation, a decoder should wipe the current frame buffer with 0x00 before applying a new palette/drawing further frames.
> An alternative could be to remap all pixel values with palette indices as close as possible to the new palette, though this might alter the resulting frame too much.
#### 5 Table (MOVIE_CHUNK_TABLE)
Table entries are used in combination with video type `0x79` entries for high-resolution videos. There are two table types:
* `0x05`: Palette lookup list
* `0x0D`: Control table
See [below](#high-resolution-video-compression) for details on the algorithm.
Such entries are common to several frames. For any change, two entries are in the container. Both entries always come in sequence (first `0x05`, then `0x0D`).
> In the existing files, a change of tables is always followed by a change of palette. The exception is the first scene, which uses the initial palette from the MOVI header.
##### Palette lookup list
This entry, with type `0x05` is a simple lookup list. Every entry of type byte is a palette index.
This list is truncated as well. The decoder will want to read past the end of this stream, in which case zero bytes must be returned.
##### Packed Control Words
Table type `0x0D` contains a runlength encoded list of control words.
**Packed Control Words** (4+(N*4) bytes)
0000 int32 Unpacked size
0004 []Packed Packed entries
The `Unpacked size` field equals the number of control words, times three (specifying the size in bytes).
The remainder of the entry is then the list of packed control words, from which the actual list of control words is unpacked.
**Packed Entry** (4 bytes)
0000 byte Word count
0001 uint24 Control Word
The field `Word count` specifies how often the `Control Word` shall be extracted. A value of 0 is never encountered.
### High-resolution Video Compression
High-resolution video compression uses scene-specific tables shared amongst several frames and frame-specific bit- and mask-streams. Frames in this compression are made up of tiles with a size of 4x4 pixel each. For the high-resolution videos of 600x300 pixel this means 150x75 tiles. Tiles are coloured according to different types of control words, from left to right, top to bottom.
**Control Word** (3 bytes, masked)
0xF00000 Count value
0x0E0000 Type
0x01FFFF Parameter
As a rough guideline, the frame-specific bit-stream provides index values into the control word table. According to the type of the control word, the current tile is coloured either directly, or with the additional help of the mask-stream and palette lookup list.
A note on the bit-stream: Reading from this stream does not automatically advance the current position. Advancing the bit-stream is a separate operation, which may advance with a different number of bits than were previously read. This may cause re-use of some bits to increase compression.
As noted above, the bit- and mask-stream, as well as the palette lookup list, have possible trailing zero values removed in the files. When the decoder wants to read beyond their end, they must provide values set to 0.
#### Decoder sequence
A possible decoder is implemented using the following sequence:
* Iterate over the tiles, outer loop is the rows, inner loop the columns. Finish the loops if either all tiles are covered, or the bit-stream has been exhausted. The stream is exhausted if the current position is at the end of or beyond the available range.
* Read a 12-bit value from the bit-stream. Use this value as an index into the control word table.
* If the `Count value` of the control word is zero
* A `Count value` of zero indicates a `Long Offset`. In this case, the `Type` and `Parameter` fields are taken together to form a 20-bit `Long Offset` value.
* Advance the bit-stream by 8 bits
* Enter a loop as long as the current control word indicates a `Long Offset`.
* Advance the bit-stream by 4 bits
* Read a 4-bit value from the bit-stream. Add this value to the `Long Offset` of the current control word and use the result as another index into the control word table. The retrieved entry becomes the new current control word.
* Advance the bit-stream by the `Count` value of the current control word. This may indicate a different amount of bits than previously read, even less.
* If the type of the current control word is `6`, use the previously processed control word as the current one for further processing.
* If the type of the current control word is `5`, tiles shall be skipped
* Read a 5-bit value from the bit-stream and advance it by 5 bits. This is the `Skip Count` value.
* A `Skip Count` of `0x1F` skips the remainder of the current tile row
* Any other value skips the given amount of columns, additionally to the current one. A value of 0 skips only the current tile.
* Any type in the range of `0` to `4` colours the current tile. See below.
* Next loop iteration
> Type `7` is never encountered. According to the documentation of TSSHP, this has the same meaning as `6`: use previous control word.
##### Encoder hints
* Using the previous control word (type `6`) is not allowed for the first tile of a row. It can't repeat the last operation from the previous row.
* Usage of "transparency" byte `0x00` is only processed if at the first index of a palette lookup. See below.
##### Colouring a tile
To colour a tile, the `Parameter` value of the current control word is used, as well as the `mask-stream` and the `palette lookup list`. The 16 pixel of a tile are coloured from left to right, top to bottom.
The palette value for a pixel is determined by an index into a small `lookup array` of palette indices. Depending on the type of the control word, this lookup array is either a made up one, or a segment of the `palette lookup list`. The length of a `lookup array` is always a power of 2 and has possible lengths of 2, 4, 8 or 16.
The corresponding index values into such lookup arrays have bit sizes of 1, 2, 3 or 4 bits. The 16 index values for a tile are packed into a `mask integer` of 2, 4, 6 or 8 bytes. Depending on the type of the control word, this `mask integer` is made up or read from the `mask-stream`.
> For example, a lookup array of 4 palette values requires an index size of 2 bits, resulting in a mask integer of 4 byte.
>
> For pixel coordinates given in x,y: Mask integer bits 0-1 colour pixel 0,0; mask integer bits 2-3 colour pixel 1,0; ...
> mask integer bits 28-29 colour pixel 2,3 and mask integer bits 30-31 colour pixel 3,3.
| Type | Lookup Array | Mask Integer |
|:----:|--------------------------------------------------------|-------------------------|
| 0 | `[ Parameter >> 0 & 0xFF, Parameter >> 8 & 0xFF ]` | Static `0xAAAA` |
| 1 | `[ Parameter >> 0 & 0xFF, Parameter >> 8 & 0xFF ]` | 2 byte from mask-stream |
| 2 | Parameter is start index into list, 4 entries | 4 byte from mask-stream |
| 3 | Parameter is start index into list, 8 entries | 6 byte from mask-stream |
| 4 | Parameter is start index into list, 16 entries | 8 byte from mask-stream |
If the lookup array index is `0`, and the entry ix `0x00`, use the color from the previous frame.
+7
View File
@@ -0,0 +1,7 @@
## Music Files
> For the time being, music files are not covered. For further information, follow these resources:
>
> SSPLAYER: The random song generator. Available from these links (including source):
> * http://www.vgmpf.com/Wiki/index.php?title=System_Shock_Random_Song_Generator
> * http://wenchy.net/old/ssplayer.html