// Program written by Maarten Meuris AKA Nyerguds
program dune_edit;
uses crt, dos;

TYPE
   chset  = array [1..8] of byte;
   coords = array [1..4] of integer;
   keymapper = Record
      up, down, left, right, space, enter, esc, back, pgup, pgdn, home, endk :char;
   end;
   option = Record
      typeid: Integer;
      text: String[20];
   end;
    lev2Lst = array[0..27] of String;
    lev3Lst = array[0..56] of option;

var inputval:longint;
    inputhex:array[0..3] of byte;
    optsOffset,level: integer;
    exefile:file;
    keyread:char;
    errormsg:String;
    select,scroll,maxdisplay,listlen:array [1..4] of integer;
    lev2List:lev2Lst;
    lev3List:lev3Lst;
const
    refAddress:array[0..3] of byte = ($32, $EC, $85, $00);
    framechar:array[1..2] of chset = (
                         ($DA, $BF, $C0, $D9, $C4, $B3, $C2, $C1),
                         ($C9, $BB, $C8, $BC, $CD, $BA, $CB, $CA));
    selectBoundaries:array[1..4] of coords = (
                         (3,4,25,5), (3,9,25,23), (32,4,54,23), (17,12,38,18));
    typeids:array[0..9] of String[15] = (
                         'unsigned byte', 'signed byte', '2-byte', '4-byte',
                         'owner', 'command', 'movement type', 'weapon type',
                         'structure', 'unit');

    mainlistlen:integer=1;
    mainlist:array[0..1] of String[20] = ('Units       ','Structures  ');

    unitlistlen:integer=26;
    unitOptsOffset:integer=195760;
    unitOptsListlen:integer=46;
    unitOptsList:lev3Lst = (
              (typeid: 2; text: 'Short name ID       '),
              (typeid: 3; text: 'Name code ref.      '),
              (typeid: 2; text: 'Long name ID        '),
              (typeid: 3; text: 'WSA file code ref.  '),
              (typeid: 2; text: 'Unknown 005         '),
              (typeid: 2; text: 'Unknown 006         '),
              (typeid: 2; text: 'Hit points          '),
              (typeid: 2; text: 'Unknown 008         '),
              (typeid: 2; text: 'Sidebar icon gfxID  '),
              (typeid: 2; text: 'Cost                '),
              (typeid: 2; text: 'Build Time          '),
              (typeid: 2; text: 'Tech level          '),
              (typeid: 8; text: 'Prerequisites       '),
              (typeid: 2; text: 'Unknown 014         '),
              (typeid: 0; text: 'Unknown 015         '),
              (typeid: 1; text: 'Upgrades needed     '),
              (typeid: 5; text: 'Sidebar command #1  '),
              (typeid: 5; text: 'Sidebar command #2  '),
              (typeid: 5; text: 'Sidebar command #3  '),
              (typeid: 5; text: 'Sidebar command #4  '),
              (typeid: 2; text: 'Unknown 021         '),
              (typeid: 0; text: 'Unknown 022         '),
              (typeid: 2; text: 'Unknown 023         '),
              (typeid: 2; text: 'Unknown 024         '),
              (typeid: 4; text: 'Owner               '),
              (typeid: 2; text: 'Unknown 026         '),
              (typeid: 2; text: 'Unknown 027         '),
              (typeid: 0; text: 'Unknown 028         '),
              (typeid: 0; text: 'Unknown 029         '),
              (typeid: 0; text: 'Unknown 030         '),
              (typeid: 0; text: 'Unknown 031         '),
              (typeid: 2; text: 'Sight               '),
              (typeid: 6; text: 'Movement type       '),
              (typeid: 2; text: 'Unknown 034         '),
              (typeid: 2; text: 'Speed               '),
              (typeid: 2; text: 'Unknown 036         '),
              (typeid: 2; text: 'Unit gfxID          '),
              (typeid: 2; text: 'Turret gfxID        '),
              (typeid: 2; text: 'Unknown 039         '),
              (typeid: 2; text: 'Unknown 040         '),
              (typeid: 2; text: 'Unknown 041         '),
              (typeid: 2; text: 'Weapon rate of fire '),
              (typeid: 2; text: 'Weapon range        '),
              (typeid: 2; text: 'Weapon damage       '),
              (typeid: 2; text: 'Unknown 045         '),
              (typeid: 7; text: 'Weapon type         '),
              (typeid: 2; text: 'Unknown 047         '),
              (typeid:-1; text: ''), (typeid:-1; text: ''),
              (typeid:-1; text: ''), (typeid:-1; text: ''),
              (typeid:-1; text: ''), (typeid:-1; text: ''),
              (typeid:-1; text: ''), (typeid:-1; text: ''),
              (typeid:-1; text: ''), (typeid:-1; text: ''));

    structlistlen:integer=18;
    structOptsOffset:integer=193930;
    structOptsListlen:integer=55;
    structOptsList:lev3Lst = (
              (typeid: 2; text: 'SNM                 '),
              (typeid: 3; text: 'NAME_CODE           '),
              (typeid: 2; text: 'LNM                 '),
              (typeid: 3; text: 'WSAF_CODE           '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 2; text: '-d-                 '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 2; text: '-e-                 '),
              (typeid: 2; text: '-f-                 '),
              (typeid: 2; text: '-g-                 '),
              (typeid: 2; text: '-h-                 '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 2; text: '-i-                 '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 1; text: 'jj                  '),
              (typeid: 1; text: 'kk                  '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 0; text: 'Unknown             '),
              (typeid: 2; text: '-l-                 '),
              (typeid: 2; text: '-m-                 '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 4; text: 'nn                  '),
              (typeid: 2; text: '-o-                 '),
              (typeid: 2; text: '-p-                 '),
              (typeid: 2; text: '-q-                 '),
              (typeid: 2; text: '-r-                 '),
              (typeid: 2; text: '-s-                 '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 1; text: 'tt                  '),
              (typeid: 1; text: 'tt                  '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 1; text: 'uu                  '),
              (typeid: 1; text: 'uu                  '),
              (typeid: 2; text: 'Unknown             '),
              (typeid: 1; text: 'vv                  '),
              (typeid: 1; text: 'vv                  '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-w-                 '),
              (typeid: 2; text: '-x-                 '),
              (typeid: 2; text: '-y-                 '),
              (typeid: 2; text: '-z-                 '),
              (typeid:-1; text: ''));
      keymap:keymapper = (up:    char(72);
                          down:  char(80);
                          left:  char(75);
                          right: char(77);
                          space: char(32);
                          enter: char(13);
                          esc:   char(27);
                          back:  char(8);
                          pgup:  char(73);
                          pgdn:  char(81);
                          home:  char(71);
                          endk:  char(79));

// =========================================
//                 MISCELLANEOUS
// =========================================

procedure cntin;
var pause: char;
begin
   gotoxy(1,25);
   repeat until keypressed;
    pause:=ReadKey();
end; {cntin}

function hexstr2byte(hex:string[2]):byte;
var i,value,multiplier:integer;
begin
  hexstr2byte:=0;
  multiplier:=16;
  if (length(hex)=0) then hex:='00';
  if (length(hex)=1) then hex:=concat('0' + hex);
  for i:=1 to 2 do
   begin
     if (byte(hex[i])>=65) and (byte(hex[i])<=70) then value:=byte(hex[i])-55
     else if (byte(hex[i])>=48) and (byte(hex[i])<=57) then value:=byte(hex[i])-48
     else value:=-1;
     if (value>=0) then
      begin
        hexstr2byte:=hexstr2byte+value*multiplier;
        multiplier:=1;
      end
     else
      begin
        i:=2;
        hexstr2byte:=0;
      end;
   end;
end;

function byte2hexstr(hex:byte):string;
begin
  byte2hexstr:='';
  if ((hex div 16) >=10) then byte2hexstr:=byte2hexstr+char((hex div 16)+55)
  else byte2hexstr:=byte2hexstr+char((hex div 16)+48);
  if ((hex mod 16) >=10) then byte2hexstr:=byte2hexstr+char((hex mod 16)+55)
  else byte2hexstr:=byte2hexstr+char((hex mod 16)+48);
end;


// =========================================
//                 INPUT / OUTPUT
// =========================================

function openFile(filename:String) : boolean;
var
  status:integer;
begin
  assign(exefile,filename);
  {$I-}
  reset(exefile,1);
  status:=IOResult;
  if status<>0 then
   begin
    writeln();
    writeln('Error opening "',filename,'"');
    openFile:=false;
    {$I+}
   end
  else openFile:=true;
end; {openFile}

procedure closeFile();
begin
  close(exefile);
  {$I+}
end; {closeFile}

function readByte(address:Integer):byte;
var
  actual:word;
begin
  seek(exefile,address);
  blockread (exefile,readByte,1,actual);
end;

procedure writeByte(address:Integer; data:byte);
var
  actual:word;
begin
  seek(exefile,address);
  blockwrite(exefile,data,1,actual);
end;

procedure readHex4(address:Integer);
var i:integer;
begin
  for i:=0 to 3 do
    inputhex[i]:=readByte(address+3-i);
end;

procedure writeHex4(address:Integer);
var i:integer;
begin
  for i:=0 to 3 do
    writeByte(address+3-i,inputhex[i]);
end;

// =========================================
//              DATA & OFFSETS
// =========================================

function getDataLen(opttype:integer):integer;
begin
  case opttype of
  -1 : getDataLen:=0;  // no val
   0 : getDataLen:=1;  // Unsigned byte (hex)
   1 : getDataLen:=1;  // Signed byte (dec)
   2 : getDataLen:=2;  // 2-byte
   3 : getDataLen:=4;  // 4-byte (HEX)
   4 : getDataLen:=1;  // owner
   5 : getDataLen:=2;  // command
   6 : getDataLen:=2;  // Movement type
   7 : getDataLen:=2;  // weapon type
   8 : getDataLen:=2;  // structures
   9 : getDataLen:=2;  // unit
   end;
end;

function getListLen(toindex:integer):integer;
var i:integer;
begin
  getListLen:=0;
  for i:=0 to toindex-1 do
  begin
    getListLen:=getListLen+getDataLen(lev3list[i].typeid);
  end;
end;

function getAddress(unitindex,optindex:integer):integer ;
begin
  getAddress:=optsOffset + (unitindex*getListLen(listlen[3]+1)) + getListLen(optindex);
end;

function getRefOffset():integer;
var multiplier,i:integer;
begin
  getRefOffset:=0;
  multiplier:=1;
  for i:=3 downto 0 do
   begin
     getRefOffset:=getRefOffset+((inputhex[i]-refAddress[i])*multiplier);
     multiplier:=multiplier*256;
   end;
end;

function getRefString(unitindex,optindex:integer):string;
var c:byte;
    i:integer;
    ref:integer;
begin
  readHex4(getAddress(unitindex,optindex));
  ref:=getRefOffset();
  i:=0;
  c:=0;
  for i:=0 to 3 do
   if inputHex[i]<>0 then c:=$FF;
  if (ref>0) and (c<>0) then
   begin
     getRefString:='';
     i:=0;
     while (c<>0) and (i<18) do
      begin
        c:=readByte(getRefOffset()+i);
        getRefString:=concat(getRefString+char(c));
        i:=i+1;
      end;
   end
  else if c=0 then getRefString:='<no reference>'
  else getRefString:='<invalid ref>';
end;

function get2ByteString(unitindex,optindex:integer):string;
var
    i:integer;
begin
  i:=readByte(getAddress(unitindex,optindex));
  i:=i+readByte(getAddress(unitindex,optindex)+1)*256;
  if i>$7FFF then i:=i-$10000;
  str(i,get2ByteString);
end;

function getBytestr(unitindex,optindex:integer):string;
begin
  str(readByte(getAddress(unitindex,optindex)),getBytestr);
end;

function getOwnerstr(unitindex,optindex:integer):string;
var
    i:integer;
begin
  i:=readByte(getAddress(unitindex,optindex));
  getOwnerstr:='';
  if  (i mod 2)        = 1 then getOwnerstr:=getOwnerstr+ 'Hk,';
  if ((i div 2) mod 2) = 1 then getOwnerstr:=getOwnerstr+ 'At,';
  if ((i div 4) mod 2) = 1 then getOwnerstr:=getOwnerstr+ 'Or,';
  if ((i div 8) mod 2) = 1 then getOwnerstr:=getOwnerstr+ 'Fr,';
  if ((i div 16)mod 2) = 1 then getOwnerstr:=getOwnerstr+ 'Sr,';
  if ((i div 32)mod 2) = 1 then getOwnerstr:=getOwnerstr+ 'Mr,';
  if length(getOwnerstr)>0 then getOwnerstr:=copy(getOwnerstr,1,length(getOwnerstr)-1);
end;


function getDataStr(unitindex,optindex:integer):string;
begin
  getDataStr:='';
  case lev3list[optindex].typeid of
   0 : getDataStr:=byte2hexstr(readByte(getAddress(unitindex,optindex))); // Unsigned byte (hex)
   1 : getDataStr:=getBytestr(unitindex,optindex);                        // Signed byte (dec)
   2 : getDataStr:=get2ByteString(unitindex,optindex);                    // 2-byte
   3 : getDataStr:=getRefString(unitindex,optindex);                      // 4-byte (HEX)
   4 : getDataStr:=getOwnerstr(unitindex,optindex);                                                // owner
   5 : getDataStr:=get2ByteString(unitindex,optindex);                    // command
   6 : getDataStr:=get2ByteString(unitindex,optindex);                    // Movement type
   7 : getDataStr:=get2ByteString(unitindex,optindex);                    // weapon type
   8 : getDataStr:=get2ByteString(unitindex,optindex);                    // structures
   9 : getDataStr:=get2ByteString(unitindex,optindex);                    // unit
   end;
end;


function inputValue(minval,maxval:integer):boolean;
var charin: char;
    inputstr:String[10];
    minlen,maxlen:integer;
begin
   charin:=char(0);
   str(minval,inputstr);
   minlen:=Length(inputstr);
   str(maxval,inputstr);
   maxlen:=Length(inputstr);
   inputstr:='';
   while (charin<>keymap.esc) and (charin<>keymap.enter) do
   begin
    charin:=ReadKey();
    if ((byte(charin)=45) and (Length(inputstr)=0) and (minval<0))
        or ((byte(charin)>=48) and (byte(charin)<=57)
          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;
   val(inputstr,inputval);
   inputValue:=false;
   errormsg:='';
   if (charin=keymap.esc) then errormsg:='Canceled'
   else if (inputval>maxval) then errormsg:='Value too large!'
   else if (inputval<minval) then errormsg:='Value too small!'
   else inputValue:=true;
end; {inputValue}


function inputHexBytes(bytes:integer):boolean;
var charin: char;
    inputstr:String[10];
    i:integer;
begin
   charin:=char(0);
   inputstr:='';
   while (charin<>keymap.esc) and (charin<>keymap.enter) do
   begin
    charin:=ReadKey();
    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)<(bytes*2)) then
     begin
       inputstr:=inputstr+charin;
       write(charin);
       if (length(inputstr)>0) and ((length(inputstr) mod 2)=0) then write(' ');
     end
    else if (charin=keymap.back) and (Length(inputstr)>0) then
     begin
       if ((length(inputstr) mod 2)=0) then write(keymap.back);
       inputstr:=Copy(inputstr, 1, (Length(inputstr)-1));
       write(keymap.back);
       write('_');
       write(keymap.back);
     end;
   end;
   while (length(inputstr)<>(bytes*2)) do
   begin
       inputstr:=inputstr+'0';
       write('0');
       if (length(inputstr)>0) and ((length(inputstr) mod 2)=0) then write(' ');
   end;
   for i:=0 to 3 do
    begin
     inputhex[i]:=hexstr2byte(copy(inputstr,1+i*2,2));
    end;
   inputHexBytes:=false;
   errormsg:='';
   if (charin=keymap.esc) then errormsg:='Canceled'
   else inputHexBytes:=true;
end;

// =========================================
//              GENERAL LAYOUT
// =========================================

procedure drawLine(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 drawFrame(x,y,w,h,m:integer; active,fill:boolean );
var charset,count,count2:integer;
begin
   if active then charset:=2
   else charset:=1;
   gotoxy(x,y);
   write(char(framechar[charset,1]));
   for count:=1 to (w-2) do
    write(char(framechar[charset,5]));
   write(char(framechar[charset,2]));
   for count:=1 to (h-2) do
    begin
      gotoxy(x,y+count);
      write(char(framechar[charset,6]));
      if fill then for count2:=1 to (w-2) do write(' ');
      gotoxy(x+w-1,y+count);
      write(char(framechar[charset,6]));
    end;
   gotoxy(x,y+h-1);
   write(char(framechar[charset,3]));
   for count:=1 to (w-2) do
    write(char(framechar[charset,5]));
   write(char(framechar[charset,4]));

   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]));
      drawLine(x+m-1,y+1,h-2,char(framechar[charset,6]));
    end;
   gotoxy(0,0);
end; {drawFrame}


// =========================================
//             SELECTION DISPLAY
// =========================================

procedure drawSelect(l:integer);
begin
  if (l<>4) or (l=level) then
   begin
     drawLine(selectBoundaries[l][1],selectBoundaries[l][2],maxdisplay[l]+1,' ');
     gotoxy(selectBoundaries[l][1],selectBoundaries[l][2]+select[l]);
     write('[');
     drawLine(selectBoundaries[l][3],selectBoundaries[l][2],maxdisplay[l]+1,' ');
     gotoxy(selectBoundaries[l][3],selectBoundaries[l][2]+select[l]);
     write(']');
   end;
   gotoxy(1,25);
