Skip to content

Instantly share code, notes, and snippets.

@msikma
Last active February 11, 2017 21:40
Show Gist options
  • Save msikma/cd504be24e051dedfdba79f33b4d0a6d to your computer and use it in GitHub Desktop.
Save msikma/cd504be24e051dedfdba79f33b4d0a6d to your computer and use it in GitHub Desktop.
// found here: http://archive.rebeccablacktech.com/g/thread/39482493
// This supposedly extracts bmp files from microsoft comic chat AVB files
// ----
// AVBuster.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <cstdlib>
#include <cstring>
#include "zlib.h"
#pragma pack(push, 1)
// type 0x8181 = avb header
typedef struct _AVB_HEADER
{
short type;
short unk;
} AVB_HEADER, *PAVB_HEADER;
// type 1 = null-terminated string
// type 2 = unknown, 2 bytes
typedef struct _AVB_UNK_HEADER_2
{
short unk;
} AVB_UNK_HEADER_2, *PAVB_UNK_HEADER_2;
// type 6 = zlib compressed bmp
typedef struct _AVB_ZLIB_BMP_HEADER
{
BITMAPINFOHEADER header;
int len_decomp;
int len_comp;
} AVB_ZLIB_BMP_HEADER, *PAVB_ZLIB_BMP_HEADER;
typedef struct _AVB_ZLIB_GESTURE_HEADER
{
short colors_magic;
short colors_length;
short num_colors;
} AVB_ZLIB_GESTURE_HEADER, *PAVB_ZLIB_GESTURE_HEADER;
// type 8 = unknown, 2 bytes
typedef struct _AVB_UNK_HEADER_8
{
short unk;
} AVB_UNK_HEADER_8, *PAVB_UNK_HEADER_8;
// gesture list entry
typedef struct _AVB_GESTURE_ENTRY
{
int gestureOffset; // 4
int unknownOffset; // 8
int poseOffset; // 12
short unk1; // 14
int facePosition; // 16
short unk2; // 20
short unk3; // 22
short unk4; // 24
char unk5; // 25
} AVB_GESTURE_ENTRY, *PAVB_GESTURE_ENTRY;
// type 0xA = unknown list
typedef struct _AVB_UNK_HEADER_A
{
short count;
} AVB_UNK_HEADER_A, *PAVB_UNK_HEADER_A;
typedef struct _AVB_UNK_HEADER_A_ENTRY
{
AVB_GESTURE_ENTRY ent;
char unk[8];
} AVB_UNK_HEADER_A_ENTRY, *PAVB_UNK_HEADER_A_ENTRY;
// type 0xB/0xC = gesture list
typedef struct _AVB_GESTURE_LIST
{
short count;
} AVB_GESTURE_LIST, *PAVB_GESTURE_LIST;
// type 0x100 = unknown
typedef struct _AVB_UNK_HEADER_100
{
short size;
short unk1;
short unk2;
short unk3;
} AVB_UNK_HEADER_100, *PAVB_UNK_HEADER_100;
// type 0x103 & 0x104 = string entry
typedef struct _AVB_STRING_HEADER
{
short size;
} AVB_STRING_HEADER, *PAVB_STRING_HEADER;
// type 0x106 = url protection header
typedef struct _AVB_PROTECT_HEADER
{
short size;
bool download_protected;
} AVB_PROTECT_HEADER, *PAVB_PROTECT_HEADER;
#pragma pack(pop)
int _tmain(int argc, _TCHAR* argv[])
{
printf("AVBuster, extractor for MS Comic Chat AVB and BGB files\nnote: official MS chars are unsupported due to them using a different compression method\n");
if(argc <= 1)
{
printf("usage: avbuster.exe character.avb\n");
return 0;
}
FILE* fp;
if(_wfopen_s(&fp, argv[1], L"rb") != 0)
{
printf("failed to open file!\n");
return 0;
}
fseek(fp, 0L, SEEK_SET);
bool headerRead = false;
AVB_HEADER header;
bool nameRead = false;
char name[256];
bool header8Read = false;
AVB_UNK_HEADER_8 header8;
bool header2Read = false;
AVB_UNK_HEADER_2 header2;
bool header100Read = false;
AVB_UNK_HEADER_100 header100;
bool header103Read = false;
AVB_STRING_HEADER header103;
char author[256];
bool header104Read = false;
AVB_STRING_HEADER header104;
char download[256];
bool header105Read = false;
AVB_STRING_HEADER header105;
char download2[256];
bool header106Read = false;
AVB_PROTECT_HEADER header106;
bool header107Read = false;
AVB_STRING_HEADER header107;
bool hasGestures = false;
AVB_UNK_HEADER_A headerA;
AVB_UNK_HEADER_A_ENTRY headerAEntries[256];
bool headerCisB = false;
AVB_GESTURE_LIST headerC;
AVB_GESTURE_ENTRY headerPoses[256];
AVB_ZLIB_GESTURE_HEADER gestureHdr[256];
memset(&gestureHdr, 0, sizeof(AVB_ZLIB_GESTURE_HEADER) * 256);
AVB_ZLIB_BMP_HEADER gestureBmpHdr[256];
AVB_ZLIB_BMP_HEADER poseBmp[256];
int zlibOffset = 0;
memset(&header, 0, sizeof(AVB_HEADER));
int current = 0;
int ni = 0;
while(true)
{
if(current > 0 && header.type == 0)
{
printf("invalid file?\n");
return 0;
}
unsigned short type;
fread(&type, sizeof(short), 1, fp);
printf("Type %x\n", type);
if(type == 6)
break;
switch(type)
{
case 0x8181:
fread(&header, sizeof(AVB_HEADER), 1, fp);
if(header.type != 1)
printf("** new header type 0x%x\n", header.type);
if(header.unk != 2)
printf("** new header unk 0x%x\n", header.unk);
if(header.type == 2)
{
printf("** unsupported type 2 (OEM/Microsoft?)\n");
return 0;
}
headerRead = true;
break;
case 1:
while(true)
{
fread(&name[ni], 1, 1, fp);
if(name[ni] == 0) break;
ni++;
}
nameRead = true;
ni = 0;
break;
case 2:
fread(&header2, sizeof(AVB_UNK_HEADER_2), 1, fp);
if(header2.unk != 5)
{
printf("** new header2: unk 0x%x\n", header2.unk);
}
header2 = header2;
header2Read = true;
break;
case 8:
fread(&header8, sizeof(AVB_UNK_HEADER_8), 1, fp);
if(header8.unk != 1)
{
printf("** new header8: unk 0x%x\n", header8.unk);
}
header8 = header8;
header8Read = true;
break;
case 0xA:
fread(&headerA, sizeof(AVB_UNK_HEADER_A), 1, fp);
for(int i = 0; i < headerA.count; i++)
fread(&headerAEntries[i], sizeof(AVB_UNK_HEADER_A_ENTRY), 1, fp);
break;
case 0xB:
headerCisB = true;
case 0xC:
hasGestures = true;
fread(&headerC, sizeof(AVB_GESTURE_LIST), 1, fp);
for(int i = 0; i < headerC.count; i++)
{
fread(&headerPoses[i], sizeof(AVB_GESTURE_ENTRY), 1, fp);
}
break;
case 0x100:
fread(&header100, sizeof(AVB_UNK_HEADER_100), 1, fp);
if(header100.size != 6)
printf("** new header100 size: 0x%x\n", header100.size);
if(header100.unk1 != 0x60)
printf("** new header100 unk1: 0x%x\n", header100.unk1);
if(header100.unk2 != 0)
printf("** new header100 unk2: 0x%x\n", header100.unk2);
if(header100.unk3 != 0x201)
printf("** new header100 unk3: 0x%x\n", header100.unk3);
header100 = header100;
header100Read = true;
break;
case 0x103:
fread(&header103, sizeof(AVB_STRING_HEADER), 1, fp);
fread(&author, header103.size, 1, fp);
header103Read = true;
break;
case 0x104:
fread(&header104, sizeof(AVB_STRING_HEADER), 1, fp);
fread(&download, header104.size, 1, fp);
header104Read = true;
break;
case 0x105:
fread(&header105, sizeof(AVB_STRING_HEADER), 1, fp);
fread(&download2, header105.size, 1, fp);
header105Read = true;
break;
case 0x106:
fread(&header106, sizeof(AVB_PROTECT_HEADER), 1, fp);
if(header106.size != 1)
printf("** new header106 size: 0x%x\n", header106.size);
if(header106.download_protected != 1)
printf("** new header106 download_protected: 0x%x\n", header106.download_protected);
header106 = header106;
header106Read = true;
break;
case 0x107:
fread(&header104, sizeof(AVB_STRING_HEADER), 1, fp);
fread(&zlibOffset, 4, 1, fp);
header107Read = true;
break;
case 0x81:
if(!headerRead)
{
printf("** older header detected, use avbcvt / bmpbgb to convert to 2.5 first\n");
return 0;
}
default:
printf("** new header # 0x%x\n", type);
if(type >= 0x100)
{
unsigned short size = 0;
fread(&size, 2, 1, fp);
printf("** header has size field, skipping 0x%x bytes...\n", size);
fseek(fp, size, SEEK_CUR);
}
break;
}
current++;
}
if(nameRead)
printf("name: %s\n", name);
if(header103Read)
printf("author: %s\n", author);
if(header104Read)
printf("download: %s\n", download);
if(header105Read)
printf("download2: %s\n", download2);
if(headerRead)
printf("header type: 0x%x unk: 0x%x\n", header.type, header.unk);
if(header2Read)
printf("header2 unk: 0x%x\n", header2.unk);
if(header8Read)
printf("header8 unk: 0x%x\n", header8.unk);
if(header100Read)
printf("header100 size: 0x%x unk1: 0x%x unk2: 0x%x unk3: 0x%x\n", header100.size, header100.unk1, header100.unk2, header100.unk3);
if(header106Read)
printf("header106 size: 0x%x download_protected: 0x%x\n", header106.size, header106.download_protected);
if(header107Read)
printf("header107 zlib offset: 0x%x\n", zlibOffset);
if(hasGestures)
{
printf("gesture count: 0x%x\n", headerC.count);
for(int i = 0; i < headerC.count; i++)
{
printf("== Gesture %d\n", i);
printf(" gestureOffset: 0x%x unknownOffset: 0x%x poseOffset: 0x%x\n unk1: 0x%x facePosition: 0x%x unk2: 0x%x\n unk3: 0x%x unk4: 0x%x unk5: 0x%x\n", headerPoses[i].gestureOffset, headerPoses[i].unknownOffset, headerPoses[i].poseOffset, headerPoses[i].unk1, headerPoses[i].facePosition, headerPoses[i].unk2, headerPoses[i].unk3, headerPoses[i].unk4, headerPoses[i].unk5);
}
}
unsigned char check = 0;
unsigned char chk2 = 0;
if(!hasGestures)
{
AVB_ZLIB_GESTURE_HEADER hdr;
AVB_ZLIB_BMP_HEADER bmpHdr;
fread(&hdr, sizeof(AVB_ZLIB_GESTURE_HEADER), 1, fp);
void* colors = 0;
if(hdr.num_colors > 0)
{
colors = malloc(hdr.num_colors * 4);
memset(colors, 0, hdr.num_colors * 4);
for(int y = 0; y < hdr.num_colors; y++)
fread((BYTE*)colors + (y * 4), 3, 1, fp);
}
fread(&bmpHdr, sizeof(AVB_ZLIB_BMP_HEADER), 1, fp);
void* compressed = malloc(bmpHdr.len_comp);
fread(compressed, bmpHdr.len_comp, 1, fp);
// create bmp and decompress
BITMAPFILEHEADER bmp;
memset(&bmp, 0, sizeof(BITMAPFILEHEADER));
bmp.bfType = 0x4D42;
bmp.bfOffBits = 0x36 + (hdr.num_colors * 4);
bmp.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (hdr.num_colors * 4) + bmpHdr.len_decomp;
void* dest = malloc(bmpHdr.len_decomp);
int size = bmpHdr.len_decomp;
int ret = uncompress((Bytef*)dest, (uLongf*)&size, (Bytef*)compressed, bmpHdr.len_comp);
if(ret != 0) return 0;
TCHAR fname[256];
swprintf(fname, L"%ws_data\\", argv[1]);
_tmkdir(fname);
swprintf(fname, L"%ws_data\\bg.bmp", argv[1]);
FILE* file = _wfopen(fname, L"wb");
fwrite(&bmp, sizeof(BITMAPFILEHEADER), 1, file);
fwrite(&bmpHdr.header, sizeof(BITMAPINFOHEADER), 1, file);
if(colors)
fwrite(colors, hdr.num_colors * 4, 1, file);
fwrite(dest, size, 1, file);
fclose(file);
if(colors) free(colors);
dest = dest;
}
else
for(int i = 0; i < headerC.count; i++)
{
void* colors = 0;
//fseek(fp, headerAEntries[i].ent.gestureOffset + zlibOffset, SEEK_SET);
fseek(fp, headerPoses[i].gestureOffset + zlibOffset, SEEK_SET);
if(!headerCisB)
{
fread(&gestureHdr[i], sizeof(AVB_ZLIB_GESTURE_HEADER), 1, fp);
if(gestureHdr[i].num_colors > 0)
{
colors = malloc(gestureHdr[i].num_colors * 4);
memset(colors, 0, gestureHdr[i].num_colors * 4);
for(int y = 0; y < gestureHdr[i].num_colors; y++)
fread(((BYTE*)colors + (y * 4)), 3, 1, fp);
}
}
fread(&gestureBmpHdr[i], sizeof(AVB_ZLIB_BMP_HEADER), 1, fp);
fread(&check, 1, 1, fp);
fread(&chk2, 1, 1, fp);
fseek(fp, -2, SEEK_CUR);
if(check != 0x78 || chk2 != 0xDA)
{
/*printf("** scanning for zlib data...\n");
int offset = 0;
while(true)
{
offset++;
fread(&check, 1, 1, fp);
if(check != 0x78) continue;
fread(&chk2, 1, 1, fp);
if(chk2 != 0xDA)
{
fseek(fp, -1, SEEK_CUR);
continue;
}
break;
}
offset -= 1;
printf("found at %x\n", offset);
fseek(fp, headerPoses[i].gestureOffset + zlibOffset + offset, SEEK_SET);
fread(&gestureHdr[i], sizeof(AVB_ZLIB_GESTURE_HEADER), 1, fp);
if(gestureHdr[i].num_colors > 0)
{
if(colors) free(colors);
colors = malloc(gestureHdr[i].num_colors * 4);
for(int y = 0; y < gestureHdr[i].num_colors; y++)
fread(&colors + (y * 4), 3, 1, fp);
}
fread(&gestureBmpHdr[i], sizeof(AVB_ZLIB_BMP_HEADER), 1, fp);
//return 0;*/
printf("** zlib data not found and searching is deprecated, exiting\n");
return 0;
}
void* compressed = malloc(gestureBmpHdr[i].len_comp);
fread(compressed, gestureBmpHdr[i].len_comp, 1, fp);
// create bmp and decompress
BITMAPFILEHEADER bmp;
memset(&bmp, 0, sizeof(BITMAPFILEHEADER));
bmp.bfType = 0x4D42;
bmp.bfOffBits = 36 + (gestureHdr[i].num_colors * 4);
bmp.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (gestureHdr[i].num_colors * 4) + gestureBmpHdr[i].len_decomp;
void* dest = malloc(gestureBmpHdr[i].len_decomp);
int size = gestureBmpHdr[i].len_decomp;
int ret = uncompress((Bytef*)dest, (uLongf*)&size, (Bytef*)compressed, gestureBmpHdr[i].len_comp);
if(ret != 0) continue;
TCHAR fname[256];
swprintf(fname, L"%ws_data\\", argv[1]);
_tmkdir(fname);
swprintf(fname, L"%ws_data\\%d.bmp", argv[1], i);
FILE* file = _wfopen(fname, L"wb");
fwrite(&bmp, sizeof(BITMAPFILEHEADER), 1, file);
fwrite(&gestureBmpHdr[i].header, sizeof(BITMAPINFOHEADER), 1, file);
if(colors)
fwrite(colors, gestureHdr[i].num_colors * 4, 1, file);
fwrite(dest, size, 1, file);
fclose(file);
if(colors) free(colors);
dest = dest;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment