program lwo2pwo; { LightWave Object to PeyloW Object }

uses
  crt;

type
{ IFF ID }
  TIFFID = packed array[0..3] of Char;
{ IFF Chunk }
  TIFFChunk = record
    chrID: Longint;
    lngSize: Longint;
  end;
{ LW R,G,B,Dummy color }
  TLWCol = packed array[0..3] of Byte;
{ LW Vertice }
  TLWPnts = array[0..2] of Single;
{ LW VMAP }
	TLWVMapHead = record
		chrType: Longint;
		intDimension: Integer;
	end; 
	TLWVMap = record
		intPnt: Integer;
		sngU: Single;
		sngV: Single;
	end;
{ LW Polygon }
  TLWPols = record
    intNumPnts: Integer;
    intPnt: array[0..2] of Integer;
    intSurf: Integer;
  end;
{ PW Point }
  TPWPnts = record
    intX: Integer;
    intY: Integer;
    intU: Integer;
    intV: Integer;
    intZ: Integer;
  end;
{ PW VMap }
	TPWVMapHead = record
		chrType: Longint;
		intDimension: Integer;
		strName: String;
		lngBytes: Longint;
	end;
{ PW Polygon }
  TPWPols = record
    intPnEd: array[0..2] of Integer;
    intExtra: Integer;
  end;
	TPWEdgPols = Array[0..3] of Integer;
{ PW Edges }
  TPWEdgs = array[0..1] of Integer;
{ PW Normals }
  TPWNorm = array[0..2] of Integer;
{ LightWave Polyon tags }
	TLWPTag = array[0..1] of Integer;


const
  MAXPOINTS: Integer = 500;
  MAXPOLS: Integer = 1000;
  MAXEDGES: Integer = 1500;
  { All other IDs are discarded as of now }
  _FORM: Longint = $464F524D;  { IFF chunk }
  _LWOB: Longint = $4C574F42;  { Object chunk }
	_LWO2: Longint = $4C574F32;	 { 6.0 Object chunk }
  _PNTS: Longint = $504E5453;  { Points/Vertices }
  _VMAP: Longint = $564D4150;	 { VMap chunk }
  _TXUV: Longint = $54585556;  { Texturemap chunk }
  _POLS: Longint = $504F4C53;  { Polygons }
  _PTAG: Longint = $50544147;  { Polygon Tags }
  _PART: Longint = $50415254;  { Selection Part }
  
  Scene2UV: Array[0..8,0..1] of Integer =
  	((120,-63), ( 32,-63), ( 32, -7),
     (120, -7), ( 96,-63), ( 96, -7),
     ( 64,-120),(  7,-63), (  7, -7));  
  
var
  sngScale: Single;
  bolEdgs: Boolean;
  bolPolar: Boolean;
  bolPnNo: Boolean;
  bolPoNo: Boolean;
  bolText: Boolean;
  bolScene2: Boolean;
  chrText: Char;
  sngText: Array[0..1] of Single;
	bolLWO2: Boolean;
  
  strLWName: String[80];
  strPWName: String[80];
  
  LWPnts: array[0..499] of TLWPnts;
  LWPols: array[0..999] of TLWPols;
  
  PWPnts: array[0..499] of TPWPnts;
  PWPols: array[0..999] of TPWPols;
  PWEdgPols: array[0..999] of TPWEdgPols;
  PWEdgs: array[0..1499] of TPWEdgs;
  PWPnNo: array[0..499] of TPWNorm;
  PWPoNo: array[0..999] of TPWNorm;
  
  intNumPnts: Integer;
  intNumPols: Integer;
  intNumEdgs: Integer;
  intMarked: Integer;
  

{ GENERATE ERROR AND HALT }
procedure Error(strErr: String);
begin
  WriteLn(strErr);
  WriteLn('Press any key to continue...');
  repeat until keypressed;
  Halt(1);
end;  

