Difference between revisions of "Stats File Formats (AMX Mod X)"

From AlliedModders Wiki
Jump to: navigation, search
 
Line 37: Line 37:
 
**<tt>uint32[9]</tt> - Body Hits (array of ints, one for each body hit area)
 
**<tt>uint32[9]</tt> - Body Hits (array of ints, one for each body hit area)
 
**<tt>uint16</tt> - If zero, end of file.  Otherwise, loop back up and read the next name.
 
**<tt>uint16</tt> - If zero, end of file.  Otherwise, loop back up and read the next name.
 +
 +
=Sample Code for Reading/Writing=
 +
 +
==C#==
 +
The StatsFile.ReadEntriesToList member function below is part of a quick C# class.  Note that it uses an ArrayList rather than an Array, since the number of items is not known beforehand.
 +
 +
<csharp>
 +
using System;
 +
using System.Collections;
 +
using System.IO;
 +
using System.Text;
 +
 +
public class StatsFile
 +
{
 +
public const short RANK_VERSION = 11;
 +
 +
public class StatsEntry
 +
{
 +
public StatsEntry()
 +
{
 +
bodyHits = new uint[9];
 +
}
 +
 +
public String name;
 +
public String unique;
 +
public uint tks;
 +
public uint damage;
 +
public uint deaths;
 +
public int kills;
 +
public uint shots;
 +
public uint hits;
 +
public uint hs;
 +
public uint bDefusions;
 +
public uint bDefused;
 +
public uint bPlants;
 +
public uint bExplosions;
 +
public uint [] bodyHits;
 +
}
 +
 +
public static ArrayList ReadEntriesToList(string file)
 +
{
 +
if (!File.Exists(file))
 +
{
 +
throw new FileNotFoundException();
 +
}
 +
 +
System.IO.FileStream stream = File.Open(file, System.IO.FileMode.Open);
 +
 +
if (stream == null)
 +
{
 +
throw new FileLoadException();
 +
}
 +
 +
BinaryReader br = new BinaryReader(stream);
 +
ArrayList list;
 +
 +
try
 +
{
 +
short vers = br.ReadInt16();
 +
 +
if (vers != RANK_VERSION)
 +
{
 +
throw new Exception("Bad stats version");
 +
}
 +
 +
ushort num = br.ReadUInt16();
 +
list = new ArrayList();
 +
 +
while (num != 0)
 +
{
 +
StatsEntry entry = new StatsEntry();
 +
 +
byte [] name = br.ReadBytes(num);
 +
num = br.ReadUInt16();
 +
byte [] unique = br.ReadBytes(num);
 +
 +
entry.name = Encoding.ASCII.GetString(name, 0, name.Length-1);
 +
entry.unique = Encoding.ASCII.GetString(unique, 0, unique.Length-1);
 +
 +
entry.tks = br.ReadUInt32();
 +
entry.damage = br.ReadUInt32();
 +
entry.deaths = br.ReadUInt32();
 +
entry.kills = br.ReadInt32();
 +
entry.shots = br.ReadUInt32();
 +
entry.hits = br.ReadUInt32();
 +
entry.hs = br.ReadUInt32();
 +
entry.bDefusions = br.ReadUInt32();
 +
entry.bDefused = br.ReadUInt32();
 +
entry.bPlants = br.ReadUInt32();
 +
entry.bExplosions = br.ReadUInt32();
 +
 +
for (int i=0; i<entry.bodyHits.Length; i++)
 +
{
 +
entry.bodyHits[i] = br.ReadUInt32();
 +
}
 +
 +
num = br.ReadUInt16();
 +
 +
list.Add(entry);
 +
}
 +
}
 +
catch
 +
{
 +
throw new FileLoadException("Error reading file");
 +
}
 +
finally
 +
{
 +
if (br != null)
 +
{
 +
br.Close();
 +
br = null;
 +
}
 +
if (stream != null)
 +
{
 +
stream.Close();
 +
stream = null;
 +
}
 +
}
 +
 +
return list;
 +
}
 +
}
 +
</csharp>

Revision as of 22:04, 29 June 2006

AMX Mod X has statistics modules for Counter-Strike (CSX), Day of Defeat (DoDX), Team Fortress Classic (TFCX), and The Specialists (TSX).

They each have their own similar but separate binary file format. These file formats must be sequentially read and written, and are not organized into tables. This makes them a bit confusing to extract iteratively, however the format is still trivial.

