PDA

View Full Version : [CoD] File Formats



serthy
3rd January 2014, 21:11
Happy New Year!

Here are all file formats i've done so far.
All formats are little endian and use the same basic types.
These are templates of a hex editor, basically C-style so everyone should be able to understand this.
With them i was able to decompile some simple models and textures, but the code is rly poor atm, if its worth, ill release it in the near future.

Here you go:

[CoD2 iwi]

LittleEndian();

typedef float f32;
typedef double f64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef char i8;
typedef short i16;
typedef int i32;

local int DXTVERSION = 0;



typedef struct
{
u8 start_I;
u8 start_W;
u8 start_i;
u8 version;
u8 DXTformat; //0x0b -> DXT1? | 0x0d -> DXT5?
u8 flag;
u16 width;
u16 height;
u16 u3;
u32 lol[4];

if( start_I != 73 || start_W != 87 || start_i != 105 )
return 0;
else if( version != 0x05 )
return -2;

if( DXTformat == 0x0b )
DXTVERSION = 1;
else if( DXTformat == 0x0d )
DXTVERSION = 5;
else
return -3;

if( u3 != 1 )
return -4;
else if( flag != 1 && flag != 0 )
return -5;
} HEADER;

typedef struct
{
u16 color0;
u16 color1;
u8 index[4];
} COLORBLOCK;

typedef struct
{
COLORBLOCK cb;
} DXT1;

typedef struct
{
u8 alpha[8];
COLORBLOCK cb;
} DXT3;

typedef struct
{
u8 alpha1;
u8 alpha2;
u8 alpha_indices[6];
COLORBLOCK cb;
} DXT5;

local int getSize( local int width , local int height , local int depth )
{
return ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );// * ( DXTVERSION == 0 ? 8 : 16 );
}

local int getMipmapCount( local int width , local int height )
{
local int max = width > height ? width : height;
local int count = 0;
local int i = 0;

for( i = 0 ; max >> i > 0 ; i++ )
{
count++;
}

return count;
}

void readMipmap( local int count )
{
if( DXTVERSION == 1 )
DXT1 blocks[count] <optimize=false>;
else if( DXTVERSION == 3 )
DXT3 blocks[count] <optimize=false>;
else if( DXTVERSION == 5 )
DXT5 blocks[count] <optimize=false>;
}

typedef struct
{
HEADER header;

local int numMipmaps = getMipmapCount( header.width , header.height );

Printf( "numMipmaps: %d\n" , numMipmaps );

while( numMipmaps > 0 )
{
numMipmaps--;

readMipmap( getSize( header.width >> numMipmaps , header.height >> numMipmaps , 4 ) );
}
} IWI;

IWI x;

Okay, the iwi structure is kind of simple, it is DXT compressed and seems to have always all possible mipmaps.
There are still some unknown bits, but i am able to convert alot of textures to dds and tga yet.


[CoD2 xModel]


//SetBackColor( cLtPurple );

//BigEndian();
LittleEndian();

typedef float f32;
typedef double f64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef char i8;
typedef short i16;
typedef int i32;

//SetBackColor( cLtGreen );

//SetBackColor( cNone );


typedef struct
{
u16 start;
} HEADER;

typedef struct
{
f32 x;
f32 y;
f32 z;
} VEC3;

typedef struct
{
SetBackColor( cLtGreen );
string name;
} MATERIALNAME;

typedef struct
{
VEC3 min;
VEC3 max;
} MINMAX;

typedef struct
{
VEC3 min;
VEC3 max;
} BOUNDS;

typedef struct
{
f32 LODdistance;
string LODxmodelsurfs;
} LOD;

typedef struct
{
VEC3 normal;
VEC3 xVec; //
VEC3 yVec; //
VEC3 zVec; //
} COLLFACE;

typedef struct
{
u16 numMaterials;
MATERIALNAME materials[numMaterials] <optimize=false>;
} LODMATERIAL;

