| Dec 22, 2011, 05:54 PM | |
| 
		  
			
			Since I get a new page claim, I thought I'd occupy this with one of JJ2's most elusive file formats -- the Jazz2 Cinematic Files (or J2Vs). The J2V File Format Like all other file formats, the file begins with a J2V Header, followed by 4 ZLIB streams. It's worth noting that the ZLIB streams are sync flushed every few frames, which explains the 00 00 FF FF at the end of every stream. So essentially the 4 streams are interleaved: Data1, Data2, Data3, Data4, back to Data1 etc. With the compressed size preceding each stream. Another thing to note is that it only contains video data. I will talk about where the audio data comes from towards the end of the post. The Header Code: struct J2V_Header {
	char Magic[8] = "CineFeed";
	int FileSize;
	int CRC32; // of the lowercase filename, e.g. "intro" or "endinglq"
	int Width;
	int Height;
	short BitsPerPixel;
	short DelayBetweenFrames; // in milliseconds
	int TotalFrames;
	int MaxUSize1;
	int MaxUSize2;
	int MaxUSize3;
	int MaxUSize4;
	int MaxCSize;
}Data1 contains frame data given as instructions per row, Data2 contains absolute x offsets, Data3 contains relative y offsets, and Data4 contains raw palette and pixel data. Data1 - Frame Data This starts with a codebyte of either 0x00 (which means no palette), or 0x01 (which means read 1024 bytes from Data4 and update palette), followed by Height (i.e. 480 or 200) sets of instructions. Here are the possible instructions: 0x00: Read the next two bytes, then copy that many bytes from Data4. 0x01-0x7F: Copy this many bytes from Data4. 0x80: End of line. 0x81: Read the next two bytes, then copy that many bytes from some offset given by Data2 and Data3. 0x82-0xFF: Read the next two bytes, then copy that many bytes minus 106 from some offset as above. Data2 - Absolute X Offset Just a bunch of offsets, in shorts. Data3 - Relative Y Offset Another bunch of offsets, in bytes. Data4 - Raw Palette and Pixel Data Palette data is pretty much 256 colours in RGBA format, for a total of 1024 bytes. Pixels are 1 byte each. How the copying from Data2/Data3 is done If you're copying to (x, y) on the current frame, you want to copy from (read(data2), y + read(data1) - 127) from the previous frame. Sound Data This is obtained from the corresponding SoundFXList.Name from Data.J2D, where it can be read as the following struct: Code: struct SoundFXList {
	int Frame;
	int Sample;
	int Volume;
	int Panning;
}Finally, here's some C# code which needs nothing but the .NET framework and compilation under unsafe code: Code: using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace jj2video
{
    class Program
    {
        static void Main()
        {
            var d = new BinaryReader[4];
            int i, frame = 0, frames, width, height, delay;
            using (var br = new BinaryReader(File.OpenRead(@"D:\Games\Jazz2\Intro.j2v")))
            {
                br.ReadBytes(16); width = br.ReadInt32(); height = br.ReadInt32();
                br.ReadInt16(); delay = br.ReadInt16(); frames = br.ReadInt32();
                br.ReadBytes(20);
                var strs = new MemoryStream[4]; for (i = 0; i < 4; i++) strs[i] = new MemoryStream();
                for (i = 0; br.BaseStream.Position != br.BaseStream.Length; i++)
                {
                    foreach (var str in strs)
                    {
                        int len = br.ReadInt32();
                        str.Write(br.ReadBytes(len), 0, len);
                    }
                }
                for (i = 0; i < 4; i++)
                {
                    strs[i].Position = 2;
                    d[i] = new BinaryReader(new System.IO.Compression.DeflateStream(strs[i], 0));
                }
            }
            var bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            var pb = new PictureBox() { Size = new Size(width, height) };
            var f = new Form() { ClientSize = pb.Size }; f.Controls.Add(pb);
            var t = new Timer() { Enabled = true, Interval = delay };
            t.Tick += new EventHandler((s, e) =>
            {
                if (frame++ >= frames) return;
                var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);
                unsafe
                {
                    var pixels = (byte*)data.Scan0;
                    var copy = new byte[data.Stride * height];
                    for (i = 0; i < copy.Length; i++) copy[i] = pixels[i];
                    if (d[0].ReadByte() == 1)
                    {
                        var p = bmp.Palette;
                        for (i = 0; i < 256; i++) p.Entries[i] = Color.FromArgb(d[3].ReadByte(), d[3].ReadByte(), d[3].ReadInt16());
                        bmp.Palette = p;
                    }
                    for (int y = 0; y < height; y++)
                    {
                        byte c;
                        int x = 0;
                        while ((c = d[0].ReadByte()) != 128)
                        {
                            if (c < 128)
                            {
                                int u = c == 0 ? d[0].ReadInt16() : c;
                                for (i = 0; i < u; i++) pixels[y * data.Stride + x++] = d[3].ReadByte();
                            }
                            else
                            {
                                int u = c == 0x81 ? d[0].ReadInt16() : c - 106;
                                int n = d[1].ReadInt16() + (d[2].ReadByte() + y - 127) * data.Stride;
                                for (i = 0; i < u; i++) pixels[y * data.Stride + x++] = copy[n++];
                            }
                        }
                    }
                }
                bmp.UnlockBits(data);
                pb.Image = bmp;
            });
            Application.Run(f);
        }
    }
}
				__________________ <TABLE border=1><TR><TD>Facts: Jazz Sprite Dynamite (JSD) Tileset Extractor Neobeo's Firetruck </TD><TD>Myths: Jazz Creation Station Plus (JCS+) - 10% Coming soon - a dedicated server! - 25% Jazz Sprite Dynamite v2 (JSDv2) - 2% Another generic single-player level - 0%</TD></TR></TABLE> Last edited by Neobeo; Dec 22, 2011 at 06:06 PM. Reason: typo | 
| Jan 19, 2012, 02:00 PM | ||||
|   
		  
			
			Nice neobeo, will try that out someday! Anyway, got a message from Michiel today. You know, the creator of JCS. But also some bad news, but we've heard that before. I quote: Quote: 
 Quote: 
 Code: #ifndef __FILEFORMATS_H__
#define __FILEFORMATS_H__
#ifdef	_LARGE_TILE_LIMIT
/*-------------------------------------------------------------------------------------------------
New version, large tile limits
-------------------------------------------------------------------------------------------------*/
#define	MAXTILES	 4096
#define	MAXANIMTILES	 256
#define	TILESET_LATESTVERSION	 0x201	 // Version 2.01
#define	LEVEL_LATESTVERSION	 0x203	 // Version 2.03
#else
/*-------------------------------------------------------------------------------------------------
Old version, old tile limits
-------------------------------------------------------------------------------------------------*/
#define	MAXTILES	 1024
#define MAXANIMTILES	 128
#define	TILESET_LATESTVERSION	 0x200	 // Version 2.00
#define	LEVEL_LATESTVERSION	 0x202	 // Version 2.02
#endif
/*-------------------------------------------------------------------------------------------------
Constants, limits, etc
-------------------------------------------------------------------------------------------------*/
#define MAXTILES_OLD	 1024
#define	MAXTILESMASK	 (MAXTILES-1)
#define	MAXTILESMASK_OLD	 (MAXTILES_OLD-1)
#define	MAXLAYERS	 8
#define MAXANIMTILES_OLD	 128
#define MAXANIMTILEFRAMES	 64
#define	ALIB_LATESTVERSION	 0x200	 // Version 2.00
#define PLIB_LATESTVERSION	 0x100	 // Version 1.00
#define SECTIONTILES	 4	 // Width of section
#define SECTIONAND	 3	 // AND Mask
#define SECTIONSHIFT	 2	 // Shift count
#define SECTIONSIZE	 (SECTIONTILES*sizeof(word))	// Size of section
#define SECTIONWIDTH	 (SECTIONTILES*32)	 // Width in pixels
#define	SECTIONWIDTHSHIFT	 (SECTIONSHIFT+5)	 // Shifter for width pixels
#define SECTIONHEIGHT	 32	 // Height in pixels
#define SECTIONHEIGHTSHIFT	 5	 // Shifter for height pixels
#define WIDTH_IN_SECTIONS(x)	 (((x)+SECTIONTILES-1) >> SECTIONSHIFT)
#define TILETYPE_NORMAL	 0
#define TILETYPE_TRANS	 1
#define TILETYPE_MENU	 4
#pragma pack(push, 1)
#pragma warning(disable : 4200)
/*-------------------------------------------------------------------------------------------------
Level file
-------------------------------------------------------------------------------------------------*/
//
// Level header structure
//
typedef struct
{
char	ID[4];
byte	Magic[4];
char	Name[32];
word	Version;
dword	FileSize,	 FileCRC;
dword	HdrPackSize,	 HdrUnPackSize;
dword	EventsPackSize,	 EventsUnPackSize;
dword	SectionsPackSize,	SectionsUnPackSize;
dword	LayersPackSize,	 LayersUnPackSize;
} LevelFileHdr;
//
// Animating tile dump structure in level file
//
typedef struct
{
word	DelayTime;
word	DelayTimeAdder;
word	PingPongDelay;
byte	PingPong;
byte	Speed;
byte	NumFrames;
union
{
word	DumpFrames[MAXANIMTILEFRAMES];
word	*pFrames;
};
} AnimTile;
//
// Level dump structure, contains most of the level data
//
typedef struct
{
// Editor
int	 LayerX;
int	 LayerY;
byte	CurLayer;
// General
byte	AmbientMin;
byte	AmbientStart;
word	NumAnimTiles;
byte	SplitScreenType;
byte	MultiPlayer;
dword	sizeof_LevelGameHdr;
// Strings
char	LevelName[32];
char	TileSet[32];
char	BonusLevel[32];
char	NextLevel[32];
char	SecretLevel[32];
char	MusicFile[32];
char	HelpString[16][512];
// Layer data
dword	lFlags[MAXLAYERS];
byte	lType[MAXLAYERS];
byte	lUsed[MAXLAYERS];
dword	lEditorWidth[MAXLAYERS];
dword	lWidth[MAXLAYERS];
dword	lHeight[MAXLAYERS];
int	 lDepth[MAXLAYERS];
byte	lDetailLevel[MAXLAYERS];
int	 lWaveX[MAXLAYERS];
int	 lWaveY[MAXLAYERS];
int	 lSpeedX[MAXLAYERS];
int	 lSpeedY[MAXLAYERS];
int	 lAutoSpeedX[MAXLAYERS];
int	 lAutoSpeedY[MAXLAYERS];
byte	lTextureType[MAXLAYERS];
byte	lTextureParam[MAXLAYERS][3];
// Tiles stuff
word	tRemap;
int	 tEvent[MAXTILES];
byte	tFlip[MAXTILES];
byte	tType[MAXTILES];
byte	tXMask[MAXTILES];	 // ** UNUSED **
AnimTile tAnim[MAXANIMTILES];
// Reserved
dword	Reserved[128];
} LevelGameHdr;
//
// Old level dump structure with less tiles and animtiles
//
typedef struct
{
// Editor
int	 LayerX;
int	 LayerY;
byte	CurLayer;
// General
byte	AmbientMin;
byte	AmbientStart;
word	NumAnimTiles;
byte	SplitScreenType;
byte	MultiPlayer;
dword	sizeof_LevelGameHdr;
// Strings
char	LevelName[32];
char	TileSet[32];
char	BonusLevel[32];
char	NextLevel[32];
char	SecretLevel[32];
char	MusicFile[32];
char	HelpString[16][512];
// Layer data
dword	lFlags[MAXLAYERS];
byte	lType[MAXLAYERS];
byte	lUsed[MAXLAYERS];
dword	lEditorWidth[MAXLAYERS];
dword	lWidth[MAXLAYERS];
dword	lHeight[MAXLAYERS];
int	 lDepth[MAXLAYERS];
byte	lDetailLevel[MAXLAYERS];
int	 lWaveX[MAXLAYERS];
int	 lWaveY[MAXLAYERS];
int	 lSpeedX[MAXLAYERS];
int	 lSpeedY[MAXLAYERS];
int	 lAutoSpeedX[MAXLAYERS];
int	 lAutoSpeedY[MAXLAYERS];
byte	lTextureType[MAXLAYERS];
byte	lTextureParam[MAXLAYERS][3];
// Tiles stuff
word	tRemap;
int	 tEvent[MAXTILES_OLD];
byte	tFlip[MAXTILES_OLD];
byte	tType[MAXTILES_OLD];
byte	tXMask[MAXTILES_OLD];	 // ** UNUSED **
AnimTile tAnim[MAXANIMTILES_OLD];
} LevelGameHdrOld;
//
// Convert old LevelGameHdrOld struct to new LevelGameHdr struct
//
#ifdef __cplusplus
inline void LevelGameHdr_202_To_203(LevelGameHdr *n)
#else
void __inline LevelGameHdr_202_To_203(LevelGameHdr *n)
#endif
{
const int TilesDiff = MAXTILES - MAXTILES_OLD;
const int AnimsDiff = MAXANIMTILES - MAXANIMTILES_OLD;
LevelGameHdrOld *o = (LevelGameHdrOld *)n;
// Copy old->new position
memcpy(n->tAnim, o->tAnim, MAXANIMTILES_OLD * sizeof(AnimTile));
memcpy(n->tXMask, o->tXMask, MAXTILES_OLD * sizeof(byte));
memcpy(n->tType, o->tType, MAXTILES_OLD * sizeof(byte));
memcpy(n->tFlip, o->tFlip, MAXTILES_OLD * sizeof(byte));
memcpy(n->tEvent, o->tEvent, MAXTILES_OLD * sizeof(int));
// Wipe out empty part of arrays
memset(n->tEvent + MAXTILES_OLD, 0, TilesDiff * sizeof(int));
memset(n->tFlip + MAXTILES_OLD, 0, TilesDiff * sizeof(byte));
memset(n->tType + MAXTILES_OLD, 0, TilesDiff * sizeof(byte));
memset(n->tXMask + MAXTILES_OLD, 0, TilesDiff * sizeof(byte));
memset(n->tAnim + MAXANIMTILES_OLD, 0, AnimsDiff * sizeof(AnimTile));
memset(n->Reserved, 0, 128 * sizeof(dword));
}
/*-------------------------------------------------------------------------------------------------
Tile file
-------------------------------------------------------------------------------------------------*/
//
// Tile file header structure
//
typedef struct
{
char	ID[4];	 // "TILE"
byte	Magic[4];	 // Magic number
char	Name[32];	 // Name of tileset
word	Version;
dword	FileSize,	 FileCRC;	 // Size of tile file
dword	HdrPackSize,	 HdrUnPackSize;	 // Header packed and unpacked size
dword	DataPackSize,	 DataUnPackSize;	 // Tiles Data packed and unpacked size
dword	CodePackSize,	 CodeUnPackSize;	 // Tiles Code packed and unpacked size
dword	MasksPackSize,	 MasksUnPackSize;	 // Tiles Masks packed and unpacked size
} TileFileHdr;
//
// Tile file structure
//
typedef struct
{
// Create info block
byte	Palette[1024];	 // Palette for tileset
dword	NumTiles;	 // Amount of tiles
byte	Full[MAXTILES*2];	 // Tiles filled flags
byte	*Data[MAXTILES*2];	 // -> Tiles (OFFSET when in a file)
byte	*Code[MAXTILES*2];	 // -> Codes (OFFSET when in a file)
dword	*Mask[MAXTILES*2];	 // -> Masks (OFFSET when in a file)
} TileFile;
//
// Old tile file structure
//
typedef struct
{
// Create info block
byte	Palette[1024];	 // Palette for tileset
dword	NumTiles;	 // Amount of tiles
byte	Full[MAXTILES_OLD*2];	 // Tiles filled flags
byte	*Data[MAXTILES_OLD*2];	 // -> Tiles (OFFSET when in a file)
byte	*Code[MAXTILES_OLD*2];	 // -> Codes (OFFSET when in a file)
dword	*Mask[MAXTILES_OLD*2];	 // -> Masks (OFFSET when in a file)
} TileFileOld;
//
// Convert old TileFileOld struct to new TileFile struct
//
#ifdef __cplusplus
inline void TileFile_200_To_201(TileFile *n)
#else
void __inline TileFile_200_To_201(TileFile *n)
#endif
{
const int TilesDiff = MAXTILES - MAXTILES_OLD;
TileFileOld *o = (TileFileOld *)n;	 // ->New tilefile
// Copy old->new position
memcpy(n->Mask+MAXTILES,	o->Mask+MAXTILES_OLD, MAXTILES_OLD * sizeof(dword *));
memcpy(n->Mask,	 o->Mask,	 MAXTILES_OLD * sizeof(dword *));
memcpy(n->Code+MAXTILES,	o->Code+MAXTILES_OLD, MAXTILES_OLD * sizeof(byte *));
memcpy(n->Code,	 o->Code,	 MAXTILES_OLD * sizeof(byte *));
memcpy(n->Data+MAXTILES,	o->Data+MAXTILES_OLD, MAXTILES_OLD * sizeof(byte *));
memcpy(n->Data,	 o->Data,	 MAXTILES_OLD * sizeof(byte *));
memcpy(n->Full+MAXTILES,	o->Full+MAXTILES_OLD,	MAXTILES_OLD * sizeof(byte));
memcpy(n->Full,	 o->Full,	 MAXTILES_OLD * sizeof(byte));
// Wipe out empty part of arrays
memset(n->Full + MAXTILES_OLD,	 0, TilesDiff * sizeof(byte));
memset(n->Full + MAXTILES_OLD + MAXTILES,	0, TilesDiff * sizeof(byte));
memset(n->Data + MAXTILES_OLD,	 0, TilesDiff * sizeof(byte *));
memset(n->Data + MAXTILES_OLD + MAXTILES,	0, TilesDiff * sizeof(byte *));
memset(n->Code + MAXTILES_OLD,	 0, TilesDiff * sizeof(byte *));
memset(n->Code + MAXTILES_OLD + MAXTILES,	0, TilesDiff * sizeof(byte *));
memset(n->Mask + MAXTILES_OLD,	 0, TilesDiff * sizeof(dword *));
memset(n->Mask + MAXTILES_OLD + MAXTILES,	0, TilesDiff * sizeof(dword *));
}
/*-------------------------------------------------------------------------------------------------
Library file
-------------------------------------------------------------------------------------------------*/
//
// Library file header
//
typedef struct
{
char	ID[4];	 // Should be "PLIB"
byte	Magic[4];	 // Magic number
dword	Version;	 // Version number
dword	FileSize,	 FileCRC;	 // Size of music file
dword	HdrPackSize,	 HdrUnPackSize;	 // Header packed and unpackacked size
} LibraryFileHdr;
//
// File in library header
//
typedef struct
{
char	Name[32];	 // File name or tag
dword	Type;	 // Type of file
dword	Offset,	 FileCRC;	 // Position in library file + unpacked file CRC
dword	PackSize,	 UnPackSize;	 // Packed and unpacked size
} LibFile;
//
// Picture file in memory and as stored in library
//
typedef struct
{
int	 Width;	 // Width of image
int	 Height;	 // Height of image
int	 BPP;	 // Bit depth of image
byte	Palette[256*4];	 // Palette
byte	Image[];	 // Image data
} Picture;
/*-------------------------------------------------------------------------------------------------
Animation library file
-------------------------------------------------------------------------------------------------*/
//
// Animation library file header structure
//
typedef struct
{
char	ID[4];	 // "ALIB"
byte	Magic[4];	 // 0x00,0xBA,0xBE,0x00
dword	HdrSize;	 // Header size
word	Version;	 // Animation lib version
byte	sizeof_Tanimations,	sizeof_Tframes;	 // sizeof(Tanimations), sizeof(Tframes)
dword	FileSize,	 FileCRC;	 // Size of file
dword	NumAnims;	 // Amount of animations
dword	AnmOffsets[];	 // Offsets to animations in file
} AnimFileHdr;
//
// Animation library file animation dump
//
typedef struct
{
char	ID[4];	 // "ANIM"
byte	NumStates;	 // Amount of states in anim
byte	NumSamples;	 // Amount of samples in anim
word	NumFrames;	 // Amount of frames in all anim states
dword	BaseSampleNum;	 // Base sample number for samples
dword	AnimHdrPackSize,	AnimHdrUnPackSize;	 // Size of animation headers
dword	FrameHdrPackSize,	FrameHdrUnPackSize;	 // Size of frame headers
dword	DataPackSize,	 DataUnPackSize;	 // Size of data+masks
dword	SamplesPackSize,	SamplesUnPackSize;	 // Size of samples
} AnimDump;
/*-------------------------------------------------------------------------------------------------
Music file
-------------------------------------------------------------------------------------------------*/
//
// Music file header structure
//
typedef struct
{
char	ID[4];	 // "MUSE"
byte	Magic[4];	 // Magic number
dword	FileSize,	 FileCRC;	 // Size of music file
dword	AmPackSize,	 AmUnPackSize;	 // Size of Advanced Module
} MusicFileHdr;
#pragma pack(pop)
#endif // __FILEFORMATS_H__??Quote: 
 
				__________________ WebJCS 2 (new and in progress) WebJCS 1 (old but complete) SGIP Simple Games in Progress list Level Packer v2 - With a GUI! PHP Tileset Compiler | ||||
| Jan 19, 2012, 02:20 PM | |
| 
		  
			
			So the null layer properties (discounting z-index/depth) are Type, Detail Level, and Wave X and Wave Y. Fascinating. I am tempted to try one last time to get them to do something.
		 | 
| Jan 20, 2012, 08:45 AM | |
| 
		  
			
			So really, no one has ever thought of mailing Michiel before?   Well, it's good to know what names they've given those unknown parts, I'm curious about what you'll find   
				__________________ | 
| Jan 20, 2012, 02:04 PM | ||
| Quote: 
 Saw his name in JCS's about dialog, added him on google+ and showed him WebJCS. He was impressed. Now we keep in contact on fb chat. If you have something you want to ask him, add him or send me a PM. 
				__________________ WebJCS 2 (new and in progress) WebJCS 1 (old but complete) SGIP Simple Games in Progress list Level Packer v2 - With a GUI! PHP Tileset Compiler | ||
| Jan 20, 2012, 05:42 PM | |
| 
		  
			
			I mean, if he has any old prototypes of anything (the game, tilesets, whatever) that would be great, but I think really what's important is that he continue to nag Arjan to nag Guerrilla Games or whatever to get you the source code, because most anything else of interest is going to be included in that. The sooner we can try to figure out how to reconcile 1.23 (or 1.20? note that those structs he gave don't include passwords) source code with TSF and JJ2+, the better.
		 | 
