The SKD and SKC format specification

Information about the engine technology is revealed and discussed here
Post Reply
Rookie One.pl
Site Admin
Posts: 2752
Joined: Fri Jan 31, 2003 7:49 pm
Location: Nowa Wies Tworoska, Poland
Contact:

The SKD and SKC format specification

Post by Rookie One.pl »

As reverse-engineered by myself. Just in case anyone finds it useful.

What follows is a technical description of the SKD version 5 and the SKC version 13 formats (the ones used by the vanilla Allied Assault). The programming language used is C. It probably won't tell you much unless you have some programming knowledge. ;)

The files are little-endian, binary. The data types used are signed 32-bit integers (int), 32-bit IEEE floats (float), 3D-space vectors (typedef float vec3_t[3]), signed 8-bit integers (char), unsigned 8-bit integers (typedef unsigned char byte) and fixed-length strings/byte arrays (char[x]).

Note that this is still a work in progress and not everything has yet been reverse-engineered (forward kinematics in SKC, face morphs in SKD, also something which looks like some kind of hitbox data, but so far I wasn't able to produce anything meaningful out of it). All struct elements named dummy* I obviously do not know the meaning of yet. The comments mostly come from the sources of Quake III and skl2md4, as I was using them as a reference.

The SKD 5 format - revision 1 (02.02.2008)
Let's take a look at the structs:

Code: Select all

/*
==============================================================================

SKD file format

==============================================================================
*/

#define SKD_IDENT			(('D'<<24)+('M'<<16)+('K'<<8)+'S')
#define SKD_VERSION			5
#define	SKD_MAX_BONES		128
#define SKD_SURFACE_IDENT	((' '<<24)+('L'<<16)+('K'<<8)+'S')

typedef struct {
	int			dummy;
} skdHitbox_t;

typedef struct {
	int			boneIndex;		// these are indexes into the boneReferences,
	float		boneWeight;		// not the global per-frame bone list
	vec3_t		offset;
} skdWeight_t;

typedef struct {
	int		dummy[4];
} skdMorph_t;

typedef struct {
	vec3_t		normal;
	vec2_t		texCoords;
	int			numWeights;
	int			numMorphs;
	/*skdWeight_t	weights[1];		// variable sized
	skdMorph_t	morphs[1];*/
} skdVertex_t;

typedef struct {
	int			indices[3];
} skdTriangle_t;

typedef struct {
	int			ident;

	char		name[MAX_QPATH];	// polyset name

	int			numTriangles;
	int			numVerts;
	int			dummy1;
	int			ofsTriangles;
	int			ofsVerts;
	int			dummy2;

	int			ofsEnd;				// next surface follows

	int			dummy3;
} skdSurface_t;

typedef enum {
	JT_24BYTES1,
	JT_POSROT_SKC,
	JT_40BYTES1,
	JT_24BYTES2,
	JT_24BYTES3,
	JT_40BYTES2,
	JT_28BYTES
} skdJointType_t;

typedef struct {
	char		name[32];
	char		parent[32];
	int			jointType;
	int			ofsValues;
	int			ofsChannels;
	int			ofsRefs;
	int			ofsEnd;
} skdBone_t;

typedef struct {
	int			ident;
	int			version;

	char		name[MAX_QPATH];	// model name

	int			numSurfaces;
	int			numBones;
	int			ofsBones;		// char	name[ MAX_QPATH ]
	int			ofsSurfaces;			// skdFrame_t[numFrames]

	// each level of detail has completely separate sets of surfaces
	int			numLODs;
	int			ofsLODs;

	int			ofsEnd;				// end of file
	int			lodIndex[8];
	int			dummy1;
	int			dummy2;
	int			numMorphTargets;
	int			ofsMorphTargets;
} skdHeader_t;
The structs appear in the file in the following order (offsets are relative to the position of the given struct in the file):
  • skdHeader_t: at the beginning of the file,
  • skdBone_t: at offset skdHeader_t->ofsBones, skdHeader_t->numBones times; the next skdBone_t follows after skdBone_t->ofsEnd + skdBone_t->ofsValues,
  • either 24, 12, 40 or 28 bytes of unknown data depending on skdBone_t->jointType (see skdJointType_t): at offset skdBone_t->ofsEnd + skdBone_t->ofsValues,
  • skdSurface_t: at offset skdHeader_t->ofsSurfaces, skdHeader_t->numSurfaces times; the next skdSurface_t follows after skdSurface_t->ofsEnd,
  • skdTriangle_t: at offset skdSurface_t->ofsTriangles, skdSurface_t->numTriangles times,
  • skdVertex_t: at offset skdSurface_t->ofsVerts, skdSurface_t->numVerts times; the struct is variable-sized depending on the bone weight and (what looks like) morph data,
  • skdWeight_t: immediately after a skdVertex_t struct, skdVertex_t->numWeights times,
  • skdMorphs_t: immediately after the last skdWeight_t struct, sdkVertex_t->numMorphs times.
I have no idea how the LOD system works just yet.

The SKC 13 format - revision 1 (02.02.2008)

Code: Select all

/*
==============================================================================

SKC file format

==============================================================================
*/

#define SKC_IDENT			(('N'<<24)+('A'<<16)+('K'<<8)+'S')
#define SKC_VERSION			13
#define SKC_MAX_CHANNEL_CHARS	32

typedef struct {
	float		floatVal[4];
} skcBone_t;

typedef struct {
	vec3_t		bounds[2];			// bounds of all surfaces of all LOD's for this frame
	float		radius;				// dist from localOrigin to corner
	vec3_t		delta;
	int		dummy; // this actually looks like a float
	int			numChannels;
} skcFrame_t;

typedef struct {
	int			ident;
	int			version;

	int			type;

	int			ofsEnd;

	float		frameTime;

	int			dummy1;
	int			dummy2;
	int			dummy3;
	int			dummy4;
	int			numChannels;
	int			ofsChannels;
	int			numFrames;
} skcHeader_t;
Now this is a bit complicated. Each frame consists of the skcFrame_t struct and the animation data, organized into what's called channels. A channel can hold either position or rotation information and consits of 4 floats (16 bytes). Every channel has a name which is comprised of the name of the bone whose movement it describes and the type of the channel: pos (position; the 4 floats are x, y, z and w, w being unused), rot (rotation stored in the form of a 4-float quaternion) or rotFK (rotation forward kinematics; no idea how this works here just yet, looking into it). What's more, all data is relative to the parent bone. E.g. in order to retrieve the absolute position of the Bip01 Pelvis bone, you need to add its pos channel value to its parent's one (Bip01 Footsteps), and then in turn to the position of the parent of that bone (Bip01).

The data appears in the following order:
  • skcHeader_t: beginning of the file,
  • skcFrame_t: first one immediately after skcHeader_t; skcHeader->numFrames times; the next frame starts at offset skcFrame_t->ofsEnd from the previous one,
  • skcBone_t: immediately after skcFrame_t; skcHeader_t->numChannels times,
  • a list of channel names; fixed-length strings of SKC_MAX_CHANNEL_CHARS; at offset skcHeader_t->ofsChannels, skcHeader_t->numChannels times.
That's about it for now. I'm going to work on it a bit more and post any updates.

Here's hoping someone actually finds this information useful.
Last edited by Rookie One.pl on Wed Feb 06, 2008 8:32 pm, edited 4 times in total.
Admin
Image
Image
Honour guide me.

here's my stuff - inequation.org | here's where I work - thefarm51.com
PKM
General
Posts: 1888
Joined: Tue Aug 09, 2005 4:43 pm
Location: Philadelpia but stuck in San Antonio TX (hell)

Re: The SKD and SKC format specification

Post by PKM »

Rookie One.pl wrote: Here's hoping someone actually finds this information useful.
i did, i couldn't get too sleep all night until this . thanks .

seriously though, freakin' amazing how you coders can figure this all out .
i'm not f****** angry, i'm from philadelphia .
Image
User avatar
bdbodger
Moderator
Posts: 2596
Joined: Tue Feb 25, 2003 7:34 am
Location: canada
Contact:

Post by bdbodger »

That cool keep going .
Image
Rookie One.pl
Site Admin
Posts: 2752
Joined: Fri Jan 31, 2003 7:49 pm
Location: Nowa Wies Tworoska, Poland
Contact:

Post by Rookie One.pl »

Thanks guys. ;) Will do my best.

