Friday, May 2, 2008

BAR Files

The data files used by Beats (BULK.BAR) are in a proprietary format that contain a number of zlib deflated files topped with a moderate header (not the simplest, but nothing like you'll see later). I first discovered the zlib'd files when I used the OS X program File Juicer in an attempt to find files embedded within the BAR. Jaeder Naub is the closest equivalent I could find for Windows, but it has troubles extracting the zlib'd files properly.

Before I start posting code it should be noted that any values posted are from the BULK.BAR file of JAM_0002. BAR files vary greatly across types and even within the same type, but the basic structure remains the same.

The header is as follows:

x00| E1 17 EF AD 00 00 00 01 Magic Number
x10| 05 00 00 00 Number of files in the BAR
This is important, as the size of the header depends on this.
Also the value is byte flipped or little endian. You'll see this A LOT.

From here on out there will be 16 byte chunks for each file. The order of these chunks isn't important.
I'll use the first chunk as an example:

x14| 5F AE 8F D1 Unknown related to zlib
I want to say it's a CRC, but I can't recreate it and all the JAM_000x BARs have the same value here. If it is a CRC, Beats semi-ignores it anyways; zeroing it causes failure, but leaving it despite a huge change in the file works OK.
x18| 00 00 00 00 Offset (minus header) of zlib
x1C| 03 01 00 00 Inflated size of zlib (259)*
x20| C4 00 00 00 Deflated size of zlib (196)*

And repeat for each file.

After each file is represented in the header, the first file appears , beginning with 78 DA.
With this header info, one should have no problem (except maybe time) extracting the files for inflation.
I'm focusing on the Jamming files, but feel free to explore others. The visualizers contain a lot of XML and difficult GIM/MIG images. Themes have a bit of XML and PSMF background videos. I haven't really looked at the CLGs (songs) as there's already a method to add your own music. If you decrypt EBOOT.BIN you'll find an embedded BAR file. Be warned: there are A LOT of files in there, though this is where you'll find Java .class files; sounds, UI elements, and fonts; and even more XML.

*Inflating and deflating the files can be a pain as there are few tools that use the same algorithm. zlibc for Windows does both perfectly. If one was so inclined, it should be trivial to make a tool in Python for other platforms.

4 comments:

FreePlay said...

#include <zlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
unsigned int in_magic, in_filecount, *in_crcs, *in_offsets, *in_inflated, *in_deflated, in_length;
unsigned char *buf_inflated, *buf_deflated;
int i;

if(argc < 2) return !(printf("Note: You can run this from the command prompt.\nIn Windows, you can just drag a .bar file to the .exe, and that'll work too.\nUsage: %s <bar file>\n", argv[0])+(strncmp(argv[0], "/cygdrive", 9) ? 0 : getc(stdin)))-1;

FILE *in, *out;
in = fopen(argv[1], "r");
if(!in)
return !(printf("BAR file %s not found.\n", argv[1]))-1;

fread(&in_magic, sizeof(unsigned int), 1, in);
if(in_magic != 0xADEF17E1)
return !(printf("Invalid BAR file.\n", argv[1]))-1;

fseek(in, 0x10, SEEK_SET);
fread(&in_filecount, sizeof(unsigned int), 1, in);
in_crcs = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
in_offsets = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
in_inflated = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
in_deflated = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
printf("%i files found\n", in_filecount);
for(i=0; i < in_filecount; i++)
{
fseek(in, 0x14+0x10*i, SEEK_SET);
fread(&(in_crcs[i]), sizeof(unsigned int), 1, in);
fread(&(in_offsets[i]), sizeof(unsigned int), 1, in);
fread(&(in_inflated[i]), sizeof(unsigned int), 1, in);
fread(&(in_deflated[i]), sizeof(unsigned int), 1, in);
printf("File %i:\nCRC = 0x%08X\nOffset = header+0x%04X\ninflated size = %i\ndeflated size = %i\n",i,in_crcs[i],in_offsets[i],in_inflated[i],in_deflated[i]);
buf_inflated = (unsigned char*)malloc(in_inflated[i]*sizeof(unsigned char));
buf_deflated = (unsigned char*)malloc(in_deflated[i]*sizeof(unsigned char));
fseek(in, in_offsets[i], SEEK_SET);
fread(buf_deflated, sizeof(unsigned char), in_deflated[i], in);
z_stream z;
// Set up the zlib inflation
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
z.avail_in = 0;
z.next_in = Z_NULL;
inflateInit2(&z, -15);
// Set up the input and output streams
z.avail_in = in_deflated[i];
z.next_in = buf_deflated;
z.avail_out = in_inflated[i];
z.next_out = (Bytef *)buf_inflated;
printf("%i\n", inflate(&z, Z_NO_FLUSH));
int inflate_ok = inflateEnd(&z);
if(inflate_ok)
{
printf("Inflate failed (%i)\n\n",inflate_ok);
//error - can't handle it; you're fucked if this doesn't work and flash0 was erased :/
} else {
printf("Inflated OK (%i)\n\n",inflate_ok);
}

char fn[16];
sprintf(fn, "%i.BARPIECE", i);
out = fopen(fn, "w");
fwrite(buf_inflated, in_deflated[i], sizeof(unsigned char), out);
fclose(out);

free(buf_deflated);
free(buf_inflated);
}
free(in_deflated);
free(in_inflated);
free(in_offsets);
free(in_crcs);

fclose(in);
return 0;
}
/*
int deflate_file(FILE *f, int offset)
{
z_stream z;
// Set up the zlib inflation
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
z.avail_in = 0;
z.next_in = Z_NULL;
inflateInit2(&z, -15);
// Set up the input and output streams
z.avail_in = internal_filesize;
z.next_in = file->data;
z.avail_out = filesize;
void *output = malloc(filesize);
z.next_out = (Bytef *)output;

// et voila.
inflate(&z, Z_NO_FLUSH);
int inflate_ok = inflateEnd(&z);
if(inflate_ok)
{
//error - can't handle it; you're fucked if this doesn't work and flash0 was erased :/
} else {
if(file->needs_sigcheck)
GenerateSigCheck((u8 *)output);
sceIoWrite(out, (unsigned char *)output, filesize);
}
return 0;
}
*/

FreePlay said...

ignore that last one, this is better:

#include <zlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
unsigned int in_magic, in_filecount, *in_crcs, *in_offsets, *in_inflated, *in_deflated, in_length;
unsigned char *buf_inflated, *buf_deflated;
int i;

if(argc < 2) return !(printf("Note: You can run this from the command prompt.\nIn Windows, you can just drag a .bar file to the .exe, and that'll work too.\nUsage: %s <bar file>\n", argv[0])+(strncmp(argv[0], "/cygdrive", 9) ? 0 : getc(stdin)))-1;

FILE *in, *out;
in = fopen(argv[1], "r");
if(!in)
return !(printf("BAR file %s not found.\n", argv[1]))-1;

fread(&in_magic, sizeof(unsigned int), 1, in);
if(in_magic != 0xADEF17E1)
return !(printf("Invalid BAR file.\n", argv[1]))-1;

fseek(in, 0x10, SEEK_SET);
fread(&in_filecount, sizeof(unsigned int), 1, in);
in_crcs = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
in_offsets = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
in_inflated = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
in_deflated = (unsigned int *)malloc(in_filecount*sizeof(unsigned int));
printf("%i files found\n", in_filecount);
for(i=0; i < in_filecount; i++)
{
fseek(in, 0x14+0x10*i, SEEK_SET);
fread(&(in_crcs[i]), sizeof(unsigned int), 1, in);
fread(&(in_offsets[i]), sizeof(unsigned int), 1, in);
fread(&(in_inflated[i]), sizeof(unsigned int), 1, in);
fread(&(in_deflated[i]), sizeof(unsigned int), 1, in);
printf("File %i:\nCRC = 0x%08X\nOffset = header+0x%04X\ninflated size = %i\ndeflated size = %i\n",i+1,in_crcs[i],in_offsets[i],in_inflated[i],in_deflated[i]);
buf_inflated = (unsigned char*)malloc(in_inflated[i]*sizeof(unsigned char));
if(in_inflated[i] != in_deflated[i])
{
buf_deflated = (unsigned char*)malloc(in_deflated[i]*sizeof(unsigned char));
fseek(in, in_offsets[i], SEEK_SET);
fread(buf_deflated, sizeof(unsigned char), in_deflated[i], in);
z_stream z;
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
z.avail_in = 0;
z.next_in = Z_NULL;
inflateInit2(&z, -15);
z.avail_in = in_deflated[i];
z.next_in = buf_deflated;
z.avail_out = in_inflated[i];
z.next_out = (Bytef *)buf_inflated;
inflate(&z, Z_NO_FLUSH);
int inflate_ok = inflateEnd(&z);
if(inflate_ok)
{
printf("Inflate failed\n\n",inflate_ok);
return;
} else {
printf("Inflated OK. Writing...\n",inflate_ok);
char fn[16];
sprintf(fn, "%i.BARPIECE", i+1);
out = fopen(fn, "w");
fwrite(buf_inflated, in_deflated[i], sizeof(unsigned char), out);
fclose(out);
printf("Wrote OK\n\n");
}
free(buf_deflated);
} else {
printf("Not deflated. Writing...\n");
fread(buf_inflated, sizeof(unsigned char), in_deflated[i], in);
char fn[16];
sprintf(fn, "%i.BARPIECE", i+1);
out = fopen(fn, "w");
fwrite(buf_inflated, in_deflated[i], sizeof(unsigned char), out);
fclose(out);
printf("Wrote OK\n\n");
}

free(buf_inflated);
}
free(in_deflated);
free(in_inflated);
free(in_offsets);
free(in_crcs);

fclose(in);
return 0;
}

FreePlay said...

know what, I give up. I keep doing it wrong.

I didn't add 0x14+0x10*in_filecount to the offsets for each file part...

FreePlay said...

Here.

It'll unpack the .BAR file to its own subfolder. It can auto-detect the most typical file formats stored in .BARs and name the output appropriately.

Source is included, of course; you'll need zlib to build it.