Introduction
Scx format was formed by Esemble Studios, Microsoft for their games, first time for Age of Empires (1997) and second for Age of Empires 2 (1999), and for their last game on genie engine Star Wars: Galactic Battlegrounds. Format is used to store maps (scenarios). It's uses C++98 standart datatypes. Also scenario data are compressed with DEFLAT method. This document primary describes scx format used for Age of Empires 2, version 1.21.
Most of this document originaly writen by DiGit's. specification with knowledge by AOHH and Iberico format and finally extend with me.
List of Structures
About Scenario
Here you will find interesting info about scenario files
Coords
Coordinations [0, 0] starts from left point of map, continue to top of map [0, mapWidth]. Tiles are one dimension array, starting from position [0, 0].This image shows how are Tile[x, y] calculated.

Data Types
A note about data lengths: they specify both the length of the data and
how to interpret it. For example:
- s32 is a signed 32-bit integer
- u16 is an unsigned 16-bit integer
- f32 is a 32-bit (single-precision) floating point number
- c4 is a 4-byte character string
- str16 is a variable-length character string (see below)
- 40 means 40 bytes of data
- 16*u16 means 16 unsigned 16-bit integers
Variable-length character strings are stored in the scenario as length/data
pairs. For example "str16" means there is a 16-bit length field with value n
followed by n bytes (characters). str32 is also used, but more rarely.
| Length |
Requires |
Description |
| c4 |
|
Version (ASCII): 1.10, 1.18, 1.21 |
| u32 |
|
Length of header (excluding version and self) |
| s32 |
|
Savable |
| u32 |
Savable >= 2 |
Timestamp of last save |
| str32 |
|
Scenario Instructions |
| u32 |
|
Individual Victories Used |
| u32 |
|
Player count (thanks iberico) |
Compressed DataIt uses Deflate method with (-MAX_WBITS, for python)
| Length |
MinVer |
Description |
| u32 |
|
Next unit ID to place |
| f32 |
|
Version 2 |
| 16*256 |
|
ASCII player names |
| 16*u32 |
1.18 |
string table player names |
| 16*16 |
|
Player Data#1 see sub-struct below |
| u8 |
|
Conquest Mode |
| u16 |
|
Mission Items Counter = n |
| u16 |
|
Mission Available |
| f32 |
|
Mission Timeline |
| n*30 |
|
Mision Item (each 30 bytes) |
| str16 |
|
Original filename, created while first scenario save |
Player Data#1
| Length |
MinVer |
Description |
| u32 |
|
Boolean: Active |
| u32 |
|
Boolean: Human |
| u32 |
|
Civilization, see IDs at this document |
| u32 |
|
CTY Mode |
Messages
| Length |
MinVer |
Description |
| u32 |
1.18 |
String table, Instructions |
| u32 |
1.18 |
String table, Hints |
| u32 |
1.18 |
String table, Victory |
| u32 |
1.18 |
String table, Loss |
| u32 |
1.18 |
String table, History |
| u32 |
1.22 |
String table, Scouts |
| str16 |
|
ASCII, Instructions |
| str16 |
|
ASCII, Hints |
| str16 |
|
ASCII, Victory |
| str16 |
|
ASCII, Loss |
| str16 |
|
ASCII, History |
| str16 |
1.22 |
ASCII, Scouts |
Cinematics
| Length |
MinVer |
Description |
| str16 |
|
ASCII, Pregame cinematic filename |
| str16 |
|
ASCII, Victory cinematic filename |
| str16 |
|
ASCII, Loss cinematic filename |
| 1 |
|
Separator (! in some version) |
Background Image
| Length |
MinVer |
Description |
| str16 |
|
ASCII, Background filename |
| u32 |
|
Picture Version |
| u32 |
|
Bitmap width |
| s32 |
|
Bitmap height |
| s16 |
|
Picture Orientation |
| 40+SIZE |
|
BITMAPINFOHEADER (if width >= 0 && height >= 0) |
WARNING: This section is readed only if above Include value is -1 or 2 !
Look at this btmp info header article about format spec for more information
| Length |
MinVer |
Description |
| s32 |
|
Size |
| u32 |
|
Width |
| s32 |
|
Height |
| s16 |
|
Planes |
| s16 |
|
BitCount |
| u32 |
|
Compression |
| u32 |
|
SizeImage SIZ |
| u32 |
|
XPels |
| u32 |
|
YPels |
| U32 |
|
Colors Used CLR |
| u32 |
|
Important Colors |
| u32*CLR |
|
Calculated: u32*ColorsUsed |
| u32*SIZ |
|
Calculated: u32*SizeImage |
Player 2 Data
| Length |
MinVer |
Description |
| 32str16 |
|
Unknown strings (2 per player?) |
| 16str16 |
|
AI names, one per player |
| 16*var |
|
AI files, see sub-struct below |
| u8 |
|
AI type, 0 = custom, 1 = standard, 2 = none. Thanks iberico. |
| u32 |
|
Separator, 0xFFFFFF9D |
| 16*24 |
|
Resources, see sub-struct below |
Struct: AI Files
| Length |
MinVer |
Description |
| u32 |
|
unknown, always 0 |
| u32 |
|
unknown, always 0 |
| str32 |
|
AI .per file text |
Struct: Resources
| Length |
MinVer |
Description |
| u32 |
|
Gold |
| u32 |
|
Wood |
| u32 |
|
Food |
| u32 |
|
Stone |
| u32 |
|
"Ore X", not used in AOK |
| u32 |
|
Trade Goods |
Global Victory
| Length |
MinVer |
Description |
| u32 |
|
Separator, 0xFFFFFF9D |
| u32 |
|
Boolean: conquest required? (for custom vict) |
| u32 |
|
Ruins |
| u32 |
|
Artifacts |
| u32 |
|
Discovery |
| u32 |
|
Explored % of map required |
| u32 |
|
Gold |
| u32 |
|
Boolean: all custom conditions required? |
| u32 |
|
Mode, see below |
| u32 |
|
Required score for score victory |
| u32 |
|
Time for timed game, in 10ths of a year (eg, 100 = 10yr) |
Diplomacy
| Length |
MinVer |
Description |
| 16*64 |
|
Per-player diplomacy, see sub-struct below |
| 161260 |
|
Individual Victories (12 Conditions per 16 Players) |
| u32 |
|
Separator, 0xFFFFFF9D |
| 16*u32 |
|
Boolean: Allied vict, per-player. Ignored (see PData3). Thanks iberico. |
Struct: Diplomacy Per-player
| Length |
MinVer |
Description |
| 16*u32 |
|
Stance with each player, 0 = allied, 1 = neutral, 3 = enemy |
Disables
| Length |
MinVer |
Description |
| 16*u32 |
|
Per-player, number of disabled techs |
| 16*120 |
|
Per-player, Disabled technology IDs (30*u32) |
| 16*120 |
1.30 |
Per-player, Extra disabled technologies (30*u32) |
| 16*u32 |
|
Per-player, number of disabled units |
| 16*120 |
|
Per-player, Disabled unit IDs (30*u32) |
| 16*120 |
1.30 |
Per-player, Extra disabled units (30*u32) |
| 16*u32 |
|
Per-player, number of disabled buildings |
| 16*80 |
|
Per-player, Disabled building IDs (20*u32) |
| 16*160 |
1.30 |
Per-player, Extra disabled buildings (40*u32) |
| u32 |
|
Combat Mode |
| u32 |
|
Naval Mode |
| u32 |
|
Boolean: all techs |
| 16*u32 |
|
Per-player, starting age. See below. 0-8 Players, 9 - Gaia, 10-16 Unused players |
Map
| Length |
MinVer |
Description |
| u32 |
|
Separator, 0xFFFFFF9D |
| s32 |
|
Player 1 camera, Y |
| s32 |
|
Player 1 camera, X |
| s32 |
1.21 |
AI Type (see list at bottom) |
| u32 |
|
Map Width (AOK caps at 256), W |
| u32 |
|
Map Height (AOK caps at 256), H |
| W*H*3 |
|
Terrain data, see sub-struct below |
Struct: Terrain
Tiles are one dimensional array. There's not any X or Y. It's calculated internal. First tile[X:0, Y:0] starts at leftmost corner and continues at uppest corner of minimap. See example image how tiles are readed.
| length |
minver |
description |
| u8 |
|
terrain id, see list at bottom |
| u8 |
|
elevation |
| u8 |
|
unused = 0 |
Units Section
| length |
minver |
description |
| u32 |
|
number of unit sections. I've always seen = 9. P |
| 8*28 |
|
Player Data 4, see sub-struct below |
| P*var |
|
Player Units, see sub-struct below. GAIA units come first. |
Struct: Player Data 4
| length |
minver |
description |
| f32 |
|
Food, duplicate |
| f32 |
|
Wood, duplicate |
| f32 |
|
Gold, duplicate |
| f32 |
|
Stone, duplicate |
| f32 |
|
"Ore X", duplicate |
| f32 |
|
Trade Goods Not in SW:GB. |
| f32 |
1.21 |
Population limit |
Struct: Player Units
| length |
minver |
description |
| u32 |
|
Unit count, N |
| N*29 |
|
Units, see sub-struct below |
Struct: Unit
| length |
minver |
description |
| f32 |
|
X position |
| f32 |
|
Y position |
| f32 |
|
Z position |
| u32 |
|
ID (for triggers, etc.) |
| u16 |
|
Unit "constant", e.g. Archer, Man-at-arms |
| u8 |
|
Status = 2 |
| f32 |
|
Rotation, in radians |
| u16 |
|
Initial animation frame |
| u32 |
|
Garrisonned in ID |
Player Data 3
| length |
minver |
description |
| u32 |
|
Number of players? Always = 9 |
| 8*64 |
|
Player Data 3, per player. See sub-struct below. |
| f64 |
|
Trigger Version = 1.6 |
Struct: Player Data 3
| length |
minver |
description |
| str16 |
|
Constant name, like "Player 1" |
| f32 |
|
Initial Camera, X (for Player 1 = editor camera) |
| f32 |
|
Initial Camera, Y |
| s16 |
|
Unknown, similar to camera X |
| s16 |
|
Unknown, similar to camera Y |
| u8 |
|
Boolean: Allied Victory (AOK reads this one) |
| u16 |
|
Player count for diplomacy, P |
| P*u8 |
|
Diplomacy for interaction: 0 = allied, 1 = neutral, 2 = ?, 3 = enemy |
| 9*u32 |
|
Diplomacy for AI system: 0=GAIA, 1=self, 2=allied, 3=neutral, 4=enemy |
| u32 |
|
Color, see values below |
| f32 |
|
Victory Version, TODO: below are Victory conditions and effects (need to clarify these structures): |
| u16 |
|
Unknown, DAT |
| 8*u8 |
|
Only included if above f32 value == 2.0 |
| DAT*44 |
|
Unknown structure, found in Grand Theft Empires |
| 7*u8 |
|
Usually 0 |
| s32 |
|
Seems to be 0 if Unknown == 1.0, -1 if Unknown == 2.0 |
Triggers Section
| length |
minver |
description |
| s8 |
|
Trigger instructions start |
| s32 |
|
Number of triggers = 0, T |
| T*var |
|
Trigger data, see sub-struct below |
| T*u32 |
|
Trigger display order array |
Structure: Trigger
| length |
minver |
description |
| u32 |
|
Boolean: enabled? |
| s8 |
|
Boolean: looping? |
| s32 |
|
String Table ID = -1 |
| u8 |
|
Boolean: objective? |
| u32 |
|
Description order (in objectives) |
| u32 |
|
Trigger starting time |
| str32 |
|
Trigger description |
| str32 |
|
Trigger name (max 44 characters in UI) |
| s32 |
|
Number of effects = E |
| E*var |
|
Effect data, see sub-struct below |
| E*s32 |
|
Effect display order array |
| s32 |
|
Number of conditions = C |
| C*var |
|
Condition data, see sub-struct below |
| C*s32 |
|
Condition display order array |
Structure: Effect
| length |
minver |
description |
| s32 |
|
Effect type |
| s32 |
|
Check, always = 0x17 |
| s32 |
|
AI script goal |
| s32 |
|
Amount, used for hp and attack |
| s32 |
|
Resource type |
| s32 |
|
Diplomacy, state for change |
| s32 |
|
N.o. units selected = N |
| s32 |
|
Unit ID |
| s32 |
|
Unit Name |
| s32 |
|
Player Source |
| s32 |
|
Player Target |
| s32 |
|
Technology |
| s32 |
|
String ID |
| s32 |
|
Sound resource ID |
| s32 |
|
Display Time (display instructions) |
| s32 |
|
Trigger ID (activate/deactivate) |
| s32 |
|
Location X |
| s32 |
|
Location Y |
| s32 |
|
Area 1 X |
| s32 |
|
Area 1 Y |
| s32 |
|
Area 2 X |
| s32 |
|
Area 2 Y |
| s32 |
|
Unit Group |
| s32 |
|
Unit Type (Civilian, Military, Building, Other) |
| s32 |
|
Instruction Panel |
| str32 |
|
Instruction Text |
| str32 |
|
Sound filename |
| s32*N |
|
Selected units IDs |
Structure: Condition
| length |
minver |
description |
| s32 |
|
Condition Type |
| s32 |
|
Check, always = 0x10 |
| s32 |
|
Amount (of Object, Diffculty) |
| s32 |
|
Resource Type |
| s32 |
|
Unit object |
| s32 |
|
Unit ID |
| s32 |
|
Unit Name |
| s32 |
|
Player |
| s32 |
|
Technology |
| s32 |
|
Timer |
| s32 |
|
Unknown |
| s32 |
|
Area 1 X |
| s32 |
|
Area 1 Y |
| s32 |
|
Area 2 X |
| s32 |
|
Area 2 Y |
| s32 |
|
AI Signal |
Included Files
| length |
minver |
description |
| u32 |
|
Boolean: files included? |
| u32 |
|
Boolean: PER File Error, data included? |
| 396 |
|
PER File Error, if PER File Error included |
| var |
|
Included files |
PER File Error
This data contains details about the last error occurrence in the PER files. This data is ignored during reading, but is written when saving a scenario with a PER file error.
The engine outputs the details on debug mode. The developers must have needed this feature to debug their AI scripting in scenarios. Normally, a dialog box contains a message about the details of the error. I don't see why they needed this; maybe they wanted to see the data in raw binary form.
-by AOHH
| length |
minver |
description |
| 256 |
|
Name of corrupted PER File |
| u32 |
|
Unknown ID (either PER file ID or Player ID) |
| u32 |
|
Line of error in PER File |
| u32 |
|
Interruption line in PER File |
| 124 |
|
Extracted code from PER File |
| u32 |
|
Error Code |