end;

procedure drawSelect();
begin
  drawSelect(level);
end;

procedure drawSelects();
var i:integer;
begin
  for i:=1 to 4 do drawSelect(i);
end;


// =========================================
//                 LIST DISPLAY
// =========================================

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
    showlist[i]:=getDataStr(select[2]+scroll[2],i+scroll[3]);
  end;
  for i:=0 to last do
  begin
    gotoxy(selectBoundaries[3][1]+27,selectBoundaries[3][2]+i);
    write('                  ');
    gotoxy(selectBoundaries[3][1]+27,selectBoundaries[3][2]+i);
    write(showlist[i]);
  end;
end;

procedure drawLev3();
var i,last: integer;
begin
  if listlen[3]<maxdisplay[3] then last:=listlen[3]
  else last:=maxdisplay[3];
  for i:=0 to last do
  begin
    gotoxy(selectBoundaries[3][1]+2,selectBoundaries[3][2]+i);
    write(lev3List[(i+scroll[3])].text);
  end;
  drawLev3data();
end;

procedure drawLev2();
var i,last: integer;
begin
  if listlen[2]<maxdisplay[2] then last:=listlen[2]
  else last:=maxdisplay[2];
  for i:=0 to last do
  begin
    gotoxy(selectBoundaries[2][1]+2,selectBoundaries[2][2]+i);
    write('                   ');
    gotoxy(selectBoundaries[2][1]+2,selectBoundaries[2][2]+i);
    write(lev2List[(i+scroll[2])]);
  end;
end;

procedure drawLev1();
var i,last: integer;
begin
  if listlen[1]<maxdisplay[1] then last:=listlen[1]
  else last:=maxdisplay[1];
  for i:=0 to last do
  begin
    gotoxy(selectBoundaries[1][1]+2,selectBoundaries[1][2]+i);
    write(mainlist[(i+scroll[1])]);
  end;
end;


procedure drawList(l:integer);
var i:integer;
begin
  if select[1]=0 then
   // LOAD UNITS LIST DATA
   begin
     listlen[3]:=unitOptsListLen;
     lev3List:=unitOptsList;
     optsOffset:=UnitOptsOffset;
     listlen[2]:=UnitListLen;
     for i:=0 to listlen[2] do
       lev2List[i]:=getRefString(i,1);
   end
  else if select[1]=1 then
   // LOAD STRUCTURES LIST DATA
   begin
     listlen[3]:=structOptsListLen;
     lev3List:=structOptsList;
     optsOffset:=StructOptsOffset;
     listlen[2]:=StructListLen;
     for i:=0 to listlen[2] do
       lev2List[i]:=getRefString(i,1);
  end;
  if l=1 then drawLev1()
  else if l=2 then drawLev2()
  else if l=3 then drawLev3();
end;

procedure drawList();
begin
   drawList(level);
