Wrp File Format - OPRWv17 to 24
Introduction
Legend
- byte 8 bits, unsigned.
- ushort 2 bytes, unsigned.
- ulong 4 bytes, unsigned.
- float 4 bytes, signed.
- asciiz variable length zero terminated string
File Format
structHeader Header;
structGridUnknown GridPackedUnknown1; //Packed with "Grid-Packing-Algorithm"
structGridUnknown2 GridPackedUnknown2; //Packed with "Grid-Packing-Algorithm"
structPeaks Peaks;
structTextureGridMaterials GridPackedMaterialIndices; //Packed with "Grid-Packing-Algorithm"
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 clutterbug
float PackedElevation[gridZ][gridX];
structMaterials Materials;
structModels Models;
structClassedModels ClassedModels;
//something grid related[size unknown]
// followed by:
byte PackedBytes2[texZ][texX]; //seems to be connected to roads, runways and special grounds
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 ObjectBlockOffset; //amount of bytes used for RoadNets
structRoadNet RoadNets[texZ,texX]; //probably organized in texture grid cells to realize terrain streaming
structObject Objects[unknown];
structMapInfo MapInfos[]; //have to be iterated until EndOfFile
Structures
structHeader
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)
}
structPeaks
structPeaks
{
ulong nPeaks;
float XZY[nPeaks][3]; //Position of the Peak (Z is height)
}
structTextureGridMaterials
structTextureGridMaterials
{
//still need to be worked out
}
structMaterials
structMaterials
{
ulong nMaterials; //Number of Rvmat entries (at least one)
structMaterial Materials[nMaterials];
}
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
structMaterial
{
asciiz rvmat_path;
byte 0x00;
}
structModels
structModels
{
ulong nModels; //number of models
asciiz modelPaths[nModels]; //e.g. ca\buildings\kostelik.p3d
}
This is a simple list of models that are used on the island. These are referenced in structObject by their index in the modelPaths array with the first index being 0.
structClassedModels
structClassedModels
{
ulong nClassedModels; //number of classed models
structClassedModel Models[nClassedModels];
}
structClassedModel
structClassedModel
{
asciiz class_name; //e.g. Land_Hangar
asciiz model_path; //e.g. ca\buildings\kostelik.p3d
float pos[3]; //XZY
ulong unknown;
}
structRoadNet
structRoadNet
{
ulong nRoadParts; // Zero or More...
structRoadPart RoadParts[nRoadParts];
}
Because of the variable length of the asciiz strings in structRoadPart, the actual position of the following 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.
It is probably saved this way to better realize terrain streaming.
structRoadPart
structRoadPart
{
ushort nRoadPositions;
float RoadPositions[nRoadPositions][3]; //XYZ
byte Flags[4];
asciiz Model;
float matrix[12];
}
structObject
structObject
{
ulong ObjectID;
ulong modelIndex;
float matrix[12];
ulong 0x02;
}
modelIndex refers to the list of modelpaths in structModels.
ObjectID is used to uniquely identify every object. Therefore you won't find two objects having the same ObjectID.
structMapInfo
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;
}
structType1
structType1
{
ulong ObjectId
float x, z
}
structType2
structType2
{
ulong ObjectId
float[4][2] Bounds
}
structType3
structType3
{
long[2] unknown
float[2][2] line?
}
structType4
structType4
{
ulong ObjectId
float[4][2] Bounds
byte[4] Color //rgba
}
structType5
structType5
{
ulong ObjectId
float[2][2] line?
}
Packed Data
All variables starting with "Packed" are compressed with the common BIS algorithm that is also used in paa and OFP pbo files.