// Program written by Maarten Meuris AKA Nyerguds
program duneedit;

uses
  crt,
  dos,
  Math,
  SysUtils,
  StrUtils;

type
  // Input key codes - "endk" is the [END] key
  keymapper = record
    up, down, left, right, space, enter, esc, back, pgup, pgdn,
    home, endk, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12: char;
  end;
  // For storing layout characters
  charset = array[1..2] of array [1..10] of byte;
  // For coordinates of layout frames
  coords = array [1..4] of integer;
  // For storing a 4-byte value as booleans
  boollist = array [0..31] of boolean;

  // For the editing description for each data type
  descr = array [1..2] of string[36];
  // For the help description for each data type
  bigdescr = array [1..8] of string[48];

  // Type designed for data types where the list index in the editor does not necessarily correspond to the internal value
  values = record
    val: integer;
    txt: string[30];
  end;

  // Type for the data identification table
  option = record
    typeid: integer;
    Text: string[23];
    helpid: integer;
  end;

  // This has been put in specific types to have a single type for the unit/structure/etc. lists
  lev2Lst = array[0..800] of string; // put on 800 to support the 600+ item Files lists
  lev3Lst = array[0..80] of Option;

  // Put on minimum used for now. Increase as needed,
  // and pad existing arrays with empty strings.
  // Would be handy if 'pointer to array of string' without length was possible.
  customNames = array[0..37] of string;

  // Struct for main editable types list
  editableType = record
    // Name of the type in the list
    typeName: string[20];
    // Ref to list with options
    optsList: ^lev3Lst;
    // Which entry in the optsList is the name reference? -1 for none.
    refOption: integer;
    // if refOption is -1, use this array as names instead of displaying bare indices.
    refArray: ^customNames;
  end;

  // Basic editable types array. Everything adjust to the size given here.
  editableTypes = array[-1..7] of editableType;

  // Specific type info information per version
  typeInfoVer = record
    // start offset of this type in this version
    offset: cardinal;
    // Length of this type in this version
    listlen: integer;
    // Optional override for general refArray for this version.
    // Only works if the original type uses a refArray.
    refArray: ^customNames;
  end;

  // An array with the available SHP frames
  graphicsSet = array[0..354] of string[19];

  // All information needed to handle one game version
  version = record
    // Short name string for showing on the editor UI
    nameShort: string[7];
    // Long name of the version
    nameLong: string[48];
    // Description of the version
    Info: bigdescr;
    // Address where the internal exe filename string can be found.
    // This is a good way to do version checking, since the game refuses
    // to start unless this string is the same as the actual exe filename.
    verAddress: cardinal;
    // Address adjustment for address references
    refAddress: cardinal;
    // Address adjustment for script references
    refHeader: cardinal;
    // The data info for all data types. This automatically adjusts to listTypes size.
    dataInfo: array[low(editableTypes)..high(editableTypes)] of typeInfoVer;
    // Strings array to use for the SHP frames list
    Graphics: ^graphicsSet;
  end;

  // Type for defining the different kinds of script lines in the const section
  // (special action scripts for buildings/units)
  scriptvalues = record
    val: integer;
    isEnd: boolean;
    hasArg: boolean;
    shortTxt: string[4];
    longTxt: string[30];
  end;

  // For storing a script line read from the exe (special action scripts for buildings/units)
  scriptLine = record
    // is actually just a tupel; never insert a value higher than $F!
    command: byte;
    // 3 remaining tupels of Word: never insert a value higher than $FFF!
    argument: integer;
  end;

var
  // Game version
  gamever,
  // Which list is active
  level,
  // Level to return to after level #4
  prevlev: integer;

  // Option list lengths (calculated on init)
  // Only -1 to 4 are used so far, but I put the length on 32 to be safe.
  // This list must be at least as long as "mainList".
  optsListlengths: array[low(editableTypes)..high(editableTypes)] of cardinal;

  // Binary editing mode enabled / Hexadecimal display mode enabled
  binEd, HexDisplay: boolean;
  // File to edit
  exefile: file;
  // input
  keyread: char;
  // Error message when editing fails or cancels
  errormsg: string;
  // filename string
  gamenamestr: string;
  // complete file path string
  gamepathstr: string;
  // navigation & scrolling data for all levels
  select, scroll, maxdisplay, listlen: array [1..5] of integer;
  // Display lists to print on screen
  lev2List, lev4List: lev2Lst;
  lev3List: ^lev3Lst;
  lev3data: array [0..19] of string[20];

