P3D File Format - ODOLV7: Difference between revisions

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


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


Note that potentically compressed arrays in these structures only have an known output length.
== Legend ==
the decompressor therefore must work on infinite input length.
see example decompression at end of document


===Odol7Stuct===
see [[Generic FileFormat Data Types]]
struct ODOL
{
  char      Signature[4];      ''//"ODOL"''
  ulong    Version;          ''// 7''
  ulong    LodCount;          ''// at least one''
  LodStruct Lod[LodCount];
  ulong    ResolutionCount;  ''// same as LodCount''
  float    Resolution[ResolutionCount];
  byte      unknownBytes[24];
  float    offset[3];        ''// model offset (unknown functionality)
  ulong    mapIconColor;      ''// RGBA 32 color
  ulong    mapSelectedColor;  ''// RGBA 32 color
  ulong    unknownValue;
  float    bboxMinPosition[3]; ''// minimum coordinates of bounding box
  float    bboxMaxPosition[3]; ''// maximum coordinates of bounding box
  float    wrpModelOffset[3];  ''// offset value to apply when object is placed on a WRP
  float    offset2[3];        ''// another offset value (unknown functionality)
};


===LodStruct===
LodStruct
{
  float  fvalue[12];  ''// unknown: contains some max/min vertices positions and so far''
  ulong  TexturesCount;
  Asciiz Textures[TexturesCount]; ''// zero ended 'C' strings 'd','a','t','a','\','1','.','p','a','a','\0','d','a','t','a','\','2','.','p','a','a','\0'...
                                    //read until count of founded char('\0') equil TexturesCount;''
  CompressedStruct Table1
  {
    ulong  Count;
    ushort Table1[Count];''// if > 511 array compressed '' 
  }
  CompressedStruct Table2
  {
  ulong''' Count; ''// this Count is same value as VerticesCount''
  ushort''' Table2[Count];''// > 511 then array is compressed
  }
    //tables can by used to join vertices, each face has got 3 or 4 vertices that are seperated for each face in odol format
    //every vertex is owned only by 1 face
    //mlodvertexindex = Table1st[ Table2nd[ odolvertexindex ] ];''
 
    '''ulong''' FacesCount;
    '''ulong''' uvalue;  ''// unknown value''
    '''struct''' Face[FacesCount];
 
    '''ulong''' uvalue2;'' // unknown value''
    '''char''' uchar[18*uvalue2];'' // unknown value''
 
    '''ulong''' NamedSelectionCount
    '''struct''' NamedSelection[NamedSelectionCount]
 
    '''ulong''' NamedPropetiesCount;
    '''struct''' NamedPropeties[NamedPropetiesCount]
 
    '''ulong''' uvalue7;// unknown value ???
    '''struct''' ustruct[uvalue7]; ''// unknown value''
 
    '''ulong''' ProxiCount;
    '''struct''' Proxi[ProxiCount];
  }; // end of lod
===VerticesStruct===
  VerticesStruct
  {
    CompressedStruct Attribs
    {
    '''ulong''' Count;
    '''ulong''' Attribs[Count]; ''// if > 255 then array is compressed
    }
    CompressedStruct UVset
    {
    '''ulong''' Count; ''// again same value''
    '''struct''' UVset[Count]; ''// if > 127 then array is compressed
    }
    CompressedStruct Position
    {
    '''ulong'''  Count; ''// again same value''
      float Position[Count][3]; //XZY.  If > etc
    }
    CompressedStruct Normals
    {
    '''ulong''' Count; ''// again same value''
    float Normals[Count][3]; //XZY. If > etc
    }
  }
===CompressedStruct===


a compressed struct is such that if the Count * sizeof data type is > 1023 then lzh compression is used
== CompressedStructures ==


Thus, ulong arrays = > 255
(potentially) compressed arrays are endemic to most blocks contained in a p3d.
float[2] = > 127
etc


===Faces===
<syntaxhighlight lang="cpp">
  '''struct''' Face
CompressedStruct
    {
{
      '''uint''' Attribs;
ulong Count;
      '''word''' TextureIndex; ''//if ((int)TextureIndex==-1) no texture;''
<type> Array[Count];
      '''char''' CountOfVertices;  // 3 or 4
};
      '''word''' VerticesIndex[CountOfVertices];'' //! size of array is not constant.''
</syntaxhighlight>
    };
===NamedSelection===
  '''struct''' NamedSelection
    {
      '''char''' name[...]; ''// zero ended string''
 
      '''uint''' VerticesSelectedCount;
      '''word''' VerticesSelected[VerticesSelectedCount];''// if VerticesSelectedCount > 511 then array is compresed by LZ algorithm. see LZ in ODOL.''
 
      '''uint''' uvalue3; ''// unknown value''
      '''word''' uarray[uvalue3];''// unknown value''
 
      '''uint''' uvalue4; ''// unknown value''
      '''uint''' uarray[uvalue4];''// unknown value // if VerticesSelectedCount > 255 then array is compresed by LZ algorithm. see LZ in ODOL.''
 
      '''char''' uchar; ''// unknown value''
 
      '''uint''' uvalue5; ''// unknown value''
      '''uint''' uarray[uvalue5];''// unknown value''
 
      '''uint''' FacesSelectedCount;
      '''word''' FacessSelected[FacesSelectedCount]''// if FacesSelectedCount > 511 then array is compresed by LZ algorithm. see LZ in ODOL.''
 
      '''uint''' uvalue6; ''// unknown value''
      '''char''' uarray[uvalue6];''// unknown value''
    };


