This is an article about our lovely format j2b with opensource code of decompressing it ;)
WARNING! It’s for ppl who knows C++ and datatype programming and has some compiler!
J2B i a compressed tracker with "Deflate" algorithm based on popular library ZLIB.
This algorithm consists of LZ77 and Huffman coding and was "Deflate" was created by Jean-Loup Gailly.
All of jj2 data are compressed in "Deflate".
I’m going to explain you a format of trackers what after decompression looks weird…
This is Epic’s format I think.
First of all: the algorithms implementation based on some streaming to EOF (end of file)
and whole C++ source code for decompress jazz2 music file data:
// dej2b.cpp
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <zlib.h>
/* if you have some compiler you must link the
library "zlib.lib" or something like this*/
#define CHUNK 16384
// compression
int def(FILE *source, FILE *dest, int level)
{
int ret, flush;
unsigned have;
z_stream strm;
Bytef in[CHUNK];
Bytef out[CHUNK];
/* allocate deflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = deflateInit(&strm, level);
if (ret != Z_OK)
return ret;
/* compress until end of file */
do {
strm.avail_in = fread(in, 1, CHUNK, source);
if (ferror(source)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
strm.next_in = in;
/* run deflate() on input until output buffer not full, finish
compression if all of source has been read in */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = deflate(&strm, flush); /* no bad return value */
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
have = CHUNK – strm.avail_out;
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
} while (strm.avail_out 0);
assert(strm.avail_in 0); /* all input will be used */
/* done when last data in file processed */
} while (flush != Z_FINISH);
assert(ret == Z_STREAM_END); /* stream will be complete */
/* clean up and return */
(void)deflateEnd(&strm);
return Z_OK;
}
// decompression
int inf(FILE *source, FILE *dest)
{
int ret;
unsigned have;
z_stream strm;
Bytef in[CHUNK];
Bytef out[CHUNK];
/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK)
return ret;
/* decompress until deflate stream ends or end of file */
do {
strm.avail_in = fread(in, 1, CHUNK, source);
if (ferror(source)) {
(void)inflateEnd(&strm);
return Z_ERRNO;
}
if (strm.avail_in == 0)
break;
strm.next_in = in;
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return ret;
}
have = CHUNK – strm.avail_out;
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
(void)inflateEnd(&strm);
return Z_ERRNO;
}
} while (strm.avail_out 0);
assert(strm.avail_in 0); /* all input will be used */
/* done when inflate() says it’s done */
} while (ret != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
/* streaming isn’t mine (it’s from some zlib site),
but other code below is only mine =p (I decoded it) :) */
// decompress jj2 data
int decompressdata(char j2dfile[255], char dj2dfile[255])
{
int ret;
FILE* fi=fopen(j2dfile,"rb");
if(!fi)
{
printf("Error: %s\n","can’t open J2Data file!");
return -3;
}
FILE* fo=fopen(dj2dfile,"wb");
if(!fo)
{
printf("Error: %s\n","can’t create file!");
return -3;
}
ret=inf(fi,fo);
fclose(fi);
fclose(fo);
return ret;
}
// decode j2b data
int decodej2b(char j2bfile[255], char dj2bfile[255])
{
int ret;
FILE* fi=fopen(j2bfile,"rb");
if(!fi)
{
printf("Error: %s\n","can’t open J2B file!");
return -3;
}
FILE* fo=fopen(dj2bfile,"wb");
if(!fo)
{
printf("Error: %s\n","can’t create file!");
return -3;
}
/* seek 24 bytes (to position 25)
to get real data (I dunno for what that bytes are)… */
fseek(fi,0×18,SEEK_SET);
ret=inf(fi,fo);
fclose(fi);
fclose(fo);
return ret;
}
// main prog
int main(int argc, char *argv[])
{
int ret;
ret=decodej2b("boss2.j2b","boss2.txt"); // for example ;)
if(ret==-3)
printf("Data failure: errorcode %d\n",ret);
if(ret==0)
printf("%s\n","Jazz Jackrabbit 2 Music File decoded successful!");
printf("%s\n","Press Enter");
getc(stdin);
return 0;
}
//End Of Source Code
Now annoying part of decoded j2b format specification
(I opened it in hex editor and compared with original tracker).
Data structure of header is the easiest part :p
It consists of:
Signature
"RIFF" (Reserved Intercharge File Format) (4 bytes)
File size (without first 8 bytes) (4 bytes)
Next signature of tracker initial values "AM INIT" (8 bytes)
Some value I don’t know what the hell is… (4 bytes)
Title of the song (ends with 0×00 character) (64 bytes)
Some next value I don’t know… (1 byte)
Number of channels (1 byte)
Initial speed of song (1 byte)
Initial tempo of song (1 byte)
Some unknown 5 bytes…
Channels panning values (attention here: size in bytes is "Number of channels")
…and
this is the data structure of dj2b header in c++:
typedef struct
{
unsigned char signature[4];
unsigned long filesize;
unsigned char aminitsig[8];
unsigned long something;
unsigned char title[64];
unsigned char something1;
unsigned char channelnum;
unsigned char speed;
unsigned char tempo;
unsigned char something2[5];
unsigned char channelpan[64]; // init tab with maximal channels number
} J2TRK;
Next thing in Epic’s RIFF format is order of patterns (it is after channelpan values):
Signature
"ORDR" (4 bytes)
Length of order (1 byte)
Some 3 unknown bytes…
Position where the order ends (it’s like real length only of order data) (1 byte)
Pattern indexes (attention here: size in bytes is "Position where the order ends")
You must have get minimal and maximal value from order to get
the lowest and the highest index of pattern!
This will help you in extracting patterns data
(number of patterns equals to difference between min and max value: num=max-min).
…structure:
typedef struct
{
unsigned char signature[4];
unsigned char length;
unsigned char something[3];
unsigned char end;
unsigned char pattindex[256]; // init tab with maximal pattern index
} J2ORDR;
Uhhh…. This is a part I had nasty problems here…
I think all of lengths are written in file like unvalidated…
for example: in the file length of pattern is 1346 so you must read 1345 bytes…
but if you have i.e. 235 bytes you read 235 bytes…
Second part of problems was encoded data in notes and effects (rowdata),
but I decoded it and I’m gonna show it :)
But now you must know the beginning:
Signature
"PATT" (4 bytes)
Length of data without 8 bytes (4 bytes)
Index of pattern (1 byte)
Real length of data only (validate it!) (4 bytes)
Number of rows in pattern (1 byte)
Data (size is validated "Real length of data only")
…
struct of pattern:
typedef struct
{
unsigned char signature[4];
unsigned long lengthwo8;
unsigned char index;
unsigned long length;
unsigned char rows;
char data[65536]; /* init tab with maximal possible value
(I don’t suppose that the pattern could be 4294967295 bytes long…) */
} J2PATT;
I’m tired… So now encoded data of rows.
There are commands which tells us what channel we have to read and
if we must leave all empty rows.
There are all decoded commands:
XY commands:
00 – go to next row, execute XY command
2x – on channel x+1 read the following data: <vol/2> (1 byte)
4x – on channel x+1 read the following data: <samplenum><note> (2 bytes)
6x – on channel x+1 read the following data: <samplenum><note><vol/2> (3 bytes)
8x – on channel x+1 read the following data: <eff_parameter><effect> (2 bytes)
Ax – on channel x+1 read the following data: <eff_parameter><effect><vol/2> (3 bytes)
Cx – on channel x+1 read the following data: <eff_parameter><effect><samplenum><note> (4 bytes)
Ex – on channel x+1 read the following data: <eff_parameter><effect><samplenum><note><vol/2> (5 bytes)
Note numbers and conversion:
31 – C-4
32 – C#4
33 – D-4
34 – D#4
35 – E-4
36 – F-4
37 – F#4
38 – G-4
39 – G#4
3a – A-4
3b – A#4
3c – B-4
3d – C-5
3e – C#5
3f – D-5
40 – D#5
41 – E-5
42 – F-5
43 – F#5
44 – G-5
45 – G#5
46 – A-5
47 – A#5
48 – B-5
49 – C-6
4a – C#6
4b – D-6
4c – D#6
4d – E-6
4e – F-6
4f – F#6
50 – G-6
51 – G#6
52 – A-6
53 – A#6
54 – B-6
Effects (not all of them, as I’ll have more time I’ll list more effects here):
0×01 – Portamento up
0×02 – Portamento down
0×08 – Panning
0×0A – Volume slide
0×0F – Speed/Tempo (as in MOD)
Next data of dj2b is instrument and then sample.
By now I decoded sample structure (instrument structure too but I don’t know what data is there):
Instrument
struct:
Signature
"RIFF" (4 bytes)
AIInstSize without 8 bytes (4 bytes)
Signature "AI INST" (8 bytes)
AIInstSize without 16 bytes (4 bytes)
0×01420000 (4 bytes)
Instrument data I don’t know it (317 bytes)
Sample
struct:
Signature
"RIFF" (4 bytes)
Length of struct without 8 bytes (4 bytes)
Signature "AS SAMP" (8 bytes)
Length of struct without 16 bytes (4 bytes)
4 unknown bytes…
SampleName ends on 0×00 (32 bytes)
0×00 (1 byte)
Panning/2 (1 byte)
6 unknown bytes…
SampleWaveSize (4 bytes)
LoopStart (4 bytes)
LoopEnd (4 bytes)
SampleFreq (4 bytes)
0×00000000 (4 bytes)
4 unknown bytes…
SampleWaveData (Attenion here: size is "SampleWaveSize")
That’s all folks…
I wanted to write a convertor from
this strange format to Impulse Tracker but I don’t have a time for it…
Maybe somebody of you will be nice and write the convertor.
If you want Impulse Tracker fileformat description I can write here.
I hope you understood something from my article…
Good luck!
End Of Article
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.
DaPete10 on August 31, 2005 01:00
It won\‘t compile in VC++6.
Jarno vos on August 31, 2005 16:31
eehh….please write this in NORMAL ENGLISH
iam not so smart
Dr. Eggman on September 02, 2005 08:19
\“It won’t compile in VC++6.\”
Microsoft makes me sick…
I compiled it under Dev-C++ and Borland and this is an example for console program.
NovaStar on September 14, 2005 08:40
Are j2b files like the cinematic files that come up as \“Jazz 2 Music File\”?
Dr. Eggman on September 24, 2005 17:20
I found other XY commands:
3x – on channel x+17 read the following data: vol/2 (1 byte)
5x – on channel x+17 read the following data: samplenum,note (2 bytes)
7x – on channel x+17 read the following data: samplenum,note,vol/2 (3 bytes)
9x – on channel x+17 read the following data: eff_parameter,effect (2 bytes)
Bx – on channel x+17 read the following data: eff_parameter,effect,vol/2 (3 bytes)
Dx – on channel x+17 read the following data: eff_parameter,effect,samplenum,note (4 bytes)
Fx – on channel x+17 read the following data: eff_parameter,effect,samplenum,note,vol/2 (5 bytes)
Dr. Eggman on September 28, 2005 14:46
I finally got all effects:
0×01 – Portamento up
0×02 – Portamento down
0×03 – Tone portamento
0×04 – Vibrato
0×05 – Volslide+Toneporta
0×06 – Volslide+Vibrato
0×07 – Tremolo
0×08 – Panning
0×09 – Set offset
0×0A – Volume slide
0×0B – Position jump
0×0D – Pattern break
0×0E – Multieffect (like Sxx in IT format)
0×0F – Speed
0×14 – Tempo
Dr. Eggman on February 09, 2006 17:28
Now after new data I got it\‘s totally out of date :p
SP-2 on October 08, 2006 04:35
my eyes are bleeding.
i wish i could be smart to understand it.
PurpleJazz on October 29, 2006 11:30
err……
Stijn on January 09, 2010 19:49
dvde
A simple description of the format would\‘ve been a thousand times more useful, now we have to reverse-engineer your code instead of the J2B format.