P3D File Format - ODOLV7: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Text replacement - "{{HashLink" to "{{Link")
 
(34 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{unsupported-doc}}
{{Feature|UnsupportedDoc}}
{{TOC|side}}
= General =


===Legend===
byte:    8 bits unsigned
char:    8 bit ascii character
char[]:  fixed length string
asciiz:  null terminated char string
asciiz... concatetaned asciiz strings
asciiz[]: fixed length and null terminated anyway
ulong:    unsigned integer 32bit. 4 bytes
ushort:  unsigned integer 16bit 2 bytes
short:    signed integer 16bit 2 bytes
float:    4 bytes


===Intro===
== Legend ==


====CompressedStruct====
see [[Generic FileFormat Data Types]]


ODOL7 uses (potentially) compressed arrays.


This obviously slows the engine down during loading. At the time of CWC development, game file sizes were important.
== CompressedStructures ==


(potentially) compressed arrays are contained in a CompressedStruct
(potentially) compressed arrays are endemic to most blocks contained in a p3d.
 
CompressedStructs are endemic to most blocks contained in the p3d.
 
CompressedStruct
{
  ulong  Count;
  <type> Array[Count];
};


<syntaxhighlight lang="cpp">
CompressedStruct
{
ulong Count;
<type> Array[Count];
};
</syntaxhighlight>


if Count * sizeof(<type>) exceeds 1023 bytes the array is compressed. The '''resulting''' array will be expanded using lzh compression exactly as found in pbo's (for instance)
if Count * sizeof(<type>) exceeds 1023 bytes the array is compressed. The '''resulting''' array will be expanded using lzh compression exactly as found in pbo's (for instance)


After de-compression, the Count remains the same because it is a count of the arraytype.
After decompression, the Count remains the same because it is a count of the arraytype.


For uncompressed arrays (byte count < 1024) the Count and data are treated 'as is'.
For uncompressed arrays (byte count < 1024) the Count and data are treated 'as is'.
Line 40: Line 29:


Thus for various Array <types>
Thus for various Array <types>
*ulong Array:    > 255  // 1024 /  sizeof(ulong)
* ulong Array:    > 255  // 1024 /  sizeof(ulong)
*float thing[2]: > 127  // 1024 / 2*sizeof(float)
* float thing[2]: > 127  // 1024 / 2*sizeof(float)
*SomeStructure:  >      // count * sizeof (SomeStructure) > 1023
* SomeStructure:  >      // count * sizeof (SomeStructure) > 1023
 
 
Note that potentially compressed arrays in these structures only have an known output length.
the decompressor therefore must work on infinite input length.
see example decompression at end of document


===Odol7Stuct===
ODOLV7
{
  StandardP3DHeader Header;
  LodStruct Lods[LodCount];
  float    Resolution[LodCount];
  ulong    Unknown;        // typically 02 01 00 00
  float    Pair[2];        // typically 48 74 07 3F  : 48 74 07 3F . pair is often the same
  ulong    Unknown[3]      // typically 00 00 00 00 00 00 00 00 00 00 B0 0D
  bytes    Unknown[120];    // a mixture of floats and index values
  bytes    UnknownFlags[6]; // typically 01 00 00 01 00 16
  ulong    Count;
  float    Array[Count];    // potentially compressed
  float    UnknownFloats[4];// almost consistently 00 00 00 00 : F9 02 15 50 : 00 00 48 43 : 0A D7 A3 3B
  ulong    UnknownLongs[3]; // generally FF FF FF FF FF FF FF FF FF FF FF FF
};


====StandardP3DHeader====
Note that potentially compressed arrays in these structures only have an known output length. the decompressor therefore must work on infinite input length.
StandardP3DHeader
{
  char      Signature[4];      ''//"ODOL"''
  ulong    Version;          ''// 7''
  ulong    LodCount;          ''// at least one''
}


===LodStruct===
LodStruct
{
  '''structs''' VerticesStructs[...];
  '''float'''  Unknown[12];          ''// contains some max/min vertices positions''
  '''struct'''  TexturesStruct[...];
 
  '''structs''' TableStructs[...];
  '''struct'''  FacesStruct[...];
  '''struct'''  UnknownStruct[...];
  ulong          nNamedSelections;
  NamedSelection NamedSelections[nNamedSelections];
  ulong          nNamedProperties;
  NamedProperty  NamedProperties[nNamedProperties];
  '''struct'''  UnknownStruct2[...];
  '''ulong'''  Unknown[3];
  '''struct'''  ProxiStruct[...];
};