===VerticesPosition===
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)
    '''struct''' VerticesPosition
    {
      '''float''' x,y,z;
    };
===VerticesNormal===
    '''struct''' VerticesNormal
    {
      '''float''' x,y,z;
    };
===UVset===
    '''struct''' UVset
    {
      '''float''' U,V;
    };
===Proxi===
  '''struct''' Proxi
    {
      '''char''' Name[...] ''// zero ended  string''
      '''float''' rotationMatrix[9];
      '''float''' translation[3];
    };


===ustruct===
After decompression, the Count remains the same because it is a count of the arraytype.
  '''struct''' ustruct ''// unknown value''
    {
      '''uint''' uvalue8;''// unknown value''
      '''uint''' uvalue9;''// unknown value''
      '''char''' uarray[12*uvalue9];''// unknown value :-( i know nothing about it''
    };
===NamedPropeties===
  '''struct''' NamedPropeties
    {
      '''char''' Name[...];
      '''char''' Value[...]; ''// 'n','o','s','h','a','d','o','w','\0','1','\0'...''
    };


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


''Lempel-Ziv compression


Note1.
Thus for various Array <types>
* ulong Array:    > 255  // 1024 /  sizeof(ulong)
* float thing[2]: > 127  // 1024 / 2*sizeof(float)
* SomeStructure:  >      // count * sizeof (SomeStructure) > 1023


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


Note2.
Note that potentially compressed arrays in these structures only have an known output length. the decompressor therefore must work on infinite input length.
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.




==== pascal code====
= Odol7Stuct =


'''function''' LZBlockRead(var F:file; var outdata:array of byte;szout:integer):byte;
See [[P3D Model Info]].
'''var'''
<syntaxhighlight lang="cpp">
k, r, pr, pi,po,i,j:integer;
ODOLV7
flags:word;
{
buf:'''array'''[0..$100e] '''of''' byte;
StandardP3DHeader Header;
c:byte;
LodStruct Lods[Header.LodCount];
crc:integer;
ModelInfo ModelInfo;
'''begin'''
};
po:=0;
</syntaxhighlight>
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====


'''int''' Decode('''unsigned char''' *in,'''unsigned char''' *out,'''int''' szin,'''int''' szout)
== StandardP3DHeader ==
{
 
        szin = szin > 0? szin: 0x7fffffff;
<syntaxhighlight lang="cpp">
        '''int'''  i, j, k, r = 0, pr, pi = 0,po = 0;
StandardP3DHeader
        '''unsigned int'''  flags = 0;
{
        '''unsigned char''' buf[0x100F], c;
char Signature[4]; //"ODOL" (vs MLOD eg)
        '''for''' (i = 0; i < 0x100F; buf[i] = 0x20, i++);
ulong Version; // 7
        '''while''' (pi < szin && po < szout)
ulong LodCount; // at least one
        {
}
                '''if''' (((flags >>= 1) & 256) == 0)
</syntaxhighlight>
                {
 
                        '''if'''(pi >= szin)'''break''';
 
                        c = in[pi++];
== LodStruct ==
                        flags = c | 0xff00;
 
                }
See:
                '''if''' (flags & 1)
* {{Link|#VertexTable}}
                {
* {{Link|#Textures}}
                        '''if'''(pi >= szin || po >= szout)'''break''';
* [[P3D Lod Edges]]
                        c = in[pi++];
* [[P3D Lod Faces]]
                        out[po++] = c;
* [[P3D Lod Sections]]
                        buf[r++] = c;
* [[P3D Named Selections]]
                        r &= 0xfff;
* {{Link|#NamedProperty}}
                } '''else'''
* [[P3D Lod Frames]]
                {
* [[P3D Lod Proxies]]
                        '''if'''(pi + 1 >= szin)'''break''';
 
                        i = in[pi++];
 
                        j = in[pi++];
<syntaxhighlight lang="cpp">
                        i |= (j & 0xf0) << 4;
LodStruct
                        j  = (j & 0x0f) + 2;
{
                        pr = r;
VertexTable VertexTable;
                        '''for''' (k = 0; k <= j; k++)
float UnknownFloat1;
                        {
float UnknownFloat2;
                                c = buf[(pr - i + k) & 0xfff];
XYZTriplet MinPos;
                                '''if'''(po >= szout)'''break''';
XYZTriplet MaxPos;
                                out[po++] = c;
XYZTriplet AutoCenterPos;
                                buf[r++] = c;
float UnknownFloat3;
                                r &= 0xfff;
Textures Textures;
                        }
LodEdges LodEdges;
                }
ulong NoOfFaces;
        }
ulong OffsetToLodSections;
        '''return''' pi;// next 4 bytes = checksum
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];
};
</syntaxhighlight>
 
=== VertexTable ===
 
See [[P3D Point and Face Flags]].
<syntaxhighlight lang="cpp">
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
}
</syntaxhighlight>
 
*Count is the same value for all four tables.
 
=== Textures ===
 
<syntaxhighlight lang="cpp">
struct
{
ulong Count;
asciiz Textures[...]; // "data/1.paa\0data/2.paa\0"...
}
</syntaxhighlight>
 
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 ===
 
<syntaxhighlight lang="cpp">
struct
{
Asciiz Property; // e.g "noshadow" = "1"
Asciiz Value;
}
</syntaxhighlight>
 
 
= Related Page(s) =
 
== LZ in ODOL ==
 
see [[Compressed LZSS File Format]]
 
[[BIS File Formats#3D Model File Formats|Model File Formats]]


=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