Last active
July 16, 2024 21:01
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------------------ | |
//--- 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