#
# imode.pm -- A perl module to generate immediate-mode routines and header
#             files automatically
#

#
# Exports a single function, gencode(string), which takes a string parameter
#   if string is "cfile", c source is generated
#   if string is "hfile", a header file is generated
#

sub gencode ($) {
    $param = shift;
    if ($param eq "cfile") {
	$do_cfile = 1;
    }
    elsif ($param eq "hfile") {
	$do_cfile = 0;
    }
    else {
	die("gencode: invalid parameter");
    }

    #
    # Mapping of one-character OpenGL types to OpenGL C types
    #
    %ogltypes = ("b"  => "GLbyte",
		    "s"  => "GLshort",
		    "i"  => "GLint",
		    "f"  => "GLfloat",
		    "d"  => "GLdouble",
		    "ub" => "GLubyte",
		    "us" => "GLushort",
		    "ui" => "GLuint");

    #
    # How to convert from a specific type to "clampf"
    # This is done in exactly the same way that OpenGL handles glColor*, except
    # perhaps for exactly when the clamp to [0,1] is done, and the fact
    # that negative and positive values are mapped in the same way.
    # (e.g. -127 -> 1.0, instead of -128 -> 1.0 for 'b').
    # See OpenGL red book, 3rd edition, Table 4-1
    #
    %clampfconv = ("b"  => "(1.0 / 127.0) *",
		   "s"  => "(1.0 / 32767.0) *",
		   "i"  => "(1.0 / 2147483647.0) *",
		   "f"  => "(float)",
		   "d"  => "(float)",
		   "ub" => "(1.0 / 255.0) *",
		   "us" => "(1.0 / 65535.0) *",
		   "ui" => "(1.0 / 4294967295.0) *");

    #
    # Print a comment
    #

    $file_header = "/*\n" .
	           " * DO NOT EDIT THIS FILE\n" .
	           " * (it is automatically generated by $0)\n" .
		   " */\n";
    
    print $file_header;

    #*******************************
    # sglParameter*
    #*******************************
    
    #
    # Template
    # 
    $ctemplate = "
void <ROUTINENAME>(int handle, <ARGLIST>) {
  int pnum;
  float *pslot;  /* May refer to int also -- but it's 4-byte data */
<EXTRAVARS>
  if ((handle < 0) || (handle > SGL_MAX_PARAMHANDLE) || (!imstat.shader)) {
    SL_ERROR(GL_INVALID_VALUE, SGL_MAJOR,
             \"<ROUTINENAME>: Invalid handle or no active surface shader\");
  }
  if ((pnum = imstat.shader->handles[handle]) == -1) {
    SL_ERROR(GL_INVALID_VALUE, SGL_MINOR,
             \"<ROUTINENAME>: No such parameter\");
  }
  if (imstat.shader->pdescs[pnum].vcnt != <PCNT>)
    SL_ERROR(GL_INVALID_OPERATION, SGL_MAJOR,
             \"<ROUTINENAME>: Wrong value-count for parameter\");
  pslot = (float *)(imstat.shader->pbuf + imstat.shader->pdescs[pnum].offset);
  switch (imstat.shader->pdescs[pnum].ptype) {
  case PT_FLOATSTORE:
<PCOPY_FLOATSTORE>
    break;
  case PT_CLAMPFSTORE:
<PCOPY_CLAMPFSTORE>
    break;
  case PT_INTSTORE:
<PCOPY_INTSTORE>
    break;
  }
} /* <ROUTINENAME> */
";
    
    $htemplate = "void <ROUTINENAME>(int handle, <ARGLIST>);\n";

    $template = ($do_cfile) ? $ctemplate : $htemplate;

    #
    # Each entry:
    #   number of params, type of param, vector?
    #
    @routines = (
		 [1,  "b", "" ],  [1,  "b", "v"],
		 [1,  "ub","" ],  [1,  "ub","v"],
		 [1,  "s", "" ],  [1,  "s", "v"],
		 [1,  "us","" ],  [1,  "us","v"],
		 [1,  "i", "" ],  [1,  "i", "v"],
		 [1,  "ui","" ],  [1,  "ui","v"],
		 [1,  "f", "" ],  [1,  "f", "v"],
		 [1,  "d", "" ],  [1,  "d", "v"],
		 [3,  "b", "" ],  [3,  "b", "v"],
		 [3,  "ub","" ],  [3,  "ub","v"],
		 [3,  "s", "" ],  [3,  "s", "v"],
		 [3,  "us","" ],  [3,  "us","v"],
		 [3,  "i", "" ],  [3,  "i", "v"],
		 [3,  "ui","" ],  [3,  "ui","v"],
		 [3,  "f", "" ],  [3,  "f", "v"],
		 [3,  "d", "" ],  [3,  "d", "v"],
		 [4,  "b", "" ],  [4,  "b", "v"],
		 [4,  "ub","" ],  [4,  "ub","v"],
		 [4,  "s", "" ],  [4,  "s", "v"],
		 [4,  "us","" ],  [4,  "us","v"],
		 [4,  "i", "" ],  [4,  "i", "v"],
		 [4,  "ui","" ],  [4,  "ui","v"],
		 [4,  "f", "" ],  [4,  "f", "v"],
		 [4,  "d", "" ],  [4,  "d", "v"],
		 [9, "b", "v"],
		 [9, "ub","v"],
		 [9, "s", "v"],
		 [9, "us","v"],
		 [9, "i", "v"],
		 [9, "ui","v"],
		 [9, "f", "v"],
		 [9, "d", "v"],
		 [16, "b", "v"],
		 [16, "ub","v"],
		 [16, "s", "v"],
		 [16, "us","v"],
		 [16, "i", "v"],
		 [16, "ui","v"],
		 [16, "f", "v"],
		 [16, "d", "v"]
		 );

    #
    # Generate sglParameter* routines
    #
    for $i (0 .. $#routines) {
	$pcnt  = $routines[$i][0];
	$ptype = $routines[$i][1];
	$vflag = $routines[$i][2];
	#
	# Storage type
	#
	$storetype = $ogltypes{$ptype};
	#
	# Routine name
	#
	$routinename = $pcnt.$ptype;
	if ($vflag eq "v") {
	    $routinename = $routinename . "v";
	}
	#
	# Arg list
	#
	if ($vflag eq "v") {
	    $arglist = $ogltypes{$ptype}." *p";
	} else {
	    $arglist = "";
	    for $j (1 .. $pcnt) {
		if ($j != 1) {$arglist = $arglist . ", ";}
		$arglist = $arglist . $ogltypes{$ptype} . " p" . $j;
	    }
	}
	#
	# extravars and pcopy
	#
	if ($vflag eq "v") {
	    $extravars = "  int i;\n";
	    $pcopy = "  for (i=0; i<".$pcnt."; i++) ".
		"*(<STORETYPE>*)(pslot++) = <CONVERT> p[i];";
	} else {
	    $extravars = "";
	    $pcopy = "";
	    for $j (1 .. $pcnt) {
		if ($j != 1) {$pcopy = $pcopy . "\n";}
		if ($j != $pcnt) {
		    $pcopy = $pcopy . "    *(<STORETYPE>*)(pslot++) = ".
		    "<CONVERT> p".$j.";";
		} else {
		    $pcopy = $pcopy . "    *(<STORETYPE>*)(pslot)   = ".
		    "<CONVERT> p".$j.";";
		}
	    }
	}
	# make copy variants
	$pcopy_floatstore = $pcopy;
	$pcopy_floatstore =~ s/<STORETYPE>/GLfloat/g;
	$pcopy_floatstore =~ s/<CONVERT>/(GLfloat)/g;
	$pcopy_clampfstore = $pcopy;
	$pcopy_clampfstore =~ s/<STORETYPE>/GLfloat/g;
	$pcopy_clampfstore =~ s/<CONVERT>/$clampfconv{$ptype}/g;
	if (($ptype eq "f") || ($ptype eq "d")) {
	    $pcopy_intstore = 
		"    SL_ERROR(GL_INVALID_OPERATION, SGL_MAJOR, \n".
		"      \"sglParameter".$routinename.
		": Can't pass double/float to this parameter\");";
	} else {
	    $pcopy_intstore = $pcopy;
	    $pcopy_intstore =~ s/<STORETYPE>/GLint/g;
	    $pcopy_intstore =~ s/<CONVERT>/(GLint)/g;
	}
	#
	# make substitutions & print
	#
	$tmp = $template;
	$tmp =~ s/<ROUTINENAME>/sglParameter$routinename/g;
	$tmp =~ s/<ARGLIST>/$arglist/g;
	$tmp =~ s/<EXTRAVARS>/$extravars/g;
	$tmp =~ s/<PCNT>/$pcnt/g;
	$tmp =~ s/<PCOPY_FLOATSTORE>/$pcopy_floatstore/g;
	$tmp =~ s/<PCOPY_CLAMPFSTORE>/$pcopy_clampfstore/g;
	$tmp =~ s/<PCOPY_INTSTORE>/$pcopy_intstore/g;
	print $tmp;
    }

    #*******************************
    # sglLightParameter*
    #*******************************

    #
    # Template
    #
    $ctemplate = "
void <ROUTINENAME>(int lightid, int handle, <ARGLIST>) {
  int pnum;
  int i, j, k;
  shaderinfo_t *light;
  float *pslot;  /* May refer to int also -- but it's 4-byte data */
  /*
   * Find light shader using its ID
   */
  light = ihash_find(&(imstat.shaderIDs), lightid);
  if (!light)
    SL_ERROR(GL_INVALID_VALUE, SGL_MINOR,
	     \"<ROUTINENAME>: No such light\");
  if (!is_light_shader(light->shader))
    SL_ERROR(GL_INVALID_OPERATION, SGL_MAJOR,
	     \"<ROUTINENAME>: Not a light shader\");
  /*
   * Check parameter handle & verify type
   */
  if ((handle < 0) || (handle > SGL_MAX_PARAMHANDLE) ||
      ((pnum = light->handles[handle]) == -1)) {
    SL_ERROR(GL_INVALID_VALUE, SGL_MINOR,
	     \"<ROUTINENAME>: Invalid parameter handle or no such parameter\");
  }
  if (light->pdescs[pnum].vcnt != <PCNT>)
    SL_ERROR(GL_INVALID_OPERATION, SGL_MAJOR,
	     \"<ROUTINENAME>: Wrong value-count for parameter\");
  /*
   * Store parameter value with light shader
   */
  pslot = (float *)(light->pbuf + light->pdescs[pnum].offset);
  switch (light->pdescs[pnum].ptype) {
  case PT_FLOATSTORE:
<PCOPY_FLOATSTORE>
    break;
  case PT_CLAMPFSTORE:
<PCOPY_CLAMPFSTORE>
    break;
  case PT_INTSTORE:
<PCOPY_INTSTORE>
    break;
  }
  /*
   * Check active surface/light combination for this same parameter
   */
  if (!imstat.shader) return;
  for (k=0; k<imstat.shader->n_parms; k++) {
    if ((imstat.shader->pdescs[k].lightshader == light) &&
	(imstat.shader->pdescs[k].lightparam == &(light->pdescs[pnum]))) {
      copy_lightp_to_surface(light, imstat.shader->pdescs[k].lightparam,
			     imstat.shader, &(imstat.shader->pdescs[k]));
    }
  }
} /* <ROUTINENAME> */
";

    $htemplate = "void <ROUTINENAME>(int lightid, int handle, <ARGLIST>);\n";

    $template = ($do_cfile) ? $ctemplate : $htemplate;

    #
    # Each entry:
    #   number of params, type of param, vector?
    #
    @routines = (
		 [1,  "b", "" ],  [1,  "b", "v"],
		 [1,  "ub","" ],  [1,  "ub","v"],
		 [1,  "s", "" ],  [1,  "s", "v"],
		 [1,  "us","" ],  [1,  "us","v"],
		 [1,  "i", "" ],  [1,  "i", "v"],
		 [1,  "ui","" ],  [1,  "ui","v"],
		 [1,  "f", "" ],  [1,  "f", "v"],
		 [1,  "d", "" ],  [1,  "d", "v"],
		 [3,  "b", "" ],  [3,  "b", "v"],
		 [3,  "ub","" ],  [3,  "ub","v"],
		 [3,  "s", "" ],  [3,  "s", "v"],
		 [3,  "us","" ],  [3,  "us","v"],
		 [3,  "i", "" ],  [3,  "i", "v"],
		 [3,  "ui","" ],  [3,  "ui","v"],
		 [3,  "f", "" ],  [3,  "f", "v"],
		 [3,  "d", "" ],  [3,  "d", "v"],
		 [4,  "b", "" ],  [4,  "b", "v"],
		 [4,  "ub","" ],  [4,  "ub","v"],
		 [4,  "s", "" ],  [4,  "s", "v"],
		 [4,  "us","" ],  [4,  "us","v"],
		 [4,  "i", "" ],  [4,  "i", "v"],
		 [4,  "ui","" ],  [4,  "ui","v"],
		 [4,  "f", "" ],  [4,  "f", "v"],
		 [4,  "d", "" ],  [4,  "d", "v"],
		 [9, "b", "v"],
		 [9, "ub","v"],
		 [9, "s", "v"],
		 [9, "us","v"],
		 [9, "i", "v"],
		 [9, "ui","v"],
		 [9, "f", "v"],
		 [9, "d", "v"],
		 [16, "b", "v"],
		 [16, "ub","v"],
		 [16, "s", "v"],
		 [16, "us","v"],
		 [16, "i", "v"],
		 [16, "ui","v"],
		 [16, "f", "v"],
		 [16, "d", "v"]
		 );

    #
    # Generate sglLightParameter* routines
    #
    for $i (0 .. $#routines) {
	$pcnt  = $routines[$i][0];
	$ptype = $routines[$i][1];
	$vflag = $routines[$i][2];
	#
	# Storage type
	#
	$storetype = $ogltypes{$ptype};
	#
	# Routine name
	#
	$routinename = $pcnt.$ptype;
	if ($vflag eq "v") {
	    $routinename = $routinename . "v";
	}
	#
	# Arg list
	#
	if ($vflag eq "v") {
	    $arglist = $ogltypes{$ptype}." *p";
	} else {
	    $arglist = "";
	    for $j (1 .. $pcnt) {
		if ($j != 1) {$arglist = $arglist . ", ";}
		$arglist = $arglist . $ogltypes{$ptype} . " p" . $j;
	    }
	}
	#
	# extravars and pcopy
	#
	if ($vflag eq "v") {
	    $extravars = "  int i;\n";
	    $pcopy = "  for (i=0; i<".$pcnt."; i++) ".
		"*(<STORETYPE>*)(pslot++) = <CONVERT> p[i];";
	} else {
	    $extravars = "";
	    $pcopy = "";
	    for $j (1 .. $pcnt) {
		if ($j != 1) {$pcopy = $pcopy . "\n";}
		if ($j != $pcnt) {
		    $pcopy = $pcopy . "    *(<STORETYPE>*)(pslot++) = ".
			"<CONVERT> p".$j.";";
		} else {
		    $pcopy = $pcopy . "    *(<STORETYPE>*)(pslot)   = ".
			"<CONVERT> p".$j.";";
		}
	    }
	}
	# make copy variants
	$pcopy_floatstore = $pcopy;
	$pcopy_floatstore =~ s/<STORETYPE>/GLfloat/g;
	$pcopy_floatstore =~ s/<CONVERT>/(GLfloat)/g;
	$pcopy_clampfstore = $pcopy;
	$pcopy_clampfstore =~ s/<STORETYPE>/GLfloat/g;
	$pcopy_clampfstore =~ s/<CONVERT>/$clampfconv{$ptype}/g;
	if (($ptype eq "f") || ($ptype eq "d")) {
	    $pcopy_intstore =
		"    SL_ERROR(GL_INVALID_OPERATION, SGL_MAJOR, \n".
	        "      \"sglParameter".$routinename.
	        ": Can't pass double/float to this parameter\");";
	} else {
	    $pcopy_intstore = $pcopy;
	    $pcopy_intstore =~ s/<STORETYPE>/GLint/g;
	    $pcopy_intstore =~ s/<CONVERT>/(GLint)/g;
	}
	#
	# make substitutions & print
	#
	$tmp = $template;
	$tmp =~ s/<ROUTINENAME>/sglLightParameter$routinename/g;
	$tmp =~ s/<ARGLIST>/$arglist/g;
	$tmp =~ s/<PCNT>/$pcnt/g;
	$tmp =~ s/<PCOPY_FLOATSTORE>/$pcopy_floatstore/g;
	$tmp =~ s/<PCOPY_CLAMPFSTORE>/$pcopy_clampfstore/g;
	$tmp =~ s/<PCOPY_INTSTORE>/$pcopy_intstore/g;
	print $tmp;
    }


    #*******************************
    # sglVertex*
    #*******************************

    #
    # Template
    #
    $ctemplate = "
void <ROUTINENAME>(<ARGLIST>) {
  float *pslot;
  int i;
  int *a;
  int *b;
  /*
   * If not using shading language, just call corresponding glVertex*
   */
  if (!imstat.shader) {<GLROUTINENAME>(<ARGLIST_CALL>); return;}
  /*
   * Must be inside sglBegin
   */
  if (imstat.vertmode == -1) {
    SL_ERROR(GL_INVALID_OPERATION, SGL_MAJOR,
	     \"<ROUTINENAME>: Must be inside sglBegin\");
    return;
  }
  /*
   * Copy position into current-vertex buffer, and perform
   * type conversion if necessary.
   */
  assert(imstat.shader->ipos != -1);
  pslot = (float *)(imstat.shader->pbuf +
		    imstat.shader->pdescs[imstat.shader->ipos].offset);
<PCOPY>
  /*
   * Copy complete vertex data (including parameters) from
   * current-vertex buffer to storage buffer
   */
  a = (int *) imstat.shader->pbuf;
  b = ((int *) imstat.vbuf) +
    imstat.verts_since_begin * imstat.shader->pbufsizeVWORD;
  imstat.verts_since_begin++;
  for (i=0; i<imstat.shader->pbufsizeVWORD; i++)
    *(b++) = *(a++);
  imstat.vbufcnt++;
  /*
   * Flush buffer if needed
   */
  if (imstat.vbufcnt == imstat.shader->maxverts) {
    /*printf(\"autoflush\\n\"); */
    _sglEmptyBuf();
  }
} /* <ROUTINENAME> */
";

    $htemplate = "void <ROUTINENAME>(<ARGLIST>);\n";

    $template = ($do_cfile) ? $ctemplate : $htemplate;

    #
    # Each entry:
    #   number of params, type of param, vector?
    #
    @routines = (
		 [2,  "d", "" ],   [2,  "d", "v" ],
		 [2,  "f", "" ],   [2,  "f", "v" ],
		 [2,  "i", "" ],   [2,  "i", "v" ],
		 [2,  "s", "" ],   [2,  "s", "v" ],
		 [3,  "d", "" ],   [3,  "d", "v" ],
		 [3,  "f", "" ],   [3,  "f", "v" ],
		 [3,  "i", "" ],   [3,  "i", "v" ],
		 [3,  "s", "" ],   [3,  "s", "v" ],
		 [4,  "d", "" ],   [4,  "d", "v" ],
		 [4,  "f", "" ],   [4,  "f", "v" ],
		 [4,  "i", "" ],   [4,  "i", "v" ],
		 [4,  "s", "" ],   [4,  "s", "v" ]
		 );

    #
    # Generate sglVertex* routines
    #
    for $i (0 .. $#routines) {
	$pcnt  = $routines[$i][0];
	$ptype = $routines[$i][1];
	$vflag = $routines[$i][2];
	#
	# Storage type -- always float for now
	#
	$storetype = "float";
	#
	# Routine name
	#
	$routinename = $pcnt.$ptype;
	if ($vflag eq "v") {
	    $routinename = $routinename . "v";
	}
	#
	# Arg list
	#
	if ($vflag eq "v") {
	    $arglist      = $ogltypes{$ptype}." *p";
	    $arglist_call = "p";
	} else {
	    $arglist      = "";
	    $arglist_call = "";
	    for $j (1 .. $pcnt) {
		if ($j != 1) {
		    $arglist      = $arglist      . ", ";
		    $arglist_call = $arglist_call . ", ";
		}
		$arglist      = $arglist      . $ogltypes{$ptype} . " p" . $j;
		$arglist_call = $arglist_call . " p" . $j;
	    }
	}
	#
	# pcopy
	#
	if ($vflag eq "v") {
	    $pcopy = "  for (i=0; i<".$pcnt."; i++) ".
		"*(".$storetype."*)(pslot++) = (".$storetype.") p[i];";
	} else {
	    $pcopy = "";
	    for $j (1 .. $pcnt) {
		if ($j != 1) {$pcopy = $pcopy . "\n";}
		if ($j != 4) {
		    $pcopy = $pcopy . "  *(".$storetype."*)(pslot++) = ".
			"(".$storetype.") p".$j.";";
		} else {
		    $pcopy = $pcopy . "  *(".$storetype."*)(pslot)   = ".
			"(".$storetype.") p".$j.";";
		}
	    }
	}
	if ($pcnt == 2) {
	    $pcopy = $pcopy . "\n  *(".$storetype."*)(pslot++) = 0.0;";
	    $pcopy = $pcopy . "\n  *(".$storetype."*)(pslot) = 1.0;";
	}
	if ($pcnt == 3) {
	    $pcopy = $pcopy . "\n  *(".$storetype."*)(pslot) = 1.0;";
	}
	#
	# make substitutions & print
	#
	$tmp = $template;
	$tmp =~ s/<ROUTINENAME>/sglVertex$routinename/g;
	$tmp =~ s/<GLROUTINENAME>/glVertex$routinename/g;
	$tmp =~ s/<ARGLIST>/$arglist/g;
	$tmp =~ s/<ARGLIST_CALL>/$arglist_call/g;
	$tmp =~ s/<PCNT>/$pcnt/g;
	$tmp =~ s/<PCOPY>/$pcopy/g;
	print $tmp;
    }

    #***************************************
    # sglNormal*, sglTangent*, sglBiNormal*
    #***************************************

    #
    # Template
    #
    $ctemplate = "
void <ROUTINENAME>(<ARGLIST>) {
  float *pslot;
<EXTRAVARS>
  /*
   * If not using shading language, make appropriate GL call (if any), and
   * return.
   */
  if (!imstat.shader) {<GLCALL> return;}
  /*
   * Do it
   */
  if (imstat.shader-><WHAT> == -1) return;
  pslot = (float *)(imstat.shader->pbuf +
		    imstat.shader->pdescs[imstat.shader-><WHAT>].offset);
<PCOPY>
} /* <ROUTINENAME> */
";

    $htemplate = "void <ROUTINENAME>(<ARGLIST>);\n";

    $template = ($do_cfile) ? $ctemplate : $htemplate;

    #
    # Each entry:
    #   number of params, type of param, vector?
    #
    @routines = (
		 [3,  "d", "" ],   [3,  "d", "v" ],
		 [3,  "f", "" ],   [3,  "f", "v" ],
		 [3,  "i", "" ],   [3,  "i", "v" ],
		 [3,  "s", "" ],   [3,  "s", "v" ]
		 );
    
    # What variable in shader structure holds the parameter's index into
    # buffer?
    %pindexlist = ("Normal"   => "inorm",
		   "Tangent"  => "itan",
		   "BiNormal" => "ibinorm");
    #
    # Generate sglNormal*, sglTangent*, sglBinormal* routines
    #
    for $rtype ('Normal','Tangent','BiNormal') {
	for $i (0 .. $#routines) {
	    $pcnt  = $routines[$i][0];
	    $ptype = $routines[$i][1];
	    $vflag = $routines[$i][2];
	    #
	    # Storage type -- always float for now
	    #
	    $storetype = "float";
	    #
	    # Routine name
	    #
	    $routinename = $pcnt.$ptype;
	    if ($vflag eq "v") {
		$routinename = $routinename . "v";
	    }
	    #
	    # Arg list
	    #
	    if ($vflag eq "v") {
		$arglist      = $ogltypes{$ptype}." *p";
		$arglist_call = "p";
	    } else {
		$arglist      = "";
		$arglist_call = "";
		for $j (1 .. $pcnt) {
		    if ($j != 1) {
			$arglist      = $arglist      . ", ";
			$arglist_call = $arglist_call . ", ";
		    }
		    $arglist      = $arglist      . $ogltypes{$ptype} . " p$j";
		    $arglist_call = $arglist_call . " p" . $j;
		}
	    }
	    #
	    # pcopy
	    #
	    if ($vflag eq "v") {
		$extravars = "  int i;\n";
		$pcopy = "  for (i=0; i<".$pcnt."; i++) ".
		    "*(".$storetype."*)(pslot++) = (".$storetype.") p[i];";
	    } else {
		$extravars = "";
		$pcopy = "";
		for $j (1 .. $pcnt) {
		    if ($j != 1) {$pcopy = $pcopy . "\n";}
		    if ($j != 4) {
			$pcopy = $pcopy . "  *(".$storetype."*)(pslot++) = ".
			    "(".$storetype.") p".$j.";";
		    } else {
			$pcopy = $pcopy . "  *(".$storetype."*)(pslot)   = ".
			    "(".$storetype.") p".$j.";";
		    }
		}
	    }
	    #
	    # make substitutions & print
	    #
	    $tmp = $template;
	    $tmp =~ s/<ROUTINENAME>/sgl$rtype$routinename/g;
	    $tmp =~ s/<ARGLIST>/$arglist/g;
	    $tmp =~ s/<EXTRAVARS>/$extravars/g;
	    if ($rtype eq "Normal") {
		$tmp =~ s/<GLCALL>/gl$rtype$routinename($arglist_call);/g;
	    } else {
		$tmp =~ s/<GLCALL>//g;
	    }
	    $tmp =~ s/<WHAT>/$pindexlist{$rtype}/g;
	    $tmp =~ s/<PCNT>/$pcnt/g;
	    $tmp =~ s/<PCOPY>/$pcopy/g;
	    print $tmp;
	}
    }

    #********************
    # Cleanup
    #********************

    # need endif to match the #ifndef _IMODE_IN_ at start of file
    print "#ifdef __cplusplus\n" if (!$do_cfile);
    print "}\n" if (!$do_cfile);
    print "#endif\n" if (!$do_cfile);
    print "#endif\n" if (!$do_cfile);
}

1;
