Jump to: navigation, 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 some of them, like 39 genMaxTextbox may be used by GAME for error-checking)
Green records are preserved in RSAV files.

About Formal Specs

Element Data Constant Meaning
0 INT genMaxMap max maps
1 INT genTitle title screen backdrop index in MXS
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 max hero graphics in PT0
27 INT genMaxEnemy1Pic max small enemy graphics in PT1
28 INT genMaxEnemy2Pic max med enemy graphics in PT2
29 INT genMaxEnemy3Pic max large graphics in PT3
30 INT genMaxNPCPic max npc graphics in PT4
31 INT genMaxWeaponPic max weapon graphics in PT5
32 INT genMaxAttackPic max attack graphics in PT6
33 INT genMaxTile max tilesets 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
45 INT genCamera camera mode
46 INT genCamArg1 cameraarg1
47 INT genCamArg2 cameraarg2
48 INT genCamArg3 cameraarg3
49 INT genCamArg4 cameraarg4
50 INT genScrBackdrop currently displaying script backdrop index in MXS
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 currently displaying text box backdrop in MXS
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 box boder pictures in PT7
84 INT genMaxPortrait Max portrait graphics in PT8
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 (For latest version see 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 screens in MXS
101 INT genBits general bitsets:
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
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 Additional general bitsets

0 - Simulate pushable NPC collision bug
1 - Disable ESC to run from battle
2 - 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))
3 - Dead heroes gain share of experience
4 - Locked heros can't be rearranged
5 - Attack captions pause battle meters
6 - Don't randomize battle ready-meters
7 - Battle menus wait for attack animations
8 - Enable better scancodes for scripts
9 - Simulate old fail vs. elemental resist bit
10 - 0 damage when immune to attack elements
11 - Recreate map slices when changing maps
12 - Harm tiles harm non-caterpillar heroes
13 - Attacks ignore extra hits by default
14 - Don't divide experience between heroes
15 - Don't reset max stats after OOB attack
16 - Don't limit max tags to 1000
17 - Enable bug 430
18 - showtextbox happens immediately
19 - Pause battle while targeting attacks
20 - Old attack positioning at bottom-left of target
21 - Wrap map layers over edge of Crop maps
22 - Never show script timer during battles
23 - Draw Backdrop slice above Script layer

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 32. If 0, the default of 4 will be used.
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) (This is a WIP feature)
212 INT genSFXVolume Initial global sound effects volume as a percentage (Needs initialising if fixInitDefaultVolumes false) (This is a WIP feature)
213-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.


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*

See Also[edit]

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