Jump to navigation Jump to search

The lump with the extension .GEN is a critical one. It contains miscellaneous information about a wide variety of unrelated things, especially max number of records for other files, password information, and other stuff that I felt like sticking in there too-- some of which really don't belong.

New general options are increasingly being added to general.reld instead, the RELOAD-format alternative.

This lump is stored in BSAVE format. It has a 7 byte header which indicates the size of the remaining data (and other things). See BSAVE Header for more information.

The .gen lump is normally 1007 bytes long (including 7 byte header). NOTE: The fixWipeGEN fixbit must be checked when reading values at offsets 199 and up.

The following is the meaning of each element.

Grey fields are obsolete or unused.
Pink fields are runtime-only fields used by GAME that do not really belong GEN in the first place, but are there anyway to make including them in SAV files more convenient, which is now a moot argument because of the conversion to RSAV.
Blue fields are editor-only fields used by CUSTOM, although almost all of them are also used in-game for error-checking valid data record numbers. GAME and CUSTOM will both automatically set all these "max/number of X" fields to the true values.
Green records are preserved in RSAV files.

About Formal Specs

Element Data Constant Meaning
0 INT genMaxMap Max (last) valid map number
1 INT genTitle Title screen backdrop index
2 INT genTitleMus title music
3 INT genVictMus victory music + 1 (0 for none)
4 INT genBatMus default battle music
5 INT genPassVersion passcode format number
6 INT genPW3Rot old (third-style) passcode rotator
7-15 BYTE*17 + 1 old (third-style) obfuscated passcode + 1 unused byte
16-25 INT*10 ancient (first-style) obsolete password data
26 INT genMaxHeroPic Last (max) valid hero spriteset number (in PT0/heroes.rgfx)
27 INT genMaxEnemy1Pic Last (max) valid small enemy spriteset number (graphics in PT1/enemies.rgfx). Includes only the spritesets in enemies.rgfx which are counted as small enemy graphics.
28 INT genMaxEnemy2Pic Last (max) valid medium spriteset number (graphics in PT2/enemies.rgfx). Includes only the spritesets in enemies.rgfx which are counted as medium enemy graphics.
29 INT genMaxEnemy3Pic Last (max) valid large enemy spriteset number (in PT3/enemies.rgfx). Includes only the spritesets in enemies.rgfx which are counted as large enemy graphics.
30 INT genMaxNPCPic Last (max) valid walkabout spriteset number (in PT4/walkabouts.rgfx)
31 INT genMaxWeaponPic Last (max) valid weapon spriteset number (in PT5/weapons.rgfx)
32 INT genMaxAttackPic Last (max) valid attack spriteset number (in PT6/attacks.rgfx)
33 INT genMaxTile Last (max) tileset number in TIL
34 INT genMaxAttack max attack definitions in DT6
35 INT genMaxHero max hero definitions in DT0
36 INT genMaxEnemy max enemy definitions in DT1
37 INT genMaxFormation max formations in FOR
38 INT genMaxPal max palettes in PAL
39 INT genMaxTextbox max text boxes in SAY
40 INT genMaxPlotscript total available plotscripts (number of records in PLOTSCR.LST)
41 INT genNewgameScript ID of new-game plotscript
42 INT genGameoverScript ID of game-over plotscript
43 INT genMaxRegularScript ID of highest manually numbered old-style plotscript
44 INT genSuspendBits suspendstuff bits:

0 - suspendnpcs
1 - suspendplayer
2 - suspendobstruction
3 - suspendherowalls
4 - suspendnpcwalls
5 - suspendcatapillar
6 - suspendrandomenemies
7 - suspendboxadvance
8 - suspendoverlay
9 - suspendambientmusic
10 - suspenddoors
11 - suspendtimers