===VerticesStructs===
= Odol7Stuct =
  VerticesStructs
  {
    CompressedStruct Attribs
    {
    '''ulong''' Count;              ''// if > 255 then array is compressed
    '''ulong''' Attribs[Count];   
    }
    CompressedStruct UVset
    {
    '''ulong''' Count;              ''// if > 127 then array is compressed
    '''float''' UVset[Count];     
    }
    struct Position
    {
    '''ulong''' Count;             
    '''float''' Position[Count][3]; ''// XZY
    }
    struct Normals
    {
    '''ulong''' Count;           
    '''float''' Normals[Count][3];  ''// XZY
    }
  }


*Count is the same value for all four tables.
See [[P3D Model Info]].
*The Position and Normals tables appear to be always uncompressed raw data.
<syntaxhighlight lang="cpp">
ODOLV7
{
StandardP3DHeader Header;
LodStruct Lods[Header.LodCount];
ModelInfo ModelInfo;
};
</syntaxhighlight>


===TexturesStruct===
  struct Textures
  {
  '''ulong'''  Count;
  '''asciiz''' Textures[...];          ''// "data/1.paa\0data/2.paa\0"...
  }


Count corresponds to the number of concatenated strings. It is required, since, architecturally at least, one of more of the asciiz strings could be null.
== StandardP3DHeader ==


===TableStruct===
<syntaxhighlight lang="cpp">
struct TableStruct
StandardP3DHeader
{
{
  CompressedStruct
char Signature[4]; //"ODOL" (vs MLOD eg)
  {
ulong Version; // 7
  '''ulong'''  Count;           ''// if > 511 etc'' 
ulong LodCount; // at least one
  '''ushort''' MlodIndex[Count];
}
  }
</syntaxhighlight>
  CompressedStruct
  {
  '''ulong'''  Count;           ''// this Count is same value as any Vertices.Count''
  '''ushort''' OdolIndex[Count];''//  
  }
}


Tables are used to join vertices. Each face has got 3 or 4 vertices that are unique for each face Eg. Every vertex is owned only by 1 face.
'''MLOD'''vertexindex = MlodIndex[ OdolIndex['''ODOL'''vertexindex] ];''


===FacesStruct===
== LodStruct ==
struct Faces
{
  '''ulong'''  FacesCount;
  '''ulong'''  unknown;
  '''struct''' Face
  {
  '''ulong'''  Attribs;
  '''short'''  TextureIndex;
  '''byte'''  Count;  // always 3 or 4
  '''ushort''' VerticesIndex[Count];
  }[FacesCount];
};