Each stats file has a header, which contains the version. After the header, each stats entry is written sequentially. Each stats entry contains strings, and the strings are prefixed with the number of bytes to read. Once this number contains zero, the stats file has no more data, and thus iteration must be stopped.

The file formats in this document are displayed as lists. When the list has an indented section, it means that that section of the file is repeated until a condition. Explanations of the data types are below. Sample code is provided for CSX.

How to Interpret

  • int8/uint8 - Signed or Unsigned Byte (8 bits). C types: char, unsigned char
  • int16/uint16 - Signed or Unsigned Word (16 bits). C types: short, unsigned short
  • int32/uint16 - Signed or Unsigned Doubleword (32 bits). C types: int, unsigned int
  • sz - Zero-terminated C-style string

If a size is followed by [N], for example, int8[9], this would mean 9 blocks of int8 data must be read.

CSX (csstats.dat)

The CSSTATS format has RANK_VERSION of 11. The version numbers are considered isolated rather than backwards or forward compatible.

  • int16 - RANK_VERSION
  • uint16 - Bytes in first string (if 0, no entries)
    • sz - Name (read N bytes, where N is previous uint16)
    • uint16 - Bytes of next string
    • sz - Steam ID (read N bytes, where N is previous uint16)
    • uint32 - Team kills
    • uint32 - Damage
    • uint32 - Deaths
    • int32 - Kills
    • uint32 - Shots
    • uint32 - Hits
    • uint32 - Headshots
    • uint32 - Defusions
    • uint32 - Defusal Attempts
    • uint32 - Plants
    • uint32 - Explosions
    • uint32[9] - Body Hits (array of ints, one for each body hit area)
    • uint16 - If zero, end of file. Otherwise, loop back up and read the next name.

Sample Code for Reading/Writing

C#

The StatsFile.ReadEntriesToList member function below is part of a quick C# class. Note that it uses an ArrayList rather than an Array, since the number of items is not known beforehand.

using System;
using System.Collections;
using System.IO;
using System.Text;
 
public class StatsFile
{
	public const short RANK_VERSION = 11;
 
	public class StatsEntry
	{
		public StatsEntry()
		{
			bodyHits = new uint[9];
		}
 
		public String name;
		public String unique;
		public uint tks;
		public uint damage;
		public uint deaths;
		public int kills;
		public uint shots;
		public uint hits;
		public uint hs;
		public uint bDefusions;
		public uint bDefused;
		public uint bPlants;
		public uint bExplosions;
		public uint [] bodyHits;
	}
 
	public static ArrayList ReadEntriesToList(string file)
	{
		if (!File.Exists(file))
		{
			throw new FileNotFoundException();
		}
 
		System.IO.FileStream stream = File.Open(file, System.IO.FileMode.Open);
 
		if (stream == null)
		{
			throw new FileLoadException();
		}
 
		BinaryReader br = new BinaryReader(stream);
		ArrayList list;
 
		try
		{
			short vers = br.ReadInt16();
 
			if (vers != RANK_VERSION)
			{
				throw new Exception("Bad stats version");
			}
 
			ushort num = br.ReadUInt16();
			list = new ArrayList();
 
			while (num != 0)
			{
				StatsEntry entry = new StatsEntry();
 
				byte [] name = br.ReadBytes(num);
				num = br.ReadUInt16();
				byte [] unique = br.ReadBytes(num);
 
				entry.name = Encoding.ASCII.GetString(name, 0, name.Length-1);
				entry.unique = Encoding.ASCII.GetString(unique, 0, unique.Length-1);
 
				entry.tks = br.ReadUInt32();
				entry.damage = br.ReadUInt32();
				entry.deaths = br.ReadUInt32();
				entry.kills = br.ReadInt32();
				entry.shots = br.ReadUInt32();
				entry.hits = br.ReadUInt32();
				entry.hs = br.ReadUInt32();
				entry.bDefusions = br.ReadUInt32();
				entry.bDefused = br.ReadUInt32();
				entry.bPlants = br.ReadUInt32();
				entry.bExplosions = br.ReadUInt32();
 
				for (int i=0; i<entry.bodyHits.Length; i++)
				{
					entry.bodyHits[i] = br.ReadUInt32();
				}
 
				num = br.ReadUInt16();
 
				list.Add(entry);
			}
		}
		catch 
		{
			throw new FileLoadException("Error reading file");
		}
		finally
		{
			if (br != null)
			{
				br.Close();
				br = null;
			}
			if (stream != null)
			{
				stream.Close();
				stream = null;
			}
		}
 
		return list;
	}
}