45 INT genCamera camera mode
46 INT genCamArg1 cameraarg1
47 INT genCamArg2 cameraarg2
48 INT genCamArg3 cameraarg3
49 INT genCamArg4 cameraarg4
50 INT genScrBackdrop Index of currently displaying script backdrop , or -1 for none. (Does not include textbox backdrops)
51 INT genDays days of play
52 INT genHours hours of play
53 INT genMinutes minutes of play
54 INT genSeconds seconds of play
55 INT genMaxVehicle max vehicle types in VEH
56 INT genMaxTagname Last named tag
57 INT genLoadgameScript load-game plotscript
58 INT genTextboxBackdrop Index of currently displaying text box backdrop
59 INT genEnemyDissolve Default enemy dissolve type. See dissolve types in DT1 (each dissolve type is numbered one less than it is in DT1, for example Random Pixels is 0.
60 INT genJoy Enable/disable joystick
61 INT genPoisonChar Posion status indicator char (default to 161 if 0)
62 INT genStunChar Stun status indicator char (default to 159 if 0)
63 INT genDamageCap Damage cap (0 if there is none)
64 INT genMuteChar Mute status indicator char (default to 163 if 0)
65-76 INT*12 genStatCap Stat caps for each stat, in this order:
Extra Hits
77 INT genMaxSFX Number of sound effects
78 INT genMasterPal Master palette number in PALETTES.BIN
79 INT genMaxMasterPal Max master palette number in PALETTES.BIN and number of UI color sets in UICOLORS.BIN
80 INT genMaxMenu Max menu in MENUS.BIN
81 INT genMaxMenuItem Max menu item in MENUITEM.BIN
82 INT genMaxItem Max item number in ITM
83 INT genMaxBoxBorder Max valid box border spriteset number (in PT7/boxborders.rgfx)
84 INT genMaxPortrait Max valid portrait spriteset number (in PT8/portraits.rgfx)
85 INT genMaxInventory Max available inventory slots. 599 is current default max. Gets rounded up to the next multiple of 3 slots (counting the zeroth slot). Zero uses the default max of 599 (600 counting zero)
86 INT genErrorLevel Error suppression level:
0: default to 4
2: show all warnings
3: suppress some warnings
4: suppress warnings from weak type checking
5: don't show errors not displayed in old versions
6: show only errors about corruption or interpreter problems
87 INT genLevelCap Maximum hero level (0-genMaxLevel) (Unlike genMaxLevel, this can be changed with scripting, and has no effect on stat growth)
88 INT genEquipMergeFormula Function used to calculate total hero elemental resistances from equipment:

0: Awful compatible formula
1: Multiplicative
2: Additive

89 INT genNumElements Number of elements which the game uses, up to 64 (any higher numbered ones should be ignored in-game)
90 INT genUnlockedReserveXP Experience gained by unlocked reserve heroes, as a % of that given to active party heroes
91 INT genLockedReserveXP Experience gained by locked reserve heroes, as a % of that given to active party heroes
92 INT genPW4Hash Hashed password, 0 if none
93 INT genPW2Offset old(second-style) passcode offset
94 INT genPW2Length old(second-style) passcode length (-1 if there is no password)
95 INT genVersion RPG file format version ID (The latest version number is CURRENT_RPG_VERSION in See also Incrementing the RPG format version number)
96 INT genStartMoney starting money
97 INT genMaxShop last shop in SHO
98 INT genPW1Offset old-old(first-style) passcode offset
99 INT genPW1Length old-old(first-style) passcode length
100 INT genNumBackdrops Number of backdrops (in MXS/backdrops.rgfx)
101 INT genBits #General bitsets 0-15
102 INT genStartX starting X
103 INT genStartY starting Y
104 INT genStartMap starting Map
105 INT genOneTimeNPC one-time-NPC indexer
106-170 INT*65 genOneTimeNPCBits one-time-NPC placeholder bits. there are 0-1039 bits, but savegames store 0-1031 and the NPC editor only allows 0-1000
171 INT genDefaultDeathSfx Default enemy death sound effect ID + 1 (0 for none)
172 INT genMaxSong last song number (SNG and BAM)
173 INT genAcceptSfx Sound effect to use for "accept" (in menus) ID + 1 (0 for none)
174 INT genCancelSfx Sound effect to use for "cancel" (in menus) ID + 1 (0 for none)
175 INT genChooseSfx Sound effect to use for moving the cursor (in menus) ID + 1 (0 for none)
176 INT gentextboxLetter Sound effect to use for displaying text box lines ID + 1 (0 for none)
177-178 INT*2 genBits2 #General bitsets 16-47
179 INT genItemLearnSFX Sound effect when learning a spell from an item. ID + 1 (0 for none)
180 INT genCantLearnSFX Sound effect when a hero couldn't learn spell from item. ID + 1 (0 for none)
181 INT genBuySFX Sound effect when buying an item from a shop. ID + 1 (0 for none)
182 INT genHireSFX Sound effect when hiring a hero from a shop. ID + 1 (0 for none)
183 INT genSellSFX Sound effect when selling an item to a shop. ID + 1 (0 for none)
184 INT genCantBuySFX Sound effect when you can't afford item/hire. ID + 1 (0 for none)
185 INT genCantSellSFX Sound effect when trying to sell an unsellable item. ID + 1 (0 for none)
186 INT genDamageDisplayTicks Number of ticks to display damage numbers in battle (default 7)
187 INT genDamageDisplayRise Number of pixels the displayed damage should rise (default 14)
188 INT genHeroWeakHP % HP threshold for hero weak state (default to 20 if 0)
189 INT genEnemyWeakHP % HP threshold for enemy weak state (default to 20 if 0)
190 INT genAutosortScheme Method of autosorting inventory:

0 - by item type
1 - into usable/not usable
2 - alphabetically
3 - by ID number
4 - no reordering beyond compacting

191 INT genMaxLevel Maximum level (0-99) This is the unchanging maximum allowed level for heroes. it is used in the hero stats editor. (Not to be confused with genLevelCap)
192 INT genBattleMode Battle mode 0=Active-time, 1=Turn-based
193 INT genItemStackSize Default maximum size of an inventory stack
194 INT genResolutionX Width in pixels of the game screen (not including graphics backend scaling) or 0 for default, 320x200
195 INT genResolutionY Height in pixels of the game screen (see above).
196 INT genEscMenuScript plotscript to run when pressing ESC or ALT to open the menu (if zero, just open the menu)
197 INT genSaveSlotCount The number of available save slots, 1 to 1000. If 0, the default of 4 will be used. In versions older than fufluns the valid range was 1-32
198 INT genMillisecPerFrame Number of milliseconds per frame, or 55 if zero.
199 INT genStealSuccessSFX ID+1 of sound effect to play for a successful item steal attack, or zero for none.
200 INT genStealFailSFX ID+1 of sound effect to play for an unsuccessful item steal attack, or zero for none.
201 INT genStealNoItemSFX ID+1 of sound effect to play for an item steal attack with nothing to steal, or zero for none.
202 INT genRegenChar Regen status indicator char (default to 32 if 0).
203 INT genDefaultScale (Obsolete). For a short while, contained the default scale/zoom factor of the display (1-16), or zero for whatever default is suitable.
204 INT genDebugMode 0=Release mode, script errors hidden, 1=Debug mode, script errors shown. This setting is editable, and is used as the base for genCurrentDebugMode
205 INT genCurrentDebugMode 0=Release mode, script errors hidden, 1=Debug mode, script errors shown. This is not directly editable. It is set based on genDebugMode but may be temporarily modified by the "Distribute Game" menu or the "Test Game" feature
206 INT genStartHero ID number the initial hero when starting a new game.
207 INT genStartTextbox ID number a textbox to open when starting a new game, 0 means none.
208 INT genWindowSize Default window size about X% of screen, in multiples of 10% (1-10). 10 means maximize. If 0, default to 8.
209 INT genLivePreviewWindowSize Test-Game default window size about X% of screen, in multiples of 10%. 10 means maximize. If 0, default to 5.
210 INT genFullscreen If 1 (or nonzero), default to fullscreen if there is no remembered preference for this game from previous plays.
211 INT genMusicVolume Initial music volume as a percentage (Needs initialising if fixInitDefaultVolumes false)
212 INT genSFXVolume Initial global sound effects volume as a percentage (Needs initialising if fixInitDefaultVolumes false)
213 INT genRungameFullscreenIndependent If false, fullscreen settings/config for games spawned by rungame are ignored
214 INT genSkipBattleRewardsTicks If > 0 then the battle rewards messages will automatically advance after this many ticks
215 INT genDefOnkeypressScript Default on-keypress Plotscript Trigger
216 INT genDefEachStepScript Default each-step Plotscript Trigger
217 INT genDefAfterBattleScript Default after-battle Plotscript Trigger
218 INT genDefInsteadOfBattleScript Default instead-of-battle Plotscript Trigger
219 INT genDefMapAutorunScript Default map autorun Plotscript Trigger
220 INT genMaxEnemyPic Last (max) valid enemy spriteset number (in enemies.rgfx)
221 INT genMinimapAlgorithm Algorithm for producing in-game minimaps:

0 - Smooth scaling (default)
1 - Scatter - original random pixel selection algorithm
2 - Majority: (Approximate) most common color

222-225 INT*4 genBits3 #General bitsets 48-111
226 INT genDefCounterProvoke Default Provoke Counterattacks setting for attacks. Value 0-8, same meanings as DT6 Provoke Counterattacks setting, except Default means 'Always'
227 INT genInventSlotx1Display When to display x1 in inventory: 0: always, 1: never, 2: only if item maximum stacksize > 1
228 INT genCameraOnWalkaboutFocus How to calculate walkabout camera focus: 0: on tile, 1: on sprite, 2: on sprite minus Z/offset
229 INT genTicksPerWalkFrame Number of ticks per walking frame. 0 = default (110ms/frame; ticks calculated from genMillisecPerFrame and rounded to a whole number)
230 INT genAddHeroScript Script trigger called when hero added
231 INT genRemoveHeroScript Script trigger called when hero deleted/removed
232 INT genMoveHeroScript Script trigger called when hero moved to a different slot
233 INT gen8bitBlendAlgo Algorithm to use for palette lookups for blending in 8-bit color mode:

0 - Dither (Half of pixels (in a chequered pattern) are nearest-matched, with 1/2 or 3/4 (alternating) of error diffused to next pixel)
1 - Less Dither (Like 'Dither' but only 1/4 of error diffused, without alternation)
2 - No Dither (nearest match color)

234 INT genPreviewBackdrop 0 or ID+1 of backdrop to use for enemy previews (and maybe more in future)
235 INT gen32bitMode 1 for 32-bit color mode, 0 for 8-bit
236 INT genDefaultBattleMenu 0 for default, or menu ID + 1 (This is a WIP feature)
237-499 INT unused space

Maximums : "max XYZ" means 'the highest numbered XYZ'. If you have 80 enemies defined, then "max enemy" == 79, because the numbering starts from 0.

General bitsets[edit]

Preference bits, backcompat bits and battle system bits are stored in a single bit array, which is split into pieces beginning at gen(genBits), gen(genBits2) and gen(genBits3). They are accessed with the prefbit() and setprefbit() functions in FB code, and read preference bit and write preference bit functions in HS code.

Note: in the Preference Bitsets and other editors, some of the names of bits are reversed from what they are below. For example the editor has a bit named "Restore HP on level-up" which appears ON if bit 2 ("Don't restore HP on level-up") is OFF.

Also note that some bits are turned on during game upgrade by fixbits.

  • 0 - Pause in battle on spell and item menus
  • 1 - Enable Caterpillar party
  • 2 - Don't restore HP on level-up
  • 3 - Don't restore MP on level-up
  • 4 - Inns don't revive dead heros
  • 5 - Hero swapping always available
  • 6 - Hide ready meter in battle
  • 7 - Hide health bar in battle
  • 8 - Disable debugging keys
  • 9 - Simulate old level-up bug
  • 10 - Permit double-triggering of scripts
  • 11 - Skip title screen
  • 12 - Skip load screen
  • 13 - Pause in battle on all menus
  • 14 - Disable Hero's Battle Cursor
  • 15 - Default passability disabled by default
  • 16 - Simulate pushable NPC collision bug
  • 17 - Disable ESC to run from battle
  • 18 - Don't save gameover/loadgame script IDs (actually prevents using the stored "game over" or "loadgame" script ID number in preference to the ones specified by gen(genGameoverScript) and gen(genLoadgameScript))
  • 19 - Dead heroes gain share of experience
  • 20 - Locked heros can't be rearranged
  • 21 - Attack captions pause battle meters
  • 22 - Don't randomize battle ready-meters
  • 23 - Battle menus wait for attack animations
  • 24 - Enable better scancodes for scripts
  • 25 - Simulate old fail vs. elemental resist bit
  • 26 - 0 damage when immune to attack elements
  • 27 - Recreate map slices when changing maps
  • 28 - Harm tiles harm non-caterpillar heroes
  • 29 - Ignore extra Hits stat
  • 30 - Don't divide experience between heroes
  • 31 - Don't reset max stats after OOB attack
  • 32 - Don't limit max tags to 1000
  • 33 - Enable bug 430
  • 34 - showtextbox happens immediately
  • 35 - Pause battle while targeting attacks
  • 36 - Old attack positioning at bottom-left of target
  • 37 - Wrap map layers over edge of Crop maps
  • 38 - Never show script timer during battles
  • 39 - Draw Backdrop slice above Script layer
  • 40 - Don't stop music when starting/loading game
  • 41 - Keep caterpillar length the same when speed changes
  • 42 - Heroes use Walk in Place animation while idle.
  • 43 - Cap minimum stats at zero.
  • 44 - Hide empty save slots at the bottom of the save/load menu
  • 45 - Attack IDs in scripts are offset by +1 (This incomplete feature is not in nightlies)
  • 46 - Negative damage harmtiles don't cure above max HP
  • 47 - Don't map joystick controls to keyboard keys for scripts
  • 48 - Disable ESC to cancel/change a hero's attack (turn-based battles only)
  • 49 - Show hero MP meter
  • 50 - Non-turn attack delays don't also cause turn delays
  • 51 - Don't break Speed ties randomly (turn-based battles only)
  • 52 - Ignore MP~ stat
  • 53 - "!Map joystick (left) stick to dpad"
  • 54 - Auto-targeted attacks in random spell lists respect attack costs (This is a WIP feature)
  • 55 - Turncoats and defectors always treat self as an ally (This is a WIP feature)
  • 56 - Battles don't display at 320x200 (This is a WIP feature)
  • 57 - Use old direction key tiebreaking (This is a WIP feature)
  • Bits up to 111 unused


The password protection in the RPG format is necessarily wet-paper-bag weak, due to the GNU GPL licensing of the engine. RPG password protection works entirely on the honor system. It's kinda like that faded sign hanging on the gates of Anhk-Morpork that says "Thank you for not invading our city"

Here is how to read the password. Please-please-pretty-please dont just ignore it. It is flimsy pathetic laughable protection, but its all we have! Dont take away our wire umbrella in the lightening storm!

Over time there have been 4 different password formats, referred to as PW1, PW2, PW3, PW4. All are stored in the GEN lump. The format is determined by looking at genVersion and genPassVersion (go down this table):

Format Indicator Introduced Last used
PW4 gen(genPassVersion) = 257 pre-Zenzizenzic (Dec 28 2010) Still in use.
PW3 gen(genPassVersion) = 256 Ozarks (June 28 2004) Still partial back-compat (see below)
PW2 gen(genVersion) >= 3 June 18 1999 Written until Dec 28 2010
PW1 Otherwise Stone Age

For backwards compatibility, Custom also writes a blank PW3 password is there is no password, or a random PW3 password if there is one.

The easiest thing to do when decoding an RPG file that still has its password in an old format is to print a warning message telling the user to upgrade their RPG file with CUSTOM.EXE


This new format stores just a 9-bit hash of the password, in gen(genPW4Hash), so that it is not retrievable.

The hash is calculated as follows (or see common.bas for the FB version):

unsigned short passwordhash (char *p) {
  if (strlen(p) == 0) return 0;
  unsigned int hash = 0;
  while (*p)
    hash = hash * 3 + *p++ * 31;
  return (hash & 511) | 512;


In this format, the password is obscured with weak, but space-wasting encryption, and the length of the password is totally unprotected.

GEN(6) is the password rotator. It will always be a random number from 1 to 254 that will be used to ascii-rotate/obfuscate the password.

GEN(7-15) is the password text. There are 17 bytes of data here (so the high byte of GEN(15) is unused). Read this string of 17 bytes. ascii-rotate each character by subtracting GEN(6) and if the result is < 0 then add 255, and then discard all characters with ascii values less than 32 (space). The resulting string will be your password. If the string is empty (all 17 chars were under 32) then the RPG file does not have a password.


This information describes the obsolete scattertable-based password storage format. It is only here as a curiosity... and a monument of shame to security-through-obscurity. The body of the scattertable lived at GEN(200) to GEN(359), with the "scattertable head" at gen(199). The first element of the password mess GEN(200) is the bit-offset of the first bit in the password, relative to the beginning of the scattertable head (199). The next element (201) is the bit-offset of the next bit in the password, and so on. So GEN(200) thru GEN(207) will give you the bits that make up the first ascii char of the password. GEN(208)-GEN(215) gives you the next char, and so on up to GEN(94)+1 bytes. After you have read all the chars from the scattertable, rotate them down by the value of GEN(93) (that is to say, subtract GEN(93) from the ascii value of each char, and if the result is less than 0, add 256).

Clear as mud, right? Here is an example. Lets suppose that the data in the GEN lump looks like this

GEN(93)=172    the ascii-rotate offset
GEN(94)=23     the number of bits -1.
               23+1 = 24, thats the total number of bits in the password
               There are 8 bits in each char of the password, so 24
               divided by 8 is 3. That means this password will end up being 3 chars long.
GEN(199)=8 [0001 0000 0000 0000] dummy number. Never 0 or 255
GEN(200)=4 [0010 0000 0000 0000] Start here. bit 4 in the table is a 0
GEN(201)=3 [1100 0000 0000 0000] bit 3 in the table is a 1, now we have 01
GEN(202)=18[0100 1000 0000 0000] bit 18 is 1: 011
GEN(203)=18[0100 1000 0000 0000] bit 18 is 1: 0111
GEN(204)=54[0110 1100 0000 0000] bit 58 is 0: 01110
GEN(205)=33[1000 0010 0000 0000] bit 33 is 1: 011101
GEN(206)=85[1010 1010 0000 0000] bit 85 is 1: 0111011
GEN(207)=82[0100 1010 0000 0000] bit 82 is 1: 01110111

So we end up with 01110111 which is char 238, (a wierd ascii math symbol) Now we subtract the value of GEN(93) and we get 66, which is the ascii code for upper case B, so we know the first letter of the password is B.

Ah, what fun. what a mess. what a waste. *sigh*


We didn't bother to document this here, but it's yet another scheme, very similar to PW3! If you care, you can read the read_PW1_password() function in the source. It stores the password in gen(5) upwards, and in gen(genPW1Offset) and gen(genPW1Length) (gen(98) and gen(99).

See Also[edit]

  • Garbage in GEN, an investigation of junk in high areas of GEN in older games