Dungeon Master 1 (DM1) — Reverse Engineering Wiki
This page documents the reverse-engineering knowledge base backing Firestaff’s
Dungeon Master 1 (DOS PC 3.4, “I34E”) implementation. The canonical reference
source is ReDMCSB, a reconstructed C decompilation of the original
Dungeon Master / Chaos Strikes Back engine. Firestaff comments cite ReDMCSB
files and F-numbered functions directly (ReDMCSB FILENAME.C:LINE,
F0NNN_FUNCTION_NAME), and DM1-specific compatibility modules live under
src/dm1/ and include/dm1_v1_*_pc34_compat.h.
All data below was extracted from the current Firestaff source tree with:
grep -rohE 'ReDMCSB [A-Z0-9_]+\.[CH]' include/dm1_v1_* src/dm1/ | sort -u
grep -rohE 'F[0-9]{4}_[A-Za-z0-9_]+' include/dm1_v1_* src/dm1/ | sort -u
1. ReDMCSB Source File Map
96 distinct ReDMCSB source/header filenames are referenced across DM1
compatibility headers and sources. They are grouped below by subsystem.
Note that DM1 and CSB (Chaos Strikes Back) share the same PC 3.4 engine
codebase in ReDMCSB, so many files serve both games.
Core / Main
| File |
Purpose |
MAINLIB.C |
Core primitives: copy/clear bytes, blit-to-screen, random number generators (F0002-F0030) |
GAMELOOP.C |
Main game loop (F0002_MAIN_GameLoop), tick dispatch |
BASE.C |
Base runtime services, error display |
COMPILE.H |
Compile-time configuration switches |
DEFS.H |
Global constant/macro definitions (element types, thing types, sensor ranges) |
MEMORY.C |
Memory allocator (used-list / defrag model for GRAPHICS.DAT) |
DATA.C |
Static game data tables |
COORD.C |
Coordinate/direction math |
Startup / Entrance / Title
| File |
Purpose |
STARTUP2.C |
Startup sequence, F0800-F0910 range functions |
ENTRANCE.C |
Dungeon entrance sequence |
TITLE.C |
Title screen |
SWSH.C |
FTL logo “swoosh” animation |
SWITCH.C / SWITCHMM.C |
Startup/menu state machine switches |
ENDGAME.C |
Endgame/victory sequence |
AMIGINIT.C |
Amiga-specific init (ReDMCSB is multi-platform; PC34 reuses shared init logic) |
SWSH.C owns the FTL-logo palette sequence; the logo payload itself is the
original SWOOSH member. Firestaff’s PC 3.4 route reads that member directly
from the selected ZIP into bounded memory through asset_read_path_alloc().
It does not unpack the archive or create a loose replacement file. The
real-media probes ftl_swoosh_palette_regression and
ftl_swoosh_ascii_sanity use the same virtual path and verify the source
palette mutations plus full 320×200 packed-to-indexed expansion. If the
operator-owned archive is absent, those optional visual probes skip; the
native archive CLI/start-menu test remains the launch gate.
Dungeon (data model)
| File |
Purpose |
DUNGEON.C |
Core dungeon data access: squares, things, thing lists (F0139-F0174) |
DUNVIEW.C |
Dungeon-view-adjacent dungeon helpers |
NEWMAP.C |
New-map / map-transition handling |
MOVESENS.C |
Movement + sensor interaction |
GROUP.C |
Monster group data and AI (F0175-F0212) |
Viewport / Rendering
| File |
Purpose |
VIEWPORT.C |
Viewport frame orchestration |
DRAWVIEW.C |
Dungeon-view drawing (F0093-F0135: floor/wall/door/creature blit chain) |
DRAW.C |
General drawing primitives |
BLIT.C / BLITMASK.C |
Raw bitmap blit + masked blit routines |
IMAGE.C / IMAGE2.C |
Bitmap/image management |
SCREEN.C |
Screen buffer management |
PALETTE.C |
Palette load/animate |
DARKCOLR.C |
Darkness/lighting color reduction |
PRIM1.C |
Low-level graphics primitives |
VIDEODRV.C |
Video driver abstraction |
MIRROR.C |
Horizontal-flip bitmap helper (used for D2R/D3R mirrored wall tiles) |
SYMBOL.C |
Symbol/glyph bitmap handling |
Objects / Items
| File |
Purpose |
OBJECT.C |
Object type/icon resolution (F0031-F0039) |
INVENTORY.C / INVNTORY.C |
Inventory panel drawing and interaction (F0332-F0349) |
CHEST.C |
Chest/container open/close |
AMMO.C |
Ammunition compatibility checks |
Champions
| File |
Purpose |
CHAMPION.C / CHAMPION.H |
Champion stats, slots, damage, skills (F0284-F0331) |
CHAMDRAW.C |
Champion portrait/state drawing |
CLIKCHAM.C |
Mouse click routing onto champion panel |
REVIVE.C |
Resurrection/reincarnation |
Combat
| File |
Purpose |
MELEE.C |
Melee combat resolution |
PROJECTILE.C |
Projectile creation/flight |
PROJEXPL.C |
Projectile explosion resolution |
ACTIDRAW.C |
Action icon drawing (attack/spell UI) |
SPELDRAW.C |
Spell casting UI drawing |
Timeline / Events
| File |
Purpose |
TIMELINE.C |
Scheduled-event queue (F0233-F0240) |
Sensors / Doors
| File |
Purpose |
SWITCHMM.C |
Wall/floor switch dispatch (shared with menu switches) |
(sensor logic is embedded in DUNGEON.C/MOVESENS.C, wrapped by Firestaff F07xx_*_Compat) |
Door toggling, sensor effect dispatch |
Audio
| File |
Purpose |
SOUND.C |
Sound effect request/playback queue (F0064-F0065) |
MUSIC.C |
Music driver: pause, play, track select, update (F0740-F0743) |
| File |
Purpose |
MOUSE.C |
Mouse pointer state, enable/disable, screen area (F0066-F0079) |
MOUSESET.C |
Mouse pointer bitmap sets |
CLIKMENU.C |
Menu click routing |
CLIKVIEW.C |
Viewport click routing |
COMMAND.C |
Command dispatch table |
Save / Load
| File |
Purpose |
LOADSAVE.C |
Save/load orchestration (F0417-F0435 checksum/obfuscation, dungeon tail) |
SAVEHEAD.C |
Save header checksum/obfuscation (F0429/F0430) |
SAVEPATH.C |
Save file path resolution |
SAVEUTIL.C |
F0417_SAVEUTIL_GetChecksumAndObfuscate and related utilities |
READWRIT.C |
Low-level read/write + obfuscation helpers |
FIO1.C / FIO1MAIN.C |
File I/O primitives |
FLOPPYAM.C / FLOPPYST.C |
Amiga/Atari ST floppy I/O (multi-platform ReDMCSB) |
Graphics data (GRAPHICS.DAT)
| File |
Purpose |
GRF1.C |
GRAPHICS.DAT access layer |
LZW.C |
LZW decompression of IMG3 graphic entries |
COPYPROE.C |
Copy-protection related graphics/data checks |
| File |
Purpose |
TEXT.C |
Message area, text printing (F0040-F0054) |
MENU.C / MENUDRAW.C |
Menu system and drawing |
DIALOG.C |
Dialog box rendering |
PANEL.C |
Side panel drawing |
| File |
Purpose |
HINT001.C, HINT004.C, HINTCASE.C, HINTGRAP.C, HINTHINT.C, HINTIORQ.C |
CSB hint-oracle subsystem (shared engine, CSB-specific content) |
CEDT004.C, CEDTINCD.C, CEDTINCI.C |
Character/portrait editor tooling |
| File |
Purpose |
ATARIST.H |
Atari ST platform header (multi-platform ReDMCSB build) |
NEC816.C / NECIO.C |
NEC/PC-98 platform I/O (Japanese release support) |
JAPANESE.C |
Japanese text/localization handling |
USIO1.C |
US/international I/O variant |
UTAMSCR.C, UTSTGRAP.C, UTSTVDI3.C |
Atari ST utility/graphics/VDI helpers |
INT1.C |
Interrupt handler shim |
APPA.C |
Appendix/miscellaneous support routines |
2. F-Number Function Registry
912 distinct F-numbered identifiers are referenced in DM1 code (functions,
constants, and struct/field markers combined). The tables below cover the
~300 function-shaped F-numbers in the ranges most central to DM1 parity work,
grouped by subsystem. Names are the canonical Firestaff spelling found in
source comments; the originating ReDMCSB file is noted per group above.
Core utilities (F0002-F0030) — MAINLIB.C
| F# |
Function |
| F0002 |
MAIN_GameLoop |
| F0007 |
MAIN_CopyBytes |
| F0008 |
MAIN_ClearBytes |
| F0010 |
MAIN_WriteSpacedWords |
| F0020 |
MAIN_BlitToViewport |
| F0021 |
MAIN_BlitToScreen |
| F0022 |
MAIN_Delay |
| F0024 |
MAIN_GetMinimumValue |
| F0025 |
MAIN_GetMaximumValue |
| F0026 |
MAIN_GetBoundedValue |
| F0027 |
MAIN_Get16bitRandomNumber |
| F0028 |
MAIN_Get1BitRandomNumber |
| F0030 |
MAIN_GetScaledProduct |
Object/icon handling (F0031-F0039) — OBJECT.C
| F# |
Function |
| F0031 |
OBJECT_LoadNames |
| F0032 |
OBJECT_GetType |
| F0033 |
OBJECT_GetIconIndex |
| F0034 |
OBJECT_DrawLeaderHandObjectName |
| F0035 |
OBJECT_ClearLeaderHandObjectName |
| F0036 |
OBJECT_ExtractIconFromBitmap |
| F0037 |
OBJECT_DrawIconToScreen |
| F0038 |
OBJECT_DrawIconInSlotBox |
| F0039 |
OBJECT_GetIconIndexInSlotBox |
Text/message area (F0040-F0054) — TEXT.C
| F# |
Function |
| F0040 |
TEXT_Print |
| F0042 |
TEXT_MESSAGEAREA_MoveCursor |
| F0043 |
TEXT_MESSAGEAREA_ClearAllRows |
| F0044 |
TEXT_MESSAGEAREA_ClearExpiredRows |
| F0045 |
TEXT_MESSAGEAREA_CreateNewRow |
| F0046 |
TEXT_MESSAGEAREA_PrintString |
| F0047 |
TEXT_MESSAGEAREA_PrintMessage |
| F0048 |
TEXT_MESSAGEAREA_PrintCharacter |
| F0051 |
TEXT_MESSAGEAREA_PrintLineFeed |
| F0052 |
TEXT_PrintToViewport |
| F0053 |
TEXT_PrintToLogicalScreen |
| F0054 |
TEXT_Initialize |
Sound (F0060-F0065) — SOUND.C
| F# |
Function |
| F0064 |
SOUND_RequestPlay |
| F0065 |
SOUND_PlayPendingSound |
Mouse/pointer (F0066-F0079) — MOUSE.C
| F# |
Function |
| F0067 |
MOUSE_SetPointerToNormal |
| F0068 |
MOUSE_SetPointerToObject |
| F0073 |
MOUSE_BuildPointerScreenArea |
| F0077 |
MOUSE_EnableScreen |
| F0078 |
MOUSE_Disable |
Dungeon viewport (F0093-F0135) — DRAWVIEW.C, VIDEODRV.C
| F# |
Function |
| F0093 |
DUNGEONVIEW_DrawDungeon |
| F0094 |
DUNGEONVIEW_LoadFloorSet |
| F0095 |
DUNGEONVIEW_LoadDoorSet |
| F0096 |
DUNGEONVIEW_LoadCurrentMapGraphics |
| F0097 |
DUNGEONVIEW_DrawViewport |
| F0098 |
DUNGEONVIEW_DrawFloorAnd (ceiling) |
| F0099 |
DUNGEONVIEW_CopyBitmapAndFlipHorizontal |
| F0100 |
DrawWallSetBitmap |
| F0101 |
DUNGEONVIEW_DrawWallSetBitmapWithoutTransparency |
| F0102 |
DUNGEONVIEW_DrawDoorBitmap |
| F0103 |
DUNGEONVIEW_DrawDoorFrameBitmapFlippedHorizontally |
| F0104 |
DrawFloorPitOrStairsBitmap |
| F0105 |
DUNGEONVIEW_DrawFloorPitOrStairsBitmapFlipped |
| F0106 |
DUNGEONVIEW_TestResetToStep1 |
| F0107 |
IsDrawnWallOrnamentAnAlcove |
| F0108 |
DrawFloorOrnament |
| F0109 |
DUNGEONVIEW_DrawDoorOrnament |
| F0110 |
DUNGEONVIEW_DrawDoorButton |
| F0111 |
DrawD2C |
| F0112 |
DUNGEONVIEW_DrawCeilingPit |
| F0113 |
DUNGEONVIEW_DrawField |
| F0114 |
GetExplosionBitmap |
| F0115 |
DUNGEONVIEW_DrawObjectsCreatures |
| F0116-F0121 |
DrawD3L/D3R/D3C/D2L/D2R/D2C (distance-3 and distance-2 wall column draws) |
| F0122-F0127 |
DrawSquareD1L/D1R/D1C/D0L/D0R/D0C (distance-1 and distance-0/foreground column draws) |
| F0128 |
G0076GetPc34Compat |
| F0129 |
VIDEO_BlitShrinkWithPaletteChanges |
| F0130 |
VIDEO_FlipHorizontal |
| F0132 |
VIDEO_Blit |
| F0133 |
VIDEO_BlitBoxFilledWithMaskedBitmap |
| F0134 |
VIDEO_FillBitmap |
| F0135 |
VIDEO_FillBox |
This F0093-F0135 range is the material matrix: the D0/D1/D2/D3 naming
encodes viewport column distance (D0 = nearest row, D3 = farthest) and
L/R/C encode left/right/center screen position — this is the routing table
behind m11_draw_viewport and the CSB viewport callback path described in
CLAUDE.md.
Dungeon data (F0139-F0174) — DUNGEON.C
| F# |
Function |
| F0139 |
MakeThingPc34 (creature-allowed-on-map gate) |
| F0140, F0141 |
ObjectWorldPc34 |
| F0142 |
DUNGEON_GetProjectileAspect |
| F0143 |
ArmourInfoPc34 / DUNGEON_GetArmourDefense |
| F0144 |
MakeThingPc34 (creature attributes) |
| F0145 |
DUNGEON_GetGroupCells |
| F0146 |
DUNGEON_SetGroupCells |
| F0147 |
DUNGEON_GetGroupDirections |
| F0148 |
DUNGEON_SetGroupDirections |
| F0149 |
DUNGEON_IsWallOrnamentAnAlcove |
| F0150 |
DUNGEON_UpdateMapCoordinatesAfterRelativeMovement |
| F0151 |
DUNGEON_GetSquare |
| F0152 |
DUNGEON_GetRelativeSquare |
| F0153 |
DUNGEON_GetRelativeSquareType |
| F0154 |
DUNGEON_GetLocationAfterLevelChange |
| F0155 |
DUNGEON_GetStairsExitDirection |
| F0156 |
DUNGEON_GetThingData |
| F0157 |
DUNGEON_GetSquareFirstThingData |
| F0158 |
DUNGEON_GetWeaponInfo |
| F0159 |
DUNGEON_GetNextThing |
| F0160 |
DUNGEON_GetSquareFirstThingIndex |
| F0161 |
DUNGEON_GetSquareFirstThing |
| F0162 |
DUNGEON_GetSquareFirstObject |
| F0163 |
DUNGEON_LinkThingToList |
| F0164 |
DUNGEON_UnlinkThingFromList |
| F0165 |
DungeonOps (discard-thing route) |
| F0166 |
DUNGEON_GetUnusedThing |
| F0167 |
DUNGEON_GetObjectForProjectileLauncherOrObjectGenerator |
| F0169 |
DUNGEON_GetRandomOrnamentIndex |
| F0170 |
DUNGEON_GetRandomOrnamentOrdinal |
| F0171 |
DUNGEON_SetSquareAspectRandomWallOrnament |
| F0172 |
SetSquareAspect |
| F0173 |
DUNGEON_SetCurrentMap |
| F0174 |
DUNGEON_SetCurrentMapAndPartyMap |
Groups/creatures (F0175-F0212) — GROUP.C
| F# |
Function |
| F0175 |
GROUP_GetThing |
| F0176 |
GROUP_GetCreatureOrdinalInCell |
| F0177 |
GROUP_GetMeleeTargetCreatureOrdinal |
| F0178 |
GROUP_GetGroupValueUpdatedWithCreatureValue |
| F0179 |
CreatureAspectUpdateReceipt |
| F0180 |
GROUP_StartWandering |
| F0181 |
GROUP_DeleteEvents |
| F0182 |
GROUP_StopAttacking |
| F0183 |
GROUP_AddActiveGroup |
| F0184 |
GROUP_RemoveActiveGroup |
| F0185 |
GROUP_GetGenerated |
| F0186 |
GROUP_DropCreatureFixedPossessions |
| F0187 |
GROUP_DropMovingCreatureFixedPossessions |
| F0188 |
GROUP_DropGroupPossessions |
| F0189 |
GROUP_Delete |
| F0190 |
GROUP_GetDamageCreatureOutcome |
| F0191 |
GROUP_GetDamageAllCreaturesOutcome |
| F0192 |
GROUP_GetResistanceAdjustedPoisonAttack |
| F0193 |
GROUP_StealFromChampion |
| F0194 |
GROUP_RemoveAllActiveGroups |
| F0195 |
GroupEventPc34Compat |
| F0196 |
ActiveGroupSlotPc34Compat |
| F0197 |
GROUP_IsViewPartyBlocked |
| F0198 |
GROUP_IsSmellPartyBlocked |
| F0199 |
GROUP_GetDistanceBetweenUnblockedSquares |
| F0200 |
GROUP_GetDistanceToVisibleParty |
| F0201 |
GROUP_GetSmelledPartyPrimaryDirectionOrdinal |
| F0202 |
GROUP_IsMovementPossible |
| F0203 |
GROUP_GetFirstPossibleMovementDirectionOrdinal |
| F0205 |
GROUP_SetDirection |
| F0206 |
GROUP_SetDirectionGroup |
| F0207 |
GROUP_IsCreatureAttacking |
| F0208 |
GROUP_AddEvent |
| F0209 |
GROUP_ProcessEvents29to41 |
| F0212 |
PROJECTILE_Create |
Explosions/projectiles (F0213-F0218) — PROJECTILE.C, PROJEXPL.C
| F# |
Function |
| F0213 |
EXPLOSION_Create |
| F0216 |
PROJECTILE_GetImpactAttack |
| F0218 |
PROJECTILE_GetImpactCount |
Timeline (F0233-F0240) — TIMELINE.C
| F# |
Function |
| F0233 |
TIMELINE_Initialize |
| F0234 |
TIMELINE_IsEventABeforeEventB |
| F0235 |
TIMELINE_GetIndex |
| F0236 |
TIMELINE_FixPlacement |
| F0237 |
TIMELINE_DeleteEvent |
| F0238 |
TIMELINE_AddEvent |
| F0239 |
TIMELINE_ExtractFirstEvent |
| F0240 |
TIMELINE_IsFirstEventExpired |
Sensors (F0267-F0276) — MOVESENS.C
| F# |
Function |
| F0267 |
MOVE_GetMoveResult |
| F0268 |
SENSOR_AddEvent |
| F0269 |
SENSOR_AddSkillExperience |
| F0270 |
SENSOR_TriggerLocalEffect |
| F0271 |
SENSOR_ProcessRotationEffect |
| F0272 |
SENSOR_TriggerEffect |
| F0273 |
SENSOR_GetObjectOfTypeInCell |
| F0274 |
SENSOR_IsObjectInPartyPossession |
| F0275 |
SENSOR_IsTriggeredByClickOnWall |
| F0276 |
SENSOR_ProcessThingAdditionOrRemoval |
Champions (F0284-F0349) — CHAMPION.C, INVENTORY.C
| F# |
Function |
| F0284 |
CHAMPION_SetPartyDirection |
| F0285 |
CHAMPION_GetIndexInCell |
| F0286 |
CHAMPION_GetTargetChampionIndex |
| F0287 |
CHAMPION_DrawBarGraphs |
| F0289 |
CHAMPION_DrawHealthOrStaminaOrManaValue |
| F0290 |
CHAMPION_DrawHealthStaminaManaValues |
| F0291 |
CHAMPION_DrawSlot |
| F0292 |
CHAMPION_DrawState |
| F0293 |
CHAMPION_DrawAllChampionStates |
| F0294 |
CHAMPION_IsAmmunitionCompatibleWithWeapon |
| F0295 |
CHAMPION_HasObjectIconInSlotBoxChanged |
| F0296 |
CHAMPION_DrawChangedObjectIcons |
| F0297 |
CHAMPION_PutObjectInLeaderHand |
| F0298 |
CHAMPION_GetObjectRemovedFromLeaderHand |
| F0299 |
ApplyObjectModifiersToStatistics |
| F0300 |
CHAMPION_GetObjectRemovedFromSlot |
| F0301 |
CHAMPION_AddObjectInSlot |
| F0302 |
INVENTORY_ProcessCommands28To65_ClickOnSlotBox |
| F0303 |
CHAMPION_GetSkillLevel |
| F0304 |
CHAMPION_AddSkillExperience |
| F0305 |
CHAMPION_GetThrowingStaminaCost |
| F0306 |
CHAMPION_GetStaminaAdjustedValue |
| F0307 |
CHAMPION_GetStatisticAdjustedAttack |
| F0308 |
CHAMPION_IsLucky |
| F0309 |
CHAMPION_GetMaximumLoad |
| F0310 |
CHAMPION_GetMovementTicks |
| F0311 |
CHAMPION_GetDexterity |
| F0312 |
CHAMPION_GetStrength |
| F0313 |
CHAMPION_GetWoundDefense |
| F0314 |
CHAMPION_WakeUp |
| F0318 |
CHAMPION_DropAllObjects |
| F0319 |
CHAMPION_Kill |
| F0320 |
CHAMPION_ApplyAndDrawPendingDamageAndWounds |
| F0321 |
CHAMPION_AddPendingDamageAndWounds |
| F0322 |
CHAMPION_Poison |
| F0323 |
CHAMPION_Unpoison |
| F0324 |
CHAMPION_DamageAll_GetDamagedChampionCount |
| F0325 |
CHAMPION_DecrementStamina |
| F0326 |
CHAMPION_ShootProjectile |
| F0327 |
CHAMPION_IsProjectileSpellCast |
| F0328 |
CHAMPION_IsObjectThrown |
| F0329 |
CHAMPION_AddObjectInLeaderHand |
| F0330 |
CHAMPION_DisableAction |
| F0331 |
CHAMPION_InitializeParty / CHAMPION_ApplyTimeEffects |
| F0332 |
INVENTORY_DrawIconToViewport |
| F0333 |
INVENTORY_OpenAndDrawChest |
| F0334 |
INVENTORY_CloseChest |
| F0335 |
INVENTORY_DestroyChest |
| F0336 |
INVENTORY_DrawPanel_BuildObjectAttributesString |
| F0337 |
INVENTORY_SetDungeonViewPalette |
| F0338 |
INVENTORY_DecreaseTorchesLightPower |
| F0339 |
INVENTORY_DrawPanel_ArrowOrEye |
| F0340 |
INVENTORY_DrawPanel_ScrollTextLine |
| F0341 |
INVENTORY_DrawPanel_Scroll |
| F0344 |
INVENTORY_DrawPanel_FoodOrWaterBar |
| F0345 |
INVENTORY_DrawPanel_FoodWaterPoisoned |
| F0346 |
INVENTORY_DrawPanel_Resurrect |
| F0347 |
INVENTORY_DrawPanel |
| F0349 |
INVENTORY_ProcessCommand70_ClickOnMouth |
F0302 owns the transaction at mouse-down. This includes the top-row
C020–C027 hand boxes (C211–C218) as well as inventory-page slots.
The transient leader hand is therefore the only in-flight owner during a
drag: releasing over a different admitted hand box completes one exchange;
releasing over the original box is consumed and must not perform a second
exchange. This prevents the apparent item disappearance caused by an
immediate swap-back.
Doors / movement / sensor dispatch (F0700-F0730) — Firestaff _Compat layer
These are Firestaff-authored bridge functions (not raw ReDMCSB F-numbers)
that wrap DUNGEON.C/MOVESENS.C logic into a callback-oriented,
testable API. They still carry F-numbers to preserve traceability to the
functional area they compat-wrap.
| F# |
Function |
| F0700 |
TriggerImmediateMouseEvent |
| F0701 |
MOVEMENT_GetStepDelta_Compat |
| F0702 |
MOVEMENT_TryMove_Compat |
| F0703 |
ReleaseChampionIcon |
| F0704 |
MOVEMENT_ResolvePostMoveEnvironment_Compat |
| F0707 |
SoundDescriptorPc34 |
| F0708 |
MOVEMENT_IsPartyStepBlockedByGroup_Compat |
| F0711 |
ConvertScanCodeToASCII |
| F0712 |
AnyKeyboardOrMouseInput |
| F0715 |
DOOR_ResolveToggleAction_Compat |
| F0716 |
DOOR_RouteFrontCellClick_Compat |
| F0718 |
SENSOR_ProcessPartyEnterLeave_Compat |
| F0720 |
TIMELINE_Init_Compat |
| F0721 |
TIMELINE_Schedule_Compat |
| F0722 |
SENSOR_EvaluateFloor_Compat |
| F0723 |
SENSOR_EvaluateWall_Compat |
| F0724 |
SENSOR_ResolveEffectDispatch_Compat |
| F0725 |
SENSOR_ProcessFloorSquare_Compat |
| F0726 |
SENSOR_ProcessWallClick_Compat |
| F0727 |
SENSOR_SquareTypeToEventType_Compat |
| F0728 |
SENSOR_ResolveHoldEffect_Compat |
| F0729 |
SENSOR_EvaluateWallCountdownEvent_Compat |
| F0730 |
NextCell_Compat |
Music (F0740-F0743) — MUSIC.C
| F# |
Function |
| F0740 |
MUSIC_Pause |
| F0741 |
MUSIC_PlayGameMusic |
| F0742 |
MUSIC_SetTrack (bound on stairs/teleporter transition) |
| F0743 |
MUSIC_Update (per-tick driver update) |
Wired into M11_GameViewState via dm1MusicSource / dm1MusicState /
dm1MusicDriver fields (see CLAUDE.md). Only the game-won track (C2) is
currently proven playable end-to-end; other tracks remain fail-closed.
Startup (F0800-F0910) — STARTUP2.C, GROUP.C (DM1-specific AI mirror), REVIVE.C
| F# |
Function |
| F0802 |
IsMagicMap |
| F0803 |
DrawMagicMapIcon |
| F0804 |
DrawMagicMap |
| F0805 |
CreatureNameScroll |
| F0810 |
DM1_GROUP_DispatchBehavior_Compat |
| F0811 |
DM1_GROUP_IsMovementPossible_Compat |
| F0812 |
DM1_GROUP_GetFirstPossibleMovementDir_Compat |
| F0813 |
DM1_GROUP_PickSingleSquareMove_Compat |
| F0814 |
DM1_GROUP_ShouldAttack_Compat |
| F0815 |
DM1_GROUP_IsMeleeRange_Compat |
| F0816 |
DM1_GROUP_ShouldUseProjectile_Compat |
| F0817 |
DM1_GROUP_SetGroupDirection_Compat |
| F0818 |
DM1_GROUP_GetDistanceToVisibleParty_Compat |
| F0819 |
DM1_GROUP_GetSmelledPartyDirOrdinal_Compat |
| F0820 |
DM1_GROUP_GetFleeDirection_Compat |
| F0821 |
DM1_GROUP_ShouldFrighten_Compat |
| F0822 |
DM1_GIGGLER_ResolveStealAttempt_Compat |
| F0823 |
DM1_GROUP_ResolveProjectileAttack_Compat |
| F0824 |
DM1_GROUP_ResolveFixedPossessionDrops_Compat |
| F0833 |
HungerThirstInput_Compat |
| F0834 |
LIFECYCLE_ClampFoodWater_Compat |
| F0841 |
LIFECYCLE_ComputeMoveTicks_Compat |
| F0859 |
LIFECYCLE_Init_Compat |
| F0860 |
RESURRECTION_ComputeBonesCreation_Compat |
| F0861 |
RESURRECTION_ShouldTriggerViAltarRebirth_Compat |
| F0862 |
RESURRECTION_GetChampionIndexFromBones_Compat |
| F0863 |
RESURRECTION_ComputeRebirthHealth_Compat |
| F0864 |
RESURRECTION_ComputeReincarnation_Compat |
| F0865 |
RESURRECTION_IsCommandValid_Compat |
| F0866 |
RESURRECTION_RouteChampionPortraitClick_Compat |
| F0867 |
RESURRECTION_ProcessCandidatePanelCommand_Compat |
| F0868 |
RESURRECTION_RunViAltarFullCycle_Compat |
| F0869 |
RESURRECTION_DecodeChampionValue_Compat |
| F0870 |
RESURRECTION_ResetDataToStartGamePlan_Compat |
| F0871-F0873 |
RESURRECTION_BuildHocMirrorCandidate{Selection,Finalize,Runtime}Receipt_Compat |
| F0881 |
WORLD_InitDefault_Compat |
| F0883 |
WORLD_Free_Compat |
| F0891 |
ORCH_WorldHash_Compat |
| F0897-F0899 |
WORLD_Serialize_Compat / WORLD_Deserialize_Compat / WORLD_SerializedSize_Compat |
| F0902 |
DrawFTLLogo |
| F0903 |
DrawErrorMessage |
| F0904 |
PaletteAnimation |
Reference: include/memory_dungeon_dat_pc34_compat.h, ReDMCSB DUNGEON.C.
The on-disk layout (little-endian, Watcom/Borland LSB-first bitfields) is:
Offset 0: DUNGEON_HEADER (44 bytes)
Offset 44: MAP[header.mapCount] (16 bytes each)
Then: raw map (square) data, thing arrays, square-first-thing table, text data
| Field |
Type |
Notes |
ornamentRandomSeed |
uint16 |
Seed for random wall/floor ornament placement (F0169-F0171) |
rawMapDataByteCount |
uint16 |
Byte size of the packed square-data blob |
mapCount |
uint8 |
Number of maps (levels) |
unreferenced |
uint8 |
Padding, unused |
textDataWordCount |
uint16 |
Word count of the text data section |
initialPartyLocation |
uint16 |
Bitfield: direction (bits 11-10), y (bits 9-5), x (bits 4-0) — decoded by F0501 |
squareFirstThingCount |
uint16 |
Number of compact SquareFirstThings entries |
thingCounts[16] |
uint16[16] |
Per-type thing counts (indexed by THING_TYPE_*) |
Known dungeon IDs (from DEFS.H): DUNGEON_ID_DM=10,
DUNGEON_ID_CSB_PRISON=12, DUNGEON_ID_CSB_GAME=13, DUNGEON_ID_KID=50.
A compressed-dungeon (save-game) signature of 0x8104 is explicitly
rejected by the DUNGEON.DAT-only loader.
Map descriptors (16 bytes each) — DungeonMapDesc_Compat
| Field |
Notes |
rawMapDataByteOffset |
Offset into the raw square-data blob |
aUnreferenced, bUnreferenced |
Padding |
offsetMapX, offsetMapY |
Map placement offsets |
level, width, height |
Decoded from bitfield A; width/height are stored value + 1 |
rawBitfieldB/C/D |
Raw 16-bit bitfields, kept alongside decoded fields |
wallOrnamentCount, randomWallOrnamentCount, floorOrnamentCount, randomFloorOrnamentCount |
Decoded from bitfield B |
doorOrnamentCount, creatureTypeCount, difficulty, allowedCreatureTypes[16] |
Decoded from bitfield C |
floorSet, wallSet, doorSet0, doorSet1 |
Decoded from bitfield D — selects which GRAPHICS.DAT graphic set to bind |
Square (tile) encoding
M034_SQUARE_TYPE(square) = square >> 5. Each square byte packs:
| Mask |
Meaning |
0xE0 (DUNGEON_SQUARE_MASK_TYPE) |
3-bit element type |
0x1F (DUNGEON_SQUARE_MASK_ATTRIBS) |
5-bit element-specific attributes |
0x10 (DUNGEON_SQUARE_MASK_THING_LIST) |
Set if the square has an entry in SquareFirstThings |
Element types (0-6)
| Value |
Name |
| 0 |
Wall |
| 1 |
Corridor |
| 2 |
Pit |
| 3 |
Stairs |
| 4 |
Door |
| 5 |
Teleporter |
| 6 |
Fake wall |
Thing types (0-15, 16 slots total)
| Value |
Type |
Bytes on disk |
| 0 |
Door |
4 |
| 1 |
Teleporter |
6 |
| 2 |
TextString |
4 |
| 3 |
Sensor |
8 |
| 4 |
Group |
16 |
| 5 |
Weapon |
4 |
| 6 |
Armour |
4 |
| 7 |
Scroll |
4 |
| 8 |
Potion |
4 |
| 9 |
Container |
8 |
| 10 |
Junk |
4 |
| 11-13 |
(unused) |
0 |
| 14 |
Projectile |
8 |
| 15 |
Explosion |
4 |
Per-type byte counts mirror ReDMCSB’s G0235_auc_Graphic559_ThingDataByteCount.
THING encoding (uint16 handle)
bits 15:14 direction (used at runtime only, not in on-disk thing arrays)
bits 13:10 thing type (0-15, see table above)
bits 9:0 index into ThingData[type] array
Special sentinel values: THING_NONE = 0xFFFF (unused slot),
THING_ENDOFLIST = 0xFFFE (end of a linked list).
Thing linked lists
Each square flagged with DUNGEON_SQUARE_MASK_THING_LIST has a compact
entry in the SquareFirstThings array (one entry per flagged square, not
one per tile — looked up via F0160/F0161). Each decoded thing record
carries a next field (a THING handle) forming a singly linked list per
square; walking stops at THING_ENDOFLIST. F0163/F0164
(DUNGEON_LinkThingToList / DUNGEON_UnlinkThingFromList) mutate these
lists; both fail closed if the loaded tables cannot admit the exact
requested mutation, including reproducing the original engine’s
BUG0_08 full-SFT-overflow corruption case for diagnostic purposes
(F0502b_DUNGEON_CheckBug0_08SftOverfill_Compat).
Per-type thing record layouts (decoded structs)
- Door (4B):
next, type (1b: set0/set1), ornamentOrdinal (4b),
vertical (1b), button (1b), magicDestructible (1b),
meleeDestructible (1b)
- TextString (4B):
next, visible (1b), textDataWordOffset (13b)
- Teleporter (6B):
next, targetMapX/Y (5b each), rotation (2b),
absoluteRotation (1b), scope (2b), audible (1b), targetMapIndex (8b)
- Sensor (8B):
next, sensorType (7b, M039_TYPE), sensorData
(9b, M040_DATA), onceOnly (1b), effect (2b), revertEffect (1b),
audible (1b), value (4b), localEffect (1b), ornamentOrdinal (4b),
plus a Remote/Local union over the last 2 bytes: Remote =
targetCell (2b) + targetMapX/Y (5b each); Local = localMultiple
(12b, health multiplier / tick count / kinetic energy depending on
sensor type)
- Group (16B):
next, slot (possession chain), creatureType (8b,
0-26 for DM), cells (8b, packs up to 4 creature cell positions),
health[4], behavior (4b), count (2b, actual count = value + 1),
direction (2b), doNotDiscard (1b)
- Weapon (4B):
next, type (7b), doNotDiscard (1b), cursed (1b),
poisoned (1b), chargeCount (4b), broken (1b), lit (1b, torches)
- Armour (4B):
next, type (7b), doNotDiscard (1b), cursed (1b),
chargeCount (4b), broken (1b)
- Scroll (4B):
next, textStringThingIndex (10b), closed (6b)
- Potion (4B):
next, power (8b), type (7b), doNotDiscard (1b)
- Container (8B):
next, slot (contents chain), type (2b)
- Junk (4B):
next, type (7b), doNotDiscard (1b), cursed (1b),
chargeCount (2b)
- Projectile (8B):
next, slot, kineticEnergy (8b), attack
(8b), eventIndex (16b)
- Explosion (4B):
next, type (7b), centered (1b), attack (8b)
Text data encoding
Each 16-bit word packs three 5-bit codes (code[0] = bits 14-10,
code[1] = bits 9-5, code[2] = bits 4-0; bit 15 unused):
| Code |
Meaning |
| 0-25 |
'A'-'Z' |
| 26 |
Space |
| 27 |
Period |
| 28 |
Separator (newline / 0x80 depending on context) |
| 29 |
Escape — next code indexes the symbol table |
| 30 |
Escape — next code indexes the word table ("THE ", "YOU ", …) |
| 31 |
End of text |
Strings are exposed by index (not content) since the table is
language-dependent. Text types: inscription (0), message (1), scroll (2),
with an optional 0x8000 mask to force decode even if the underlying
TextString thing is marked invisible.
DM1 PC 3.4 and the CSB reference/source lineage both use GRAPHICS.DAT, but the on-disk
compression differs by platform port in the ReDMCSB source tree:
- PC IMG3 — the PC 3.4 (DOS) graphic entry format. Entries are
LZW-compressed (ReDMCSB
LZW.C), decoded through the
F0687-F0691 chain implemented in Firestaff as
redmcsb_f0687_f0688_img3_pc34_compat, redmcsb_f0689_img3_expand_pc34_compat,
redmcsb_f0685_img3_line_fill_pc34_compat, and
redmcsb_f0691_draw_compressed_img3_pc34_compat. Firestaff’s DM1 path
reads GRAPHICS.DAT headers via memory_graphics_dat_header_pc34_compat.h,
which stores parallel compressedByteCounts / decompressedByteCounts
arrays per entry — confirming each entry can be independently
compressed/uncompressed.
- Amiga IMG1 — CSB’s Amiga GRAPHICS.DAT uses a nibble RLE scheme,
distinct from PC IMG3 (see
csb_v1_amiga_graphics_dat.h,
dm1_v1_amiga_graphics_dat.h, csb_v1_csbgraphics_dat_lzw_boundary.h,
csb_v1_csbgraphics_dat_classify.h). This is not interchangeable with
the PC LZW format even though both are called “GRAPHICS.DAT”.
- FM Towns variants exist for DM2 (
dm2_v1_fmtowns_graphics_dat.h,
csb_v1_fmtowns_graphics_dat.h). The DM1 FM Towns port ships on a
BIN/CUE disc image and drives all in-game presentation through the
TownsOS EGB graphics library and its own Phar Lap P3 executable
(EDM.EXP / JDM.EXP); see the dedicated
DM1 FM Towns guide and the disassembly
evidence at
parity-evidence/dm1_fmtowns_menu_p3_disassembly.md.
Firestaff’s memory-allocator model for GRAPHICS.DAT (memory_graphics_dat_*
modules — allocator entry/boundary/orchestrator, defrag loop/entry, used
list, slots, bitmap reuse) mirrors ReDMCSB MEMORY.C’s used-list +
defragmentation allocator so that graphic bitmap lifetime and memory
pressure match the original engine’s caching behavior (entries can be
evicted and re-decoded from the compressed source, matching the original’s
memory-constrained DOS environment).
Reference: ReDMCSB LOADSAVE.C, SAVEHEAD.C, SAVEUTIL.C, READWRIT.C;
Firestaff headers include/memory_savegame_pc34_compat.h,
include/redmcsb_f0435_save_tail_pc34_compat.h,
include/dm1_v1_original_save_pc34_handoff.h.
Obfuscation (F0417/F0418/F0419/F0420)
F0417_SAVEUTIL_GetChecksumAndObfuscate (SAVEUTIL.C) is the shared
XOR-obfuscation-plus-checksum primitive used across the save format. The
save header uses a minimal subset: 10 uint16 “Noise” entries are fed
through the F0417 XOR+checksum loop (SAVEHEAD.C:44,97,104). The dungeon
tail and other sections reuse the same primitive via READWRIT.C
F0417/F0419/F0420.
Load order (F0429-F0435)
SAVEHEAD.C F0429/F0430 — read and deobfuscate/checksum the save
header.
READWRIT.C F0433 — dungeon-tail checksum/obfuscation pass.
LOADSAVE.C F0435 — continuation after the header’s first three
parts: reads EVENTS (with per-part Keys[C3]/Checksums[C3]),
then the uint16 TIMELINE, then the dungeon tail proper.
Firestaff models this as an indexed part sequence:
REDMCSB_F0435_PC34_EVENTS_PART_INDEX = 3,
REDMCSB_F0435_PC34_TIMELINE_PART_INDEX = 4, with distinct failure
codes (EVENTS_FAILED = -1, TIMELINE_FAILED = -2,
DUNGEON_TAIL_FAILED = -3) so a byte-identical original save can be
diagnosed at the exact failing part.
Save tail layout (matches F0433/F0435)
DUNGEON_HEADER
MAP[]
column-cumulative SquareFirstThings counts
SquareFirstThings
text data
thing data for all 16 types
raw map (square) data
checksum
Firestaff retains the original tail bytes verbatim
(originalSaveTailBytes/originalSaveTailByteCount/
originalSaveTailPristine in DungeonDatState_Compat) until a live world
serializer explicitly replaces them, so F0433 can round-trip an original
save without lossy decode/re-encode of reserved dungeon fields — this is
the mechanism behind the project’s byte-identical DOS savegame
compatibility requirement.
6. Key Data Structures
DungeonThings_Compat (include/memory_dungeon_dat_pc34_compat.h)
Aggregates all decoded thing data for a loaded dungeon:
squareFirstThings[] + squareFirstThingCount
- Per-type decoded arrays + counts:
doors, textStrings, teleporters,
sensors, groups, weapons, armours, scrolls, potions,
containers, junks, projectiles, explosions
rawThingData[16] + thingCounts[16] — raw byte blobs for all 16
thing-type slots (kept even for types not yet fully decoded)
textData[] + textDataWordCount
loaded flag
DungeonMapDesc_Compat
16-byte on-disk map descriptor, decoded into level/width/height, ornament
counts, difficulty, allowed creature types, and floor/wall/door graphic
set selectors (see §3).
DungeonDatState_Compat
Top-level loader state: header, maps[], per-column cumulative
square-first-thing counts (mirrors ReDMCSB G0280), tiles[] (loaded
lazily by F0502, separate from header/map load by F0500), file size,
loaded/tilesLoaded flags, and the pristine save-tail byte cache described
above.
Thing type size/count limits
| Constant |
Value |
DUNGEON_THING_TYPE_COUNT |
16 |
DUNGEON_CREATURE_TYPE_MAX |
26 (27 types including 0) |
DUNGEON_WEAPON_TYPE_MAX |
45 |
DUNGEON_ARMOUR_TYPE_MAX |
57 |
DUNGEON_JUNK_TYPE_MAX |
52 |
DUNGEON_POTION_TYPE_MAX |
20 |
DUNGEON_CONTAINER_TYPE_MAX |
3 |
DUNGEON_MAX_MAPS |
32 (sanity cap) |
7. Creature Type Table
DM1 defines DUNGEON_CREATURE_TYPE_MAX = 26, meaning creature type indices
run 0-26 inclusive (27 distinct types). Per-type attributes (used for
combat resolution, AI behavior gating, and sound selection) are held in
DM1_V1_F0144_CreatureInfoPc34 records (include/dm1_v1_creature_attributes_f0144_pc34_compat.h,
sourced from ReDMCSB DUNGEON.C F0144), keyed by creatureType (uint8,
0-26). Related lookup tables:
- Map eligibility:
dm1_v1_creature_allowed_on_map_f0139_pc34_compat.h
(F0139) — cross-references DungeonMapDesc_Compat.allowedCreatureTypes[16]
and creatureTypeCount from the map descriptor.
- AI behavior:
dm1_v1_creature_ai_behavior_pc34_compat.h,
dm1_v1_creature_behavior_bootstrap_pc34_compat.h — behavior dispatch
mirrored in the F0810-F0824 range (movement, melee/projectile attack
selection, flee/frighten logic, giggler steal-attempt resolution).
- Rendering:
dm1_v1_creature_render_pc34_compat.h,
dm1_v1_creature_viewport_pc34_compat.h,
dm1_v1_f0115_f0219_creature_item_material_pc34_compat.h — viewport
creature draw path feeding into F0115 (DUNGEONVIEW_DrawObjectsCreatures).
- Sound:
dm1_v1_creature_sound_pc34_compat.h.
- Palette:
redmcsb_f8160_creature_palette_c25_pc34_compat.h (F8160,
creature replacement-color palette, C25) and
redmcsb_f0695_set_creature_replacement_colors_pc34_compat.h (F0695).
- Name scroll UI:
redmcsb_f0805_creature_name_scroll_pc34_compat.h
(F0805, used by the magic-map / bestiary display).
Groups reference creatureType (8-bit field, 0-26) directly in the
on-disk DungeonGroup_Compat record (§3), with up to 4 creatures per
group packed via the cells and health[4] fields.
Note: Firestaff exposes these tables by index, not by hardcoded
English name — matching the project’s index-first design used for text
strings (§3) so that future localization work is not blocked by
name-string assumptions baked into the reverse-engineered data layer.
8. Parity Evidence
The parity-evidence/ directory contains the cross-game pass corpus. The
exact current count is repository-derived rather than maintained as a
narrative number; the DM1-specific subset is identified by
pass{NNN}_dm1_v1_* filenames,
following the naming convention pass{NNN}_{description}.md described in
CLAUDE.md.
Each pass document is a source-lock evidence record: it ties a specific
Firestaff implementation change to the exact ReDMCSB source lines it
reproduces, and typically records the pc34_compat module(s) touched.
Examples visible in the current tree span the full breadth of subsystems
covered above — inventory slot placement and drop order (pass1091,
pass863), viewport occlusion gating (pass404), initial-hall capture
paths (pass466), door/entrance graphics (pass840), palette and cursor
bitmap handling (pass850-pass858, pass870-pass871), mandatory
graphic indices (pass852), wound defense factor (pass853), champion
rename/reincarnate character-string handling (pass854-pass856), square
type → event type mapping (pass859, matching F0727 above), champion
icon and bar-graph byte offsets (pass860-pass862), and box
screen-edge layout constants (pass864-pass866).
This evidence corpus is the practical cross-reference for any future
reverse-engineering work: before re-deriving behavior from ReDMCSB source,
check whether a parity-evidence/pass*_dm1_v1_* document already records
the exact lines and the Firestaff module that encodes them.