Wrp File Format - OPRWv17 to 24: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (→‎Introduction: legend)
m (re-organised)
Line 1: Line 1:
{{unsupported-doc}}
{{unsupported-doc}}


==Introduction==
==Introduction==
For a general description of wrp files see [[Wrp_File_Format_-_8WVR|8WVR]]
===Legend===
===Legend===
[http://en.wikipedia.org/wiki/Endianness#Little-endian Little endian] byte order, lsb first for numeric values,
[http://en.wikipedia.org/wiki/Endianness#Little-endian Little endian] byte order, lsb first for numeric values,
text is stored in [http://en.wikipedia.org/wiki/Endianness#Big-endian Big endian] byte order.
text is stored in [http://en.wikipedia.org/wiki/Endianness#Big-endian Big endian] byte order.


*'''byte''' 8 bits, unsigned.
*'''byte''' 8 bits, unsigned.
Line 13: Line 13:
*'''float''' 4 bytes, signed.
*'''float''' 4 bytes, signed.
*'''asciiz'''  variable length zero terminated string
*'''asciiz'''  variable length zero terminated string
*float:  32 bit signed single precision floating point value (4 bytes)
*char:  ascii character(s)
*char:  ascii character(s)
===String===
String
{
  ulong Length;
  char  ascii[Length];
}
This is a Basic or Pascal way of doing things. Unlike the more familiar Asciiz (null terminated) strings used in other file formats, here, speed is used to NOT calculate string length (or at least hunt the null char)


===XYPair===
===XYPair===
Line 29: Line 20:
   ulong x,y;
   ulong x,y;
  }
  }
===XYZTriplet===
XYZTriplet
{
  float    x,y,z;
}
Normally, this structure is normally associated with positional information.


==File Format==
==File Format==
<code><nowiki>
OPRWv18
structHeader Header;
{
structGridBlock UnknownGrid1; //Packed with "Grid-Packing-Algorithm"
  WrpHeader    Header;
structGridBlock UnknownGrid2; //Packed with "Grid-Packing-Algorithm"
structPeaks Peaks;
  GridBlock    UnknownGrid1;  
structGridBlock GridMaterialIndices; //Packed with "Grid-Packing-Algorithm"
  GridBlock    UnknownGrid2;  
ushort PackedRandom[texZ][texX]; //probably random values needed for calculating clutter models position
byte PackedBytes1[gridZ][gridX]; // seems to be related to clutters or sat mask distance as it is responsible for the </nowiki>[http://tinyurl.com/6b27ed clutterbug]<nowiki>
  ulong        nPeaks;
float PackedElevation[gridZ][gridX];
  XYZTriplet    PeakPositions[nPeaks];
structMaterials Materials;
structModels Models;
  GridBlock    TextureIndex;  
structClassedModels ClassedModels;
structGridBlock UnknownGrid3; //Packed with "Grid-Packing-Algorithm"
  ushort       PackedRandom[Header.TextureGridSize.y][Header.TextureGridSize.y];   //probably random values needed for calculating clutter models position
ulong SizeOfObjects;         // div / 60 for nObjects
  byte         PackedBytes1[Header.TerrainGridSize.y][Header.TerrainGridSize.y];  
structGridBlock UnknownGrid4; //Packed with "Grid-Packing-Algorithm"
                //seems to be related to clutters or sat mask distance as it is responsible for the [http://tinyurl.com/6b27ed clutterbug]
ulong SizeOfMapInfo;       // size of structMapInfo in bytes
byte PackedBytes2[texZ][texX]; //seems to be connected to roads, runways and special grounds
  float         PackedElevation[Header.TerrainGridSize.y][Header.TerrainGridSize.y];
byte PackedBytes3[gridZ][gridX]; //unknown but 0x00 for forests and roads and sea, mostly 0x03 and rarely 0x02
ulong maxObjectID; //not number of objects
  ulong        nTextures;
ulong ObjectBlockOffset; //amount of bytes used for RoadNets
  Texture      Textures[nTextures];       //"PrefixRoot\data\layers\some.rvmat\0\0"
structRoadNet RoadNets[texZ,texX]; //probably organized in texture grid cells to realize terrain streaming
structObject Objects[SizeOfObjects/60];
  ulong        nModels;
structMapInfo MapInfos[]; //have to be iterated until EndOfFile
  asciiz        modelPaths[nModels];     //"ca\buildings\kostelik.p3d\0"
</nowiki></code>
 
  ulong         nClassedModels;
==Structures==
  ClassedModel  Models[nClassedModels];    //"Land_Hangar\0" : "ca\buildings\Hangar.p3d\0"
  GridBlock    UnknownGrid3;
  ulong        SizeOfObjects;             //in bytes
  GridBlock    UnknownGrid4;  
  ulong         SizeOfMapInfo;             //in bytes
  byte         PackedBytes2[Header.TextureGridSize.y][Header.TextureGridSize.y];   //seems to be connected to roads, runways and special grounds
  byte         PackedBytes3[Header.TerrainGridSize.y][Header.TerrainGridSize.y];   //unknown but 0x00 for forests and roads and sea, mostly 0x03 and rarely 0x02
  ulong         maxObjectID;
  ulong         SizeOfRoadNets;             //in bytes
  RoadNet      RoadNets[Header.TextureGridSize.y][Header.TextureGridSize.y];       //probably organized in texture grid cells to realize terrain streaming
  Object        Objects[SizeOfObjects/SizeOfObject]; // SizeOfObject ==60
  MapInfo      MapInfos[...];
}


===structHeader===
*Because of the variable length of the asciiz strings in '''RoadParts''', the actual position of the following [[#Objects]] block cannot be pre-calculated. Therefore the ''SizeOfRoadNets'' can be used, as it is the size (in bytes) of this '''RoadNet''' block.
<code><nowiki>
*Similarly, SizeOfObjects can be used to skip to the MapInfo
structHeader
{
char signature[4]; //"OPWR"
ulong version; //0x12 = 18
ulong texX,texZ;        //textures cell dimensions. 256x256 eg
ulong gridX,gridZ; //elevations cell dimensions. 256x256 eg
float gridSize; //in meters (normally 50.0)
}
</nowiki></code>
----


===structGridBlock===
===WrpHeader===


when present, the leading byte = 0x01
WrpHeader   
when a null GridBlock exists
  {
  {
   byte flag; // =0
  char   Filetype;       // "OPWR"
  ulong NullBits;// = 0
  ulong   version;         // 0x12 = 18
  XYPair  TextureGridSize; // 256 x 256 eg
  XYPair  TerrainGridSize; // ditto
  float  CellSize;        // generally 50.0 meters
  }
  }


GridBlock is currently in the 'discussion tab'.  
This is a traditional wrp header endemic to most wrp formats. The CellSize was introduced during Elite
 
----
----


===structPeaks===
===GridBlock===
<code><nowiki>
structPeaks
{
ulong nPeaks;
float XZY[nPeaks][3]; //Position of the Peak  (Z is height)
}
</nowiki></code>
----


This is a compressed grid of cells


----
*when present, the leading flag = 0x01
===GridMaterialIndices===
*when a null GridBlock exists
<code><nowiki>
{
structGridBlock GridMaterialIndices
  byte  flag;    // =0
{
  ulong NullBits; // = 0
//still need to be worked out
}
}
</nowiki></code>
----


GridBlock is currently in the 'discussion tab'.


----
----


===structMaterials===
===TextureIndex===
GridBlock TextureIndex
{
//still need to be worked out
}
 
===Texture===
Texture
{
  Concatenated Asciiz rvmat_paths; //"PrefixRoot\data\layers\some.rvmat\0\0"
}


<code><nowiki>
*This, and it's TextureIndex are more properly seen as a Materials list. However, the texturing intent is retained from previous versions of wrp files.
structMaterials
{
ulong nMaterials; //Number of Rvmat entries (at least one)
structMaterial Materials[nMaterials];
}
</nowiki></code>
At least one material is always present at the first position of ''Materials'' with an empty path, so ''nMaterials'' appears to be one more than the real count of materials.


====structMaterial====
*see [[P3D_File_Format_-_ODOLV40_Revised|PrefixRoot\]] description. Essentially, it is a reference to a pbo.
<code><nowiki>
structMaterial
{
asciiz rvmat_path;
byte 0x00;
}
</nowiki></code>
----


Each cell in the grid references a (series of) texture(s) for it's surface. The TextureIndex is an index into these material names.


----
Thus, '''each''' entry of this materials list consists of concatenated asciiz strings in the form


===structModels===
"AnRvmatFile\0AnotherOne\0EvenMore\0\0"
<code><nowiki>
structModels
{
ulong nModels;         //number of models
asciiz modelPaths[nModels]; //e.g. ca\buildings\kostelik.p3d
}
</nowiki></code>


This is a simple list of models that are used on the island. These are referenced in [[#structObject|structObject]] by their index in the ''modelPaths'' array with the first index being 0.
Ie, the end of '''this''' entry = \0\0  


----
It so happens that only one, single 'texture', a single rvmat file, is used per cell, but, the construct is there to have any amount.


therefore, this list is of the form


----
"AnRvmatFile\0\0"


===structClassedModels===
There is always, at least one materials entry. The 1st entry.
<code><nowiki>
The first entry (effectively index 0) is a dummy, with an empty path, because it will never be accessed. Indexes start at 1.
structClassedModels
{
ulong nClassedModels; //number of classed models
structClassedModel Models[nClassedModels];
}
</nowiki></code>


====structClassedModel====
===ClassedModel===
<code><nowiki>
<code><nowiki>
structClassedModel
ClassedModel
{
{
asciiz class_name; //e.g. Land_Hangar
asciiz     class_name; //"Land_Hangar\0"
asciiz model_path;       //e.g. ca\buildings\kostelik.p3d
asciiz     model_path; //"ca\buildings\Hangar.p3d"
float pos[3];           //XZY
XYZTriplet Position;
ulong unknown;
ulong     unknown;
}
}
</nowiki></code>
</nowiki></code>
----


----
===RoadNet===
===structRoadNet===
<code><nowiki>
<code><nowiki>
structRoadNet
RoadNet
{
{
ulong nRoadParts; // Zero or More...  
  ulong   nRoadParts; // Zero or More...  
structRoadPart RoadParts[nRoadParts];
  RoadPart RoadParts[nRoadParts];
}
}
</nowiki></code>
</nowiki></code>
Because of the variable length of the asciiz strings in '''structRoadPart''', the actual position of the following [[#structObjects|structObjects]] block cannot be pre-calculated. Therefore the ''ObjectBlockOffset'' can be used, as it is the size (in bytes) of this '''structRoadNet''' block.


Each grid cell of an island contains a '''structRoadNet''' entry. If there are no road(s) in that cell, there are no '''structRoadParts'''. Consequently, a 256 x 256 cell island with '''no''' roads would have a '''structRoadNet''' block consisting of 256 x 256 x 4 zeros. So if ''ObjectBlockOffset'' == texZ * texX * 4, you can skip this block, because you know it does not contain any roads.
Every cell on the map contains a '''RoadNet''' entry. If there are no road(s) for that cell, there are no '''RoadParts''' (index =0)


It is probably saved this way to better realize terrain streaming.
It is probably saved this way to better realize terrain streaming.


====structRoadPart====
====RoadPart====
<code><nowiki>
<code><nowiki>
structRoadPart
RoadPart
{
{
ushort nRoadPositions;
ushort     nRoadPositions;
float RoadPositions[nRoadPositions][3]; //XYZ
XYZTriplet RoadPositions[nRoadPositions];
byte Flags[4];
byte       Flags[4];
asciiz Model;
asciiz     Model;
float matrix[12];
float     matrix[12];
}
}
</nowiki></code>
</nowiki></code>
----


 
===Object Optional===
----
 
===structObject Optional===
<code><nowiki>
<code><nowiki>
structObject
Object
{
{
ulong ObjectID;
ulong ObjectID;
ulong modelIndex;
ulong modelIndex; // into the [[#Models|models path name list]] (1 based)
float matrix[12];
float TransformMatrix[3][4]; // standard directX transform matrix
ulong 0x02;
ulong 0x02;
}
}
</nowiki></code>
</nowiki></code>
*maxObjectID is somewhat irrelevant. It is indeed the id of the highest value ObjectID, but, these 'ID's, while unique are highly arbitrary in nature and alter on any change to the 'world' when editing in oxygen/visitor. This ID can occur anywhere in the object list, and the id values before it are non-sequential. maxObjectID '''cannot''' be used as a nObject ID's eg.


If ''SizeOfObjects'' above == 0 then there are no objects.
*If ''SizeOfObjects'' above == 0 then there are no objects.


''modelIndex'' refers to the list of modelpaths in [[#structModels|structModels]].
Note that unlike it's 4/8WVR cousin, where each Object entry contains (an often) repetitious list of p3d files. The same construct is (now) used as per Textures and Indexes.
Namely, a common list of models is indexed into, by each Object entry.


''ObjectID'' is used to uniquely identify every object. Therefore you won't find two objects having the same ''ObjectID''.
----


===MapInfo Optional===


----
MapInfo
{
  ulong infoType;
  MapData[...];
}


===structMapInfo Optional===
Mapinfo, when it exists, extends to end of file. The sizeof each entry, is determined by it's info type thus:
<code><nowiki>
structMapInfo
{
ulong infoType;
if(infoType in [0,1,2,10,11,13,14,15,16,17,22,23,26,27,30]) structType1 type1;
if(infoType in [24,31,32]) structType2 type2;
if(infoType in [25,33]) structType3 type3;
if(infoType in [3,4,8,9,18,19,20,28,29]) structType4 type4;
if(infoType in [34]) structType5 type5;
}
</nowiki></code>


Mapinfo, when it exists, extends to end of file.
====MapData====
MapData
{
  if(infoType in [0,1,2,10,11,13,14,15,16,17,22,23,26,27,30]) MapType1;
  if(infoType in [24,31,32])                                  MapType2;
  if(infoType in [25,33])                                    MapType3;
  if(infoType in [3,4,8,9,18,19,20,28,29])                    MapType4;
  if(infoType in [34])                                        MapType5;
}


====structType1====
====MapType1====
<code><nowiki>
<code><nowiki>
structType1
MapType1
{
{
ulong ObjectId
ulong ObjectId
float x, z
float x, z
}
}
</nowiki></code>
</nowiki></code>


====structType2====
====MapType2====
<code><nowiki>
<code><nowiki>
structType2
MapType2
{
{
ulong ObjectId
  ulong ObjectId
float[4][2] Bounds
  float Bounds[4][2];
}
}
</nowiki></code>
</nowiki></code>


====structType3====
====MapType3====
<code><nowiki>
<code><nowiki>
structType3
MapType3
{
{
long[2] unknown
ulong unknown[2];
float[2][2] line?
float line[2][2];?
}
}
</nowiki></code>
</nowiki></code>


====structType4====
====MapType4====
<code><nowiki>
<code><nowiki>
structType4
MapType4
{
{
ulong ObjectId
ulong ObjectId
float[4][2] Bounds
float Bounds[4][2];
byte[4] Color //rgba
byte Color[4] //rgba
}
}
</nowiki></code>
</nowiki></code>


====structType5====
====MapType5====
<code><nowiki>
<code><nowiki>
structType5
MapType5
{
{
ulong ObjectId
  ulong ObjectId
float[2][2] line?
  float line[2][2];?
}
}
</nowiki></code>
</nowiki></code>

Revision as of 05:14, 5 February 2009

Template:unsupported-doc

Introduction

For a general description of wrp files see 8WVR

Legend

Little endian byte order, lsb first for numeric values, text is stored in Big endian byte order.

  • byte 8 bits, unsigned.
  • ushort 2 bytes, unsigned.
  • ulong 4 bytes, unsigned.
  • float 4 bytes, signed.
  • asciiz variable length zero terminated string
  • char: ascii character(s)

XYPair

XYPair
{
 ulong x,y;
}

XYZTriplet

XYZTriplet
{
 float    x,y,z;
}
Normally, this structure is normally associated with positional information.

File Format

OPRWv18
{
 WrpHeader     Header;

 GridBlock     UnknownGrid1; 
 GridBlock     UnknownGrid2; 

 ulong         nPeaks;
 XYZTriplet    PeakPositions[nPeaks];

 GridBlock     TextureIndex; 

 ushort        PackedRandom[Header.TextureGridSize.y][Header.TextureGridSize.y];   //probably random values needed for calculating clutter models position
 byte          PackedBytes1[Header.TerrainGridSize.y][Header.TerrainGridSize.y]; 
                //seems to be related to clutters or sat mask distance as it is responsible for the clutterbug

 float         PackedElevation[Header.TerrainGridSize.y][Header.TerrainGridSize.y];

 ulong         nTextures;
 Texture       Textures[nTextures];        //"PrefixRoot\data\layers\some.rvmat\0\0"

 ulong         nModels;	
 asciiz        modelPaths[nModels];	    //"ca\buildings\kostelik.p3d\0"

 ulong         nClassedModels;
 ClassedModel  Models[nClassedModels];     //"Land_Hangar\0" : "ca\buildings\Hangar.p3d\0"

 GridBlock     UnknownGrid3; 

 ulong         SizeOfObjects;              //in bytes

 GridBlock     UnknownGrid4; 

 ulong         SizeOfMapInfo;              //in bytes
 byte          PackedBytes2[Header.TextureGridSize.y][Header.TextureGridSize.y];   //seems to be connected to roads, runways and special grounds
 byte          PackedBytes3[Header.TerrainGridSize.y][Header.TerrainGridSize.y];   //unknown but 0x00 for forests and roads and sea, mostly 0x03 and rarely 0x02
 ulong         maxObjectID;
 ulong         SizeOfRoadNets;             //in bytes
 RoadNet       RoadNets[Header.TextureGridSize.y][Header.TextureGridSize.y];        //probably organized in texture grid cells to realize terrain streaming

 Object        Objects[SizeOfObjects/SizeOfObject]; // SizeOfObject ==60

 MapInfo       MapInfos[...];
}
  • Because of the variable length of the asciiz strings in RoadParts, the actual position of the following #Objects block cannot be pre-calculated. Therefore the SizeOfRoadNets can be used, as it is the size (in bytes) of this RoadNet block.
  • Similarly, SizeOfObjects can be used to skip to the MapInfo

WrpHeader

WrpHeader     
{
 char    Filetype;        // "OPWR"
 ulong   version;         // 0x12 = 18
 XYPair  TextureGridSize; // 256 x 256 eg
 XYPair  TerrainGridSize; // ditto
 float   CellSize;        // generally 50.0 meters
}

This is a traditional wrp header endemic to most wrp formats. The CellSize was introduced during Elite


GridBlock

This is a compressed grid of cells

  • when present, the leading flag = 0x01
  • when a null GridBlock exists
{
  byte  flag;     // =0
  ulong NullBits; // = 0
}

GridBlock is currently in the 'discussion tab'.


TextureIndex

GridBlock TextureIndex
{
//still need to be worked out
}

Texture

Texture
{
 Concatenated Asciiz rvmat_paths; //"PrefixRoot\data\layers\some.rvmat\0\0"
}
  • This, and it's TextureIndex are more properly seen as a Materials list. However, the texturing intent is retained from previous versions of wrp files.
  • see PrefixRoot\ description. Essentially, it is a reference to a pbo.

Each cell in the grid references a (series of) texture(s) for it's surface. The TextureIndex is an index into these material names.

Thus, each entry of this materials list consists of concatenated asciiz strings in the form

"AnRvmatFile\0AnotherOne\0EvenMore\0\0"

Ie, the end of this entry = \0\0

It so happens that only one, single 'texture', a single rvmat file, is used per cell, but, the construct is there to have any amount.

therefore, this list is of the form

"AnRvmatFile\0\0"

There is always, at least one materials entry. The 1st entry. The first entry (effectively index 0) is a dummy, with an empty path, because it will never be accessed. Indexes start at 1.

ClassedModel

ClassedModel { asciiz class_name; //"Land_Hangar\0" asciiz model_path; //"ca\buildings\Hangar.p3d" XYZTriplet Position; ulong unknown; }

RoadNet

RoadNet { ulong nRoadParts; // Zero or More... RoadPart RoadParts[nRoadParts]; }

Every cell on the map contains a RoadNet entry. If there are no road(s) for that cell, there are no RoadParts (index =0)

It is probably saved this way to better realize terrain streaming.

RoadPart

RoadPart { ushort nRoadPositions; XYZTriplet RoadPositions[nRoadPositions]; byte Flags[4]; asciiz Model; float matrix[12]; }

Object Optional

Object { ulong ObjectID; ulong modelIndex; // into the [[#Models|models path name list]] (1 based) float TransformMatrix[3][4]; // standard directX transform matrix ulong 0x02; }

  • maxObjectID is somewhat irrelevant. It is indeed the id of the highest value ObjectID, but, these 'ID's, while unique are highly arbitrary in nature and alter on any change to the 'world' when editing in oxygen/visitor. This ID can occur anywhere in the object list, and the id values before it are non-sequential. maxObjectID cannot be used as a nObject ID's eg.
  • If SizeOfObjects above == 0 then there are no objects.

Note that unlike it's 4/8WVR cousin, where each Object entry contains (an often) repetitious list of p3d files. The same construct is (now) used as per Textures and Indexes. Namely, a common list of models is indexed into, by each Object entry.


MapInfo Optional

MapInfo
{
 ulong infoType;
 MapData[...];
}

Mapinfo, when it exists, extends to end of file. The sizeof each entry, is determined by it's info type thus:

MapData

MapData
{
 if(infoType in [0,1,2,10,11,13,14,15,16,17,22,23,26,27,30]) MapType1;
 if(infoType in [24,31,32])                                  MapType2;
 if(infoType in [25,33])                                     MapType3;
 if(infoType in [3,4,8,9,18,19,20,28,29])                    MapType4;
 if(infoType in [34])                                        MapType5;
}

MapType1

MapType1 { ulong ObjectId float x, z }

MapType2

MapType2 { ulong ObjectId float Bounds[4][2]; }

MapType3

MapType3 { ulong unknown[2]; float line[2][2];? }

MapType4

MapType4 { ulong ObjectId float Bounds[4][2]; byte Color[4] //rgba }

MapType5

MapType5 { ulong ObjectId float line[2][2];? }

Packed Data

All variables starting with "Packed" are compressed with the common BIS algorithm that is also used in paa and OFP pbo files.