home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Home Edutainment Collection 4: Games & Extensions
/
Aztech-HomeEdutainmentCollection-Vol4-3DGamesExtensions.iso
/
wc
/
dm2conv.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1995-05-24
|
45KB
|
1,548 lines
{$A+,B-,D+,E+,F-,G+,I-,L+,N-,O-,P-,Q-,R-,S-,T-,V-,X+}
{$M 16384,0,655360}
{ DM2CONV v3.0 by Vincenzo Alcamo }
{ This program is Public Domain }
type
CHAR8 = array[1..8] of char;
WAD_HEADER = record
Sig : longint;
Num : longint;
Start : longint;
end;
WAD_ENTRY = record
Start : longint;
Size : longint;
Name : CHAR8;
end;
THING = record
XPos : integer;
YPos : integer;
Angle: integer;
Code : word;
Flags: word;
end;
SIDEDEF = record
XOffs,YOffs : integer;
UpT,LoT,MidT : CHAR8;
Sector : word;
end;
SECTOR = record
Y1,Y2 : integer;
Floor,Ceiling : CHAR8;
Lum,Action,Tag : word;
end;
LINEDEF = record
V1,V2 : word;
Attr : word;
Action,Tag : word;
RSide,LSide: word;
end;
GAMETYPE = (GT_DOOM,GT_DOOM2,GT_HERETIC);
ERRORS = (ERR_NONE,ERR_TOOSYM,ERR_ENDIF_NOIF,ERR_TOORESP,
ERR_NORESP,ERR_READRESP,ERR_NOLABEL,
ERR_BADEND,ERR_NOEQ,ERR_BADNUM,ERR_TOOREPN,
ERR_NOTHINGMODE,ERR_NOCOND,
ERR_LASTSYNTAX, {marks the last syntax error}
ERR_BADELSE,ERR_BADENDIF,
ERR_NOMEM,ERR_OPEN,ERR_READ,ERR_WRITE,ERR_TOOENTRY,ERR_PWAD);
const
IWAD_SIG = Ord('I')+(Ord('W')+(Ord('A')+Ord('D') shl 8) shl 8) shl 8;
PWAD_SIG = Ord('P')+(Ord('W')+(Ord('A')+Ord('D') shl 8) shl 8) shl 8;
N_THINGS = 'THINGS'#0#0;
N_SECTORS= 'SECTORS'#0;
N_SIDEDEFS='SIDEDEFS';
N_LINEDEFS='LINEDEFS';
NULL_NAME= #0#0#0#0#0#0#0#0;
BUFFSIZE = 65528; {biggest allocable block }
MAXENTRY = BUFFSIZE div sizeof(WAD_ENTRY);
MAXTHING = BUFFSIZE div sizeof(THING);
MAXSIDE = BUFFSIZE div sizeof(SIDEDEF);
MAXSECT = BUFFSIZE div sizeof(SECTOR);
MAXLINE = BUFFSIZE div sizeof(LINEDEF);
MAXREPN = 1024; { maximum number of replace name}
MAXREPT = 4096; { maximum number of rep thing info }
MAXSYMS = 1024; { maximum space for symbol table }
MAXRESP = 10; { maximum number of nested response files }
MAXACTION= 256; { maximum number of linedef/sector action to replace }
MAXOBJ = 500; { maximum number of object info }
REP_FLAG = $4000; { maximum value for thing id / flag }
REP_CONV = $2000; { flag for converted objects }
REP_ALL = REP_FLAG+REP_CONV; {all objects}
REP_DEAF = $0008; { flag for DEAF object: defined by DOOM engine }
REP_MULTI= $0010; { flag for MULTI object: defined by DOOM engine }
REP_ZERO = $0020; { flag for ZERO object }
REP_RANGE= $8000; { flag for range expression}
REP_QIF = $C000; { flag for question_mark }
REP_QELSE= $C100; { ?ELSE command }
REP_QEND = $C200; { ?END command }
KEY_IFDEF = 'IFDEF';
KEY_IFNDEF= 'IFNDEF';
KEY_ELSE = 'ELSE';
KEY_ENDIF = 'ENDIF';
SYM_SOURCE = 'SOURCE';
SYM_DEST = 'DEST';
SYM_HELP = 'HELP';
SYM_SEED = 'SEED';
SYM_FROM = 'FROM';
SYM_TO = 'TO';
SYM_REMAP = 'REMAP';
SYM_ONCE = 'ONCE';
SYM_MIX = 'MIX';
SYM_DEBUG = 'DEBUG';
MUS2NAMES : array[1..32] of CHAR8 = (
'D_RUNNIN','D_STALKS','D_COUNTD','D_BETWEE','D_DOOM'#0#0,
'D_THE_DA','D_SHAWN'#0,'D_DDTBLU','D_IN_CIT','D_DEAD'#0#0,
'D_STLKS2','D_THEDA2','D_DOOM2'#0,'D_DDTBL2','D_RUNNI2',
'D_DEAD2'#0,'D_STLKS3','D_ROMERO','D_SHAWN2','D_MESSAG',
'D_COUNT2','D_DDTBL3','D_AMPIE'#0,'D_THEDA3','D_ADRIAN',
'D_MESSG2','D_ROMER2','D_TENSE'#0,'D_SHAWN3','D_OPENIN',
'D_EVIL'#0#0,'D_ULTIMA');
type
REPNAME = record
Before : CHAR8;
After : CHAR8;
end;
REPACTION = record
After : word;
Before : word;
end;
RESPONSE = record
RFile : text;
Name : string;
IfLev : integer;
Line : integer;
end;
S_GAMETYPE = set of GAMETYPE;
OBJINFO = record
Code : word;
Radius : word;
Height : word;
Games : S_GAMETYPE;
Name : string[20];
end;
A_REPNAME = array[1..MAXREPN] of REPNAME;
A_BUFFER = array[1..BUFFSIZE] of byte;
A_DIRLIST = array[1..MAXENTRY] of WAD_ENTRY;
A_THINGS = array[1..MAXTHING] of THING;
A_SIDEDEFS= array[1..MAXSIDE] of SIDEDEF;
A_SECTORS = array[1..MAXSECT] of SECTOR;
A_LINEDEFS= array[1..MAXLINE] of LINEDEF;
A_REPLACE = array[1..MAXREPT] of word;
A_REPACTION=array[1..MAXACTION] of REPACTION;
A_OBJINFO = array[1..MAXOBJ] of OBJINFO;
SYMBOLSPACE=array[1..MAXSYMS] of char;
var
Buffer : ^A_BUFFER;
Dirlist : ^A_DIRLIST;
Things : ^A_THINGS;
Sidedefs : ^A_SIDEDEFS;
Sectors : ^A_SECTORS;
Linedefs : ^A_LINEDEFS;
Symbols : ^SYMBOLSPACE;
RepThing : ^A_REPLACE;
RepText : ^A_REPNAME;
RepFloor : ^A_REPNAME;
RepDirs : ^A_REPNAME;
RepLAct : ^A_REPACTION;
RepSAct : ^A_REPACTION;
Objects : ^A_OBJINFO;
Resp : array[1..MAXRESP] of RESPONSE;
RespLev : integer;
SourceName : string; {name of source file}
DestName : string; {name of destination file}
RandomSeed : longint; {seed for random number generator}
Game1 : GAMETYPE; {type of source wad}
Game2 : GAMETYPE; {type of dest wad}
const
NRepThing: integer = 0; {number of replaces for each category}
NRepText : integer = 0;
NRepFloor: integer = 0;
NRepDirs : integer = 0;
NRepLAct : integer = 0;
NRepSAct : integer = 0;
NObjects : integer = 0;
RemappedThing : word = 0; {various remap counters}
RemappedText : word = 0;
RemappedFloor : word = 0;
RemappedDirs : word = 0;
RemappedLAct : word = 0;
RemappedSAct : word = 0;
RemappedLev : word = 0;
RemappedMus : word = 0;
Debug : boolean = False; {debug mode}
{Return a right-padded string of N characters from a string}
function StringN(s:String;n:Integer):String;
var i:Integer;
begin
StringN:=Copy(s,1,n);
StringN[0]:=Char(n);
for i:=Length(s)+1 to n do StringN[i]:=' ';
end;
{Converts string to uppercase}
function Upper(s:String):String;
var i:Integer;
begin
Upper[0]:=s[0];
for i:=1 to Length(s) do Upper[i]:=UpCase(s[i]);
end;
{Add a suffix(extension) to a filename (only if the filename hasn't one)}
function AddSuffix(s,n:String):String;
var i:Integer;
begin
i:=Length(s);
while i>0 do
if s[i]='.' then break
else dec(i);
if i>0 then AddSuffix:=s
else AddSuffix:=s+'.'+n;
end;
{Return the first word of a string}
function GetWord(var s:string):string;
var i,j:integer;
begin
j:=1;
while (j<=length(s)) and (s[j]<=#32) do inc(j);
i:=j;
while (i<=length(s)) and (s[i]>#32) do inc(i);
GetWord:=Copy(s,j,i-j);
s:=Copy(s,i,255);
end;
{Add a long to a pointer}
function AddPtr(p:pointer;l:longint):pointer;
begin
AddPtr:=pointer(longint(p)+l);
end;
{Return the value of a specified environment variable}
{If name is '' the full path of the program is returned}
function GetEnv(name:string):string; assembler;
asm
push ds
mov ds, PrefixSeg
mov ax, ds:[$2C]
mov ds, ax
xor si, si
cld
les di, name
xor dx, dx
mov dl, es:[di]
inc di
mov bx, di
@@CICLO:
cmp byte ptr ds:[si], 0
je @@FINE
mov di, bx
mov cx, dx
rep cmpsb
jne @@NEXT
lodsb
cmp al, '='
je @@FOUND
@@NEXT:
cmp dx, 0
je @@ZERO
dec si
@@ZERO:
lodsb
cmp al, 0
jne @@ZERO
jmp @@CICLO
@@FINE:
cmp dx, 0
jne @@FOUND
add si, 3
@@FOUND:
les di, @RESULT
push di
inc di
xor cx, cx
@@COPY:
lodsb
stosb
inc cx
cmp al, 0
jne @@COPY
xchg ax, cx
dec ax
pop di
stosb
pop ds
end;
{Concat the exe path with the specified filename}
function AsInEXEDir(s:string):string;
var t:string;
i:integer;
begin
t:=GetEnv('');
i:=length(t);
while (i>0) and (t[i]<>'\') and (t[i]<>'/') do dec(i);
t[0]:=chr(i);
i:=length(s);
while (i>0) and (s[i]<>'\') and (s[i]<>'/') do dec(i);
AsInEXEDir:=t+copy(s,i+1,255);
end;
procedure SyntaxHelp;
begin
if RespLev>0 then
writeln('(Line ',Resp[RespLev].Line,' in file ',Resp[RespLev].Name,')');
end;
var ErrStr:string;
procedure MyHalt(err:ERRORS);
begin
if err<>ERR_NONE then write('ERROR: ');
case err of
ERR_NOMEM: writeln('Not enough memory!');
ERR_TOOSYM: writeln('Symbol table full!');
ERR_ENDIF_NOIF: writeln('ENDIF without IF');
ERR_TOORESP: writeln('Too many nested response files!');
ERR_NORESP: writeln('Cannot find response file!');
ERR_READRESP: writeln('Cannot read response file!');
ERR_NOLABEL: writeln('Label not found in response file!');
ERR_BADEND: writeln('Expression incorrectly terminated');
ERR_NOEQ: writeln('Missing ''='' in expression!');
ERR_BADNUM: writeln('Bad number in expression!');
ERR_NOTHINGMODE: writeln('Command not allowed outside THINGS section!');
ERR_NOCOND: writeln('No valid relational operator specified!');
ERR_BADELSE: writeln('Bad ?ELSE expression found!');
ERR_BADENDIF:writeln('Bad ?END expression found!');
ERR_TOOREPN: writeln('Replace table full!');
ERR_READ: writeln('Cannot read from file: ',SourceName);
ERR_WRITE: writeln('Cannot write to file: ',DestName);
ERR_OPEN: writeln('Cannot open file: ',ErrStr);
ERR_PWAD: writeln('File is not a valid WAD: ',SourceName);
ERR_TOOENTRY:writeln('Too many entries in file: ',SourceName);
end;
if (err>ERR_NONE) and (err<ERR_LASTSYNTAX) then SyntaxHelp;
Halt(ord(err));
end;
function MyHeapError(size:word):integer; far;
begin
if size<>0 then MyHalt(ERR_NOMEM);
MyHeapError:=1;
end;
procedure Initialize;
begin
RespLev:=0;
HeapError:=@MyHeapError;
New(RepText);
New(RepFloor);
New(RepDirs);
New(RepThing);
New(Buffer);
New(DirList);
New(RepLAct);
New(RepSAct);
New(Objects);
New(Symbols);
Symbols^[1]:=#0;
Things:=pointer(Buffer);
Linedefs:=pointer(Buffer);
Sidedefs:=pointer(Buffer);
Sectors:=pointer(Buffer);
end;
var SymbolName : ^string;
SymbolValue: ^string;
SymbolFound: boolean;
function GetSymbol(name:string):string;
begin
SymbolName:=@Symbols^;
while SymbolName^<>'' do begin
SymbolValue:=AddPtr(SymbolName,length(SymbolName^)+1);
if SymbolName^=name then begin
GetSymbol:=SymbolValue^;
SymbolFound:=True;
exit;
end;
SymbolName:=AddPtr(SymbolValue,length(SymbolValue^)+1);
end;
SymbolFound:=False;
GetSymbol:=Upper(GetEnv(name));
end;
procedure SetSymbol(name,value:string);
begin
GetSymbol(name);
if SymbolFound then begin
SymbolValue:=AddPtr(SymbolValue,length(SymbolValue^)+1);
while SymbolValue^<>'' do begin
SymbolName^:=SymbolValue^;
SymbolValue:=AddPtr(SymbolValue,length(SymbolName^)+1);
SymbolName:=AddPtr(SymbolName,length(SymbolName^)+1);
end;
end;
if value<>'' then begin
if longint(SymbolName)+length(name)+length(value)+2>longint(Symbols)+sizeof(SYMBOLSPACE) then
MyHalt(ERR_TOOSYM);
SymbolName^:=name;
SymbolValue:=AddPtr(SymbolName,length(name)+1);
SymbolValue^:=value;
SymbolName:=AddPtr(SymbolValue,length(value)+1);
end;
SymbolName^:='';
end;
procedure Title;
begin
writeln('DM2CONV v3.0 by Vincenzo Alcamo (alcamo@arci01.bo.cnr.it) VERSION 950521');
end;
procedure Help;
begin
Title;
writeln('Interchange maps among DOOM, DOOM II and HERETIC.');
writeln;
writeln('Usage: DM2CONV <input> [output] [/symbol[=[value]]]... <@response>...');
writeln;
writeln(' input name of wad file to convert');
writeln(' output name of output file (if omitted, the source is overwritten)');
writeln(' symbol symbol to define (/symbol=value) or undefine (/symbol=)');
writeln(' @response name of response file');
writeln;
writeln('To convert levels from game_A to game_B use the appropriate response file,');
writeln('following this name convention: D=DOOM, D2=DOOM II, H=HERETIC.');
writeln('Example for DOOM to HERETIC conversion: DM2CONV input output @:DTOH');
writeln('Example for DOOM II to DOOM conversion: DM2CONV input output @:D2TOD');
writeln;
writeln('If you use the wads built by GFXMAKER you should define the GFX symbol.');
writeln('Example for HERETIC to DOOM conversion: DM2CONV input output /GFX @:HTOD');
writeln;
writeln('Full instructions are contained inside DM2CONV.DOC: this file and the official');
writeln('response file DEFAULT.RSP are part of the DM2CONV distribution package.');
writeln('REMEMBER: DM2CONV is PUBLIC DOMAIN (or FREEWARE if you prefer).');
end;
function MyVal(s:string):integer;
var i,j:integer;
begin
Val(s,j,i);
if (i<>0) or (j>=REP_FLAG) or (j<0) then MyHalt(ERR_BADNUM);
MyVal:=j;
end;
procedure ParseSymbol(s:string);
var i:integer;
begin
if s='' then begin
SymbolName:=@Symbols^;
while SymbolName^<>'' do begin
SymbolValue:=AddPtr(SymbolName,length(SymbolName^)+1);
writeln(SymbolName^,'=',SymbolValue^);
SymbolName:=AddPtr(SymbolValue,length(SymbolValue^)+1);
end;
end
else begin
i:=1;
while (i<=length(s)) and (s[i]<>'=') do inc(i);
if i>length(s) then SetSymbol(s,s)
else SetSymbol(copy(s,1,i-1),copy(s,i+1,255));
end;
end;
function GetArgument:string;
var i:integer;
s:string;
begin
if eof(Resp[RespLev].RFile) then begin
close(Resp[RespLev].RFile);
dec(RespLev);
s:='';
end
else begin
readln(Resp[RespLev].RFile,s);
inc(Resp[RespLev].Line);
if ioresult<>0 then MyHalt(ERR_READRESP);
i:=1;
while (i<=length(s)) and (s[i]<=#32) do inc(i);
s:=copy(s,i,255);
end;
i:=1;
while i<=length(s) do begin
if s[i]=';' then s[0]:=chr(i-1);
inc(i);
end;
i:=length(s);
while (i>0) and (s[i]<=#32) do dec(i);
s[0]:=chr(i);
GetArgument:=s;
end;
function GetIdentifier(var s:string):string;
var i:integer;
begin
s:=s+#0;
i:=1;
while (s[i]='_') or ((s[i]>='0') and (s[i]<='9')) or ((s[i]>='A') and (s[i]<='Z')) do inc(i);
GetIdentifier:=Copy(s,1,i-1);
s:=Copy(s,i,length(s)-i);
end;
function CheckLevel(var s:string):word;
var i,j:word;
begin
j:=0;
if (length(s)>0) and (s[1]=':') then begin
i:=2;
while i<=length(s) do begin
case s[i] of
'0': j:=j or REP_ZERO; {allow no skill flags}
'1': j:=j or 1; {skill level 1-2}
'2': j:=j or 2; {skill level 3}
'3': j:=j or 4; {skill level 4-5}
'D': j:=j or REP_DEAF; {deaf flag}
'M': j:=j or REP_MULTI; {multiplayer}
'O': j:=j or REP_FLAG; {only objects not already converted}
'A': j:=j or REP_ALL; {all objects}
'C': j:=j or REP_CONV; {only converted objects}
else break;
end;
inc(i);
end;
s:=Copy(s,i,255);
end;
CheckLevel:=j;
end;
procedure ParseThing(var s:string);
var i,j,k: integer;
rnum : integer;
once : word;
procedure GetOnceFlag;
var t:string;
i,j:integer;
begin
t:=GetSymbol(SYM_ONCE);
if t='' then j:=0
else begin
val(t,j,i);
if i<>0 then j:=1;
end;
case j of
0: once:=REP_ALL;
2: once:=REP_CONV;
else once:=REP_FLAG;
end;
end;
function GetNum:word;
var t:string;
i,j,k,l:integer;
begin
s:=Copy(s,2,255);
t:=GetIdentifier(s);
if length(t)=0 then MyHalt(ERR_BADNUM);
if (t[1]>='0') and (t[1]<='9') then GetNum:=MyVal(t)
else begin
l:=0;
for i:=1 to NObjects do with Objects^[i] do begin
j:=1;
k:=1;
repeat
if Name[k]<=' ' then inc(k)
else if t[j]<>UpCase(Name[k]) then break
else begin
inc(j);
inc(k);
end;
until (j>length(t)) or (k>length(Name));
if (j>length(t)) and ((l=0) or (k>length(Name))) then l:=Code;
end;
if l=0 then MyHalt(ERR_BADNUM);
GetNum:=l;
end;
end;
procedure PutRep(i:word);
begin
inc(NRepThing);
if NRepThing>MAXREPT then MyHalt(ERR_TOOREPN);
RepThing^[NRepThing]:=i;
end;
begin
if s='?ELSE' then begin PutRep(REP_QELSE); exit; end;
if s='?END' then begin PutRep(REP_QEND); exit; end;
if s[1]='?' then begin
inc(NRepThing);
rnum:=NRepThing;
s[1]:=',';
end
else begin
rnum:=0;
s:=','+s;
end;
GetOnceFlag;
inc(s[0]);
s[length(s)]:=#21; {#21 is a sentinel}
while s[1]=',' do begin
PutRep(GetNum);
j:=CheckLevel(s);
if s[1]='-' then begin
PutRep(REP_RANGE);
PutRep(GetNum);
j:=CheckLevel(s);
end;
if j and REP_ALL=0 then j:=j or once;
PutRep(j);
end;
if rnum>0 then begin
case s[1] of
'=': j:=0; { = 0 }
'<': if s[2]='>' then j:=1 { <> 1 }
else j:=2+ord(s[2]='='); { < 2 <= 3}
'>': j:=4+ord(s[2]='='); { > 4 >= 5}
else MyHalt(ERR_NOCOND);
end;
RepThing^[rnum]:=j+REP_QIF;
s:=Copy(s,2+(j and 1),255);
PutRep(REP_QIF+MyVal(GetIdentifier(s)));
if s[1]<>#21 then MyHalt(ERR_BADEND);
exit;
end;
if s[1]<>'=' then MyHalt(ERR_NOEQ);
inc(NRepThing);
rnum:=NRepThing;
i:=0;
s[1]:=',';
while s[1]=',' do begin
PutRep(GetNum);
j:=0;
if s[1]='@' then begin
s:=Copy(s,2,255);
j:=MyVal(GetIdentifier(s));
if (s[1]>='#') and (s[1]<='&') then begin
inc(j,REP_FLAG); { percentual quantity }
s:=Copy(s,2,255);
end;
end;
PutRep(j);
PutRep(CheckLevel(s));
inc(i);
end;
RepThing^[rnum]:=REP_FLAG+i;
if (s[1]<>#21) or (i=0) then MyHalt(ERR_BADEND);
end;
procedure ParseName(s:string;i:integer;var table:A_REPNAME;var num:integer);
var r:REPNAME;
j:integer;
begin
FillChar(r,sizeof(r),0);
j:=1;
while (j<=8) and (j<i) do begin
r.Before[j]:=UpCase(s[j]);
inc(j);
end;
j:=1;
while (j<=8) and (i<length(s)) do begin
inc(i);
r.After[j]:=UpCase(s[i]);
inc(j);
end;
i:=1;
while (i<=num) and (table[i].Before<>r.Before) do inc(i);
if j=1 then begin {remove name}
if i<=num then begin
table[i]:=table[num];
dec(num);
end;
end
else begin {add name}
if i>num then begin
inc(num);
if num>MAXREPN then MyHalt(ERR_TOOREPN);
end;
table[i]:=r;
end;
end;
procedure ParseAction(s:string;var table:A_REPACTION;var num:integer);
var t : string;
i,j : integer;
k : word;
procedure PutAction;
begin
inc(num);
if num>MAXREPN then MyHalt(ERR_TOOREPN);
table[num].Before:=k;
inc(j);
end;
begin
j:=0;
s:=','+s;
while s[1]=',' do begin
s:=copy(s,2,255);
k:=MyVal(GetIdentifier(s));
PutAction;
if s[1]='-' then begin
s:=copy(s,2,255);
k:=MyVal(GetIdentifier(s));
inc(k,REP_RANGE);
PutAction;
end;
end;
if s[1]<>'=' then MyHalt(ERR_NOEQ);
s:=copy(s,2,255);
k:=MyVal(GetIdentifier(s));
if s<>'' then MyHalt(ERR_BADEND);
for i:=num-j+1 to num do table[i].After:=k;
end;
procedure ParseObject(s:string);
var obj : OBJINFO;
i : integer;
begin
s:=s+#21;
obj.Code:=MyVal(GetIdentifier(s));
if s[1]<>'=' then MyHalt(ERR_NOEQ);
obj.Radius:=0;
obj.Height:=0;
obj.Games:=[];
if (s[2]='(') or (s[2]='[') then begin
s:=copy(s,3,255);
obj.Radius:=MyVal(GetIdentifier(s));
if s[1]=',' then begin
s:=copy(s,2,255);
obj.Radius:=MyVal(GetIdentifier(s));
end;
if (s[1]<>')') and (s[1]<>']') then MyHalt(ERR_BADEND);
end;
i:=2;
while (i<=length(s)) and (s[i]<>',') do begin
case upcase(s[i]) of
'D': if s[i+1]='2' then begin
Include(obj.Games,GT_DOOM2);
inc(i);
end
else Include(obj.Games,GT_DOOM);
'H': Include(obj.Games,GT_HERETIC);
end;
inc(i);
end;
if (i>length(s)) or (s[i]<>',') then MyHalt(ERR_BADEND);
obj.Name:=copy(s,i+1,length(s)-i-1);
if NObjects=MAXOBJ then MyHalt(ERR_TOOREPN);
inc(NObjects);
Objects^[NObjects]:=obj;
end;
procedure Parse;
type PARSE_TYPE = (PT_THING,PT_TEXTURE,PT_FLOOR,PT_LINEDEF,
PT_SECTOR,PT_NAME,PT_OBJECT);
var
i,j : integer;
s,t : string;
index : integer;
p_mode : PARSE_TYPE;
begin
p_mode:=PT_THING;
RespLev:=0;
index:=1;
while index<=ParamCount do begin
if RespLev>0 then t:=GetArgument
else t:=ParamStr(index);
s:=Upper(GetWord(t));
if (s='') or (s[1]=':') then {DO NOTHING}
else if s[1]='@' then begin
if RespLev=MAXRESP then MyHalt(ERR_TOORESP)
else begin
s:=Copy(s,2,255);
i:=1;
while (i<=length(s)) and (s[i]<>':') do inc(i);
t:=copy(s,i,255);
s:=copy(s,1,i-1);
if s='' then
if RespLev>0 then s:=Resp[RespLev].Name
else s:='DEFAULT';
j:=RespLev+1;
Resp[j].IfLev:=0;
Resp[j].Line:=0;
assign(Resp[j].RFile,s);
FileMode:=0;
reset(Resp[j].RFile);
if ioresult<>0 then begin
s:=AddSuffix(s,'RSP');
assign(Resp[j].RFile,s);
reset(Resp[j].RFile);
end;
if ioresult<>0 then begin
s:=AsInEXEDir(s);
assign(Resp[j].RFile,s);
reset(Resp[j].RFile);
end;
if ioresult<>0 then MyHalt(ERR_NORESP);
Resp[j].Name:=s;
inc(RespLev);
if t<>'' then begin
i:=RespLev;
s:=GetArgument;
while (i=RespLev) and (Upper(GetWord(s))<>t) do s:=GetArgument;
if i<>RespLev then MyHalt(ERR_NOLABEL);
end;
end;
end
else if (s[1]='/') or (s[1]='-') then begin
while (s<>'') and ((s[1]='/') or (s[1]='-')) do begin
ParseSymbol(copy(s,2,255));
s:=Upper(GetWord(t));
end;
end
else if s[1]='[' then begin
t:=copy(s,2,3);
if t='THI' then p_mode:=PT_THING
else if t='TEX' then p_mode:=PT_TEXTURE
else if t='FLO' then p_mode:=PT_FLOOR
else if t='LIN' then p_mode:=PT_LINEDEF
else if t='SEC' then p_mode:=PT_SECTOR
else if t='NAM' then p_mode:=PT_NAME
else if t='OBJ' then p_mode:=PT_OBJECT
else begin
writeln('WARNING: Unknown section ',s);
SyntaxHelp;
end;
end
else begin
if s[1]='?' then i:=-1
else i:=Pos('=',s);
if i<>0 then begin
repeat
if s[1]<>'?' then begin
if i=0 then i:=Pos('=',s);
if i=0 then MyHalt(ERR_NOEQ);
end
else if p_mode<>PT_THING then MyHalt(ERR_NOTHINGMODE);
case p_mode of
PT_THING: ParseThing(s);
PT_TEXTURE: ParseName(s,i,RepText^,NRepText);
PT_FLOOR: ParseName(s,i,RepFloor^,NRepFloor);
PT_NAME: ParseName(s,i,RepDirs^,NRepDirs);
PT_LINEDEF: ParseAction(s,RepLAct^,NRepLAct);
PT_SECTOR: ParseAction(s,RepSAct^,NRepSAct);
PT_OBJECT: begin
ParseObject(s+' '+t);
t:='';
end;
end;
s:=Upper(GetWord(t));
i:=0;
until (s='') or (s[1]=';');
end
else if RespLev>0 then begin
if (s=KEY_IFDEF) or (s=KEY_IFNDEF) then begin
i:=ord(s=KEY_IFDEF);
s:=Upper(GetWord(t));
inc(Resp[RespLev].IfLev);
if i<>ord(GetSymbol(s)<>'') then begin {condition false}
j:=Resp[RespLev].IfLev;
i:=RespLev;
while (i=RespLev) and (j<=Resp[RespLev].IfLev) do begin
t:=GetArgument;
s:=Upper(GetWord(t));
if (s=KEY_IFDEF) or (s=KEY_IFNDEF) then inc(Resp[RespLev].IfLev)
else if s=KEY_ENDIF then dec(Resp[RespLev].IfLev)
else if (s=KEY_ELSE) and (j=Resp[RespLev].IfLev) then i:=0;
end;
end;
end
else if s=KEY_ELSE then begin
j:=Resp[RespLev].IfLev;
i:=RespLev;
while (i=RespLev) and (j<=Resp[RespLev].IfLev) do begin
t:=GetArgument;
s:=Upper(GetWord(t));
if (s=KEY_IFDEF) or (s=KEY_IFNDEF) then inc(Resp[RespLev].IfLev)
else if s=KEY_ENDIF then dec(Resp[RespLev].IfLev);
end;
end
else if s=KEY_ENDIF then begin
if Resp[RespLev].IfLev=0 then MyHalt(ERR_ENDIF_NOIF);
dec(Resp[RespLev].IfLev);
end
else if s='SET' then begin
repeat
ParseSymbol(Upper(GetWord(t)))
until t='';
end
else if s='RETURN' then begin
close(Resp[RespLev].RFile);
dec(RespLev);
end
else if s='ABORT' then MyHalt(ERR_NONE)
else if s='ECHO' then writeln(Copy(t,2,255))
else begin
writeln('WARNING: Unknown keyword ',s);
SyntaxHelp;
end;
end
else begin
if GetSymbol(SYM_SOURCE)='' then SetSymbol(SYM_SOURCE,s)
else if GetSymbol(SYM_DEST)='' then SetSymbol(SYM_DEST,s)
else begin
writeln('WARNING: Unknown keyword ',s);
SyntaxHelp;
end;
end;
end;
if RespLev=0 then inc(index);
end;
SourceName:=GetSymbol(SYM_SOURCE);
DestName:=GetSymbol(SYM_DEST);
if SourceName<>'' then SourceName:=AddSuffix(SourceName,'WAD');
if DestName<>'' then DestName:=AddSuffix(DestName,'WAD');
Debug:=GetSymbol(SYM_DEBUG)<>'';
end;
procedure BlockR(var f:file;var dest;size:word);
begin
BlockRead(f,dest,size);
if ioresult<>0 then MyHalt(ERR_READ);
end;
procedure BlockW(var f:file;var dest;size:word);
begin
BlockWrite(f,dest,size);
if ioresult<>0 then MyHalt(ERR_WRITE);
end;
procedure FSeek(var f:file;p:longint);
begin
Seek(f,p);
if ioresult<>0 then MyHalt(ERR_READ);
end;
procedure CopyDest;
var a,b : file;
l : longint;
size : word;
begin
writeln('Copying source to destination');
Assign(a,SourceName);
FileMode:=0; {open for read only}
ErrStr:=SourceName;
Reset(a,1);
if ioresult<>0 then MyHalt(ERR_OPEN);
Assign(b,DestName);
FileMode:=1; {open for write only}
ErrStr:=DestName;
Rewrite(b,1);
if ioresult<>0 then MyHalt(ERR_OPEN);
l:=FileSize(a);
while l>0 do begin
if l>BUFFSIZE then size:=BUFFSIZE
else size:=l;
BlockR(a,buffer^,size);
BlockW(b,buffer^,size);
dec(l,size);
end;
Close(a);
Close(b);
end;
function RemapName(var table:A_REPNAME;var name:CHAR8;num:integer):integer; assembler;
asm
cld
les di, name
mov cx, 8
@@LOOP:
mov al, es:[di]
cmp al, 0
je @@FILLZERO
cmp al, 'a'
jb @@STORE
cmp al, 'z'
ja @@STORE
sub al, 32
@@STORE:
stosb
loop @@LOOP
@@FILLZERO:
rep stosb
@@OK:
push ds
lds si, name
les di, table
mov cx, num
cld
lodsw
mov bx, [si]
mov dx, [si+2]
mov si, [si+4]
@@CICLO:
scasw
jnz @@NEXT
cmp bx, es:[di]
jnz @@NEXT
cmp dx, es:[di+2]
jnz @@NEXT
cmp si, es:[di+4]
jnz @@NEXT
mov ax, es
mov ds, ax
mov si, di
add si, 6
les di, name
mov cx, 8
rep movsb
mov ax, 1
jmp @@FINE
@@NEXT:
add di, 14
loop @@CICLO
xor ax, ax
@@FINE:
pop ds
end;
function RemapNum(var table:A_REPACTION;var action:word;num:integer):integer; assembler;
asm
push ds
les di, action
mov bx, es:[di]
lds si, table
mov ax, num
mov cx, ax
add ax, ax
add ax, ax
add si, ax
dec si
dec si
std
@@LOOP:
lodsw
cmp ax, REP_RANGE
jb @@NORANGE
sub ax, REP_RANGE
cmp ax, bx
jb @@NEXT
lodsw
lodsw
dec cx
cmp ax, bx
jbe @@FOUND
jmp @@NEXT
@@NORANGE:
cmp ax, bx
je @@FOUND
@@NEXT:
lodsw
loop @@LOOP
xor ax, ax
jmp @@FINE
@@FOUND:
les di, action
movsw
mov ax, 1
@@FINE:
pop ds
end;
procedure SetRandomSeed;
var s:string;
i:integer;
begin
s:=GetSymbol(SYM_SEED);
RandomSeed:=0;
if s=SYM_SEED then begin
Randomize;
RandomSeed:=RandSeed;
end
else if s<>'' then begin
Val(s,RandomSeed,i);
if i<>0 then RandomSeed:=0;
end;
end;
function LenNum(n:word):integer;
begin
if n<10 then LenNum:=1
else if n<100 then LenNum:=2
else if n<1000 then LenNum:=3
else LenNum:=4;
end;
var ThingIndex : array[1..MAXTHING] of integer;
procedure Choose(var max:integer;n,c,lev:integer);
var i,j:integer;
begin
if n<max then begin
for i:=1 to n do begin
j:=Random(max)+1;
with Things^[ThingIndex[j]] do begin
Code:=c;
if lev and (REP_ZERO+7)<>0 then Flags:=lev and 7;
Flags:=Flags or REP_CONV or (lev and (REP_DEAF+REP_MULTI));
end;
ThingIndex[j]:=ThingIndex[max];
dec(max);
end;
inc(RemappedThing,n);
end
else begin
for i:=1 to max do with Things^[ThingIndex[i]] do begin
Code:=c;
if lev and (REP_ZERO+7)<>0 then Flags:=lev and 7;
Flags:=Flags or REP_CONV or (lev and (REP_DEAF+REP_MULTI));
end;
inc(RemappedThing,max);
max:=0;
end;
end;
procedure ReplaceThings(totobj:Integer);
var repn : integer;
i,j,k,l: word;
level : word;
once : word;
multi : boolean;
numobj : integer;
amount : array[1..128] of word;
numrep : integer;
numabs : integer;
iflev : integer;
runlev : integer;
iflevs : array[0..16] of integer;
condit : boolean;
col : integer;
const glev : integer = 0;
begin
inc(glev);
if debug then writeln('=== OBJECT CONVERSION, LEVEL ',glev);
RandSeed:=RandomSeed;
repn:=1;
iflev:=0;
runlev:=0;
while repn<=NRepThing do begin
numobj:=0;
l:=RepThing^[repn];
if l=REP_QELSE then begin
inc(repn);
if odd(iflev) or (iflev=0) then MyHalt(ERR_BADELSE);
iflev:=iflev or 1;
continue;
end;
if l=REP_QEND then begin
inc(repn);
if iflev<2 then MyHalt(ERR_BADENDIF);
iflev:=iflevs[(iflev-2)div 2];
if iflev<runlev then runlev:=iflev;
continue;
end;
if l>=REP_QIF then inc(repn);
if (runlev=iflev) and debug then begin
write('SOURCE OBJECTS: ');
col:=1;
end;
while RepThing^[repn]<REP_FLAG do begin
j:=RepThing^[repn];
inc(repn);
if RepThing^[repn] and REP_RANGE>0 then begin
inc(repn);
k:=RepThing^[repn];
inc(repn);
end
else k:=j;
once:=RepThing^[repn];
inc(repn);
level:=once and 7; {level 1 or 2 or 3}
if level=0 then level:=7;
multi:=once and REP_MULTI>0; {multiplayer flag}
once:=once and REP_ALL;
if runlev=iflev then begin
if debug then begin
if col<3 then write(#32#32)
else writeln;
col:=col mod 3+1;
if j<>k then write('Objects #':18-LenNum(j)-LenNum(k),j,'-#',k)
else begin
i:=1;
while (i<=NObjects) and ((Objects^[i].Code<>j) or not (Game1 in Objects^[i].Games)) do inc(i);
if i<=NObjects then write(Objects^[i].Name:20)
else write('Unknown object #':20-LenNum(j),j);
end;
numabs:=numobj;
end;
for i:=1 to totobj do with Things^[i] do
if (Code>=j) and (Code<=k) and (Flags and level>0) and
((once=REP_ALL) or ((Flags xor once)and REP_CONV=0)) and
(not multi or (Flags and REP_MULTI>0)) then begin
inc(numobj);
ThingIndex[numobj]:=i;
end;
if debug then begin
numabs:=numobj-numabs;
write('=',numabs,#32:4-LenNum(numabs));
end;
end;
end;
if (runlev=iflev) and debug then writeln;
if l>=REP_QIF then begin
i:=RepThing^[repn] and not REP_QIF;
inc(repn);
j:=iflev;
iflevs[iflev div 2]:=iflev;
iflev:=(iflev+2) and $FFFE;
if runlev=j then begin
l:=l and not REP_QIF;
case l of
0: condit:=numobj=i;
1: condit:=numobj<>i;
2: condit:=numobj<i;
3: condit:=numobj<=i;
4: condit:=numobj>i;
5: condit:=numobj>=i;
end;
if debug then writeln('IF ',numobj,copy('= <>< <=> >=',l*2+1,2),i,condit:8);
runlev:=iflev+1-ord(condit);
end;
continue;
end;
numrep:=RepThing^[repn]-REP_FLAG;
inc(repn);
if (numobj=0) or (numrep=0) then inc(repn,numrep*3)
else begin
numabs:=0;
j:=repn+1;
for i:=1 to numrep do begin
k:=RepThing^[j];
if k=0 then k:=REP_FLAG
else begin
if k>=REP_FLAG then k:=(longint(numobj)*(k-REP_FLAG)+50)div 100;
inc(numabs,k);
end;
amount[i]:=k;
inc(j,3);
end;
if numabs>numobj then begin
k:=numobj;
for i:=1 to numrep do begin
j:=amount[i];
if j<REP_FLAG then begin
if numabs=0 then amount[i]:=0
else amount[i]:=(longint(j)*k+numabs div 2)div numabs;
dec(numabs,j);
dec(k,amount[i]);
end;
end;
numabs:=numobj;
end;
numabs:=numobj-numabs;
j:=0;
for i:=1 to numrep do if amount[i]>=REP_FLAG then inc(j);
for i:=1 to numrep do if amount[i]>=REP_FLAG then begin
amount[i]:=(numabs+j div 2)div j;
dec(numabs,amount[i]);
dec(j);
end;
if debug then begin
write('CONVERTED OBJECTS: ');
col:=1;
end;
for i:=1 to numrep do begin
j:=RepThing^[repn];
if debug then begin
if col<3 then write(#32#32)
else writeln;
col:=col mod 3+1;
k:=1;
while (k<=NObjects) and ((Objects^[k].Code<>j) or not (Game2 in Objects^[k].Games)) do inc(k);
if k<=NObjects then write(Objects^[k].Name:20)
else write('Unknown object #':20-LenNum(j),j);
write('=',amount[i],#32:4-LenNum(amount[i]));
end;
Choose(numobj,amount[i],j,RepThing^[repn+2]);
inc(repn,3);
end;
if debug then writeln;
end;
end;
for i:=1 to totobj do with Things^[i] do Flags:=Flags and not REP_CONV;
end;
function IdentifyGame(s:string;default:GAMETYPE):GAMETYPE;
begin
if (s='D') or (s='DOOM') then IdentifyGame:=GT_DOOM
else if (s='D2') or (s='DOOM2') then IdentifyGame:=GT_DOOM2
else if (s='H') or (s='HERETIC') then IdentifyGame:=GT_HERETIC
else IdentifyGame:=default;
end;
function RemapStatus:integer;
var s:string;
i,j:integer;
begin
s:=GetSymbol(SYM_REMAP);
if s='' then RemapStatus:=0
else begin
val(s,i,j);
if j<>0 then i:=1;
RemapStatus:=i;
end;
end;
procedure SetMusicName(var d:WAD_ENTRY;j:integer);
begin
if (j>0) and (j<=99) then with d do case Game2 of
GT_DOOM2: begin
if j<=32 then Name:=MUS2NAMES[j]
else begin
Name:='D_MUSxy'#0;
Name[6]:=chr(j div 10+48);
Name[7]:=chr(j mod 10+48);
end;
end;
GT_DOOM: begin
Name:='D_ExMy'#0#0;
Name[4]:=chr((j-1) div 9+49);
Name[6]:=chr((j-1) mod 9+49);
end;
GT_HERETIC: begin
Name:='MUS_ExMy';
Name[6]:=chr((j-1) div 9+49);
Name[8]:=chr((j-1) mod 9+49);
end;
end;
end;
procedure Process;
var f : file;
fpos : longint;
head : WAD_HEADER;
num : integer;
i,j,k,l : integer;
save : boolean;
levpos : array[1..99] of integer;
levmap : array[1..99] of integer;
muspos : array[1..99] of integer;
remap : integer;
mix : boolean;
begin
save:=False;
mix:=GetSymbol(SYM_MIX)<>'';
Game1:=IdentifyGame(GetSymbol(SYM_FROM),GT_DOOM);
Game2:=IdentifyGame(GetSymbol(SYM_TO),GT_DOOM2);
remap:=RemapStatus;
SetRandomSeed;
if DestName<>'' then begin
CopyDest;
SourceName:=DestName;
end
else DestName:=SourceName;
Assign(f,DestName);
FileMode:=2; {open for read/write}
ErrStr:=DestName;
Reset(f,1);
if ioresult<>0 then MyHalt(ERR_OPEN);
BlockR(f,head,sizeof(head));
if (head.Sig<>PWAD_SIG) and (head.Sig<>IWAD_SIG) then MyHalt(ERR_PWAD);
num:=head.Num;
if num>MAXENTRY then MyHalt(ERR_TOOENTRY);
FSeek(f,head.Start);
BlockR(f,Dirlist^,num*sizeof(WAD_ENTRY));
write('Processing with ');
write('REMAP=');
if remap=0 then write('OFF') else write('ON(',remap,')');
write(',MIX=');
if mix then write('ON') else write('OFF');
writeln(',SEED=',RandSeed);
for i:=1 to 99 do begin
levmap[i]:=0;
muspos[i]:=0;
end;
k:=0;
for i:=1 to num do with Dirlist^[i] do begin
if copy(Name,1,3)='MAP' then begin
j:=(ord(name[4])-48)*10+ord(name[5])-48;
if (j>0) and (j<=99) then begin
levpos[j]:=i;
levmap[j]:=j;
end;
end
else if (Name[1]='E') and (Name[3]='M') and (Name[5]=#0) then begin
j:=(ord(Name[2])-49)*9+ord(Name[4])-48;
if (j>0) and (j<=99) then begin
levpos[j]:=i;
levmap[j]:=j;
end;
end
else if copy(Name,1,3)='MUS' then begin
if mix then begin inc(k);j:=k; end
else j:=(ord(Name[6])-49)*9+ord(Name[8])-48;
if (j>0) and (j<=99) then muspos[j]:=i;
end
else if copy(Name,1,5)='D_MUS' then begin
if mix then begin inc(k);j:=k; end
else j:=(ord(name[6])-48)*10+ord(name[7])-48;
if (j>0) and (j<=99) then muspos[j]:=i;
end
else if (Name[1]='D') and (Name[2]='_') then begin
if (Name[3]='E') and (Name[5]='M') then begin
if mix then begin inc(k);j:=k; end
else j:=(ord(Name[4])-49)*9+ord(Name[6])-48
end
else begin
j:=32;
while (j>0) and (MUS2NAMES[j]<>Name) do dec(j);
if mix and (j>0) then begin inc(k);j:=k; end
end;
if (j>0) and (j<=99) then muspos[j]:=i;
end;
end;
if remap>0 then
for i:=1 to 99 do if levmap[i]>0 then begin
levmap[i]:=remap;
inc(remap);
end;
for i:=1 to 99 do if levmap[i]>0 then with Dirlist^[levpos[i]] do begin
j:=levmap[i];
case Game2 of
GT_DOOM2: begin
Name:='MAPxy'#0#0#0;
Name[4]:=chr(j div 10+48);
Name[5]:=chr(j mod 10+48);
end;
GT_DOOM,GT_HERETIC: begin
Name:='ExMy'#0#0#0#0;
Name[2]:=chr((j-1) div 9+49);
Name[4]:=chr((j-1) mod 9+49);
end;
end;
inc(RemappedLev);
save:=True;
end;
if mix then begin {mix musics}
Randomize;
for i:=1 to k-1 do begin
j:=Random(k-i)+i;
l:=muspos[i];
muspos[i]:=muspos[j];
muspos[j]:=l;
end;
for i:=1 to k do begin
SetMusicName(Dirlist^[muspos[i]],i);
inc(RemappedMus);
save:=True;
end;
end
else for i:=1 to 99 do if muspos[i]>0 then begin
SetMusicName(Dirlist^[muspos[i]],levmap[i]);
inc(RemappedMus);
save:=True;
end;
if NRepDirs>0 then begin
for i:=1 to num do with Dirlist^[i] do
inc(RemappedDirs,RemapName(RepDirs^,Name,NRepDirs));
end;
for i:=1 to num do with Dirlist^[i] do begin
if (Name=N_LINEDEFS) and (NRepLAct>0) then begin
FSeek(f,Start);
k:=Size div sizeof(LINEDEF);
while k>0 do begin
fpos:=FilePos(f);
l:=MAXLINE;
if l>k then l:=k;
BlockR(f,Linedefs^,l*sizeof(LINEDEF));
for j:=1 to l do
inc(RemappedLAct,RemapNum(RepLAct^,Linedefs^[j].Action,NRepLAct));
FSeek(f,fpos);
BlockW(f,Linedefs^,l*sizeof(LINEDEF));
dec(k,l);
end;
end
else if (Name=N_SECTORS) and (NRepSAct+NRepFloor>0) then begin
FSeek(f,Start);
k:=Size div sizeof(SECTOR);
while k>0 do begin
fpos:=FilePos(f);
l:=MAXSECT;
if l>k then l:=k;
BlockR(f,Sectors^,l*sizeof(SECTOR));
if NRepSAct>0 then
for j:=1 to l do
inc(RemappedSAct,RemapNum(RepSAct^,Sectors^[j].Action,NRepSAct));
if NRepFloor>0 then
for j:=1 to l do
inc(RemappedFloor,RemapName(RepFloor^,Sectors^[j].Floor,NRepFloor)+
RemapName(RepFloor^,Sectors^[j].Ceiling,NRepFloor));
FSeek(f,fpos);
BlockW(f,Sectors^,l*sizeof(SECTOR));
dec(k,l);
end;
end
else if (Name=N_SIDEDEFS) and (NRepText>0) then begin
FSeek(f,Start);
k:=Size div sizeof(SIDEDEF);
while k>0 do begin
fpos:=FilePos(f);
l:=MAXSIDE;
if l>k then l:=k;
BlockR(f,Sidedefs^,l*sizeof(SIDEDEF));
for j:=1 to l do
inc(RemappedText,RemapName(RepText^,Sidedefs^[j].UpT,NRepText)+
RemapName(RepText^,Sidedefs^[j].LoT,NRepText)+
RemapName(RepText^,Sidedefs^[j].MidT,NRepText));
FSeek(f,fpos);
BlockW(f,Sidedefs^,l*sizeof(SIDEDEF));
dec(k,l);
end;
end
else if (Name=N_THINGS) and (NRepThing>0) then begin
FSeek(f,Start);
k:=Size div sizeof(THING);
BlockR(f,Things^,k*sizeof(THING));
ReplaceThings(k);
FSeek(f,Start);
BlockW(f,Things^,k*sizeof(THING));
end;
end;
if save or (RemappedDirs>0) then begin
FSeek(f,head.Start);
BlockW(f,Dirlist^,num*sizeof(WAD_ENTRY));
end;
Close(f);
writeln('Remapped LEVELS:',RemappedLev:4,' MUSICS:',RemappedMus:4,
' TEXTURES:',RemappedText:4,' FLOORS :',RemappedFloor:4);
writeln(' THINGS:',RemappedThing:4,' NAMES :',RemappedDirs:4,
' LACTIONS:',RemappedLAct:4,' SACTIONS:',RemappedSAct:4);
end;
begin
Initialize;
Parse;
if (SourceName='') or (GetSymbol(SYM_HELP)<>'') then Help
else Process;
end.