typedef struct
{
SetBackColor( cLtGreen );

HEADER header;

u8 groundLit; // 00

BOUNDS bounds;

local int numLOD = 1;

LOD lod[numLOD] <optimize=false>;

f32 ukn;
u32 endLOD_null;

SetBackColor( cLtRed );

u8 u4[3];

//u32 unknown0;
u32 unknown1;
i32 unknown2;
i32 hasColl;

if( hasColl > 0 )
{
u32 numCollData;
COLLFACE c[numCollData];
MINMAX mm1;
u8 unknown3[12];
}

SetBackColor( cLtGreen );

LODMATERIAL LODMat[numLOD] <optimize=false>;

SetBackColor( cLtYellow );

local int count = 1;
MINMAX mm2[count];
} XMODEL;

XMODEL x;


Okay, the xModel format seems to be more troublesome when i did this first look some weeks ago, seems that all collision data (if any, LOD to HIGH) is in this file.



[CoD2 xModelSurf]

LittleEndian();

// modelType 0 -> rigid
// modelType 1 -> animated
// modelType 2 -> viewmodel
// modelType 3 -> playerbody
// modelType 4 -> viewhands

local int fileType = 2;

typedef float f32;
typedef double f64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef char i8;
typedef short i16;
typedef int i32;

typedef struct
{
f32 x;
f32 y;
f32 z;
} VEC3;

typedef struct
{
SetBackColor( cLtGreen );
u8 color_b; //OK!
u8 color_g; //OK!
u8 color_r; //OK!
u8 color_a; //OK!
SetBackColor( cNone );
} COLOR;

typedef struct
{
f32 u;
f32 v;
} UV;

local int modus = 1; //different possibilities to read this chunk

typedef struct
{
if( modus == 0 )
{
u16 boneID;
VEC3 v1;
u16 unkn;
}
else
{
u8 unkn1;
u16 boneID;
VEC3 v1;
u8 unkn2;
}
} CHUNK;

typedef struct
{
VEC3 normal;
COLOR color;
UV uv;

if( fileType == 0 )
{
u8 lol1[4];
f32 unknownFloats[4];
u8 lol2[4];
VEC3 position;
}
else if( fileType == 1 )
{
f32 unknownFloats[6];
u8 chunkCount;
u16 u7;
VEC3 position;

if( chunkCount != 0 )
{
if( modus == 0 )
i8 y;

Printf( "%d\n" , chunkCount );

CHUNK chunk[chunkCount] <optimize=false>;

if( modus != 0 )
u8 lol;
}
}
} VERTEX;

typedef struct
{
u16 start; //14 00
} HEADER;

typedef struct
{
u16 ID[3];
} VERTORDER;

typedef struct
{
u8 u1;
u16 numVerts;
u16 numTris;

if( u1 == 0 && ReadUShort( FTell() ) != 0 )
{
u8 u2 , u3;

Printf( "u2 = %d | u3 = %d\n" , u2 , u3 );
}

u16 u4;
VERTEX v[numVerts] <optimize=false>;
VERTORDER vertOrder[numTris] <optimize=false>;
} MATCHUNK;

typedef struct
{
HEADER header;
u16 numMatChunks;
MATCHUNK mc[numMatChunks] <optimize=false>;
} XMODELSURF;

XMODELSURF x;


I didnt get the mesh weight part to the vertices, and also some coords (Z-coord) is kind of distorted, but cant remember correctly for now.



[CoD2 xModelPart]

LittleEndian();

typedef float f32;
typedef double f64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef char i8;
typedef short i16;
typedef int i32;

local int modelType = 1;
// modelType 0 -> rigid
// modelType 1 -> animated
// modelType 2 -> viewmodel
// modelType 3 -> playerbody
// modelType 4 -> viewhands

typedef struct
{
u16 start;
} HEADER;

typedef struct
{
string name;
} BONENAME;

typedef struct
{
u8 parentBoneID;

if( modelType == 0 || modelType == 1 ) //rigid|animated
{
f32 x; // offset relative to its
f32 y; // parent bone position
f32 z; // they seem distorted/wrong im 50% of all cases
i16 unkn[3];
}
else if( modelType == 3 ) // playerbody
{
u8 unknown[18]; // differs from the same model as modelType = 2
}
else if( modelType == 2 || modelType == 4 ) // viewmodel & viewhands
{
f32 zero[3];
u8 unknown[6];
}
} RELATIVE;

