Skip to content

Instantly share code, notes, and snippets.

@segfault-bilibili
Last active July 16, 2024 21:01
Show Gist options
  • Save segfault-bilibili/5b0e2698e17100b0ba3f35912518b672 to your computer and use it in GitHub Desktop.
Save segfault-bilibili/5b0e2698e17100b0ba3f35912518b672 to your computer and use it in GitHub Desktop.
Decode *.vfxb files in Magia Record's resource/image_native/mini/anime_v2
//------------------------------------------------
//--- 010 Editor v14.0.1 Binary Template
//
// File: vfxb.bt
// Authors: segfault-bilibili
// Version: 0.1
// Purpose: Decode *.vfxb files (and decompessed LZ4 data) in Magia Record's resource/image_native/mini/anime_v2
// Category: Game
// File Mask: *.vfxb
// ID Bytes: 64 69 75 47
// History:
// 0.1: 20240716
//------------------------------------------------
typedef char typeLE[4]<read = typeLERead, write = typeLEWrite>;
string typeLERead(typeLE v)
{
local int max = Strlen(v);
local char type[4];
local int i;
for (i = 0; i < max; i++)
{
type[i] = v[max - 1 - i];
}
return type;
}
void typeLEWrite(typeLE v, string s)
{
local int i = 0;
local int max = Strlen(s);
if (max > 4)
{
max = 4;
}
for (i = 0; i < 4; i++)
{
if (i < max)
{
v[i] = s[max - 1 - i];
}
else
{
v[i] = 0;
}
}
}
struct TLVStruct;
string TLVRead(TLVStruct &chunk)
{
string s;
int i = 0;
SPrintf(s, "%s", typeLERead(chunk.type));
if (chunk.len > 8)
{
char nestedType[4];
for (i = 0; i < 4; i++)
{
nestedType[i] = chunk.rawData[4 - 1 - i];
}
if (nestedType == "Name")
{
SPrintf(s, "%s [%s]", s, chunk.nestedTLV[0].name);
}
}
return s;
}
int paddingLen(int len)
{
return (((len - 1) / 4) + 1) * 4 - len;
}
int incompleteTLV()
{
local int remaining = FileSize() - FTell();
return (remaining > 0 && remaining < 8);
}
typedef struct
{
typeLE type;
uint len;
local int canHaveNestedTLV = false;
switch (typeLERead(type))
{
case "CMPS":
{
char nestedType[4];
uint rev;
uint decompressed_len;
uint compressed_len;
uchar lz4_compressed_data[compressed_len];
break;
}
case "Name":
{
char name[len];
break;
}
case "GitH":
{
char git_hash[len];
break;
}
case "UniC":
{
uint unitCount;
break;
}
case "Unit":
case "EMsk":
case "Pack":
case "Ptcl":
case "Col":
case "Rgba":
case "Key":
case "Rand":
case "Brig":
case "Red":
case "Gree":
case "Blue":
case "Alph":
case "Cons":
case "Pos":
case "X":
case "Y":
case "Z":
case "TCo1":
case "DatP":
canHaveNestedTLV = true;
default:
{
local int next_len = 0;
local int remaining = len;
local int nestedTLVLen = 0;
if (canHaveNestedTLV)
while (remaining >= 8)
{
next_len = ReadUInt(FTell() + 4);
if (8 + next_len <= FileSize() - FTell())
{
TLVStruct nestedTLV;
remaining -= 8 + nestedTLV.len + paddingLen(nestedTLV.len);
}
else
{
break;
}
}
if (remaining > 0)
{
FSkip(remaining);
}
}
}
if (len > 0)
{
FSeek(FTell() - len);
uchar rawData[len];
}
if (paddingLen(len) > 0)
{
uchar padding[paddingLen(len)];
}
} TLVStruct<read = TLVRead>;
LittleEndian();
while (!FEof())
{
TLVStruct TLV;
if (incompleteTLV())
{
uchar unexpected[FileSize() - FTell()];
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment