GEN
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. |
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: HP MP Atk Aim Def Dog Mag Wil Spd Ctr Focus 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 |
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 compat.bi. 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 |
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 |
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.
Passwords[edit]
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
PW4[edit]
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; }
PW3[edit]
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.
PW2[edit]
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. BIT OFFSETS/SCATTERTABLE 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
ARCHINYM.LMP . ATTACK.BIN . BINSIZE.BIN . BROWSE.TXT . DEFPAL#.BIN . DEFPASS.BIN . distrib.reld . FIXBITS.BIN . heroes.reld . heroform.reld . LOOKUP1.BIN . MENUS.BIN . MENUITEM.BIN . PALETTES.BIN . PLOTSCR.LST . SFXDATA.BIN . slicetree_#_#.reld . SLICELOOKUP.TXT . SONGDATA.BIN . UICOLORS.BIN . GEN . general.reld . BAM . Map Format . T . P . E . D . L . N . Z . DOR . DOX . DT0 . DT1 . DT6 . EFS . FOR . FNT . HSP . HSZ . ITM . MAP . MAS . MN . MXS . PAL . PT0 . PT1 . PT2 . PT3 . PT4 . PT5 . PT6 . PT7 . PT8 . SAY . SHO . SNG . STF . STT . TAP . TIL . TMN . VEH