The TextureIndex is a zero based array. If set to -1, there are no textures for this face.
See:
* {{Link|#VertexTable}}
* {{Link|#Textures}}
* [[P3D Lod Edges]]
* [[P3D Lod Faces]]
* [[P3D Lod Sections]]
* [[P3D Named Selections]]
* {{Link|#NamedProperty}}
* [[P3D Lod Frames]]
* [[P3D Lod Proxies]]


===UnknownStruct===
  UnknownStruct
  {
  '''ulong'''  Count;
  '''byte'''  Unknown[Count][18];
  }


===NamedSelection===
<syntaxhighlight lang="cpp">
NamedSelection
LodStruct
{
{
  '''asciiz''' name;
VertexTable VertexTable;
  CompressedStruct Vertices
float UnknownFloat1;
  {
float UnknownFloat2;
  '''ulong'''  Count;                 ''// if > 511 then array is compressed
XYZTriplet MinPos;
  '''ushort''' Vertices[Count];
XYZTriplet MaxPos;
  }
XYZTriplet AutoCenterPos;
  CompressedStruct UnknownUshort
float UnknownFloat3;
  {
Textures Textures;
  '''ulong'''  Count;                 ''// if > 511 then array is compressed
LodEdges LodEdges;
  '''ushort'''  Unknown[Count];
ulong NoOfFaces;
  }
ulong OffsetToLodSections;
  CompressedStruct UnknownUlong
LodFace LodFaces[NoOfFaces]; // ie polygons
  {
ulong nSections;
  '''ulong''' Count;                   ''// if > 255 then array is compressed
LODSection LODSections[nSections];
  '''ulong''' Unknown[Count];
ulong nNamedSelections;
  }
NamedSelection NamedSelections[nNamedSelections];
  '''byte''' IsSectional;  
ulong nTokens;
  CompressedStruct UnknownUlong2
NamedProperty NamedProperties[nTokens];
  {
ulong nFrames;
  '''ulong''' Count;                   ''// if etc
Frame Frames[nFrames];
  '''ulong''' Unknown[Count];
ulong IconColor;
  }
ulong SelectedColor;
  CompressedStruct Faces
ulong Unknown;
  {
ulong nProxies;
  '''ulong''' Count;                   ''// if etc
LodProxy LodProxies[nProxies];
  '''ushort''' Faces[Count];
};
  }
</syntaxhighlight>
  CompressedStruct TextureWeights      // probably
  {
  '''ulong''' Count;                   ''// if etc
  '''byte''' TextureWeights[Count];
  }
};
===NamedProperty===


  NamedProperty
=== VertexTable ===
  {
    '''asciiz''' Name;  ''// "noshadow\0"
    '''asciiz''' Value; ''//"1\0"'
  };


===UnknownStruct2===
See [[P3D Point and Face Flags]].
UnknownStruct2
<syntaxhighlight lang="cpp">
{
struct
    ulong Count;
{  
    struct Unknown
ulong Count;
    {
ulong PointFlags[Count]; // compressed
      ulong uvalue;// unknown value
ulong Count;
      ulong Count;
UVPair UV1[Count]; // compressed
      float array[Count][3];
ulong Count;
    }[Count];
XYZTriplet Points[Count]; // UNcompressed
};
ulong Count;
XYZTriplet Normals[Count]; // UNcompressed
}
</syntaxhighlight>


===ProxiStruct===
*Count is the same value for all four tables.
ProxiStruct
{
  '''ulong'''  Count;
  '''struct''' Proxi
  {
  '''asciiz''' Name;
  '''float''' rotationMatrix[9];
  '''float''' translation[3];
  '''ulong''' Index[2];
  }[Count];;
}


===LZ in ODOL===
=== Textures ===


''Lempel-Ziv compression
<syntaxhighlight lang="cpp">
struct
{
ulong Count;
asciiz Textures[...]; // "data/1.paa\0data/2.paa\0"...
}
</syntaxhighlight>


Note1.
Count corresponds to the number of concatenated strings. It is required, since, architecturally at least, one of more of the asciiz strings could be null.


Regardless of method, 4 extra bytes representing the checksum exist at end of the data count.
=== NamedProperty ===


Note2.
<syntaxhighlight lang="cpp">
The compression code is identical to that employed by pbo packed structures. However, unlike pbo's, the size  of the compressed data is unknown, only it's ultimate length. The code below fudges it.
struct
{
Asciiz Property; // e.g "noshadow" = "1"
Asciiz Value;
}
</syntaxhighlight>




==== pascal code====
= Related Page(s) =


'''function''' LZBlockRead(var F:file; var outdata:array of byte;szout:integer):byte;
== LZ in ODOL ==
'''var'''
k, r, pr, pi,po,i,j:integer;
flags:word;
buf:'''array'''[0..$100e] '''of''' byte;
c:byte;
crc:integer;
'''begin'''
po:=0;
pi:=0;
flags:=0;
r:=0;
'''for''' k := 0 '''to''' $100F-1 '''do''' buf[k] := $20;
        '''while''' (po < szout) '''do'''
        '''begin'''
            flags:= flags '''shr''' 1;
            '''if''' ((flags '''and''' $100)= 0) '''then'''
                '''begin'''
                  BlockRead(F,c,1);  ''// direct reading from file''
                  inc(pi);
                  flags := c '''or''' $ff00;
                '''end''';
            '''if''' (flags and 1)=1 '''then'''
                '''begin'''
                  '''if''' (po >= szout)'''then''' '''break''';
                  BlockRead(F,c,1);  ''// direct reading from file''
                  inc(pi);
                  outdata[po] := c;
                  inc(po);
                  buf[r] := c;
                  inc(r);
                  r :=r and $fff;
                '''end'''
            '''else'''
                '''begin'''
                  i:=0;
                  BlockRead(F,i,1);  ''// direct reading from file''
                  inc(pi);
                  j:=0;
                  BlockRead(F,j,1);'' // direct reading from file''
                  inc(pi);
                  i :=i or ((j '''and''' $f0) shl 4);
                  j := (j '''and''' $0f) + 2;
                  pr := r;
                  '''for''' k := 0 '''to''' j '''do'''
                    '''begin'''
                      c := buf[(pr - i + k''') and''' $fff];
                      '''if''' (po >= szout) '''then break''';
                      outdata[po]:= c;
                      inc(po);
                      buf[r]:= c;
                      inc(r);
                      r :=r '''and''' $fff;
                    '''end''';
              '''end''';
        '''end''';
      BlockRead(F,crc,4);  ''// 4 byte checksum.''
      result:= pi;
'''end''';


====C code====
see [[Compressed LZSS File Format]]


'''int''' Decode('''unsigned char''' *in,'''unsigned char''' *out,'''int''' szin,'''int''' szout)
[[BIS File Formats#3D Model File Formats|Model File Formats]]
{
        szin = szin > 0? szin: 0x7fffffff;
        '''int'''  i, j, k, r = 0, pr, pi = 0,po = 0;
        '''unsigned int'''  flags = 0;
        '''unsigned char''' buf[0x100F], c;
        '''for''' (i = 0; i < 0x100F; buf[i] = 0x20, i++);
        '''while''' (pi < szin && po < szout)
        {
                '''if''' (((flags >>= 1) & 256) == 0)
                {
                        '''if'''(pi >= szin)'''break''';
                        c = in[pi++];
                        flags = c | 0xff00;
                }
                '''if''' (flags & 1)
                {
                        '''if'''(pi >= szin || po >= szout)'''break''';
                        c = in[pi++];
                        out[po++] = c;
                        buf[r++] = c;
                        r &= 0xfff;
                } '''else'''
                {
                        '''if'''(pi + 1 >= szin)'''break''';
                        i = in[pi++];
                        j = in[pi++];
                        i |= (j & 0xf0) << 4;
                        j  = (j & 0x0f) + 2;
                        pr = r;
                        '''for''' (k = 0; k <= j; k++)
                        {
                                c = buf[(pr - i + k) & 0xfff];
                                '''if'''(po >= szout)'''break''';
                                out[po++] = c;
                                buf[r++] = c;
                                r &= 0xfff;
                        }
                }
        }
        '''return''' pi;// next 4 bytes = checksum
}


=Related Page(s)=


[[BIS_File_Formats#3D_Model_File_Formats|Model File Formats]]
[[Category:BIS_File_Formats]]
[[Category:BIS_File_Formats]]

Latest revision as of 17:43, 4 January 2023

bi symbol white.png
Disclaimer: This page describes internal undocumented structures of Bohemia Interactive software.

This page contains unofficial information.

Some usage of this information may constitute a violation of the rights of Bohemia Interactive and is in no way endorsed or recommended by Bohemia Interactive.
Bohemia Interactive is not willing to tolerate use of such tools if it contravenes any general licenses granted to end users of this community wiki or BI products.

General

Legend

see Generic FileFormat Data Types


CompressedStructures

(potentially) compressed arrays are endemic to most blocks contained in a p3d.

CompressedStruct
{
	ulong	Count;
	<type>	Array[Count];
};

if Count * sizeof(<type>) exceeds 1023 bytes the array is compressed. The resulting array will be expanded using lzh compression exactly as found in pbo's (for instance)

After decompression, the Count remains the same because it is a count of the arraytype.

For uncompressed arrays (byte count < 1024) the Count and data are treated 'as is'.


Thus for various Array <types>

  • ulong Array: > 255 // 1024 / sizeof(ulong)
  • float thing[2]: > 127 // 1024 / 2*sizeof(float)
  • SomeStructure: > // count * sizeof (SomeStructure) > 1023


Note that potentially compressed arrays in these structures only have an known output length. the decompressor therefore must work on infinite input length.


Odol7Stuct

See P3D Model Info.

ODOLV7
{
	StandardP3DHeader	Header;
	LodStruct			Lods[Header.LodCount];
	ModelInfo			ModelInfo;
};


StandardP3DHeader

StandardP3DHeader
{
	char	Signature[4];	//"ODOL" (vs MLOD eg)
	ulong	Version;		// 7
	ulong	LodCount;		// at least one
}


LodStruct

See:


LodStruct
{
	VertexTable		VertexTable;
	float			UnknownFloat1;
	float			UnknownFloat2;
	XYZTriplet		MinPos;
	XYZTriplet		MaxPos;
	XYZTriplet		AutoCenterPos;
	float			UnknownFloat3;
	Textures		Textures;
	LodEdges		LodEdges;
	ulong			NoOfFaces;
	ulong			OffsetToLodSections;
	LodFace			LodFaces[NoOfFaces]; // ie polygons
	ulong			nSections;
	LODSection		LODSections[nSections];
	ulong			nNamedSelections;
	NamedSelection	NamedSelections[nNamedSelections];
	ulong			nTokens;
	NamedProperty	NamedProperties[nTokens];
	ulong			nFrames;
	Frame			Frames[nFrames];
	ulong			IconColor;
	ulong			SelectedColor;
	ulong			Unknown;
	ulong			nProxies;
	LodProxy		LodProxies[nProxies];
};

VertexTable

See P3D Point and Face Flags.

	struct
	{ 
	ulong		Count;
	ulong		PointFlags[Count];	// compressed
	ulong		Count;
	UVPair		UV1[Count];			// compressed 
	ulong		Count;
	XYZTriplet	Points[Count];		// UNcompressed
	ulong		Count;
	XYZTriplet	Normals[Count];		// UNcompressed
	}
  • Count is the same value for all four tables.

Textures

struct
{
ulong	Count;
asciiz	Textures[...]; // "data/1.paa\0data/2.paa\0"...
}

Count corresponds to the number of concatenated strings. It is required, since, architecturally at least, one of more of the asciiz strings could be null.

NamedProperty

struct
{
	Asciiz Property; // e.g "noshadow" = "1"
	Asciiz Value;
}


Related Page(s)

LZ in ODOL

see Compressed LZSS File Format

Model File Formats