end;

procedure drawLists();
var i:integer;
begin
   for i:=1 to 3 do drawList(i);
end;

procedure updateLists();
var i: integer;
begin
  for i:=2 to 3 do
    begin
      scroll[i]:=0;
      select[i]:=0;
      drawSelect(i);
    end;
    drawLists();
end;


// =========================================
//                INPUT LAYOUT
// =========================================

procedure inputInt(minval,maxval:integer);
var isInput:boolean;
begin
   write(' (');
   write(minval);
   write(',');
   write(maxval);
   write(')');
   gotoxy(selectBoundaries[4][1]+2,selectBoundaries[4][2]);
   write('__________________');
   drawSelect();
   gotoxy(selectBoundaries[4][1]+2,selectBoundaries[4][2]);
   isInput:=inputValue(minval,maxval);
   gotoxy(selectBoundaries[4][1]+32,selectBoundaries[4][2]);
   if isInput then
    begin
      write('Saved.');
      gotoxy(selectBoundaries[4][1]+32,selectBoundaries[4][2]+2);
      write(inputval);
    end
   else write(errormsg);
   cntin();
   level:=3;
end;

procedure inputHex4();
var isInput:boolean;
begin
   gotoxy(selectBoundaries[4][1]+2,selectBoundaries[4][2]);
   write('__ __ __ __');
   drawSelect();
   gotoxy(selectBoundaries[4][1]+2,selectBoundaries[4][2]);
   isInput:=inputHexBytes(4);
   gotoxy(selectBoundaries[4][1]+32,selectBoundaries[4][2]);
   if isInput then
    begin
      write('Saved.');
      gotoxy(selectBoundaries[4][1]+32,selectBoundaries[4][2]+2);
      write(concat( byte2hexStr(inputhex[0])+' '
                   +byte2hexStr(inputhex[1])+' '
                   +byte2hexStr(inputhex[2])+' '
                   +byte2hexStr(inputhex[3])));
    end
   else write(errormsg);
   cntin();
   level:=3;