typedef struct
{
HEADER header;
u16 numBonesRelative; // position relative to parent bone
u16 numBonesAbsolute; // position absolute to world/origin
RELATIVE rel[numBonesRelative] <optimize=false>;
BONENAME bones[numBonesRelative + numBonesAbsolute] <optimize=false>;
u8 unknown[numBonesRelative + numBonesAbsolute]; // hit location ID? depends on jointnames
} XMODELPARTS;

XMODELPARTS x;

The xModelPart is known to 99% if i remember correctly. So there shouldnt be any problems here.






I've done them some weeks ago, so i dont remember correctly if theyre the latest ones and when they apply.
Would apprecciate it if some1 want to help me to unreveal the complete formats, also for other ones.
If you improved them, please share them to the remaining community and make them public!

Cheers, Serthy

serthy
4th January 2014, 14:11
but i am able to convert alot of textures to dds and tga yet

Here it is (only iwi to dds for now lol): 585

Ni3ls
4th January 2014, 14:48
I dont get what it do or how to use it :P

serthy
4th January 2014, 15:23
I dont get what it do or how to use it :P
Drag'n'Drop iwi's to the exe.
It's only iwi to dds right now, i try to add the other way around too and some opengl stuff for model displaying maybe animation loading later on.

kung foo man
5th January 2014, 16:17
CoD2 D3DBSP

Tech: LuaJIT + FFI for C-structs

Toujane example
587

Movement:

WASD for Position
Arrow Keys for Camera
Q/E for Camera Down/Up (like in CoD2)



typedef struct
{
float position[3];
float normal[3];
int bla[11];
} aDrawVert;

typedef struct
{
int length;
int offset;
} aLump;

typedef struct
{
char ident[4];
int version;
aLump lumps[100];
} aHeader;

typedef struct
{
short index[3];
} aTriangle; // depricated

typedef struct
{
unsigned short offset;
//unsigned short b;
//unsigned short c;
} aDrawIndex;

/*typedef struct
{
unsigned short a;
unsigned short b;
unsigned short c;
} aDrawIndex;*/


// is empty in mp_analyse1
typedef struct
{
float position[3];
} aCollisionVert;

typedef struct
{
int planeIndex;
int children[2];
int mins[3];
int maxs[3];
} aNode;

typedef struct
{
//int cluster; // ???
//int area; // ???
//int mins[3]; // ???
//int maxs[3]; // ???
//int faceFirst;
//int faceCount;
//int brushFirst;
//int brushCount;
int someA[4];
int firstLeafBrush; // lookup in leafbrushes
int numLeafBrushes;
int someB[3];
} aLeaf;

typedef struct
{
unsigned short bla;
unsigned short foo;
unsigned int vertexFirst;
unsigned short nVertex;

unsigned short nTriangles;
unsigned int triangleFirst;
} aTriangleSoup;


Draw all triangle faces:



function bspdraw()

colors = {
{0, 0, 0},
{0, 0, 1},
{0, 1, 0},
{0, 1, 1},
{1, 0, 0},
{1, 0, 1},
{1, 1, 0},
{1, 1, 1}
}

for i = 0,nTriangleSoups-1 do
soup = triangleSoups + i

gl.glBegin(gl.GL_TRIANGLES)
for nTriangles = 0, soup.nTriangles-1 do
drawIndex = drawIndexes + (soup.triangleFirst + nTriangles)

co = colors[(nTriangles%8) + 1]
gl.glColor3f(co[1], co[2], co[3])

a = vertices + (drawIndex.offset + soup.vertexFirst)
gl.glVertex3fv(a.position)
end
gl.glEnd()
end
end


Serthy, could you post your full solution of how to use your structs? That would help alot :D

kung foo man
8th August 2014, 18:46
Hey Serthy, sorry for late respone, I didn't see your answer in the other thread for some reason. :D

I tried to port your code to C# and it works so far, but I wonder how to "read the pixels" now, like "RGBA color = iwi.getPixel(x,y)". This is what I got so far:



using UnityEngine;
using System.Collections;
using System; // System.Byte etc.

