Last active
February 11, 2017 21:40
-
-
Save msikma/cd504be24e051dedfdba79f33b4d0a6d to your computer and use it in GitHub Desktop.
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
// 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