end;
// =========================================
//                MAIN LAYOUT
// =========================================

procedure drawLevFrame(lvl:integer);
begin
  case lvl of
   1 : drawFrame(1,3,27,4,-1,level=1,false);
   2 : drawFrame(1,8,27,17,-1,level=2,false);
   3 : drawFrame(30,3,50,22,27,level=3,false);
   4 : drawFrame(15,5,52,15,-1,level=4,true);
  end;
end;

procedure drawLevFrame();
begin
  drawLevFrame(level);
end;

procedure clearWorkArea();
var i: integer;
begin
    for i:=3 to 25 do
      begin
        gotoxy(1,i);
        clreol;
      end;
end;

procedure drawLayout();
begin
   if level=4 then
   begin
     drawLevFrame(3);
     drawLevFrame(level);
     gotoxy(17,6);
     write('Section   : ');
     write(lev2list[select[2]+scroll[2]]);
     gotoxy(17,7);
     write('Option    : ');
     write(lev3list[select[3]+scroll[3]].text);
     gotoxy(17,8);
     write('Data type : ');
     write(typeids[lev3list[select[3]+scroll[3]].typeid]);

     case lev3list[select[3]+scroll[3]].typeid of
      0 : inputInt(0,255);                    //unsigned byte
      1 : inputInt(-128,127);                 //signed byte
      2 : inputInt(-32768,32767);             //2-byte
      3 : inputHex4();                        //4-byte
      4 : inputInt(0,255);                    //owner
      5 : inputInt(-32768,32767);             //command
      6 : inputInt(-32768,32767);             //Movement type
      7 : inputInt(-32768,32767);             //weapon type
      8 : inputInt(0,255);                    //structures
      9 : inputInt(0,255);                    //unit
      end;
     clearWorkArea();
     drawLists();
     drawSelects();
   end;
   drawLevFrame(1);
   drawLevFrame(2);
   drawLevFrame(3);
   drawSelect();
