Yesterday, during an online chat with IznoGod, I was made aware that the story behind the COD2 modding community discovering the file functions was not universally known. This made it hit home to me that, as old modders have moved on, and new modders have taken their place, some of the great stories of COD modding have been lost in time. So, I will take this opportunity to tell the story, as tribute to those great modders:
In early 2006 to early 2007, a year or two before the COD4 mod tools came out, the community of COD2 modders did not know about any of the file functions possible in COD2. That's because none were included in the official COD2 documentation. Purely by chance, a young modder by the name of
Amwhere discovered them. He posted his findings on the now-defunct former official Infinity Ward support forums IWNation.com (of which I am proud to say I was an administrator at) circa February 2007, and we took up his findings and ran with them.
As IWNation.com is no longer online, and as the Waybackmachine hasn't cached the thread (see
here), I cannot reproduce for you what Amwhere actually posted. All I have of his original post is his presentation of the actual file functions:
Code:
//Writing to a file
f = OpenFile( "test.txt", "append" ); //int OpenFile(string filename, string readmode) readmode = "read", "write", "append"
fprintln( f, "Hello World!" ); //void fprintln (int file, string text) Writes a csv line, ie. "Hello World!,"
closefile(f); //void closefile(int file) Closes the file and clears the buffer. DO NOT forget to do this, can cause errors if you don't!
//Reading From a File
e = OpenFile("test.txt", "read");
lines = freadln(e); //int freadln(int file) Returns the number of CSV elements in the file - only works the first time on a file, returns -1 afterwords.
fgetarg(e, 0); //string fgetarg (int file, int arg) Returns the contents of the CSV element at position arg.
//An Example:
iprintln(lines + " total lines");
for(i = 0; i < lines; i++)
{
a[i] = fgetarg(e, i);
iprintln("Line " + i + ": " + a[i]);
}
CloseFile(e);
REMEMBER: we did not have the luxury of a proper presentation of the functions via an official documentation from the developers like people have today, with the COD4 Script Documents, which were not released until January 2008 - a whole year after
Amwhere had posted. All we had was what I just posted.
After
Amwhere had posted, several modders, including myself got working on putting the findings to good use. One such one was the Dvar Replacement Module (DRM) by
Nedgerblanski/laTruffer. This was intended to get around the max limit on dvars that the COD2 engine has. Several mods like the eXtreme+ mod for COD2 had already been hitting. Here is Ned's DRM module as implemented into the eXtreme+ mod by
Joker,
Patmansan, and
myself (I was an eXtreme+ mod crew member) :
Code:
// Parse a config file
drm_parsefile(fname, fdesc)
{
drm_log("DRM: Reading config file " + fname);
for( ; ; )
{
elems = freadln(fdesc);
if(elems == -1)
break;
if(elems == 0)
continue;
line = fgetarg(fdesc, 0);
//drm_log("Line read : >" + line + "<");
if((getsubstr(line, 0, 2) == "//") || (getsubstr(line, 0, 1) == "#"))
{
//drm_log(" comment -> ignored\n");
continue;
}
cleanline = "";
last = " ";
for(i = 0; i < line.size; i ++)
{
//drm_log("line[i] : >" + line[i] + "<");
switch(line[i])
{
case " " : // tab
case " " : // space 0xa0
case " " : // space 0x20
if(last != " ")
{
//drm_log("added space");
cleanline += " ";
last = " ";
}
break;
case "/" :
if(last == "/")
{
//drm_log("exiting");
i = line.size; // exiting from for loop
break;
}
else
{
//drm_log("adding slash");
cleanline += "/";
last = "/";
}
break;
case "#" :
//drm_log("exiting");
i = line.size; // exiting from for loop
break;
default :
//drm_log("adding : >" + line[i] + "<");
cleanline += line[i];
last = line[i];
break;
}
}
if((cleanline.size >= 2) && (getsubstr(cleanline, cleanline.size - 2) == " /"))
{
// Ends with " /"
notsocleanline = cleanline;
cleanline = getsubstr(notsocleanline, 0, notsocleanline.size - 2);
}
if((cleanline.size >= 1) && ((cleanline[cleanline.size - 1] == " ") || (cleanline[cleanline.size - 1] == "/")))
{
// Ends with " " or "/"
notsocleanline = cleanline;
cleanline = getsubstr(notsocleanline, 0, notsocleanline.size - 1);
}
if(cleanline == "")
{
//drm_log(" nothing left -> ignored\n");
continue;
}
//drm_log("cleanline : >" + cleanline + "<");
array = strtok(cleanline, " ");
setcmd = array[0];
if((setcmd != "set") && (setcmd != "seta") && (setcmd != "sets"))
{
//drm_log(" does not begin with set, seta or sets -> ignored\n");
continue;
}
if(array.size == 1)
{
//drm_log(" missing var name -> ignored\n");
continue;
}
var = array[1];
if(array.size == 2)
// Value is null
val = "";
else
// Value is not null
val = getsubstr(cleanline, setcmd.size + var.size + 2);
//drm_log(" OK ! var " + var + " will be set to \"" + val + "\"\n");
game["drm"][var] = val;
}
closefile(fdesc);
}
Using the DRM module, it becomes possible to have almost as many dvars as you like, without hitting the max limit. All you do is replace using the standard getCvar() functions with ones like these:
Code:
// Replacement for getcvar
drm_getcvar(var)
{
if(isDefined(game["drm"][var]))
return(game["drm"][var]);
return("");
}
// Replacement for getcvarint
drm_getcvarint(var)
{
if(isDefined(game["drm"][var]))
return(atoi(game["drm"][var]));
return(0);
}
// Replacement for getcvarfloat
drm_getcvarfloat(var)
{
if(isDefined(game["drm"][var]))
return(atof(game["drm"][var]));
return(0);
}
The following 2 code utilities are used by the code above:
Code:
/*********************************************************************************************
atof() - a COD aproximation of C++ atof() function which converts a string to double
(i.e. it parses the C string str interpreting its content as a floating point number and
returns its value as a double).
It was written by nedgerblansky (aka La Truffe) for the AWE Community Edition mod -
circa 2007.
**********************************************************************************************/
atof( str )
{
if( !isdefined( str ) || !str.size )
return( 0 );
switch( str[0] )
{
case "+" :
sign = 1;
offset = 1;
break;
case "-" :
sign = -1;
offset = 1;
break;
default :
sign = 1;
offset = 0;
break;
}
str2 = getsubstr( str, offset );
parts = strtok( str2, "." );
intpart = atoi( parts[0] );
decpart = atoi( parts[1] );
if( decpart < 0 )
return( 0 );
if( decpart )
for( i = 0; i < parts[1].size; i ++ )
decpart = decpart / 10;
return( (intpart + decpart) * sign );
}
/****************************************************************************
atoi() - Convert string to integer.
Parses the C string str interpreting its content as an integral number,
which is returned as an int value.
It was written by nedgerblansky (aka La Truffe) for the AWE Community
Edition mod - circa 2007.
******************************************************************************/
atoi( str )
{
if( !isdefined( str ) || !str.size )
return( 0 );
ctoi = [];
ctoi["0"] = 0;
ctoi["1"] = 1;
ctoi["2"] = 2;
ctoi["3"] = 3;
ctoi["4"] = 4;
ctoi["5"] = 5;
ctoi["6"] = 6;
ctoi["7"] = 7;
ctoi["8"] = 8;
ctoi["9"] = 9;
switch( str[0] )
{
case "+" :
sign = 1;
offset = 1;
break;
case "-" :
sign = -1;
offset = 1;
break;
default :
sign = 1;
offset = 0;
break;
}
val = 0;
for( i = offset; i < str.size; i ++ )
{
switch( str[i] )
{
case "0" :
case "1" :
case "2" :
case "3" :
case "4" :
case "5" :
case "6" :
case "7" :
case "8" :
case "9" :
val = val * 10 + ctoi[ str[i] ];
break;
default :
return( 0 );
}
}
return( val * sign );
}
One of the most rewarding aspects of modding in the COD2 modding community back then was that we were often doing stuff without any official mod tools released, or proper documentation. No one came forward from IW to help us when we were posting on the forums that we needed a work around for the max dvar limit, and
Amwhere finding the file functions by chance, and the other prominent COD2 modders - of which I am proud to say I was one of - took up the findings and ran with them. How I miss those golden days of COD modding!
If you've made it to this point, thanks for listening. I know it was a little long winded.