I asked Prometheus, the author of LightRay 3D, if he could make the format specs public, since he's the only person known to have received the official specs from EA. Let's hope he shares it. If he does, OpenMoHAA development will gain substantial momentum. :)
Admin
Image
Image
Honour guide me.

here's my stuff - inequation.org | here's where I work - thefarm51.com
chacham15
Private
Posts: 7
Joined: Sun Apr 20, 2008 8:00 pm

Post by chacham15 »

If you look at the code that you have in qfiles.h, Thilo Schulz added some code to allow models from Ravensoft to be added. But if you look through the currently commented out code, it contains the following definitions:

typedef struct {
int ident;
int version;

int type;

int ofsEnd;

float frameTime;

int i3;
int i4;
int i5;
int i6;
int numChannels;
int ofsChannels;
int numFrames;
} skcHeader_t;

typedef struct {
int ident;
int version;

char name[MAX_QPATH]; // model name

int numSurfaces;
int numBones;
int ofsBones; // char name[ MAX_QPATH ]
int ofsSurfaces; // skdFrame_t[numFrames]

// each level of detail has completely separate sets of surfaces
int numLODs;
int ofsLODs;

int ofsEnd; // end of file
int lodIndex[8];
int numBoxes;
int ofsBoxes;
int numMorphTargets;
int ofsMorphTargets;
} skdHeader_t;

So, now theres no need for dummy variables.

-chacham15
m0d hippY
Warrant Officer
Posts: 132
Joined: Tue Apr 27, 2004 3:58 am

Post by m0d hippY »

excellent post. Thanks for the info, I'm sure this will come in handy. Any more information that could be of some use?
Image
Image
MOHAATiger
Lance Corporal
Posts: 24
Joined: Sat May 24, 2008 4:32 pm

help

Post by MOHAATiger »

Can you give me some help in my post ? It is located here. It is related to this stuff, I believe ...


/forum/viewtopic.php?t=13190
Rookie One.pl
Site Admin
Posts: 2752
Joined: Fri Jan 31, 2003 7:49 pm
Location: Nowa Wies Tworoska, Poland
Contact:

Post by Rookie One.pl »

No it is not related.
Admin
Image
Image
Honour guide me.

here's my stuff - inequation.org | here's where I work - thefarm51.com
Post Reply