Downloads containing tileset.cpp

Downloads
Name Author Game Mode Rating
Tileset ExtractorFeatured Download Neobeo Utility 9.9 Download file

File preview

/* 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;
}