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, 05: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, 01:27 PM | |
Not that words are traditionally listed in stream1 anyway.
|
Oct 2, 2012, 01: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, 03: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, 08: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, 11: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, 11: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, 04: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, 03: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;i can probably still be optimized and written better as I translated it myself from ASM. Last edited by jvbsl; Jul 2, 2015 at 04:13 PM. |
Jul 2, 2015, 03:54 PM | |
Also known as the CRC algorithm
|
Jul 2, 2015, 04: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 04:12 PM. Reason: Checked |
Jul 2, 2015, 04:36 PM | |
Aug 3, 2015, 06: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, 08: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, 12:21 PM | ||
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 6, 2015, 08: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, 11:26 AM | |
Well done!
|
«
Previous Thread
|
Next Thread
»
Thread Tools | |
|
|
All times are GMT -8. The time now is 02:49 PM.
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 - 2024, Jelsoft Enterprises Ltd.
Original site design by Ovi Demetrian. DrJones is the puppet master. Eat your lima beans, Johnny.