Skip to content

Instantly share code, notes, and snippets.

@usernameak
Last active August 16, 2021 10:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save usernameak/158bf0302596d8d4b78741c6e2db21f6 to your computer and use it in GitHub Desktop.
Save usernameak/158bf0302596d8d4b78741c6e2db21f6 to your computer and use it in GitHub Desktop.

Specification of *.BAR resource archive format from BREW 3.1.x

Everything is little-endian

Header:

struct bar_header_t {
    uint8_t file_type; // always 0x11
    uint8_t padding; // zero byte for alignment
    uint16_t version; // format version
    uint16_t oldest_version; // ?, minimum compatible format version?

    uint16_t num_map_entries;
    uint32_t offset_to_map;
    uint32_t map_size;

    uint32_t offset_to_offsets;
    uint32_t num_offsets;

    uint32_t offset_to_rsc_data;
    uint32_t rsc_data_size;
} header;

Right after the header (header.offset_to_map points into there) there's resource list (header.num_map_entries entries, total size is in header.map_size, one entry can describe multiple resources of same type with sequential IDs)

struct bar_map_entry_t {
    uint16_t resource_type; // resource types. values (RESTYPE_*) can be found in the SDK in AEEShell.h
    uint16_t first_id; // ID of the first resource described in the entry
    uint16_t add_ids; // amount of additional IDs allocated right after first_id that are described by the same record
    uint16_t offset_table_idx; // index of the offset of first resource data (see below), the rest of the offsets are right after the one this entry points to
}; 

Right after that list, at header.offset_to_offsets, there's offset table (array of num_offsets uint32s). It seems that it should be always sorted in ascending order. Resource size is defined by difference between current and next offset (or offset_to_rsc_data + rsc_data_size - offset, if it's the last resource)

Right after the offset list, at header.offset_to_rsc_data, there're resources themselves (total size is header.rsc_data_size).

Information on common resource types can be found in AEEShell.h from SDK, including the internal structure.

Resource types:

enum {
   RESTYPE_STRING = 1, // string resource
   RESTYPE_IMAGE  = 6, // binary file resource (there's tiny header in the beginning, see below)
   RESTYPE_DIALOG = 0x2000, // misc resources related to GUI (structure unknown)
   RESTYPE_CONTROL= 0x2001,
   RESTYPE_LISTITEM=0x2002,
   RESTYPE_BINARY = 0x5000 // custom resource (used, for example, in *.mif files (yes, they're BARs too))
};

RESTYPE_IMAGE header:

typedef struct AEEResBlob
{
   byte  bDataOffset;     /* how far into the Blob the real data starts */
   byte  bZero;           /* always zero, is padding in 1.x BREW */

   char  szMimeType[1];   /* null-terminated string (not just [1]) mimetype,
                             can be up to 249 characters in length */
} AEEResBlob;

RESTYPE_STRING header:

one or two bytes specify the charset:

FF FE - UTF-16LE
03 - Latin-1
02 - UTF-8
FD FE - Shift_Jis
FE FE - EUC-KR
FC - EUC-CN
FA - ASCII?
04 - ISO-8859-8
05 - GSM 03.38
06 - ??? 
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
unsigned char EmuIOReadByte(FILE *f) {
unsigned char b;
fread(&b, 1, 1, f);
return b;
}
unsigned short EmuIOReadShort(FILE *f) {
unsigned char b1 = EmuIOReadByte(f);
unsigned char b2 = EmuIOReadByte(f);
return (b1 << 0) | (b2 << 8);
}
unsigned int EmuIOReadInt(FILE *f) {
unsigned char b1 = EmuIOReadByte(f);
unsigned char b2 = EmuIOReadByte(f);
unsigned char b3 = EmuIOReadByte(f);
unsigned char b4 = EmuIOReadByte(f);
return (b1 << 0) | (b2 << 8) | (b3 << 16) | (b4 << 24);
}
void make_directory(const char *filename) {
#ifdef WIN32
mkdir(filename);
#else
mkdir(filename, 0777);
#endif
}
int main(int argc, char **argv) {
char filename[256];
sprintf(filename, "%s.dir", argv[1]);
make_directory(filename);
FILE *f = fopen(argv[1], "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char file_type = EmuIOReadByte(f);
EmuIOReadByte(f); // padding
unsigned short version = EmuIOReadShort(f);
unsigned short oldest_version = EmuIOReadShort(f);
printf("version %u, oldest version %u\n", version, oldest_version);
unsigned short num_map_entries = EmuIOReadShort(f);
unsigned int offset_to_map = EmuIOReadInt(f);
unsigned int map_size = EmuIOReadInt(f);
printf("%u map entries\n", num_map_entries);
unsigned int offset_to_offsets = EmuIOReadInt(f);
unsigned int num_offsets = EmuIOReadInt(f);
printf("%u offsets\n", num_offsets);
unsigned int offset_to_rsc_data = EmuIOReadInt(f);
unsigned int rsc_data_size = EmuIOReadInt(f);
printf("rsc data size %u\n", rsc_data_size);
unsigned int offset_table[num_offsets];
fseek(f, offset_to_offsets, SEEK_SET);
for(int i = 0; i < num_offsets; i++) {
unsigned int offset = EmuIOReadInt(f);
offset_table[i] = offset;
printf("offset %u\n", offset);
}
printf("offset_table end %u\n", ftell(f));
fseek(f, offset_to_map, SEEK_SET);
for(int i = 0; i < num_map_entries; i++) {
unsigned short resource_type = EmuIOReadShort(f);
unsigned short first_id = EmuIOReadShort(f);
unsigned short add_ids = EmuIOReadShort(f);
unsigned short offset_table_idx = EmuIOReadShort(f);
printf("map entry: restype %u, id range %u...%u, offset table index %u\n", resource_type, first_id, first_id + add_ids, offset_table_idx);
unsigned short cur_offset_table_idx = offset_table_idx;
long mark = ftell(f);
for(unsigned int j = first_id; j <= first_id + add_ids; j++) {
unsigned int offset = offset_table[cur_offset_table_idx];
unsigned int next_offset = cur_offset_table_idx + 1 >= num_offsets ? fsize : offset_table[cur_offset_table_idx + 1];
sprintf(filename, "%s.dir/%d", argv[1], resource_type);
make_directory(filename);
sprintf(filename, "%s.dir/%d/%d.%s", argv[1], resource_type, j, resource_type == 1 ? "txt" : "bin");
FILE *wf = fopen(filename, "wb");
fseek(f, offset, SEEK_SET);
char *data = (char *) malloc(next_offset - offset);
printf("esize %u\n", next_offset - offset);
fread(data, 1, next_offset - offset, f);
if(resource_type == 6) {
unsigned char start_offset = (unsigned char) data[0];
fwrite(data + start_offset, 1, next_offset - offset - start_offset, wf);
/*sprintf(filename, "RES_%d_%d.BIN1", j, resource_type);
FILE *wf1 = fopen(filename, "wb");
fwrite(data, 1, next_offset - offset, wf1);
fclose(wf1);*/
} else if(resource_type == 1) {
/*sprintf(filename, "%s.dir/%d/%d.bin", argv[1], resource_type, j);
FILE *wf1 = fopen(filename, "wb");
fwrite(data, 1, next_offset - offset, wf1);
fclose(wf1);*/
unsigned int start_offset = 1;
const char *charset_name = "LATIN1";
unsigned char charset_first_byte = (unsigned char) data[0];
if(charset_first_byte == 0xFF) {
unsigned char charset_second_byte = (unsigned char) data[1];
if(charset_second_byte == 0xFE) {
charset_name = "UTF-16LE";
start_offset = 2;
}
} else if(charset_first_byte == 0x02) {
charset_name = "UTF-8";
} else if(charset_first_byte == 0xFE) {
unsigned char charset_second_byte = (unsigned char) data[1];
if(charset_second_byte == 0xFE) {
charset_name = "EUC-KR";
start_offset = 2;
}
}
printf("charset_name %s\n", charset_name);
fwrite(data + start_offset, 1, next_offset - offset - start_offset, wf);
} else {
fwrite(data, 1, next_offset - offset, wf);
}
fclose(wf);
printf("resource id %u, type %u, offset %u\n", j, resource_type, offset_table[cur_offset_table_idx]);
cur_offset_table_idx++;
}
fseek(f, mark, SEEK_SET);
}
// for(int i = 0; i < )
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment