Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Tileset Extractor | Neobeo | Utility | 9.9 |
/* tileset.cpp
*
* A solitary file, which contains
* code for extracting JJ2 tilesets
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
#pragma pack(1)
struct TILE_Header { //262 bytes
char Copyright[180];
char Magic[4];
int Signature;
char LevelName[32];
short Version;
int FileSize;
int CRC32;
int CData1;
int UData1;
int CData2;
int UData2;
int CData3;
int UData3;
int CData4;
int UData4;
} tileHeader;
struct TilesetInfo123 { //27652 bytes
int PaletteColor[256];
int TileCount;
char FastBlit[1024];
char Zeros1[1024];
int ImageAddress[1024];
int Zeros2[1024];
int TMaskAddress[1024];
int Zeros3[1024];
int MaskAddress[1024];
int FMaskAddress[1024];
} tile123;
struct TilesetInfo124 { //107524 bytes
int PaletteColor[256];
int TileCount;
char FastBlit[4096];
char Zeros1[4096];
int ImageAddress[4096];
int Zeros2[4096];
int TMaskAddress[4096];
int Zeros3[4096];
int MaskAddress[4096];
int FMaskAddress[4096];
} tile124;
char *ErrorMsg[] = {
"No error",
"Filename too short"
"Not a J2T file",
"Error opening file",
"Error during decompression",
"Unknown version of J2T",
"File has invalid number of tiles",
"Error during saving process"
};
enum {
NOERROR = 0,
FILENAMETOOSHORT,
FILENOTJ2T,
CANNOTOPENFILE,
ERRORREADING,
ERRORUNCOMPRESSING,
UNKNOWNVERSION,
BADNUMBEROFTILES,
CANNOTSAVE,
};
char *Data2, *Data4;
char imgBuffer[409 * 32][10][32];
int ROWS;
png_bytep *rowPointers;
png_color palette[256];
png_color monochrome[2] = {87, 0, 203, 0, 0, 0};
/****************** End of defines/declares and start of code ******************/
//Haha, I stole this snippet from someone, and can't
//remember who or where, so I'll leave it uncredited
//but basically what it does is switch the bit order
unsigned char ReverseBits(unsigned char b) {
return (unsigned char)(((b * 0x0802LU & 0x22110LU) |
(b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
}
//Reads from the file, then uncompresses it into the buffer
bool ReadUncompress(FILE *fileRead, char *buffer, int lenInput, int lenOutput) {
char *in = (char*)malloc(lenInput);
fread(in, 1, lenInput, fileRead);
int ret = uncompress((unsigned char*)buffer,
(unsigned long*)&lenOutput, (unsigned char*)in, lenInput);
free(in);
return (ret != 0);
}
//Uses rows from rowPointers to save to a picture
bool SavePicture(char *Arg1, char *Arg2, bool UseMonochromePalette) {
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
int BPP;
char strFO[256];
strcpy(strFO, Arg1);
strcat(strFO, Arg2);
FILE *fo = fopen(strFO, "wb");
if (!fo) return true;
png_init_io(png_ptr, fo);
//one-time switch
if (UseMonochromePalette) {
png_set_PLTE(png_ptr, info_ptr, monochrome, 2);
BPP = 1;
} else {
png_set_PLTE(png_ptr, info_ptr, palette, 256);
BPP = 8;
}
png_set_IHDR(png_ptr, info_ptr, 320, ROWS * 32, BPP, PNG_COLOR_TYPE_PALETTE,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_rows(png_ptr, info_ptr, rowPointers);
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, png_voidp_NULL);
png_write_end(png_ptr, NULL);
png_destroy_write_struct(&png_ptr, NULL);
return false;
}
//The part that extracts tilesets
int ExtractTileset(char *FileName) {
int i, j, k, l; //General-purpose local variables
//Makes sure file is at least 5 chars int
char Extension[4];
int lenFileName = strlen(FileName);
if (lenFileName < 5) return FILENAMETOOSHORT;
//Makes sure that filename ends with .j2t
for (i = 0; i < 4; ++i)
Extension[i] = tolower(FileName[lenFileName - 4 + i]);
if (memcmp(Extension, ".j2t", 4))
return FILENOTJ2T;
char BaseFileName[256] = {0};
memcpy(BaseFileName, FileName, strlen(FileName) - 4);
//Reads the 262-byte header
FILE *fTileset = fopen(FileName, "rb");
if (!fTileset)
return CANNOTOPENFILE;
if (!fread(&tileHeader, 1, sizeof(TILE_Header), fTileset)) {
fclose(fTileset);
return ERRORREADING;
}
//Reads Data1 into info123 or info124, depending on version
if (tileHeader.Version == 0x200) {
if (ReadUncompress(fTileset, (char *)&tile123, tileHeader.CData1, tileHeader.UData1)) {
fclose(fTileset);
return ERRORUNCOMPRESSING;
}
//copies data to tile124, to minimize switches between 1.23 and 1.24
//there's probably an easier way to do this, by my C++ is not refined
memcpy(&tile124.PaletteColor, &tile123.PaletteColor, 1028);
memcpy(&tile124.FastBlit, &tile123.FastBlit, 1024);
memcpy(&tile124.Zeros1, &tile123.Zeros1, 1024);
memcpy(&tile124.ImageAddress, &tile123.ImageAddress, 4096);
memcpy(&tile124.Zeros2, &tile123.Zeros2, 4096);
memcpy(&tile124.TMaskAddress, &tile123.TMaskAddress, 4096);
memcpy(&tile124.Zeros3, &tile123.Zeros3, 4096);
memcpy(&tile124.MaskAddress, &tile123.MaskAddress, 4096);
memcpy(&tile124.FMaskAddress, &tile123.FMaskAddress, 4096);
} else if (tileHeader.Version == 0x201) {
if (ReadUncompress(fTileset, (char *)&tile124, tileHeader.CData1, tileHeader.UData1)) {
fclose(fTileset);
return ERRORUNCOMPRESSING;
}
} else {
fclose(fTileset);
return UNKNOWNVERSION;
}
//If v1.23 tileset has > 1020 tiles, or v1.24 tileset
//has > 4090 tiles, or if tiles is not multiple of 10
if ((tileHeader.Version == 0x200 && tile123.TileCount > 1020) ||
tile124.TileCount > 4090 || tile124.TileCount % 10) {
fclose(fTileset);
return BADNUMBEROFTILES;
}
//Reads uncompressed Data2
Data2 = (char*)malloc(tileHeader.UData2);
if (ReadUncompress(fTileset, Data2, tileHeader.CData2, tileHeader.UData2)) {
fclose(fTileset);
return ERRORUNCOMPRESSING;
}
//Reads uncompressed Data4
fseek(fTileset, tileHeader.CData3, SEEK_CUR);
Data4 = (char*)malloc(tileHeader.UData4);
if (ReadUncompress(fTileset, Data4, tileHeader.CData4, tileHeader.UData4)) {
fclose(fTileset);
return ERRORUNCOMPRESSING;
}
//The fun part -- actually copying the tileset into JCS format
ROWS = tile124.TileCount / 10;
for (i = 0; i < ROWS; ++i) { //tilerow 1 to ROWS for the current tileset
for (j = 0; j < 10; ++j) { //tile 1 to 10 for the current tilerow
for (k = 0; k < 32; ++k) { //pixelrow 1 to 32 for the current tile
memcpy(imgBuffer[i * 32 + k][j],
&Data2[tile124.ImageAddress[i * 10 + j] + k * 32], 32);
}
}
}
//Just initialize rowPointers to point to imgBuffer, only needs to be done once
rowPointers = (png_bytep*)malloc(ROWS * 32 * sizeof(png_bytep));
for (i = 0; i < ROWS * 32; ++i) //sets the rowpointer for each
rowPointers[i] = (unsigned char*)imgBuffer[i];
//Copy tileset palette to png palette, and set our own palette[0]
memcpy(palette, monochrome, 3);
for (i = 1; i < 256; ++i)
memcpy(&palette[i], &tile124.PaletteColor[i], 3);
//Finally we save the image to a file
if (SavePicture(BaseFileName, "-image.png", false))
return CANNOTSAVE;
//The image png is done, now we do the mask png:
//You should not recycle imgBuffer like I did, I was just lazy
ROWS = tile124.TileCount / 10;
for (i = 0; i < ROWS; ++i) { //tilerow 1 to ROWS for the current tileset
for (j = 0; j < 10; ++j) { //tile 1 to 10 for the current tilerow
for (k = 0; k < 32; ++k) { //pixelrow 1 to 32 for the current tile
for (l = 0; l < 4; ++l) { //only 4 bytes (32 bits) per pixelrow
imgBuffer[i * 32 + k][0][j * 4 + l] = //we "cheat" the array =(
ReverseBits(Data4[tile124.MaskAddress[i * 10 + j] + k * 4 + l]);
}
}
}
}
//Finally we save the image to a file
if (SavePicture(BaseFileName, "-mask.png", true))
return CANNOTSAVE;
free(Data2);
free(Data4);
fclose(fTileset);
return 0;
}
//Program starts here
int main(int argc, char *argv[]) {
bool BreakAtEnd = false;
//Must have at least one argument
if (argc < 2) {
printf("I need files.");
getc(stdin);
return 0;
}
//Parses each file individually
for (int i = 1; i < argc; ++i) {
int ret = ExtractTileset(argv[i]);
if (ret) {
printf("Error parsing %s\nReason: %s\n", argv[i], ErrorMsg[ret]);
BreakAtEnd = true;
} else {
printf("%s succeeded\n", argv[i]);
}
}
if (BreakAtEnd) getc(stdin);
return 0;
}
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.