~ndiddy/satconv

a0a0de4eb3804f34dc9157673c4eed626a8a088c — Nathan Misner 2 years ago abdaee2
fixed rgb sprite output, added 4bpp support to qdbmp
7 files changed, 130 insertions(+), 29 deletions(-)

M Makefile
M qdbmp.c
M readme.txt
M satconv
M satconv.c
M sprite.c
M sprite.h
M Makefile => Makefile +1 -1
@@ 1,7 1,7 @@
CC = gcc

TARGET = satconv
CFLAGS = -g -std=gnu99 -Wall -DEZXML_NOMMAP
CFLAGS = -g -std=gnu99 -Wall -fsanitize=address -DEZXML_NOMMAP

OBJECTS = satconv.o ezxml.o qdbmp.o map.o sprite.o tile.o


M qdbmp.c => qdbmp.c +19 -1
@@ 442,6 442,7 @@ USHORT BMP_GetDepth( BMP* bmp )
void BMP_GetPixelRGB( BMP* bmp, UINT x, UINT y, UCHAR* r, UCHAR* g, UCHAR* b )
{
	UCHAR*	pixel;
    UCHAR   pixel_index;
	UINT	bytes_per_row;
	UCHAR	bytes_per_pixel;



@@ 465,7 466,24 @@ void BMP_GetPixelRGB( BMP* bmp, UINT x, UINT y, UCHAR* r, UCHAR* g, UCHAR* b )
		/* In indexed color mode the pixel's value is an index within the palette */
		if ( bmp->Palette != NULL )
		{
			pixel = bmp->Palette + *pixel * 4; //TODO - por para 4bpp
            if (bmp->Header.BitsPerPixel == 8) {
                pixel = bmp->Palette + *pixel * 4;
            }
            else if (bmp->Header.BitsPerPixel == 4) {
                pixel_index = bmp->Data[((bmp->Header.Height - y - 1 ) * bytes_per_row) + (x / 2)];
                printf("full index: 0x%02x ", pixel_index);
                if (x % 2 == 0) { // odd pixels
                    printf(" odd ");
                    pixel_index >>= 4;
                    pixel_index &= 0xf;
                }
                else { // even pixels
                    printf(" even ");
                    pixel_index &= 0xf;
                }
                printf("4bpp! x: %lu, y: %lu, index: %d\n", x, y, pixel_index);
                pixel = bmp->Palette + (pixel_index * 4);
            }
		}

		/* Note: colors are stored in BGR order */

M readme.txt => readme.txt +17 -3
@@ 2,8 2,13 @@ Satconv is free software and is GPLv3+ licensed.

---Satconv asset list file format---

s directory: Reads all the .bmp images in the named directory and outputs a
             .spr file.
s4 directory: Reads all the .bmp images in the named directory and outputs a
              palette .spr file. All files must be in numerical order starting
              at 0.

sr directory: Reads all the .bmp images in the named directory and outputs an
              RGB .spr file. All files must be in numerical order starting
              at 0.

m41 map.tmx: Converts map.tmx (Tiled XML) to a .map file (4bpp graphics and SCL_PN_10BIT format)
m42 map.tmx: Converts map.tmx (Tiled XML) to a .map file (4bpp graphics and 2 word format)


@@ 14,7 19,8 @@ t0 file.bmp: Converts file.bmp to a framebuffer graphics file
t1 file.bmp: Converts file.bmp to a .tle file (8x8 tile size)
t2 file.bmp: Converts file.bmp to a .tle file (16x16 tile size)

---spr file format---
---palette spr file format---
4 bytes: 0x0000
4 bytes: number of palettes
n bytes: palette data
4 bytes: number of sprites


@@ 24,6 30,14 @@ n bytes: palette data
4 bytes: sprite palette number
n bytes: sprite tile data

---rgb spr file format---
4 bytes: 0x0001
4 bytes: number of sprites
--- each sprite entry ---
4 bytes: sprite x size
4 bytes: sprite y size
n bytes: sprite tile data

---map file format---
4 bytes: map x size
4 bytes: map y size

M satconv => satconv +0 -0
M satconv.c => satconv.c +22 -2
@@ 47,21 47,40 @@ int main(int argc, char **argv) {
		char *infile;
		char *ext;
		int ext_index;
		int type;
		#define OUTFILE_LEN (32)
		char outfile[OUTFILE_LEN];

		switch(line[0]) {
			// sprite directory
			case 's':
				infile = &line[2];
			case 'S':
				switch(line[1]) {
					case '4':
						type = SPRITE_4BPP;
						break;

					case 'r':
					case 'R':
						type = SPRITE_RGB;
						break;

					default:
						printf("Invalid sprite type %c\nBad line: %s\n", line[1], line);
						continue;
				}

				infile = &line[3];
				// cd-rom filesystem only allows 8.3 filenames
				ext_index = MIN(8, strlen(infile));
				memcpy(outfile, infile, ext_index);
				strcpy(outfile + ext_index, ".spr");
				sprite_process(infile, outfile);
				sprite_process(infile, outfile, type);
				break;
			
			// tile graphics
			case 't':
			case 'T':
				infile = &line[3];
				ext = strchr(infile, '.');
				ext_index = MIN(8, ext - infile);


@@ 72,6 91,7 @@ int main(int argc, char **argv) {
			
			// tilemap
			case 'm':
			case 'M':
				infile = &line[4];
				ext = strchr(infile, '.');
				ext_index = MIN(8, ext - infile);

M sprite.c => sprite.c +65 -21
@@ 31,6 31,8 @@
#include "qdbmp.h"
#include "sprite.h"

#define  RGB16_COLOR(r,g,b) (((b)<<10) + ((g)<<5) + (r) + 0x8000)

#define PALETTE_SIZE (16)
static uint32_t **palette_list;
static int palette_cursor;


@@ 45,11 47,9 @@ typedef struct {
static IMAGE_INFO *info_list;
static int info_cursor;

void sprite_convert(BMP *tile) {
void Sprite_Convert4BPP(BMP *tile) {
	uint32_t palette_buffer[PALETTE_SIZE];
	uint8_t r;
	uint8_t g;
	uint8_t b;
	uint8_t r, g, b;
	
	IMAGE_INFO *info = &info_list[info_cursor];
	info_cursor++;


@@ 100,9 100,32 @@ void sprite_convert(BMP *tile) {
	}
}

void Sprite_ConvertRGB(BMP *tile) {
	uint8_t r, g, b;
	uint16_t color;

	IMAGE_INFO *info = &info_list[info_cursor];
	info_cursor++;
	info->x = BMP_GetWidth(tile);
	info->y = BMP_GetHeight(tile);
	info->graphics = malloc(info->x * info->y * 2);

	int graphicsCursor = 0;
	for (int y = 0; y < info->y; y++) {
		for (int x = 0; x < info->x; x++) {
			BMP_GetPixelRGB(tile, x, y, &r, &g, &b);
			r >>= 3; g >>= 3; b >>= 3;
			color = RGB16_COLOR(r, g, b);
			color = htons(color);
			memcpy(&(info->graphics[graphicsCursor]), &color, sizeof(color));
			graphicsCursor += 2;
		}
	}
}

#define FILENAME_BUFLEN (256)

int sprite_process(char *dirname, char *outname) {
int sprite_process(char *dirname, char *outname, int type) {
	struct dirent *entry;
	DIR *dir_ptr;
	palette_cursor = 0;


@@ 117,7 140,7 @@ int sprite_process(char *dirname, char *outname) {
		return 0;
	}
	while ((entry = readdir(dir_ptr)) != NULL) {
		if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
		if ((entry->d_name[0] == '.') || (strcmp(entry->d_name, "..") == 0)) {
			continue;
		}
		if (entry->d_type == DT_REG) {


@@ 128,9 151,11 @@ int sprite_process(char *dirname, char *outname) {
	
	// allocate memory based on the number of files
	// the maximum number of palettes possible is one per frame
	palette_list = malloc(num_files * sizeof(uint32_t *));
	for (int i = 0; i < num_files; i++) {
		palette_list[i] = malloc(PALETTE_SIZE * sizeof(uint32_t));
	if (type == SPRITE_4BPP) {
		palette_list = malloc(num_files * sizeof(uint32_t *));
		for (int i = 0; i < num_files; i++) {
			palette_list[i] = malloc(PALETTE_SIZE * sizeof(uint32_t));
		}
	}
	info_list = malloc(num_files * sizeof(IMAGE_INFO));
	


@@ 141,15 166,21 @@ int sprite_process(char *dirname, char *outname) {
			printf("Error: overran filename buffer by %d bytes. Increase FILENAME_BUFLEN define in sprite.c.\n", retval);
			return 0;
		}
		printf("Reading %d %s\n", type, filename);
		BMP *tiledata = BMP_ReadFile(filename);
		BMP_CHECK_ERROR(stdout, 0);
		if (BMP_GetDepth(tiledata) != 4) {
			printf("Error: file %s isn't 4bpp.\n", entry->d_name);
			BMP_Free(tiledata);
			closedir(dir_ptr);
			return 0;
		if (type == SPRITE_4BPP) {
			if (BMP_GetDepth(tiledata) != 4) {
				printf("Error: file %s isn't 4bpp.\n", entry->d_name);
				BMP_Free(tiledata);
				closedir(dir_ptr);
				return 0;
			}
			Sprite_Convert4BPP(tiledata);
		}
		else {
			Sprite_ConvertRGB(tiledata);
		}
		sprite_convert(tiledata);
		BMP_Free(tiledata);
	}
	// write out the sprite data


@@ 165,9 196,15 @@ int sprite_process(char *dirname, char *outname) {
		return 0;
	}
	// saturn is big-endian so all the data over one byte must be byteswapped
	// write type
	int type_be = htonl(type);
	fwrite(&type_be, sizeof(type_be), 1, out);

	// write number of palettes
	int palette_cursor_be = htonl(palette_cursor);
	fwrite(&palette_cursor_be, sizeof(palette_cursor_be), 1, out);
	if (type == SPRITE_4BPP) {
		int palette_cursor_be = htonl(palette_cursor);
		fwrite(&palette_cursor_be, sizeof(palette_cursor_be), 1, out);
	}
	// write all the palettes (they're already byteswapped)
	for (int i = 0; i < palette_cursor; i++) {
		printf("Writing palette %d\n", i);


@@ 181,16 218,23 @@ int sprite_process(char *dirname, char *outname) {
	for (int i = 0; i < num_files; i++) {
		tmp_x = htonl(info_list[i].x);
		tmp_y = htonl(info_list[i].y);
		tmp_pal = htonl(info_list[i].pal);
		fwrite(&tmp_x, sizeof(tmp_x), 1, out);
		fwrite(&tmp_y, sizeof(tmp_y), 1, out);
		fwrite(&tmp_pal, sizeof(tmp_pal), 1, out);
		fwrite(info_list[i].graphics, sizeof(uint8_t), (info_list[i].x >> 1) * info_list[i].y, out);
		if (type == SPRITE_4BPP) {
			tmp_pal = htonl(info_list[i].pal);
			fwrite(&tmp_pal, sizeof(tmp_pal), 1, out);
			fwrite(info_list[i].graphics, sizeof(uint8_t), (info_list[i].x >> 1) * info_list[i].y, out);
		}
		else if (type == SPRITE_RGB) {
			fwrite(info_list[i].graphics, sizeof(uint8_t), info_list[i].x * info_list[i].y * 2, out);
		}
	}
	fclose(out);	
	// clean up memory
	for (int i = 0; i < num_files; i++) {
		free(palette_list[i]);
		if (type == SPRITE_4BPP) {
			free(palette_list[i]);
		}
		free(info_list[i].graphics);
	}
	free(palette_list);

M sprite.h => sprite.h +6 -1
@@ 20,9 20,14 @@
#ifndef SPRITE_H
#define SPRITE_H

typedef enum {
	SPRITE_4BPP = 0,
	SPRITE_RGB,
} SPRITE_TYPE;

// converts a directory of sprite graphics into one file.
// dirname: path to the graphics directory
// outfile: the file to write to
int sprite_process(char *dirname, char *outname);
int sprite_process(char *dirname, char *outname, int type);

#endif