CPF/Source Code

From SimsWiki
< CPF
Revision as of 22:10, 26 January 2007 by Jfade (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Contents

Overview

This page contains source code in various languages for handling the CPF resource.

Source Code in R

Overview

This page contains a sample R function to read a CPF and extract information about it.

Use at your own risk. Any comments please use the Talk:CPF/Source Code page.

Source

#
# raw is a list of bytes (raw format). They
# should have been read by readBin(f, "raw", n) or
# using a decompressing routine
#
# The return value is a list, with "human" as a human-readable
# list of things in the CPF.
#
convert_cpf <- function(raw)
{
  cpf <- NULL
  cpf$id <- "CPF"
  cpf$version <- get_little_endian(raw, 2)
  n <- get_little_endian(raw[3:6], 4) # number of entries
  cpf$data <- NULL
  pos <- 7  # first 6 bytes are version and number of fields
  # start decoding at position 7
  for (i in 1:n) {
    xtype <- get_little_endian(raw[pos:(pos+3)], 4)
    # xtype is the type of the data, in a crazy hex code
    pos <- pos + 4
    # after each interpretation of raw bytes, we should move pos
    nlen <- get_little_endian(raw[pos:(pos+3)], 4)  # len of field name
    pos <- pos + 4
    name <- rawToChar(raw[pos:(pos+nlen-1)])
    pos <- pos + nlen
    if (xtype == 0xEB61E4F7 || xtype == 0x0C264712 || xtype == 0xABC78708) {  # integer or float
# Nota Bene: I didn't care for float data, I just read and ignore
      data <- get_little_endian(raw[pos:(pos+3)], 4)
      pos <- pos + 4
      cpf[[name]] <- data
    }
    else if (xtype == 0x0B8BEA18) { # string
# get string length
      slen <- get_little_endian(raw[pos:(pos+3)], 4)
      pos <- pos + 4
      str <- rawToChar(raw[pos:(pos+slen-1)])
      pos <- pos + slen
      cpf[[name]] <- str
    }
    else if (xtype == 0xCBA908E1) { # boolean
      cpf[[name]] <- raw[pos]
      pos <- pos + 1
    }
  }
  return(cpf)
}

#
# get_little_endian converts n bytes in little-endian
# format. It means that the first byte is the less significant
#
get_little_endian <- function(bytes, n)
{
  return(sum(256^(0:(n-1)) * as.integer(bytes[1:n])))
}


Source Code in C#

Some C# code for handling the CPF resource.

Overview

This code assumes that you've already extracted (and decompressed if need be) the CPF resource and stuck it into it's own file. You can feed the path to this file into any of the 3 main functions (FetchArray, FetchCount, and ValidCPF) and it will return whatever info you need.

Note: Where you see references to CCB.Math.ToDec or CCB.Math.ToHex, those are just functions I wrote to easily convert from hex to decimal, and vice versa. You can do the same thing without those functions via several different methods.

Source

	public class CPF
	{
		public static long FetchCount(string DatPath)
		{
			if (ValidCPF(DatPath) == false) 
			{
				//Not a valid CPF file!
				return 0;
			}
			//Valid CPF file. Time to find and return the number of items in this file.
			FileStream fs = new System.IO.FileStream(DatPath, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
			BinaryReader r = new System.IO.BinaryReader(fs);
			byte[] ByteChunk = new byte[4];
			fs.Position = 6;
			ByteChunk = r.ReadBytes(4);
			Array.Reverse(ByteChunk);
			r.Close();
			fs.Close();
			return CCB.Math.ToDec(getBytesAsHexString(ByteChunk, false));
		}

		public static bool ValidCPF(string DatPath)
		{
			FileStream fs = new System.IO.FileStream(DatPath, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
			BinaryReader r = new System.IO.BinaryReader(fs);
			//verify that this is a CPF resource.
			byte[] ByteChunk = new byte[4];
			ByteChunk = r.ReadBytes(4);
			if (getBytesAsHexString(ByteChunk, false) != "E050E7CB") 
			{
				//Not a valid CPF file!
				r.Close();
				fs.Close();
				return false;
			}
			//Valid CPF file.
			r.Close();
			fs.Close();
			return true;
		}
		public static string[] FetchArray(string DatPath)
		{
			if (ValidCPF(DatPath) == false) {
                string[] FakeArray = new string[1];
				FakeArray[0] = "Error";
				return FakeArray;
			}
			//Valid CPF file. Time to DO THE LOOP BABY!
			string[] BigArray = new string[FetchCount(DatPath)];
			long Count = FetchCount(DatPath);
			FileStream fs = new System.IO.FileStream(DatPath, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
			BinaryReader r = new System.IO.BinaryReader(fs);
			byte[] ByteChunk;
			string Type = "";
			string Name = "";
			string Data = "";
			string TestData = "";
			int Length = 0;
			long CurrPos = 0;
			fs.Position = 10;
			SuperAwesomeLoop:
				if (CurrPos == Count) 
				{
					//Woot, woot woot, doneness be good!
					goto CloseAndReturn;
				}
			//The CPF file has 5 types of data. So, we're going to grab the next 4 bytes to see what type of data we're going to
			//grab here, then we'll grab the length and name of the data field, and finally after getting the length of the data
			//field, we'll get the data itself! Yay!
			ByteChunk = new byte[4];
			ByteChunk = r.ReadBytes(4);
			Array.Reverse(ByteChunk);
			TestData = (getBytesAsHexString(ByteChunk, false));
				switch(TestData) 
				{
					case "EB61E4F7":
						Type = "int";
						break;
					case "0B8BEA18":
						Type = "string";
						break;
					case "ABC78708":
						Type = "float";
						break;
					case "CBA908E1":
						Type = "boolean";
						break;
					case "0C264712":
						Type = "int";
						break;
				}
			//With the type figured out, let's grab the length of the name field.
			ByteChunk = r.ReadBytes(4);
			Array.Reverse(ByteChunk);
			Length = CCB.Math.ToDec(getBytesAsHexString(ByteChunk, false));
			ByteChunk = new byte[Length];
			//with the size of the byte chunk changed, let's get the name of the field.
			ByteChunk = r.ReadBytes(Length);
			Name = getBytesAsCharString(ByteChunk);
			//name obtained! Lets get the actual data!
			if (Type == "string")
			{
				ByteChunk = new byte[4];
				ByteChunk = r.ReadBytes(4);
				Array.Reverse(ByteChunk);
				Length = CCB.Math.ToDec(getBytesAsHexString(ByteChunk, false));
				ByteChunk = new byte[Length];
				ByteChunk = r.ReadBytes(Length);
				Data = getBytesAsCharString(ByteChunk);
			} 
			else if (Type == "boolean") 
			{
				ByteChunk = new byte[1];
				ByteChunk = r.ReadBytes(1);
				Data = (CCB.Math.ToDec(getBytesAsHexString(ByteChunk, false))).ToString();
			}
			else if (Type == "int")
			{
				ByteChunk = new byte[4];
				ByteChunk = r.ReadBytes(4);
				Array.Reverse(ByteChunk);
				Data = (CCB.Math.ToDec(getBytesAsHexString(ByteChunk, false))).ToString();
			}
			else
			{
				ByteChunk = new byte[4];
				ByteChunk = r.ReadBytes(4);
				Array.Reverse(ByteChunk);
				Data = getBytesAsHexString(ByteChunk, false);
			}
			//Join all the stuff we've grabbed into one comma delimited string and stick it in the array, mkay?
			BigArray[CurrPos] = Type + "," + Name + "," + Data;
			CurrPos ++;
			goto SuperAwesomeLoop;
			CloseAndReturn:
			r.Close();
			fs.Close();
			return BigArray;
		}

		public static string getBytesAsHexString(byte [] byBytes, bool bSpaced)
		{
			char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
			int nLen = byBytes.Length * 2;
				if ( bSpaced )
					nLen = byBytes.Length * 3;
			char[] chars = new char[nLen];
			for (int i = 0; i < byBytes.Length; i++) 
			{
				int b = byBytes[i];
				if ( bSpaced )
			{
				chars[i * 3] = hexDigits[b >> 4];
				chars[i * 3 + 1] = hexDigits[b & 0xF];
				chars[i * 3 + 2] = ' ';
			}
			else
			{
			chars[i * 2] = hexDigits[b >> 4];
			chars[i * 2 + 1] = hexDigits[b & 0xF];
			}
		}
		return new string(chars);
		}
		public static string getBytesAsCharString(byte [] byBytes)
		{
			int nLen = byBytes.Length;
			char[] chars = new char[nLen];
			for (int i = 0; i < byBytes.Length; i++) 
			{
				int b = byBytes[i];
				chars[i] = (char)b;
			}
			return new string(chars);
		}
	}

See Also

Personal tools
Namespaces

Variants
Actions
Navigation
game select
Toolbox