Wrp File Format - OPRWv17 to 24 – Talk
Lou Montana (talk | contribs) m (Text replacement - " (={2,})([^ = ])(.*)([^ = ])(={2,}) * " to " $1 $2$3$4 $5 ") |
|||
(8 intermediate revisions by 2 users not shown) | |||
Line 20: | Line 20: | ||
:this block ''ushort[gridZ][gridX] unknown //packed'' seems to consist of random numbers. So my guess is, that these random values are used to calculate the positions of the clutter models, so that the island is always looking the same without saving the pos of every grass model. Could this be the case? --[[User:T D|T_D]] 13:50, 19 October 2008 (CEST) | :this block ''ushort[gridZ][gridX] unknown //packed'' seems to consist of random numbers. So my guess is, that these random values are used to calculate the positions of the clutter models, so that the island is always looking the same without saving the pos of every grass model. Could this be the case? --[[User:T D|T_D]] 13:50, 19 October 2008 (CEST) | ||
== | == struct GridBlock == | ||
In the same manner as LZH decompression produces a typeless output, so does GridBlock. | |||
*LZH uses bytes, | |||
*GridBlock uses (effectively) ulongs | |||
In Cspeak, they both return void *. | |||
*There are (up to) five Gridblocks in the file. | |||
*In this instance, resulting decoded output is either byte[TexureSizeXY], or ushort[TexureSizeXY], (as appropriate) | |||
GridBlock | |||
{ | { | ||
byte | byte IsPresent; | ||
ABPacket[1]; | if (!IsPresent) // no gridblock | ||
ulong NullBits; // always 0 | |||
else | |||
ABPacket[1]...; | |||
} | } | ||
Either there is, or there isn't, a beginning ABPacket for this GridBlock | |||
ABPacket | ABPacket | ||
Line 39: | Line 53: | ||
The atomic unit | The atomic unit is a ushort pair A,B | ||
ABPair | ABPair | ||
Line 46: | Line 60: | ||
} | } | ||
flagbits declare how many ABpairs and how many '''EMBEDDED''' ABpackets follow. A mixture of 16 items exist for each ABpacket. | |||
flagbits declare how many ABpairs and how many EMBEDDED ABpackets | |||
Embedded ABPackets can (and often do) contain further, embedded ABpackets. | Embedded ABPackets can (and often do) contain further, embedded ABpackets. | ||
Line 54: | Line 66: | ||
There is only a single ABpacket struct to start off. Everything else in the block, is embedded to that 1st struct. | There is only a single ABpacket struct to start off. Everything else in the block, is embedded to that 1st struct. | ||
Flagbits are read right to left (lsb 1st) | |||
*bit = 0 means the next item is an ABpair | |||
*bit = 1 means next item is the start of an EMBEDDED ABpacket | |||
bit = 0 means the next item is an ABpair | |||
bit = 1 means next item is the start of an EMBEDDED ABpacket | |||
Flagbit Examples: | Flagbit Examples: | ||
Line 69: | Line 78: | ||
0x1000: 12 ABpairs followed by One ABpacket followed by 3 AB pairs | 0x1000: 12 ABpairs followed by One ABpacket followed by 3 AB pairs | ||
=== Nitty Gritty === | |||
The output size is already known from the TextureCellSize and it's type (byte or short). | |||
ABpair =0 is never stored as data, therefore this buffer should be cleared to zeroes to begin with. | |||
Where an AB pair = 0, it is a 'space fill', it is not data as such, but used to 'ignore' padded flagbits=0 | |||
FlagBits 0x0100 | |||
one embededd ABpacket, and (up to) 15 genuine ABdata pairs. | |||
/* | |||
** it is assumed that the FILE io will throw it's own error if Eof is exceeded | |||
*/ | |||
uLongPair CellDimensions; | |||
ushort *Grid; // for the purposes of decoding. To the outside world, it is a void * | |||
bool ReadPackedGrid(BisFileIO * WrpFile,uLongPair Size,int TypeSize)////byte or ushort | |||
{ | |||
CellDimensions=Size; | |||
switch(WrpFile->GetByte()) | |||
{ | |||
case 1: break; | |||
case 0: return WrpFile->GetLong()==0; | |||
default:return false; | |||
} | |||
if (!(Grid=(ushort *)calloc(Size.x*Size.y,TypeSize))) throw GENERR_NOMEM; | |||
uLongPair BlockSize=CorrectSize(CellDimensions); | |||
ulong SnapOffset=WrpFile->CurrentOffset; // in case 1st Blocksize is too large | |||
if (!RecursePacketRead(WrpFile, BlockSize)) | |||
{ | |||
memset(Grid,0,Size.x*Size.y*TypeSize);// clear down again | |||
WrpFile->CurrentOffset=SnapOffset; | |||
BlockSize/=4;// reduce to what's wanted | |||
if (!RecursePacketRead(WrpFile, BlockSize)) | |||
return false; | |||
} | |||
return true; | |||
} | |||
==== CorrectSize ==== | |||
/* | |||
** The decoder works in symmetric blocks of 4 | |||
** Therefore the wanted output size doesn't necessarily fit | |||
** The next available size is used instead | |||
16 : 16 | |||
32 : 64 | |||
64 : 64 | |||
128: 256 | |||
256: 256 | |||
512: 1024 | |||
etc | |||
*/ | |||
int CorrectSize(uLongPair y) | |||
{ | |||
uLongPair x = (int)ceil((log10(y.x)/log10(2))/2); | |||
return (int)pow(2, 2*x.x); | |||
} | |||
bool RecursePacketRead(BisFileIO *WrpFile, uLongPair BlockSize, uLongPair BlockOffset=0) | |||
{ | |||
uLongPair ThisBlockSize = BlockSize / 4;//256..64..16..1 | |||
uLongPair ThisBlockOffset; | |||
ushort A,B; | |||
ushort PacketFlag = WrpFile->GetUshort(); | |||
for (int BitCount = 0; BitCount < 16; BitCount++, PacketFlag >>= 1) | |||
{ | |||
ThisBlockOffset.x = BlockOffset.x + (BitCount % 4) * ThisBlockSize.x*sizeof(ushort);// 0,1,2,3 | |||
ThisBlockOffset.y = BlockOffset.y + ((BitCount / 4) * ThisBlockSize.y); // 0,1,2,3 | |||
if (PacketFlag & 1) | |||
{ | |||
if (!RecursePacketRead(WrpFile, ThisBlockSize, ThisBlockOffset)) return false; | |||
} | |||
else | |||
{ | |||
A = WrpFile->GetUshort(); | |||
B = WrpFile->GetUshort(); | |||
if (!A && !B) continue;// filler | |||
if ((ThisBlockOffset.x>=CellDimensions.x) || (ThisBlockOffset.y>=CellDimensions.y)) return false; | |||
/* | |||
A1 A5 A9 A13 B1 ..........B13................................... | |||
.................. b2 b3 b4 | |||
A4 A8 A12 A16 B4 ...........B16............................... | |||
*/ | |||
for (ulong iy = 0; iy < ThisBlockSize.y; iy++) | |||
for (ulong ix = 0; ix < ThisBlockSize.x; ix++) | |||
{ | |||
int OffsetAB=((ThisBlockOffset.y + iy)*CellDimensions.x)+ThisBlockOffset.x + ix; | |||
Grid[OffsetAB] = A; Grid[OffsetAB+ThisBlockSize.x] = B; | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
[[User:Mikero|Mikero (nee Ook?)]] 07:01, 9 February 2009 (CET) (thanks TD) | |||
== structMaterials == | == structMaterials == |
Latest revision as of 19:22, 31 January 2021
- For the TextureIndices block I found out that it is organized in 8x4 blocks. If the block starts with 0x00 you just read in 32 short values describing the block. Otherwise you mostly see 2 identical short values describing one 8x4 block filled just with this value. Sometimes instead of the 0x00 value or the 2 identical short values, other values occur probably describing what sort of data follows and what position the next 8x4 block in the whole texX x texZ block has. But I am still not sure what order these 8x4 blocks follow. The TextureIndices block also seems to start with some "header" data which I couldnt identify so far. --T_D 12:11, 13 October 2008 (CEST)
- The unknown int in the the TextureGridRoads block is the checksum of the packed data before so it does not belong to the TextureGridRoads block --T_D 14:54, 17 October 2008 (CEST)
- Agreed, yeah... saw that, also, maxObjectID isn't part of the TextureGridRoads structure and it will probably end up really being NoOfObjects.
- No it is not NoOfObjects. Already checked that by reading objects from 8WVR and comparing readed objects with this value --T_D 04:33, 18 October 2008 (CEST)
- Experience has taught me that one can never be to certain about these things until one is certain, if you know what I mean... :)
- It maybe MaxObjectId it maybe not... time will tell.
- Building the standard SampleMap.pew into a 8wvr.wrp yeilds 5134 obejcts, then into a oprw18.wrp yeilds 5132 in the MaxOjectId/NoOfObjects field. If you add 1 to this value this is the number of obejcts that happens to be in the array of objects in this particular file.
- However, this senario doesn't hold true for a certain 'ca' mod oprw18.wrp. That value (as I mentioned earlier) is 637,876 and the number of actual objects in the list array is 602,481.
- This may be because there is a prior structure in the file that denotes out of the 637,876 objects ones that are not specified in the Objects array... dunno, well see if that pans out.
- The most logical senario is that this is the NoOfObjects less some other structure count = ObejctList count.
- If this isn't the senario then there maybe another ObjectCount item lying around the file somewhere that does indicate how many objects are in the objects list array.
- this block ushort[gridZ][gridX] unknown //packed seems to consist of random numbers. So my guess is, that these random values are used to calculate the positions of the clutter models, so that the island is always looking the same without saving the pos of every grass model. Could this be the case? --T_D 13:50, 19 October 2008 (CEST)
struct GridBlock
In the same manner as LZH decompression produces a typeless output, so does GridBlock.
- LZH uses bytes,
- GridBlock uses (effectively) ulongs
In Cspeak, they both return void *.
- There are (up to) five Gridblocks in the file.
- In this instance, resulting decoded output is either byte[TexureSizeXY], or ushort[TexureSizeXY], (as appropriate)
GridBlock { byte IsPresent; if (!IsPresent) // no gridblock ulong NullBits; // always 0 else ABPacket[1]...; }
Either there is, or there isn't, a beginning ABPacket for this GridBlock
ABPacket { ushort flagbits; Array[16]; Mixtures of 16 ABpairs and/or more ABPackets }
The atomic unit is a ushort pair A,B
ABPair { ushort AB[2]; }
flagbits declare how many ABpairs and how many EMBEDDED ABpackets follow. A mixture of 16 items exist for each ABpacket.
Embedded ABPackets can (and often do) contain further, embedded ABpackets.
There is only a single ABpacket struct to start off. Everything else in the block, is embedded to that 1st struct.
Flagbits are read right to left (lsb 1st)
- bit = 0 means the next item is an ABpair
- bit = 1 means next item is the start of an EMBEDDED ABpacket
Flagbit Examples:
0x0000: standard. 16 ABpairs follow 0xFFFF: 16 Embedded ABpackets follow 0xC000: There are 14 ABpairs immediately following this flag, followed by two embedded ABpackets (which in turn will contain more ABPair/Packets) 0x1000: 12 ABpairs followed by One ABpacket followed by 3 AB pairs
Nitty Gritty
The output size is already known from the TextureCellSize and it's type (byte or short). ABpair =0 is never stored as data, therefore this buffer should be cleared to zeroes to begin with.
Where an AB pair = 0, it is a 'space fill', it is not data as such, but used to 'ignore' padded flagbits=0
FlagBits 0x0100
one embededd ABpacket, and (up to) 15 genuine ABdata pairs.
/* ** it is assumed that the FILE io will throw it's own error if Eof is exceeded */ uLongPair CellDimensions; ushort *Grid; // for the purposes of decoding. To the outside world, it is a void * bool ReadPackedGrid(BisFileIO * WrpFile,uLongPair Size,int TypeSize)////byte or ushort { CellDimensions=Size; switch(WrpFile->GetByte()) { case 1: break; case 0: return WrpFile->GetLong()==0; default:return false; } if (!(Grid=(ushort *)calloc(Size.x*Size.y,TypeSize))) throw GENERR_NOMEM; uLongPair BlockSize=CorrectSize(CellDimensions); ulong SnapOffset=WrpFile->CurrentOffset; // in case 1st Blocksize is too large if (!RecursePacketRead(WrpFile, BlockSize)) { memset(Grid,0,Size.x*Size.y*TypeSize);// clear down again WrpFile->CurrentOffset=SnapOffset; BlockSize/=4;// reduce to what's wanted if (!RecursePacketRead(WrpFile, BlockSize)) return false; } return true; }
CorrectSize
/* ** The decoder works in symmetric blocks of 4 ** Therefore the wanted output size doesn't necessarily fit ** The next available size is used instead 16 : 16 32 : 64 64 : 64 128: 256 256: 256 512: 1024 etc */
int CorrectSize(uLongPair y) { uLongPair x = (int)ceil((log10(y.x)/log10(2))/2); return (int)pow(2, 2*x.x); }
bool RecursePacketRead(BisFileIO *WrpFile, uLongPair BlockSize, uLongPair BlockOffset=0) { uLongPair ThisBlockSize = BlockSize / 4;//256..64..16..1 uLongPair ThisBlockOffset; ushort A,B; ushort PacketFlag = WrpFile->GetUshort(); for (int BitCount = 0; BitCount < 16; BitCount++, PacketFlag >>= 1) { ThisBlockOffset.x = BlockOffset.x + (BitCount % 4) * ThisBlockSize.x*sizeof(ushort);// 0,1,2,3 ThisBlockOffset.y = BlockOffset.y + ((BitCount / 4) * ThisBlockSize.y); // 0,1,2,3 if (PacketFlag & 1) { if (!RecursePacketRead(WrpFile, ThisBlockSize, ThisBlockOffset)) return false; } else { A = WrpFile->GetUshort(); B = WrpFile->GetUshort(); if (!A && !B) continue;// filler if ((ThisBlockOffset.x>=CellDimensions.x) || (ThisBlockOffset.y>=CellDimensions.y)) return false; /* A1 A5 A9 A13 B1 ..........B13................................... .................. b2 b3 b4 A4 A8 A12 A16 B4 ...........B16............................... */ for (ulong iy = 0; iy < ThisBlockSize.y; iy++) for (ulong ix = 0; ix < ThisBlockSize.x; ix++) { int OffsetAB=((ThisBlockOffset.y + iy)*CellDimensions.x)+ThisBlockOffset.x + ix; Grid[OffsetAB] = A; Grid[OffsetAB+ThisBlockSize.x] = B; } } } return true; }
Mikero (nee Ook?) 07:01, 9 February 2009 (CET) (thanks TD)
structMaterials
i think the reason for the 1st null entry eg 'one less' than stated is for faster indexing
an material index with a value of zero means, no rvmat, don't go looking.
any other value in the 'cell' (for want of a better term) means the 'cell' has an rvmat.
This is a small point, but, I also suspect that structMaterials is indeed a concatenated string type. I suspect, that BI can add additional rvmats to the same 'object' if they choose to do so. It's very typical of them to use this string type in class:inheritance type stuctures eg.