public class HEADER
{
public Byte start_I;
public Byte start_W;
public Byte start_i;
public Byte version;
public Byte DXTformat; //0x0b -> DXT1? | 0x0d -> DXT5?
public Byte flag;
public UInt16 width;
public UInt16 height;
public UInt16 u3;
public UInt32[/*4*/] lol;

public HEADER(System.IO.BinaryReader binaryReader)
{
start_I = binaryReader.ReadByte();
start_W = binaryReader.ReadByte();
start_i = binaryReader.ReadByte();
version = binaryReader.ReadByte();
DXTformat = binaryReader.ReadByte(); //0x0b -> DXT1? | 0x0d -> DXT5?
flag = binaryReader.ReadByte();
width = binaryReader.ReadUInt16();
height = binaryReader.ReadUInt16();
u3 = binaryReader.ReadUInt16();
lol = new UInt32[4] {
binaryReader.ReadUInt32(),
binaryReader.ReadUInt32(),
binaryReader.ReadUInt32(),
binaryReader.ReadUInt32()
};
}

public int getVersion()
{
if( start_I != 73 || start_W != 87 || start_i != 105 )
return 0;
else if( version != 0x05 )
return -2;

int DXTVERSION = 0;

if( DXTformat == 0x0b )
DXTVERSION = 1;
else if( DXTformat == 0x0d )
DXTVERSION = 5;
else
return -3;

if( u3 != 1 )
return -4;
else if( flag != 1 && flag != 0 )
return -5;

return DXTVERSION;
}
}

public class COLORBLOCK
{
public UInt16 color0;
public UInt16 color1;
public Byte[/*4*/] index;

public COLORBLOCK(System.IO.BinaryReader binaryReader_)
{
color0 = binaryReader_.ReadUInt16();
color1 = binaryReader_.ReadUInt16();
index = binaryReader_.ReadBytes(4);
}
}

public class DXT1
{
public COLORBLOCK cb;

public DXT1(System.IO.BinaryReader binaryReader_)
{
cb = new COLORBLOCK(binaryReader_);
}
}

public class DXT3
{
public Byte[/*8*/] alpha;
public COLORBLOCK cb;
}

public class DXT5
{
public Byte alpha1;
public Byte alpha2;
public Byte[/*6*/] alpha_indices;
COLORBLOCK cb;
}

public class IWI
{
HEADER header;
System.IO.BinaryReader binaryReader;

public IWI(System.IO.BinaryReader binaryReader_) {
binaryReader = binaryReader_;

header = new HEADER(binaryReader);
int numMipmaps = getMipmapCount( header.width , header.height );

Debug.Log("numMipmaps: " + numMipmaps + " size="+header.width + "/"+header.height + " version=" + header.getVersion());

while( numMipmaps > 0 )
{
numMipmaps--;
readMipmap( getSize( header.width >> numMipmaps , header.height >> numMipmaps , 4 ) );
}
}

int getSize(int width, int height, int depth)
{
return ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );// * ( DXTVERSION == 0 ? 8 : 16 );
}

int getMipmapCount(int width, int height)
{
int max = width > height ? width : height;
int count = 0;
int i = 0;

for( i = 0 ; max >> i > 0 ; i++ )
{
count++;
}

return count;
}

DXT1[] dxt1;

void readMipmap(int count)
{
int DXTVERSION = header.getVersion();

dxt1 = new DXT1[count];
for (int i = 0; i < count; i++)
dxt1[i] = new DXT1(binaryReader);

Debug.Log("num of dxt1=" + dxt1.Length);

//if( DXTVERSION == 1 )
// DXT1 blocks[count];
//else if( DXTVERSION == 3 )
// DXT3 blocks[count];
//else if( DXTVERSION == 5 )
// DXT5 blocks[count];
}
}

public class iwi2dds : MonoBehaviour {
KILLTUBE.GreatExplorer explorer = new KILLTUBE.GreatExplorer();
IWI iwi;

void Start () {
explorer.AddArchive(@"G:\GAMES\CoD2 1.3\main\iw_08.iwd");
System.IO.BinaryReader binaryReader = explorer.GetGreatFile("images/155_cannon.iwi");
iwi = new IWI(binaryReader);
}
}




Output:

728

(I hope thats correct so far xD)

serthy
8th August 2014, 22:55
I tried to port your code to C# and it works so far, but I wonder how to "read the pixels" now, like "RGBA color = iwi.getPixel(x,y)".

