Advertisement
Guest User

Microsoft Comic Chat AVB/BGB extractor (with notes)

a guest
Jan 11th, 2014
321
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.11 KB | None | 0 0
  1. // ===================================
  2. // AVBuster 0.1 - A tool to extract Microsoft Comic Chat 2.5 AVB/BGB files into bitmaps
  3. // ===================================
  4. // Written 16 years after Comic Chat final version was released.
  5. // The code might be ugly, but it works.
  6.  
  7. // ===================================
  8. // AVB/BGB format
  9. // ===================================
  10. // Format is similar to RIFF, data is stored in chunks which are identified by a 2 byte short
  11. // If the chunk ID >= 0x100 then the next 2 byte short is a field which says the size of the chunk (excluding the ID and size fields)
  12. // Bitmaps are stored with their BITMAPFILEHEADER removed (regenerated at runtime), their BITMAPINFOHEADER stored in the AVB_ZLIB_BMP_HEADER struct/chunk and their image data compressed with Zlib.
  13. // Older Comic Chat AVBs use a slightly different format for structs and don't compress the bitmap data, instead it's stored fully in the file
  14.  
  15. // ===================================
  16. // Notes
  17. // ===================================
  18. // This extractor doesn't support older AVBs as they can be easily converted to the new format with AVBCVT/BMPBGB (make sure you rebuild AVBCVT's PE header with CFF explorer first otherwise it won't run)
  19. // This tool also doesn't extract official MS AVBs (BGB files extract fine though)
  20. // They're stored in a slightly different format which I looked into and found pointless to add support for since they're completely different to custom characters.
  21.  
  22. #include "stdafx.h"
  23. #include <cstdlib>
  24. #include <cstring>
  25. #include "zlib.h"
  26.  
  27. #pragma pack(push, 1)
  28.  
  29. // type 0x8181 = avb header
  30. typedef struct _AVB_HEADER
  31. {
  32.     short type;
  33.     short unk;
  34. } AVB_HEADER, *PAVB_HEADER;
  35.  
  36. // type 1 = null-terminated string
  37.  
  38. // type 2 = unknown, 2 bytes
  39. typedef struct _AVB_UNK_HEADER_2
  40. {
  41.     short unk;
  42. } AVB_UNK_HEADER_2, *PAVB_UNK_HEADER_2;
  43.  
  44. // type 6 = zlib compressed bmp
  45. typedef struct _AVB_ZLIB_BMP_HEADER
  46. {
  47.     BITMAPINFOHEADER header;
  48.     int len_decomp;
  49.     int len_comp;
  50. } AVB_ZLIB_BMP_HEADER, *PAVB_ZLIB_BMP_HEADER;
  51.  
  52. typedef struct _AVB_ZLIB_GESTURE_HEADER
  53. {
  54.     short colors_magic;
  55.     short colors_length;
  56.     short num_colors;
  57. } AVB_ZLIB_GESTURE_HEADER, *PAVB_ZLIB_GESTURE_HEADER;
  58.  
  59. // type 8 = unknown, 2 bytes
  60. typedef struct _AVB_UNK_HEADER_8
  61. {
  62.     short unk;
  63. } AVB_UNK_HEADER_8, *PAVB_UNK_HEADER_8;
  64.  
  65.  
  66. // gesture list entry
  67. typedef struct _AVB_GESTURE_ENTRY
  68. {
  69.     int gestureOffset; // 4
  70.     int unknownOffset; // 8
  71.     int poseOffset; // 12
  72.     short unk1; // 14
  73.     int facePosition; // 16
  74.     short unk2; // 20
  75.     short unk3; // 22
  76.     short unk4; // 24
  77.     char unk5; // 25
  78. } AVB_GESTURE_ENTRY, *PAVB_GESTURE_ENTRY;
  79.  
  80. // type 0xA = unknown list
  81. typedef struct _AVB_UNK_HEADER_A
  82. {
  83.     short count;
  84. } AVB_UNK_HEADER_A, *PAVB_UNK_HEADER_A;
  85.  
  86. typedef struct _AVB_UNK_HEADER_A_ENTRY
  87. {
  88.     AVB_GESTURE_ENTRY ent;
  89.     char unk[8];
  90. } AVB_UNK_HEADER_A_ENTRY, *PAVB_UNK_HEADER_A_ENTRY;
  91.  
  92. // type 0xB/0xC = gesture list
  93. typedef struct _AVB_GESTURE_LIST
  94. {
  95.     short count;
  96. } AVB_GESTURE_LIST, *PAVB_GESTURE_LIST;
  97.  
  98. // type 0x100 = unknown
  99. typedef struct _AVB_UNK_HEADER_100
  100. {
  101.     short size;
  102.     short unk1;
  103.     short unk2;
  104.     short unk3;
  105. } AVB_UNK_HEADER_100, *PAVB_UNK_HEADER_100;
  106.  
  107. // type 0x103 & 0x104 = string entry
  108. typedef struct _AVB_STRING_HEADER
  109. {
  110.     short size;
  111. } AVB_STRING_HEADER, *PAVB_STRING_HEADER;
  112.  
  113. // type 0x106 = url protection header
  114. typedef struct _AVB_PROTECT_HEADER
  115. {
  116.     short size;
  117.     bool download_protected;
  118. } AVB_PROTECT_HEADER, *PAVB_PROTECT_HEADER;
  119.  
  120. #pragma pack(pop)
  121.  
  122.  
  123. int _tmain(int argc, _TCHAR* argv[])
  124. {
  125.     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");
  126.     if(argc <= 1)
  127.     {
  128.         printf("usage: avbuster.exe character.avb\n");
  129.         return 0;
  130.     }
  131.  
  132.     FILE* fp;
  133.     if(_wfopen_s(&fp, argv[1], L"rb") != 0)
  134.     {
  135.         printf("failed to open file!\n");
  136.         return 0;
  137.     }
  138.  
  139.     fseek(fp, 0L, SEEK_SET);
  140.  
  141.     bool headerRead = false;
  142.     AVB_HEADER header;
  143.     bool nameRead = false;
  144.     char name[256];
  145.     bool header8Read = false;
  146.     AVB_UNK_HEADER_8 header8;
  147.     bool header2Read = false;
  148.     AVB_UNK_HEADER_2 header2;
  149.     bool header100Read = false;
  150.     AVB_UNK_HEADER_100 header100;
  151.     bool header103Read = false;
  152.     AVB_STRING_HEADER header103;
  153.     char author[256];
  154.    
  155.     bool header104Read = false;
  156.     AVB_STRING_HEADER header104;
  157.     char download[256];
  158.     bool header105Read = false;
  159.     AVB_STRING_HEADER header105;
  160.     char download2[256];
  161.     bool header106Read = false;
  162.     AVB_PROTECT_HEADER header106;
  163.     bool header107Read = false;
  164.     AVB_STRING_HEADER header107;
  165.  
  166.     bool hasGestures = false;
  167.     AVB_UNK_HEADER_A headerA;
  168.     AVB_UNK_HEADER_A_ENTRY headerAEntries[256];
  169.     bool headerCisB = false;
  170.     AVB_GESTURE_LIST headerC;
  171.     AVB_GESTURE_ENTRY headerPoses[256];
  172.     AVB_ZLIB_GESTURE_HEADER gestureHdr[256];
  173.     memset(&gestureHdr, 0, sizeof(AVB_ZLIB_GESTURE_HEADER) * 256);
  174.  
  175.     AVB_ZLIB_BMP_HEADER gestureBmpHdr[256];
  176.     AVB_ZLIB_BMP_HEADER poseBmp[256];
  177.  
  178.     int zlibOffset = 0;
  179.  
  180.     memset(&header, 0, sizeof(AVB_HEADER));
  181.     int current = 0;
  182.     int ni = 0;
  183.     while(true)
  184.     {
  185.         if(current > 0 && header.type == 0)
  186.         {
  187.             printf("invalid file?\n");
  188.             return 0;
  189.         }
  190.         unsigned short type;
  191.         fread(&type, sizeof(short), 1, fp);
  192.         printf("Type %x\n", type);
  193.         if(type == 6)
  194.             break;
  195.         switch(type)
  196.         {
  197.         case 0x8181:
  198.             fread(&header, sizeof(AVB_HEADER), 1, fp);
  199.             if(header.type != 1)
  200.                 printf("** new header type 0x%x\n", header.type);
  201.             if(header.unk != 2)
  202.                 printf("** new header unk 0x%x\n", header.unk);
  203.  
  204.             if(header.type == 2)
  205.             {
  206.                 printf("** unsupported type 2 (OEM/Microsoft?)\n");
  207.                 return 0;
  208.             }
  209.             headerRead = true;
  210.             break;
  211.         case 1:
  212.             while(true)
  213.             {
  214.                 fread(&name[ni], 1, 1, fp);
  215.                 if(name[ni] == 0) break;
  216.                 ni++;
  217.             }
  218.             nameRead = true;
  219.             ni = 0;
  220.             break;
  221.         case 2:
  222.             fread(&header2, sizeof(AVB_UNK_HEADER_2), 1, fp);
  223.             if(header2.unk != 5)
  224.             {
  225.                 printf("** new header2: unk 0x%x\n", header2.unk);
  226.             }
  227.             header2 = header2;
  228.             header2Read = true;
  229.             break;
  230.         case 8:
  231.             fread(&header8, sizeof(AVB_UNK_HEADER_8), 1, fp);
  232.             if(header8.unk != 1)
  233.             {
  234.                 printf("** new header8: unk 0x%x\n", header8.unk);
  235.             }
  236.             header8 = header8;
  237.             header8Read = true;
  238.             break;
  239.         case 0xA:
  240.             fread(&headerA, sizeof(AVB_UNK_HEADER_A), 1, fp);
  241.             for(int i = 0; i < headerA.count; i++)
  242.                 fread(&headerAEntries[i], sizeof(AVB_UNK_HEADER_A_ENTRY), 1, fp);
  243.             break;
  244.         case 0xB:
  245.             headerCisB = true;
  246.         case 0xC:
  247.             hasGestures = true;
  248.             fread(&headerC, sizeof(AVB_GESTURE_LIST), 1, fp);
  249.             for(int i = 0; i < headerC.count; i++)
  250.             {
  251.                 fread(&headerPoses[i], sizeof(AVB_GESTURE_ENTRY), 1, fp);
  252.             }
  253.             break;
  254.         case 0x100:
  255.             fread(&header100, sizeof(AVB_UNK_HEADER_100), 1, fp);
  256.             if(header100.size != 6)
  257.                 printf("** new header100 size: 0x%x\n", header100.size);
  258.             if(header100.unk1 != 0x60)
  259.                 printf("** new header100 unk1: 0x%x\n", header100.unk1);
  260.             if(header100.unk2 != 0)
  261.                 printf("** new header100 unk2: 0x%x\n", header100.unk2);
  262.             if(header100.unk3 != 0x201)
  263.                 printf("** new header100 unk3: 0x%x\n", header100.unk3);
  264.             header100 = header100;
  265.             header100Read = true;
  266.             break;
  267.         case 0x103:
  268.             fread(&header103, sizeof(AVB_STRING_HEADER), 1, fp);
  269.             fread(&author, header103.size, 1, fp);
  270.             header103Read = true;
  271.             break;
  272.         case 0x104:
  273.             fread(&header104, sizeof(AVB_STRING_HEADER), 1, fp);
  274.             fread(&download, header104.size, 1, fp);
  275.             header104Read = true;
  276.             break;
  277.         case 0x105:
  278.             fread(&header105, sizeof(AVB_STRING_HEADER), 1, fp);
  279.             fread(&download2, header105.size, 1, fp);
  280.             header105Read = true;
  281.             break;
  282.         case 0x106:
  283.             fread(&header106, sizeof(AVB_PROTECT_HEADER), 1, fp);
  284.             if(header106.size != 1)
  285.                 printf("** new header106 size: 0x%x\n", header106.size);
  286.             if(header106.download_protected != 1)
  287.                 printf("** new header106 download_protected: 0x%x\n", header106.download_protected);
  288.             header106 = header106;
  289.             header106Read = true;
  290.             break;
  291.         case 0x107:
  292.             fread(&header104, sizeof(AVB_STRING_HEADER), 1, fp);
  293.             fread(&zlibOffset, 4, 1, fp);
  294.             header107Read = true;
  295.             break;
  296.  
  297.            
  298.         case 0x81:
  299.             if(!headerRead)
  300.             {
  301.                 printf("** older header detected, use avbcvt / bmpbgb to convert to 2.5 first\n");
  302.                 return 0;
  303.             }
  304.         default:
  305.             printf("** new header # 0x%x\n", type);
  306.             if(type >= 0x100)
  307.             {
  308.                 unsigned short size = 0;
  309.                 fread(&size, 2, 1, fp);
  310.                 printf("** header has size field, skipping 0x%x bytes...\n", size);
  311.                 fseek(fp, size, SEEK_CUR);
  312.             }
  313.            
  314.             break;
  315.         }
  316.         current++;
  317.     }
  318.  
  319.     if(nameRead)
  320.         printf("name: %s\n", name);
  321.     if(header103Read)
  322.         printf("author: %s\n", author);
  323.     if(header104Read)
  324.         printf("download: %s\n", download);
  325.     if(header105Read)
  326.         printf("download2: %s\n", download2);
  327.     if(headerRead)
  328.         printf("header type: 0x%x unk: 0x%x\n", header.type, header.unk);
  329.     if(header2Read)
  330.         printf("header2 unk: 0x%x\n", header2.unk);
  331.     if(header8Read)
  332.         printf("header8 unk: 0x%x\n", header8.unk);
  333.     if(header100Read)
  334.         printf("header100 size: 0x%x unk1: 0x%x unk2: 0x%x unk3: 0x%x\n", header100.size, header100.unk1, header100.unk2, header100.unk3);
  335.     if(header106Read)
  336.         printf("header106 size: 0x%x download_protected: 0x%x\n", header106.size, header106.download_protected);
  337.     if(header107Read)
  338.         printf("header107 zlib offset: 0x%x\n", zlibOffset);
  339.     if(hasGestures)
  340.     {
  341.         printf("gesture count: 0x%x\n", headerC.count);
  342.         for(int i = 0; i < headerC.count; i++)
  343.         {
  344.             printf("== Gesture %d\n", i);
  345.             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);
  346.         }
  347.  
  348.     }
  349.  
  350.  
  351.     unsigned char check = 0;
  352.     unsigned char chk2 = 0;
  353.     if(!hasGestures)
  354.     {
  355.         AVB_ZLIB_GESTURE_HEADER hdr;
  356.         AVB_ZLIB_BMP_HEADER bmpHdr;
  357.         fread(&hdr, sizeof(AVB_ZLIB_GESTURE_HEADER), 1, fp);
  358.         void* colors = 0;
  359.         if(hdr.num_colors > 0)
  360.         {
  361.             colors = malloc(hdr.num_colors * 4);
  362.             memset(colors, 0, hdr.num_colors * 4);
  363.             for(int y = 0; y < hdr.num_colors; y++)
  364.                 fread((BYTE*)colors + (y * 4), 3, 1, fp);
  365.         }
  366.  
  367.         fread(&bmpHdr, sizeof(AVB_ZLIB_BMP_HEADER), 1, fp);
  368.  
  369.         void* compressed = malloc(bmpHdr.len_comp);
  370.         fread(compressed, bmpHdr.len_comp, 1, fp);
  371.  
  372.         // create bmp and decompress
  373.         BITMAPFILEHEADER bmp;
  374.         memset(&bmp, 0, sizeof(BITMAPFILEHEADER));
  375.         bmp.bfType = 0x4D42;
  376.         bmp.bfOffBits = 0x36 + (hdr.num_colors * 4);
  377.         bmp.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (hdr.num_colors * 4) + bmpHdr.len_decomp;
  378.        
  379.         void* dest = malloc(bmpHdr.len_decomp);
  380.         int size = bmpHdr.len_decomp;
  381.  
  382.         int ret = uncompress((Bytef*)dest, (uLongf*)&size, (Bytef*)compressed, bmpHdr.len_comp);
  383.         if(ret != 0) return 0;
  384.  
  385.         TCHAR fname[256];
  386.         swprintf(fname, L"%ws_data\\", argv[1]);
  387.         _tmkdir(fname);
  388.         swprintf(fname, L"%ws_data\\bg.bmp", argv[1]);
  389.         FILE* file = _wfopen(fname, L"wb");
  390.         fwrite(&bmp, sizeof(BITMAPFILEHEADER), 1, file);
  391.         fwrite(&bmpHdr.header, sizeof(BITMAPINFOHEADER), 1, file);
  392.         if(colors)
  393.             fwrite(colors, hdr.num_colors * 4, 1, file);
  394.         fwrite(dest, size, 1, file);
  395.         fclose(file);
  396.        
  397.         if(colors) free(colors);
  398.  
  399.         dest = dest;
  400.  
  401.     }
  402.     else
  403.         for(int i = 0; i < headerC.count; i++)
  404.         {
  405.             void* colors = 0;
  406.             //fseek(fp, headerAEntries[i].ent.gestureOffset + zlibOffset, SEEK_SET);
  407.             fseek(fp, headerPoses[i].gestureOffset + zlibOffset, SEEK_SET);
  408.  
  409.             if(!headerCisB)
  410.             {
  411.                 fread(&gestureHdr[i], sizeof(AVB_ZLIB_GESTURE_HEADER), 1, fp);
  412.                 if(gestureHdr[i].num_colors > 0)
  413.                 {
  414.                     colors = malloc(gestureHdr[i].num_colors * 4);
  415.                     memset(colors, 0, gestureHdr[i].num_colors * 4);
  416.                     for(int y = 0; y < gestureHdr[i].num_colors; y++)
  417.                         fread(((BYTE*)colors + (y * 4)), 3, 1, fp);
  418.                 }
  419.             }
  420.  
  421.             fread(&gestureBmpHdr[i], sizeof(AVB_ZLIB_BMP_HEADER), 1, fp);
  422.  
  423.             fread(&check, 1, 1, fp);
  424.             fread(&chk2, 1, 1, fp);
  425.             fseek(fp, -2, SEEK_CUR);
  426.             if(check != 0x78 || chk2 != 0xDA)
  427.             {
  428.                 /*printf("** scanning for zlib data...\n");
  429.                 int offset = 0;
  430.                 while(true)
  431.                 {
  432.                     offset++;
  433.                     fread(&check, 1, 1, fp);
  434.                     if(check != 0x78) continue;
  435.                     fread(&chk2, 1, 1, fp);
  436.                     if(chk2 != 0xDA)
  437.                     {
  438.                         fseek(fp, -1, SEEK_CUR);
  439.                         continue;
  440.                     }
  441.                     break;
  442.                 }
  443.                 offset -= 1;
  444.                 printf("found at %x\n", offset);
  445.                 fseek(fp, headerPoses[i].gestureOffset + zlibOffset + offset, SEEK_SET);
  446.                 fread(&gestureHdr[i], sizeof(AVB_ZLIB_GESTURE_HEADER), 1, fp);
  447.                 if(gestureHdr[i].num_colors > 0)
  448.                 {
  449.                     if(colors) free(colors);
  450.                     colors = malloc(gestureHdr[i].num_colors * 4);
  451.                     for(int y = 0; y < gestureHdr[i].num_colors; y++)
  452.                         fread(&colors + (y * 4), 3, 1, fp);
  453.                 }
  454.                 fread(&gestureBmpHdr[i], sizeof(AVB_ZLIB_BMP_HEADER), 1, fp);
  455.                 //return 0;*/
  456.                 printf("** zlib data not found and searching is deprecated, exiting\n");
  457.                 return 0;
  458.             }
  459.  
  460.             void* compressed = malloc(gestureBmpHdr[i].len_comp);
  461.             fread(compressed, gestureBmpHdr[i].len_comp, 1, fp);
  462.  
  463.             // create bmp and decompress
  464.             BITMAPFILEHEADER bmp;
  465.             memset(&bmp, 0, sizeof(BITMAPFILEHEADER));
  466.             bmp.bfType = 0x4D42;
  467.             bmp.bfOffBits = 36 + (gestureHdr[i].num_colors * 4);
  468.             bmp.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (gestureHdr[i].num_colors * 4) + gestureBmpHdr[i].len_decomp;
  469.        
  470.             void* dest = malloc(gestureBmpHdr[i].len_decomp);
  471.             int size = gestureBmpHdr[i].len_decomp;
  472.  
  473.             int ret = uncompress((Bytef*)dest, (uLongf*)&size, (Bytef*)compressed, gestureBmpHdr[i].len_comp);
  474.             if(ret != 0) continue;
  475.  
  476.             TCHAR fname[256];
  477.             swprintf(fname, L"%ws_data\\", argv[1]);
  478.             _tmkdir(fname);
  479.             swprintf(fname, L"%ws_data\\%d.bmp", argv[1], i);
  480.             FILE* file = _wfopen(fname, L"wb");
  481.             fwrite(&bmp, sizeof(BITMAPFILEHEADER), 1, file);
  482.             fwrite(&gestureBmpHdr[i].header, sizeof(BITMAPINFOHEADER), 1, file);
  483.             if(colors)
  484.                 fwrite(colors, gestureHdr[i].num_colors * 4, 1, file);
  485.             fwrite(dest, size, 1, file);
  486.             fclose(file);
  487.             if(colors) free(colors);
  488.  
  489.             dest = dest;
  490.  
  491.         }
  492.  
  493.     return 0;
  494. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement