Apr 2, 2006, 12:46 PM | ||||
JJ2 File Format Specifications
Edit: I just slipped this tileset extractor on top so people can see it.
Well, I was working on a file-format entry for the ERE, but the formatting never came out the way I wanted it. Seeing as I'm in somewhat of a rush, I've decided to dump my article here, in the hopes that someone can move it to ERE or some other public article. Zlib Compression in JJ2 This article will give you a crash course to understanding zlib, how it can be compressed / decompressed and how it is implemented in JJ2. You need either zlib.lib and zlib.h if you’re programming for C++, or zlib.dll if you’re programming for Visual Basic. Armed with this knowledge alone, it is possible to decrypt JJ2 files into their raw formats, and then figure out the formats from there. Declaring zlib functions Just about every programming language requires you to declare a function before you can use it. Notes about declares: The compress function will not be used at all, and is only provided for closure. This is because it is superseded by compress2, which allows compression at higher levels. Also, if you do not care about the integrity of your files, you can ignore the crc32 function and skip all crc checks. Without further ado: Declares in C++ Code:
#include <zlib.h> Don’t forget to add "Private"s in front of them if you’re going to use them in your forms. Code:
Declare Function compress Lib "zlib.dll" (ByRef dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long) As Long Declare Function compress2 Lib "zlib.dll" (ByRef dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long, ByVal Level As Integer) As Long Declare Function uncompress Lib "zlib.dll" (ByRef dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long) As Long Declare Function crc32 Lib "zlib.dll" (ByVal crc As Long, ByRef buffer As Any, ByVal bufferLen As Long) As Long As a bare minimum, all zlib-compressed files in JJ2 files should at least provide the compressed and uncompressed sizes in its header. For example, consider an example zlib section of a file which goes:
In this self-made example, the first four bytes (which reads 68 in hex) represents the size of the compressed stream (the input data). The next four bytes (which reads 79 in hex) represents the size of the uncompressed stream (the output data). Armed with that data, we now know that we will be decompressing a 68-byte buffer into a 79-byte one. Note that zlib streams starts with a "78 DA" when compressed at the maximum level, and this can be very useful for skimming through large files to find out where a zlib stream begins or how many zlib streams there are. In case you are already confused, a zlib stream is basically an array of bytes which contains compressed data, and is very unfriendly to the human eye. Fortunately, there is a very simple function which can be used to decompress a zlib stream into a buffer. This function is uncompress. Using uncompress in C++ For this short snippet we will assume that you have the above "example" file stored as "tutorial.dat". This assumes you are writing a console program and have already #included anything necessary. Code:
char *in; char *out; int CompressedSize; int UncompressedSize; int main() { FILE *fi = fopen("tutorial.dat", "rb"); //Opens tutorial.dat for binary reading fread(&CompressedSize, 4, 1, fi); //Reads the first four bytes and stores it fread(&UncompressedSize, 4, 1, fi); //Reads the next four bytes and stores it in = malloc(CompressedSize); //Allocates a size of CompressedSize for input stream fread(in, 1, CompressedSize, fi); //Reads CompressedSize bytes from the file fclose(fi); //We finished reading the file, no need to use it anymore out = malloc(UncompressedSize); //Allocates a size of UncompressedSize for output uncompress(out, &UncompressedSize, in, CompressedSize); //Decompresses, YAY :) //Here "out" contains the uncompressed data, so do whatever you want with it. //For simplicity, the above example is actually a string, so print it to screen: printf(out); } Using uncompress in VB Here we use the same assumptions: a file tutorial.dat with containing that binary data. Code:
Dim inBuffer() As Byte Dim outBuffer() As Byte Dim CompressedSize As Long Dim UncompressedSize As Long Sub Main() Open "tutorial.dat" For Binary Access Read As #1 'Opens tutorial.dat for reading Get #1, , CompressedSize 'Reads the first four bytes, stores in CompressedSize Get #1, , UncompressedSize 'Reads the next four bytes, stores in UncompressedSize ReDim inBuffer(CompressedSize - 1) 'Create buffer of CompressedSize bytes Get #1, , inBuffer 'Reads bytes into inBuffer Close #1 'Finish reading from file, no need to use it anymore ReDim outBuffer(UncompressedSize - 1) 'Create buffer of UncompressedSize bytes Uncompress outBuffer(0), UncompressedSize, inBuffer(0), CompressedSize 'YAY :) 'Here "outBuffer" contains the uncompressed data, so do whatever you want with it. End Sub
__________________
<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; Apr 2, 2006 at 11:25 PM. |
Apr 2, 2006, 12:48 PM | |
Compressing files
Overall, uncompressing shouldn’t be too difficult, since you already know both the exact compressed AND decompressed size. Trying to compress data however, is more difficult, because you don’t know how big the new zlib stream will be. However, the zlib manual states that the compressed size of a buffer will be at most (1.01 * size + 12) bytes big, regardless of compression level. Here we will use compress2, using a compression level of 9 (maximum). Using compress2 in C++ For this short snippet we will assume that the in buffer already contains a string called "Let us...". We will compress this into zlib and turn it into a file that is similar to JJ2's file formats. Code:
//char *in; char in[] = "Let us attempt to compress this string"; char *out; int CompressedSize; int UncompressedSize; int main() { UncompressedSize = sizeof(in); CompressedSize = (int)((float)(UncompressedSize * 1.01) + 12); //Allocate space out = malloc(CompressedSize); //Allocates a size of UncompressedSize for output compress2(out, &CompressedSize, in, UncompressedSize, 9); //Compresses, YAY :) //Note that CompressedSize now holds the real uncompressed size //Here "out" contains the compressed data, so do whatever you want with it. //For our example, we will write this to testcompress.dat FILE *fo = fopen("testcompress.dat", "wb"); //Opens testcompress.dat for writing fwrite(out, 4, 1, &CompressedSize); //Writes the compressed size fwrite(out, 4, 1, &UncompressedSize); //Writes the uncompressed size fwrite(out, 1, CompressedSize, fo); //Writes compressed stream to file fclose(out); //We finished writing to the file, no need to use it anymore } Using compress2 in VB Same thing as above, string to zlib, write to testcompress.dat. Code:
'Dim inBuffer() As Byte Dim inBuffer As String Dim outBuffer() As Byte Dim CompressedSize As Long Dim UncompressedSize As Long Sub Main() inBuffer = "Let us attempt to compress this string" UncompressedSize = Len(inBuffer) + 1 'Add a null-char to the end of the string CompressedSize = UncompressedSize * 1.01 + 12 'Sets maximum output size ReDim outBuffer(CompressedSize - 1) 'Allocates output space compress2 outBuffer(0), CompressedSize, inBuffer, UncompressedSize, 9 'YAY :) ReDim Preserve outBuffer(CompressedSize - 1) 'Adjusts output space 'Here "outBuffer" contains the uncompressed data, so do whatever you want with it. 'For out example, we’ll just do something simple - dump it into testcompress.dat Open "testcompress.dat" For Binary Access Write As #1 'Opens file for writing Put #1, , CompressedSize 'Writes CompressedSize to the first four bytes Put #1, , UncompressedSize 'Writes UncompressedSize to the next four bytes Put #1, , outBuffer 'Writes the output buffer to the file Close #1 'Finish writing to file, no need to use it anymore End Sub CRC Checking Finally we get to the crc32 stage. All (or most) of the JJ2 files contain an internal (and some even external) CRC check to verify that the file is not corrupted in any way. Seeing how most programmers are lazy, they will probably skip the CRC mechanism and just assume that file is valid, but it will be included here just for completeness anyway. Declare Function crc32 Lib "zlib.dll" (ByVal crc As Long, ByRef buffer As Any, ByVal bufferLen As Long) As Long or, in C++ format, uLong crc32 (uLong crc, const Bytef *buf, uInt len); As you can see, the crc32 function takes 3 arguments. The first one is a previous crc value, in case we do the CRC check in chunks. For simplicity, we will always use CRC on the whole buffer at once, which means we leave CRC as 0. buf/buffer is the buffer, and len/bufferLen is the length of the buffer. If you’ve fully understood the uncompress and compress2 functions then there is no need for an example here, since the buffer and length arguments are used here in exactly the same way as before. Final notes/other stuff None of the above code has been tested. They were jotted down directly on notepad and have never gone through any stage of compilation. If there are any bugs within them, report them here. The posts below will give info regarding specific file format headers, but this information alone should already be adventurous enough. ;D
__________________
<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> |
Apr 2, 2006, 12:48 PM | |
The J2A File Format
Well, it should probably be called the anims.j2a file format, since that's the only file I'm aware of that uses the j2a extension. The ALIB Header This is not a C/C++ structure, but I will be using a similar format to define the ALIB header. Code:
struct ALIB_Header { char Magic[4] = "ALIB"; //just an identifier, ignore null-char overflow long Unknown = 0x00BEBA00; //little endian, no idea what its for long HeaderSize; //Equals 464 bytes for v1.23 anims.j2a short Version = 0x0200; //Probably means v2.0 short Unknown = 0x1808; //no idea what this is long FileSize; //Equals 8182764 bytes for v1.23 anims.j2a long CRC32; //Note: CRC buffer starts after the end of header long SetCount; //Number of sets in the anims.j2a (109 in v1.23) long SetAddress[SetCount]; //Each set's starting address within the file } Each ANIM sub-file starts with a 44-byte header like this: Code:
struct ANIM_Header { char Magic[4] = "ANIM"; //just an identifier, ignore null-char overflow unsigned char AnimationCount; //number of animations in set unsigned char SampleCount; //number of sound samples in set short FrameCount; //total number of frames in set long SampleUnknown; //Unknown, but related to the sound samples long CData1; //Compressed size of Data1 (Animation Info) long UData1; //Uncompressed, remember the zlib tutorial? long CData2; long UData2; long CData3; long UData3; long CData4; long UData4; //lazy all the way up to here } Data1 - Animation Info Assuming you have already uncompressed this, you will get a buffer of (UData1) bytes, which also happens to be (8 * AnimationCount). This is because each animation info is only 8 bytes long: Code:
struct AnimInfo { short FrameCount; //number of frames for this particular animation short FPS; //not really sure what this is, so I'm guessing FPS long Reserved; //zeroes, used internally by Jazz2.exe } Data2 - Frame Info Same as above, uncompress, and discover that UData2 = 24 * FrameCount. Each frame info is 24 bytes long: Code:
struct FrameInfo { short Width; short Height; short ColdspotX; //relative to the hotspot!! short ColdspotY; //relative to the hotspot short HotspotX; //This is a misnomer. In reality, this is where the short HotspotY; //frame starts drawing relative to the "hotspot". short GunspotX; //relative to hotspot short GunspotY; //... long ImageAddress; //Address in Data3 where image starts long MaskAddress; //Address in Data3 where mask starts } Data3 - Image Data By now uncompressing it should be a breeze. But how do you use the info contained in here? JJ2 uses its own format (or maybe one that already exists, but I don't recognise), which is similar to RLE. The first four bytes contain width and height, except that the width has some flag that is sometimes set on its most significant bit. I have no idea what that is, so we'll skip it. After those 4 bytes, try to read the image data as codebytes. A codebyte < 128 (0x80) means to skip that many pixels. A codebyte > 128 (0x80) means to read and set that many pixels from the buffer onto the image. A codebyte = 128 (0x80) means to skip to the next row. Note that the overall image data is aligned to 8 bytes, so there can be up to 7 bytes of unused bytes between the current frame's image data and the next one. You probably already realised that the image is 8-bits, so get a palette from some random level to start with. Anyway, the mask data is also in this Data3, and is 2-bit (black or white). I have yet to check whether this is clipping mask or transparency mask (more likely the latter), but anyway the mask data is aligned to 4 bytes. Data4 - Sample Data I have never been good with sound. In short, I haven't even attempted to find out what could possibly lie in here. End of anims.j2a The next post should include an example of reading a single frame in anims.j2a. Unless I'm very lazy or out of time, in which case I'll jump to J2T and J2L.
__________________
<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> |
Apr 2, 2006, 12:49 PM | |||
Example of using anims.j2a
Just a very basic example, we'll use the first frame of the first animation of the first set for this tutorial. This assumes that you have already decompressed the first ANIM sub-file into Data1, Data2 and Data3 buffers. Peeking into Data1 - Animation Info Okay, we will just look at the first animation (8 bytes): Code:
07 00 0A 00 00 00 00 00 Peeking into Data2 - Frame Info We shall just look at the first frame defined in this buffer (in this case, the first frame of the first animation), 24 bytes long: Code:
11 00 10 00 00 00 00 00 F7 FF F5 FF 00 00 00 00 00 00 00 00 D0 05 02 00 The first "11 00" means a width of 17 pixels, "10 00" a height of 16 pixels. The "00 00" and "00 00" after it would refer to the coldspot's x and y, except a zero means that none is defined. The "F7 FF" which converts to -9 in a signed short, means that the frame starts drawing 9 pixels to the left of the hotspot. Similarly the "F5 FF" means the frame starts drawing 11 pixels above. The next four zeros obey the same rule as the coldspot. The four zeros after that indicate the starting address of the image within Data3 (image data). Note that this address is aligned to 8 bytes (always a multiple of 8). Finally the last four zeros indicate the starting address (in Data3) of the mask for this frame. This one is aligned to bytes. If the mask address is given as -1 (FF FF FF FF), then there is no mask given for it. Peeking into Data3 - Image Data Yay, we've come to the really fun part. It is slightly technical but not very difficult. If you are already familiar with RLE this concept should be a breeze. Remember we had an image address of 00000000, so we will read address 0 of Data3: Code:
11 80 10 00 06 81 23 80 03 81 24 02 82 25 24 80 81 25 01 83 25 23 25 02 82 23 25 80 85 24 23 24 22 24 02 81 25 80 85 23 24 23 21 23 01 81 25 80 88 24 25 23 40 22 25 22 25 80 89 2f ...... Code:
06 81 23 80 skips 6 bytes, then copies one byte, then NEXT 03 81 24 02 82 25 24 80 skips 3 bytes, copies 1, skips 2, copies 2 81 25 01 83 25 23 25 02 82 23 25 80 85 24 23 24 22 24 02 81 25 80 85 23 24 23 21 23 01 81 25 80 88 24 25 23 40 22 25 22 25 80 89 2f ...
__________________
<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> |
Apr 2, 2006, 12:50 PM | |
The J2L File Format
I had never really looked into the header with that much detail, so some parts here were stolen from another post, at least for the LEVL Header. This is mostly for stuff I've never thought about, like "Hide level in Home Cooked List" or passworded levels. The LEVL Header Ok, without further ado let's rewrite that as a more comprehensible struct: Code:
struct LEVL_Header { char Copyright[180]; char Magic[4] = "LEVL"; char PasswordHash[3]; //never really thought about this char HideLevel; char LevelName[32]; short Version; //I only looked at one file, had a value of 0x202 long FileSize; long CRC32; long CData1; long UData1; long CData2; long UData2; long CData3; long UData3; long CData4; long UData4; } Data1 - General Level Data Like everything else I've never had a need for research, I haven't looked into this buffer much. Someone else should probably update this. I've only looked at a few 1.23 files, and I think this has size 33517 (0x82ED) for the official levels, or 34029 (0x84ED) for levels saved in JCS. Code:
struct J2L_Data1 { Oh dear, I have no idea what the first 19 bytes are, so I'll assume char Unknown[7]; long CRC32; long Unknown; long BufferSize; char LevelName[32]; char Tileset[32]; char BonusLevel[32]; char NextLevel[32]; char SecretLevel[32]; char MusicFile[32]; char HelpString[16][512]; //I'd rather call it "Text" //Looking back, I actually don't know a lot of stuff :( } Try poking into the buffer at offset 0x20FB and you'll read something like: Code:
struct VitalLayerInfo { char DoesLayerHaveAnyTiles[8]; long LayerWidth[8]; long LayerWidthRelated[8]; //Not sure, but its similar to width? long LayerHeight[8]; } ![]() Data2 - The Event Map Each event is a long (4 bytes long), so this buffer should be (Layer4Width * Layer4Height * 4) bytes long. So, reading an event from a coordinate in the level shouldn't be too difficult at all. The hard(er) part would be to parse the events. But since each event has a different set of arguments, I'll leave this as an exercise for the reader. Data3 - The Dictionary This is probably an unusual way of defining a buffer, but I use dictionary because it contains a lot of "words", which cannot be used individually but have to be stringed together by Data4 to create something meaningful. The size of this buffer is (WordCount * 8), but I'm not sure if the number of words is actually defined in the file, so I use UData3 instead. Each word contains 4 "Tiles", and each Tile is a short which corresponds to that tile index in the J2T declared. If a Tile has its 0x1000 (4096) bit set, it is a flipped tile. There are probably other flags for translucent or caption, but I haven't gone into detail with these. As an example, battle1.j2l has a dictionary that begins like this: Code:
00 00 00 00 00 00 00 00 00 00 00 00 51 00 0F 00 0F 00 0F 00 0F 00 0F 00 0F 00 0F 00 0F 00 0E 00 word[0] = {0, 0, 0, 0} <-- an empty tile group, word[0] must always be this word[1] = {0, 0, 81, 15} <-- contains two empty tiles, then two bricks word[2] = {15, 15, 15, 15} <-- 4 bricks word[3] = {15, 15, 15, 14} <-- 4 bricks, the 4th one using a different tile Note that because it can use shorts, there can be a maximum of 65535 words (in theory). Each word is unique, so it usually a LOT less words, if similar tilegroups are used all over the level. This concept should not be difficult if you are already familiar with LWZ. Data4 - Taking words and stringing them together Okay... so that's not really a proper name for Data4, but it describes what it does. Words are only defined for LayersThatHaveAnyTiles (refer to data2). For example, battle1.j2l again: the first layer that is "defined" is layer 3, which is 128x128. The width is 128, so that means it reads 32 words per row (remember there are 4 tiles per word). Note that it rounds up, so a 124 width would read 31 words per row, but 125 would be 32. Slight correction: this part uses info from the LayerWidthRelated defined above. Or at least that's my assumption, because layer 8 pretends it has width 60 rather than the 30 we see in JCS? I don't really get it lol. Enough random chitchat ![]() Since each word is 2 bytes long, we should have 8372 words in Data4, thus a buffer size 16744. So basically, this is just a sort of "mapping" done which copies and paste those "words" in the dictionary onto a larger screen.
__________________
<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> |
Apr 2, 2006, 12:50 PM | |
The J2T File Format
My time is slowly running out, so we'll cut to the chase. Code:
struct TILE_Header { char Copyright[180]; char Magic[4] = "TILE"; long Signature = 0xAFBEADDE //looks like DEADBEAF on a hex editor char LevelName[32]; short Version; //0x200 for v1.23, 0x201 for v1.24 long FileSize; long CRC32; long CData1; long UData1; long CData2; long UData2; long CData3; long UData3; long CData4; long UData4; } For the files I checked so far, this buffer always has a size of 27652 bytes for v1.23, or 107524 bytes for TSF. This is due to it supporting up to 4096 tiles instead of the original 1024. Code:
#if version = 0x0200 then MAX_TILES = 1024 #if version = 0x0201 then MAX_TILES = 4096 struct TilesetInfo { long PaletteColor[256]; //in the format {red, green, blue, zero} long TileCount; //number of tiles, always a multiple of 10 char Unknown[MAX_TILES]; //I suspect this has something to do with transparency; haven't looked at it yet char Zeros[MAX_TILES]; //Unsure, all zeros? long ImageAddress[MAX_TILES]; long Zeros[MAX_TILES]; long TMaskAddress[MAX_TILES]; //Transparency masking, for bitblt long Zeros[MAX_TILES]; long MaskAddress[MAX_TILES]; //Clipping mask, or the "tile mask" as it is called in JCS long FMaskAddress[MAX_TILES]; //flipped version of the above } In specific, ImageAddress reads from Data2, TMaskAddress reads from Data3, MaskAddress and FMaskAddress reads from Data4, and you should probably memorise this to avoid confusion later on. If you are planning to make a tileset extractor of some sort, you would probably need only to use ImageAddress and MaskAddress. Data2 - Image Address Each image is 1024 bytes long, uncompressed. Each byte represents a palette index, so you have a 32*32 picture just by reading each byte as a pixel. Note that each image here is unique, so for example if your tileset has a blank space in say, tile #42, then ImageAddress[42] will be equal to zero (offset zero in the Data2 buffer). Data3 - Transparency Mask Similar to Data2, except each mask is 128 bytes. This is because the mask is only 1 bit-per-pixel (black or white). If you are trying to extract mask information into a picture for the first time, expect some problems with bit orders; I can't remember whether it reads MSB-first or LSB-first. Data4 - Clipping Mask Same notes as Data3. Other notes Remember that Data2 is not equivalent to the actual tileset graphics as seen in JCS, and Data4 is not the mask either. Data2 simply contains an image of each unique tile in the set, likewise with Data4. You must read Data1 if you want to find out exactly what image and mask a particular tile# has. EDIT: Very minor edit, don't ask.
__________________
<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; Apr 2, 2006 at 01:13 PM. |
Apr 2, 2006, 12:51 PM | |
Post-release notes
We've only covered 3 file formats (J2A, J2T, J2L) in the series. If you're adventurous enough, you could use the same methods to hack the other JJ2 file formats. This is sort of like a parting gift from me, and I hope you will have more use for it than I did. I really hope that when I get back here (maybe 6 months?) there will be some useful programs and new ERE entries containing this information ![]()
__________________
<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> |
Apr 2, 2006, 01:03 PM | |
I haven't a CLUE what this is all about but DANG you rock! =D
|
Apr 2, 2006, 01:05 PM | |
OMG 7-double post!
|
Apr 2, 2006, 01:16 PM | |
Once again, awesome work...
I can't wait to get back to working with this stuff.. (I didn't know J2L except for the header and first part of Data1, or a fair bit of J2A data2) I'll try to find some more useful code/documentation I made to share later.. one thing I guess I'll have to double check is where you have: (in J2T) char Unknown[256]; //I suspect this has something to do with transparency; haven't looked at it yet I have a longer thing written down (entirely possible I had expanded it based on the bits), but I think it flagged tiles that either had some transparency and/or some pure white I think (probably a bug or white used to indicate transparent or somesuch..).. a PHP J2T decoder and a couple comments I gave someone..: it's slow and poorly made (requires GD, only does image [no mask], doesn't really do anything cleanly), but it's more of a proof of concept that you can do stuff in non-C/C++/VB/whatever... PHP Code:
|
Apr 2, 2006, 01:50 PM | ||
Quote:
![]() I have never learnt any PHP beyond "hello world", but it sure looks like great stuff (it actually looks a lot like C/C++) ![]() I've never actually used most of the information in J2T either, I just did some last-minute research to provide closure for my article. So I'll be really glad if people will fill in the missing information, publish it in ERE, all in time for my "first return visit".
__________________
<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> |
Apr 2, 2006, 02:03 PM | |
That's pure gobbledegook to me, but I'm hoping it can be put to some practical purpose.
__________________
"If there's anything more important than my ego, I want it caught and shot now." -Zaphod Beeblebrox |
Apr 2, 2006, 02:07 PM | ||
Quote:
|
Apr 2, 2006, 02:16 PM | |
Awesome. I didn't have the attention span to actually read all of that.
__________________
"If there's anything more important than my ego, I want it caught and shot now." -Zaphod Beeblebrox |
Apr 2, 2006, 04:58 PM | |
If you need a fully featured example, I've released an open-source Tileset Extractor which supports both v1.23 and TSF. I've never had any use for it, but others might, and besides J2T is the easiest format to understand, yada yada etc...
__________________
<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> |
Apr 2, 2006, 10:46 PM | |||
Neobeo, you own dude! I would say keep up the good work but unfortunately that wouldn't be very easy considering where you are off to :\
__________________
Quote:
Quote:
|
Apr 2, 2006, 11:15 PM | |
OMG
|
Apr 3, 2006, 12:03 AM | ||
Quote:
|
Apr 3, 2006, 08:06 AM | |
Awesome. Don't understand much yet, but I'm on the way.
![]()
__________________
"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) |
Apr 4, 2006, 05:15 PM | ||
Quote:
![]()
__________________
![]() |
Apr 4, 2006, 08:01 PM | |
School keeps me too busy right now... maybe late May?
Sorry about that Ice M A N. |
Apr 5, 2006, 01:14 AM | ||
Quote:
![]() (download Section) Slow ? Code:
public bool Excist(string name) { //database stuff. Discover if the filename is allready in the DB. return result; } public string GetUrl(string tilename) { //Get url from database. return url; } public string GenerateUrl(string tilename) { string url = ""; if(Excist(tilename)) { //generate image //database stuff... //Set string. } else{ GetUrl(string tilename) } return url; } Last edited by Marijn; Apr 5, 2006 at 04:51 AM. |
Apr 5, 2006, 07:04 AM | |
J2O is written in PHP, foo.
Code:
$aTilesetData = /* some code to retrieve upload info from database */; if(is_readable('rendered/'.$aTilesetData['filename'].$aTilesetData['uploadtime']'.png')) { //rendered image exists } else { //generate image and save } ![]() |
Apr 5, 2006, 08:04 AM | ||
yes yes... I suggested that obvious solution as early as the code was made... it's just a matter of getting around to doing it, taking into account the tileset would need to be readjusted when reuploaded, what if a pack includes a tileset already "generated" (could use a combination filesize name and/or hash in the J2T file) etc.. (plus does J2O's host have GD enabled?)
Quote:
|
Apr 5, 2006, 10:21 AM | ||||
It seems a lot more practical for the uploader to include screenshots, though. The tileset itself only shows so much, while a screenshot can be a lot more informative.
Here's an idea: What about something that could take a scaled screenshot of an entire level (or rather, all the layers where the X and Y speeds equaled 1) with perhaps all of the visible events? I know Neobeo practically made something like this already, but I think it would have its uses.
__________________
|
Apr 5, 2006, 10:29 AM | |
Also (I and Bob spoke about that before) you would have issues with wether uploaders from the past actually wanted screenshots of their levels on J2O. Issue would be wether to only work with permission (= most old tilesets won't have a preview) or not. J2C posted screenies from tilesets but that wasn't retrospective I think.
__________________
Interesting Jazz-related links: Thread: Gameplay Theories - Thread: Make Up Your Own Gametype |
Apr 5, 2006, 11:22 AM | ||
Quote:
*neobeo (apparently?) didn't find where the X and Y speeds are (although it should be trivial to find that.. I just don't have JCS/JJ2 so it makes it difficult).. and it would also need to handle layers that tile, etc... *once J2A edits start coming out, the visible events will change (we could do JCS-style labels, but it wouldn't really be useful in a scaled image...)... even using the standard anims would somewhat difficult, requireing prodding around in anims.j2a.. actually.. no.. we could just preprocess it and get the first frame of each event and just use those PNGs or whatever to composite the image.. *I'd guess it'd be amazingly slow in PHP (which begs the question does the J2O server allow for PHP to run an external C/whatever program somehow..?) *Should we worry about CliffyB style hidden messages in levels (a la JJ1) *What about retroactive appication to the current uploads (as fquist was talking about) Of course I was planning on doing something like what you said anyway (a complete composite view of levels) once school is out (i.e. this summer) and perhaps starting a 2D game compatible with JJ2 files... an "OpenJazz2" if you will.. And then again, as you said, just having the uploader provide a preview image would be more practical... |
Apr 5, 2006, 11:26 AM | ||
Quote:
__________________
![]() |
Apr 5, 2006, 07:42 PM | |
A few thoughts on tileset previews for J2O.
The images should be kept lightweight, as in small in file size. The purpose of a preview is just to let the user get a quick glance at what the tileset looks like without having to download it and all. Therefore, the quality of the preview image can be dropped down greatly in order to save on file sizes (and download times). I'm thinking scaling the image down to 50%, and save it as a low-quality JPEG so that the compression can be way up. See if you can use a compiled (c?) version of this conversion program on the server as PHP or other scripting languages are relatively slow. Then just invoke the compiled program from the site script. The images should obviously be cached. If there's a concern about size, delete preview images that haven't been viewed in over a month or so. Generate preview images on demand.
__________________
<div style="float: right; width: 100px; height: 70px; margin: 5px 15px;"><img src="http://madskills.org/monolith/idleserver.gif" style="width: 98px; height: 65px;"><img src="http://madskills.org/monolith/theserver.gif" style="width: 98px; height: 65px; position: relative; top: -65px;"></div><div style="margin: 0 3em; font-size: 80%; font-style: italic;">Love is patient, love is kind. It does not envy, it does not boast, it is not proud. It is not rude, it is not self-seeking, it is not easily angered, it keeps no record of wrongs. Love does not delight in evil but rejoices with the truth. It always protects, always trusts, always hopes, always perseveres.</div><div style="text-align: right; text-size: 80%;">1 Corinthians 13:4-7</div> |
Apr 6, 2006, 06:18 AM | |||||
Quote:
Also, I didn't mean to imply my entire-level screenshot idea would be done dynamically on J2O. It would just be a program that people may choose to use.
__________________
|
Apr 7, 2006, 12:28 PM | |
Neobeo you should also make a 1.24 to 1.23 J2L converter.
The JCS patch I released a while ago really sux bad.
__________________
<a href="http://nmap.org/"><img border="0" alt="Nmap Security Scanner" src="http://images.insecure.org/nmap/images/prop/nmap_bnr_matrix_pfos.gif"/></a> |
Apr 7, 2006, 08:50 PM | ||
Quote:
If you see a chatlog in any of them, that was made in VB, and has been abandoned in place of a C++ one. So... http://www.files.bz/files/5589/vcrtest1.png http://www.files.bz/files/5589/vcrtest2.png http://www.files.bz/files/5589/vcrtest3.png http://www.files.bz/files/5589/vcrtest4.png http://www.files.bz/files/5589/vcrchat1.png http://www.files.bz/files/5589/vcrchat2.png http://www.files.bz/files/5589/vcrchat3.png http://www.files.bz/files/5589/vcrchat4.png (Coincidence? I just noticed today that all the vcrchat screenshots have BlurredD in them) About anyone discussing tileset screenshots, (and if you haven't noticed), I uploaded an opensource tileset extractor. As for OpenJazz2, it sounds great and good luck with it. I actually tried making a Jazz2 implementation once, more of an ASM to C++ decompilation rather than a re-creation of the game engine, and haven't really gotten that far. Finally, the 1.24 to 1.23 J2L converter should work, except I have never made a 1.24 level and therefore have no idea what the differences are.
__________________
<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> |
![]() |
«
Previous Thread
|
Next Thread
»
Thread Tools | |
|
|
All times are GMT -8. The time now is 03:55 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.