{ SHOW HELP AND HALT }
procedure ShowHelp;
begin
  WriteLn('LWO2PWO v0.9');
  WriteLn('Usage:');
  WriteLn('  lwo2pwo [options] <file.lwo> [file.pwo]');
  WriteLn('Options:');
  WriteLn('  -? : Help (This screen)');
  WriteLn('  -s<sng> : Scale object (small < 1.0 < BIG)');
  WriteLn('  {+|-}e : Create edges');
  WriteLn('  {-|+}n : Create polygon normals');
  WriteLn('  {+|-}N : Create point normals');
	WriteLn('  -t[x|y|z][sng] : Texture map (planar)');
	WriteLn('  -T : Extended texture for Scene2');
  WriteLn('Example:');
  WriteLn('  lwo2pwo -e -N -s0.5 object.lwo test.pwo');
  WriteLn('Saves the object contained in object.lwo scaled to half');
  WriteLn('with edge and point normal data to the file test.pwo');
  WriteLn;
  WriteLn('Press any key to continue...');
  repeat until keypressed;
  Halt(0);
end;

{ PARSE PARAMETER STRINGS }
procedure ParseParamStr;
var
  bolInFound: Boolean;
  intParm: Integer;
  intRes: Integer;
  strCase: String;
  
  procedure ShowParameters;
  begin
    WriteLn('LWO file: ', strLWName);
    WriteLn('PWO file: ', strPWName);
    Write('Scale: ');
    WriteLn(sngScale:10:5);
  end;
  
begin
  if paramCount < 1 then
    ShowHelp;
  bolInFound := false;
  strLWName := '';
  strPWName := '';
  sngScale := 1.0;
  bolEdgs := true;
  bolPoNo := false;
  bolPnNo := true;
  bolText := false;
  bolScene2 := false;
  
{ Parse all parameters }
  for intParm := 1 to ParamCount do begin
  	strCase := Copy(ParamStr(intParm), 1, 2); 
  	{case strCase of}
  	{ Help }
  	if strCase = '-?' then begin
  	    ShowHelp;
  	  end
  	{ Scale }
  	else if strCase = '-s'then begin
  	    val(Copy(ParamStr(intParm), 3, 255), sngScale, intRes);
  	    if intRes <> 0 then
  	      Error('Ivalid -s parameter');
  	  end
  	else if strCase = '+e' then bolEdgs := true
  	else if strCase = '+n' then bolPoNo := true
  	else if strCase = '+N' then bolPnNo := true
  	else if strCase = '-e' then bolEdgs := false
  	else if strCase = '-n' then bolPoNo := false
  	else if strCase = '-N' then bolPnNo := false
  	else if strCase = '-T' then begin
  		bolScene2 := true;
  		bolText := true;
  	end
  	else if strCase = '-t' then bolText := true
		else
  	  if not bolInFound then begin
  	    strLWName := ParamStr(intParm);
  	    bolInFound := true;
  	  end else
  	    strPWName := ParamStr(intParm);
  	end;
  {end;}
  if strLWName = '' then
    Error('Must specify an input file'#10#13'  "lwo2pwo -?" for help');
  if strPWName = '' then
    strPWName := Copy(strLWName, 1, Length(strLWName) - 3) + 'pwo';
  ShowParameters;
end;

procedure ParseLW;
var
  filLW: File;
  rChunk: TIFFChunk;
  intFoundChunks: Integer;
  lngBytes: Longint;
  lngBytesRead: Longint;
  
  procedure ReadChunk(var rChunk: TIFFChunk);
  var
    lngRead: Longint;
  begin
    blockread(filLW, rChunk, SizeOf(TIFFChunk), lngRead);
    if lngRead <> SizeOf(TIFFChunk) then
      Error('Error while reading chunk');
    if (rChunk.lngSize and 1) = 1 then
    	lngBytesRead := lngBytesRead + rChunk.lngSize + 9
    else
    	lngBytesRead := lngBytesRead + rChunk.lngSize + 8;
  end;
  
  procedure SkipChunk(var rChunk: TIFFChunk);
  begin
   {$I-}
    Seek(filLW, rChunk.lngSize + FilePos(filLW));
   {$I+}
    if IOResult <> 0 then
      Error('Could not skip "' + TIFFID(rChunk.chrID) + '" chunk');
  end;

  procedure ReadPoints(var rChunk: TIFFChunk);
  var
    lngNumPnts: Longint;
    lngRes: Longint;
    intPoint: Integer;
  begin
    lngNumPnts := rChunk.lngSize div SizeOf(TLWPnts);
    if lngNumPnts > MAXPOINTS then
      Error('To many points in object');
    for intPoint := 0 to lngNumPnts - 1 do begin
      BlockRead(filLW, LWPnts[intPoint], SizeOf(TLWPnts), lngRes);
      if lngRes <> SizeOf(TLWPnts) then
        Error('Error while reading points');
    end;
    intNumPnts := lngNumPnts;
  end;
    
  procedure ReadVMap(var rChunk: TIFFChunk);
  	procedure ReadVMapHead(var rMapHead: TPWVMapHead);
		var
			rLWMapHead: TLWVMapHead;
			lngRes: Longint;
			chrChar: Char;
  	begin
  		BlockRead(filLW, rLWMapHead, SizeOf(TLWVMapHead), lngRes);
  		if lngRes <> SizeOf(TLWVMapHead) then
  			Error('Error while reading VMap');
  		rMapHead.lngBytes := lngRes;
  		rMapHead.strName := '';
  		rMapHead.chrType := rLWMapHead.chrType;
  		rMapHead.intDimension := rLWMapHead.intDimension;
  		repeat
  			BlockRead(filLW, chrChar, 1, lngRes);
  			if lngRes <> 1 then
  				Error('Error while reading VMap');
  			rMapHead.strName := rMapHead.strName + chrChar;
  			rMapHead.lngBytes := rMapHead.lngBytes + 1;
  		until chrChar = #0;
  		if (rMapHead.lngBytes and 1) = 1 then begin
  			rMapHead.lngBytes := rMapHead.lngBytes + 1;
  			BlockRead(filLW, chrChar, 1, lngRes);
  			if lngRes <> 1 then
  				Error('Error while reading VMap');
  		end;
  	end;
  	procedure SkipVMap(lngBytes: Longint);
		var
			lngRes: Longint;
  	begin
  		{$I-}
  			Seek(filLW, lngBytes + FilePos(filLW));
  		{$I+}
  		if IOResult <> 0 then
  			Error('Error while skipping VMap chunk');
  	end;
  var
  	lngNumPnts: Longint;
  	lngPoint: Longint;
  	rMapHead: TPWVMapHead;
  	lngRes: Longint;
  	rVMap: TLWVmap;
  	lngEnd: LongInt;
  	sngM: Single;
  begin
  	sngM := 0;
  	lngEnd := FilePos(filLW) + rChunk.lngSize;
    ReadVMapHead(rMapHead);
    if rMapHead.chrType = _TXUV then begin
    	lngNumPnts := (rChunk.lngSize - rMapHead.lngBytes) div SizeOf(TLWVMap);
			WriteLn('VMap TextureUV map with ', lngNumPnts, ' points found.');
    	for lngPoint := 1 to lngNumPnts do begin
    		BlockRead(filLW, rVMap, SizeOf(TLWVMap), lngRes);
    		if lngRes <> SizeOf(TLWVMap) then
    			Error('Error while reading VMap'); 
    		if rVMap.sngV > sngM then
    			sngM := rVMap.sngV;
    		PWPnts[rVMap.intPnt].intU := Round(rVMap.sngU * 254) - 127;
    		PWPnts[rVMap.intPnt].intV := -(Round(rVMap.sngV * 254) - 127);
    	end;
    end else
    	SkipVMap(rChunk.lngSize - rMapHead.lngBytes);
		Seek(filLW, lngEnd);
  end;
  
  procedure ReadPolys(var rChunk: TIFFChunk);
  var
    lngNumPols: Longint;
    lngRes: Longint;
    intPol: Integer;
    intTmp: Integer;
  	chrID: TIFFID;
  begin
  	if bolLWO2 then begin
  		BlockRead(filLW, chrID, SizeOf(TIFFID), lngRes);
  		if lngRes <> SizeOf(TIFFID) then
  			Error('Error while reading polygons');
  		lngNumPols := (rChunk.lngSize - 4) div (SizeOf(TLWPols) - 2);
  	end else
    	lngNumPols := rChunk.lngSize div SizeOf(TLWPols);
    if lngNumPols > MAXPOLS then
      Error('To many polygons in object');
    for intPol := 0 to lngNumPols - 1 do begin
			if bolLWO2 then begin
    		BlockRead(filLW, LWPols[intPol], SizeOf(TLWPols) - 2, lngRes);
    		if lngRes <> (SizeOf(TLWPols) - 2) then
    	  	Error('Error while reading polygons');
    	end else begin
    		BlockRead(filLW, LWPols[intPol], SizeOf(TLWPols), lngRes);    	
    		if lngRes <> SizeOf(TLWPols) then
    	  	Error('Error while reading polygons');
    	end;
    	if (LWPols[intPol].intNumPnts and $3FFFF) <> 3 then
    		Error('Only triangles supported');
    { Swap polygon face }
    	with LWPols[intPol] do begin
    		intTmp := intPnt[0];
    		intPnt[0] := intPnt[2];
    		intPnt[2] := intTmp;
    		intSurf := 0;
    	end;
    end;
    intNumPols := lngNumPols;
  end;
  
  procedure ReadPTags(var rChunk: TIFFChunk);
  var
  	lngID: Longint;
  	intNumTags: Longint;
  	intTag: Integer;
  	PTag: TLWPTag;
  	lngRes: Longint;
  	lngSkip: Longint;
  begin
  	BlockRead(filLW, lngID, 4, lngRes);
  	if lngRes <> 4 then
  		Error('Error While reading polygon tag');
  	if lngID <> _PART then
  		seek(filLW, FilePos(filLW) + rChunk.lngSize - 4)
  	else begin
  		intNumTags := (rChunk.lngSize - 4) div 4;
  		for intTag := 1 to intNumTags do begin
  			BlockRead(filLW, PTag, 4, lngRes);
  			LWPols[PTag[0]].intSurf := 1;
  		end;
  		WriteLn(intNumTags, ' polygons marked.');
  	end;
  end; 
  
begin
  assign(filLW, strLWName);
 {$I-}
  reset(filLW, 1); 
 {$I+}
  if IOResult <> 0 then
    Error('Could not open LWO file');
{ Check if it is an IFF file }
  ReadChunk(rChunk);
  if rChunk.chrID = _FORM then begin
    WriteLn('IFF file found: ', rChunk.lngSize + 8, ' bytes');
  	lngBytes := rChunk.lngSize - 4;
  end else
    Error('LWO file is not an IFF file');
{ Check if it is a LightWave object file }
  ReadChunk(rChunk);
  if (rChunk.chrID = _LWOB) or (rChunk.chrID = _LWO2) then begin
  	bolLWO2 := rChunk.chrID = _LWO2;
  	if bolLWO2 then
  		WriteLn('LightWave Object2 file confirmed')
  	else
  		WriteLn('LightWave Object file confirmed');
  end else
    Error('LWO file is not a LightWave Object file');
  Seek(filLW, FilePos(filLW) - 4);
{ Find PNTS and POLS chunks, skip the rest! }
	intFoundChunks := 0;
	lngBytesRead := 0;
	while lngBytesRead < lngBytes do begin
	  ReadChunk(rChunk);
	  with rChunk do begin
	    if chrID = _PNTS then begin 
	    	ReadPoints(rChunk);
	    	intFoundChunks := intFoundChunks + 1;
	    end
	    else if chrID = _VMAP then begin
	      ReadVMap(rChunk);
	      intFoundChunks := intFoundChunks + 1;
	    end
	    else if chrID = _POLS then begin
	    	ReadPolys(rChunk);
	    	intFoundChunks := intFoundChunks + 1;
	    end
	    else if chrID = _PTAG then begin
	    	ReadPTags(rChunk);
	    	intFoundChunks := intFoundChunks + 1;
			end
			else	    	
	  		SkipChunk(rChunk);
	  end; 
	end;
{ Close file }
  Close(filLW);
end;

procedure CreatePW;

	procedure CreatePnts;
  var
    intPnt: Integer;
    intCrd: Integer;
    intMax: Array[0..1] of Integer;
	begin
	  for intPnt := 0 to intNumPnts - 1 do begin
	    PWPnts[intPnt].intX := -Round(LWPnts[intPnt][0] * 127 * sngScale);
	    PWPnts[intPnt].intY := -Round(LWPnts[intPnt][1] * 127 * sngScale);
	    PWPnts[intPnt].intZ := -Round(LWPnts[intPnt][2] * 127 * sngScale);	    
	  end;

    intMax[0] := 127;
    intmax[1] := 127;
  	if bolText then begin
  		intMax[0] := 1;
  		intMax[1] := 1;
  		for intPnt := 0 to intNumPnts - 1 do begin
  			if Abs(PWPnts[intPnt].intX) > intMax[0] then
  				intMax[0] := Abs(PWPnts[intPnt].intX);
  			if Abs(PWPnts[intPnt].intZ) > intMax[1] then
  				intmax[1] := Abs(PWPnts[intPnt].intZ)
  		end;
  		sngText[0] := intmax[0];
  		sngText[1] := intmax[1];
			for intPnt := 0 to intNumPnts - 1 do begin
    	  PWPnts[intPnt].intU := Round(PWPnts[intPnt].intX / sngText[0] * 120);
    	  PWPnts[intPnt].intV := Round(PWPnts[intPnt].intZ / sngText[1] * 120);			
				if bolScene2 then begin
					PWPnts[intPnt].intV := (PWPnts[intPnt].intV div 2) + 64;
					if intPnt >= 24 then begin
						PWPnts[intPnt].intU := Scene2UV[(intPnt - 24) mod 9, 0];
						PWPnts[intPnt].intV := Scene2UV[(intPnt - 24) mod 9, 1];						
					end;
				end;
			end;
  	end;

	  
	  WriteLn(intNumPnts, ' points created');
	end;
	
	procedure CreateEdgs;
	var
	  intPol: Integer;
	  intEdg: Integer;
	  intPnt: Integer;
	  intEdgs: array[0..2] of Integer;
	  
	  function FindEdg(intA, intB: Integer): Integer;
	  var
	    intPnt: Integer;
	  begin
	    if intNumEdgs > 0 then begin
	      intPnt := 0;
	      while (not (((PWEdgs[intPnt][0] = intA) and (PWEdgs[intPnt][1] = intB)) or
	      			((PWEdgs[intPnt][1] = intA) and (PWEdgs[intPnt][0] = intB))))
	      			and (intPnt < intNumEdgs) do
	      	inc(intPnt);
	      if intPnt >= intNumEdgs then
	        intPnt := -1;
	    end else
	      intPnt := -1;
	    FindEdg := intPnt;
	  end;
	  
	  function CreateEdg(intA, intB: Integer): Integer;
	  begin
	    PWEdgs[intNumEdgs][0] := intA;
	    PWEdgs[intNumEdgs][1] := intB;
	    inc(intNumEdgs);
	    CreateEdg := intNumEdgs - 1;
	  end;
	  
	begin
	  intNumEdgs := 0;
	  for intPol := 0 to intNumPols - 1 do begin
	    for intPnt := 0 to 2 do begin
	      intEdg := FindEdg(PWPols[intPol].intPnEd[intPnt], PWPols[intPol].intPnEd[(intPnt + 1) mod 3]);
				if intEdg < 0 then
				  intEdg := CreateEdg(PWPols[intPol].intPnEd[intPnt], PWPols[intPol].intPnEd[(intPnt + 1) mod 3]);
	      intEdgs[intPnt] := intEdg;
	    end;
	    for intPnt := 0 to 2 do
	    	PWEdgPols[intPol][intPnt] := intEdgs[intPnt];
	  	PWEdgPols[intPol][3] := PWPols[intPol].intExtra;
	  end;
	  WriteLn(intNumEdgs, ' edges created');
	end;
	
	procedure CreatePols;
	var
	  intPol: Integer;
	  intPnt: Integer;
	begin
	  for intPol := 0 to intNumPols - 1 do begin
	    for intPnt := 0 to 2 do
	      PWPols[intPol].intPnEd[intPnt] := LWPols[intPol].intPnt[intPnt];
	  	PWPols[intPol].intExtra := LWPols[intPol].intSurf;
	  end;
	  WriteLn(intNumPols, ' polygons created');
	end;

  procedure CreatePoNo;
  type
    TVec = array[0..2] of Extended;
  var
    intPol: Integer;
    intPnt: Integer;
    V1,V2: TVec;
    N: TVec;
    sngLe: Extended;
  begin
    for intPnt := 0 to intNumPnts - 1 do begin
      PWPnNo[intPnt][0] := 0;
      PWPnNo[intPnt][0] := 0;
      PWPnNo[intPnt][0] := 0;      
    end;
    for intPol:= 0 to intNumPols - 1 do begin
  	 	V1[0] := PWPnts[PWPols[intPol].intPnEd[1]].intX - PWPnts[PWPols[intPol].intPnEd[0]].intX;
     	V1[1] := PWPnts[PWPols[intPol].intPnEd[1]].intY - PWPnts[PWPols[intPol].intPnEd[0]].intY;
    	V1[2] := PWPnts[PWPols[intPol].intPnEd[1]].intZ - PWPnts[PWPols[intPol].intPnEd[0]].intZ;
    	V2[0] := PWPnts[PWPols[intPol].intPnEd[2]].intX - PWPnts[PWPols[intPol].intPnEd[0]].intX;
    	V2[1] := PWPnts[PWPols[intPol].intPnEd[2]].intY - PWPnts[PWPols[intPol].intPnEd[0]].intY;
    	V2[2] := PWPnts[PWPols[intPol].intPnEd[2]].intZ - PWPnts[PWPols[intPol].intPnEd[0]].intZ;
    	N[0] := V1[1] * V2[2] - V1[2] * V2[1];
    	N[1] := V1[2] * V2[0] - V1[0] * V2[2];
    	N[2] := V1[0] * V2[1] - V1[1] * V2[0];
    	sngLe := Sqrt(N[0]*N[0] + N[1]*N[1] + N[2]*N[2]);
    	for intPnt := 0 to 2 do begin
    		PWPoNo[intPol][intPnt] := Round(N[intPnt] * 255 / sngLe);
    		PWPnNo[PWPols[intPol].intPnEd[0]][intPnt] := PWPnNo[PWPols[intPol].intPnEd[0]][intPnt] + PWPoNo[intPol][intPnt];
    		PWPnNo[PWPols[intPol].intPnEd[1]][intPnt] := PWPnNo[PWPols[intPol].intPnEd[1]][intPnt] + PWPoNo[intPol][intPnt];
    		PWPnNo[PWPols[intPol].intPnEd[2]][intPnt] := PWPnNo[PWPols[intPol].intPnEd[2]][intPnt] + PWPoNo[intPol][intPnt];    		
    	end;    	
    end;
    WriteLn(intNumPols, ' polygon normals calculated');
  end;
  
  procedure CreatePnNo;
  var
    intPnt: Integer;
    N: array[0..2] of Extended;
    sngLe: Extended;
  begin  
    for intPnt := 0 to intNumPnts - 1 do begin
   		N[0] := PWPnNo[intPnt][0];
   		N[1] := PWPnNo[intPnt][1];
   		N[2] := PWPnNo[intPnt][2];
     	sngLe := Sqrt(N[0]*N[0] + N[1]*N[1] + N[2]*N[2]);
     	PWPnNo[intPnt][0] := Round(N[0] * 255 / sngLe);
     	PWPnNo[intPnt][1] := Round(N[1] * 255 / sngLe);
     	PWPnNo[intPnt][2] := Round(N[2] * 255 / sngLe);
    end;
    WriteLn(intNumPnts, ' point normals created');
  end;

begin
  CreatePnts;
  CreatePols;
  if bolPoNo or bolPnNo then
    CreatePoNo;
  if bolPnNo then
    CreatePnNo;
  if bolEdgs then
    CreateEdgs;
end;

procedure SavePW;
const
  ERR: String = 'Error while writing to PWO file';
var
  filPW: File;
  intS: Integer;
  lngRes: Longint;
  intZero: Integer;
begin
  intZero := 0;
  Assign(filPW, strPWName);
 {$I-}
  Rewrite(filPW, 1);
 {$I+}
  if IOResult <> 0 then
    Error('Error while creating PWO file');
{ Save points }
  BlockWrite(filPW, intNumPnts, 2, lngRes);
  BlockWrite(filPW, PWPnts, SizeOf(TPWPnts) * intNumPnts, lngRes);
  if lngRes <> SizeOf(TPWPnts) * intNumPnts then
    Error(Err);
  WriteLn(intNumPnts * SizeOf(TPWPnts), ' bytes Point data saved');
{ Save Polys }
  BlockWrite(filPW, intNumPols, 2, lngRes);
  BlockWrite(filPW, PWPols, SizeOf(TPWPols) * intNumPols, lngRes);
  if lngRes <> SizeOf(TPWPols) * intNumPols then
    Error(Err);
  WriteLn(intNumPols * SizeOf(TPWPols), ' bytes Polygon data saved');
{ Save Edges }
  if bolEdgs then begin
 		BlockWrite(filPW, intNumEdgs, 2, lngRes);
    BlockWrite(filPW, PWEdgs, SizeOf(TPWEdgs) * intNumEdgs, lngRes);
	  if lngRes <> SizeOf(TPWEdgs) * intNumEdgs then
  	  Error(Err);
  	WriteLn(intNumEdgs * SizeOf(TPWEdgs), ' bytes Edge data saved');
  	BlockWrite(filPW, intNumPols, 2, lngRes);
	  BlockWrite(filPW, PWEdgPols, SizeOf(TPWEdgPols) * intNumPols, lngRes);
	  if lngRes <> SizeOf(TPWEdgPols) * intNumPols then
	    Error(Err);
		WriteLn(intNumPols * SizeOf(TPWEdgPols), ' bytes Polygon data saved (edges)');
  end else begin
  	BlockWrite(filPW, intZero, 2, lngRes);
  	if lngRes <> 2 then
  	  Error(Err);
  	BlockWrite(filPW, intZero, 2, lngRes);
  	if lngRes <> 2 then
  	  Error(Err);
  end;
{ Save polygon normals }
  if bolPoNo then begin
    BlockWrite(filPW, intNumPols, 2, lngRes);
    BlockWrite(filPW, PWPoNo, SizeOf(TPWNorm) * intNumPols, lngRes);
	  if lngRes <> SizeOf(TPWNorm) * intNumPols then
  	  Error(Err);
  	WriteLn(intNumPols * SizeOf(TPWNorm), ' bytes Polygon normal data saved');
  end else begin
  	BlockWrite(filPW, intZero, 2, lngRes);
  	if lngRes <> 2 then
  	  Error(Err);
  end;
{ Save point normals }
  if bolPnNo then begin
  	BlockWrite(filPW, intNumPnts, 2, lngRes);
    BlockWrite(filPW, PWPnNo, SizeOf(TPWNorm) * intNumPnts, lngRes);
	  if lngRes <> SizeOf(TPWNorm) * intNumPnts then
  	  Error(Err);
  	WriteLn(intNumPnts * SizeOf(TPWNorm), ' bytes Point normal data saved');
  end else begin
  	BlockWrite(filPW, intZero, 2, lngRes);
  	if lngRes <> 2 then
  	  Error(Err);
  end;
  Close(filPW);
end;

begin
	ParseParamStr;
	ParseLW;
	CreatePW;
	SavePW;
	WriteLn('Press any key to continue...');
	repeat until keypressed;
end.