DXT is a texture compression format, you have to decompress it to extract RGBA values from it.
MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/bb694531(v=vs.85).aspx) has one of the best explanations of DXT texture compression and here is a good C++ library (https://code.google.com/p/libsquish/) for compression/decompression of the DXT format.
However, gpu's can handle DXT textures the best and almost all editing programs can handle DDS images (DXT compression), so why bother converting it to RGBA? :D

kung foo man
9th August 2014, 17:25
Unity3D needs at some point the raw RGBA data ^^

I found a good DevIL C# wrapper and it works perfect so far :)



public Texture2D LoadImageFromData(byte[] imageData)
{
const int cNumImages = 1;
uint[] handles = new uint[cNumImages];
Texture2D resTexture = null;

/* First we initialize the library. */
/*Do not forget that... */
DevILLoader.ilInit();

/* We want all images to be loaded in a consistent manner */
DevILLoader.ilEnable(DevILConstants.IL_ORIGIN_SET) ;

/* In the next section, we load one image */
DevILLoader.ilGenImages(cNumImages, handles);
DevILLoader.ilBindImage(handles[0]);

//
//uint res = PluginLoader.ilLoadL(DevILConstants.IL_PNG, imageData, (uint)imageData.Length);
bool res = DevILLoader.ilLoadL(DevILConstants.IL_TYPE_UNKNOWN , imageData, (uint)imageData.Length);
if (!res)
{
Debug.LogWarning("Error! Cannot load image from data");
return resTexture;
}

/* Let's spy on it a little bit */
int width = DevILLoader.ilGetInteger(DevILConstants.IL_IMAGE_W IDTH); // getting image width
int height = DevILLoader.ilGetInteger(DevILConstants.IL_IMAGE_H EIGHT); // and height
Debug.Log("Base image resolution: w = " + width + "; h = " + height);

// create result texture
resTexture = new Texture2D(width, height, TextureFormat.RGBA32, true);

//
Color32[] texColors = GetColorDataFromCurrentImage();

// set first mip map
resTexture.SetPixels32(texColors, 0);

// now, try to set another levels of bitmap
{
uint currMipMapLevel = 1;
const uint cMaxMipMapLevel = 15;
while (currMipMapLevel < cMaxMipMapLevel)
{
res = DevILLoader.ilActiveMipmap(currMipMapLevel);
Debug.Log("res = " + res + " currMipMapLevel = " + currMipMapLevel);
if (!res)
{
break;
}

//
texColors = GetColorDataFromCurrentImage();

Debug.Log("currMipMapLevel = " + currMipMapLevel);

// set next mip map
resTexture.SetPixels32(texColors, (int)currMipMapLevel);

++currMipMapLevel;

// restore base image
DevILLoader.ilBindImage(handles[0]);
}
}

resTexture.Apply(true);
//resTexture.Apply(false); // show this to ven!

/* Finally, clean the mess! */
DevILLoader.ilDeleteImages(cNumImages, handles);

return resTexture;
}

public Color32[] GetColorDataFromCurrentImage()
{
int width = DevILLoader.ilGetInteger(DevILConstants.IL_IMAGE_W IDTH); // getting image width
int height = DevILLoader.ilGetInteger(DevILConstants.IL_IMAGE_H EIGHT); // and height

Debug.Log("Image resolution: w = " + width + "; h = " + height);

/* how much memory will we need? */
int memoryNeeded = width * height * 4;

/* We multiply by 4 here because we want 4 components per pixel */
byte[] imageColorData = new byte[memoryNeeded];

/* finally get the image data */
DevILLoader.ilCopyPixels(0, 0, 0, (uint)width, (uint)height, 1, DevILConstants.IL_RGBA, DevILConstants.IL_UNSIGNED_BYTE, imageColorData);

if (imageColorData.Length <= 0)
{
return null;
}

// create colors from color data
Color32[] texColors = new Color32[imageColorData.Length / 4];

for (int i = 0, j = 0; i < imageColorData.Length; i += 4, ++j)
{
texColors[j].r = imageColorData[i];
texColors[j].g = imageColorData[i + 1];
texColors[j].b = imageColorData[i + 2];
texColors[j].a = imageColorData[i + 3];
}

return texColors;
}


IWI is supported by default. :D

kung foo man
7th January 2017, 22:47
Oh shiet, I never knew what kind of pseudo C code that was, but today Serthy told me about the 010 Hex Editor. This is how the .iwi template code looks on a .iwi file:

1246

It's extremly powerful to actually understand whats going on in binary data. You can execute C like code inside structs and even color the structs, as seen in image (header is blue, colorblocks are red).

I wish I had known this tool before :')

Edit:

IWI implementation for C++: https://github.com/DentonW/DevIL/blob/master/DevIL/src-IL/src/il_iwi.cpp
IWI format description by CptAsgard: https://github.com/CptAsgard/CoD2Unity/blob/master/Assets/cod2iwifiles.txt
IWI implementation in C#: https://github.com/CptAsgard/CoD2Unity/blob/master/Assets/Scripts/IWILoader.cs

I experimented a bit with 010 Editor, and this is what I added for IWI format (Enums and added the filesize/offsets in header):



LittleEndian();

typedef float f32;
typedef double f64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef char i8;
typedef short i16;
typedef int i32;

local int DXTVERSION = 0;

enum <u8> Usage {
Color = 0x00,
Default = 0x01, // Fallback texture for engine
Skybox = 0x05
};

enum <u8> ImageFormat {
ARGB32 = 0x01,
RGB24 = 0x02,
GA16 = 0x03,
A8 = 0x04,
DXT1 = 0x0B,
DXT3 = 0x0C,
DXT5 = 0x0D
};

string comment_dxtformat(u8 dxtformat) {
//switch (dxtformat) {
// case 0x01: return "one";
// case 0x0d: return "DXT5";
//}
string buffer;
SPrintf(buffer, "whatever is %d", dxtformat);
return buffer;
}
//

typedef struct
{
SetBackColor( cLtBlue );
u8 start_I;
u8 start_W;
u8 start_i;
u8 version;
ImageFormat DXTformat <comment=comment_dxtformat>;
Usage flag;
u16 width;
u16 height;
u16 u3;
u32 filesize;
u32 offset_texture;
u32 offset_mipmap1;
u32 offset_mipmap2;

if( start_I != 73 || start_W != 87 || start_i != 105 )
return 0;
else if( version != 0x05 )
return -2;

if( DXTformat == 0x0b ) {
DXTVERSION = 1;

}
else if( DXTformat == 0x0d )
DXTVERSION = 5;
else
return -3;

if( u3 != 1 )
return -4;
else if( flag != 1 && flag != 0 )
return -5;

} HEADER;

typedef struct
{
SetBackColor( cLtRed );
u16 color0;
u16 color1;
u8 index[4];
} COLORBLOCK;

typedef struct
{
COLORBLOCK cb;
} DXT1;

typedef struct
{
u8 alpha[8];
COLORBLOCK cb;
} DXT3;



typedef struct
{
u8 alpha1;
u8 alpha2;
u8 alpha_indices[6];
COLORBLOCK cb;
} DXT5;

Printf("sizeof dxt1=%d dxt3=%d dxt5=%d\n",
sizeof( DXT1 ),
sizeof( DXT3 ),
sizeof( DXT5 )
);

local int getSize( local int width , local int height , local int depth )
{
return ( ( width + 3 ) / 4 ) * ( ( height + 3 ) / 4 );// * ( DXTVERSION == 0 ? 8 : 16 );
}

local int getMipmapCount( local int width , local int height )
{
local int max = width > height ? width : height;
local int count = 0;
local int i = 0;

for( i = 0 ; max >> i > 0 ; i++ )
{
count++;
}

return count;
}

void readMipmap( local int count )
{
if( DXTVERSION == 1 )
DXT1 blocks[count] <optimize=false>;
else if( DXTVERSION == 3 )
DXT3 blocks[count] <optimize=false>;
else if( DXTVERSION == 5 )
DXT5 blocks[count] <optimize=false>;
}

typedef struct
{
HEADER header <open=true>;

local int numMipmaps = getMipmapCount( header.width , header.height );

Printf( "numMipmaps: %d\n" , numMipmaps );

while( numMipmaps > 0 )
{
numMipmaps--;

// remove comment for seeing the blocks
// kinda wrong tho, since it doesnt care about offset_texture/offset_mipmap1/offset_mipmap2?
//readMipmap( getSize( header.width >> numMipmaps , header.height >> numMipmaps , 4 ) );
}
} IWI;

IWI x <open=true>;




Looks like:

1247