end;

procedure FillWorkArea();
begin
      ClearWorkArea();
      drawLayout();
      drawLists();
      drawSelects();
end;

// =========================================
//                NAVIGATION
// =========================================

procedure MoveDown(lines:integer);
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
    // scroll
    begin
      scroll[level]:=scroll[level]+lines;
      if scroll[level] > (listlen[level]-maxdisplay[level]) then scroll[level]:=(listlen[level]-maxdisplay[level]);
    end;
  drawList();
  if level=1 then updateLists();
  if level=2 then drawLev3data();
end;

procedure MoveUp(lines:integer);
begin
  if ((select[level]-lines)>0) then
    // move
    select[level]:=select[level]-lines
  else
    // move beyond visible field
    begin
     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;
  drawList();
  if level=1 then updateLists();
  if level=2 then drawLev3data();
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;

procedure input();
begin
 if level<>4 then
  begin
   if (keyread=keymap.ESC) then keyread:=char(255)
   else if (level=3) and (keyread=keymap.ENTER) or (keyread=keymap.SPACE) then level:=4
   else if (keyread=keymap.RIGHT) then MoveRight()
   else if (keyread=keymap.LEFT)  then MoveLeft()
   else if (keyread=keymap.DOWN)  then MoveDown(1)
   else if (keyread=keymap.UP)    then MoveUp(1)
   else if (keyread=keymap.PGDN)  then MoveDown(maxdisplay[level])
   else if (keyread=keymap.PGUP)  then MoveUp(maxdisplay[level])
   else if (keyread=keymap.ENDK)  then MoveDown(listlen[level])
   else if (keyread=keymap.HOME)  then MoveUp(listlen[level]);
   drawLayout();
  end
 else
  begin
   if (keyread=keymap.DOWN) then MoveDown(level)
   else if (keyread=keymap.UP) then MoveUp(level);
   if (keyread=keymap.ESC) and (level=4) then begin level:=level-1; FillWorkArea(); end;
  end;
  // if level<3 then drawList(2);
end;

// =========================================
//               INITIALIZATION
// =========================================

procedure init();
var i: integer;
begin
  for i:=1 to 25 do
  begin
    gotoxy(1,i);
    clreol;
  end;
  gotoxy(1,1);
  for i:=1 to 80 do
    write(char(framechar[2,5]));
  gotoxy(3,1);
  write(' Dune II Editor ');
  gotoxy(65,1);
  write(' by Nyerguds ');
  level:=1;
  listlen[1]:=mainListlen;
  listlen[2]:=unitListLen;
  listlen[3]:=unitOptsListLen;
  listlen[4]:=0;
  for i:=1 to 4 do
  begin
   select[i]:=0;
   scroll[i]:=0;
   maxdisplay[i]:=selectBoundaries[i][4]-selectBoundaries[i][2];
   drawSelect(i);
  end;
  drawLists();
end;

begin
  if (openfile('DUNE2.EXE')) then
   begin
    init();
    drawLayout();
    keyread:=char(0);
      while keyread <> char(255) do
      begin
        keyread:=ReadKey();
        gotoxy(1,25);
        input();
      end;
    closeFile();
   end
  else cntin;
end.

