ESO Mod talk:Land Data Format

The UESPWiki – Your source for The Elder Scrolls since 1995
Jump to: navigation, search

Bit of Infos[edit]

"Depending on the data index the content and format of the data is different..."

  • These are actually "ZoBuffer2D<T>" objects. They are used in a bunch of place always with same types, depending on needs, here are details of how the game load them /deserialize them from binary :
  • see your " if (uStack144 != 0xbeef000c) {" here :)
    ZoReadBuffer::Read_uint32(param_1,&uStack144);
    if (uStack144 + 0x4110ffff < 0xb) {
        ZoTerrainCellData::LoadOldVersionSupport(this,uStack144,param_1);
    }
    else {
        if (uStack144 != 0xbeef000c) {
            ZoLog::LogSomething(0x3,0x1,"LoadPartsFromBuffer","Data Corruption Detected.");
            ZoCrashHandler::TerminateProcess();
            lVar4 = 0x0;
            do {
                plVar3 = *(long **)(&this->field_0x2e8 + lVar4 * 0x8);
                if (plVar3 != NULL) {
                    *(undefined8 *)(&this->field_0x2e8 + lVar4 * 0x8) = 0x0;
                    LOCK();
                    plVar1 = plVar3 + 0x1;
                    iVar2 = *(int *)plVar1;
                    *(int *)plVar1 = *(int *)plVar1 + -0x1;
                    if (iVar2 == 0x1) {
                        (**(code **)(*plVar3 + 0x8))();
                    }
                }
                lVar4 += 0x1;
            } while (lVar4 != 0x10);
            return;
        }
        bStack50 = 0x0;
        uStack57 = '\0';
        ZoReadBuffer::Read_uint8(param_1,&uStack57);
        ZoReadBuffer::Read_uint32(param_1,&uStack140);
        ZoReadBuffer::Read_uint8(param_1,&bStack50);
        if (bStack50 != 0x0) {
            ppvStack136 = &this->field_0x98;
            ppvStack128 = &this->field_0xc8;
            pZStack120 = (ZoBuffer2D<unsigned_int> *)&this->field_0xe0;
            pZStack112 = (ZoBuffer2D<unsigned_int> *)&this->field_0xf8;
            pZStack104 = (ZoBuffer2D<unsigned_int> *)&this->field_0x110;
            pZStack96 = (ZoBuffer2D<unsigned_int> *)&this->field_0x128;
            ppvStack88 = &this->field_0x140;
            ppvStack80 = &this->field_0xb0;
            ppvStack72 = &this->field_0x158;
            bVar5 = 0x0;
            do {
                bStack49 = 0x0;
                uStack64 = 0x0;
                uStack56 = 0x0;
                ZoReadBuffer::Read_uint8(param_1,&bStack49);
                ZoReadBuffer::Read_uint32(param_1,&uStack64);
                ZoReadBuffer::Read_uint32(param_1,&uStack56);
                if (uStack56 != 0x0) {
                    lVar4 = (**(code **)(*(long *)param_1 + 0x20))(param_1);
                    ZoReadBuffer::ZoReadBuffer((ZoReadBuffer *)apuStack176,(uchar *)((ulong)uStack64 + lVar4),uStack56,0x1);
                    switch(bStack49) {
                    case 0x0:
                        ZoBuffer2D<float>::Load((ZoBuffer2D<float> *)ppvStack136,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x1:
                        ZoBuffer2D<unsigned_int>::Load((ZoBuffer2D<unsigned_int> *)ppvStack128,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x2:
                        ZoBuffer2D<unsigned_int>::Load(pZStack120,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x3:
                        ZoBuffer2D<unsigned_int>::Load(pZStack112,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x4:
                        ZoBuffer2D<unsigned_int>::Load(pZStack104,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x5:
                        ZoBuffer2D<unsigned_int>::Load(pZStack96,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x6:
                        ZoBuffer2D<ZoVec3T<float>>::Load((ZoBuffer2D<ZoVec3T<float>> *)ppvStack88,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x7:
                        ZoBuffer2D<unsigned_short>::Load((ZoBuffer2D<unsigned_short> *)ppvStack80,(ZoReadBuffer *)apuStack176);
                        break;
                    case 0x8:
                        ZoBuffer2D<unsigned_int>::Load((ZoBuffer2D<unsigned_int> *)ppvStack72,(ZoReadBuffer *)apuStack176);
                    }
                }
                bVar5 += 0x1;
            } while (bVar5 < bStack50);
        }
        apuStack176[0] = &this->field_0x28;
        ZoReadBuffer::Read_buffer(param_1,(char **)apuStack176,0x40);
        ZoReadBuffer::Read_float(param_1,(float *)((long)&this->field_0x64 + 0x4));
        ZoReadBuffer::Read_float(param_1,(float *)&this->field_0x6c);
        ZoReadBuffer::Read_float(param_1,(float *)((long)&this->field_0x6c + 0x4));
        ZoReadBuffer::Read_float(param_1,(float *)&this->field_0x74);

  • For their actual usage, they are extensively used in world data.
  • What you are trying to mess with and calling it "land" are actually most of the actual "ZOTerrainCellData", with the stream 0 being, you guessed it, the heightmap, as 0-1000f values.
  • Just as you do, heighmap is used by going from floatheight to shortheigh
  • ingame :
uint ZoTerrainCellData::FloatHeightToShortHeight(float param_1)
{
    return (int)((param_1 / 1000.0) * 65535.0) & 0xffff;
}

float ZoTerrainCellData::ShortHeightToFloatHeight(uint param_1)

{
    return ((float)param_1 / 65535.0) * 1000.0;
}

  • TerrainCellDatas contains actually waaay more than just heightmap, as they suffice to streaming world, and by adding "map/location/watervolumes/world.."a GigaLoad of other "...Defs_Client", they are alpha and omega to the whole world rendering alongside with any position or texturing system related.
  • regarding your export, the github project nearly was able to output the heighmap : it just lacks the standard size of a "SquareCell", which is 5*5 of these 65*65 cells.
  • There are also more than one world loaded in each, so you have to give attention to the 04*.dat files first : the ones that starts with 01 00 00 00 and which got the "terrain-X" in their contents are valuable :
 * their order reflect exact order of terrain cell datas
 * their two Dwords after the 01000000 are actually sizes. You can divide by 100 (or 0x64, if it "clicks" something in your head relative to how to retrieve all of these info in mnf unknowns you have ;))
 * that will lead to exact number of cells.
 * subzones then will have nearly always 01f4 / 01f4 , so 5x5(*65) cells, and the "big zone" will usually have from 11x11 up to 90x90 (cyrodil).
 * to export properly, and with devil, you shoul remember the bottom to top issue, and that the cells relates 1:1 to WorldPosition as ServerSide implemented Spatial partitionning (Grid)
 * So to output properly, you should output 5 cells and go up 65pixel and so on until you reach the correct number of Height cells. 
 * so you should rework your AddBigimage along these lines :


using namespace eso;
    unsigned int MH = 55;
    unsigned int MW = 55;
    unsigned int MC = 5;
    int skip_cell_files = 0;


ILuint g_BigImage;
int g_BigImageX = 0;
int g_BigImageY = 0;
int g_BigImageX2 = 0;

ILuint g_BigImage2;
int g_BigImage2X = 0;
int g_BigImage2Y = 0;

void AddBigImage(std::vector<word> Data)
{
    ilBindImage(g_BigImage);
    ilSetPixels((g_BigImageX * 65) + (65 *MC* g_BigImageX2), g_BigImageY * 65, 0, 65, 65, 1, IL_LUMINANCE, IL_UNSIGNED_SHORT, Data.data());



    ++g_BigImageX;

    if(g_BigImageX >= MC)
    {
        g_BigImageX = 0;
        ++g_BigImageY;
    }
    if(g_BigImageY >= MH)
    {
        g_BigImageX2++;
        g_BigImageY = 0;
    }
}

* MH being number of 65* Cells expected for height, MW beigh total wide expected in same unit, and so on.
* Full example for eso081.dat:
_$ export TARGET="..\..\..\..\..\EsoExportScripts\export\tmp\081\"
_$ SKIP=0; HP=0;WP=0;for I in $(grep terrain-height 04*.dat -r |awk '{print $3}');do SIZESTR=$(hexdump -s 4 -n8 $I |sed 's/^.......//g'|sed 's/0000//g' |sed 's/^ //' );HEIGHT=$(echo $SIZESTR|awk '{print $1}');WIDTH=$(echo $SIZESTR|awk '{print $2}');W=$(($(printf "%d" $((16#$WIDTH)))/100));H=$(($(printf "%d" $((16#$HEIGHT)))/100)); SKIP=$(($HP*$WP)); echo "./EsoLand.exe -d $TARGET -h $W -w $H -s $SKIP";PH=HP;HP=$(($H+$PH));PW=WP;WP=$(($W+$PW));done
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 0
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 25
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 50 -w 35 -s 100
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 2700
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 15 -w 15 -s 3250
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 10 -w 10 -s 5200
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 10 -w 10 -s 6750
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 8500
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 9450
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 10450
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 11500
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 12600
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 5 -w 5 -s 13750
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 15 -w 15 -s 14950
./EsoLand.exe -d ..\..\..\..\..\EsoExportScripts\export\tmp\081\ -h 15 -w 15 -s 18850

* Which will give, as long as you implemented "skiping" cells and my quick fixe, this outcome :
* Auridon eso081.dat Heightmap.png

* To be added, as said it relies on homegene spatial partitioning with server side : View distance for units (and actual server sending or removing objects to you) is 2 cell wide, so a 9x9 cell square from the cell you are as center :)
* Note that 1 Cell (65*65) is WorldPosition 10000*10000 :)
* Have fun !
* Please avoid all this mess and rely on proper MNF info (filetypes 0x44000000 for definitions, 0x0000 0000 with cell id as shorts for cell datas as a grid, eg :

 42347, 0x80052A60, 0x000000E9, 0x44000000, 0x00000247, 0x0000010B, 0x767463AD, 0x00000B1F, 0x00, 0x51, 0x0004, 
 545890, 0x80001F75, 0x00000000, 0x40001D21, 0x00064FEA, 0x00000EC1, 0x90663027, 0x00A3E2A6, 0x00, 0x51, 0x0004,
 545891, 0x800AE61B, 0x00010000, 0x40001D21, 0x00064FEA, 0x00000C1A, 0xD8ACC4A7, 0x00A3F167, 0x00, 0x51, 0x0004,
 545892, 0x8002081C, 0x00020000, 0x40001D21, 0x00064FEA, 0x00000CF0, 0x9BADDC2F, 0x00A3FD81, 0x00, 0x51, 0x0004,
 545893, 0x800697CC, 0x00030000, 0x40001D21, 0x00064FEA, 0x00000CE1, 0x456CF2F6, 0x00A40A71, 0x00, 0x51, 0x0004,
 545894, 0x8003978D, 0x00040000, 0x40001D21, 0x00064FEA, 0x00000D7A, 0x0CED63B0, 0x00A41752, 0x00, 0x51, 0x0004,
 545895, 0x8003C05F, 0x00000001, 0x40001D21, 0x00064FEA, 0x00000EB1, 0x7CACD1F9, 0x00A424CC, 0x00, 0x51, 0x0004,
 545896, 0x8007BC79, 0x00010001, 0x40001D21, 0x00064FEA, 0x00000CD2, 0x4E7540C1, 0x00A4337D, 0x00, 0x51, 0x0004,
 545897, 0x80044D46, 0x00020001, 0x40001D21, 0x00064FEA, 0x00000CF9, 0xCEE26A10, 0x00A4404F, 0x00, 0x51, 0x0004,
 545898, 0x80016CE5, 0x00030001, 0x40001D21, 0x00064FEA, 0x00000BDC, 0xE495D6AF, 0x00A44D48, 0x00, 0x51, 0x0004,
 545899, 0x800507F9, 0x00040001, 0x40001D21, 0x00064FEA, 0x00000B50, 0xA72132E8, 0x00A45924, 0x00, 0x51, 0x0004,
 545900, 0x8008E01A, 0x00000002, 0x40001D21, 0x00064FEA, 0x00000DB7, 0x9935E148, 0x00A46474, 0x00, 0x51, 0x0004,

— Unsigned comment by Khemael‎ (talkcontribs)