const
  // General info Strings
  editorname: string[14] = 'Dune II Editor';
  editorversion: string[8] = 'v2.01.0';
  editorversiondate: string[30] = 'October 25 2012';
  editorauthor: string[8] = 'Nyerguds';
  editorauthoremail: string[20] = 'nyerguds@gmail.com';
  defnamestr: string[9] = 'DUNE2.EXE';

  // == INI DUMP OPTIONS ==

  rulesfileprefix: string[6] = 'rules-';
  rulesfileext: string[3] = 'ini';
  profilerulesname: string[7] = 'profile';

  ConstructHeader: string[9] = 'Construct';
  ConstructInfo: String[88] = 'Name=Cost,BuildTime,HitPoints,Sight,BuildLevel,BuildPriority,TargetPriority,SortPriority';
  ConstructOpts: array[1..8] of integer = (24, 25, 21, 22, 26, 36, 37, 28);

  CombatHeader: String[6] = 'Combat';
  CombatInfo: String[34] = 'Name=Range,Damage,RateOfFire,Speed';
  CombatOpts: array[1..4] of integer = (69, 70, 68, 61);

  // == END OF INI DUMP OPTIONS ==

  // Input key codes
  keymap: keymapper =
    (
    up: char(072);
    down: char(080);
    left: char(075);
    right: char(077);
    space: char(032);
    enter: char(013);
    esc: char(027);
    back: char(008);
    pgup: char(073);
    pgdn: char(081);
    home: char(071);
    endk: char(079);
    f1: char(059);
    f2: char(060);
    f3: char(061);
    f4: char(062);
    f5: char(063);
    f6: char(064);
    f7: char(065);
    f8: char(066);
    f9: char(067);
    f10: char(068);
    f11: char(133);
    f12: char(134)
    );

  // character codes for drawing frames.
  // The two types in the array are Inactive (single line) and Active (double line)

  // DOS ASCII version
  charsetdos: charset =
    (
    // north-west corner, north-east corner, south-west corner, south-east corner
    // Horizontal line, vertical line, T-shape, upside-down T-shape, arrow up, arrow down
    // NWC  NEC  SWC  SEC  HOR  VER   T   UDT  AUP  ADN
    //  |"  "|    |_   _|   -    |   "|"  _|_   ^    v
    ($DA, $BF, $C0, $D9, $C4, $B3, $C2, $C1, $18, $19), // inactive
    ($C9, $BB, $C8, $BC, $CD, $BA, $CB, $CA, $18, $19)  // active
    );

  // pure UTF-7 version
  charsetutf7: charset =
    (
    // ('.', '.', '`', '''','-', '|', '-', '-', '^', 'v')
    ($2E, $2E, $60, $27, $2D, $7C, $2D, $2D, $5E, $76), // inactive
    // ('+', '+', '+', '+', '=', '#', '=', '=', '^', 'v')
    ($2B, $2B, $2B, $2B, $3D, $23, $3D, $3D, $5E, $76)  // active
    );

  // which frame character set to use
  framechar: ^charset = @charsetdos;


  //color scheme
  // Selected item on active level
  activeitemcolor: byte = Yellow;
  activeitembgcolor: byte = LightRed;
  // nonselected item on active level
  activelevcolor: byte = white;
  activelevbgcolor: byte = Blue;
  // Selected item on inactive level
  inactiveitemcolor: byte = White;
  inactiveitembgcolor: byte = Black;
  // nonselected item on inactive level
  // these serve as general layout colors
  inactivelevcolor: byte = LightGray;
  inactivelevbgcolor: byte = Blue;

  // == LAYOUT CONTROL ==
  // Coordinates of layout frames
  selectBoundaries: array[1..5] of coords =
    (
    (3, 4, 25, 6), // level 1 list
    (3, 9, 25, 23), // level 2 list
    (30, 4, 54, 23), // level 3 list
    (17, 12, 39, 17), // level 4 list
    (17, 12, 39, 17)  // level 5 list
    );

  // Selection box length for each level
  selectionLengths: array[1..5] of integer = (23, 23, 25, 33, 33);

  // WARNING, NOT all of these are coordinates:
  // they are upper left corner X and Y, width, and height
  drawBoundaries: array[1..5] of coords =
    (
    (1, 3, 27, 5), // level 1 frame
    (1, 8, 27, 17), // level 2 frame
    (28, 3, 53, 22), // level 3 frame
    (15, 5, 52, 15), // level 4 frame
    (15, 5, 52, 15)  // level 5 frame
    );
  secondcolumn: array[1..5] of integer = (-1, -1, 29, -1, -1);
  secondcolumnSelection: integer = 21;
  // == END OF LAYOUT CONTROL ==

  // == LISTS CONTROL ==

  // Option lists for units/structs
  // Size needs to be adjusted with typeid:-1 values to correspond to the lev3Lst type.
  movementtypeOptsList: lev3Lst =
    (
    (typeid: 03; Text: 'Name string reference  '; helpid: 03),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00)
    );

  unitOptsList: lev3Lst =
    (
    (typeid: 02; Text: 'Short name ID          '; helpid: 02),
    (typeid: 03; Text: 'Name string reference  '; helpid: 03),
    (typeid: 02; Text: 'Long name ID           '; helpid: 02),
    (typeid: 03; Text: 'WSA filename string ref'; helpid: 03),
    (typeid: 12; Text: 'Generate shadow        '; helpid: 00),
    (typeid: 13; Text: 'Is factory            *'; helpid: 37),
    (typeid: 14; Text: '(Unused bit flag 03)   '; helpid: 00),
    (typeid: 15; Text: 'No concrete needed    *'; helpid: 37),
    (typeid: 16; Text: 'Can receive airdrops  *'; helpid: 37),
    (typeid: 17; Text: 'Worm camouflage        '; helpid: 00),
    (typeid: 18; Text: 'Has a rotating turret  '; helpid: 00),
    (typeid: 19; Text: 'Can be captured       *'; helpid: 37),
    (typeid: 12; Text: 'Pick up for repairs    '; helpid: 00),
    (typeid: 13; Text: 'No death message       '; helpid: 00),
    (typeid: 14; Text: 'TAB selects this       '; helpid: 00),
    (typeid: 15; Text: 'Full CPU outside screen'; helpid: 19),
    (typeid: 16; Text: 'Can target air units   '; helpid: 00),
    (typeid: 17; Text: 'Is a priority target   '; helpid: 32),
    (typeid: 18; Text: '(Unused bit flag 07)   '; helpid: 00),
    (typeid: 19; Text: '(Unused bit flag 08)   '; helpid: 00),
    (typeid: 20; Text: 'Infantry spawn chance  '; helpid: 13),
    (typeid: 02; Text: 'Hit points             '; helpid: 00),
    (typeid: 02; Text: 'Sight                  '; helpid: 00),
    (typeid: 24; Text: 'Sidebar icon           '; helpid: 24),
    (typeid: 02; Text: 'Cost                   '; helpid: 00),
    (typeid: 02; Text: 'Build Time             '; helpid: 00),
    (typeid: 02; Text: 'Tech level             '; helpid: 00),
    (typeid: 10; Text: 'Prerequisites          '; helpid: 10),
    (typeid: 01; Text: 'Build icon order       '; helpid: 17),
    (typeid: 01; Text: 'Upgrades needed        '; helpid: 00),
    (typeid: 05; Text: 'Sidebar command #1     '; helpid: 05),
    (typeid: 05; Text: 'Sidebar command #2     '; helpid: 05),
    (typeid: 05; Text: 'Sidebar command #3     '; helpid: 05),
    (typeid: 05; Text: 'Sidebar command #4     '; helpid: 05),
    (typeid: 01; Text: 'CHOAM status (unusable)'; helpid: 39),
    (typeid: 02; Text: 'Hint string ID         '; helpid: 02),
    (typeid: 02; Text: 'AI build priority      '; helpid: 00),
    (typeid: 02; Text: 'Threat level for AI    '; helpid: 33),
    (typeid: 04; Text: 'Owner                  '; helpid: 04),
    // end of shared data
    (typeid: 02; Text: 'Unit array range min   '; helpid: 34),
    (typeid: 02; Text: 'Unit array range max   '; helpid: 34),
    (typeid: 12; Text: '(Unused bit flag 01)   '; helpid: -1),
    (typeid: 13; Text: 'Is a bullet            '; helpid: 00),
    (typeid: 14; Text: 'Explode when dying     '; helpid: 00),
    (typeid: 15; Text: 'Sonic Immunity         '; helpid: 00),
    (typeid: 16; Text: 'Bumpy Movement         '; helpid: 00),
    (typeid: 17; Text: 'Tracked crushing       '; helpid: 00),
    (typeid: 18; Text: 'Ground unit            '; helpid: 00),
    (typeid: 19; Text: 'Stay on map (air units)'; helpid: 00),
    (typeid: 12; Text: '(Unused bit flag 01)   '; helpid: -1),
    (typeid: 13; Text: '(Unused bit flag 02)   '; helpid: -1),
    (typeid: 14; Text: 'Fires Twice            '; helpid: 00),
    // only works if "Explode on target"=true and if "weapon ROF"=0
    (typeid: 15; Text: 'Sand impact (bullet)   '; helpid: 18),
    (typeid: 16; Text: 'Deviation immunity     '; helpid: 00),
    (typeid: 17; Text: 'Has two anims (bullet) '; helpid: 00),
    (typeid: 18; Text: 'Is inaccurate (bullet) '; helpid: 00),
    (typeid: 19; Text: 'Is a normal unit       '; helpid: 00),
    (typeid: 02; Text: 'Sprite dimensions      '; helpid: 00),
    // related to AI building more or less of this unit? Seems completely removed in Opendune, despite having a value.
    (typeid: 02; Text: 'Unknown 031 (unused?)  '; helpid: -1),
    (typeid: 06; Text: 'Movement type          '; helpid: 06),
    (typeid: 02; Text: 'Move animation speed   '; helpid: 00),
    (typeid: 02; Text: 'Speed                  '; helpid: 00),
    (typeid: 02; Text: 'Turning speed          '; helpid: 00),
    (typeid: 24; Text: 'Unit graphics          '; helpid: 24),
    (typeid: 24; Text: 'Turret graphics        '; helpid: 24),
    (typeid: 05; Text: 'Default AI command     '; helpid: 00),
    (typeid: 25; Text: 'Unit display mode      '; helpid: 25),
    (typeid: 24; Text: 'Death animation        '; helpid: 16),
    (typeid: 02; Text: 'Weapon rate of fire    '; helpid: 00),
    (typeid: 02; Text: 'Weapon range           '; helpid: 00),
    (typeid: 02; Text: 'Weapon damage          '; helpid: 00),
    // see segra's unit scripts explanation
    (typeid: 02; Text: 'Special animation      '; helpid: 35),
    (typeid: 07; Text: 'Weapon type            '; helpid: 07),
    (typeid: 11; Text: 'Weapon sound           '; helpid: 11),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00)
    );

  structOptsList: lev3Lst =
    (
    (typeid: 02; Text: 'Short name ID          '; helpid: 02),
    (typeid: 03; Text: 'Name string reference  '; helpid: 03),
    (typeid: 02; Text: 'Long name ID           '; helpid: 02),
    (typeid: 03; Text: 'WSA filename string ref'; helpid: 03),
    (typeid: 12; Text: 'Generate shadow       *'; helpid: 38),
    (typeid: 13; Text: 'Is factory             '; helpid: 00),
    (typeid: 14; Text: '(Unused bit flag 03)   '; helpid: 00),
    (typeid: 15; Text: 'No concrete needed     '; helpid: 00),
    (typeid: 16; Text: 'Can receive airdrops   '; helpid: 00),
    (typeid: 17; Text: 'Worm camouflage       *'; helpid: 38),
    (typeid: 18; Text: 'Has a rotating turret *'; helpid: 38),
    (typeid: 19; Text: 'Can be captured        '; helpid: 00),
    (typeid: 12; Text: 'Pick up for repairs   *'; helpid: 38),
    (typeid: 13; Text: 'No death message      *'; helpid: 38),
    (typeid: 14; Text: 'TAB selects this      *'; helpid: 38),
    (typeid: 15; Text: 'Full CPU outside scrn *'; helpid: 38),
    (typeid: 16; Text: 'Can target air units  *'; helpid: 38),
    (typeid: 17; Text: 'Is a priority target  *'; helpid: 38),
    (typeid: 18; Text: '(Unused bit flag 07)   '; helpid: 00),
    (typeid: 19; Text: '(Unused bit flag 08)   '; helpid: 00),
    (typeid: 20; Text: 'Inf spawn chance / cell'; helpid: 13),
    (typeid: 02; Text: 'Hit points             '; helpid: 00),
    (typeid: 02; Text: 'Sight                  '; helpid: 00),
    (typeid: 24; Text: 'Sidebar icon           '; helpid: 24),
    (typeid: 02; Text: 'Cost                   '; helpid: 00),
    (typeid: 02; Text: 'Build Time             '; helpid: 00),
    (typeid: 02; Text: 'Tech level             '; helpid: 00),
    (typeid: 10; Text: 'Prerequisites          '; helpid: 10),
    (typeid: 01; Text: 'Build icon order       '; helpid: 17),
    (typeid: 01; Text: 'CY upgrades needed     '; helpid: 00),
    (typeid: 05; Text: 'Sidebar command #1    *'; helpid: 38),
    (typeid: 05; Text: 'Sidebar command #2    *'; helpid: 38),
    (typeid: 05; Text: 'Sidebar command #3    *'; helpid: 38),
    (typeid: 05; Text: 'Sidebar command #4    *'; helpid: 38),
    (typeid: 01; Text: 'CHOAM status          *'; helpid: 38),
    (typeid: 02; Text: 'Hint string ID         '; helpid: 02),
    (typeid: 02; Text: 'AI build priority      '; helpid: 00),
    (typeid: 02; Text: 'Threat level for AI    '; helpid: 33),
    (typeid: 04; Text: 'Owner                  '; helpid: 04),
    // end of shared data
    (typeid: 30; Text: 'Units allowed to enter '; helpid: 00),
    (typeid: 02; Text: 'Spice storage          '; helpid: 00),
    (typeid: 02; Text: 'Power consumed         '; helpid: 00),
    (typeid: 08; Text: 'Foundation size        '; helpid: 08),
    (typeid: 32; Text: 'Structure graphics ID  '; helpid: 36),
    (typeid: 31; Text: 'Anim: Normal           '; helpid: 31),
    (typeid: 31; Text: 'Anim: Production busy  '; helpid: 31),
    (typeid: 31; Text: 'Anim: Production ended '; helpid: 31),
    (typeid: 07; Text: 'Construction optn #1   '; helpid: 12),
    (typeid: 07; Text: 'Construction optn #2   '; helpid: 12),
    (typeid: 07; Text: 'Construction optn #3   '; helpid: 12),
    (typeid: 07; Text: 'Construction optn #4   '; helpid: 12),
    (typeid: 07; Text: 'Construction optn #5   '; helpid: 12),
    (typeid: 07; Text: 'Construction optn #6   '; helpid: 12),
    (typeid: 07; Text: 'Construction optn #7   '; helpid: 12),
    (typeid: 07; Text: 'Construction optn #8   '; helpid: 12),
    (typeid: 02; Text: '1st upgrade tech level '; helpid: 15),
    (typeid: 02; Text: '2nd upgrade tech level '; helpid: 15),
    (typeid: 02; Text: '3rd upgrade tech level '; helpid: 15),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00)
    );

  houseOptsList: lev3Lst =
    (
    (typeid: 03; Text: 'Name string reference  '; helpid: 03),
    (typeid: 20; Text: 'Weakness to deviation  '; helpid: 20),
    (typeid: 02; Text: 'LemonFactor (?)        '; helpid: 00),
    (typeid: 09; Text: 'Decay factor           '; helpid: 09),
    (typeid: 02; Text: 'Radar color index      '; helpid: 14),
    (typeid: 02; Text: 'Palace recharge time   '; helpid: 00),
    (typeid: 02; Text: 'Frigate delay time     '; helpid: 00),
    (typeid: 22; Text: 'Voice prefix letter    '; helpid: 22),
    (typeid: 23; Text: 'Palace special ability '; helpid: 23),
    (typeid: 21; Text: 'Win music              '; helpid: 21),
    (typeid: 21; Text: 'Lose music             '; helpid: 21),
    (typeid: 21; Text: 'Mentat music           '; helpid: 21),
    (typeid: 03; Text: 'House voice reference  '; helpid: 03),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00)
    );

  actionOptsList: lev3Lst =
    (
    (typeid: 02; Text: 'Name ID                '; helpid: 02),
    (typeid: 03; Text: 'Name string reference  '; helpid: 03),
    (typeid: 26; Text: 'Action interrupt mode  '; helpid: 26),
    (typeid: 27; Text: 'Sidebar mode           '; helpid: 27),
    (typeid: 28; Text: 'Response sound         '; helpid: 28),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00)
    );

  fileOptsList: lev3Lst =
    (
    (typeid: 03; Text: 'Filename string ref.   '; helpid: 03),
    (typeid: 00; Text: 'File size              '; helpid: 00),
    (typeid: 00; Text: 'Buffer ptr   (unusable)'; helpid: 39),
    (typeid: 00; Text: 'Fileread ptr (unusable)'; helpid: 39),
    (typeid: 29; Text: 'Parent file            '; helpid: 29),
    (typeid: 01; Text: 'Unknown 011            '; helpid: -1),
    (typeid: 12; Text: 'Is mapped in memory    '; helpid: 00),
    (typeid: 13; Text: 'Is loaded in XMS memory'; helpid: 00),
    (typeid: 14; Text: '(Unused bit flag 03)   '; helpid: 00),
    (typeid: 15; Text: '(Unused bit flag 04)   '; helpid: 00),
    (typeid: 16; Text: 'Can be loaded from PAK '; helpid: 00),
    (typeid: 17; Text: '(Unused bit flag 06)   '; helpid: 00),
    (typeid: 18; Text: '(Unused bit flag 07)   '; helpid: 00),
    (typeid: 19; Text: '(Unused bit flag 08)   '; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00)
    );

  terrainOptsList: lev3Lst =
    (
    (typeid: 02; Text: 'Unused 01              '; helpid: 00),
    (typeid: 02; Text: 'Unused 02              '; helpid: 00),
    (typeid: 35; Text: 'Moving speed: Foot     '; helpid: 00),
    (typeid: 35; Text: 'Moving speed: Tracked  '; helpid: 00),
    (typeid: 35; Text: 'Moving speed: Harvester'; helpid: 00),
    (typeid: 35; Text: 'Moving speed: Wheeled  '; helpid: 00),
    (typeid: 35; Text: 'Moving speed: Winged   '; helpid: 00),
    (typeid: 35; Text: 'Moving speed: Slither  '; helpid: 00),
    (typeid: 34; Text: 'Let unit wobble        '; helpid: 00),
    (typeid: 34; Text: 'Valid for structure 1  '; helpid: 00),
    (typeid: 34; Text: 'is sand                '; helpid: 00),
    (typeid: 34; Text: 'Valid for structure 2  '; helpid: 00),
    (typeid: 02; Text: 'Spice value            '; helpid: 40),
    (typeid: 36; Text: 'Crater type            '; helpid: 00),
    (typeid: 02; Text: 'Radar colour           '; helpid: 00),
    (typeid: 02; Text: 'Sprite ID              '; helpid: 00),
    (typeid: 02; Text: 'Unused 13              '; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00)
    );

  terrainNames: customNames =
    (
    'Sand       ',
    'Sand Edge  ',
    'Dune       ',
    'Dune Edge  ',
    'Rock       ',
    'Rock Edge  ',
    'Hill       ',
    'Hill Edge  ',
    'Spice      ',
    'Thick Spice',
    'Concrete   ',
    'Wall       ',
    'Structure  ',
    'Rubble     ',
    'Spice Bloom',
    '', '', '', '', '', '', '', '', '', '',
    '', '', '', '', '', '', '', '', '', '',
    '', '', ''
    );

  voiceOptsList: lev3Lst =
    (
    (typeid: 03; Text: 'Filename string ref.   '; helpid: 03),
    (typeid: 02; Text: 'Sound priority         '; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00)
    );

  musicOptsList: lev3Lst =
    (
    (typeid: 03; Text: 'Filename string ref.   '; helpid: 03),
    (typeid: 02; Text: 'Track in this file     '; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00), (typeid: -1; Text: ''; helpid: 00),
    (typeid: -1; Text: ''; helpid: 00)
    );

  musicNames: customNames =
    (
    {NULL.0}   'None (0)',
    {dune1.2}  'Bonus',
    {dune1.3}  'Lose Ordos',
    {dune1.4}  'Lose Harkonnen',
    {dune1.5}  'Lose Atreides',
    {dune17.4} 'Win Ordos',
    {dune8.3}  'Win Harkonnen',
    {dune8.2}  'Win Atreides',
    {dune1.6}  'Idle 1',
    {dune2.6}  'Idle 2',
    {dune3.6}  'Idle 3',
    {dune4.6}  'Idle 4',
    {dune5.6}  'Idle 5',
    {dune6.6}  'Idle 6',
    {dune9.4}  'Idle 7',
    {dune9.5}  'Idle 8',
    {dune18.6} 'Idle 9',
    {dune10.7} 'Attack 1',
    {dune11.7} 'Attack 2',
    {dune12.7} 'Attack 3',
    {dune13.7} 'Attack 4',
    {dune14.7} 'Attack 5',
    {dune15.7} 'Attack 6',
    {dune1.8}  '',
    {dune7.2}  'Brief Harkonnen',
    {dune7.3}  'Brief Atreides',
    {dune7.4}  'Brief Ordos',
    {dune0.2}  'Intro',
    {dune7.6}  'Menu',
    {dune16.7} 'Conquest',
    {dune19.4} 'Finale Harkonnen',
    {dune19.2} 'Finale Atreides',
    {dune19.3} 'Finale Ordos',
    {dune20.2} 'Credits',
    {dune16.8} 'Cutscene',
    {dune0.3}  '',
    {dune0.4}  'Logos',
    {dune0.5}  ''
    );

  musicNamesDemo: customNames =
    (
    {NULL.0}   'None (0)',
    {dune1.6}  'Idle 1',
    {dune3.6}  'Idle 3',
    {dune12.7} 'Attack 3',
    {dune0.2}  'Intro',
    {dune0.3}  '',
    {dune0.4}  'Logos',
    {dune0.5}  '',
    '', '', '', '', '', '', '', '', '', '', '',
    '', '', '', '', '', '', '', '', '', '', '',
    '', '', '', '', '', '', '', ''
    );

  // List of main data types to edit. Its size is defined in
  // the type definition of 'listTypes' in the top section of the file.
  // Types can be put on negative indexes in the array to hide them from
  // the main list and use them purely as data list.
  mainlist: editableTypes =
    (
    (
    typeName: 'Movement'; // Not shown because MoveUp doesn't go below index 0 ;)
    optsList: @movementtypeOptsList;
    refOption: 0;
    refArray: nil;
    ),
    (
    typeName: 'Units';
    optsList: @unitOptsList;
    refOption: 1;
    refArray: nil;
    ),
    (
    typeName: 'Structures';
    optsList: @structOptsList;
    refOption: 1;
    refArray: nil;
    ),
    (
    typeName: 'Houses';
    optsList: @houseOptsList;
    refOption: 0;
    refArray: nil;
    ),
    (
    typeName: 'Terrain';
    optsList: @terrainOptsList;
    refOption: -1;
    refArray: @terrainNames;
    ),
    (
    typeName: 'Actions';
    optsList: @actionOptsList;
    refOption: 1;
    refArray: nil;
    ),
    (
    typeName: 'Voices';
    optsList: @voiceOptsList;
    refOption: 0;
    refArray: nil;
    ),
    (
    typeName: 'Music';
    optsList: @musicOptsList;
    refOption: -1;
    refArray: @musicNames;
    ),
    (
    typeName: 'Files';
    optsList: @fileOptsList;
    refOption: 0;
    refArray: nil;
    )
    );



  optHelp: array[-1..41] of bigdescr =
    (
    //     [________________________________________________] = max length for each line
    {-1} ('', '--==UNKNOWN OPTION==--', '',
    'If you find out what this does, please tell me!', ' ', '', '', ''),
    {00} ('[No help for this item]', '', '', '', '', '', '', ''), // keep empty as default value
    {01} ('', '', '', '', '', '', '', ''),
    {02} ('The NAME ID is a value that refers to an index',
    'in the game''s strings file (DUNE.ENG/GER/FRE).', '',
    'If you want to change them, you have to look', 'them up in the strings file.', '', '', ''),
    {03} ('STRING REFERENCES are internal references which',
    'point to a zero-terminated string. The editor ',
    'converts these values to normal file addresses.',
    '', 'Be VERY careful when editing these; changing',
    'name references can have disastrous results.', 'Both this editor and the game rely on these.', ''),
    {04} ('The OWNERSHIP value is a list of switches that',
    'allow or deny specific Houses the right to build', 'this specific unit or structure.',
    '', '', '', '', ''),
    {05} ('The SIDEBAR COMMANDS allow players to give', 'instructions to units.',
    'After a move or attack command is completed, the', 'fourth command is automatically executed.',
    '', 'Note that the Area Guard command automatically',
    'executes ''Retreat'' after attacking something.', ''),
    {06} ('The MOVEMENT TYPE determines a lot of the unit''s',
    'behaviour on the battlefield. Each movement type',
    'has its own unique characteristics, like how ',
    'units on foot can be crushed by tracked units,', 'and slithering units can''t move on rock.',
    '', '', ''),
    {07} ('The WEAPON TYPE is the projectile the unit will',
    'fire. Since all projectiles are units, all units',
    'appear in the selection list. However, using',
    'normal units as projectiles doesn''t work, since',
    'units need to have a certain set of options',
    'enabled to be seen as a weapon type.', '', ''),
    {08} ('The FOUNDATION determines the amount of cells a',
    'structure takes, and which shape they are in.', '',
    'Note that changing the foundations of the', 'Concrete Slabs can give very unexpected results.',
    '', '', ''),
    {09} ('This is a ''weakness factor'' that indicates how',
    'much damage structures of this house take every', 'time the decay strikes.',
    '', 'Internally, the game adds 1 to this value. This',
    'means that ''0'' still does 1 damage, and ''-1''', 'disables the decay completely.', ''),
    {10} ('The PREREQUISITE structures are the structures a',
    'player needs to have before he can build this', 'unit or structure.', '', '', '', '', ''),
    {11} ('SOUNDS are played when a unit attacks or when a',
    'weapon is fired.', 'The sounds indicated with "VOC" have a .VOC file',
    'that is played. The rest only give generated', 'sounds.', '', '', ''),
    {12} ('Choosing a CONSTRUCTION OPTION gives a structure',
    'a unit to add to its production options list.', '', '', '', '', '', ''),
    {13} ('When something is destroyed, this percentage',
    'determines the chance that a soldier will spawn.',
    'For structures, this system is used for each',
    'separate cell occupied by the structure.', '',
    'The list only has rough values. You can use',
    '[F3] or [F4] for more detailed editing.', ''),
    {14} ('The index on the color palette that will be used',
    'as radar color for this House.', '', '', '', '', '', ''),
    {15} ('UPGRADE TECH LEVEL: Determines at which tech',
    'level this factory upgrade can first be done.', '',
    'Note that the tech level is not always the same',
    'as the mission number; the game often shifts',
    'the tech levels depending on which house you', 'are playing with.', ''),
    {16} ('The DEATH ANIMATION is a 3-frame animation that',
    'sometimes plays when a unit is destroyed.', '',
    'Choose the first frame of the animation you want',
    'to use, or choose index #000 (the mouse arrow)',
    'to disable the death animation.', '', ''),
    {17} ('The BUILD ICON ORDER value determines the place',
    'this construction option will have in the build', 'list of the structure that produces it.',
    'A higher value will put it higher in the list.', '',
    'The only exceptions are the Concrete Slabs,', 'which are always put on top for easy access.', ''),
    {18} ('This option causes the "Sand Impact" animation',
    'to play when the weapon reaches its destination,', 'and it is on sand.',
    'This option only works on unit types that act as',
    'a weapon: the "Weapon rate of fire" has to be 0,',
    'and the "Explode on target" option has to be',
    'enabled.', ''),
    {19} ('This option forces the program to go through',
    'the full number of script cycles for this unit,', 'even if the unit is outside of the user''s',
    'screen area.', 'Normally, units outside of the screen area only',
    'execute 1/10th of the script cycles, to save', 'CPU cycles.', ''),
    {20} ('The chance that a unit of this House will switch',
    'sides when attacked by a Deviator.', '', '', 'The list only has rough values. You can use',
    '[F3] or [F4] for more detailed editing.', '', ''),
    {21} ('The MUSIC tracks are the MIDI tracks in the', 'game.', '', '', '', '', '', ''),
    {22} ('The LETTER to put before the mentat voice',
    'filenames to get the voices for this House.', '', '', '', '', '', ''),
    {23} ('The PALACE SPECIAL ABILITY determines which',
    'special weapon a Commander gets by building a', 'House Palace', '', '', '', '', ''),
    {24} ('GRAPHICS are all SHP graphics used by the game.',
    'They are read from MOUSE.SHP, BTTN.ENG/GER/FRE,', 'SHAPES.SHP, UNITS2.SHP, UNITS1.SHP and',
    'UNITS.SHP (in that order).', '', 'Note that value 0 (normally the mouse arrow) is',
    'sometimes used as "None" value.', ''),
    {25} ('UNIT GRAPHICS DISPLAY MODES determine how the',
    'game draws this unit''s graphics. It determines',
    'how many frames are shown for each direction.',
    '', '', '', '', ''),
    {26} ('ACTION INTERRUPT MODE. Determines how this',
    'action is executed. It can either wait for the',
    'previous action''s script to complete its current',
    'cycle before starting this new one, abort the',
    'current action immediately, or execute the new',
    'action as subroutine without aborting the', 'previous action.', ''),
    {27} ('SIDEBAR MODE. Determines what mode the sidebar',
    'will switch to to give this command.', '', '', '', '', '', ''),
    {28} ('The RESPONSE SOUND is a sound clip from VOC.PAK',
    'or one of the mentat PAK files.', 'These unit responses only work for infantry.',
    '', '', '', '', ''),
    {29} ('The PARENT FILE determines in which PAK archive',
    'the game is going to look for this file.', '',
    'This option is obviously ignored for PAK files,',
    'for which it is put to ''0'', making it show the', 'first file in the list.', '', ''),
    {30} ('The FILE TYPE is an indicator of how the game', 'handles this file.',
    '', '', '', '', '', ''),
    {31} ('ANIMATION SCRIPT: This value is a reference to a',
    'script that tells the game how to play this',
    'structure''s animations. These scripts can''t be',
    'edited, but you can see them in a detailed list.', '', '', '', ''),
    {32} ('This option causes nearby units to automatically',
    'attack this one. Units on which this option is',
    'disabled will not be attacked by other units',
    'automatically.', '', '', '', ''),
    {33} ('This option is used to rank units and structures',
    'in terms of importance, to make the AI select',
    'more dangerous/vital units or structures first,',
    'so it can attack or defend more effectively.', '', '', '', ''),
    {34} ('This minimum and maximum set boundaries on where',
    'the game is allowed to store units of this type', 'in its array of unit objects in memory.',
    '', 'These boundaries separate the units in classes',
    'like aircraft, ground units, weapons and special',
    'stuff like the sandworm, Frigate and Saboteur.', ''),
    {35} ('These include things like the worm swallow',
    'animation. The editor does not contain a list of', 'these yet.', '', '', '', '', ''),
    {36} ('This determines the index in ICON.MAP to use for',
    'this structure''s graphics.', '', 'The values in ICON.MAP determine which graphics',
    'from ICON.ICN to use for each frame of the', 'structure.', '', ''),
    //     [________________________________________________] = max length for each line
    {37} ('This option is shared by units and structures,',
    'but is only applicable to structures. Changing', 'it on units has no effect.',
    '', '', '', '', ''),
    {38} ('This option is shared by units and structures,',
    'but is only applicable to units. Changing it on', 'structures has no effect.',
    '', '', '', '', ''),
    {39} ('This value is part of the hardcoded data, but is',
    'changed during the course of the game, so', 'editing it has no effect.', '', '', '', '', ''),
    {40} ('This determines how much spice is harvested from',
    'this terrain. Setting it to ''-1'' means it can be',
    'transformed to spice by spice blooms or', 'destroyed harvesters', '', '', '', ''),
    {41} ('', '', '', '', '', '', '', '')
    );

  // == END OF LISTS CONTROL ==


  // == TYPES CONTROL ==

  // DATA TYPE LIST - Editing description for each data type
  typedescr: array[0..36] of descr =
    (
    {00} ('Quadruple byte                      ',
    '(-2147483648,2147483647)            '),
    {01} ('Single byte (-128,127)              ', ''),
    {02} ('Double byte (-32768,32767)          ', ''),
    {03} ('Reference to 0-terminated string.   ',
    'Give decimal offset in the EXE.     '),
    {04} ('Ownership. Press space to select or ',
    'deselect a side.                    '),
    {05} ('Unit command. Select one from the   ',
    'list.                               '),
    {06} ('Movement type.  Select one from the ',
    'list.                               '),
    {07} ('Unit. Select one from the list.     ', ''),
    {08} ('Foundation dimensions. Select one   ',
    'from the list.                      '),
    {09} ('Decay factor. Indicates decay damage',
    'dealt to structures of this house.  '),
    {10} ('Prerquisite structures. Press space ',
    'to select or deselect a structure.  '),
    {11} ('Weapon Sounds. Select one from the  ',
    'list.                               '),
    {12} ('Boolean (Yes/No) - bit 1            ', ''),
    {13} ('Boolean (Yes/No) - bit 2            ', ''),
    {14} ('Boolean (Yes/No) - bit 3            ', ''),
    {15} ('Boolean (Yes/No) - bit 4            ', ''),
    {16} ('Boolean (Yes/No) - bit 5            ', ''),
    {17} ('Boolean (Yes/No) - bit 6            ', ''),
    {18} ('Boolean (Yes/No) - bit 7            ', ''),
    {19} ('Boolean (Yes/No) - bit 8            ', ''),
    {20} ('Percentage. Select a value from the ',
    'list or use [F5] for full editing.  '),
    {21} ('Music. Select a track from the list.', ''),
    {22} ('Character. Type a character and     ',
    'press [ENTER] to confirm.           '),
    {23} ('Palace Special Ability for this     ',
    'House. Select one from the list.    '),
    {24} ('Graphics. Select one from the list. ',
    '000 is sometimes used as "None" too.'),
    {25} ('Graphics display mode. Determines   ',
    'how this unit''s frames are shown.   '),
    {26} ('Action interrupt mode. Determines   ',
    'how this action is executed.        '),
    {27} ('Sidebar mode. Determines which      ',
    'sidebar to use to give this command.'),
    {28} ('Sound clip. These are SB sound      ',
    'files.                              '),
    {29} ('Parent PAK file. The .PAK archive   ',
    'in which this file needs to be.     '),
    {30} ('Allowed units. Press space to select',
    'or deselect a unit.                 '),
    {31} ('Animation scripts can''t be edited,  ',
    'but you can look at the actions.    '),
    {32} ('Structure graphics ID. Represents   ',
    'an index in the ICON.MAP file.      '),
    {33} ('Single byte (0,255)                 ', ''),
    {34} ('Boolean (Yes/No) as two bytes       ', ''),
    {35} ('Percentage. Select a value from the ',
    'list or use [F4] for full editing.  '),
    {36} ('Crater type that appears when a     ', 'missile explodes on this terrain.   ')
    );


  // Strings for specific data types

  booleans: array[0..1] of values =
    (
    (val: 0; txt: 'No'),
    (val: 1; txt: 'Yes')
    );

  foundations: array[0..6] of values =
    (
    (val: 0; txt: '1x1'),
    (val: 1; txt: '2x1'),
    (val: 2; txt: '1x2'),
    (val: 3; txt: '2x2'),
    (val: 4; txt: '2x3'),
    (val: 5; txt: '3x2'),
    (val: 6; txt: '3x3')
    );

  palaceSpecials: array[0..3] of values =
    (
    (val: 0; txt: 'None'),
    (val: 1; txt: 'Death Hand'),
    (val: 2; txt: 'Fremen'),
    (val: 3; txt: 'Saboteur')
    );

  displayModes: array[0..5] of values =
    (
    (val: 0; txt: 'Use single frame'),
    (val: 1; txt: 'Normal (unit)'),
    (val: 2; txt: 'Normal (weapon)'),
    (val: 3; txt: 'Normal frames * 3'),
    (val: 4; txt: 'Normal frames * 4'),
    (val: 5; txt: '''Thopter special')
    );

  interruptModes: array[0..2] of values =
    (
    (val: 0; txt: 'Finish action cycle'),
    (val: 1; txt: 'Interrupt action'),
    (val: 2; txt: 'Subroutine action')
    );

  sidebarModes: array[0..3] of values =
    (
    (val: 1; txt: 'Select target    '),
    (val: 2; txt: 'Place structure   (CRASHES)'),
    (val: 3; txt: 'Keep commands bar'),
    (val: 4; txt: 'Clear sidebar    ')
    );

  structureGraphics: array[0..25] of values =
    (
    (val: 1; txt: '01: Rock craters '),
    (val: 2; txt: '02: Sand craters '),
    (val: 3; txt: '03: Debris       '),
    (val: 4; txt: '04: Dead bodies  '),
    (val: 5; txt: '05: Sand tracks  '),
    (val: 6; txt: '06: Wall         '),
    (val: 7; txt: '07: Fog-of-war   '),
    (val: 8; txt: '08: Concrete slab'),
    (val: 9; txt: '09: landscape    '),
    (val: 10; txt: '10: Spice blooms '),
    (val: 11; txt: '11: House Palace '),
    (val: 12; txt: '12: Light Factory'),
    (val: 13; txt: '13: Heavy Factory'),
    (val: 14; txt: '14: Hi-Tech      '),
    (val: 15; txt: '15: IX           '),
    (val: 16; txt: '16: WOR          '),
    (val: 17; txt: '17: Constr. Yard '),
    (val: 18; txt: '18: Barracks     '),
    (val: 19; txt: '19: Windtrap     '),
    (val: 20; txt: '20: Starport     '),
    (val: 21; txt: '21: Refinery     '),
    (val: 22; txt: '22: Repair       '),
    (val: 23; txt: '23: Gun Turret   '),
    (val: 24; txt: '24: Rocket Turret'),
    (val: 25; txt: '25: Silo         '),
    (val: 26; txt: '26: Radar Outpost')
    );

  sounds: array[0..35] of values =
    (
    (val: -1; txt: 'None                       '),
    (val: 0; txt: 'Not Applicable             '),
    (val: 20; txt: 'Place down                 '),
    (val: 22; txt: 'Twinkling                  '),
    (val: 23; txt: 'Intro chord                '),
    (val: 24; txt: 'Carryall dropoff           '),
    (val: 30; txt: 'Infantry kill 1       (VOC)'),
    (val: 35; txt: 'Infantry kill 2       (VOC)'),
    (val: 33; txt: 'Infantry kill 3       (VOC)'),
    (val: 34; txt: 'Infantry kill 4       (VOC)'),
    (val: 31; txt: 'Infantry kill 5       (VOC)'),
    (val: 36; txt: 'Infantry crush        (VOC)'),
    (val: 38; txt: 'Click                 (VOC)'),
    (val: 39; txt: 'Missile               (VOC)'),
    (val: 40; txt: 'Hit sand              (VOC)'),
    (val: 41; txt: 'Tank shot             (VOC)'),
    (val: 42; txt: 'Rocket                (VOC)'),
    (val: 43; txt: 'Sonic wave                 '),
    (val: 44; txt: 'Structure crumble     (VOC)'),
    (val: 46; txt: 'Death Hand target          '),
    (val: 47; txt: '"Cannot Place" beep        '),
    (val: 49; txt: 'Tank shot             (VOC)'),
    (val: 55; txt: 'Tank shot             (VOC)'),
    (val: 51; txt: 'Exploding unit        (VOC)'),
    (val: 52; txt: 'Money increase             '),
    (val: 53; txt: 'Money decrease             '),
    (val: 54; txt: 'Weapon hit                 '),
    (val: 56; txt: 'Tank shot             (VOC)'),
    (val: 57; txt: 'Tank shot             (VOC)'),
    (val: 58; txt: 'Single gun            (VOC)'),
    (val: 59; txt: 'Machinegun            (VOC)'),
    (val: 60; txt: '"Constr. Complete"    (VOC)'),
    (val: 61; txt: 'Plingplong                 '),
    (val: 62; txt: 'Radar static          (VOC)'),
    (val: 63; txt: 'Worm attack           (VOC)'),
    (val: 64; txt: 'Mini Rocket           (VOC)')
    );

  percentages: array[0..16] of values =
    (
    (val: $00; txt: '  0.00 %'),
    (val: $10; txt: '  6.25 %'),
    (val: $20; txt: ' 12.50 %'),
    (val: $30; txt: ' 18.75 %'),
    (val: $40; txt: ' 25.00 %'),
    (val: $50; txt: ' 31.25 %'),
    (val: $60; txt: ' 37.50 %'),
    (val: $70; txt: ' 43.75 %'),
    (val: $80; txt: ' 50.00 %'),
    (val: $90; txt: ' 56.25 %'),
    (val: $A0; txt: ' 62.50 %'),
    (val: $B0; txt: ' 68.75 %'),
    (val: $C0; txt: ' 75.00 %'),
    (val: $D0; txt: ' 81.25 %'),
    (val: $E0; txt: ' 87.50 %'),
    (val: $F0; txt: ' 93.75 %'),
    (val: $100; txt: '100.00 %')
    );

  graphicsHi: graphicsSet =
    (
    //MOUSE.SHP
    {000} 'Mouse: arrow       ',
    // 000 - 000 is also used as "blank" sometimes.
    {001} 'Mouse: Scroll up   ', // 001
    {002} 'Mouse: Scroll left ', // 002
    {003} 'Mouse: Scroll down ', // 003
    {004} 'Mouse: Scroll right', // 004
    {005} 'Mouse: Target      ', // 005
    {006} 'Selection indicator', // 006
    // BTTN.SHP
    {007} 'MENTAT button      ', // 000
    {008} 'MENTAT btn (pushed)', // 001
    {009} 'OPTIONS button     ', // 002
    {010} 'OPTIONS btn(pushed)', // 003
    {011} '"Credits" text     ', // 004
    //SHAPES.SHP
    {012} 'Money Bar          ', // 000
    {013} 'Counter: empty     ', // 001
    {014} 'Counter: 0         ', // 002
    {015} 'Counter: 1         ', // 003
    {016} 'Counter: 2         ', // 004
    {017} 'Counter: 3         ', // 005
    {018} 'Counter: 4         ', // 006
    {019} 'Counter: 5         ', // 007
    {020} 'Counter: 6         ', // 008
    {021} 'Counter: 7         ', // 009
    {022} 'Counter: 8         ', // 010
    {023} 'Counter: 9         ', // 011
    {024} 'Foundation preview ', // 012
    {025} 'Option (unselected)', // 013
    {026} 'Option (selected)  ', // 014
    {027} 'Health indicators  ', // 015
    {028} 'Icon: Attack       ', // 016
    {039} 'Icon: Move         ', // 017
    {030} 'Icon: Death Hand   ', // 018
    {031} 'Red dot   2x2      ', // 019
    {032} 'Red dot   3x3      ', // 020
    {033} 'Blue dot  2x2      ', // 021
    {034} 'Blue dot  3x3      ', // 022
    {035} 'Green dot 2x2      ', // 023
    {036} 'Green dot 3x3      ', // 024
    {037} 'Sand dot 2x2       ', // 025
    {038} 'Sand dot 3x3       ', // 026
    {039} 'Concrete dot 2x2   ', // 027
    {040} 'Concrete dot 3x3   ', // 028
    {041} 'Spice dot 2x2      ', // 029
    {042} 'Spice dot 3x3      ', // 030
    {043} 'Dunes dot 2x2      ', // 031
    {044} 'Dunes dot 3x3      ', // 032
    {045} 'Rock dot 2x2       ', // 033
    {046} 'Rock dot 3x3       ', // 034
    {047} 'Dark concr dot 2x2 ', // 035
    {048} 'Dark concr dot 3x3 ', // 036
    {049} 'Black dot 2x2      ', // 037
    {040} 'Black dot 3x3      ', // 038
    {051} 'Dark blue dot 2x2  ', // 039
    {052} 'Dark blue dot 3x3  ', // 040
    {053} 'brown dot 2x2      ', // 041
    {054} 'brown dot 3x3      ', // 042
    {055} 'White dot 2x2      ', // 043
    {056} 'White dot 3x3      ', // 044
    {057} 'Dark brown dot 2x2 ', // 045
    {058} 'Dark brown dot 3x3 ', // 046
    {059} 'Arrow up           ', // 047
    {060} 'Arrow up (pushed)  ', // 048
    {061} 'Arrow down         ', // 049
    {062} 'Arrow down (pushed)', // 050
    {063} 'Foundation grid    ', // 051
    {064} 'Black square       ', // 052
    {065} 'Icon: Concrete Slab', // 053
    {066} 'Icon: Palace       ', // 054
    {067} 'Icon: Light Factory', // 055
    {068} 'Icon: Heavy Factory', // 056
    {069} 'Icon: Hi-Tech      ', // 057
    {070} 'Icon: House of IX  ', // 058
    {071} 'Icon: WOR          ', // 059
    {072} 'Icon: Constr. Yard ', // 060
    {073} 'Icon: Wind Trap    ', // 061
    {074} 'Icon: Barracks     ', // 062
    {075} 'Icon: Starport     ', // 063
    {076} 'Icon: Refinery     ', // 064
    {077} 'Icon: Repair       ', // 065
    {078} 'Icon: Wall         ', // 066
    {079} 'Icon: Gun Turret   ', // 067
    {080} 'Icon: Rocket turret', // 068
    {081} 'Icon: Silo         ', // 069
    {082} 'Icon: Radar Outpost', // 070
    {083} 'Icon: Large Concr. ', // 071
    {084} 'Icon: Siege Tank   ', // 072
    {085} 'Icon: Rocket Lnchr.', // 073
    {086} 'Icon: Quad         ', // 074
    {087} 'Icon: Devastator   ', // 075
    {088} 'Icon: Trooper      ', // 076
    {089} 'Icon: Carryall     ', // 077
    {090} 'Icon: Combat Tank  ', // 078
    {091} 'Icon: Sonic Tank   ', // 079
    {092} 'Icon: Trike        ', // 080
    {093} 'Icon: Inf. Squad   ', // 081
    {094} 'Icon: Fremen Squad ', // 082
    {095} 'Icon: Sardaukar    ', // 083
    {096} 'Icon: Saboteur     ', // 084
    {097} 'Icon: Ornithopter  ', // 085
    {098} 'Icon: Deviator     ', // 086
    {099} 'Icon: Raider Trike ', // 087
    {100} 'Icon: Harvester    ', // 088
    {101} 'Icon: MCV          ', // 089
    {102} 'Icon: Soldier      ', // 090
    {103} 'Icon: Trooper Squad', // 091
    {104} 'Icon: Fremen       ', // 092
    {105} 'Icon: Sandworm     ', // 093
    {106} 'Icon: Spice        ', // 094
    {107} 'Icon: Sand         ', // 095
    {108} 'Icon: Dunes        ', // 096
    {109} 'Icon: Rock         ', // 097
    {110} 'Icon: Mountain     ', // 098
    //UNITS2.SHP
    {111} 'Small Tank body: N ', // 000
    {112} 'Small Tank body: NE', // 001
    {113} 'Small Tank body: E ', // 002
    {114} 'Small Tank body: SE', // 003
    {115} 'Small Tank body: S ', // 004
    {116} 'Small Tank turr: N ', // 005
    {117} 'Small Tank turr: NE', // 006
    {118} 'Small Tank turr: E ', // 007
    {119} 'Small Tank turr: SE', // 008
    {120} 'Small Tank turr: S ', // 009
    {121} 'Siege Tank body: N ', // 010
    {122} 'Siege Tank body: NE', // 011
    {123} 'Siege Tank body: E ', // 012
    {124} 'Siege Tank body: SE', // 013
    {125} 'Siege Tank body: S ', // 014
    {126} 'Siege Tank turr: N ', // 015
    {127} 'Siege Tank turr: NE', // 016
    {128} 'Siege Tank turr: E ', // 017
    {129} 'Siege Tank turr: SE', // 018
    {130} 'Siege Tank turr: S ', // 019
    {131} 'Devastator body: N ', // 020
    {132} 'Devastator body: NE', // 021
    {133} 'Devastator body: E ', // 022
    {134} 'Devastator body: SE', // 023
    {135} 'Devastator body: S ', // 024
    {136} 'Devastator turr: N ', // 025
    {137} 'Devastator turr: NE', // 026
    {138} 'Devastator turr: E ', // 027
    {139} 'Devastator turr: SE', // 028
    {140} 'Devastator turr: S ', // 029
    {141} 'Sonic tank turr: N ', // 030
    {142} 'Sonic tank turr: NE', // 031
    {143} 'Sonic tank turr: E ', // 032
    {144} 'Sonic tank turr: SE', // 033
    {145} 'Sonic tank turr: S ', // 034
    {146} 'R.Launcher turr: N ', // 035
    {147} 'R.Launcher turr: NE', // 036
    {148} 'R.Launcher turr: E ', // 037
    {149} 'R.Launcher turr: SE', // 038
    {150} 'R.Launcher turr: S ', // 039
    //UNITS1.SHP
    {151} 'Lg explosion (#1)  ', // 000
    {152} 'Lg explosion (#2)  ', // 001
    {153} 'Cannon impact (#1) ', // 002
    {154} 'Cannon impact (#2) ', // 003
    {155} 'Cannon impact (#3) ', // 004
    {156} 'Sand impact (#1)   ', // 005
    {157} 'Sand impact (#2)   ', // 006
    {158} 'Sand impact (#3)   ', // 007
    {159} 'Sonic wave (#1)    ', // 008
    {160} 'Sonic wave (#2)    ', // 009
    {161} 'Sonic wave (#3)    ', // 010
    {162} 'Sm tank burn (#1)  ', // 011
    {163} 'Sm tank burn (#2)  ', // 012
    {164} 'Sm tank burn (#3)  ', // 013
    {165} 'Lg tank burn (#1)  ', // 014
    {166} 'Lg tank burn (#2)  ', // 015
    {167} 'Lg tank burn (#3)  ', // 016
    {168} 'Flames burn (#1)   ', // 017
    {169} 'Flames burn (#2)   ', // 018
    {170} 'Flames burn (#3)   ', // 019
    {171} 'Flames burn (#4)   ', // 020
    {172} 'Flames burn (#5)   ', // 021
    {173} 'Flames burn (#6)   ', // 022
    {174} 'Bullet (small)     ', // 023
    {175} 'Bullet (medium)    ', // 024
    {176} 'Bullet (large)     ', // 025
    {177} 'Wheel debris (#1)  ', // 026
    {178} 'Wheel debris (#2)  ', // 027
    {179} 'Wheel debris (#3)  ', // 028
    {180} 'Vehicle smoke (#1) ', // 029
    {181} 'Vehicle smoke (#2) ', // 030
    {182} 'Vehicle smoke (#3) ', // 031
    {183} 'Sm explosion (#1)  ', // 032
    {184} 'Sm explosion (#2)  ', // 033
    {185} 'Sm explosion (#3)  ', // 034
    {186} 'Sm explosion (#4)  ', // 035
    {187} 'Sm explosion (#5)  ', // 036
    {188} 'Lg explosion 1 (#1)', // 037
    {189} 'Lg explosion 1 (#2)', // 038
    {190} 'Lg explosion 1 (#3)', // 039
    {191} 'Lg explosion 1 (#4)', // 040
    {192} 'Lg explosion 1 (#5)', // 041
    {193} 'Lg explosion 2 (#1)', // 042
    {194} 'Lg explosion 2 (#2)', // 043
    {195} 'Lg explosion 2 (#3)', // 044
    {196} 'Lg explosion 2 (#4)', // 045
    {197} 'Lg explosion 2 (#5)', // 046
    {198} 'Lg explosion 3 (#1)', // 047
    {199} 'Lg explosion 3 (#2)', // 048
    {200} 'Lg explosion 3 (#3)', // 049
    {201} 'Lg explosion 3 (#4)', // 050
    {202} 'Lg explosion 3 (#5)', // 051
    {203} 'Lg explosion 4 (#1)', // 052
    {204} 'Lg explosion 4 (#2)', // 053
    {205} 'Lg explosion 4 (#3)', // 054
    {206} 'Lg explosion 4 (#4)', // 055
    {207} 'Lg explosion 4 (#5)', // 056
    {208} 'Deviator gas (#1)  ', // 057
    {209} 'Deviator gas (#2)  ', // 058
    {210} 'Deviator gas (#3)  ', // 059
    {211} 'Deviator gas (#4)  ', // 060
    {212} 'Deviator gas (#5)  ', // 061
    {213} 'Blank anim (#1)    ', // 062
    {214} 'Blank anim (#2)    ', // 063
    {215} 'Blank anim (#3)    ', // 064
    {216} 'Blank anim (#4)    ', // 065
    {217} 'Blank anim (#5)    ', // 066
    {218} 'Worm eat anim (#1) ', // 067
    {219} 'Worm eat anim (#2) ', // 068
    {220} 'Worm eat anim (#3) ', // 069
    {221} 'Worm eat anim (#4) ', // 070
    {222} 'Worm eat anim (#5) ', // 071
    {223} 'Harv anim N  (#1)  ', // 072
    {224} 'Harv anim N  (#2)  ', // 073
    {225} 'Harv anim N  (#3)  ', // 074
    {226} 'Harv anim NE (#1)  ', // 075
    {227} 'Harv anim NE (#2)  ', // 076
    {228} 'Harv anim NE (#3)  ', // 077
    {229} 'Harv anim E  (#1)  ', // 078
    {230} 'Harv anim E  (#2)  ', // 079
    {231} 'Harv anim E  (#3)  ', // 080
    {232} 'Harv anim SE (#1)  ', // 081
    {233} 'Harv anim SE (#2)  ', // 082
    {234} 'Harv anim SE (#3)  ', // 083
    {235} 'Harv anim S  (#1)  ', // 084
    {236} 'Harv anim S  (#2)  ', // 085
    {237} 'Harv anim S  (#3)  ', // 086
    //UNITS.SHP
    {238} 'Quad: N            ', // 000
    {239} 'Quad: NE           ', // 001
    {240} 'Quad: E            ', // 002
    {241} 'Quad: SE           ', // 003
    {242} 'Quad: S            ', // 004
    {243} 'Trike: N           ', // 005
    {244} 'Trike: NE          ', // 006
    {245} 'Trike: E           ', // 007
    {246} 'Trike: SE          ', // 008
    {247} 'Trike: S           ', // 009
    {248} 'Harvester: N       ', // 010
    {249} 'Harvester: NE      ', // 011
    {250} 'Harvester: E       ', // 012
    {251} 'Harvester: SE      ', // 013
    {252} 'Harvester: S       ', // 014
    {253} 'MCV: N             ', // 015
    {254} 'MCV: NE            ', // 016
    {255} 'MCV: E             ', // 017
    {256} 'MCV: SE            ', // 018
    {257} 'MCV: S             ', // 019
    {258} 'Med rocket 1: N    ', // 020
    {259} 'Med rocket 1: NNE  ', // 021
    {260} 'Med rocket 1: NE   ', // 022
    {261} 'Med rocket 1: ENE  ', // 023
    {262} 'Med rocket 1: E    ', // 024
    {263} 'Med rocket 2: N    ', // 025
    {264} 'Med rocket 2: NNE  ', // 026
    {265} 'Med rocket 2: NE   ', // 027
    {266} 'Med rocket 2: ENE  ', // 028
    {267} 'Med rocket 2: E    ', // 029
    {268} 'Sm rocket 1: N     ', // 030
    {269} 'Sm rocket 1: NNE   ', // 031
    {270} 'Sm rocket 1: NE    ', // 032
    {271} 'Sm rocket 1: ENE   ', // 033
    {272} 'Sm rocket 1: E     ', // 034
    {273} 'Sm rocket 2: N     ', // 035
    {274} 'Sm rocket 2: NNE   ', // 036
    {275} 'Sm rocket 2: NE    ', // 037
    {276} 'Sm rocket 2: ENE   ', // 038
    {277} 'Sm rocket 2: E     ', // 039
    {278} 'Death Hand: N      ', // 040
    {279} 'Death Hand: NNE    ', // 041
    {280} 'Death Hand: NE     ', // 042
    {281} 'Death Hand: ENE    ', // 043
    {282} 'Death Hand: E      ', // 044
    {283} 'Empty Carryall: N  ', // 045
    {284} 'Empty Carryall: NE ', // 046
    {285} 'Empty Carryall: E  ', // 047
    {286} 'Full Carryall:  N  ', // 048
    {287} 'Full Carryall:  NE ', // 049
    {288} 'Full Carryall:  E  ', // 050
    {289}'''thopter: N  (#1)  ', // 051
    {290}'''thopter: N  (#2)  ', // 052
    {291}'''thopter: N  (#3)  ', // 053
    {292}'''thopter: NE (#1)  ', // 054
    {293}'''thopter: NE (#2)  ', // 055
    {294}'''thopter: NE (#3)  ', // 056
    {295}'''thopter: E  (#1)  ', // 057
    {296}'''thopter: E  (#2)  ', // 058
    {297}'''thopter: E  (#3)  ', // 059
    {298} 'Frigate: N         ', // 060
    {299} 'Frigate: NE        ', // 061
    {300} 'Frigate: E         ', // 062
    {301} 'Saboteur: N  (#1)  ', // 063
    {302} 'Saboteur: N  (#2)  ', // 064
    {303} 'Saboteur: N  (#3)  ', // 065
    {304} 'Saboteur: NE (#1)  ', // 066
    {305} 'Saboteur: NE (#2)  ', // 067
    {306} 'Saboteur: NE (#3)  ', // 068
    {307} 'Saboteur: E  (#1)  ', // 069
    {308} 'Saboteur: E  (#2)  ', // 070
    {309} 'Saboteur: E  (#3)  ', // 071
    {310} 'Saboteur: blow up? ', // 072
    {311} 'Soldier: N  (#1)   ', // 073
    {312} 'Soldier: N  (#2)   ', // 074
    {313} 'Soldier: N  (#3)   ', // 075
    {314} 'Soldier: NE (#1)   ', // 076
    {315} 'Soldier: NE (#2)   ', // 077
    {316} 'Soldier: NE (#3)   ', // 078
    {317} 'Soldier: E  (#1)   ', // 079
    {318} 'Soldier: E  (#2)   ', // 080
    {319} 'Soldier: E  (#3)   ', // 081
    {320} 'Trooper: N  (#1)   ', // 082
    {321} 'Trooper: N  (#2)   ', // 083
    {322} 'Trooper: N  (#3)   ', // 084
    {323} 'Trooper: NE (#1)   ', // 085
    {324} 'Trooper: NE (#2)   ', // 086
    {325} 'Trooper: NE (#3)   ', // 087
    {326} 'Trooper: E  (#1)   ', // 088
    {327} 'Trooper: E  (#2)   ', // 089
    {328} 'Trooper: E  (#3)   ', // 090
    {329} 'Inf Squad: N  (#1) ', // 091
    {330} 'Inf Squad: N  (#2) ', // 092
    {331} 'Inf Squad: N  (#3) ', // 093
    {332} 'Inf Squad: N  (#4) ', // 094
    {333} 'Inf Squad: NE (#1) ', // 095
    {334} 'Inf Squad: NE (#2) ', // 096
    {335} 'Inf Squad: NE (#3) ', // 097
    {336} 'Inf Squad: NE (#4) ', // 098
    {337} 'Inf Squad: E  (#1) ', // 099
    {338} 'Inf Squad: E  (#2) ', // 100
    {339} 'Inf Squad: E  (#3) ', // 101
    {340} 'Inf Squad: E  (#4) ', // 102
    {341} 'Troopers: N  (#1)  ', // 103
    {342} 'Troopers: N  (#2)  ', // 104
    {343} 'Troopers: N  (#3)  ', // 105
    {344} 'Troopers: N  (#4)  ', // 106
    {345} 'Troopers: NE (#1)  ', // 107
    {346} 'Troopers: NE (#2)  ', // 108
    {347} 'Troopers: NE (#3)  ', // 109
    {348} 'Troopers: NE (#4)  ', // 110
    {349} 'Troopers: E  (#1)  ', // 111
    {350} 'Troopers: E  (#2)  ', // 112
    {351} 'Troopers: E  (#3)  ', // 113
    {352} 'Troopers: E  (#4)  ', // 114
    {353} 'Landed ornithopter ', // 115
    {354} 'Transparent pixel  '  // 116
    );

  graphicsLo: graphicsSet =
    (
    //MOUSE.SHP
    {000} 'Mouse: arrow       ',
    // 000 - 000 is also used as "blank" sometimes.
    {001} 'Mouse: Scroll up   ', // 001
    {002} 'Mouse: Scroll left ', // 002
    {003} 'Mouse: Scroll down ', // 003
    {004} 'Mouse: Scroll right', // 004
    {005} 'Mouse: Target      ', // 005
    {006} 'Selection indicator', // 006
    //SHAPES.SHP
    {007} 'Money Bar          ', // 000
    {008} 'Counter: empty     ', // 001
    {009} 'Counter: 0         ', // 002
    {010} 'Counter: 1         ', // 003
    {011} 'Counter: 2         ', // 004
    {012} 'Counter: 3         ', // 005
    {013} 'Counter: 4         ', // 006
    {014} 'Counter: 5         ', // 007
    {015} 'Counter: 6         ', // 008
    {016} 'Counter: 7         ', // 009
    {017} 'Counter: 8         ', // 010
    {018} 'Counter: 9         ', // 011
    {019} 'Foundation preview ', // 012
    {020} 'Option (unselected)', // 013
    {021} 'Option (selected)  ', // 014
    {022} 'Health indicators  ', // 015
    {023} 'Icon: Attack       ', // 016
    {024} 'Icon: Move         ', // 017
    {025} 'Icon: Death Hand   ', // 018
    {026} 'Red dot   2x2      ', // 019
    {027} 'Red dot   3x3      ', // 020
    {028} 'Blue dot  2x2      ', // 021
    {029} 'Blue dot  3x3      ', // 022
    {030} 'Green dot 2x2      ', // 023
    {031} 'Green dot 3x3      ', // 024
    {032} 'Sand dot 2x2       ', // 025
    {033} 'Sand dot 3x3       ', // 026
    {034} 'Concrete dot 2x2   ', // 027
    {035} 'Concrete dot 3x3   ', // 028
    {036} 'Spice dot 2x2      ', // 029
    {037} 'Spice dot 3x3      ', // 030
    {038} 'Dunes dot 2x2      ', // 031
    {039} 'Dunes dot 3x3      ', // 032
    {040} 'Rock dot 2x2       ', // 033
    {041} 'Rock dot 3x3       ', // 034
    {042} 'Dark concr dot 2x2 ', // 035
    {043} 'Dark concr dot 3x3 ', // 036
    {044} 'Black dot 2x2      ', // 037
    {045} 'Black dot 3x3      ', // 038
    {046} 'Dark blue dot 2x2  ', // 039
    {047} 'Dark blue dot 3x3  ', // 040
    {048} 'brown dot 2x2      ', // 041
    {049} 'brown dot 3x3      ', // 042
    {050} 'White dot 2x2      ', // 043
    {051} 'White dot 3x3      ', // 044
    {052} 'Dark brown dot 2x2 ', // 045
    {053} 'Dark brown dot 3x3 ', // 046
    {054} 'Arrow up           ', // 047
    {055} 'Arrow up (pushed)  ', // 048
    {056} 'Arrow down         ', // 049
    {057} 'Arrow down (pushed)', // 050
    {058} 'Foundation grid    ', // 051
    {059} 'Black square       ', // 052
    {060} 'Icon: Concrete Slab', // 053
    {061} 'Icon: Palace       ', // 054
    {062} 'Icon: Light Factory', // 055
    {063} 'Icon: Heavy Factory', // 056
    {064} 'Icon: Hi-Tech      ', // 057
    {065} 'Icon: House of IX  ', // 058
    {066} 'Icon: WOR          ', // 059
    {067} 'Icon: Constr. Yard ', // 060
    {068} 'Icon: Wind Trap    ', // 061
    {069} 'Icon: Barracks     ', // 062
    {070} 'Icon: Starport     ', // 063
    {071} 'Icon: Refinery     ', // 064
    {072} 'Icon: Repair       ', // 065
    {073} 'Icon: Wall         ', // 066
    {074} 'Icon: Gun Turret   ', // 067
    {075} 'Icon: Rocket turret', // 068
    {076} 'Icon: Silo         ', // 069
    {077} 'Icon: Radar Outpost', // 070
    {078} 'Icon: Large Concr. ', // 071
    {079} 'Icon: Siege Tank   ', // 072
    {080} 'Icon: Rocket Lnchr.', // 073
    {081} 'Icon: Quad         ', // 074
    {082} 'Icon: Devastator   ', // 075
    {083} 'Icon: Trooper      ', // 076
    {084} 'Icon: Carryall     ', // 077
    {085} 'Icon: Combat Tank  ', // 078
    {086} 'Icon: Sonic Tank   ', // 079
    {087} 'Icon: Trike        ', // 080
    {088} 'Icon: Inf. Squad   ', // 081
    {089} 'Icon: Fremen Squad ', // 082
    {090} 'Icon: Sardaukar    ', // 083
    {091} 'Icon: Saboteur     ', // 084
    {092} 'Icon: Ornithopter  ', // 085
    {093} 'Icon: Deviator     ', // 086
    {094} 'Icon: Raider Trike ', // 087
    {095} 'Icon: Harvester    ', // 088
    {096} 'Icon: MCV          ', // 089
    {097} 'Icon: Soldier      ', // 090
    {098} 'Icon: Trooper Squad', // 091
    {099} 'Icon: Fremen       ', // 092
    {100} 'Icon: Sandworm     ', // 093
    {101} 'MENTAT button      ', // 094
    {102} 'MENTAT btn (pushed)', // 095
    {103} 'OPTIONS button     ', // 096
    {104} 'OPTIONS btn(pushed)', // 097
    {105} 'Icon: Spice        ', // 098
    {106} 'Icon: Sand         ', // 099
    {107} 'Icon: Dunes        ', // 100
    {108} 'Icon: Rock         ', // 101
    {109} 'Icon: Mountain     ', // 102
    //UNITS2.SHP
    {110} 'Small Tank body: N ', // 000
    {111} 'Small Tank body: NE', // 001
    {112} 'Small Tank body: E ', // 002
    {113} 'Small Tank body: SE', // 003
    {114} 'Small Tank body: S ', // 004
    {115} 'Small Tank turr: N ', // 005
    {116} 'Small Tank turr: NE', // 006
    {117} 'Small Tank turr: E ', // 007
    {118} 'Small Tank turr: SE', // 008
    {119} 'Small Tank turr: S ', // 009
    {120} 'Siege Tank body: N ', // 010
    {121} 'Siege Tank body: NE', // 011
    {122} 'Siege Tank body: E ', // 012
    {123} 'Siege Tank body: SE', // 013
    {124} 'Siege Tank body: S ', // 014
    {125} 'Siege Tank turr: N ', // 015
    {126} 'Siege Tank turr: NE', // 016
    {127} 'Siege Tank turr: E ', // 017
    {128} 'Siege Tank turr: SE', // 018
    {129} 'Siege Tank turr: S ', // 019
    {130} 'Devastator body: N ', // 020
    {131} 'Devastator body: NE', // 021
    {132} 'Devastator body: E ', // 022
    {133} 'Devastator body: SE', // 023
    {134} 'Devastator body: S ', // 024
    {135} 'Devastator turr: N ', // 025
    {136} 'Devastator turr: NE', // 026
    {137} 'Devastator turr: E ', // 027
    {138} 'Devastator turr: SE', // 028
    {139} 'Devastator turr: S ', // 029
    {140} 'Sonic tank turr: N ', // 030
    {141} 'Sonic tank turr: NE', // 031
    {142} 'Sonic tank turr: E ', // 032
    {143} 'Sonic tank turr: SE', // 033
    {144} 'Sonic tank turr: S ', // 034
    {145} 'R.Launcher turr: N ', // 035
    {146} 'R.Launcher turr: NE', // 036
    {147} 'R.Launcher turr: E ', // 037
    {148} 'R.Launcher turr: SE', // 038
    {149} 'R.Launcher turr: S ', // 039
    //UNITS1.SHP
    {150} 'Lg explosion (#1)  ', // 000
    {151} 'Lg explosion (#2)  ', // 001
    {152} 'Cannon impact (#1) ', // 002
    {153} 'Cannon impact (#2) ', // 003
    {154} 'Cannon impact (#3) ', // 004
    {155} 'Sand impact (#1)   ', // 005
    {156} 'Sand impact (#2)   ', // 006
    {157} 'Sand impact (#3)   ', // 007
    {158} 'Sonic wave (#1)    ', // 008
    {159} 'Sonic wave (#2)    ', // 009
    {160} 'Sonic wave (#3)    ', // 010
    {161} 'Sm tank burn (#1)  ', // 011
    {162} 'Sm tank burn (#2)  ', // 012
    {163} 'Sm tank burn (#3)  ', // 013
    {164} 'Lg tank burn (#1)  ', // 014
    {165} 'Lg tank burn (#2)  ', // 015
    {166} 'Lg tank burn (#3)  ', // 016
    {167} 'Flames burn (#1)   ', // 017
    {168} 'Flames burn (#2)   ', // 018
    {169} 'Flames burn (#3)   ', // 019
    {170} 'Flames burn (#4)   ', // 020
    {171} 'Flames burn (#5)   ', // 021
    {172} 'Flames burn (#6)   ', // 022
    {173} 'Bullet (small)     ', // 023
    {174} 'Bullet (medium)    ', // 024
    {175} 'Bullet (large)     ', // 025
    {176} 'Wheel debris (#1)  ', // 026
    {177} 'Wheel debris (#2)  ', // 027
    {178} 'Wheel debris (#3)  ', // 028
    {179} 'Vehicle smoke (#1) ', // 029
    {180} 'Vehicle smoke (#2) ', // 030
    {181} 'Vehicle smoke (#3) ', // 031
    {182} 'Sm explosion (#1)  ', // 032
    {183} 'Sm explosion (#2)  ', // 033
    {184} 'Sm explosion (#3)  ', // 034
    {185} 'Sm explosion (#4)  ', // 035
    {186} 'Sm explosion (#5)  ', // 036
    {187} 'Lg explosion 1 (#1)', // 037
    {188} 'Lg explosion 1 (#2)', // 038
    {189} 'Lg explosion 1 (#3)', // 039
    {190} 'Lg explosion 1 (#4)', // 040
    {191} 'Lg explosion 1 (#5)', // 041
    {192} 'Lg explosion 2 (#1)', // 042
    {193} 'Lg explosion 2 (#2)', // 043
    {194} 'Lg explosion 2 (#3)', // 044
    {195} 'Lg explosion 2 (#4)', // 045
    {196} 'Lg explosion 2 (#5)', // 046
    {197} 'Lg explosion 3 (#1)', // 047
    {198} 'Lg explosion 3 (#2)', // 048
    {199} 'Lg explosion 3 (#3)', // 049
    {200} 'Lg explosion 3 (#4)', // 050
    {201} 'Lg explosion 3 (#5)', // 051
    {202} 'Lg explosion 4 (#1)', // 052
    {203} 'Lg explosion 4 (#2)', // 053
    {204} 'Lg explosion 4 (#3)', // 054
    {205} 'Lg explosion 4 (#4)', // 055
    {206} 'Lg explosion 4 (#5)', // 056
    {207} 'Deviator gas (#1)  ', // 057
    {208} 'Deviator gas (#2)  ', // 058
    {209} 'Deviator gas (#3)  ', // 059
    {210} 'Deviator gas (#4)  ', // 060
    {211} 'Deviator gas (#5)  ', // 061
    {212} 'Blank anim (#1)    ', // 062
    {213} 'Blank anim (#2)    ', // 063
    {214} 'Blank anim (#3)    ', // 064
    {215} 'Blank anim (#4)    ', // 065
    {216} 'Blank anim (#5)    ', // 066
    {217} 'Worm eat anim (#1) ', // 067
    {218} 'Worm eat anim (#2) ', // 068
    {219} 'Worm eat anim (#3) ', // 069
    {220} 'Worm eat anim (#4) ', // 070
    {221} 'Worm eat anim (#5) ', // 071
    {222} 'Harv anim N  (#1)  ', // 072
    {223} 'Harv anim N  (#2)  ', // 073
    {224} 'Harv anim N  (#3)  ', // 074
    {225} 'Harv anim NE (#1)  ', // 075
    {226} 'Harv anim NE (#2)  ', // 076
    {227} 'Harv anim NE (#3)  ', // 077
    {228} 'Harv anim E  (#1)  ', // 078
    {229} 'Harv anim E  (#2)  ', // 079
    {230} 'Harv anim E  (#3)  ', // 080
    {231} 'Harv anim SE (#1)  ', // 081
    {232} 'Harv anim SE (#2)  ', // 082
    {233} 'Harv anim SE (#3)  ', // 083
    {234} 'Harv anim S  (#1)  ', // 084
    {235} 'Harv anim S  (#2)  ', // 085
    {236} 'Harv anim S  (#3)  ', // 086
    //UNITS.SHP
    {237} 'Quad: N            ', // 000
    {238} 'Quad: NE           ', // 001
    {239} 'Quad: E            ', // 002
    {240} 'Quad: SE           ', // 003
    {241} 'Quad: S            ', // 004
    {242} 'Trike: N           ', // 005
    {243} 'Trike: NE          ', // 006
    {244} 'Trike: E           ', // 007
    {245} 'Trike: SE          ', // 008
    {246} 'Trike: S           ', // 009
    {247} 'Harvester: N       ', // 010
    {248} 'Harvester: NE      ', // 011
    {249} 'Harvester: E       ', // 012
    {250} 'Harvester: SE      ', // 013
    {251} 'Harvester: S       ', // 014
    {252} 'MCV: N             ', // 015
    {253} 'MCV: NE            ', // 016
    {254} 'MCV: E             ', // 017
    {255} 'MCV: SE            ', // 018
    {256} 'MCV: S             ', // 019
    {257} 'Med rocket 1: N    ', // 020
    {258} 'Med rocket 1: NNE  ', // 021
    {259} 'Med rocket 1: NE   ', // 022
    {260} 'Med rocket 1: ENE  ', // 023
    {261} 'Med rocket 1: E    ', // 024
    {262} 'Med rocket 2: N    ', // 025
    {263} 'Med rocket 2: NNE  ', // 026
    {264} 'Med rocket 2: NE   ', // 027
    {265} 'Med rocket 2: ENE  ', // 028
    {266} 'Med rocket 2: E    ', // 029
    {267} 'Sm rocket 1: N     ', // 030
    {268} 'Sm rocket 1: NNE   ', // 031
    {269} 'Sm rocket 1: NE    ', // 032
    {270} 'Sm rocket 1: ENE   ', // 033
    {271} 'Sm rocket 1: E     ', // 034
    {272} 'Sm rocket 2: N     ', // 035
    {273} 'Sm rocket 2: NNE   ', // 036
    {274} 'Sm rocket 2: NE    ', // 037
    {275} 'Sm rocket 2: ENE   ', // 038
    {276} 'Sm rocket 2: E     ', // 039
    {277} 'Death Hand: N      ', // 040
    {278} 'Death Hand: NNE    ', // 041
    {279} 'Death Hand: NE     ', // 042
    {280} 'Death Hand: ENE    ', // 043
    {281} 'Death Hand: E      ', // 044
    {282} 'Empty Carryall: N  ', // 045
    {283} 'Empty Carryall: NE ', // 046
    {284} 'Empty Carryall: E  ', // 047
    {285} 'Full Carryall:  N  ', // 048
    {286} 'Full Carryall:  NE ', // 049
    {287} 'Full Carryall:  E  ', // 050
    {288}'''thopter: N  (#1)  ', // 051
    {289}'''thopter: N  (#2)  ', // 052
    {290}'''thopter: N  (#3)  ', // 053
    {291}'''thopter: NE (#1)  ', // 054
    {292}'''thopter: NE (#2)  ', // 055
    {293}'''thopter: NE (#3)  ', // 056
    {294}'''thopter: E  (#1)  ', // 057
    {295}'''thopter: E  (#2)  ', // 058
    {296}'''thopter: E  (#3)  ', // 059
    {297} 'Frigate: N         ', // 060
    {298} 'Frigate: NE        ', // 061
    {299} 'Frigate: E         ', // 062
    {300} 'Saboteur: N  (#1)  ', // 063
    {301} 'Saboteur: N  (#2)  ', // 064
    {302} 'Saboteur: N  (#3)  ', // 065
    {303} 'Saboteur: NE (#1)  ', // 066
    {304} 'Saboteur: NE (#2)  ', // 067
    {305} 'Saboteur: NE (#3)  ', // 068
    {306} 'Saboteur: E  (#1)  ', // 069
    {307} 'Saboteur: E  (#2)  ', // 070
    {308} 'Saboteur: E  (#3)  ', // 071
    {309} 'Saboteur: blow up? ', // 072
    {310} 'Soldier: N  (#1)   ', // 073
    {311} 'Soldier: N  (#2)   ', // 074
    {312} 'Soldier: N  (#3)   ', // 075
    {313} 'Soldier: NE (#1)   ', // 076
    {314} 'Soldier: NE (#2)   ', // 077
    {315} 'Soldier: NE (#3)   ', // 078
    {316} 'Soldier: E  (#1)   ', // 079
    {317} 'Soldier: E  (#2)   ', // 080
    {318} 'Soldier: E  (#3)   ', // 081
    {319} 'Trooper: N  (#1)   ', // 082
    {320} 'Trooper: N  (#2)   ', // 083
    {321} 'Trooper: N  (#3)   ', // 084
    {322} 'Trooper: NE (#1)   ', // 085
    {323} 'Trooper: NE (#2)   ', // 086
    {324} 'Trooper: NE (#3)   ', // 087
    {325} 'Trooper: E  (#1)   ', // 088
    {326} 'Trooper: E  (#2)   ', // 089
    {327} 'Trooper: E  (#3)   ', // 090
    {328} 'Inf Squad: N  (#1) ', // 091
    {329} 'Inf Squad: N  (#2) ', // 092
    {330} 'Inf Squad: N  (#3) ', // 093
    {331} 'Inf Squad: N  (#4) ', // 094
    {332} 'Inf Squad: NE (#1) ', // 095
    {333} 'Inf Squad: NE (#2) ', // 096
    {334} 'Inf Squad: NE (#3) ', // 097
    {335} 'Inf Squad: NE (#4) ', // 098
    {336} 'Inf Squad: E  (#1) ', // 099
    {337} 'Inf Squad: E  (#2) ', // 100
    {338} 'Inf Squad: E  (#3) ', // 101
    {339} 'Inf Squad: E  (#4) ', // 102
    {340} 'Troopers: N  (#1)  ', // 103
    {341} 'Troopers: N  (#2)  ', // 104
    {342} 'Troopers: N  (#3)  ', // 105
    {343} 'Troopers: N  (#4)  ', // 106
    {344} 'Troopers: NE (#1)  ', // 107
    {345} 'Troopers: NE (#2)  ', // 108
    {346} 'Troopers: NE (#3)  ', // 109
    {347} 'Troopers: NE (#4)  ', // 110
    {348} 'Troopers: E  (#1)  ', // 111
    {349} 'Troopers: E  (#2)  ', // 112
    {350} 'Troopers: E  (#3)  ', // 113
    {351} 'Troopers: E  (#4)  ', // 114
    {352} 'Landed ornithopter ', // 115
    {353} 'Transparent pixel  ', // 116
    {354} ''                     // ---
    );

  decayFactors: array[0..10] of values =
    (
    (val: -1; txt: 'No decay'),
    (val: 0; txt: '0 (damage=1)'),
    (val: 1; txt: '1 (damage=2)'),
    (val: 2; txt: '2 (damage=3)'),
    (val: 3; txt: '3 (damage=4)'),
    (val: 4; txt: '4 (damage=5)'),
    (val: 5; txt: '5 (damage=6)'),
    (val: 6; txt: '6 (damage=7)'),
    (val: 7; txt: '7 (damage=8)'),
    (val: 8; txt: '8 (damage=9)'),
    (val: 9; txt: '9 (damage=10)')
    );

  craterTypes: array[0..2] of values =
    (
    (val: 0; txt: 'None'),
    (val: 1; txt: 'Sand crater'),
    (val: 2; txt: 'Rock crater')
    );


  // default EMPTY command used when removing a script command, so the editor
  // sees the full available editing space.
  // User will not be able to add empty command manually: when an ending command
  // is inserted, all following lines will have to be automatically cleared with
  // this value.
  defaultEmptyAnimScriptLine: scriptLine = (command: $F; argument: $314);

  structAnim: array[0..9] of scriptvalues =
    (
    (val: $0; isEnd: True; hasArg: False; shorttxt: 'RM';
    longtxt: 'Remove from map (#1)    '),
    (val: $1; isEnd: True; hasArg: False; shorttxt: 'ST';
    longtxt: 'Stop animation          '),
    (val: $2; isEnd: False; hasArg: False; shorttxt: '??';
    longtxt: 'Unknown                 '),
    (val: $3; isEnd: False; hasArg: True; shorttxt: 'WT';
    longtxt: 'Wait for amount of time '),
    (val: $4; isEnd: True; hasArg: False; shorttxt: 'LP';
    longtxt: 'Loop (Jump to step 0)   '),
    (val: $5; isEnd: False; hasArg: True; shorttxt: 'SN';
    longtxt: 'Play Sound with ID      '),
    (val: $6; isEnd: False; hasArg: True; shorttxt: 'FR';
    longtxt: 'Change graphics frame to'),
    (val: $7; isEnd: True; hasArg: True; shorttxt: 'JM';
    longtxt: 'Jump back # script steps'),
    (val: $8; isEnd: False; hasArg: True; shorttxt: 'ID';
    longtxt: 'Change graphics ID to   '),
    (val: $9; isEnd: True; hasArg: False; shorttxt: 'RM';
    longtxt: 'Remove from map (dup)   ')
    );

  // "special animation" in unit opts is an index in a list of refs to animations.
  // These are the commands used in these animations.
  // Not sure if I'll ever be able to use these. Unlike the buildings, there are no direct
  // references to them. Units use an animation ID instead. This probably means there's
  // a nice ordered list of them somewhere though, to link them to those IDs.
  unitAnim: array[0..14] of scriptvalues = (
    (val: $0; isEnd: True; hasArg: False; shorttxt: 'ST';
    longtxt: 'Stop animation          '),
    (val: $1; isEnd: False; hasArg: True; shorttxt: 'FF';
    longtxt: '[False] Change frame to '),
    (val: $2; isEnd: False; hasArg: True; shorttxt: 'WT';
    longtxt: 'Wait for amount of time '),
    (val: $3; isEnd: False; hasArg: True; shorttxt: 'WR';
    longtxt: 'Wait for random time  < '),
    (val: $4; isEnd: False; hasArg: True; shorttxt: 'TF';
    longtxt: '[True]  Change frame to '),
    (val: $5; isEnd: True; hasArg: False; shorttxt: 'LP';
    longtxt: 'Loop (Jump to step 0)   '),
    (val: $6; isEnd: False; hasArg: True; shorttxt: 'PS';
    longtxt: 'modify position cell    '),
    (val: $7; isEnd: False; hasArg: True; shorttxt: 'PS';
    longtxt: 'modify pos. cell (dup)  '),
    (val: $8; isEnd: False; hasArg: True; shorttxt: 'B?';
    longtxt: 'Bloom check?            '),
    (val: $9; isEnd: False; hasArg: True; shorttxt: 'SN';
    longtxt: 'Play Sound with ID      '),
    (val: $A; isEnd: False; hasArg: True; shorttxt: 'RV';
    longtxt: 'Revealed check?         '),
    (val: $B; isEnd: False; hasArg: True; shorttxt: 'ST';
    longtxt: 'Structure anim?         '),
    (val: $C; isEnd: True; hasArg: False; shorttxt: 'ST';
    longtxt: 'Stop animation (dup1)   '),
    (val: $D; isEnd: False; hasArg: True; shorttxt: 'BK';
    longtxt: 'Spice Bloom kills unit  '),
    (val: $E; isEnd: True; hasArg: False; shorttxt: 'ST';
    longtxt: 'Stop animation (dup2)   ')
    );

  // == END OF TYPES CONTROL ==

  // == DUNE II VERSION CONTROL ==

  versions: array[0..5] of version =
    (
    (
    nameShort: 'DEMO';
    nameLong: 'Demo version';
    Info: ('This version is the automatically playing demo',
    'released on the Westwood FTP site.', 'Editing it is probably not very useful.',
    '', '', '', '', '');
    verAddress: $36FFE;
    refAddress: $324D90F0;
    refHeader: $4A00;
    dataInfo: ((offset: $3A530; listLen: 6; refArray: nil),    // movement types list
    (offset: $31470; listlen: 27; refArray: nil),   // units list
    (offset: $30CFA; listlen: 19; refArray: nil),   // structures list
    (offset: $3A14A; listlen: 6; refArray: nil),    // houses list
    (offset: $3A38C; listlen: 15; refArray: nil),   // terrain types lis
    (offset: $32BCE; listlen: 14; refArray: nil),   // actions list
    (offset: $377D4; listlen: 131; refArray: nil),  // sounds list
    (offset: $37AEC; listlen: 8; refArray: @musicNamesDemo), // music list
    (offset: $32C80; listlen: 189; refArray: nil)); // files list
    Graphics: @graphicsLo;
    ),
    (
    nameShort: 'DEMO-KY';
    nameLong: 'Demo version from the Legend of Kyrandia CD';
    Info: ('This version has no ingame demo, but just loops',
    'the game intro. Because of this, all the',
    'undoubtedly magnificent edits you''re planning',
    'to make have no significance at all.', '', 'Go do something more productive.', '', '');
    verAddress: $37BF0;
    refAddress: $33BB7810;
    refHeader: $4C00;
    dataInfo: ((offset: $3C41E; listlen: 6; refArray: nil),    // movement types list
    (offset: $30450; listlen: 27; refArray: nil),   // units list
    (offset: $2FD2A; listlen: 19; refArray: nil),   // structures list
    (offset: $3C034; listlen: 6; refArray: nil),    // houses list
    (offset: $3C27A; listlen: 15; refArray: nil),   // terrain types lis
    (offset: $31CEE; listlen: 14; refArray: nil),   // actions list
    (offset: $3954A; listlen: 131; refArray: nil),  // sounds list
    (offset: $3985C; listlen: 38; refArray: nil),   // music list
    (offset: $31DA0; listlen: 677; refArray: nil)); // files list
    Graphics: @graphicsHi;
    ),
    (
    nameShort: '1.00';
    nameLong: 'United States v1.00';
    Info: ('The original English-only US release of Dune II.', '',
    '', '', '', '', '', '');
    verAddress: $37FA2;
    refAddress: $332B8110;
    refHeader: $4C00;
    dataInfo: ((offset: $3B66A; listlen: 6; refArray: nil),    // movement types list
    (offset: $30750; listlen: 27; refArray: nil),   // units list
    (offset: $2FFDA; listlen: 19; refArray: nil),   // structures list
    (offset: $3B284; listlen: 6; refArray: nil),    // houses list
    (offset: $3B4C6; listlen: 15; refArray: nil),   // terrain types lis
    (offset: $31CFE; listlen: 14; refArray: nil),   // actions list
    (offset: $386B8; listlen: 132; refArray: nil),  // sounds list
    (offset: $389D0; listlen: 38; refArray: nil),   // music list
    (offset: $31DB0; listlen: 568; refArray: nil)); // files list
    Graphics: @graphicsLo;
    ),
    (
    nameShort: '1.07-US';
    nameLong: 'United States v1.07';
    Info: ('The updated version of the English-only US',
    'release of Dune II. It fixes a number of bugs,',
    'like the fact the Starport countdown could get', 'stuck on "T-minus 0".',
    '', '', '', '');
    verAddress: $37BB2;
    refAddress: $32EC8500;
    refHeader: $4C00;
    dataInfo: ((offset: $3B146; listlen: 6; refArray: nil),    // movement types list
    (offset: $2FCB0; listlen: 27; refArray: nil),   // units list
    (offset: $2F58A; listlen: 19; refArray: nil),   // structures list
    (offset: $3AD5C; listlen: 6; refArray: nil),    // houses list
    (offset: $3AFA2; listlen: 15; refArray: nil),   // terrain types lis
    (offset: $3154E; listlen: 14; refArray: nil),   // actions list
    (offset: $382C8; listlen: 131; refArray: nil),  // sounds list
    (offset: $385E0; listlen: 38; refArray: nil),   // music list
    (offset: $31600; listlen: 606; refArray: nil)); // files list
    Graphics: @graphicsHi;
    ),
    (
    nameShort: '1.07-EU';
    nameLong: 'European v1.07';
    Info: ('The European release of Dune II, with three',
    'languages: English, French and German.',
    'It has all the bug fixes of the US 1.07 version.', '', '', '', '', '');
    verAddress: $38132;
    refAddress: $33447F80;
    refHeader: $4C00;
    dataInfo: ((offset: $3BC62; listlen: 6; refArray: nil),    // movement types list
    (offset: $2FD00; listlen: 27; refArray: nil),   // units list
    (offset: $2F5DA; listlen: 19; refArray: nil),   // structures list
    (offset: $3B878; listlen: 6; refArray: nil),    // houses list
    (offset: $3BABE; listlen: 15; refArray: nil),   // terrain types lis
    (offset: $3159E; listlen: 14; refArray: nil),   // actions list
    (offset: $38DDA; listlen: 131; refArray: nil),  // sounds list
    (offset: $390EC; listlen: 38; refArray: nil),   // music list
    (offset: $31650; listlen: 676; refArray: nil)); // files list
    Graphics: @graphicsHi;
    ),
    (
    nameShort: '1.07-HS';
    nameLong: 'HitSquad v1.07';
    Info: ('An alternate release of the European 3-language',
    'version, with three languages: English, French',
    'and German. It has all the bug fixes of the', 'US 1.07 version.',
    'It is unknown if this is a later or earlier',
    'version than the other EU one, but this one',
    'misses the specific German font for the sidebar.', '');
    verAddress: $380D2;
    refAddress: $333E7FE0;
    refHeader: $4C00;
    dataInfo: ((offset: $3BBF6; listlen: 6; refArray: nil),    // movement types list
    (offset: $2FCB0; listlen: 27; refArray: nil),   // units list
    (offset: $2F58A; listlen: 19; refArray: nil),   // structures list
    (offset: $3B80C; listlen: 6; refArray: nil),    // houses list
    (offset: $3BA52; listlen: 15; refArray: nil),   // terrain types lis
    (offset: $3154E; listlen: 14; refArray: nil),   // actions list
    (offset: $38D7A; listlen: 131; refArray: nil),  // sounds list
    (offset: $3908C; listlen: 38; refArray: nil),   // music list
    (offset: $31600; listlen: 675; refArray: nil)); // files list
    Graphics: @graphicsHi;
    )
    );

  // == END VERSION-SPECIFIC CONTROLS ==


  // =========================================
  //                 MISCELLANEOUS
  // =========================================

  // To convert value to string on the fly without needing a variable to store it in.
  function toString(b: boolean): string;
  begin
    str(byte(b), Result);
  end;

  function toString(b: byte): string;
  begin
    str(b, Result);
  end;

  function toString(i: integer): string;
  begin
    str(i, Result);
  end;

  function toString(i: cardinal): string;
  begin
    str(i, Result);
  end;

  // to convert signed input to unsigned integer for writing
  function convertToUnsigned(input: integer; nrofbytes: byte): cardinal;
  var
    maxval: int64;
  begin
    maxval := 1 shl (nrofbytes * 8);
    if input < 0 then
      Result := cardinal(maxval + input)
    else
      Result := input;
  end;
  // to convert signed input to unsigned integer for writing
  function convertToSigned(input: cardinal; nrofbytes: byte): integer;
  var
    shiftbits, lastbit: integer;
    maxval: int64;
  begin
    shiftbits := ((8 * nrofbytes)) - 1;
    lastbit := input shr shiftbits;
    maxval := 1 shl (nrofbytes * 8);
    if (lastbit <> 0) then
      Result := integer(input - maxval)
    else
      Result := input;
  end;


  // Press Key To Continue procedure with buffer clear.
  procedure cntin(Msg: string);
  begin
    if (length(Msg) > 0) then
    begin
      writeln();
      Write(Msg);
    end;
    repeat
    until keypressed;
    ReadKey();
    while keypressed do
      ReadKey(); {Clears the keyboard buffer}
    if (length(Msg) > 0) then
      writeln();
  end; {cntin}

  procedure cntin();
  begin
    cntin('');
  end; {cntin}

  procedure cntin(showMsg: boolean);
  begin
    if showMsg then
      cntin('Press any key to continue. . . ')
    else
      cntin('');
  end; {cntin}

  // Sets cursor to end of page to 'hide' it.
  procedure resetCursor();
  begin
    gotoxy(80, 25);
  end;

  function capitalize(str: string): string;
  begin
    if (byte(str[1]) >= 97) and (byte(str[1]) <= 122) then
      str[1] := char(byte(str[1]) - 32);
    Result := str;
  end;

  function isNumeric(s: string): boolean;
  var
    val, i: integer;
  begin
    Result := True;
    for i := 1 to length(s) do
    begin
      val := byte(s[i]) - 48;
      if (val < 0) or (val > 9) then
        Result := False;
    end;
  end;

  function GetWord(startstr: string; wordnumber: integer; delim: string): string;
  begin
    startstr := startstr + delim;
    while wordnumber > 1 do
    begin
      startstr := copy(startstr, pos(delim, startstr) + length(delim),
        (length(startstr) - pos(delim, startstr)));
      wordnumber := wordnumber - 1;
    end;
    if pos(delim, startstr) > 0 then
      Result := copy(startstr, 1, pos(delim, startstr) - 1)
    else
      Result := startstr;
  end;

  // For abbreviating prerequisites & options to 4 characters
  function smartAbbrev(toAbbrev: string): string;
  var
    firstword: string;
    secondword: string;
  begin
    Result := '';
    // cuts off "'" of "'thopter".
    if (pos('''', toAbbrev) = 1) then
      toAbbrev := copy(toAbbrev, 2, length(toAbbrev) - 1);
    if (pos(' ', toAbbrev) = 0) and (pos('-', toAbbrev) = 0) then
    begin
      firstword := toAbbrev;
      secondword := '';
    end;
    if (pos(' ', toAbbrev) = 0) or ((pos(' ', toAbbrev) <> 0) and
      (pos('-', toAbbrev) <> 0) and (pos(' ', toAbbrev) > pos('-', toAbbrev))) then
    begin
      firstword := GetWord(toAbbrev, 1, '-');
      secondword := GetWord(toAbbrev, 2, '-');
    end;

    if (pos('-', toAbbrev) = 0) or ((pos(' ', toAbbrev) <> 0) and
      (pos('-', toAbbrev) <> 0) and (pos(' ', toAbbrev) < pos('-', toAbbrev))) then
    begin
      firstword := GetWord(toAbbrev, 1, ' ');
      secondword := GetWord(toAbbrev, 2, ' ');
    end;

    firstword := capitalize(firstword);
    secondword := capitalize(secondword);

    if SecondWord <> '' then
    begin
      if length(secondword) = 1 then
        Result := copy(firstword, 1, 3) + secondword
      else if length(firstword) > 2 then
        Result := copy(firstword, 1, 2) + copy(secondword, 1, 2)
      else
        Result := copy(concat(firstword, copy(secondword, 1, 3)), 1, 4);
    end
    else
    begin
      if isNumeric(copy(firstword, length(firstword) - 2, 3)) then
        Result := copy(firstword, 1, 1) + Copy(firstword, length(firstword) - 2, 3)
      else if isNumeric(Copy(firstword, length(firstword) - 1, 2)) then
        Result := copy(firstword, 1, 2) + Copy(firstword, length(firstword) - 1, 2)
      else if isNumeric(firstword[length(firstword)]) then
        Result := copy(firstword, 1, 3) + Copy(firstword, length(firstword), 1)
      else
        Result := copy(firstword, 1, 4);
    end;
  end;

  function hexchar2byte(hex: char): byte;
  begin
    Result := 0;
    if (byte(hex) >= 65) and (byte(hex) <= 70) then
      Result := byte(hex) - 55
    else if (byte(hex) >= 48) and (byte(hex) <= 57) then
      Result := byte(hex) - 48;
  end;

  function hexstr2byte(hex: string): byte;
  var
    multiplier: integer;
  begin
    Result := 0;
    multiplier := 16;
    if (length(hex) = 0) then
      hex := '00';
    if (length(hex) = 1) then
      hex := concat('0' + hex);
    Result := hexchar2byte(hex[1]) * multiplier + hexchar2byte(hex[2]);
  end;

  function byte2hexstr(hex: byte): string;
  begin
    Result := '';
    if ((hex div 16) >= 10) then
      Result := Result + char((hex div 16) - 10 + $41)
    else
      Result := Result + char((hex div 16) + $30);
    if ((hex mod 16) >= 10) then
      Result := Result + char((hex mod 16) - 10 + $41)
    else
      Result := Result + char((hex mod 16) + $30);
  end;

  function byte2binstr(bin: byte; split: boolean): string;
  var
    i: integer;
  begin
    Result := '';
    for i := 0 to 7 do
    begin
      Result := toString(cardinal(bin mod 2)) + Result;
      bin := bin div 2;
      if (i = 3) and split then
        Result := ' ' + Result;
    end;
  end;

  // =========================================
  //              BARE NAVIGATION
  // =========================================
  procedure MoveDown(Lines: integer);
  begin
    if Lines > 0 then
    begin
      if ((select[level] + Lines) < maxdisplay[level]) then
        // normal move
        select[level] := select[level] + Lines
      else
      begin
        // move beyond visible field
        Lines := Lines - (maxdisplay[level] - select[level]);
        select[level] := maxdisplay[level];
      end;
      if (Lines > 0) and (select[level] = maxdisplay[level]) and
        (maxdisplay[level] < listlen[level]) then
      begin
        // scroll
        scroll[level] := scroll[level] + Lines;
        if scroll[level] > (listlen[level] - maxdisplay[level]) then
          scroll[level] := (listlen[level] - maxdisplay[level]);
      end;
    end;
  end;

  procedure MoveUp(Lines: integer);
  begin
    if Lines > 0 then
    begin
      if ((select[level] - Lines) > 0) then
        // move
        select[level] := select[level] - Lines
      else
      begin
        // move beyond visible field
        Lines := Lines - select[level];
        select[level] := 0;
      end;
      if (Lines > 0) and (select[level] = 0) and (scroll[level] >= 0) then
        // scroll
      begin
        scroll[level] := scroll[level] - Lines;
        if scroll[level] < 0 then
          scroll[level] := 0;
      end;
    end;
  end;

  procedure MoveRight();
  begin
    if level = 3 then
      level := 1
    else
      level := level + 1;
  end;

  procedure MoveLeft();
  begin
    if level = 1 then
      level := 3
    else
      level := level - 1;
  end;


  // =========================================
  //                 INPUT / OUTPUT
  // =========================================

  function openFile(filename: string): boolean;
  var
    status: integer;
  begin
    Assign(exefile, filename);
  {$I-}
    reset(exefile, 1);
  {$I+}
    status := IOResult;
    if status <> 0 then
    begin
      writeln('Error opening "', filename, '".');
      Result := False;
    end
    else
    begin
      Result := True;
    end;
  end; {openFile}

  procedure closeFile();
  begin
    Close(exefile);
  {$I+}
  end; {closeFile}

  function readByte(address: cardinal): byte;
  begin
    if FileSize(exefile) >= address then
    begin
      Result := 0;
      seek(exefile, address);
      blockread(exefile, Result, 1);
    end;
  end;

  // converts big-endian to little-endian
  function readBytes(address: cardinal; nrofbytes: byte): cardinal;
  var
    i, cur: byte;
  begin
    nrofbytes := min(4, nrofbytes);
    nrofbytes := max(1, nrofbytes);
    nrofbytes := nrofbytes - 1;
    Result := 0;
    for i := 0 to nrofbytes do
    begin
      cur := readByte(address);
      Result := Result + (cur shl (8 * i));
      address := address + 1;
    end;
  end;

  function readBytesSigned(address: cardinal; nrofbytes: byte): integer;
  var
    initialRead: cardinal;
  begin
    initialRead := readBytes(address, nrofbytes);
    Result := convertToSigned(initialRead, nrofbytes);
  end;

  procedure writeByte(address: cardinal; Data: byte);
  begin
    seek(exefile, address);
    blockwrite(exefile, Data, 1);
  end;

  // bytenr: 0 = lowest byte, 3 = highest byte
  function getByte(Value: cardinal; bytenr: cardinal): byte;
  begin
    bytenr := min(3, bytenr);
    bytenr := max(0, bytenr);
    Value := Value shr (8 * (bytenr));
    Value := Value and $FF;
    Result := byte(Value);
  end;

  // converts little-endian to big-endian
  procedure writeBytes(address, Value: cardinal; nrofbytes: byte);
  var
    i: byte;
  begin
    nrofbytes := min(4, nrofbytes);
    nrofbytes := max(1, nrofbytes);
    for i := 0 to nrofbytes - 1 do
    begin
      writeByte(address, getByte(Value, i));
      address := address + 1;
    end;
  end;

  // =========================================
  //              DATA & OFFSETS
  // =========================================

  function getDataLen(typeindex, optindex: integer): byte;
  begin
    // DATA TYPE LIST - Defines length for each data type. VERY important.
    case mainlist[typeindex].optsList^[optindex].typeid of
      -1: Result := 0;    // Safety value for empty list item.
      0: Result := 4;     // 4-byte (INT)
      1: Result := 1;     // signed byte
      2: Result := 2;     // 2-byte
      3: Result := 4;     // 4-byte (REF)
      4: Result := 1;     // owners
      5: Result := 2;     // command
      6: Result := 2;     // movement type
      7: Result := 2;     // weapon type
      8: Result := 2;     // foundation
      9: Result := 2;     // decay factor
      10: Result := 4;    // prerequisites
      11: Result := 2;    // sound
      // Bit switches must ALWAYS be used in groups of 8. Last one adds the size.
      12: Result := 0;    // bit switch 1/8
      13: Result := 0;    // bit switch 2/8
      14: Result := 0;    // bit switch 3/8
      15: Result := 0;    // bit switch 4/8
      16: Result := 0;    // bit switch 5/8
      17: Result := 0;    // bit switch 6/8
      18: Result := 0;    // bit switch 7/8
      19: Result := 1;    // bit switch 8/8
      20: Result := 2;    // percentage (2-byte)
      21: Result := 2;    // music
      22: Result := 2;    // character
      23: Result := 2;    // palace special ability
      24: Result := 2;    // SHP Graphics
      25: Result := 2;    // unit display mode
      26: Result := 2;    // action interrupt mode
      27: Result := 2;    // sidebar mode
      28: Result := 2;    // voc sound
      29: Result := 1;    // parent PAK
      30: Result := 4;    // bit list of allowed units
      31: Result := 4;    // structure animation
      32: Result := 2;    // structure graphics set
      33: Result := 1;    // unsigned byte
      34: Result := 2;    // boolean
      35: Result := 1;    // percentage (1-byte)
      36: Result := 2;    // Crater type
      else
        Result := 0;
    end;
  end;

  function getListLen(typeindex, toindex: integer): cardinal;
  var
    i: cardinal;
  begin
    Result := 0;
    if toindex > 0 then
      for i := 0 to toindex - 1 do
        Result := Result + getDataLen(typeindex, i);
  end;

  function getAddress(typeindex, unitindex, optindex: integer): cardinal;
  var
    optslistLen: cardinal;
  begin
    optslistLen := optsListlengths[typeindex];
    Result := versions[gamever].dataInfo[typeindex].offset;
    Result := Result + cardinal(unitindex) * getListLen(typeindex, optslistLen + 1);
    Result := Result + getListLen(typeindex, optindex);
  end;

  // Get offset from reference
  function getRefOffset(input: cardinal): cardinal;
  begin
    if input >= versions[gamever].refAddress then
      Result := input - versions[gamever].refAddress
    else if (input = 0) then
      Result := 0
    else
      Result := High(cardinal);
    if (Result >= FileSize(exefile)) then
      Result := High(cardinal);
  end;

  // Get reference from offset
  function getOffsetRef(input: cardinal): cardinal;
  begin
    if (input < 0) or (input > FileSize(exefile)) then
      Result := High(cardinal)
    else if input = 0 then
      Result := 0
    else
      Result := input + versions[gamever].refAddress;
  end;

  // Reads zero-terminated string
  function readString(address: cardinal): string;
  var
    c: byte;
    i: cardinal;
  begin
    i := 0;
    c := $FF;
    Result := '';
    if address < FileSize(exefile) then
    begin
      while (c <> 0) and (i < 100) do
        // 100 = max string length; built in to prevent overflows
      begin
        c := readByte(address + i);
        if (c <> 0) then
          Result := concat(Result + char(c));
        i := i + 1;
      end;
    end
    else
      Result := '';
  end;

  function getRefAddress(typeindex, unitindex, optindex: integer): cardinal;
  begin
    Result := getRefOffset(readBytes(getAddress(typeindex, unitindex,
      optindex), 4));
  end;

  // =========================================
  //             DATA TYPE DISPLAY
  // =========================================

  function GetTypeDescr(id: integer): descr;
  begin
    Result := typedescr[id];
  end;

  function GetOptHelp(helpid: integer): bigdescr;
  begin
    Result := optHelp[helpid];
  end;


  // Reads zero-terminated string defined by a reference
  function getRefString(Value: cardinal): string;
  var
    val: cardinal;
  begin
    if Value = 0 then
      Result := '<no reference>'
    else
    begin
      val := getRefOffset(Value);
      if val = high(cardinal) then
        Result := '<invalid ref>'
      else
        Result := readString(val);
    end;
  end;

  function getRefString(typeindex, unitindex, optindex: integer): string;
  var
    address, Data: cardinal;
  begin
    address := getAddress(typeindex, unitindex, optindex);
    Data := readBytes(address, 4);
    Result := getRefString(Data);
  end;

  function getBytesString(nrofbytes: byte; Value: cardinal; split: boolean): string;
  var
    i: byte;
  begin
    Result := '';
    nrofbytes := min(4, nrofbytes);
    nrofbytes := max(1, nrofbytes);
    for i := nrofbytes - 1 downto 0 do
    begin
      Result := Result + byte2hexStr(getByte(Value, i));
      if (i <> 0) and split then
        Result := Result + ' ';
    end;
  end;

  function getBytesString(Value: integer; split: boolean): string;
  begin
    Result := getBytesString(4, Value, split);
  end;

  function getBytesString(typeindex, unitindex, optindex: integer;
    split: boolean): string;
  var
    len: byte;
  begin
    len := getDataLen(typeindex, optindex);
    Result := getBytesString(len, readBytes(
      getAddress(typeindex, unitindex, optindex), len), split);
  end;

  function getBinString(nrofbytes: byte; Value: cardinal; split: boolean): string;
  var
    i: byte;
  begin
    Result := '';
    nrofbytes := min(4, nrofbytes);
    nrofbytes := max(1, nrofbytes);
    for i := nrofbytes - 1 downto 0 do
    begin
      Result := Result + byte2binStr(getByte(Value, i), split);
      if (i <> 0) and split then
        Result := Result + ' | ';
    end;
  end;

  function getBinString(typeindex, unitindex, optindex: integer; split: boolean): string;
  var
    len: byte;
  begin
    len := getDataLen(typeindex, optindex);
    Result := getBinString(len, readBytes(
      getAddress(typeindex, unitindex, optindex), len), split);
  end;

  //unsigned
  function getBytes(typeindex, unitindex, optindex: integer): cardinal;
  begin
    Result := readBytes(getAddress(typeindex, unitindex, optindex),
      getDataLen(typeindex, optindex));
  end;

  //unsigned
  function getBytes(typeindex, unitindex, optindex: integer; nrofbytes: byte): cardinal;
  begin
    Result := readBytes(getAddress(typeindex, unitindex, optindex), nrofbytes);
  end;

  //signed
  function getBytesSigned(typeindex, unitindex, optindex: integer): integer;
  begin
    Result := readBytesSigned(getAddress(typeindex, unitindex, optindex),
      getDataLen(typeindex, optindex));
  end;

  //signed
  function getBytesSigned(typeindex, unitindex, optindex: integer;
    nrofbytes: byte): integer;
  begin
    Result := readBytesSigned(getAddress(typeindex, unitindex, optindex), nrofbytes);
  end;

  // ==================
  // TYPE-SPECIFIC DATA
  // ==================


  // get a name string of one of the main lists
  function GetBasicTypeString(typeindex, unitindex: integer): string;
  begin
    if unitindex = -1 then
      Result := 'None'
    else
    begin
      if (unitindex >= versions[gamever].dataInfo[typeindex].listLen) then
        Result := '<Invalid>'
      else if (mainlist[typeindex].refOption >= 0) then
        Result := getRefString(typeindex, unitindex, mainlist[typeindex].refOption)
      else
      begin
        Result := '';
        if ((mainlist[typeindex].refArray <> nil) and
          (high(mainlist[typeindex].refArray^) >= unitindex)) then
        begin
          if ((versions[gamever].dataInfo[typeindex].refArray <> nil) and
            (high(versions[gamever].dataInfo[typeindex].refArray^) >= unitindex)) then
            Result := versions[gamever].dataInfo[typeindex].refArray^[unitindex]
          else
            Result := mainlist[typeindex].refArray^[unitindex];
        end;
        if (Result = '') then
          Result := toString(unitindex);
      end;
    end;
  end;

  // get a name string of one of the main lists
  function GetBasicTypeString(typeindex, unitindex, optindex, basictype:
    integer): string;
  var
    val: integer;
  begin
    val := getBytesSigned(typeindex, unitindex, optindex);
    Result := GetBasicTypeString(basictype, val);
  end;

  // get a name string of one of the main lists
  function GetGraphicsString(typeindex, unitindex, optindex: integer;
    docutoff: boolean; cutoff: integer; ignoreUnkn, showvalue: boolean): string;
  var
    Data, i: integer;
  begin
    Data := getBytesSigned(typeindex, unitindex, optindex);
    if ignoreUnkn then
      Result := toString(Data)
    else
      Result := '<Unknown>';
    if Data = -1 then
      Result := 'None'
    else
    begin
      for i := 0 to high(versions[gamever].Graphics^) do
        if Data = i then
          Result := versions[gamever].Graphics^[i];
      if (showvalue) then
        Result := toString(Data) + ' - ' + Result;
      if docutoff then
        Result := Copy(Result, 1, cutoff);
    end;
  end;

  // allows extra parameter to determine where to cut off the string.
  function getValuesStr(typeindex, unitindex, optindex: integer;
    vallist: array of values; docutoff: boolean; cutoff: integer;
    ignoreUnkn, showvalue: boolean): string;
  var
    Data, i: integer;
  begin
    Data := getBytesSigned(typeindex, unitindex, optindex);
    if ignoreUnkn then
      Result := toString(Data)
    else
      Result := '<Unknown>';
    for i := 0 to high(vallist) do
      if Data = vallist[i].val then
        Result := vallist[i].txt;
    if (showvalue) then
      Result := toString(Data) + ' - ' + Result;
    if docutoff then
      Result := Copy(Result, 1, cutoff);
  end;

  function getValuesStr(typeindex, unitindex, optindex: integer;
    vallist: array of values; showvalue: boolean): string;
  begin
    Result := getValuesStr(typeindex, unitindex, optindex, vallist,
      False, 0, False, showvalue);
  end;

  function getValuesStr(typeindex, unitindex, optindex: integer;
    vallist: array of values; ignoreUnkn, showvalue: boolean): string;
  begin
    Result := getValuesStr(typeindex, unitindex, optindex, vallist,
      False, 0, ignoreUnkn, showvalue);
  end;

  function getValuesStr(typeindex, unitindex, optindex: integer;
    vallist: array of values; docutoff: boolean; cutoff: integer;
    showvalue: boolean): string;
  begin
    Result := getValuesStr(typeindex, unitindex, optindex, vallist,
      docutoff, cutoff, False, showvalue);
  end;

  function getPercentageStr(typeindex, unitindex, optindex: integer): string;
  var
    Data, percdata: integer;
  begin
    Data := getBytes(typeindex, unitindex, optindex);
    Result := toString(Data) + '/256';
    while length(Result) < 7 do
      Result := Result + ' ';
    percdata := Data * 100 + 49;
    percdata := percdata div 256;
    Result := Result + ' (' + toString(percdata) + '%)';
    // NO LONGER DISPLAYED - full info above is more useful for editing.
    // Full list is now only used for quick selection.
    //for i:=0 to high(percentages) do
    // if data=percentages[i].val then Result:=percentages[i].txt;
  end;

  function readPrerequisites(typeindex, unitindex, optindex: integer): boollist;
  var
    val, i, j: cardinal;
  begin
    for i := 0 to 31 do
      Result[i] := False;
    val := 0;
    for i := 0 to max(0, (GetDataLen(typeindex, optindex) - 1)) do
    begin
      val := readByte(getAddress(typeindex, unitindex, optindex) + i);
      for j := 0 to 7 do
      begin
        if (val mod 2) = 1 then
          Result[i * 8 + j] := True;
        val := val div 2;
      end;
    end;
  end;

  procedure writePrerequisites(p: boollist; typeindex, unitindex, optindex: integer);
  var
    modifier, i, j: cardinal;
    val: byte;
  begin
    val := 0;
    for i := 0 to max(0, (GetDataLen(typeindex, optindex) - 1)) do
    begin
      modifier := 1;
      val := 0;
      for j := 0 to 7 do
      begin
        if p[i * 8 + j] then
          val := val + modifier;
        modifier := modifier * 2;
      end;
      writeByte(getAddress(typeindex, unitindex, optindex) + i, val);
    end;
  end;

  // smart abbreviation shortens the structure strings to 4 characters.
  // Non-smart cuts off after 2.
  function getPrerequisitestr(typeindex, unitindex, optindex, basetype, cutoff: integer;
    shorten: boolean): string;
  var
    b: boollist;
    i, active: integer;
  begin
    b := readPrerequisites(typeindex, unitindex, optindex);
    Result := '';
    active := 0;
    for i := 0 to versions[gamever].dataInfo[basetype].listLen - 1 do
      if b[i] then
      begin
        if (active = 0) then
          Result := GetBasicTypeString(basetype, i) + ','
        else if (active = 1) then
        begin
          if shorten then
          begin
            if (cutoff <= 0) then
              Result :=
                smartAbbrev(copy(Result, 1, length(Result) - 1)) +
                ',' + smartAbbrev(GetBasicTypeString(basetype, i)) + ','
            else
              Result :=
                copy(Result, 1, cutoff) + ',' +
                copy(GetBasicTypeString(basetype, i), 1, cutoff) + ',';
          end
          else
            Result := Result + GetBasicTypeString(basetype, i) + ',';
        end
        else
        begin
          if shorten then
          begin
            if (cutoff = 0) then
              Result :=
                Result + smartAbbrev(GetBasicTypeString(basetype, i)) + ','
            else
              Result :=
                Result + copy(GetBasicTypeString(basetype, i), 1, cutoff) + ',';
          end
          else
            Result := Result + GetBasicTypeString(basetype, i) + ',';
        end;
        active := active + 1;
      end;
    if active > 0 then
    begin
      Result := copy(Result, 1, length(Result) - 1);
      if (shorten) and (length(Result) > 19) then
        Result := concat(copy(Result, 1, 17) + '..');
    end
    else
      Result := '(none)';
  end;

  function readSwitchBoolean(typeindex, unitindex, optindex, bitindex: integer): boolean;
  var
    val: byte;
    addr: integer;
  begin
    addr := getAddress(typeindex, unitindex, optindex);
    val := byte(readBytes(addr, 1));
    //val := readBytesInt(getAddress(typeindex, unitindex, optindex), 1);
    Result := ((val and (byte(1) shl bitindex)) > 0);
  end;

  procedure writeSwitchBoolean(Value: boolean;
    typeindex, unitindex, optindex, bitindex: integer);
  var
    val: byte;
  begin
    val := byte(readBytes(getAddress(typeindex, unitindex, optindex), 1));
    if Value then
      val := val or (word(1) shl bitindex)
    else
      val := val and not (word(1) shl bitindex);
    writeByte(getAddress(typeindex, unitindex, optindex), val);
  end;

  function getSwitchBooleanstr(typeindex, unitindex, optindex, bitindex:
    integer): string;
  var
    val: boolean;
  begin
    val := readSwitchBoolean(typeindex, unitindex, optindex, bitindex);
    if not HexDisplay then
      Result := booleans[word(val)].txt
    else
      Result := (toString(word(val)));
  end;


  // for all anim types
  function readAnimScriptRef(typeindex, unitindex, optindex: integer): cardinal;
  begin
    Result := readBytes(getAddress(typeindex, unitindex, optindex) + 2, 2);
  end;

  function GetAnimScriptLine(reference, line: cardinal): scriptLine;
  var
    scriptcode: cardinal;
    curbyte: byte;
  begin
    reference := (reference * $10) + versions[gamever].refHeader;
    scriptcode := readBytes(reference + (line * 2), 2);
    curbyte := getByte(scriptcode, 1);
    Result.command := curbyte div $10;
    Result.argument := ((curbyte mod $10) * $100) + getByte(scriptcode, 0);
    if Result.argument > $800 then
      Result.argument := ($1000 - Result.argument) * -1;
  end;

  function getAnimScriptEnd(reference: integer;
    scriptcodes: array of scriptvalues): integer;
  var
    lineNr: integer;
    animScript: scriptLine;
  begin
    if reference = 0 then
      Result := -1
    else
    begin
      lineNr := 0;
      repeat
        animScript := GetAnimScriptLine(reference, lineNr);
        lineNr := lineNr + 1;
      until ((animScript.command > scriptcodes[high(scriptcodes)].val) or
          (scriptcodes[animScript.Command].isEnd));
      Result := lineNr - 1;
    end;
  end;

  function getAnimScriptEnd(typeindex, unitindex, optindex: integer;
    scriptcodes: array of scriptvalues): integer;
  begin
    Result :=
      getAnimScriptEnd(readAnimScriptRef(typeindex, unitindex, optindex), scriptcodes);
  end;

  function readAnimScriptLineStr(reference, line: cardinal;
    scriptcodes: array of scriptvalues; shortdesc: boolean): string;
  var
    animScript: scriptLine;
  begin
    if reference = 0 then
      Result := ''
    else
    begin
      animScript := GetAnimScriptLine(reference, line);
      if (shortdesc) then
        Result := scriptcodes[animScript.command].shorttxt
      else
        Result := scriptcodes[animScript.command].longtxt + '   ';
      if scriptcodes[animScript.command].hasArg then
        Result :=
          Result + toString(abs(animScript.argument));
    end;
  end;

  function readAnimScriptLineStr(typeindex, unitindex, optindex, line: integer;
    scriptcodes: array of scriptvalues; shortdesc: boolean): string;
  begin
    Result :=
      readAnimScriptLineStr(readAnimScriptRef(typeindex, unitindex, optindex),
      line, scriptcodes, shortdesc);
  end;

  function getAnimScriptStr(typeindex, unitindex, optindex: integer;
    scriptcodes: array of scriptvalues): string;
  var
    i, scriptEnd: integer;
    scriptLine: string;
  begin
    Result := '';
    scriptEnd := getAnimScriptEnd(typeindex, unitindex, optindex, scriptcodes);
    scriptLine := '';
    i := 0;
    repeat
      scriptLine := readAnimScriptLineStr(typeindex, unitindex, optindex,
        i, scriptcodes, True);
      i := i + 1;
      if (scriptLine <> '') then
        Result := Result + scriptLine + ' ';
    until (i > scriptEnd) or (scriptLine = '');
    if (Result = '') then
      Result := 'None';
  end;

  function getDataStr(typeindex, unitindex, optindex: integer; shorten: boolean): string;
  begin
    Result := '';
    // DATA TYPE LIST - Gets correct strings to display in second column of 3rd list
    case mainlist[typeindex].optsList^[optindex].typeid of
      // 4-byte (INT)
      0: Result := toString(getBytesSigned(typeindex, unitindex, optindex));
      // signed byte
      1: Result := toString(getBytesSigned(typeindex, unitindex, optindex));
      // 2-byte
      2: Result := toString(getBytesSigned(typeindex, unitindex, optindex));
      // 4-byte (REF)
      3: Result := getRefString(typeindex, unitindex, optindex);
      // owners
      4: Result := getPrerequisitestr(typeindex, unitindex, optindex, 2, 2, shorten);
      // command
      5: Result := GetBasicTypeString(typeindex, unitindex, optindex, 4);
      // movement type
      6: Result := GetBasicTypeString(typeindex, unitindex, optindex, -1);
      // weapon type
      7: Result := GetBasicTypeString(typeindex, unitindex, optindex, 0);
      // foundation
      8: Result := getValuesStr(typeindex, unitindex, optindex,
          foundations, not shorten);
      // decay factor
      9: Result := getValuesStr(typeindex, unitindex, optindex,
          decayFactors, True, False);
      // prerequisites
      10: Result := getPrerequisitestr(typeindex, unitindex, optindex, 1, 0, shorten);
      // sound
      11: Result := getValuesStr(typeindex, unitindex, optindex, sounds, not shorten);
      // bit switch 1/8
      12: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 0);
      // bit switch 2/8
      13: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 1);
      // bit switch 3/8
      14: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 2);
      // bit switch 4/8
      15: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 3);
      // bit switch 5/8
      16: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 4);
      // bit switch 6/8
      17: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 5);
      // bit switch 7/8
      18: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 6);
      // bit switch 8/8
      19: Result := getSwitchBooleanstr(typeindex, unitindex, optindex, 7);
      // percentage
      20: Result := getPercentageStr(typeindex, unitindex, optindex);
      // music
      21: Result := GetBasicTypeString(typeindex, unitindex, optindex, 6);
      // character
      22: Result := char(getBytes(typeindex, unitindex, optindex, 1));
      // palace special ability
      23: Result := getValuesStr(typeindex, unitindex, optindex, PalaceSpecials, False);
      // SHP Graphics
      24: Result := GetGraphicsString(typeindex, unitindex, optindex,
          False, 0, False, not shorten);
      // unit display mode
      25: Result := getValuesStr(typeindex, unitindex, optindex, displayModes, False);
      // action interrupt mode
      26: Result := getValuesStr(typeindex, unitindex, optindex,
          interruptModes, not shorten);
      // sidebar mode
      27: Result := getValuesStr(typeindex, unitindex, optindex,
          sidebarmodes, shorten, 17, False);
      // voc sound
      28: Result := GetBasicTypeString(typeindex, unitindex, optindex, 5);
      // parent PAK
      29: Result := GetBasicTypeString(typeindex, unitindex, optindex, 7);
      // bit list of allowed units
      30: Result := getPrerequisitestr(typeindex, unitindex, optindex, 0, 0, shorten);
      // structure animation
      31: Result := getAnimScriptStr(typeindex, unitindex, optindex, structAnim);
      // structure graphics set
      32: Result := getValuesStr(typeindex, unitindex, optindex,
          structureGraphics, not shorten);
      // unsigned byte
      33: Result := tostring(getBytes(typeindex, unitindex, optindex));
      // boolean
      34: Result := getValuesStr(typeindex, unitindex, optindex,
          booleans, False, 0, False);
      // percentage (1-byte)
      35: Result := getPercentageStr(typeindex, unitindex, optindex);
      // Crater type
      36: Result := getValuesStr(typeindex, unitindex, optindex,
          craterTypes, False, 0, False);
    end;
    //lev3data normally contains strings of 20 chars, but in the current display mode I use 19.
    if shorten then
      Result := Copy(getDataStr, 1, 19);
  end;

  function getDataStr(typeindex, unitindex, optindex: integer): string;
  begin
    Result := getDataStr(typeindex, unitindex, optindex, True);
  end;

  function inputValue(inputstr: string; minval: integer; maxval: cardinal;
    nrofbytes: byte): cardinal;
  var
    keyread0, charin: char;
    minlen, maxlen, boundaryCheck: integer;
    resultSigned: integer;
    tempstr: string;
  begin
    keyread0 := char(255);
    charin := char(0);
    str(minval, tempstr);
    minlen := Length(tempstr);
    str(maxval, tempstr);
    maxlen := Length(tempstr);
    Write(inputstr);
    while ((charin <> keymap.esc) and (charin <> keymap.enter) and
        not ((keyread0 = char(0)) and (charin = keymap.F2)) and
        (charin <> 'b') and not ((keyread0 = char(0)) and (charin = keymap.F3)) and
        (charin <> 'x') and not ((keyread0 = char(0)) and (charin = keymap.F4)) and
        (charin <> 'd') and not ((keyread0 = char(0)) and (charin = keymap.F5)) and
        (charin <> 'D')) do
    begin
      keyread0 := char(255);
      charin := ReadKey();
      while keypressed do
      begin
        keyread0 := charin;
        charin := ReadKey();
      end;

      if ((byte(charin) = byte('-')) and (Length(inputstr) = 0) and (minval < 0)) or
        ((byte(charin) >= $30) and (byte(charin) <= $39) and
        (((Length(inputstr) < minlen) and (Copy(inputstr, 1, 1) = '-')) or
        ((Length(inputstr) < maxlen) and (Copy(inputstr, 1, 1) <> '-')))) then
      begin
        inputstr := inputstr + charin;
        Write(charin);
      end
      else if (charin = keymap.back) and (Length(inputstr) > 0) then
      begin
        inputstr := Copy(inputstr, 1, (Length(inputstr) - 1));
        Write(keymap.back);
        Write('_');
        Write(keymap.back);
      end;
    end;
    if (minval < 0) then
    begin
      val(inputstr, resultSigned);
      Result := convertToUnsigned(resultSigned, nrofbytes);
    end
    else
      val(inputstr, Result);
    errormsg := '';
    if ((charin = keymap.esc) or ((keyread0 = char(0)) and (charin = keymap.F2)) or
      (charin = 'b') or ((keyread0 = char(0)) and (charin = keymap.F3)) or
      (charin = 'x') or ((keyread0 = char(0)) and (charin = keymap.F4)) or
      (charin = 'd') or ((keyread0 = char(0)) and (charin = keymap.F5)) or
      (charin = 'D')) then
      errormsg := 'Canceled'
    else if ((minval < 0) and (resultSigned > abs(maxval))) or
      ((minval >= 0) and (Result > maxval)) then
      errormsg := 'Value too large!'
    else if ((minval < 0) and (resultSigned < minval)) or
      ((minval >= 0) and (abs(Result) < minval)) then
      errormsg := 'Value too small!';
    // test on overflow beyond the value range
    if ((errormsg = '') and (Length(inputstr) > 0)) then
    begin
      charin := inputstr[1];
      if ((Length(inputstr) = maxlen) and (charin <> '-')) then
      begin
        tempstr := Copy(inputstr, 1, 1);
        val(tempstr, resultSigned);
        str(maxval, tempstr);
        tempstr := Copy(tempstr, 1, 1);
        val(tempstr, boundaryCheck);
        if (resultSigned > boundaryCheck) then
          errormsg := 'Value too large!'
        else if (resultSigned = boundaryCheck) then
        begin
          tempstr := Copy(inputstr, 2, Length(inputstr) - 1);
          val(tempstr, resultSigned);
          str(maxval, tempstr);
          tempstr := Copy(tempstr, 2, Length(tempstr) - 1);
          val(tempstr, boundaryCheck);
          if (resultSigned > boundaryCheck) then
            errormsg := 'Value too large!';
        end;
      end
      else if ((minval < 0) and (charin = '-') and
        (Length(inputstr) = minlen)) then
      begin
        tempstr := Copy(inputstr, 2, 1);
        val(tempstr, resultSigned);
        str(minval, tempstr);
        tempstr := Copy(tempstr, 2, 1);
        val(tempstr, boundaryCheck);
        if (resultSigned > boundaryCheck) then
          errormsg := 'Value too small!'
        else if (resultSigned = boundaryCheck) then
        begin
          tempstr := Copy(inputstr, 3, Length(inputstr) - 2);
          val(tempstr, resultSigned);
          str(maxval, tempstr);
          tempstr := Copy(tempstr, 3, Length(tempstr) - 2);
          val(tempstr, boundaryCheck);
          if (resultSigned > boundaryCheck) then
            errormsg := 'Value too small!';
        end;
      end;
    end;
  end; {inputValue}

  function inputChar(input: char): char;
  var
    keyread0, charin: char;
  begin
    charin := input;
    Result := input;
    keyread0 := char(255);
    Write(charin);
    while ((charin <> keymap.esc) and (charin <> keymap.enter) and
        not ((keyread0 = char(0)) and
        ((charin = keymap.F2) or
        (charin = keymap.F3) or
        (charin = keymap.F4) or
        (charin = keymap.F5)))) do
    begin
      keyread0 := char(255);
      charin := ReadKey();
      while keypressed do
      begin
        keyread0 := charin;
        charin := ReadKey();
      end;
      if ((byte(charin) >= 65) and (byte(charin) <= 90)) or
        ((byte(charin) >= 97) and (byte(charin) <= 122)) then
      begin
        Write(keymap.back);
        Write(charin);
        Result := charin;
      end;
    end;
    if ((charin = keymap.esc) or ((keyread0 = char(0)) and
      (charin = keymap.F2)) or ((keyread0 = char(0)) and (charin = keymap.F3)) or
      ((keyread0 = char(0)) and (charin = keymap.F4)) or
      ((keyread0 = char(0)) and (charin = keymap.F5))) then
      errormsg := 'Canceled'
    else
      errormsg := '';
  end; {inputChar}

  function inputHexBytes(inputstr: string; nrofbytes: byte): cardinal;
  var
    keyread0, charin: char;
    i: integer;
  begin
    Result := 0;
    nrofbytes := min(4, nrofbytes);
    nrofbytes := max(1, nrofbytes);
    for i := 1 to length(inputstr) do
    begin
      Write(inputstr[i]);
      if (i <> 0) and (i mod 2 = 0) and (i < (length(inputstr))) then
        Write(' ');
    end;
    charin := char(0);
    keyread0 := char(255);
    while ((charin <> keymap.esc) and (charin <> 'x') and
        (charin <> keymap.enter) and not ((keyread0 = char(0)) and
        ((charin = keymap.F2) or
        (charin = keymap.F3) or
        (charin = keymap.F4) or
        (charin = keymap.F5)))) do
    begin
      keyread0 := char(255);
      charin := ReadKey();
      while keypressed do
      begin
        keyread0 := charin;
        charin := ReadKey();
      end;
      if (byte(charin) >= 97) and (byte(charin) <= 102) then
        charin := char(byte(charin) - 32);
      if (((byte(charin) >= 48) and (byte(charin) <= 57)) or
        ((byte(charin) >= 65) and (byte(charin) <= 70))) and
        (length(inputstr) < (nrofbytes * 2)) then
      begin
        inputstr := inputstr + charin;
        Write(charin);
        if (length(inputstr) > 0) and ((length(inputstr) mod 2) = 0) and
          (length(inputstr) < (nrofbytes * 2)) then
          Write(' ');
      end
      else if (charin = keymap.back) and (Length(inputstr) > 0) then
      begin
        if ((length(inputstr) mod 2) = 0) and (length(inputstr) < (nrofbytes * 2)) then
          Write(keymap.back);
        inputstr := Copy(inputstr, 1, (Length(inputstr) - 1));
        Write(keymap.back);
        Write('_');
        Write(keymap.back);
      end;
    end;
    while (length(inputstr) < (nrofbytes * 2)) do
    begin
      inputstr := inputstr + '0';
      Write('0');
      if (length(inputstr) > 0) and ((length(inputstr) mod 2) = 0) then
        Write(' ');
    end;
    while (length(inputstr) < (4 * 2)) do
      inputstr := '0' + inputstr;
    for i := 0 to 3 do
    begin
      Result := Result * $100 + hexstr2byte(copy(inputstr, 1 + i * 2, 2));
    end;
    if ((charin = keymap.esc) or (charin = 'x') or
      ((keyread0 = char(0)) and (charin = keymap.F2)) or
      ((keyread0 = char(0)) and (charin = keymap.F3)) or
      ((keyread0 = char(0)) and (charin = keymap.F4)) or
      ((keyread0 = char(0)) and (charin = keymap.F5))) then
      errormsg := 'Canceled'
    else
      errormsg := '';
  end;

  // =========================================
  //              GENERAL LAYOUT
  // =========================================

  procedure TextCol(lev, i: integer);
  begin
    if (i = select[lev]) and (level = lev) then
    begin
      TextColor(activeitemcolor);
      TextBackground(activeitembgcolor);
    end
    else if (i = select[lev]) and (level <> lev) then
    begin
      TextColor(inactiveitemcolor);
      TextBackground(inactiveitembgcolor);
    end
    else if (i <> select[lev]) and (level = lev) then
    begin
      TextColor(activelevcolor);
      TextBackground(activelevbgcolor);
    end
    else if (i <> select[lev]) and (level <> lev) then
    begin
      TextColor(inactivelevcolor);
      TextBackground(inactivelevbgcolor);
    end;
  end;

  procedure ResetCol();
  begin
    TextColor(activelevcolor);
    TextBackground(activelevbgcolor);
  end;

  procedure drawLineV(x, y, len: integer; c: char);
  var
    Count: integer;
  begin
    for Count := 0 to (len - 1) do
    begin
      gotoxy(x, y + Count);
      Write(c);
    end;
  end;

  procedure drawLineH(x, y, len: integer; c: char);
  var
    Count: integer;
  begin
    gotoxy(x, y);
    for Count := 0 to len - 1 do
      Write(c);
  end;

  procedure clearBlock(x, y, w, h: integer);
  var
    Count: integer;
  begin
    for Count := 0 to h - 1 do
    begin
      gotoxy(x, y + Count);
      drawLineH(x, y + Count, w, ' ');
    end;
  end;

  // x-coord, y-coord, width, height, line in middle, draw as active frame, clear inside
  procedure drawFrame(x, y, w, h, m: integer; active, fill: boolean);
  var
    charset: integer;
  begin
    if active then
      charset := 2
    else
      charset := 1;
    gotoxy(x, y);
    Write(char(framechar^[charset, 1]));
    gotoxy(x + w - 1, y);
    Write(char(framechar^[charset, 2]));
    gotoxy(x, y + h - 1);
    Write(char(framechar^[charset, 3]));
    gotoxy(x + w - 1, y + h - 1);
    Write(char(framechar^[charset, 4]));
    drawLineH(x + 1, y, w - 2, char(framechar^[charset, 5]));
    drawLineH(x + 1, y + h - 1, w - 2, char(framechar^[charset, 5]));
    drawlineV(x, y + 1, h - 2, char(framechar^[charset, 6]));
    drawlineV(x + w - 1, y + 1, h - 2, char(framechar^[charset, 6]));
    if fill then
      clearBlock(x + 1, y + 1, w - 2, h - 2);
    if (m > 1) then
    begin
      gotoxy(x + m - 1, y);
      Write(char(framechar^[charset, 7]));
      gotoxy(x + m - 1, y + h - 1);
      Write(char(framechar^[charset, 8]));
      drawLineV(x + m - 1, y + 1, h - 2, char(framechar^[charset, 6]));
    end;
    resetCursor();
  end; {drawFrame}

  procedure drawFrameArrows(l: integer);
  var
    x, y1, y2, charset: integer;
    up, down: boolean;
  begin
    x := selectBoundaries[l][1] - 1;
    y1 := selectBoundaries[l][2];
    y2 := selectBoundaries[l][4];
    // Is scrolled down
    up := scroll[l] > 0;
    // Can scroll && hasn't scrolled to maximum
    down := (maxdisplay[l] < listlen[l]) and (scroll[l] < (listlen[l] - maxdisplay[l]));
    if level = l then
    begin
      charset := 2;
      TextColor(activelevcolor);
    end
    else
    begin
      charset := 1;
      TextColor(inactivelevcolor);
    end;
    gotoxy(x, y1);
    if up then
      Write(char(framechar^[charset, 9]))
    else
      Write(' ');
    //gotoxy(1,1); write('writing down arrow');
    gotoxy(x, y2);
    if down then
      Write(char(framechar^[charset, 10]))
    else
      Write(' ');
    resetCol();
    resetCursor();
  end;


  procedure drawHeader();
  begin
    gotoxy(1, 1);
    drawLineH(1, 1, 80, char(framechar^[2, 5]));
    gotoxy(3, 1);
    Write(' ');
    Write(editorname);
    Write(' ');
    Write(editorversion);
    Write(' ');
    gotoxy(74 - length(editorauthor), 1);
    Write(' by ');
    Write(editorauthor);
    Write(' ');
  end;

  procedure drawHeader2();
  begin
    gotoxy(2, 2);
    TextColor(inactivelevcolor);
    Write('File: [');
    if length(gamenamestr) > 40 then
      Write(copy(gamenamestr, 1, 40))
    else
      Write(gamenamestr);
    Write(']');
    gotoxy(max(1, (80 - 14 - length(versions[gamever].nameShort))), 2);
    Write('Dune ');
    TextColor(activelevcolor);
    Write('V');
    TextColor(inactivelevcolor);
    Write('ersion: ' + versions[gamever].nameShort);
  end;

  procedure drawFooter();
  begin
    gotoxy(2, 25);
    TextColor(inactivelevcolor);
    Write('F1=');
    TextColor(activelevcolor);
    Write('h');
    TextColor(inactivelevcolor);
    Write('elp');

    gotoxy(10, 25);
    Write('F2/F3/F4/F5=');
    TextColor(activelevcolor);
    Write('b');
    TextColor(inactivelevcolor);
    Write('in/he');
    TextColor(activelevcolor);
    Write('x');
    TextColor(inactivelevcolor);
    Write('/[');
    TextColor(activelevcolor);
    Write('d');
    TextColor(inactivelevcolor);
    Write('/');
    TextColor(activelevcolor);
    Write('D');
    TextColor(inactivelevcolor);
    Write(']ec');
    gotoxy(38, 25);
    Write('F7/F8=');
    TextColor(activelevcolor);
    Write('s');
    TextColor(inactivelevcolor);
    Write('ave/');
    TextColor(activelevcolor);
    Write('l');
    TextColor(inactivelevcolor);
    Write('oad ini');
    gotoxy(59, 25);
    Write('F10=i');
    TextColor(activelevcolor);
    Write('n');
    TextColor(inactivelevcolor);
    Write('i dump');
    gotoxy(72, 25);
    Write('F12=');
    TextColor(activelevcolor);
    Write('i');
    TextColor(inactivelevcolor);
    Write('nfo');
  end;

  procedure drawHeaders();
  begin
    TextBackground(inactivelevbgcolor);
    TextColor(inactivelevcolor);
    drawHeader();
    drawHeader2();
    drawFooter();
  end;

  // =========================================
  //             SELECTION DISPLAY
  // =========================================

  function calcSelectBin(len: integer): integer;
  var
    i: integer;
  begin
    Result := len;
    for i := 0 to (len) do
    begin
      if (i mod 4 = 0) and (i <> 0) then
        Result := calcSelectBin + 1;
      if (i mod 8 = 0) and (i <> 0) then
        Result := calcSelectBin + 2;
    end;
  end;

  procedure drawSelectBin();
  var
    i: integer;
  begin
    gotoxy(selectBoundaries[4][1] + 1, selectBoundaries[4][2] - 2);
    for i := 0 to calcSelectBin(listlen[4]) do
      Write(' ');
    gotoxy(selectBoundaries[4][1] + 1 + calcSelectBin(listlen[4]) -
      calcSelectBin(select[4] + scroll[4]), selectBoundaries[4][2] - 2);
    Write(char(framechar^[2, 10]));
    gotoxy(selectBoundaries[4][1] + 1, selectBoundaries[4][2]);
    for i := 0 to calcSelectBin(listlen[4]) do
      Write(' ');
    gotoxy(selectBoundaries[4][1] + 1 + calcSelectBin(listlen[4]) -
      calcSelectBin(select[4] + scroll[4]), selectBoundaries[4][2]);
    Write(char(framechar^[2, 9]));
    resetCursor();
  end;

  procedure drawSel(l, pos: integer);
  begin
    if maxdisplay[l] >= 0 then
    begin
      TextCol(l, pos);
      case l of
        1: drawLineH(selectBoundaries[l][1], selectBoundaries[l][2] +
            pos, selectionLengths[l], ' ');
        2: drawLineH(selectBoundaries[l][1], selectBoundaries[l][2] +
            pos, selectionLengths[l], ' ');
        3: drawLineH(selectBoundaries[l][1], selectBoundaries[l][2] +
            pos, selectionLengths[l], ' ');
        4: drawLineH(selectBoundaries[l][1], selectBoundaries[l][2] +
            pos, selectionLengths[l], ' ');
      end;
      gotoxy(selectBoundaries[l][1] + 1, selectBoundaries[l][2] + pos);
      case l of
        1: Write(mainlist[(pos + scroll[l])].typeName);
        2: Write(lev2List[(pos + scroll[l])]);
        3:
        begin
          Write(lev3List^[(pos + scroll[l])].Text);
          gotoxy(selectBoundaries[3][1] + secondcolumn[3] - 1,
            selectBoundaries[3][2] + pos);
          drawLineH(selectBoundaries[3][1] + secondcolumn[3] - 1,
            selectBoundaries[3][2] + pos, secondcolumnSelection, ' '); // second line
          gotoxy(selectBoundaries[3][1] + secondcolumn[3], selectBoundaries[3][2] + pos);
          Write(lev3data[pos]);
        end;
        4: if (listlen[4] >= 0) then
            Write(lev4List[(pos + scroll[l])]);
      end;
      ResetCol();
      resetCursor();
    end;
  end;

  procedure drawSelect(l, pos: integer);
  begin
    if binEd and (level = 4) then
      drawSelectBin()
    else
      drawSel(l, pos);
  end;

  procedure drawSelect(lev: integer);
  begin
    if binEd and (level = 4) then
      drawSelectBin()
    else
      drawSel(lev, select[lev]);
  end;

  procedure drawSelect();
  begin
    if binEd and (level = 4) then
      drawSelectBin()
    else
      drawSel(level, select[level]);
  end;

  procedure drawSelects();
  var
    i: integer;
  begin
    if level <> 4 then
      for i := 1 to 3 do
        drawSelect(i)
    else
      drawSelect(4);
  end;


  // =========================================
  //                 LIST DISPLAY
  // =========================================

  procedure makeValuesList(typeindex, unitindex, optindex: integer;
    vallist: array of values);
  var
    val, i: integer;
    datalen: byte;
  begin
    datalen := getDataLen(typeindex, optindex);
    if ((datalen < 4) and (1 shl ((8 * datalen) - 1) <= vallist[high(vallist)].val)) then
      val := getBytes(typeindex, unitindex, optindex)
    else
      val := getBytesSigned(typeindex, unitindex, optindex);
    listlen[4] := high(vallist);
    for i := 0 to listlen[4] do
    begin
      if val = vallist[i].val then
        lev4List[i] := '* ' + uppercase(vallist[i].txt)
      else
        lev4List[i] := '  ' + vallist[i].txt;
    end;
  end;

  procedure MakePrerequisitesList(typeindex, unitindex, optindex, basetype: integer);
  var
    i: integer;
    o: boollist;
  begin
    o := readPrerequisites(typeindex, unitindex, optindex);
    listlen[4] := versions[gamever].dataInfo[basetype].listLen - 1;
    for i := 0 to listlen[4] do
    begin
      if o[i] then
        lev4List[i] := '+ ' + uppercase(GetBasicTypeString(basetype, i))
      else
        lev4List[i] := '  ' + GetBasicTypeString(basetype, i);
    end;
  end;

  procedure makeBasicTypeList(typeIndex, unitindex, optindex, basetype: integer;
    HasEmpty: boolean);
  var
    val, i: integer;
  begin
    val := getBytesSigned(typeIndex, unitindex, optindex);
    listlen[4] := versions[gamever].dataInfo[basetype].listLen - byte(not HasEmpty);
    if HasEmpty then
    begin
      lev4List[0] := 'None';
      if (val = -1) then
        lev4List[0] := '* ' + uppercase(lev4List[0])
      else
        lev4List[0] := '  ' + lev4List[0];
    end;
    for i := 0 to listlen[4] do
    begin
      if (i = val) then
        lev4List[i + byte(HasEmpty)] := '* ' + uppercase(GetBasicTypeString(basetype, i))
      else
        lev4List[i + byte(HasEmpty)] := '  ' + GetBasicTypeString(basetype, i);
    end;
  end;

  procedure makeBasicTypeList(typeIndex, unitindex, optindex, basetype: integer);
  begin
    makeBasicTypeList(typeIndex, unitindex, optindex, basetype, True);
  end;

  procedure makePAKList(typeIndex, unitindex, optindex: integer);
  var
    val, i: integer;
  begin
    val := getBytesSigned(typeIndex, unitindex, optindex);
    listlen[4] := versions[gamever].dataInfo[4].listLen - 1;
    i := 0;
    while i < listlen[4] do
    begin
      //    if (readBytesInt(getAddress(4,i,1),GetDataLen(4,1))=0) // check filesize value
      if (pos('.PAK', uppercase(GetBasicTypeString(7, i))) = 0)
      // check filename extension
      then
        listlen[4] := i - 1;
      i := i + 1;
    end;
    // one byte value!
    if listlen[4] > $FF then
      listlen[4] := $FF;
    for i := 0 to listlen[4] do
    begin
      if (i = val) then
        lev4List[i] := '* ' + uppercase(GetBasicTypeString(7, i))
      else
        lev4List[i] := '  ' + lowercase(GetBasicTypeString(7, i));
    end;
  end;

  procedure makeGraphicsList(typeindex, unitindex, optindex: integer);
  var
    val, i: integer;
  begin
    val := getBytesSigned(typeindex, unitindex, optindex);
    listlen[4] := high(versions[gamever].Graphics^);
    while (versions[gamever].Graphics^[listlen[4]] = '') do
      listlen[4] := listlen[4] - 1;
    // +1 for None value
    listlen[4] := listlen[4] + 1;
    lev4List[0] := 'None';
    if (val = -1) then
      lev4List[0] := ' -1 * ' + uppercase(lev4List[0])
    else
      lev4List[0] := ' -1   ' + lev4List[0];
    for i := 0 to listlen[4] - 1 do
    begin
      if (i = val) then
        lev4List[i + 1] := tostring(i) + ' * ' +
          uppercase(versions[gamever].Graphics^[i])
      else
        lev4List[i + 1] := tostring(i) + '   ' + versions[gamever].Graphics^[i];
      if (i < 100) then
        lev4List[i + 1] := '0' + lev4List[i + 1];
      if (i < 10) then
        lev4List[i + 1] := '0' + lev4List[i + 1];
    end;
  end;

  procedure makeAnimScriptList(typeindex, unitindex, optindex: integer;
    scriptcodes: array of scriptvalues);
  var
    address, i: cardinal;
    animScript: scriptLine;
  begin
    address := readAnimScriptRef(typeindex, unitindex, optindex);
    listlen[4] := getAnimScriptEnd(typeindex, unitindex, optindex, scriptcodes);
    for i := 0 to listlen[4] do
    begin
      lev4List[i] := readAnimScriptLineStr(address, i, scriptcodes, False);
    end;
    // For maybe allowing script editing later
    repeat
      i := i + 1;
      animScript := GetAnimScriptLine(address, i);
      if ((animScript.command = defaultEmptyAnimScriptLine.command) and
        (animScript.argument = defaultEmptyAnimScriptLine.argument)) then
      begin
        listlen[4] := i;
        lev4List[i] := '[Available script space]';
      end;
    until ((animScript.command <> defaultEmptyAnimScriptLine.command) or
        (animScript.argument <> defaultEmptyAnimScriptLine.argument));
    if (listlen[4] = -1) then
    begin
      lev4List[0] := '[No script actions]';
      listlen[4] := 0;
    end;
  end;

  procedure makeSwitchBooleanList(typeindex, unitindex, optindex, bitindex: integer);
  var
    b: boolean;
    i: integer;
  begin
    b := readSwitchBoolean(typeindex, unitindex, optindex, bitindex);
    maxdisplay[4] := 1;
    listlen[4] := 1;
    for i := 0 to listlen[4] do
    begin
      if integer(b) = booleans[i].val then
        lev4List[i] := uppercase(booleans[i].txt)
      else
        lev4List[i] := booleans[i].txt;
    end;
  end;

  procedure drawBinList(typeindex, unitindex, optindex: integer);
  begin
    gotoxy(selectBoundaries[4][1] + 1, selectBoundaries[4][2] - 1);
    Write(getBinString(typeindex, unitindex, optindex, True));
    gotoxy(selectBoundaries[4][1] + 11, selectBoundaries[4][2] + 2);
    Write(getBytesString(typeindex, unitindex, optindex, True));
    resetCursor();
    ResetCol();
  end;

  // if maxdisplay[4] is -1 this function does nothing
  // This function also adjusts the listlen if the list contains less items than the full length,
  procedure drawLev4Select();
  var
    i, last: integer;
  begin
    if listlen[4] < maxdisplay[4] then
      last := listlen[4]
    else
      last := maxdisplay[4];
    for i := 0 to last do
    begin
      TextCol(4, i);
      drawLineH(selectBoundaries[4][1], selectBoundaries[4][2] + i,
        selectionLengths[4], ' ');
      gotoxy(selectBoundaries[4][1] + 1, selectBoundaries[4][2] + i);
      Write(lev4List[(i + scroll[4])]);
    end;
    ResetCol();
    drawFrameArrows(4); // to make sure arrows appear after automatic scroll
    //resetCursor();
  end;


  procedure drawLev4();
  var
    typeindex, unitindex, optindex: integer;
  begin
    typeindex := select[1] + scroll[1];
    unitindex := select[2] + scroll[2];
    optindex := select[3] + scroll[3];
    if not binEd then
    begin
      // DATA TYPE LIST - Creates level 4 list for each data type
      // Fills the listlen[4] and lev4List variables.
      case mainlist[typeindex].OptsList^[optindex].typeid of
        // 4-byte (INT)
        0: listlen[4] := -1;
        // signed byte
        1: listlen[4] := -1;
        // 2-byte
        2: listlen[4] := -1;
        // 4-byte (REF)
        3: listlen[4] := -1;
        // owners
        4: MakePrerequisitesList(typeindex, unitindex, optindex, 2);
        // command
        5: makeBasicTypeList(typeindex, unitindex, optindex, 4);
        // movement type
        6: makeBasicTypeList(typeindex, unitindex, optindex, -1, False);
        // weapon type
        7: makeBasicTypeList(typeindex, unitindex, optindex, 0);
        // foundation
        8: makeValuesList(typeindex, unitindex, optindex, foundations);
        // decay factor
        9: makeValuesList(typeindex, unitindex, optindex, decayFactors);
        // prerequisites
        10: makePrerequisitesList(typeindex, unitindex, optindex, 1);
        // sound
        11: makeValuesList(typeindex, unitindex, optindex, sounds);
        // bit switch 1/8
        12: makeSwitchBooleanList(typeindex, unitindex, optindex, 0);
        // bit switch 2/8
        13: makeSwitchBooleanList(typeindex, unitindex, optindex, 1);
        // bit switch 3/8
        14: makeSwitchBooleanList(typeindex, unitindex, optindex, 2);
        // bit switch 4/8
        15: makeSwitchBooleanList(typeindex, unitindex, optindex, 3);
        // bit switch 5/8
        16: makeSwitchBooleanList(typeindex, unitindex, optindex, 4);
        // bit switch 6/8
        17: makeSwitchBooleanList(typeindex, unitindex, optindex, 5);
        // bit switch 7/8
        18: makeSwitchBooleanList(typeindex, unitindex, optindex, 6);
        // bit switch 8/8
        19: makeSwitchBooleanList(typeindex, unitindex, optindex, 7);
        // percentage (2-byte)
        20: makeValuesList(typeindex, unitindex, optindex, percentages);
        // music
        21: makeBasicTypeList(typeindex, unitindex, optindex, 6);
        // character
        22: listlen[4] := -1;
        // palace special ability
        23: makeValuesList(typeindex, unitindex, optindex, palaceSpecials);
        // SHP Graphics
        24: makeGraphicsList(typeindex, unitindex, optindex);
        // unit display mode
        25: makeValuesList(typeindex, unitindex, optindex, displayModes);
        // action interrupt mode
        26: makeValuesList(typeindex, unitindex, optindex, interruptModes);
        // sidebar mode
        27: makeValuesList(typeindex, unitindex, optindex, sidebarModes);
        // voc sound
        28: makeBasicTypeList(typeindex, unitindex, optindex, 5);
        // parent PAK
        29: makePAKList(typeindex, unitindex, optindex);
        // bit list of allowed units
        30: makePrerequisitesList(typeindex, unitindex, optindex, 0);
        // structure animation
        31: makeAnimScriptList(typeindex, unitindex, optindex, structAnim);
        // structure graphics set
        32: makeValuesList(typeindex, unitindex, optindex, structureGraphics);
        // unsigned byte
        33: listlen[4] := -1;
        // boolean
        34: makeValuesList(typeindex, unitindex, optindex, booleans);
        // percentage (1-byte)
        35: makeValuesList(typeindex, unitindex, optindex, percentages);
        // Crater type
        36: makeValuesList(typeindex, unitindex, optindex, craterTypes);
      end;
      maxdisplay[4] := selectBoundaries[4][1] - selectBoundaries[4][2];
      if listlen[4] < maxdisplay[4] then
        maxdisplay[4] := listlen[4];
      drawLev4Select();
    end
    else
      drawBinList(typeindex, unitindex, optindex);
  end;

  procedure drawLev3data();
  var
    i, last: integer;
    //    showlist:array [0..19] of String[20];
  begin
    if listlen[3] < maxdisplay[3] then
      last := listlen[3]
    else
      last := maxdisplay[3];
    for i := 0 to last do
    begin
      TextCol(3, i);
      if not HexDisplay then
        lev3data[i] := getDataStr(select[1] + scroll[1], select[2] +
          scroll[2], i + scroll[3])
      else
      begin
        if (lev3list^[i + scroll[3]].typeid >= 12) and
          (lev3list^[i + scroll[3]].typeid <= 19) then
          lev3data[i] := getDataStr(select[1] + scroll[1], select[2] +
            scroll[2], i + scroll[3])
        else
          lev3data[i] := getBytesString(select[1] + scroll[1],
            select[2] + scroll[2], i + scroll[3], True);
      end;
    end;
    for i := 0 to last do
    begin
      TextCol(3, i);
      drawLineH(selectBoundaries[3][1] + secondcolumn[3] - 1,
        selectBoundaries[3][2] + i, secondcolumnSelection, ' ');
      gotoxy(selectBoundaries[3][1] + secondcolumn[3], selectBoundaries[3][2] + i);
      Write(lev3data[i]);
    end;
    ResetCol();
    if last < (selectBoundaries[3][4] - selectBoundaries[3][2]) then
      for i := last + 1 to (selectBoundaries[3][4] - selectBoundaries[3][2]) do
      begin
        drawLineH(selectBoundaries[3][1] + secondcolumn[3] - 1,
          selectBoundaries[3]  [2] + i, secondcolumnSelection, ' ');
        gotoxy(selectBoundaries[3][1] + 1, selectBoundaries[3][2] + i);
      end;
    resetCursor();
  end;

  procedure drawLev3();
  var
    i, last: integer;
  begin
    maxdisplay[3] := min((selectBoundaries[3][4] - selectBoundaries[3][2]), listlen[3]);
    if listlen[3] < maxdisplay[3] then
      last := listlen[3]
    else
      last := maxdisplay[3];
    for i := 0 to last do
    begin
      TextCol(3, i);
      drawLineH(selectBoundaries[3][1], selectBoundaries[3][2] + i,
        selectionLengths[3], ' ');
      gotoxy(selectBoundaries[3][1] + 1, selectBoundaries[3][2] + i);
      Write(lev3List^[(i + scroll[3])].Text);
    end;
    ResetCol();
    if last < (selectBoundaries[3][4] - selectBoundaries[3][2]) then
      for i := last + 1 to (selectBoundaries[3][4] - selectBoundaries[3][2]) do
      begin
        drawLineH(selectBoundaries[3][1], selectBoundaries[3][2] +
          i, selectionLengths[3], ' ');
        gotoxy(selectBoundaries[3][1] + 1, selectBoundaries[3][2] + i);
      end;
    drawLev3data();
  end;

  procedure drawLev2();
  var
    i, last: integer;
  begin
    maxdisplay[2] := min((selectBoundaries[2][4] - selectBoundaries[2][2]), listlen[2]);
    if listlen[2] < maxdisplay[2] then
      last := listlen[2]
    else
      last := maxdisplay[2];
    for i := 0 to last do
    begin
      TextCol(2, i);
      drawLineH(selectBoundaries[2][1], selectBoundaries[2][2] + i,
        selectionLengths[2], ' ');
      gotoxy(selectBoundaries[2][1] + 1, selectBoundaries[2][2] + i);
      Write(lev2List[(i + scroll[2])]);
    end;
    ResetCol();
    if last < (selectBoundaries[2][4] - selectBoundaries[2][2]) then
      for i := last + 1 to (selectBoundaries[2][4] - selectBoundaries[2][2]) do
      begin
        drawLineH(selectBoundaries[2][1], selectBoundaries[2][2] +
          i, selectionLengths[2], ' ');
      end;
    resetCursor();
  end;

  procedure drawLev1();
  var
    i, last: integer;
  begin
    maxdisplay[1] := min((selectBoundaries[1][4] - selectBoundaries[1][2]), listlen[1]);
    if listlen[1] < maxdisplay[1] then
      last := listlen[1]
    else
      last := maxdisplay[1];
    for i := 0 to last do
    begin
      TextCol(1, i);
      drawLineH(selectBoundaries[1][1], selectBoundaries[1][2] + i,
        selectionLengths[1], ' ');
      gotoxy(selectBoundaries[1][1] + 1, selectBoundaries[1][2] + i);
      Write(mainlist[(i + scroll[1])].typeName);
    end;
    ResetCol();
    if last < (selectBoundaries[1][4] - selectBoundaries[1][2]) then
      for i := last + 1 to (selectBoundaries[1][4] - selectBoundaries[1][2]) do
      begin
        drawLineH(selectBoundaries[2][1], selectBoundaries[2][2] +
          i, selectionLengths[2], ' ');
      end;
    resetCursor();
  end;

  procedure drawList(l: integer);
  begin
    case l of
      1: drawLev1();
      2: drawLev2();
      3: drawLev3();
      4: drawLev4();
    end;
    drawFrameArrows(l);
  end;

  procedure drawList();
  begin
    drawList(level);
  end;

  procedure drawLists();
  var
    i: integer;
  begin
    for i := 1 to 3 do
      drawList(i);
  end;

  procedure hexdisplay_adaptlist2item(itemnumber: integer);
  const
    cutoff: integer = 12;
  begin
    lev2List[itemnumber] := copy(lev2List[itemnumber], 1, cutoff);
    while length(lev2List[itemnumber]) < (cutoff + 1) do
      lev2List[itemnumber] := lev2List[itemnumber] + ' ';
    if (lev3list^[select[3] + scroll[3]].typeid >= 12) and
      (lev3list^[select[3] + scroll[3]].typeid <= 19) then
      lev2List[itemnumber] := lev2List[itemnumber] +
        getDataStr(select[1] + scroll[1], itemnumber, select[3] + scroll[3])
    else
      lev2List[itemnumber] := lev2List[itemnumber] +
        getBytesString(select[1] + scroll[1], itemnumber, select[3] + scroll[3], False);
  end;

  procedure updateLev2Lists();
  var
    listtype, i: integer;
  begin
    listtype := select[1] + scroll[1];
    listlen[3] := optsListlengths[listtype];
    lev3List := mainlist[listtype].OptsList;
    listlen[2] := versions[gamever].dataInfo[listtype].listLen - 1;
    for i := 0 to listlen[2] do
    begin
      lev2List[i] := GetBasicTypeString(listtype, i);
      if HexDisplay then
        hexdisplay_adaptlist2item(i);
    end;
  end;

  procedure updateLists();
  var
    i: integer;
  begin
    for i := 2 to 3 do
    begin
      scroll[i] := 0;
      select[i] := 0;
    end;
    updateLev2Lists();
    for i := 2 to 3 do
    begin
      drawList(i);
      drawSelect(i);
    end;
  end;

  // =========================================
  //                INPUT LAYOUT
  // =========================================

  procedure showinputInt(typeindex, unitindex, optindex, minval: integer;
    maxval: cardinal; signed: boolean);
  var
    inputlength, x, y: integer;
    input: cardinal;
    curval: string;
    typeinfo: descr;
  begin
    typeinfo := GetTypeDescr(lev3list^[optindex].typeid);
    maxdisplay[4] := 0;
    listlen[4] := -1;
    scroll[4] := 0;
    select[4] := 0;
    x := selectBoundaries[4][1];
    y := selectBoundaries[4][2] - 2;
    inputlength := 20;
    if (typeinfo[2]) <> '' then
      y := y + 1;
    gotoxy(x, y);
    Write('Old value : ');
    if (signed) then
      curval := toString(getBytesSigned(typeindex, unitindex, optindex))
    else
      curval := toString(getBytes(typeindex, unitindex, optindex));
    Write(curval);
    x := x + 2;
    y := y + 3;
    gotoxy(x, y);
    Write('___________________');
    gotoxy(x, y);
    x := x + inputlength + 5;

    input := inputValue(curval, minval, maxval, getDataLen(typeindex, optindex));

    if length(errormsg) = 0 then
    begin
      // SAVE
      writeBytes(getAddress(typeindex, unitindex, optindex), input,
        getDataLen(typeindex, optindex));
    end
    else if errormsg <> 'Canceled' then
    begin
      gotoxy(x, y);
      Write(errormsg);
      resetCursor();
      cntin();
    end;
    resetCursor();
    level := prevlev;
  end;

  procedure showinputInt(typeindex, unitindex, optindex: integer;
    signed, showRange: boolean);
  var
    i, datalen: byte;
    minval, tmp: integer;
    maxval: cardinal;
  begin
    minval := -128;
    maxval := 0;
    datalen := getDataLen(typeindex, optindex);
    for i := 2 to (max(1, datalen)) do
      minval := minval * 256;
    if signed then
      maxval := (minval + 1) * -1
    else
    begin
      tmp := minval + 1;
      maxval := tmp * -1;
      maxval := (maxval * 2) + 1;
      minval := 0;
    end;
    if (showRange) then
    begin
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 3);
      Write('Data range: ');
      Write(minval, ',', maxval);
    end;
    showinputInt(typeindex, unitindex, optindex, minval, maxval, signed);
  end;

  procedure showinputInt(typeindex, unitindex, optindex: integer; signed: boolean);
  begin
    showinputInt(typeindex, unitindex, optindex, signed, False);
  end;

  procedure showinputOffset(typeindex, unitindex, optindex: integer);
  var
    input, curval, ref: cardinal;
    typeinfo: descr;
  begin
    typeinfo := GetTypeDescr(lev3list^[optindex].typeid);
    maxdisplay[4] := 0;
    listlen[4] := -1;
    scroll[4] := 0;
    select[4] := 0;
    if (typeinfo[2]) <> '' then
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 1)
    else
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2]);
    Write('Old value : ');
    curval := getRefAddress(typeindex, unitindex, optindex);
    if curval <> high(cardinal) then
    begin
      Write(getBytesString(typeindex, unitindex, optindex, True));
      Write(' (');
      if curval = 0 then
        Write('blank')
      else
        Write('offset ' + toString(curval));
      Write(')');
    end
    else
    begin
      Write('<invalid>');
      curval := 0;
    end;
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 3);
    Write('WARNING - Don''t edit these unless you know');
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 4);
    Write('          exactly what you''re doing!');
    gotoxy(selectBoundaries[4][1] + 2, selectBoundaries[4][2] + 1);
    Write('___________________');
    gotoxy(selectBoundaries[4][1] + 2, selectBoundaries[4][2] + 1);

    input := inputValue(toString(curval), 0, FileSize(exefile) - 1, 4);

    gotoxy(selectBoundaries[4][1] + 32, selectBoundaries[4][2] + 1);
    if length(errormsg) = 0 then
    begin
      // SAVE
      ref := getOffsetRef(input);
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 3);
      Write('                                                 ');
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 3);
      Write('New value : ');
      Write(getBytesString(4, ref, True));
      if input <> 0 then
      begin
        Write(' (offset ');
        Write(getRefOffset(ref));
        Write(')');
      end
      else
        Write(' (blank)');
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 4);
      Write('                                                 ');
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 4);
      Write('New string: ');
      if input <> 0 then
        Write('"');
      Write(getRefString(ref));
      if input <> 0 then
        Write('"');
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 6);
      Write('[ENTER]/[SPACE]: save     [ESC]: cancel');
      keyread := ReadKey();
      if (keyread = keymap.ENTER) or (keyread = keymap.ENTER) then
        writeBytes(getAddress(typeindex, unitindex, optindex), ref, 4);
      while keypressed do
        ReadKey(); // clear character buffer
    end
    else if errormsg <> 'Canceled' then
    begin
      Write(errormsg);
      cntin();
    end;
    resetCursor();
    level := prevlev;
  end;

  procedure showinputHex(typeindex, unitindex, optindex: integer);
  var
    input: cardinal;
  begin
    maxdisplay[4] := 0;
    listlen[4] := -1;
    scroll[4] := 0;
    select[4] := 0;
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 3);
    Write('Old value : ' + getBytesString(typeindex, unitindex, optindex, True));
    gotoxy(selectBoundaries[4][1] + 2, selectBoundaries[4][2]);
    input := inputHexBytes(getBytesString(typeindex, unitindex, optindex, False),
      max(1, getDataLen(typeindex, optindex)));
    gotoxy(selectBoundaries[4][1] + 32, selectBoundaries[4][2]);
    if length(errormsg) = 0 then
      writeBytes(getAddress(typeindex, unitindex, optindex), input,
        getDataLen(typeindex, optindex));
    ResetCol();
    resetCursor();
    level := prevlev;
  end;


  procedure showinputBin(typeindex, unitindex, optindex: integer);
  begin
    binEd := True;
    listlen[4] := max(1, getDataLen(typeindex, optindex)) * 8 - 1;
    maxdisplay[4] := listlen[4];
    scroll[4] := 0;
    // SPECIAL CODE FOR BIT SWITCH VALUES
    if (lev3list^[optindex].typeid >= 12) and (lev3list^[optindex].typeid <= 19) then
      select[4] := lev3list^[optindex].typeid - 12
    else
      select[4] := 0;
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 3);
    Write('Old value : ' + getBytesString(typeindex, unitindex, optindex, True));
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] + 2);
    Write('New value:');
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 2);
    drawBinList(typeindex, unitindex, optindex);
    drawSelectBin();
  end;

  procedure showinputChar(typeindex, unitindex, optindex: integer);
  var
    input: char;
  begin
    maxdisplay[4] := 0;
    listlen[4] := -1;
    scroll[4] := 0;
    select[4] := 0;
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 1);
    Write('Old value : "' + char(getBytes(typeindex, unitindex, optindex, 1)) + '"');
    gotoxy(selectBoundaries[4][1] + 2, selectBoundaries[4][2] + 1);
    input := inputChar(char(getBytes(typeindex, unitindex, optindex, 1)));
    gotoxy(selectBoundaries[4][1] + 32, selectBoundaries[4][2] + 1);
    if length(errormsg) = 0 then
      writeBytes(getAddress(typeindex, unitindex, optindex), byte(input), 1);
    ResetCol();
    resetCursor();
    level := prevlev;
  end;

  // ======================
  // Show list instructions
  // ======================

  procedure showBasicList(typeindex, unitindex, optindex: integer; hasblank: boolean);
  var
    val: integer;
  begin
    val := getBytesSigned(typeindex, unitindex, optindex);
    if (getDataStr(typeindex, unitindex, optindex) <> '<Invalid>') and (val <> -1) then
      moveDown(val + byte(hasblank));
    drawLev4Select(); // resets view after scrolling
  end;

  procedure showBasicList(typeindex, unitindex, optindex: integer);
  begin
    showBasicList(typeindex, unitindex, optindex, False);
  end;

  procedure showSwitchBooleanList(typeindex, unitindex, optindex, bitindex: integer);
  var
    val: boolean;
  begin
    val := readSwitchBoolean(typeindex, unitindex, optindex, bitindex);
    moveDown(byte(val));
    drawLev4Select(); // resets view after scrolling
  end;

  procedure showValuesList(typeindex, unitindex, optindex: integer;
    vallist: array of values);
  var
    scrolling, i, val, diff, diff2: integer;
    datalen: byte;
  begin
    datalen := getDataLen(typeindex, optindex);
    if ((datalen < 4) and (1 shl ((8 * datalen) - 1) <= vallist[high(vallist)].val)) then
      val := getBytes(typeindex, unitindex, optindex)
    else
      val := getBytesSigned(typeindex, unitindex, optindex);
    scrolling := -1;
    for i := 0 to high(vallist) do
      if val = vallist[i].val then
        scrolling := i;
    if (scrolling <> -1) then
      moveDown(scrolling)
    // always assumes that the latest value in the list is the highest.
    else if (val > vallist[high(vallist)].val) then
      moveDown(high(vallist))
    // current value is not in the list: jump to the item on the list with the closest value
    else if (val < vallist[high(vallist)].val) and (val > vallist[low(vallist)].val) then
    begin
      diff := abs(val - vallist[0].val);
      scrolling := 0;
      for i := 0 to high(vallist) do
      begin
        diff2 := abs(val - vallist[i].val);
        if (diff2 < diff) then
        begin
          diff := diff2;
          scrolling := i;
        end;
      end;
      moveDown(scrolling);
    end;
    drawLev4Select(); // resets view after scrolling
  end;

  // =========================================
  //                MAIN LAYOUT
  // =========================================

  procedure drawLevFrame(lvl: integer; active: boolean);
  begin
    TextBackground(activelevbgcolor);
    if level = lvl then
      TextColor(activelevcolor)
    else
      TextColor(inactivelevcolor);
    drawFrame(drawBoundaries[lvl][1], drawBoundaries[lvl][2],
      drawBoundaries[lvl]  [3], drawBoundaries[lvl][4], secondcolumn[lvl],
      active, lvl = 4);
    TextColor(activelevcolor);
  end;

  procedure drawLevFrame(lvl: integer);
  begin
    case lvl of
      1: drawLevFrame(lvl, level = 1);
      2: drawLevFrame(lvl, level = 2);
      3: drawLevFrame(lvl, level = 3);
      4: drawLevFrame(lvl, True);
    end;
  end;

  procedure drawLevFrame();
  begin
    drawLevFrame(level);
  end;

  procedure drawLevFrames();
  var
    i: integer;
  begin
    for i := 1 to 3 do
    begin
      drawLevFrame(i);
    end;
  end;

  procedure clearWorkArea();
  begin
    ClrScr();
    drawHeaders();
  end;

  procedure drawLayout4(edtype: integer);
  var
    typeindex, unitindex, optindex: integer;
    typeinfo: descr;
  begin
    typeindex := select[1] + scroll[1];
    unitindex := select[2] + scroll[2];
    optindex := select[3] + scroll[3];
    typeinfo := GetTypeDescr(lev3list^[optindex].typeid);
    drawLevFrame(prevlev, True);
    drawList(prevlev);
    drawSelect(prevlev);
    drawLevFrame(4);
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 7);
    Write('[');
    if edtype = 1 then
      Write('BIN ');
    if edtype = 2 then
      Write('HEX ');
    if (edtype = 3) or (edtype = 4) then
      Write('DEC ');
    Write('EDIT');
    if (edtype = 3) then
      Write(' SIGNED')
    else if (edtype = 4) then
      Write(' UNSIGNED');
    Write(']');
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 6);
    Write('Section   : ');
    Write(GetBasicTypeString(typeindex, unitindex));
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 5);
    Write('Option    : ');
    Write(lev3list^[optindex].Text);
    gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 4);
    Write('Address   : ');
    if ((edtype = 1) or (edtype = 2)) then
      Write(getBytesString(getAddress(typeindex, unitindex, optindex), False))
    else
      Write(toString(getAddress(typeindex, unitindex, optindex)));
    if (edtype = 0) then
    begin
      gotoxy(selectBoundaries[4][1], selectBoundaries[4][2] - 3);
      Write('Data type : ');
      gotoxy(selectBoundaries[4][1] + 12, selectBoundaries[4][2] - 3);
      Write(typeinfo[1]);
      gotoxy(selectBoundaries[4][1] + 12, selectBoundaries[4][2] - 2);
      Write(typeinfo[2]);
      gotoxy(17, 10);
      maxdisplay[4] := 0;
      listlen[4] := 0;
      select[4] := 0;
      scroll[4] := 0;
      drawList();
      // DATA TYPE LIST - Gives extra instructions for showing each data type,
      //                  or opens special type-specific editor
      case lev3list^[optindex].typeid of
        // 4-byte (INT)
        0: showinputInt(typeindex, unitindex, optindex, True);
        // signed byte
        1: showinputInt(typeindex, unitindex, optindex, True);
        // 2-byte
        2: showinputInt(typeindex, unitindex, optindex, True);
        // 4-byte (REF)
        3: showinputOffset(typeindex, unitindex, optindex);
        // owners
        4: ; {no special show instructions needed}
        // command
        5: showBasicList(typeindex, unitindex, optindex, True);
        // movement type
        6: showBasicList(typeindex, unitindex, optindex, False);
        // weapon type
        7: showBasicList(typeindex, unitindex, optindex, True);
        // foundation
        8: showValuesList(typeindex, unitindex, optindex, foundations);
        // decay factor
        9: showValuesList(typeindex, unitindex, optindex, decayFactors);
        // prerequisites
        10: ; {no special show instructions needed}
        // sound
        11: showValuesList(typeindex, unitindex, optindex, sounds);
        // bit switch 1/8
        12: showSwitchBooleanList(typeindex, unitindex, optindex, 0);
        // bit switch 2/8
        13: showSwitchBooleanList(typeindex, unitindex, optindex, 1);
        // bit switch 3/8
        14: showSwitchBooleanList(typeindex, unitindex, optindex, 2);
        // bit switch 4/8
        15: showSwitchBooleanList(typeindex, unitindex, optindex, 3);
        // bit switch 5/8
        16: showSwitchBooleanList(typeindex, unitindex, optindex, 4);
        // bit switch 6/8
        17: showSwitchBooleanList(typeindex, unitindex, optindex, 5);
        // bit switch 7/8
        18: showSwitchBooleanList(typeindex, unitindex, optindex, 6);
        // bit switch 8/8
        19: showSwitchBooleanList(typeindex, unitindex, optindex, 7);
        // percentage
        20: showValuesList(typeindex, unitindex, optindex, percentages);
        // music
        21: showBasicList(typeindex, unitindex, optindex, True);
        // character
        22: showinputChar(typeindex, unitindex, optindex);
        // palace special ability
        23: showValuesList(typeindex, unitindex, optindex, palaceSpecials);
        // SHP Graphics
        24: showBasicList(typeindex, unitindex, optindex, True);
        // unit display mode
        25: showValuesList(typeindex, unitindex, optindex, displayModes);
        // action interrupt mode
        26: showValuesList(typeindex, unitindex, optindex, interruptModes);
        // sidebar mode
        27: showValuesList(typeindex, unitindex, optindex, sidebarModes);
        // voc sound
        28: showBasicList(typeindex, unitindex, optindex, True);
        // parent PAK
        29: showBasicList(typeindex, unitindex, optindex, False);
        // bit list of allowed units
        30: ; {no special show instructions needed}
        // structure animation
        31: ; {no special show instructions needed}
        // structure graphics set
        32: showValuesList(typeindex, unitindex, optindex, structureGraphics);
        // unsigned byte
        33: showinputInt(typeindex, unitindex, optindex, False);
        // boolean
        34: showValuesList(typeindex, unitindex, optindex, booleans);
        // percentage (1-byte)
        35: showValuesList(typeindex, unitindex, optindex, percentages);
        // Crater Types
        36: showValuesList(typeindex, unitindex, optindex, craterTypes);
      end;
    end
    else if edtype = 1 then
      showinputBin(typeindex, unitindex, optindex)
    else if edtype = 2 then
      showinputHex(typeindex, unitindex, optindex)
    else if edtype = 3 then
      showinputInt(typeindex, unitindex, optindex, True, True)
    else if edtype = 4 then
      showinputInt(typeindex, unitindex, optindex, False, True);
    if level <> 4 then
    begin
      if HexDisplay then
      begin
        lev2List[unitindex] :=
          getDataStr(typeindex, unitindex, mainlist[typeindex].refOption);
        hexdisplay_adaptlist2item(unitindex);
      end;
      ClearWorkArea();
      drawLevFrames();
      drawLists();
      drawSelects();
    end;
  end;

  procedure drawLayout();
  begin
    if level = 4 then
      drawLayout4(0)
    else
      drawLevFrames();
  end;

  procedure FillWorkArea();
  begin
    ClearWorkArea();
    drawLayout();
    drawLists();
    drawSelects();
  end;

  // =========================================
  //              LEVEL 4 CONTROLS
  // =========================================

  procedure startlev4();
  begin
    prevlev := level;
    level := 4;
    if not HexDisplay then
      drawLayout()
    else
    begin
      if (lev3list^[select[3] + scroll[3]].typeid >= 12) and
        (lev3list^[select[3] + scroll[3]].typeid <= 19) then
        drawLayout4(1)
      else
        drawLayout4(2);
    end;
  end;

  procedure startlev4bin();
  begin
    prevlev := level;
    level := 4;
    drawLayout4(1);
  end;

  procedure startlev4hex();
  begin
    prevlev := level;
    level := 4;
    drawLayout4(2);
  end;

  procedure startlev4dec();
  begin
    prevlev := level;
    level := 4;
    drawLayout4(3);
  end;

  procedure startlev4decun();
  begin
    prevlev := level;
    level := 4;
    drawLayout4(4);
  end;

  procedure exitlev4();
  begin
    select[4] := 0;
    scroll[4] := 0;
    level := prevlev;
    if binEd then
    begin
      if HexDisplay then
      begin
        lev2List[select[2] + scroll[2]] :=
          getDataStr(select[1] + scroll[1], select[2] + scroll[2],
          mainlist[select[1] + scroll[1]].refOption);
        hexdisplay_adaptlist2item(select[2] + scroll[2]);
      end;
      binEd := False;
    end;
    FillWorkArea();
  end;

  // =========================================
  //              OUTPUT PROCEDURES
  // =========================================

  procedure savePrerequisites(typeindex, unitindex, optindex: integer);
  var
    o: boollist;
    listpos: integer;
  begin
    if keyread = keymap.SPACE then
    begin
      o := readPrerequisites(typeindex, unitindex, optindex);
      listpos := select[4] + scroll[4];
      o[listpos] := (not o[listpos]);
      writePrerequisites(o, typeindex, unitindex, optindex);
      drawLev4(); // no arrows refresh needed
    end
    else if keyread = keymap.ENTER then
      exitlev4();
  end;

  procedure saveBasicList(typeindex, unitindex, optindex: integer; hasblank: boolean);
  var
    Value: integer;
    len: byte;
  begin
    len := getDataLen(typeindex, optindex);
    Value := select[4] + scroll[4];
    if hasblank then
      Value := Value - 1;
    writeBytes(getAddress(typeindex, unitindex, optindex),
      convertToUnsigned(Value, len), len);
    exitlev4();
  end;

  procedure saveValuesList(typeindex, unitindex, optindex: integer;
    vallist: array of values);
  var
    Value, Len: integer;
  begin
    Len := getDataLen(typeindex, optindex);
    Value := vallist[(select[4] + scroll[4])].val;
    // fix for percentages: if exactly the round value just beyond the bytes'
    // storage capacity, subtract one to get 'Westwood 100%'.
    if (len < 4) and (Value = 1 shl (8 * Len)) then
      Value := Value - 1;
    writeBytes(getAddress(typeindex, unitindex, optindex),
      convertToUnsigned(Value, Len), Len);
    exitlev4();
  end;

  procedure saveBitSwitchList(typeindex, unitindex, optindex, bitindex: integer);
  var
    Value: integer;
  begin
    Value := select[4] + scroll[4];
    writeSwitchBoolean((Value = 1), typeindex, unitindex, optindex, bitindex);
    exitlev4();
  end;

  procedure inputLev4();
  var
    typeindex, unitindex, optindex: integer;
  begin
    typeindex := select[1] + scroll[1];
    unitindex := select[2] + scroll[2];
    optindex := select[3] + scroll[3];
    if binEd then
      savePrerequisites(typeindex, unitindex, optindex)
    else
      // DATA TYPE LIST - Determines which procedure to call to save each data type
      //                  when the list is opened
      case lev3list^[select[3] + scroll[3]].typeid of
        // 4-byte (INT)
        0: ;
        // signed byte
        1: ;
        // 2-byte
        2: ;
        // 4-byte (REF)
        3: ;
        // owners
        4: savePrerequisites(typeindex, unitindex, optindex);
        // command
        5: saveBasicList(typeindex, unitindex, optindex, True);
        // movement type
        6: saveBasicList(typeindex, unitindex, optindex, False);
        // weapon type
        7: saveBasicList(typeindex, unitindex, optindex, True);
        // foundation
        8: saveValuesList(typeindex, unitindex, optindex, foundations);
        // decay factor
        9: saveValuesList(typeindex, unitindex, optindex, decayFactors);
        // prerequisites
        10: savePrerequisites(typeindex, unitindex, optindex);
        // sound
        11: saveValuesList(typeindex, unitindex, optindex, sounds);
        // bit switch 1/8
        12: saveBitSwitchList(typeindex, unitindex, optindex, 0);
        // bit switch 2/8
        13: saveBitSwitchList(typeindex, unitindex, optindex, 1);
        // bit switch 3/8
        14: saveBitSwitchList(typeindex, unitindex, optindex, 2);
        // bit switch 4/8
        15: saveBitSwitchList(typeindex, unitindex, optindex, 3);
        // bit switch 5/8
        16: saveBitSwitchList(typeindex, unitindex, optindex, 4);
        // bit switch 6/8
        17: saveBitSwitchList(typeindex, unitindex, optindex, 5);
        // bit switch 7/8
        18: saveBitSwitchList(typeindex, unitindex, optindex, 6);
        // bit switch 8/8
        19: saveBitSwitchList(typeindex, unitindex, optindex, 7);
        // percentage (2-byte)
        20: saveValuesList(typeindex, unitindex, optindex, percentages);
        // music
        21: saveBasicList(typeindex, unitindex, optindex, True);
        // character
        22: ;
        // palace special ability
        23: saveValuesList(typeindex, unitindex, optindex, palaceSpecials);
        // SHP Graphics
        24: saveBasicList(typeindex, unitindex, optindex, True);
        // unit display mode
        25: saveValuesList(typeindex, unitindex, optindex, displayModes);
        // action interrupt mode
        26: saveValuesList(typeindex, unitindex, optindex, interruptModes);
        // sidebar mode
        27: saveValuesList(typeindex, unitindex, optindex, SidebarModes);
        // voc sound
        28: saveBasicList(typeindex, unitindex, optindex, True);
        // parent PAK
        29: saveBasicList(typeindex, unitindex, optindex, False);
        // bit list of allowed units
        30: savePrerequisites(typeindex, unitindex, optindex);
        // structure animation
        31: ; {Add function to open & edit current level 4 list item?}
        // structure graphics set
        32: saveValuesList(typeindex, unitindex, optindex, structureGraphics);
        // unsigned byte
        33: ;
        // boolean
        34: saveValuesList(typeindex, unitindex, optindex, booleans);
        // percentage (1-byte)
        35: saveValuesList(typeindex, unitindex, optindex, percentages);
        // Crater Types
        36: saveValuesList(typeindex, unitindex, optindex, craterTypes);
      end;
  end;

  // =========================================
  //              DATA DUMP FUNCTIONS
  // =========================================

  procedure DumpToIni(filename: string);
  var
    iniFile: Text;
    i, endi, typeindex, unitindex, optindex: integer;
    HexDisplayOrig: boolean;
  begin
    HexDisplayOrig := HexDisplay;
    HexDisplay := False;
    Assign(iniFile, filename); {assign a text file}
    Rewrite(iniFile); {open the file as empty file for writing}
    writeln(iniFile, '; Rules dump for version "' +
      versions[gamever].nameShort + '" (' +
      versions[gamever].nameLong + ')');
    writeln(iniFile, '; Created with ' + editorname + ' ' +
      editorversion + ' (created by ' + editorauthor + ')');
    writeln(iniFile, ';');
    endi := 7;
    while (endi >= 1) and (versions[gamever].Info[endi] = '') do
      endi := endi - 1;

    for i := 1 to endi do
    begin
      if versions[gamever].Info[i] <> '' then
        writeln(iniFile, '; ' + versions[gamever].Info[i]);
    end;
    writeln(iniFile, ';');
    for typeindex := low(mainlist) to high(mainlist) do
    begin
      writeln(iniFile, '');
      writeln(iniFile, '');
      writeln(iniFile, '; ' + mainlist[typeindex].typeName);
      for unitindex := 0 to versions[gamever].dataInfo[typeindex].listLen - 1 do
      begin
        writeln(iniFile, '');
        writeln(iniFile, '[' + Trim(GetBasicTypeString(typeindex, unitindex)) + ']');
        optindex := 0;
        while (optindex <= high(mainlist[typeindex].optsList^)) and
          (mainlist[typeindex].optsList^[optindex].typeid <> -1) do
        begin
          writeln(iniFile, mainlist[typeindex].optsList^[optindex].Text +
            ' = ' + TrimRight(getDataStr(typeindex, unitindex, optindex, False)));
          optindex := optindex + 1;
        end;
      end;
    end;
    Close(iniFile);
    HexDisplay := HexDisplayOrig;
  end;

  procedure writeIniLines(var iniFile: Text; typeindex: integer;
  var options: array of integer);
  var
    unitindex, optindex: integer;
  begin
    for unitindex := 0 to versions[gamever].dataInfo[typeindex].listLen - 1 do
    begin
      Write(iniFile, Trim(GetBasicTypeString(typeindex, unitindex)));
      Write(iniFile, '=');
      for optindex := 0 to high(options) do
      begin
        Write(iniFile, tostring(getBytes(typeindex, unitindex, options[optindex])));
        if (optindex <> high(options)) then
          Write(iniFile, ',')
        else
          writeln(iniFile);
      end;
    end;
  end;

  procedure writeProfileIniFile(filename: string);
  var
    iniFile: Text;
    HexDisplayOrig: boolean;
  begin
    HexDisplayOrig := HexDisplay;
    HexDisplay := False;
    Assign(iniFile, filename); {assign a text file}
    Rewrite(iniFile); {open the file as empty file for writing}
    writeln(iniFile, '; Created with ' + editorname + ' ' +
      editorversion + ' (created by ' + editorauthor + ')');
    writeln(iniFile);
    writeln(iniFile, '[' + ConstructHeader + ']');
    writeln(iniFile, '; ' + ConstructInfo);
    writeIniLines(iniFile, 0, ConstructOpts);
    writeIniLines(iniFile, 1, ConstructOpts);
    writeln(iniFile);
    writeln(iniFile, '[' + CombatHeader + ']');
    writeln(iniFile, '; ' + CombatInfo);
    writeIniLines(iniFile, 0, CombatOpts);

    Close(iniFile);
    HexDisplay := HexDisplayOrig;
  end;

  procedure writeIniLineToExe(typeindex, unitindex: integer; line: string; options: array of integer);
  var
    optindex, curval, success: integer;
    len: byte;
    curword: string;
  begin
    for optindex := 1 to High(options) do
    begin
      curword := ExtractWord(optindex, line, [',', ' ']);
      Val(curword, curval, success);
      if (success = 0) then
      begin
        len := getDataLen(typeindex, options[optindex]);
        writeBytes(getAddress(typeindex, unitindex, options[optindex]),
          convertToUnsigned(curval, len), len);
      end;
    end;
  end;

  function lookupTypeStr(typeindex: integer; Name: string): integer;
  var
    unitindex: integer;
  begin
    Result := -1;
    for unitindex := 0 to versions[gamever].dataInfo[typeindex].listLen - 1 do
    begin
      if uppercase(GetBasicTypeString(typeindex, unitindex)) = uppercase(Name) then
      begin
        Result := unitindex;
        Exit;
      end;
    end;
  end;

  procedure readProfileIniFile(filename: string);
  var
    IniFile: Text;
    Line, Key, Value, CurrentHeader: string;
    CommentPos, OpenBracketPos, CloseBracketPos, EqualPos, TypeIndex, UnitIndex: integer;
  begin
    Assign(IniFile, FileName);
    Reset(IniFile);
    repeat
      Readln(IniFile, Line);
      Line := Trim(Line);
      CommentPos := Pos(';', Line);
      if (CommentPos > 1) then
      begin
        Line := Trim(Copy(Line, 1, CommentPos - 1));
        CommentPos := 0;
      end;
      OpenBracketPos := Pos('[', Line);
      CloseBracketPos := Pos(']', Line);
      EqualPos := Pos('=', Line);
      if ((CommentPos <> 1) and (Length(Line) > 0)) then
      begin
        if ((OpenBracketPos = 1) and (CloseBracketPos <> 0)) then
        begin
          CurrentHeader := Trim(Copy(Line, OpenBracketPos + 1, CloseBracketPos - 2));
        end
        else if ((CurrentHeader <> '') and (EqualPos <> 0)) then
        begin
          key := Trim(Copy(line, 1, EqualPos - 1));
          Value := Trim(Copy(Line, EqualPos + 1, Length(Line) - EqualPos));
          // handle ini key-value line
          TypeIndex := 0;
          UnitIndex := lookupTypeStr(TypeIndex, Key);
          if (UnitIndex < 0) and (UpperCase(CurrentHeader) = UpperCase(ConstructHeader)) then
          begin
            TypeIndex := 1;
            UnitIndex := lookupTypeStr(typeindex, Key);
          end;
          if (UnitIndex >= 0) then
          begin
            if (UpperCase(CurrentHeader) = UpperCase(CombatHeader)) then
              writeIniLineToExe(TypeIndex, UnitIndex, Value, CombatOpts)
            else if (UpperCase(CurrentHeader) = UpperCase(ConstructHeader)) then
              writeIniLineToExe(TypeIndex, UnitIndex, Value, ConstructOpts);
          end;
          // end handle ini key-value line
        end;
      end;
    until EOF(IniFile);
    Close(IniFile);
  end;

  // =========================================
  //              HELP & INFO SCREENS
  // =========================================

  procedure greyframes();
  begin
    prevlev := level;
    level := 4;
    drawLevFrame(prevlev, True);
    drawList(prevlev);
    drawSelect(prevlev);
    level := prevlev;
  end;

  procedure egotrip();
  var
    x, y, Width, Height, i: integer;
  begin
    greyframes();
    x := 14;
    y := 4;
    Width := 54;
    Height := 19;
    drawFrame(x, y, Width, Height, -1, True, True);
    x := x + 2;
    gotoxy(x, y);
    Write('[INFO]');
    y := y + 1;
    gotoxy(x, y);
    Write(editorname + ' ' + editorversion);
    gotoxy(x + 33, y);
    Write('Powered by Pascal');
    y := y + 1;
    gotoxy(x, y);
    for i := 1 to length(editorname + editorversion) + 1 do
      Write(char(framechar^[1][5]));
    // length of line:
    //    '__________________________________________________'
    y := y + 1;
    gotoxy(x, y);
    Write('Created by ' + editorauthor + ' <' + editorauthoremail + '>');
    y := y + 1;
    gotoxy(x, y);
    Write('Project started on February 27, 2008.');
    y := y + 1;
    gotoxy(x, y);
    Write('Current version: ' + editorversiondate + '.');
    y := y + 1;
    y := y + 1;
    gotoxy(x, y);
    Write('Originally requested by MrFlibble, to help him in');
    y := y + 1;
    gotoxy(x, y);
    Write('his efforts to create the "Super Dune II Second');
    y := y + 1;
    gotoxy(x, y);
    Write('Edition" mod.');
    y := y + 1;
    y := y + 1;
    gotoxy(x, y);
    Write('Many thanks to MrFlibble for his constant support,');
    y := y + 1;
    gotoxy(x, y);
    Write('to Segra for his research into the exe file, and');
    y := y + 1;
    gotoxy(x, y);
    Write('to TrueBrain for the OpenDune project, which gave');
    y := y + 1;
    gotoxy(x, y);
    Write('us all readable source code of Dune II.');
    y := y + 1;
    y := y + 1;
    gotoxy(x, y);
    Write('Layout based on the "C&C structure editor V1.02"');
    y := y + 1;
    gotoxy(x, y);
    Write('by Wladimir van der Laan.');

    resetCursor();
    cntin();
    FillWorkArea();
  end;

  procedure help();
  var
    x, y, Width, Height, i, max: integer;
    help: bigdescr;
  const
    col2: integer = 12;
  begin
    greyframes();
    x := 13;
    y := 3;
    Width := 56;
    Height := 22;
    if level = 3 then   // type-specific help
    begin
      help := GetOptHelp(lev3List^[select[3] + scroll[3]].helpid);
      max := high(help);
      i := max;
      while ((help[max] = '') and (max > 0)) do
        max := max - 1;
      y := 11 - (max div 2);
      Height := 4 + max;
    end;
    drawFrame(x, y, Width, Height, -1, True, True);
    x := x + 2;
    gotoxy(x, y);
    Write('[HELP]');
    y := y + 1;
    gotoxy(x, y);
    Write(editorname + ' ' + editorversion);
    y := y + 1;
    gotoxy(x, y);
    for i := 1 to length(editorname + editorversion) + 1 do
      Write(char(framechar^[1][5]));
    if level = 3 then
      // type-specific help
    begin
      for i := 1 to max do
      begin
        y := y + 1;
        gotoxy(x, y);
        Write(help[i]);
      end;
    end
    else
      // general program help
    begin
      //           allowed length #1: '____________'
      //           allowed length #2:    ': ___________________________________'
      y := y + 1;
      gotoxy(x, y);
      Write('Left/Right');
      gotoxy(x + col2, y);
      Write(': Navigate between sections');
      y := y + 1;
      gotoxy(x, y);
      Write('Up/Down');
      gotoxy(x + col2, y);
      Write(': Navigate through section list');
      y := y + 1;
      gotoxy(x + 1, y);
      Write('(PageUp/PageDown/Home/End for faster scrolling)');
      y := y + 1;
      y := y + 1;
      gotoxy(x, y);
      Write('Space/Enter');
      gotoxy(x + col2, y);
      Write(': Modify/confirm selected value');
      y := y + 1;
      gotoxy(x, y);
      Write('F1/h');
      gotoxy(x + col2, y);
      Write(': Shows help on the selected option');
      y := y + 1;
      gotoxy(x, y);
      Write('F2/b');
      gotoxy(x + col2, y);
      Write(': Edit as bare binary value');
      y := y + 1;
      gotoxy(x, y);
      Write('F3/x');
      gotoxy(x + col2, y);
      Write(': Edit as bare hexadecimal value');
      y := y + 1;
      gotoxy(x, y);
      Write('F4/d');
      gotoxy(x + col2, y);
      Write(': Edit as bare decimal value (signed)');
      y := y + 1;
      gotoxy(x, y);
      Write('F5/D');
      gotoxy(x + col2, y);
      Write(': Edit as bare decimal value (unsigned)');
      y := y + 1;
      y := y + 1;
      gotoxy(x, y);
      Write('F7/s');
      gotoxy(x + col2, y);
      //    '________________________________________'
      Write(': Save unit/struct data to profile.ini');
      y := y + 1;
      gotoxy(x, y);
      Write('F8/l');
      gotoxy(x + col2, y);
      Write(': Load unit/struct data from profile.ini');
      y := y + 1;
      gotoxy(x, y);
      Write('F9/v');
      gotoxy(x + col2, y);
      Write(': Dune EXE version information');
      y := y + 1;
      gotoxy(x, y);
      Write('F10/n');
      gotoxy(x + col2, y);
      Write(': Dumps all data to an ini-file');
      y := y + 1;
      gotoxy(x, y);
      Write('F11/r');
      gotoxy(x + col2, y);
      Write(': Toggles hexadecimal "Research" mode');
      y := y + 1;
      gotoxy(x, y);
      Write('F12/i');
      gotoxy(x + col2, y);
      Write(': Info screen about the editor');
      y := y + 1;
      gotoxy(x, y);
      Write('[ESC]');
      gotoxy(x + col2, y);
      Write(': Close popup frame / Exit program');
    end;
    resetCursor();
    cntin();
    FillWorkArea();
  end;

  procedure versioninfo();
  var
    x, y, Width, Height, i: integer;
    help: bigdescr;
  begin
    greyframes();
    x := 15;
    y := 7;
    Width := 52;
    Height := 11;
    drawFrame(x, y, Width, Height, -1, True, True);
    x := x + 2;
    gotoxy(x, y);
    Write('[GAME VERSION INFO]');
    help := versions[gamever].info;
    y := y + 1;
    gotoxy(x, y);
    Write(versions[gamever].nameLong);
    y := y + 1;
    gotoxy(x, y);
    for i := 1 to length(versions[gamever].nameLong) do
      Write(char(framechar^[1][5]));
    help := versions[gamever].info;
    for i := 1 to 7 do
    begin
      y := y + 1;
      gotoxy(x, y);
      Write(help[i]);
    end;
    resetCursor();
    cntin();
    FillWorkArea();
  end;

  procedure showDumpToIni(profileDump: boolean);
  var
    x, y, Width, Height: integer;
    rulesfile: string;
  begin
    greyframes();
    if (profileDump) then
      rulesfile := ExtractFilePath(gamepathstr) + profilerulesname +
        '.' + rulesfileext
    else
      rulesfile := ExtractFilePath(gamepathstr) + rulesfileprefix +
        versions[gamever].nameShort + '.' + rulesfileext;
    x := 10;
    y := 10;
    Width := 62;
    Height := 7;
    drawFrame(x, y, Width, Height, -1, True, True);
    x := x + 2;
    gotoxy(x, y);
    Write('[GAME INFO DUMP]');
    y := y + 2;
    gotoxy(x, y);
    Write('Dumping information to:');
    y := y + 1;
    gotoxy(x, y);
    if length(rulesfile) > 58 then
      Write(trimRight(Copy(rulesfile, 1, 27)) + '...' +
        TrimLeft(Copy(rulesfile, length(rulesfile) - 27, length(rulesfile))))
    else
      Write(rulesfile);
    if (profileDump) then
      writeProfileIniFile(rulesfile)
    else
      DumpToIni(rulesfile);
    y := y + 1;
    gotoxy(x, y);
    Write('Done.');
    resetCursor();
    cntin();
    FillWorkArea();
  end;

  procedure showLoadFromIni();
  var
    x, y, Width, Height: integer;
    rulesfile: string;
    exists: boolean;
  begin
    greyframes();
    rulesfile := ExtractFilePath(gamepathstr) + profilerulesname + '.' + rulesfileext;
    x := 10;
    y := 10;
    Width := 62;
    Height := 7;
    drawFrame(x, y, Width, Height, -1, True, True);
    x := x + 2;
    gotoxy(x, y);
    Write('[GAME INFO LOAD]');
    y := y + 2;
    gotoxy(x, y);
    Write('Loading information from:');
    y := y + 1;
    gotoxy(x, y);
    if length(rulesfile) > 58 then
      Write(trimRight(Copy(rulesfile, 1, 27)) + '...' +
        TrimLeft(Copy(rulesfile, length(rulesfile) - 27, length(rulesfile))))
    else
      Write(rulesfile);

    exists := FileExists(rulesfile);
    if exists then
      ReadProfileIniFile(rulesfile);
    y := y + 1;
    gotoxy(x, y);
    if (exists) then
      Write('Done.')
    else
      Write('Failed: file "' + profilerulesname + '.' + rulesfileext + '" not found.');
    resetCursor();
    cntin();
    FillWorkArea();
  end;

  // =========================================
  //                NAVIGATION
  // =========================================

  procedure MoveVertGen(scr, sel: integer);
  begin
    if scr <> scroll[level] then
      drawList();
    if sel <> select[level] then
      if not BinEd then
      begin
        drawSelect(level, sel);
        drawSelect(level, select[level]);
      end
      else
        drawSelectBin();
    if (scr <> scroll[level]) or (sel <> select[level]) then
    begin
      if level = 1 then
        updateLists();
      if level = 2 then
        drawLev3data();
      if (level = 3) and (HexDisplay) then
      begin
        updateLev2Lists();
        drawLev2();
      end;
    end;
  end;

  procedure MoveHorGen(oldlevel: integer);
  begin
    if oldlevel <> level then
    begin
      if not binEd then
        drawLevFrame(oldlevel);
      drawLevFrame(level);
      if not binEd then
        drawList(oldlevel);
      drawList(level);
      if not binEd then
        drawSelect(oldlevel);
      drawSelect(level);
    end;
  end;

  procedure MoveDown2(Lines: integer);
  var
    scr, sel: integer;
  begin
    scr := scroll[level];
    sel := select[level];
    if not binEd then
      MoveDown(Lines)
    else
      MoveUp(Lines);
    MoveVertGen(scr, sel);
  end;

  procedure MoveUp2(Lines: integer);
  var
    scr, sel: integer;
  begin
    scr := scroll[level];
    sel := select[level];
    if not binEd then
      MoveUp(Lines)
    else
      MoveDown(Lines);
    MoveVertGen(scr, sel);
  end;

  procedure MoveRight2();
  var
    oldlevel: integer;
  begin
    oldlevel := level;
    MoveRight();
    MoveHorGen(oldlevel);
  end;

  procedure MoveLeft2();
  var
    oldlevel: integer;
  begin
    oldlevel := level;
    MoveLeft();
    MoveHorGen(oldlevel);
  end;

  procedure SwitchHexDisplay();
  begin
    HexDisplay := not HexDisplay;
    updateLev2Lists();
    drawLev2();
    drawLev3data();
  end;

  procedure inputKey();
  var
    keyread0: char;
  begin
    keyread0 := char(255);
    keyread := ReadKey();
    while keypressed do
    begin
      keyread0 := keyread;
      keyread := ReadKey();
    end;
    if (keyread = keymap.ESC) and (level <> 4) then
      keyread := char(255)
    else if (level = 4) and ((keyread = keymap.ESC) or
      ((keyread0 = char(0)) and (keyread = keymap.F2)) or (keyread = 'b') or
      ((keyread0 = char(0)) and (keyread = keymap.F3)) or (keyread = 'x') or
      ((keyread0 = char(0)) and (keyread = keymap.F4)) or (keyread = 'd') or
      ((keyread0 = char(0)) and (keyread = keymap.F5)) or (keyread = 'D')) then
      exitlev4()
    else if ((level = 2) or (level = 3)) and
      ((keyread = keymap.ENTER) or (keyread = keymap.SPACE)) then
      startlev4()
    else if ((level = 2) or (level = 3)) and
      (((keyread0 = char(0)) and (keyread = keymap.F2)) or (keyread = 'b')) then
      startlev4bin()
    else if ((level = 2) or (level = 3)) and
      (((keyread0 = char(0)) and (keyread = keymap.F3)) or (keyread = 'x')) then
      startlev4hex()
    else if ((level = 2) or (level = 3)) and
      (((keyread0 = char(0)) and (keyread = keymap.F4)) or (keyread = 'd')) then
      startlev4dec()
    else if ((level = 2) or (level = 3)) and
      (((keyread0 = char(0)) and (keyread = keymap.F5)) or (keyread = 'D')) then
      startlev4decun()
    else if ((keyread0 = char(0)) and (keyread = keymap.RIGHT)) and (level <> 4) then
      MoveRight2()
    else if ((keyread0 = char(0)) and (keyread = keymap.RIGHT)) and (level = 4) then
      MoveDown2(1)
    else if ((keyread0 = char(0)) and (keyread = keymap.LEFT)) and (level <> 4) then
      MoveLeft2()
    else if ((keyread0 = char(0)) and (keyread = keymap.LEFT)) and (level = 4) then
      MoveUp2(1)
    else if ((keyread0 = char(0)) and (keyread = keymap.DOWN)) then
      MoveDown2(1)
    else if ((keyread0 = char(0)) and (keyread = keymap.UP)) then
      MoveUp2(1)
    else if ((keyread0 = char(0)) and (keyread = keymap.PGDN)) then
      MoveDown2(maxdisplay[level])
    else if ((keyread0 = char(0)) and (keyread = keymap.PGUP)) then
      MoveUp2(maxdisplay[level])
    else if ((keyread0 = char(0)) and (keyread = keymap.ENDK)) then
      MoveDown2(listlen[level])
    else if ((keyread0 = char(0)) and (keyread = keymap.HOME)) then
      MoveUp2(listlen[level])
    else if (((keyread0 = char(0)) and (keyread = keymap.F1)) or (keyread = 'h')) and
      (level <> 4) then
      help()
    else if (((keyread0 = char(0)) and (keyread = keymap.F7)) or (keyread = 's')) and
      (level <> 4) then
      showDumpToIni(True)
    else if (((keyread0 = char(0)) and (keyread = keymap.F8)) or (keyread = 'l')) and
      (level <> 4) then
      showLoadFromIni()
    else if (((keyread0 = char(0)) and (keyread = keymap.F9)) or (keyread = 'v')) and
      (level <> 4) then
      versioninfo()
    else if (((keyread0 = char(0)) and (keyread = keymap.F10)) or (keyread = 'n')) and
      (level <> 4) then
      showDumpToIni(False)
    else if (((keyread0 = char(0)) and (keyread = keymap.F11)) or (keyread = 'r')) and
      (level <> 4) then
      SwitchHexDisplay()
    else if (((keyread0 = char(0)) and (keyread = keymap.F12)) or (keyread = 'i')) and
      (level <> 4) then
      egotrip()
    else if (level = 4) and ((keyread = keymap.SPACE) or (keyread = keymap.ENTER)) then
      inputlev4()
    else
      keyread := char(0);
  end;

  // =========================================
  //               INITIALIZATION
  // =========================================

  function GetOptsListLength(list: lev3Lst): integer;
  begin
    Result := high(list) - 1;
    while list[Result].typeid = -1 do
      Result := Result - 1;
  end;

  procedure SetOptsListLengths();
  var
    i: integer;
  begin
    for i := low(editableTypes) to high(editableTypes) do
      optsListlengths[i] := GetOptsListLength(mainlist[i].optsList^);
  end;

  procedure init();
  var
    i: integer;
  begin
    BinEd := False;
    HexDisplay := False;
    TextBackground(inactivelevbgcolor);
    TextColor(inactivelevcolor);
    clrScr();
    drawHeaders();
    level := 1;
    SetOptsListLengths();
    for i := 1 to high(listlen) do
    begin
      select[i] := 0;
      scroll[i] := 0;
      listlen[i] := 0;
    end;
    listlen[1] := high(mainlist);
    listlen[2] := versions[gamever].dataInfo[0].listLen;
    listlen[3] := optsListlengths[0];
    updateLev2Lists();
    FillWorkArea();
  end;

  procedure checkver(checkstring: string);
  var
    i: integer;
    s: string;
  begin
    gamever := -1;
    i := 0;
    while ((gamever = -1) and (i <= high(versions))) do
    begin
      s := readString(versions[i].verAddress);
      if (uppercase(s) = uppercase(checkstring)) then
        gamever := i;
      i := i + 1;
    end;
  end;

  procedure initfilename();
  var
    filename: string;
    filenamelen: integer;
  begin
    gamenamestr := defnamestr;
    gamepathstr := ExpandFileName(defnamestr);
    if ParamCount <> 0 then
    begin
      filename := ExtractFileName(ParamStr(1));
      filenamelen := length(filename);
      if (filenamelen > 0) then
      begin
        gamepathstr := ExpandFileName(ParamStr(1));
        gamenamestr := uppercase(filename);
      end;
    end;
  end;

{$R *.res}

begin
  clrscr();
  gotoxy(1, 3);
  initfilename();
  if (openfile(gamepathstr)) then
  begin
    checkver(gamenamestr);
    if (gamever = -1) and (uppercase(defnamestr) <> uppercase(gamenamestr)) then
      checkver(defnamestr);
    if gamever <> -1 then
    begin
      init();
      keyread := char(0);
      while (keyread <> char(255)) do
      begin
        inputKey(); // MAIN FUNCTION
      end;
      closeFile();
      NormVideo;
      clrscr();
      drawHeader();
      gotoxy(1, 25);
      gotoxy(1, 3);
      writeln('Program exited. Have fun!');
    end
    else
    begin
      closeFile();
      NormVideo;
      clrscr();
      drawHeader();
      gotoxy(1, 25);
      gotoxy(1, 3);
      writeln('ERROR: Cannot identify the Dune 2 version of the file "',
        gamenamestr, '".');
      cntin(True);
    end;
  end
  else
  begin
    drawHeader();
    gotoxy(1, 4);
    cntin(True);
  end;
end.
