Thanks to Hoodium- they lifted the anti-debugger/emulator 0xf00000f1 exception overwrite
int __usercall sub_115D72C0@<eax>(unsigned int maybeOutBuffer@<edx>, _BYTE *compressedBuffer@<ecx>, int firstDwordMinusOne, int secondDword)
int __usercall probably4LZDecompress@<eax>(unsigned int maybeOutBuffer@<edx>, _BYTE *compressedBuffer@<ecx>, int firstDwordMinusOne, int secondDword)
{
_BYTE *v4; // eax@1
unsigned int v5; // edi@1
int v6; // edx@1
int result; // eax@4
unsigned int v8; // ebx@6
size_t v9; // ebx@6
int v10; // esi@7
unsigned int v11; // esi@10
unsigned int v12; // edx@10
_WORD *v13; // esi@11
int v14; // eax@12
unsigned int *v15; // ebx@12
unsigned int v16; // ecx@14
int v17; // esi@14
int v18; // edi@15
int v19; // ecx@16
unsigned int v20; // edi@20
unsigned int v21; // ebx@20
char *v22; // esi@21
char *v23; // esi@21
int v24; // ecx@22
_BYTE *v25; // edx@23
_BYTE *v26; // edi@26
int v27; // ebx@26
unsigned int v28; // edi@29
int v29; // ecx@29
unsigned int v30; // ebx@32
char v31; // cl@33
unsigned int v32; // edi@36
int *v33; // ebx@36
int v34; // ecx@37
_BYTE *v35; // [sp+0h] [bp-60h]@1
unsigned int v36; // [sp+4h] [bp-5Ch]@1
int v37; // [sp+8h] [bp-58h]@1
int v38; // [sp+Ch] [bp-54h]@1
unsigned int v39; // [sp+10h] [bp-50h]@20
unsigned int v40; // [sp+14h] [bp-4Ch]@1
unsigned int v41; // [sp+14h] [bp-4Ch]@14
char v42; // [sp+18h] [bp-48h]@6
unsigned int v43; // [sp+18h] [bp-48h]@24
__int128 v44; // [sp+1Ch] [bp-44h]@1
__int128 v45; // [sp+2Ch] [bp-34h]@1
int v46; // [sp+3Ch] [bp-24h]@1
int v47; // [sp+40h] [bp-20h]@1
int v48; // [sp+44h] [bp-1Ch]@1
int v49; // [sp+48h] [bp-18h]@1
int v50; // [sp+4Ch] [bp-14h]@1
int v51; // [sp+50h] [bp-10h]@1
int v52; // [sp+54h] [bp-Ch]@1
int v53; // [sp+58h] [bp-8h]@1
v4 = compressedBuffer;
v35 = compressedBuffer;
v5 = maybeOutBuffer;
v36 = maybeOutBuffer;
v6 = (int)&compressedBuffer[firstDwordMinusOne];// first byte of compressed data
v40 = v36;
v38 = (int)&compressedBuffer[firstDwordMinusOne];
v37 = secondDword + v36;
v46 = 0;
v47 = 1;
v48 = 2;
v49 = 1;
v50 = 4;
v51 = 4;
v52 = 4;
v53 = 4;
_mm_storeu_si128((__m128i *)&v44, _mm_load_si128((const __m128i *)&xmmword_116C4370));
_mm_storeu_si128((__m128i *)&v45, _mm_load_si128((const __m128i *)&xmmword_116C2380));
if ( secondDword )
{
while ( 1 )
{
v8 = *v4++;
v42 = v8;
v9 = v8 >> 4;
if ( v9 == 15 )
{
do
{
v10 = *v4++;
v9 += v10;
}
while ( (unsigned int)v4 < v6 - 15 && v10 == 255 );
v5 = v40;
if ( v9 + v40 < v40 || &v4[v9] < v4 )
return v35 - v4 - 1;
}
v11 = secondDword + v36;
v12 = v9 + v5;
if ( v9 + v5 > v37 - 12 )
goto LABEL_41;
v13 = &v4[v9];
if ( (unsigned int)&v4[v9] > v38 - 8 )
{
v11 = secondDword + v36;
LABEL_41:
if ( &v4[v9] == (_BYTE *)v38 && v12 <= v11 )
{
memcpy((void *)v5, v4, v9);
return v5 + v9 - v36;
}
return v35 - v4 - 1;
}
v14 = (int)&v4[-v5];
v15 = (unsigned int *)(v9 + v5);
do
{
*(_DWORD *)v5 = *(_DWORD *)(v14 + v5);
*(_DWORD *)(v5 + 4) = *(_DWORD *)(v14 + v5 + 4);
v5 += 8;
}
while ( v5 < v12 );
v16 = *v13;
v4 = v13 + 1;
v17 = v12 - v16;
v41 = v16;
if ( v12 - v16 < v36 )
return v35 - v4 - 1;
v18 = v42 & 0xF;
*v15 = v16;
if ( v18 == 15 )
{
while ( 1 )
{
v19 = *v4++;
if ( (unsigned int)v4 > v38 - 5 )
return v35 - v4 - 1;
v18 += v19;
if ( v19 != 255 )
{
if ( (unsigned int *)((char *)v15 + v18) < v15 )
return v35 - v4 - 1;
v16 = v41;
break;
}
}
}
v20 = v18 + 4;
v21 = (unsigned int)v15 + v20;
v39 = v21;
if ( v16 >= 8 )
{
*(_DWORD *)v12 = *(_DWORD *)v17;
v24 = *(_DWORD *)(v17 + 4);
v23 = (char *)(v17 + 8);
*(_DWORD *)(v12 + 4) = v24;
}
else
{
*(_BYTE *)v12 = *(_BYTE *)v17;
*(_BYTE *)(v12 + 1) = *(_BYTE *)(v17 + 1);
*(_BYTE *)(v12 + 2) = *(_BYTE *)(v17 + 2);
*(_BYTE *)(v12 + 3) = *(_BYTE *)(v17 + 3);
v22 = (char *)(*(&v46 + v41) + v17);
*(_DWORD *)(v12 + 4) = *(_DWORD *)v22;
v23 = &v22[-*((_DWORD *)&v44 + v41)];
}
v25 = (_BYTE *)(v12 + 8);
if ( v21 <= v37 - 12 )
break;
v43 = v37 - 7;
if ( v21 > v37 - 5 )
return v35 - v4 - 1;
if ( (unsigned int)v25 < v37 - 7 )
{
v26 = v25;
v27 = v23 - v25;
do
{
*(_DWORD *)v26 = *(_DWORD *)&v26[v27];
*((_DWORD *)v26 + 1) = *(_DWORD *)&v26[v27 + 4];
v26 += 8;
}
while ( (unsigned int)v26 < v43 );
v21 = v39;
v23 += v43 - (_DWORD)v25;
v25 = (_BYTE *)(v37 - 7);
}
v28 = 0;
v29 = v21 - (_DWORD)v25;
if ( (unsigned int)v25 > v21 )
v29 = 0;
if ( v29 )
{
v30 = v29;
do
{
v31 = *v23++;
++v28;
*v25++ = v31;
}
while ( v28 < v30 );
v5 = v39;
v6 = v38;
v40 = v39;
}
else
{
LABEL_39:
v6 = v38;
v5 = v21;
v40 = v21;
}
}
*(_DWORD *)v25 = *(_DWORD *)v23;
*((_DWORD *)v25 + 1) = *((_DWORD *)v23 + 1);
if ( v20 > 0x10 )
{
v32 = (unsigned int)(v25 + 8);
v33 = (int *)(v23 + 8);
do
{
v34 = *v33;
v33 += 2;
*(_DWORD *)v32 = v34;
*(_DWORD *)(v32 + 4) = *(v33 - 1);
v32 += 8;
}
while ( v32 < v39 );
v21 = v39;
}
goto LABEL_39;
}
if ( firstDwordMinusOne != 1 || *compressedBuffer )
result = -1;
else
result = 0;
return result;
}
I didn't tidy it up, but does it seem familar to you? I'm not well into compression so I see nothing actually
XMM and int128- some long long buffers going on?
UPDATE:
First DWORD is uncompressedSizeBuffer:
example:
mapdata/mapdata.fl
1stDWORD = 45-8= 37
c:\ff8\data\x\field\mapdata\maplist 0x0A 0x0D == 37 bytes
vars used in decoding:
xmmword_116C4370 xmmword 0FFFFFFFF000000000000000000000000h
xmmword_116C2380 xmmword 3000000020000000100000000h
I copied and fixed the code to new project, here the source so far:
// ConsoleApplication2.cpp : Ten plik zawiera funkcję „main”. W nim rozpoczyna się i kończy wykonywanie programu.
//
#include <iostream>
#include <xmmintrin.h>
#define BYTE unsigned char;
int probably4LZDecompress(unsigned int maybeOutBuffer, unsigned char* compressedBuffer, int firstDwordMinusOne, int secondDword)
{
unsigned char* v4; // eax@1
unsigned int v5; // edi@1
int v6; // edx@1
int result; // eax@4
unsigned int v8; // ebx@6
size_t v9; // ebx@6
int v10; // esi@7
unsigned int v11; // esi@10
unsigned int v12; // edx@10
unsigned short* v13; // esi@11
int v14; // eax@12
unsigned int* v15; // ebx@12
unsigned int v16; // ecx@14
int v17; // esi@14
int v18; // edi@15
int v19; // ecx@16
unsigned int v20; // edi@20
unsigned int v21; // ebx@20
char* v22; // esi@21
char* v23; // esi@21
int v24; // ecx@22
unsigned char* v25; // edx@23
unsigned char* v26; // edi@26
int v27; // ebx@26
unsigned int v28; // edi@29
int v29; // ecx@29
unsigned int v30; // ebx@32
char v31; // cl@33
unsigned int v32; // edi@36
int* v33; // ebx@36
int v34; // ecx@37
unsigned char* v35; // [sp+0h] [bp-60h]@1
unsigned int v36; // [sp+4h] [bp-5Ch]@1
int v37; // [sp+8h] [bp-58h]@1
int v38; // [sp+Ch] [bp-54h]@1
unsigned int v39; // [sp+10h] [bp-50h]@20
unsigned int v40; // [sp+14h] [bp-4Ch]@1
unsigned int v41; // [sp+14h] [bp-4Ch]@14
char v42; // [sp+18h] [bp-48h]@6
unsigned int v43; // [sp+18h] [bp-48h]@24
__m128 v44; // [sp+1Ch] [bp-44h]@1
__m128 v45; // [sp+2Ch] [bp-34h]@1
int v46; // [sp+3Ch] [bp-24h]@1
int v47; // [sp+40h] [bp-20h]@1
int v48; // [sp+44h] [bp-1Ch]@1
int v49; // [sp+48h] [bp-18h]@1
int v50; // [sp+4Ch] [bp-14h]@1
int v51; // [sp+50h] [bp-10h]@1
int v52; // [sp+54h] [bp-Ch]@1
int v53; // [sp+58h] [bp-8h]@1
v4 = compressedBuffer;
v35 = compressedBuffer;
v5 = maybeOutBuffer;
v36 = maybeOutBuffer;
v6 = (int)& compressedBuffer[firstDwordMinusOne];// first byte of compressed data
v40 = v36;
v38 = (int)& compressedBuffer[firstDwordMinusOne];
v37 = secondDword + v36;
v46 = 0;
v47 = 1;
v48 = 2;
v49 = 1;
v50 = 4;
v51 = 4;
v52 = 4;
v53 = 4;
__m128 xmmword_116C4370;
__m128 xmmword_116C2380;
xmmword_116C4370.m128_u64[1] = 0xFFFFFFFF00000000;
xmmword_116C4370.m128_u64[0] = 0x0000000000000000;
xmmword_116C2380.m128_u64[1] = 0x0000000300000002;
xmmword_116C2380.m128_u64[0] = 0x0000000100000000;
/*_mm_storeu_ps
_mm_storeu_si128((__m128i*) & v44, _mm_load_si128((const __m128i*) & xmmword_116C4370));
_mm_storeu_si128((__m128i*) & v45, _mm_load_si128((const __m128i*) & xmmword_116C2380));*/
//looks like _mm_storeu_si128 stores to &v44, and _mm_load_si128 loads _m128, so that's just v44=xmmword...
v44 = xmmword_116C4370;
v45 = xmmword_116C2380;
if (secondDword)
{
while (1)
{
v8 = *v4++;
v42 = v8;
v9 = v8 >> 4;
if (v9 == 15)
{
do
{
v10 = *v4++;
v9 += v10;
} while ((unsigned int)v4 < v6 - 15 && v10 == 255);
v5 = v40;
if (v9 + v40 < v40 || &v4[v9] < v4)
return v35 - v4 - 1;
}
v11 = secondDword + v36;
v12 = v9 + v5;
if (v9 + v5 > v37 - 12)
goto LABEL_41;
v13 = (unsigned short*)& v4[v9];
if ((unsigned int)& v4[v9] > v38 - 8)
{
v11 = secondDword + v36;
LABEL_41:
if (&v4[v9] == (unsigned char*)v38 && v12 <= v11)
{
memcpy((void*)v5, v4, v9);
return v5 + v9 - v36;
}
return v35 - v4 - 1;
}
v14 = (int)& v4[-v5];
v15 = (unsigned int*)(v9 + v5);
do
{
*(unsigned int*)v5 = *(unsigned int*)(v14 + v5);
*(unsigned int*)(v5 + 4) = *(unsigned int*)(v14 + v5 + 4);
v5 += 8;
} while (v5 < v12);
v16 = *v13;
v4 = (unsigned char*)v13 + 1;
v17 = v12 - v16;
v41 = v16;
if (v12 - v16 < v36)
return v35 - v4 - 1;
v18 = v42 & 0xF;
*v15 = v16;
if (v18 == 15)
{
while (1)
{
v19 = *v4++;
if ((unsigned int)v4 > v38 - 5)
return v35 - v4 - 1;
v18 += v19;
if (v19 != 255)
{
if ((unsigned int*)((char*)v15 + v18) < v15)
return v35 - v4 - 1;
v16 = v41;
break;
}
}
}
v20 = v18 + 4;
v21 = (unsigned int)v15 + v20;
v39 = v21;
if (v16 >= 8)
{
*(unsigned int*)v12 = *(unsigned int*)v17;
v24 = *(unsigned int*)(v17 + 4);
v23 = (char*)(v17 + 8);
*(unsigned int*)(v12 + 4) = v24;
}
else
{
*(unsigned char*)v12 = *(unsigned char*)v17;
*(unsigned char*)(v12 + 1) = *(unsigned char*)(v17 + 1);
*(unsigned char*)(v12 + 2) = *(unsigned char*)(v17 + 2);
*(unsigned char*)(v12 + 3) = *(unsigned char*)(v17 + 3);
v22 = (char*)(*(&v46 + v41) + v17);
*(unsigned int*)(v12 + 4) = *(unsigned int*)v22;
v23 = &v22[-*((unsigned int*)& v44 + v41)];
}
v25 = (unsigned char*)(v12 + 8);
if (v21 <= v37 - 12)
break;
v43 = v37 - 7;
if (v21 > v37 - 5)
return v35 - v4 - 1;
if ((unsigned int)v25 < v37 - 7)
{
v26 = v25;
v27 = (unsigned char)v23 - (unsigned char)v25;
do
{
*(unsigned int*)v26 = *(unsigned int*)& v26[v27];
*((unsigned int*)v26 + 1) = *(unsigned int*)& v26[v27 + 4];
v26 += 8;
} while ((unsigned int)v26 < v43);
v21 = v39;
v23 += v43 - (unsigned int)v25;
v25 = (unsigned char*)(v37 - 7);
}
v28 = 0;
v29 = v21 - (unsigned int)v25;
if ((unsigned int)v25 > v21)
v29 = 0;
if (v29)
{
v30 = v29;
do
{
v31 = *v23++;
++v28;
*v25++ = v31;
} while (v28 < v30);
v5 = v39;
v6 = v38;
v40 = v39;
}
else
{
LABEL_39:
v6 = v38;
v5 = v21;
v40 = v21;
}
}
*(unsigned int*)v25 = *(unsigned int*)v23;
*((unsigned int*)v25 + 1) = *((unsigned int*)v23 + 1);
if (v20 > 0x10)
{
v32 = (unsigned int)(v25 + 8);
v33 = (int*)(v23 + 8);
do
{
v34 = *v33;
v33 += 2;
*(unsigned int*)v32 = v34;
*(unsigned int*)(v32 + 4) = *(v33 - 1);
v32 += 8;
} while (v32 < v39);
v21 = v39;
}
goto LABEL_39;
}
if (firstDwordMinusOne != 1 || *compressedBuffer)
result = -1;
else
result = 0;
return result;
}
int main()
{
FILE* f = fopen("D:\\ff8_R\\EXPORT\\world_fs_export\\mapdata\\mapdata.fl", "rb");
fseek(f, 0, 2);
auto position = ftell(f);
rewind(f);
unsigned int* buffer = (unsigned int*)calloc(position, 1);
fread(buffer, sizeof(char), position, f);
fclose(f);
unsigned int firstDword = (*buffer) - 8;
unsigned int secondDword = *(buffer + 2);
unsigned char * buffer_ = (unsigned char*)(buffer + 3);
unsigned int* outBuffer = (unsigned int*)calloc(firstDword, 1);
unsigned int firstParm = (unsigned int)outBuffer;
int testing = probably4LZDecompress((unsigned int)firstParm, buffer_, firstDword, secondDword);
}
and it didn't quite worked well on line 119- I had mismatch with data which I had on demastered code.
It turns out the casual field.fs contains compression method of ==2
there are some issues:
in field.fi the mapdata/maplist has 0x25 uncompressed size (that is true actually)- but absolute offset points to 0x25- that's true, but it's way after the header. You have to seek -4 to get secondDword which will give 0x25 (37) - this is how much data should be read- currently Deling reads the size correctly- but with the header missing 8 bytes afterward
So to summarize:
field.fl ->
c:\\ff8\\data\\x\\field\\mapdata.fl is entry 2
field.fi entry 2 (so 12) - that is correct:
uncompressed size - 37
location in FS file - 25
compression flag - 2
If we jump to 0x19- we get the DWORD+4ZL... this is wrong (more or less)
now Deling exports by size of uncompressed- that's okay, but there's 12bytes header data
So the content reads 37 bytes, while it should read 37+12 = 49