| Jan 21, 2012, 02:24 AM | |
| 
		  
			
			I think it does. /multiversion on lets the 1.23 players join the TSF server. I guess it's the same. It can help a lot in making anti cheats. Also, don't give the source code to everyone if you get it. I suggest only nimrod, djazzy, and someone else who doesn't want / never made and published bad cheats should get the source code. | 
| Jan 21, 2012, 07:49 AM | |
| 
		  
			
			Three immediate things come to mind. 
 
				__________________ <TABLE border=1><TR><TD>Facts: Jazz Sprite Dynamite (JSD) Tileset Extractor Neobeo's Firetruck </TD><TD>Myths: Jazz Creation Station Plus (JCS+) - 10% Coming soon - a dedicated server! - 25% Jazz Sprite Dynamite v2 (JSDv2) - 2% Another generic single-player level - 0%</TD></TR></TABLE> | 
| Jan 21, 2012, 06:39 PM | |
| 
		  
			
			They had to hire someone to port it, I think, and the MAC version is really buggy...
		 | 
| Jul 30, 2012, 04:31 PM | |
| 
		  
			
			Could you post an example .j2l? I looked at a few levels with fewer than 256 words and couldn't figure out what you meant.
		 | 
| Aug 1, 2012, 12:27 PM | |
| 
		  
			
			Not that words are traditionally listed in stream1 anyway.
		 | 
