Crimson config blob (crimson.cfg)¶
crimson.cfg is the fixed-size configuration blob used by the classic
Crimsonland executable. It is not the save/status file (that is game.cfg).
Location and size¶
- Path:
game_base_path\\crimson.cfg(built viagame_build_path). - Size: 0x480 bytes (1152 bytes).
- Endianness: little-endian for integer/float fields.
Struct view (crimson_cfg_t)¶
config_blob (DAT_00480348) is typed as crimson_cfg_t:
typedef struct crimson_cfg_t {
unsigned char sound_disabled;
unsigned char music_disabled;
unsigned char highscore_date_mode;
unsigned char highscore_duplicate_mode;
unsigned char hud_indicator_toggle[2];
unsigned char reserved0_06[0x08];
unsigned char fx_detail_flag0;
unsigned char reserved0_0f;
unsigned char fx_detail_flag1;
unsigned char fx_detail_flag2;
unsigned char reserved0_12[2];
int player_count;
game_mode_id_t game_mode;
int player_mode_flags;
unsigned char reserved0_20[0x24];
int aim_scheme;
unsigned char reserved0_48[0x28];
float texture_scale;
char player_name_buf[12];
int name_slot_selected;
int name_slot_count;
int name_slot_order[8];
char saved_names[8][27];
char player_name[32];
int player_name_length;
unsigned char reserved1[0x14];
int display_bpp;
int screen_width;
int screen_height;
int windowed;
int keybinds_p1[13];
unsigned char reserved2[0x0c];
int keybinds_p2[13];
unsigned char reserved3[0x0c];
unsigned char reserved4[0x200];
unsigned char hardcore;
unsigned char full_version;
unsigned char reserved5[2];
int perk_prompt_counter;
unsigned char reserved6[0x14];
float sfx_volume;
float music_volume;
unsigned char fx_toggle;
unsigned char score_load_gate;
unsigned char reserved7[2];
int detail_preset;
float mouse_sensitivity;
int key_pick_perk;
int key_reload;
} crimson_cfg_t;
Control scheme notes¶
For player controls, movement and aiming are stored separately in the blob:
config_player_mode_flags[player]controls movement mode.config_aim_scheme[player]controls aiming mode.
On the original EXE, these can be mixed in practice (for example
move_mode = 2 static movement with aim_scheme = 5 computer aiming).
Observed file:
game_bins/crimsonland/1.9.93-gog/crimson.cfg- size 0x480
- width 1024, height 768
- windowed flag = 1
- texture scale = 1.0
Hardcoded defaults (from config_sync_from_grim when grim_config_invoked is set):
- width 800, height 600
- windowed flag = 0 (fullscreen)
- bpp = 32
- sfx/music volume = 1.0
- player name defaults to
10tons - saved names default to
"default"x8
Load / write behavior¶
From the decompile (see docs/re/static/detangling.md):
config_load_presetsreads the 0x480-byte blob intoconfig_blob.config_sync_from_grim:- seeds a default blob (in memory)
- reads Grim config values (vtable +0x24)
- loads
crimson.cfgoverrides when present - writes the 0x480-byte blob back out
config_ensure_filewritescrimson.cfgwhen missing.
This means the file is treated as a fixed struct and rewritten wholesale.
Field layout¶
Base address in the decompile is DAT_00480348. Offsets below are relative to
the blob start.
| Offset | Size | Default | Name | Description |
|---|---|---|---|---|
0x000 |
0xA8 | - | Header / mixed fields | Not all unused; contains multiple small config fields (see below for known offsets). |
0x0A8 |
216 | "default" x8 |
config_saved_names |
8 saved names (player name cache), 27 bytes each. |
0x180 |
32 | 10tons |
config_player_name |
Current player name (char[32] or similar?). |
0x1A0 |
4 | 0 |
config_player_name_length |
Length of player name. |
0x1A4 |
28 | - | Unused/Padding? | Gap. |
0x1B8 |
4 | 32 |
config_display_bpp |
Bits per pixel (16/32). |
0x1BC |
4 | 800 |
config_screen_width |
Screen width. |
0x1C0 |
4 | 600 |
config_screen_height |
Screen height. |
0x1C4 |
4 | 0 |
config_windowed |
Windowed mode flag (0=fullscreen). |
0x1C8 |
52 | - | P1 Keybinds | Player 1 control bindings (13 dwords). |
0x1FC |
12 | - | P1 Padding | Padding (3 dwords). |
0x208 |
52 | - | P2 Keybinds | Player 2 control bindings (13 dwords). |
0x23C |
12 | - | P2 Padding | Padding (3 dwords). |
0x248 |
512 | - | Unused/Reserved | Large gap (possibly for P3/P4 + padding). |
0x448 |
1 | 0 |
config_hardcore |
Hardcore mode flag. |
0x449 |
1 | 1 |
config_full_version |
Full version flag. |
0x44A |
2 | - | Unused? | Alignment. |
0x44C |
4 | 0 |
config_perk_prompt_counter |
Counter for perk prompts. |
0x450 |
28 | - | Unused? | Gap. |
0x464 |
4 | 1.0 |
config_sfx_volume |
SFX Volume (float). |
0x468 |
4 | 1.0 |
config_music_volume |
Music Volume (float). |
0x46C |
1 | 0 |
config_fx_toggle |
FX toggle (gore/particle/effects paths). Separate from the config_fx_detail_flag* trio. |
0x46D |
1 | 0 |
config_score_load_gate |
Score loading flag. |
0x46E |
2 | - | Unused? | Alignment. |
0x470 |
4 | 5 |
config_detail_preset |
Graphics detail preset (1..5). Drives config_fx_detail_flag* via config_apply_detail_preset (0x00447580). |
0x474 |
4 | - | config_mouse_sensitivity |
Mouse sensitivity (float; options slider clamps 0.1..1.0). |
0x478 |
4 | - | config_key_pick_perk |
Keybind: Pick Perk. |
0x47C |
4 | - | config_key_reload |
Keybind: Reload. |
0x480 |
- | - | End | End of file. |
FX detail flags (0x0E / 0x10 / 0x11)¶
These three bytes live inside the 0x000..0x0A7 “header/mixed” region of the blob:
| Offset | Size | Name | Meaning (observed in decompile) |
|---|---|---|---|
0x0E |
1 | config_fx_detail_flag0 |
Enables extra shadow-ish passes (e.g. ui_element_render draws the +7,+7 shadow when nonzero; also used in creature rendering). |
0x10 |
1 | config_fx_detail_flag1 |
Enables heavier FX in some render paths (notably extra large quads in projectile_render; also used in bonus_render). |
0x11 |
1 | config_fx_detail_flag2 |
Additional FX tier toggle (currently only observed in bonus_render, gating an extra sprite_effect_pool render pass). |
How config_detail_preset maps to the flags¶
config_apply_detail_preset (0x00447580) applies the “Graphics detail” preset to these flags:
config_detail_preset |
flag0 (0x0E) |
flag1 (0x10) |
flag2 (0x11) |
|---|---|---|---|
| 1 | 0 | 0 | 0 |
| 2 | 0 | 0 | unchanged |
| 3 | 1 | 1 | 1 |
| 4 | 1 | 1 | 1 |
| 5 | 1 | 1 | 1 |
Notes:
- The options UI clamps
config_detail_presetto 1..5, then callsconfig_apply_detail_preset(options_menu_updateat0x004475d0). - Default init (
config_init_defaultsat0x004028f0) setsflag0/1/2to 1 andconfig_detail_presetto 5.
Keybind Block Structure¶
Each player block is 52 bytes (13 dwords), followed by 12 bytes padding. The values map to DirectInput key codes (scancodes).
Offset refers to index within the block (0-12).
| Index | Name | P1 Default | P2 Default | Notes |
|---|---|---|---|---|
0 |
Move Forward | 0x11 (W) |
0xc8 (Up) |
|
1 |
Move Backward | 0x1f (S) |
0xd0 (Down) |
|
2 |
Turn Left | 0x1e (A) |
0xcb (Left) |
|
3 |
Turn Right | 0x20 (D) |
0xcd (Right) |
|
4 |
Fire | 0x100 (LMouse) |
0x9d (RControl) |
P1 uses mouse button 0 by default. |
5 |
Reserved 0 | 0x17e |
0x17e |
|
6 |
Reserved 1 | 0x17e |
0x17e |
|
7 |
Aim Left | 0x10 (Q) |
0xd3 (Delete) |
|
8 |
Aim Right | 0x12 (E) |
0xd1 (PageDown) |
|
9 |
Axis Aim Y | 0x140 |
0x17e |
Analog axis (P1 Mouse Y). |
10 |
Axis Aim X | 0x13f |
0x17e |
Analog axis (P1 Mouse X). |
11 |
Axis Move Y | 0x153 |
0x17e |
Analog axis. |
12 |
Axis Move X | 0x17e |
0x17e |
Analog axis. |
P3/P4 Note: The game loop in config_load_presets iterates only twice (for P1 and P2). The large gap after P2 suggests P3/P4 slots might have been planned but are not loaded by this version of the executable.
Notes¶
-
The blob is always written at full size; unknown fields should be preserved when round-tripping.
-
game.cfgis a different file (save/status) and does not share this layout.