| Oct 2, 2012, 12:54 PM | ||
| Quote: 
  If you're interested in how it works, check out the source links! Here's a link that maybe is interesting: http://djazz.mine.nu/lab/j2d/ I have only tested it in Google Chrome. Enjoy! Some exported images:    This is actually six images:        
				__________________ WebJCS 2 (new and in progress) WebJCS 1 (old but complete) SGIP Simple Games in Progress list Level Packer v2 - With a GUI! PHP Tileset Compiler | ||
| Oct 2, 2012, 02:03 PM | |
| 
		  
			
			Wait, are you saying this Picture.thanks OEM screen is still in our J2D's? Or was that extracted from 1.00g/h?
		 
				__________________ | 
| Oct 20, 2012, 07:14 AM | ||
| 
				
				The J2S file format
			 
		  
			
			Behold, a complete JJ2 language editor! http://djazz.mine.nu/lab/j2s/ I made it on request from Grytolle, who wanted to improve his Swedish translation of JJ2. Here it is, including translation of the helpstrings in the default levels: http://www.jazz2online.com/downloads...language-file/ Some are really funny, if you speak Swedish. Quote: 
 
				__________________ WebJCS 2 (new and in progress) WebJCS 1 (old but complete) SGIP Simple Games in Progress list Level Packer v2 - With a GUI! PHP Tileset Compiler | ||
| Oct 20, 2012, 10:27 AM | |
| 
		  
			
			But JJ2 does use the text strings in the .j2s file, not the ones in the .j2l. That's why a hypothetical car.j2l will use the text strings from carrot1.j2l instead of whatever ones you try to specify for it internally. Unless I'm misunderstanding what you're saying here?
		 | 
| Oct 20, 2012, 10:49 AM | ||
| Quote: 
 
				__________________ WebJCS 2 (new and in progress) WebJCS 1 (old but complete) SGIP Simple Games in Progress list Level Packer v2 - With a GUI! PHP Tileset Compiler | ||
| Oct 20, 2012, 03:34 PM | |
| 
		  
			
			No knowledge here. Just experience. Yeah JJ2 does use the J2S basing on some not entirely specified level name data that causes conflicts. That's why at first the levels of my levelpack were using filenames in Polish. Anyway, it's a pretty neat concept that does work good on a vanilla JJ2 (entirely vanilla, meaning no custom levels etc.). What I could propose, is that given we seem to have conquered J2S as a group (lol claiming rights oh well), we could incorporate new elements like pregame or new gamemode prompts into the language files! Because... uh... we can. AAAAND there could be people out there who use JJ2+ and yet barely know English! (just a guess but it could very well be true) if I sound unreasonable well then I have my reasons I'm an artist and all 
				__________________ "So unless I overwrote my heart with yours, I think not. But I might have." - Violet CLM Two Games Joined releases: Control / Splinter (twin singles) || Ballistic Bunny (EP) || Beyond (maxi-single) || Beyond: Remixed (remix EP) || Inner Monsters OST (mini-album) || Shadows (album) | 
| Jul 2, 2015, 02:41 PM | ||
| Quote: 
 Code: uint32_t lookup[] = {0x00000000, 0x77073096, 0x0EE0E612C, 0x990951BA, 0x76DC419, 0x706AF48F,
 0x0E963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0x0E0D5E91E,
 0x97D2D988, 0x9B64C2B, 0x7EB17CBD, 0x0E7B82D07, 0x90BF1D91,
 0x1DB71064, 0x6AB020F2, 0x0F3B97148, 0x84BE41DE, 0x1ADAD47D,
 0x6DDDE4EB, 0x0F4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0,
 0x0FD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0x0FA0F3D63,
 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0x0D56041E4, 0x0A2677172,
 0x3C03E4D1, 0x4B04D447, 0x0D20D85FD, 0x0A50AB56B, 0x35B5A8FA,
 0x42B2986C, 0x0DBBBC9D6, 0x0ACBCF940, 0x32D86CE3, 0x45DF5C75,
 0x0DCD60DCF, 0x0ABD13D59, 0x26D930AC, 0x51DE003A, 0x0C8D75180,
 0x0BFD06116, 0x21B4F4B5, 0x56B3C423, 0x0CFBA9599, 0x0B8BDA50F,
 0x2802B89E, 0x5F058808, 0x0C60CD9B2, 0x0B10BE924, 0x2F6F7C87,
 0x58684C11, 0x0C1611DAB, 0x0B6662D3D, 0x76DC4190, 0x1DB7106,
 0x98D220BC, 0x0EFD5102A, 0x71B18589, 0x6B6B51F, 0x9FBFE4A5,
 0x0E8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0x0E10E9818,
 0x7F6A0DBB, 0x86D3D2D, 0x91646C97, 0x0E6635C01, 0x6B6B51F4,
 0x1C6C6162, 0x856530D8, 0x0F262004E, 0x6C0695ED, 0x1B01A57B,
 0x8208F4C1, 0x0F50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,
 0x0FCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0x0FBD44C65,
 0x4DB26158, 0x3AB551CE, 0x0A3BC0074, 0x0D4BB30E2, 0x4ADFA541,
 0x3DD895D7, 0x0A4D1C46D, 0x0D3D6F4FB, 0x4369E96A, 0x346ED9FC,
 0x0AD678846, 0x0DA60B8D0, 0x44042D73, 0x33031DE5, 0x0AA0A4C5F,
 0x0DD0D7CC9, 0x5005713C, 0x270241AA, 0x0BE0B1010, 0x0C90C2086,
 0x5768B525, 0x206F85B3, 0x0B966D409, 0x0CE61E49F, 0x5EDEF90E,
 0x29D9C998, 0x0B0D09822, 0x0C7D7A8B4, 0x59B33D17, 0x2EB40D81,
 0x0B7BD5C3B, 0x0C0BA6CAD, 0x0EDB88320, 0x9ABFB3B6, 0x3B6E20C,
 0x74B1D29A, 0x0EAD54739, 0x9DD277AF, 0x4DB2615, 0x73DC1683,
 0x0E3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0x0E40ECF0B,
 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0x0F00F9344, 0x8708A3D2,
 0x1E01F268, 0x6906C2FE, 0x0F762575D, 0x806567CB, 0x196C3671,
 0x6E6B06E7, 0x0FED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
 0x0F9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0x0D6D6A3E8,
 0x0A1D1937E, 0x38D8C2C4, 0x4FDFF252, 0x0D1BB67F1, 0x0A6BC5767,
 0x3FB506DD, 0x48B2364B, 0x0D80D2BDA, 0x0AF0A1B4C, 0x36034AF6,
 0x41047A60, 0x0DF60EFC3, 0x0A867DF55, 0x316E8EEF, 0x4669BE79,
 0x0CB61B38C, 0x0BC66831A, 0x256FD2A0, 0x5268E236, 0x0CC0C7795,
 0x0BB0B4703, 0x220216B9, 0x5505262F, 0x0C5BA3BBE, 0x0B2BD0B28,
 0x2BB45A92, 0x5CB36A04, 0x0C2D7FFA7, 0x0B5D0CF31, 0x2CD99E8B,
 0x5BDEAE1D, 0x9B64C2B0, 0x0EC63F226, 0x756AA39C, 0x26D930A,
 0x9C0906A9, 0x0EB0E363F, 0x72076785, 0x5005713, 0x95BF4A82,
 0x0E2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0x0E5D5BE0D,
 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0x0F1D4E242, 0x68DDB3F8,
 0x1FDA836E, 0x81BE16CD, 0x0F6B9265B, 0x6FB077E1, 0x18B74777,
 0x88085AE6, 0x0FF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF,
 0x0F862AE69, 0x616BFFD3, 0x166CCF45, 0x0A00AE278, 0x0D70DD2EE,
 0x4E048354, 0x3903B3C2, 0x0A7672661, 0x0D06016F7, 0x4969474D,
 0x3E6E77DB, 0x0AED16A4A, 0x0D9D65ADC, 0x40DF0B66, 0x37D83BF0,
 0x0A9BCAE53, 0x0DEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0x0BDBDF21C,
 0x0CABAC28A, 0x53B39330, 0x24B4A3A6, 0x0BAD03605, 0x0CDD70693,
 0x54DE5729, 0x23D967BF, 0x0B3667A2E, 0x0C4614AB8, 0x5D681B02,
 0x2A6F2B94, 0x0B40BBE37, 0x0C30C8EA1, 0x5A05DF1B};
uint32_t hash(char* input)
{
	uint32_t hash = 0xFFFFFFFF;
	int count = strlen(input);
	for (int i=0;ican probably still be optimized and written better as I translated it myself from ASM. Last edited by jvbsl; Jul 2, 2015 at 03:13 PM. | ||
| Jul 2, 2015, 02:54 PM | |
| 
		  
			
			Also known as the CRC algorithm    | 
| Jul 2, 2015, 03:06 PM | |
| 
		  
			
			it is the normal CRC? God damnit I should have looked first^^ Edit: It indeed is but just 3 Bytes are actually used. But now atleast everyone knows First post and already posted bullshit^^ Last edited by jvbsl; Jul 2, 2015 at 03:12 PM. Reason: Checked | 
| Jul 2, 2015, 03:36 PM | |
| Aug 3, 2015, 05:26 PM | |
| 
		  
			
			So, how would one come around to extracting all the sprites in the game? Trying to work on a fan-game here.
		 | 
| Aug 3, 2015, 07:35 PM | |
| 
		  
			
			Here you go or Spriters Resource has a lot of stuff too, if you'd find that format more convenient (although, I don't know if you can beat this) | 
| Aug 4, 2015, 11:21 AM | |
| 
				__________________ WebJCS 2 (new and in progress) WebJCS 1 (old but complete) SGIP Simple Games in Progress list Level Packer v2 - With a GUI! PHP Tileset Compiler | 
| Oct 6, 2015, 07:13 AM | |
| 
		  
			
			I wrote a Python script to extract the sound effects from Anims.j2a. Information on the Jazz2's sample format is scarce, so I ended up relying heavily on DJazz's PHP code. Anyway, here it is: Code: #!/usr/bin/env python2
import wave
from os import mkdir
from StringIO import StringIO
from struct import unpack
from zlib import decompress
# These functions return the next 8/16/32 bits of a file as a little endian int.
def readInt8(f): return unpack('<b', f.read(1))[0]
def readInt16(f): return unpack('<h', f.read(2))[0]
def readInt32(f): return unpack('<l', f.read(4))[0]
def parseAlibHeader(j2a):
    """Parses a j2a file's header, and returns a dict of the contents.
    
    Entries in returned dict:
    "magic number" -- 'ALIB'; identifies file format
    "unknown1" -- 0x00beba00; purpose unknown
    "header size" -- size of this header, in bytes
    "version" -- 0x0200; probably means v2.0
    "unknown2" -- 0x1808; purpose unknown
    "file size" -- obvious
    "crc32" -- CRC32 hash of file
    "set count" -- number of animation sets in file
    "set offsets" -- list containing the offsets for each set, in bytes
    """
    j2a.seek(0)
    header = {}
    header["magic number"] = j2a.read(4)
    header["unknown1"] = readInt32(j2a)
    header["header size"] = readInt32(j2a)
    header["version"] = readInt16(j2a)
    header["unknown2"] = readInt16(j2a)
    header["file size"] = readInt32(j2a)
    header["crc32"] = readInt32(j2a)
    header["set count"] = readInt32(j2a)
    header["set offsets"] = [readInt32(j2a) for i in range(header["set count"])]
    return header
def parseAnimHeader(j2a, offset):
    """Parses an animation set's header, and returns a dict of the contents.
    Entries in returned dict:
    "magic number" -- 'ANIM'; marks start of set
    "animation count" -- number of animations in this set
    "sample count" -- number of sound samples in set
    "frame count" -- total number of frames in all animations in set
    "prior sample count" -- total number of samples before this set
    "animation info",
    "frame info",
    "image data",
    "sample data" -- dicts detailing the size of each part of the set
    The rest of the set is compressed with zlib, so the last 4 entries above
    contain these entries:
    "compressed" -- actual size in file
    "uncompressed" -- size when uncompressed
    """
    j2a.seek(offset)
    header = {}
    header["magic number"] = j2a.read(4)
    header["animation count"] = readInt8(j2a)
    header["sample count"] = readInt8(j2a)
    header["frame count"] = readInt16(j2a)
    header["prior sample count"] = readInt32(j2a)
    for key in "animation info", "frame info", "image data", "sample data":
        header[key] = {
            "compressed": readInt32(j2a),
            "uncompressed":readInt32(j2a)
        }
    return header
def sampleDataOffset(setOffset, animHeader):
    """Returns the offset of an animation set's sample data."""
    offset = setOffset + 44  # animHeader is 44 bytes
    for key in "animation info", "frame info", "image data":
        offset += animHeader[key]["compressed"]
    return offset
def decompressInFile(f, offset, size):
    """Decompresses portion of file, returning result as a StringIO."""
    f.seek(offset)
    return StringIO(decompress(f.read(size)))
def parseSample(data, offset):
    """Parses a sample, returning a dict of the contents.
    Entries in returned dict:
    "size" -- combined size of all entries
    "chunk id" -- 'RIFF'; identifies chunk type
    "chunk size" -- size of everything below this, except "padding3"
    "format" -- 'AS  '; identifies format contained in RIFF
    "samp subchunk" -- dict containing format info
    "data" -- waveform data of sample
    "padding" -- 8 bytes of padding
    "samp subchunk" contains these entries:
    "id" -- 'SAMP'; identifies header
    "padding1" -- 48 bytes of padding (probably)
    "data size" -- size of data, in bytes
    "padding2" -- 8 bytes of padding
    "sample rate" -- samples per second
    """
    data.seek(offset)
    sample = {}
    sample["size"] = readInt32(data)
    sample["chunk id"] = data.read(4)
    sample["chunk size"] = readInt32(data)
    sample["format"] = data.read(4)
    samp = {}
    samp["id"] = data.read(4)
    samp["padding1"] = data.read(48)
    samp["data size"] = readInt32(data)
    samp["padding2"] = data.read(8)
    samp["sample rate"] = readInt32(data)
    sample["samp subchunk"] = samp
    sample["data"] = data.read(samp["data size"])
    sample["padding3"] = data.read(8)
    return sample
def parseSampleData(data, sampleCount):
    """Parses sample data, returning a list of samples.
    See parseSample for info on elements.
    """
    
    samples = []
    data.seek(0)
    # In theory, parsing one sample should put me at the start of the next.
    # Unfortunately, there's at least one place where this fails, so I have to
    # keep track of the offset, manually.
    offset = 0
    for i in range(sampleCount):
        samples.append(parseSample(data, offset))
        offset += samples[-1]["size"]
    return samples
def writeWav2(filename, sample):
    # todo: don't overwrite file if it already exists
    wavFile = wave.open(filename, 'w')
    # the 1st two are hardcoded until I find this info in the sample
    wavFile.setnchannels(1)
    wavFile.setsampwidth(2)
    wavFile.setframerate(sample["samp subchunk"]["sample rate"])
    wavFile.writeframes(sample["data"])
    wavFile.close()
def main():
    filename = "Anims.j2a"  # I can probably just leave this hardcoded
    # todo: throw error if file not found
    with open(filename, 'r') as j2a:
        alibHeader = parseAlibHeader(j2a)  # find animation sets
        # todo: verify file with checksum
        print("{} found.".format(filename))
        print("Total size: {} bytes".format(alibHeader["file size"]))
        print("{} animation sets found.\n".format(alibHeader["set count"]))
        # todo: don't crash if this exists already
        mkdir("sounds")
        
        # for each set
        for setNum, offset in enumerate(alibHeader["set offsets"]):
            animHeader = parseAnimHeader(j2a, offset)  # find sample data
            print("Decompressing data for set {}...".format(\
                    alibHeader["set offsets"].index(offset)))
            # find samples
            decompressed = decompressInFile(j2a,\
                    sampleDataOffset(offset, animHeader),\
                    animHeader["sample data"]["compressed"])
            samples = parseSampleData(decompressed, animHeader["sample count"])
            print("{} samples found. Writing to disk...".format(len(samples)))
            # write samples
            wavName = "sounds/sfx_{}_{}.wav"
            for sampleNum, sample in enumerate(samples):
                writeWav2(wavName.format(setNum, sampleNum), sample)
if __name__ == '__main__':
    main() | 
| Oct 6, 2015, 10:26 AM | |
| 
		  
			
			Well done!
		 | 
|  | 
  		«
  			Previous Thread
  			|
  			Next Thread
  		»
  	
  
  
  
  
  
  
  
  
  
  
  
  | Thread Tools | |
| 
 | 
 | 
All times are GMT -8. The time now is 11:13 AM.
		Jazz2Online © 1999-INFINITY (Site Credits). 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. Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Original site design by Ovi Demetrian. DrJones is the puppet master. Eat your lima beans, Johnny.


 
    	    
    		  
    		  
    		  
    		





 
		 
  
		 
  
 