2020-07-06 22:14:50 +00:00
# if defined(REC98_PROJECT)
# include "platform.h" /* see also [https://github.com/nmlgc/ReC98/issues/8] */
2020-07-09 07:37:00 +00:00
# elif defined(_MSC_VER) /*Microsoft C++*/ || defined(__BORLANDC__) || defined(__TURBOC__)
2020-07-06 22:14:50 +00:00
typedef unsigned char uint8_t ;
typedef unsigned short uint16_t ;
typedef unsigned long uint32_t ;
# else
# include <stdint.h>
# endif
2020-07-09 07:37:00 +00:00
# if defined(_MSC_VER) /*Microsoft C++*/ || defined(__BORLANDC__) || defined(__TURBOC__)
2020-07-06 22:14:50 +00:00
# include <io.h>
# else
# include <sys / types.h>
# include <sys / stat.h>
# include <unistd.h>
# endif
# include <stdlib.h>
# include <string.h>
# include <malloc.h>
# include <stdio.h>
# include <fcntl.h>
# if defined(__GNUC__)
# include <endian.h>
# else
# define le16toh(x) (x)
# define le32toh(x) (x)
# define htole16(x) (x)
# define htole32(x) (x)
# endif
# include "bmp2arrl.h"
/* O_BINARY is needed for MS-DOS, Windows, etc.
* Linux , Mac OS X , etc . do not have O_BINARY */
# ifndef O_BINARY
# define O_BINARY (0)
# endif
static unsigned char bmp_tmp [ 128 ] ; /* more than enough */
/*
typedef struct tagBITMAPFILEHEADER {
WORD bfType ; + 0
DWORD bfSize ; + 2
WORD bfReserved1 ; + 6
WORD bfReserved2 ; + 8
DWORD bfOffBits ; + 10
} BITMAPFILEHEADER , * LPBITMAPFILEHEADER , * PBITMAPFILEHEADER ; = 14
typedef struct tagBITMAPINFOHEADER {
DWORD biSize ; + 0
LONG biWidth ; + 4
LONG biHeight ; + 8
WORD biPlanes ; + 12
WORD biBitCount ; + 14
DWORD biCompression ; + 16
DWORD biSizeImage ; + 20
LONG biXPelsPerMeter ; + 24
LONG biYPelsPerMeter ; + 28
DWORD biClrUsed ; + 32
DWORD biClrImportant ; + 36
} BITMAPINFOHEADER , * PBITMAPINFOHEADER ; = 40
*/
2020-09-04 20:29:54 +00:00
/* error reporting */
static const char * BMP2ARR_PARAMETERLESS_ERRORS [ BMP2ARR_ERROR_COUNT ] = {
/* SUCCESS */ " Success " ,
/* INTERNAL_ERROR_ERROR */ " bmp2arr_error structure used incorrectly " ,
/* INTERNAL_NULLPTR */ " Tried to dereference a null pointer " ,
/* INTERNAL_ARRAY_TOO_SMALL */ " Internal array too small " ,
/* EXPECTED_ARGUMENT */ NULL ,
/* EXPECTED_SWITCH */ NULL ,
/* MISSING_INPUT_BMP */ " Input BMP file name required (-i) " ,
/* MISSING_OUTPUT_FILE */ " Output file name required (-o) " ,
2020-09-04 20:42:33 +00:00
/* MISSING_OUTPUT_TYPE */ " Output type required (-of) " ,
2020-09-04 20:29:54 +00:00
/* MISSING_SPRITE_WIDTH */ " Sprite width required (-sw) " ,
/* MISSING_SPRITE_HEIGHT */ " Sprite height required (-sh) " ,
/* INVALID_SWITCH */ NULL ,
/* INVALID_OUTPUT_TYPE */ " Invalid output type; must be (asm | bin | bmp | c) " ,
/* INVALID_PRESHIFT */ " Invalid preshift type, must be (outer | inner) " ,
/* INVALID_PRESHIFT_WIDTH */ " Pre-shifting is only supported for 8-pixel wide sprites " ,
/* INVALID_SPRITE_WIDTH */ " Sprite width must be 8 or 16 " ,
/* INVALID_SPRITE_HEIGHT */ " Sprite height must be between 1 and 32 " ,
/* INPUT_OPEN_ERROR */ NULL ,
/* INPUT_OUT_OF_MEMORY */ " Not enough memory to read the input BMP " ,
/* INPUT_INVALID */ " Error reading input file (not a valid BMP?) " ,
/* INPUT_TOO_SMALL */ " Input BMP is too small to contain even a single sprite " ,
2020-09-04 22:24:58 +00:00
/* INPUT_COLOR_AT */ NULL ,
2020-09-04 20:29:54 +00:00
/* INPUT_ALREADY_LOADED */ " Input BMP already loaded " ,
/* OUTPUT_NO_INPUT_LOADED */ " Input BMP needs to be loaded before saving the output file " ,
/* OUTPUT_OPEN_ERROR */ NULL ,
/* OUTPUT_IO_ERROR */ " Error writing output file " ,
} ;
static enum bmp2arr_error nullptr_error ( void ) {
fprintf ( stderr , " %s \n " , BMP2ARR_PARAMETERLESS_ERRORS [ INTERNAL_NULLPTR ] ) ;
return INTERNAL_NULLPTR ;
}
enum bmp2arr_error bmp2arr_error_report ( struct bmp2arr_error_info * err ) {
if ( err = = NULL ) {
return nullptr_error ( ) ;
}
switch ( err - > type ) {
case EXPECTED_ARGUMENT :
fprintf ( stderr , " *** Error: Missing argument for '-%s' \n " , err - > param_str ) ;
break ;
case EXPECTED_SWITCH :
fprintf ( stderr , " *** Error: Expected a command-line switch at '%s' \n " , err - > param_str ) ;
break ;
case INVALID_SWITCH :
fprintf ( stderr , " *** Error: Invalid command-line switch: '%s' \n " , err - > param_str ) ;
break ;
case INPUT_OPEN_ERROR :
fprintf ( stderr , " *** Error: Failed to open input file: '%s' \n " , err - > param_str ) ;
break ;
2020-09-04 22:24:58 +00:00
case INPUT_COLOR_AT :
fprintf ( stderr , " *** Error: Found a non-#000/#FFF pixel at (%d, %d) \n " , err - > param_int [ 0 ] , err - > param_int [ 1 ] ) ;
break ;
2020-09-04 20:29:54 +00:00
case OUTPUT_OPEN_ERROR :
fprintf ( stderr , " *** Error: Failed to open output file: '%s' \n " , err - > param_str ) ;
break ;
default :
fprintf ( stderr , " *** Error: %s \n " , BMP2ARR_PARAMETERLESS_ERRORS [ err - > type ] ) ;
break ;
}
return err - > type ;
}
/* error generation */
enum bmp2arr_error bmp2arr_error_set ( struct rec98_bmp2arr_task * t , enum bmp2arr_error type ) {
if ( t = = NULL ) {
return nullptr_error ( ) ;
}
t - > err . type = type ;
if ( BMP2ARR_PARAMETERLESS_ERRORS [ type ] = = NULL )
t - > err . type = INTERNAL_ERROR_ERROR ;
t - > err . param_str = NULL ;
2020-09-04 22:24:58 +00:00
t - > err . param_int [ 0 ] = 0 ;
t - > err . param_int [ 1 ] = 0 ;
2020-09-04 20:29:54 +00:00
return type ;
}
enum bmp2arr_error bmp2arr_error_set_str ( struct rec98_bmp2arr_task * t , enum bmp2arr_error type , const char * param ) {
if ( t = = NULL ) {
return nullptr_error ( ) ;
}
t - > err . type = type ;
if ( BMP2ARR_PARAMETERLESS_ERRORS [ type ] ! = NULL )
t - > err . type = INTERNAL_ERROR_ERROR ;
t - > err . param_str = param ;
2020-09-04 22:24:58 +00:00
t - > err . param_int [ 0 ] = 0 ;
t - > err . param_int [ 1 ] = 0 ;
return type ;
}
enum bmp2arr_error bmp2arr_error_set_int_int ( struct rec98_bmp2arr_task * t , enum bmp2arr_error type , int param_1 , int param_2 ) {
if ( t = = NULL ) {
return nullptr_error ( ) ;
}
t - > err . type = type ;
if ( BMP2ARR_PARAMETERLESS_ERRORS [ type ] ! = NULL )
t - > err . type = INTERNAL_ERROR_ERROR ;
t - > err . param_str = NULL ;
t - > err . param_int [ 0 ] = param_1 ;
t - > err . param_int [ 1 ] = param_2 ;
2020-09-04 20:29:54 +00:00
return type ;
}
2020-09-04 22:24:58 +00:00
static int is_black_or_white ( uint8_t r , uint8_t g , uint8_t b ) {
return (
( ( r = = 0x00 ) & & ( g = = 0x00 ) & & ( b = = 0x00 ) ) | |
( ( r = = 0xFF ) & & ( g = = 0xFF ) & & ( b = = 0xFF ) )
) ;
}
2020-07-06 22:14:50 +00:00
static void memcpyxor ( unsigned char * dst , unsigned char * src , unsigned int bytes , unsigned char xorval ) {
while ( bytes - - ! = 0 )
* dst + + = * src + + ^ xorval ;
}
2020-09-04 22:24:58 +00:00
/* return -1 on success, or the position of a non-#000/#FFF pixel otherwise */
static int memcpy24to1 ( unsigned char * dst , unsigned char * src , unsigned int w ) {
unsigned int w_total = w ;
uint8_t r , g , b , tmp ;
2020-07-06 22:14:50 +00:00
unsigned int x ;
while ( w > = 8 ) {
tmp = 0 ;
for ( x = 0 ; x < 8 ; x + + ) {
b = * src + + ;
g = * src + + ;
r = * src + + ;
2020-09-04 22:24:58 +00:00
if ( is_black_or_white ( r , g , b ) )
2020-07-06 22:14:50 +00:00
tmp | = 0x80 > > x ;
2020-09-04 22:24:58 +00:00
else
return ( ( w_total - w ) + x ) ;
2020-07-06 22:14:50 +00:00
}
* dst + + = tmp ;
w - = 8 ;
}
if ( w > 0 ) {
tmp = 0 ;
for ( x = 0 ; x < w ; x + + ) {
b = * src + + ;
g = * src + + ;
r = * src + + ;
2020-09-04 22:24:58 +00:00
if ( is_black_or_white ( r , g , b ) )
2020-07-06 22:14:50 +00:00
tmp | = 0x80 > > x ;
2020-09-04 22:24:58 +00:00
else
return ( ( w_total - w ) + x ) ;
2020-07-06 22:14:50 +00:00
}
* dst + + = tmp ;
}
2020-09-04 22:24:58 +00:00
return - 1 ;
2020-07-06 22:14:50 +00:00
}
2020-09-04 22:24:58 +00:00
static int memcpy32to1 ( unsigned char * dst , unsigned char * src , unsigned int w ) {
unsigned int w_total = w ;
uint8_t r , g , b , tmp ;
2020-07-06 22:14:50 +00:00
unsigned int x ;
while ( w > = 8 ) {
tmp = 0 ;
for ( x = 0 ; x < 8 ; x + + ) {
b = * src + + ;
g = * src + + ;
r = * src + + ;
src + + ; /* A */
2020-09-04 22:24:58 +00:00
if ( is_black_or_white ( r , g , b ) )
2020-07-06 22:14:50 +00:00
tmp | = 0x80 > > x ;
2020-09-04 22:24:58 +00:00
else
return ( ( w_total - w ) + x ) ;
2020-07-06 22:14:50 +00:00
}
* dst + + = tmp ;
w - = 8 ;
}
if ( w > 0 ) {
tmp = 0 ;
for ( x = 0 ; x < w ; x + + ) {
b = * src + + ;
g = * src + + ;
r = * src + + ;
src + + ; /* A */
2020-09-04 22:24:58 +00:00
if ( is_black_or_white ( r , g , b ) )
2020-07-06 22:14:50 +00:00
tmp | = 0x80 > > x ;
2020-09-04 22:24:58 +00:00
else
return ( ( w_total - w ) + x ) ;
2020-07-06 22:14:50 +00:00
}
* dst + + = tmp ;
}
2020-09-04 22:24:58 +00:00
return - 1 ;
2020-07-06 22:14:50 +00:00
}
static int saveout_write_prologue ( struct rec98_bmp2arr_task * t , struct saveout_ctx * sctx ) {
if ( t - > output_type = = REC98_OUT_C ) {
fprintf ( sctx - > fp , " /* Generated by bmp2arr from %s, do not modify directly. */ \n " , t - > input_bmp ) ;
fprintf ( sctx - > fp , " /* Sprite sheet: %d sprites (%d x %d) of %d x %d sprites. */ \n " , sctx - > sscols * sctx - > ssrows , sctx - > sscols , sctx - > ssrows , t - > sprite_width , t - > sprite_height ) ;
fprintf ( sctx - > fp , " \n " ) ;
fprintf ( sctx - > fp , " const unsigned char %s " , t - > output_symname ! = NULL ? t - > output_symname : " untitled " ) ;
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_OUTER )
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " [8/*PRESHIFT*/] " ) ;
fprintf ( sctx - > fp , " [%d] " , sctx - > ssrows * sctx - > sscols ) ;
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_INNER )
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " [8/*PRESHIFT*/] " ) ;
fprintf ( sctx - > fp , " [%d/*%d bytes x %d rows*/] = { \n " ,
sctx - > bytesperrow * t - > sprite_height , sctx - > bytesperrow , t - > sprite_height ) ;
}
else if ( t - > output_type = = REC98_OUT_ASM ) {
fprintf ( sctx - > fp , " ; Generated by bmp2arr from %s, do not modify directly. \n " , t - > input_bmp ) ;
fprintf ( sctx - > fp , " ; Sprite sheet: %d sprites (%d x %d) of %d x %d sprites. \n " , sctx - > sscols * sctx - > ssrows , sctx - > sscols , sctx - > ssrows , t - > sprite_width , t - > sprite_height ) ;
fprintf ( sctx - > fp , " \n " ) ;
fprintf ( sctx - > fp , " ; const unsigned char %s " , t - > output_symname ! = NULL ? t - > output_symname : " untitled " ) ;
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_OUTER )
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " [8/*PRESHIFT*/] " ) ;
fprintf ( sctx - > fp , " [%d] " , sctx - > ssrows * sctx - > sscols ) ;
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_INNER )
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " [8/*PRESHIFT*/] " ) ;
fprintf ( sctx - > fp , " [%d/*%d bytes x %d rows*/]; \n " ,
sctx - > bytesperrow * t - > sprite_height , sctx - > bytesperrow , t - > sprite_height ) ;
fprintf ( sctx - > fp , " public %s \n " , t - > output_symname ! = NULL ? t - > output_symname : " untitled " ) ;
fprintf ( sctx - > fp , " label %s byte \n " , t - > output_symname ! = NULL ? t - > output_symname : " untitled " ) ;
}
else if ( t - > output_type = = REC98_OUT_BIN ) {
/* none needed */
}
else if ( t - > output_type = = REC98_OUT_BMP ) {
const unsigned int balign = ( sctx - > bytesperrow + 3u ) & ( ~ 3u ) ;
2020-09-04 14:48:32 +00:00
const unsigned int bitmap_size = t - > sprite_height * sctx - > ssrows * sctx - > sscols * ( ( t - > flags & PRESHIFT_ANY ) ? 8u : 1u ) ;
2020-07-06 22:14:50 +00:00
/* BITMAPFILEHEADER */
memcpy ( bmp_tmp + 0 , " BM " , 2 ) ;
2020-09-04 14:48:32 +00:00
* ( ( uint32_t * ) ( bmp_tmp + 2 ) ) = htole32 ( 14 + 40 + 4 * 2 + ( balign * bitmap_size ) ) ; /* bfSize */
2020-07-06 22:14:50 +00:00
* ( ( uint16_t * ) ( bmp_tmp + 6 ) ) = htole16 ( 0 ) ;
* ( ( uint16_t * ) ( bmp_tmp + 8 ) ) = htole16 ( 0 ) ;
2020-09-04 14:48:32 +00:00
* ( ( uint32_t * ) ( bmp_tmp + 10 ) ) = htole32 ( 14 + 40 + 4 * 2 ) ; /* bfOffBits */
2020-07-06 22:14:50 +00:00
fwrite ( bmp_tmp , 14 , 1 , sctx - > fp ) ;
/* BITMAPINFOHEADER */
* ( ( uint32_t * ) ( bmp_tmp + 0 ) ) = htole32 ( 40 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 4 ) ) = htole32 ( sctx - > bytesperrow * 8u ) ;
2020-09-04 14:48:32 +00:00
* ( ( uint32_t * ) ( bmp_tmp + 8 ) ) = htole32 ( bitmap_size ) ;
2020-07-06 22:14:50 +00:00
* ( ( uint16_t * ) ( bmp_tmp + 12 ) ) = htole16 ( 1 ) ;
* ( ( uint16_t * ) ( bmp_tmp + 14 ) ) = htole16 ( 1 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 16 ) ) = htole32 ( 0 ) ;
2020-09-04 14:48:32 +00:00
* ( ( uint32_t * ) ( bmp_tmp + 20 ) ) = htole32 ( balign * bitmap_size ) ;
2020-07-06 22:14:50 +00:00
* ( ( uint32_t * ) ( bmp_tmp + 24 ) ) = htole32 ( 0 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 28 ) ) = htole32 ( 0 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 32 ) ) = htole32 ( 2 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 36 ) ) = htole32 ( 2 ) ;
fwrite ( bmp_tmp , 40 , 1 , sctx - > fp ) ;
/* color palette */
bmp_tmp [ 0 + 0 ] = 0x00 ;
bmp_tmp [ 0 + 1 ] = 0x00 ;
bmp_tmp [ 0 + 2 ] = 0x00 ;
bmp_tmp [ 0 + 3 ] = 0x00 ;
bmp_tmp [ 4 + 0 ] = 0xFF ;
bmp_tmp [ 4 + 1 ] = 0xFF ;
bmp_tmp [ 4 + 2 ] = 0xFF ;
bmp_tmp [ 4 + 3 ] = 0x00 ;
fwrite ( bmp_tmp , 4 , 2 , sctx - > fp ) ;
}
return 0 ;
}
static int saveout_write_epilogue ( struct rec98_bmp2arr_task * t , struct saveout_ctx * sctx ) {
if ( t - > output_type = = REC98_OUT_C ) {
fprintf ( sctx - > fp , " };/*end spritesheet*/ \n " ) ;
}
else if ( t - > output_type = = REC98_OUT_ASM ) {
/* none needed */
}
else if ( t - > output_type = = REC98_OUT_BIN ) {
/* none needed */
}
else if ( t - > output_type = = REC98_OUT_BMP ) {
/* none needed */
}
return 0 ;
}
/* C-string utils */
void cstr_free ( char * * s ) {
if ( s ! = NULL ) {
if ( * s ! = NULL ) {
2020-07-09 07:39:06 +00:00
free ( ( void * ) ( * s ) ) ;
2020-07-06 22:14:50 +00:00
* s = NULL ;
}
}
}
void cstr_set ( char * * s , const char * n ) {
if ( s ! = NULL ) {
cstr_free ( s ) ;
if ( n ! = NULL ) * s = strdup ( n ) ;
}
}
void rec98_bmp2arr_task_free_bmp ( struct rec98_bmp2arr_task * t ) {
if ( t ! = NULL ) {
if ( t - > bmp ! = NULL ) {
2020-07-09 07:39:06 +00:00
free ( ( void * ) ( t - > bmp ) ) ;
2020-07-06 22:14:50 +00:00
t - > bmp = NULL ;
}
}
}
/* assume *t is uninitialized data */
2020-09-04 20:29:54 +00:00
enum bmp2arr_error rec98_bmp2arr_task_init ( struct rec98_bmp2arr_task * t ) {
if ( t = = NULL )
return INTERNAL_NULLPTR ;
2020-07-06 22:14:50 +00:00
memset ( t , 0 , sizeof ( * t ) ) ;
2020-09-04 20:29:54 +00:00
return SUCCESS ;
2020-07-06 22:14:50 +00:00
}
/* assume *t is initialized data */
2020-09-04 20:29:54 +00:00
enum bmp2arr_error rec98_bmp2arr_task_free ( struct rec98_bmp2arr_task * t ) {
if ( t = = NULL )
return INTERNAL_NULLPTR ;
2020-07-06 22:14:50 +00:00
rec98_bmp2arr_task_free_bmp ( t ) ;
cstr_free ( & t - > output_symname ) ;
cstr_free ( & t - > output_file ) ;
cstr_free ( & t - > input_bmp ) ;
memset ( t , 0 , sizeof ( * t ) ) ;
2020-09-04 20:29:54 +00:00
return SUCCESS ;
2020-07-06 22:14:50 +00:00
}
2020-09-04 20:29:54 +00:00
enum bmp2arr_error rec98_bmp2arr_save_debug_bmp_out ( struct rec98_bmp2arr_task * t ) {
2020-07-06 22:14:50 +00:00
unsigned int y ;
int fd ;
2020-09-04 20:29:54 +00:00
if ( t = = NULL )
return INTERNAL_NULLPTR ;
if ( t - > output_file = = NULL )
return bmp2arr_error_set ( t , MISSING_OUTPUT_FILE ) ;
if ( t - > bmp = = NULL )
return bmp2arr_error_set ( t , OUTPUT_NO_INPUT_LOADED ) ;
2020-07-06 22:14:50 +00:00
fd = open ( t - > output_file , O_WRONLY | O_CREAT | O_TRUNC | O_BINARY , 0644 ) ;
2020-09-04 20:29:54 +00:00
if ( fd < 0 )
return bmp2arr_error_set_str ( t , OUTPUT_OPEN_ERROR , t - > output_file ) ;
2020-07-06 22:14:50 +00:00
/* BITMAPFILEHEADER */
memcpy ( bmp_tmp + 0 , " BM " , 2 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 2 ) ) = htole32 ( 14 + 40 + 4 * 2 + ( t - > bmp_height * t - > bmp_stride ) ) ; /* bfSize */
* ( ( uint16_t * ) ( bmp_tmp + 6 ) ) = htole16 ( 0 ) ;
* ( ( uint16_t * ) ( bmp_tmp + 8 ) ) = htole16 ( 0 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 10 ) ) = htole32 ( 14 + 40 + 4 * 2 ) ; /* bfOffBits */
write ( fd , bmp_tmp , 14 ) ;
/* BITMAPINFOHEADER */
* ( ( uint32_t * ) ( bmp_tmp + 0 ) ) = htole32 ( 40 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 4 ) ) = htole32 ( t - > bmp_width ) ;
* ( ( uint32_t * ) ( bmp_tmp + 8 ) ) = htole32 ( t - > bmp_height ) ;
* ( ( uint16_t * ) ( bmp_tmp + 12 ) ) = htole16 ( 1 ) ;
* ( ( uint16_t * ) ( bmp_tmp + 14 ) ) = htole16 ( 1 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 16 ) ) = htole32 ( 0 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 20 ) ) = htole32 ( t - > bmp_height * t - > bmp_stride ) ;
* ( ( uint32_t * ) ( bmp_tmp + 24 ) ) = htole32 ( 0 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 28 ) ) = htole32 ( 0 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 32 ) ) = htole32 ( 2 ) ;
* ( ( uint32_t * ) ( bmp_tmp + 36 ) ) = htole32 ( 2 ) ;
write ( fd , bmp_tmp , 40 ) ;
/* color palette */
bmp_tmp [ 0 + 0 ] = 0x00 ;
bmp_tmp [ 0 + 1 ] = 0x00 ;
bmp_tmp [ 0 + 2 ] = 0x00 ;
bmp_tmp [ 0 + 3 ] = 0x00 ;
bmp_tmp [ 4 + 0 ] = 0xFF ;
bmp_tmp [ 4 + 1 ] = 0xFF ;
bmp_tmp [ 4 + 2 ] = 0xFF ;
bmp_tmp [ 4 + 3 ] = 0x00 ;
write ( fd , bmp_tmp , 4 * 2 ) ;
/* the bits */
y = t - > bmp_height - 1 ;
do {
write ( fd , t - > bmp + ( y * t - > bmp_stride ) , t - > bmp_stride ) ;
} while ( y - - ! = 0 ) ;
close ( fd ) ;
2020-09-04 20:29:54 +00:00
return bmp2arr_error_set ( t , SUCCESS ) ;
2020-07-06 22:14:50 +00:00
}
2020-07-06 22:16:05 +00:00
static int saveout_write_sprite ( struct rec98_bmp2arr_task * t , struct saveout_ctx * sctx , const unsigned char * bmp /*length bytesperrow * height*/ ) {
2020-07-06 22:14:50 +00:00
unsigned int r , c , b ;
if ( t - > output_type = = REC98_OUT_C ) {
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_INNER ) {
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " %c " , sctx - > sspreshift ! = 0 ? ' , ' : ' ' ) ;
}
else {
fprintf ( sctx - > fp , " %c " , sctx - > spritenum ! = 0 ? ' , ' : ' ' ) ;
}
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_ANY ) {
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " {/*sprite %u preshift %u*/ \n " , sctx - > spritenum , sctx - > sspreshift ) ;
}
else {
fprintf ( sctx - > fp , " {/*sprite %u*/ \n " , sctx - > spritenum ) ;
}
for ( r = 0 ; r < t - > sprite_height ; r + + ) {
fprintf ( sctx - > fp , " \t " ) ;
for ( c = 0 ; c < sctx - > bytesperrow ; c + + ) {
fprintf ( sctx - > fp , " %c " , ( c ! = 0 | | r ! = 0 ) ? ' , ' : ' ' ) ;
fprintf ( sctx - > fp , " 0x%02x " , * bmp + + ) ;
}
2020-09-04 14:48:32 +00:00
fprintf ( sctx - > fp , " /* row %u */ " , ( t - > flags & UPSIDEDOWN ) ? ( t - > sprite_height - 1u - r ) : r ) ;
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " \n " ) ;
}
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_ANY ) {
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " }/*end sprite %u preshift %u*/ \n " , sctx - > spritenum , sctx - > sspreshift ) ;
}
else {
fprintf ( sctx - > fp , " }/*end sprite %u*/ \n " , sctx - > spritenum ) ;
}
}
else if ( t - > output_type = = REC98_OUT_ASM ) {
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_ANY ) {
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " ; sprite %u preshift %u \n " , sctx - > spritenum , sctx - > sspreshift ) ;
}
else {
fprintf ( sctx - > fp , " ; sprite %u \n " , sctx - > spritenum ) ;
}
for ( r = 0 ; r < t - > sprite_height ; r + + ) {
fprintf ( sctx - > fp , " \t db " ) ;
for ( c = 0 ; c < sctx - > bytesperrow ; c + + ) {
if ( c ! = 0 ) fprintf ( sctx - > fp , " , " ) ;
for ( b = 0 ; b < 8 ; b + + )
fprintf ( sctx - > fp , " %u " , ( * bmp > > ( 7u - b ) ) & 1u ) ;
fprintf ( sctx - > fp , " b " ) ;
bmp + + ;
}
2020-09-04 14:48:32 +00:00
fprintf ( sctx - > fp , " ; row %u " , ( t - > flags & UPSIDEDOWN ) ? ( t - > sprite_height - 1u - r ) : r ) ;
2020-07-06 22:14:50 +00:00
fprintf ( sctx - > fp , " \n " ) ;
}
}
else if ( t - > output_type = = REC98_OUT_BIN ) {
fwrite ( bmp , sctx - > bytesperrow * t - > sprite_height , 1 , sctx - > fp ) ;
}
else if ( t - > output_type = = REC98_OUT_BMP ) {
const unsigned int balign = ( sctx - > bytesperrow + 3u ) & ( ~ 3u ) ;
for ( r = 0 ; r < t - > sprite_height ; r + + ) {
memcpy ( bmp_tmp , bmp , sctx - > bytesperrow ) ;
fwrite ( bmp_tmp , balign , 1 , sctx - > fp ) ;
bmp + = sctx - > bytesperrow ;
}
}
return 0 ;
}
2020-09-04 20:29:54 +00:00
enum bmp2arr_error rec98_bmp2arr_save_output ( struct rec98_bmp2arr_task * t ) {
2020-07-06 22:14:50 +00:00
struct saveout_ctx sctx ;
2020-09-04 20:29:54 +00:00
if ( t = = NULL )
return INTERNAL_NULLPTR ;
if ( t - > output_file = = NULL )
return bmp2arr_error_set ( t , MISSING_OUTPUT_FILE ) ;
2020-09-04 20:42:33 +00:00
if ( t - > output_type = = REC98_OUT_NONE )
return bmp2arr_error_set ( t , MISSING_OUTPUT_TYPE ) ;
if ( t - > output_type > BMP2ARR_OUTPUT_TYPE_COUNT )
return bmp2arr_error_set ( t , INVALID_OUTPUT_TYPE ) ;
2020-09-04 20:29:54 +00:00
if ( t - > bmp = = NULL )
return bmp2arr_error_set ( t , OUTPUT_NO_INPUT_LOADED ) ;
if ( t - > sprite_width < 8 )
return bmp2arr_error_set ( t , INVALID_SPRITE_WIDTH ) ;
if ( t - > sprite_height = = 0 )
return bmp2arr_error_set ( t , MISSING_SPRITE_HEIGHT ) ;
if ( ( t - > flags & PRESHIFT_ANY ) = = PRESHIFT_ANY )
return bmp2arr_error_set ( t , INVALID_PRESHIFT ) ;
2020-07-06 22:14:50 +00:00
sctx . sscols = t - > bmp_width / t - > sprite_width ;
sctx . ssrows = t - > bmp_height / t - > sprite_height ;
2020-09-04 20:29:54 +00:00
if ( sctx . sscols = = 0 | | sctx . ssrows = = 0 )
return bmp2arr_error_set ( t , INPUT_TOO_SMALL ) ;
2020-07-06 22:14:50 +00:00
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_ANY & & t - > sprite_width ! = 8 )
2020-09-04 20:29:54 +00:00
return bmp2arr_error_set ( t , INVALID_PRESHIFT_WIDTH ) ;
2020-07-06 22:14:50 +00:00
sctx . bytesperrow = ( t - > sprite_width + 7u ) / 8u ;
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_ANY ) sctx . bytesperrow + = 1u ; /* in the examples, an 8-pixel wide sprite is shifted across 16 pixels */
2020-07-06 22:14:50 +00:00
if ( ( sctx . bytesperrow * t - > sprite_height ) > sizeof ( bmp_tmp ) )
2020-09-04 20:29:54 +00:00
return bmp2arr_error_set ( t , INTERNAL_ARRAY_TOO_SMALL ) ;
2020-07-06 22:14:50 +00:00
sctx . fp = fopen ( t - > output_file , " wb " ) ;
2020-09-04 20:29:54 +00:00
if ( sctx . fp = = NULL )
return bmp2arr_error_set_str ( t , OUTPUT_OPEN_ERROR , t - > output_file ) ;
2020-07-06 22:14:50 +00:00
2020-09-04 20:50:16 +00:00
if ( ! ( t - > flags & QUIET ) ) {
2020-09-04 21:13:59 +00:00
unsigned int cels = sctx . sscols * sctx . ssrows ;
const char * str_layout = NULL ;
static const char * TYPES [ BMP2ARR_OUTPUT_TYPE_COUNT ] = {
NULL , " C " , " ASM " , " binary " , " BMP "
} ;
if ( t - > flags & PRESHIFT_OUTER ) {
str_layout = " [PRESHIFT][cels][rows] " ;
} else if ( t - > flags & PRESHIFT_INNER ) {
str_layout = " [cels][PRESHIFT][rows] " ;
} else {
str_layout = " [cels][rows] (no pre-shifting) " ;
}
fprintf ( stderr ,
" Input bitmap: %s (size %d x %d) \n "
" Sprite size: %d x %d \n "
" = Cels: %d (arranged as %d x %d) \n "
" Data layout: %s \n "
" Y direction: %s \n "
" Output file: %s (Format: %s) \n "
,
t - > input_bmp , ( sctx . sscols * t - > sprite_width ) , ( sctx . ssrows * t - > sprite_height ) ,
t - > sprite_width , t - > sprite_height ,
cels , sctx . sscols , sctx . ssrows ,
str_layout ,
( t - > flags & UPSIDEDOWN ) ? " upside down " : " normal " ,
t - > output_file , TYPES [ t - > output_type ]
) ;
2020-09-04 20:50:16 +00:00
}
2020-07-06 22:14:50 +00:00
if ( saveout_write_prologue ( t , & sctx ) )
goto fioerr ;
2020-09-04 14:48:32 +00:00
if ( t - > flags & PRESHIFT_INNER ) {
2020-07-06 22:14:50 +00:00
for ( sctx . ssrow = 0 ; sctx . ssrow < sctx . ssrows ; sctx . ssrow + + ) {
sctx . spritenum = sctx . ssrow * sctx . sscols ;
for ( sctx . sscol = 0 ; sctx . sscol < sctx . sscols ; sctx . sscol + + , sctx . spritenum + + ) {
if ( t - > output_type = = REC98_OUT_C ) {
fprintf ( sctx . fp , " %c " , sctx . spritenum ! = 0 ? ' , ' : ' ' ) ;
fprintf ( sctx . fp , " {/*preshift*/ \n " ) ;
}
for ( sctx . sspreshift = 0 ; sctx . sspreshift < 8 ; sctx . sspreshift + + ) {
unsigned char * dbits = bmp_tmp ; /* use bmp_tmp[], this is why the size check */
unsigned int y , b ;
for ( y = 0 ; y < t - > sprite_height ; y + + ) {
unsigned int shif = 0 ;
unsigned int sr =
2020-09-04 14:48:32 +00:00
( t - > flags & UPSIDEDOWN ) ? ( t - > sprite_height - 1 - y ) : y ;
2020-07-06 22:14:50 +00:00
const unsigned char * sbits =
( const unsigned char * ) t - > bmp +
( sctx . sscol * ( ( t - > sprite_width + 7u ) / 8u ) ) +
( ( ( sctx . ssrow * t - > sprite_height ) + sr ) * t - > bmp_stride ) ;
b = sctx . bytesperrow ;
while ( b > = 2 ) {
b - - ;
shif = ( shif < < 8u ) + ( ( ( unsigned int ) ( * sbits + + ) ) < < ( 8u - sctx . sspreshift ) ) ;
* dbits + + = shif > > 8u ;
}
while ( b > = 1 ) {
b - - ;
shif = ( shif < < 8u ) ;
* dbits + + = shif > > 8u ;
}
}
if ( saveout_write_sprite ( t , & sctx , bmp_tmp ) )
goto fioerr ;
}
if ( t - > output_type = = REC98_OUT_C ) {
fprintf ( sctx . fp , " }/*end preshift*/ \n " ) ;
}
}
}
}
2020-09-04 14:48:32 +00:00
else if ( t - > flags & PRESHIFT_OUTER ) {
2020-07-06 22:14:50 +00:00
for ( sctx . sspreshift = 0 ; sctx . sspreshift < 8 ; sctx . sspreshift + + ) {
if ( t - > output_type = = REC98_OUT_C ) {
fprintf ( sctx . fp , " %c " , sctx . sspreshift ! = 0 ? ' , ' : ' ' ) ;
fprintf ( sctx . fp , " {/*preshift %u*/ \n " , sctx . sspreshift ) ;
}
for ( sctx . ssrow = 0 ; sctx . ssrow < sctx . ssrows ; sctx . ssrow + + ) {
sctx . spritenum = sctx . ssrow * sctx . sscols ;
for ( sctx . sscol = 0 ; sctx . sscol < sctx . sscols ; sctx . sscol + + , sctx . spritenum + + ) {
unsigned char * dbits = bmp_tmp ; /* use bmp_tmp[], this is why the size check */
unsigned int y , b ;
for ( y = 0 ; y < t - > sprite_height ; y + + ) {
unsigned int shif = 0 ;
unsigned int sr =
2020-09-04 14:48:32 +00:00
( t - > flags & UPSIDEDOWN ) ? ( t - > sprite_height - 1 - y ) : y ;
2020-07-06 22:14:50 +00:00
const unsigned char * sbits =
( const unsigned char * ) t - > bmp +
( sctx . sscol * ( ( t - > sprite_width + 7u ) / 8u ) ) +
( ( ( sctx . ssrow * t - > sprite_height ) + sr ) * t - > bmp_stride ) ;
b = sctx . bytesperrow ;
while ( b > = 2 ) {
b - - ;
shif = ( shif < < 8u ) + ( ( ( unsigned int ) ( * sbits + + ) ) < < ( 8u - sctx . sspreshift ) ) ;
* dbits + + = shif > > 8u ;
}
while ( b > = 1 ) {
b - - ;
shif = ( shif < < 8u ) ;
* dbits + + = shif > > 8u ;
}
}
if ( saveout_write_sprite ( t , & sctx , bmp_tmp ) )
goto fioerr ;
}
}
if ( t - > output_type = = REC98_OUT_C ) {
fprintf ( sctx . fp , " }/*end preshift %u*/ \n " , sctx . sspreshift ) ;
}
}
}
else {
sctx . sspreshift = 0 ;
for ( sctx . ssrow = 0 ; sctx . ssrow < sctx . ssrows ; sctx . ssrow + + ) {
sctx . spritenum = sctx . ssrow * sctx . sscols ;
for ( sctx . sscol = 0 ; sctx . sscol < sctx . sscols ; sctx . sscol + + , sctx . spritenum + + ) {
unsigned char * dbits = bmp_tmp ; /* use bmp_tmp[], this is why the size check */
unsigned int y , b ;
for ( y = 0 ; y < t - > sprite_height ; y + + ) {
unsigned int sr =
2020-09-04 14:48:32 +00:00
( t - > flags & UPSIDEDOWN ) ? ( t - > sprite_height - 1 - y ) : y ;
2020-07-06 22:14:50 +00:00
const unsigned char * sbits =
( const unsigned char * ) t - > bmp +
( sctx . sscol * ( ( t - > sprite_width + 7u ) / 8u ) ) +
( ( ( sctx . ssrow * t - > sprite_height ) + sr ) * t - > bmp_stride ) ;
for ( b = 0 ; b < sctx . bytesperrow ; b + + )
* dbits + + = * sbits + + ;
}
if ( saveout_write_sprite ( t , & sctx , bmp_tmp ) )
goto fioerr ;
}
}
}
if ( saveout_write_epilogue ( t , & sctx ) )
goto fioerr ;
fclose ( sctx . fp ) ;
2020-09-04 20:29:54 +00:00
return bmp2arr_error_set ( t , SUCCESS ) ;
2020-07-06 22:14:50 +00:00
fioerr :
fclose ( sctx . fp ) ;
2020-09-04 20:29:54 +00:00
return bmp2arr_error_set ( t , OUTPUT_IO_ERROR ) ;
2020-07-06 22:14:50 +00:00
}
2020-09-04 20:29:54 +00:00
enum bmp2arr_error rec98_bmp2arr_load_bitmap ( struct rec98_bmp2arr_task * t ) {
enum bmp2arr_error err = SUCCESS ;
2020-07-06 22:14:50 +00:00
unsigned char * tmprow = NULL ;
uint8_t xorval = 0 ;
uint32_t srcstride ;
uint32_t offbits ;
uint32_t bisize ;
uint16_t bpp ;
uint32_t row ;
int fd ;
2020-09-04 20:29:54 +00:00
if ( t = = NULL )
return INTERNAL_NULLPTR ;
if ( t - > input_bmp = = NULL )
return bmp2arr_error_set ( t , MISSING_INPUT_BMP ) ;
if ( t - > bmp ! = NULL )
return bmp2arr_error_set ( t , INPUT_ALREADY_LOADED ) ;
2020-07-06 22:14:50 +00:00
fd = open ( t - > input_bmp , O_RDONLY | O_BINARY ) ;
2020-09-04 20:29:54 +00:00
if ( fd < 0 )
return bmp2arr_error_set_str ( t , INPUT_OPEN_ERROR , t - > input_bmp ) ;
2020-07-06 22:14:50 +00:00
if ( lseek ( fd , 0 , SEEK_SET ) ! = 0 ) goto fioerr ;
/* BITMAPFILEHEADER */
if ( read ( fd , bmp_tmp , 14 ) ! = 14 ) goto fioerr ;
if ( memcmp ( bmp_tmp , " BM " , 2 ) ) goto fioerr ;
offbits = le32toh ( * ( ( uint32_t * ) ( bmp_tmp + 10 ) ) ) ; /* endian.h little endian to host */
/* BITMAPINFOHEADER */
if ( read ( fd , bmp_tmp , 40 ) ! = 40 ) goto fioerr ;
bisize = le32toh ( * ( ( uint32_t * ) ( bmp_tmp + 0 ) ) ) ;
if ( bisize < 40 ) goto fioerr ; /* *sigh* GIMP has decided to export the larger header with NO option to emit the traditional 40-byte format */
t - > bmp_width = le32toh ( * ( ( uint32_t * ) ( bmp_tmp + 4 ) ) ) ;
t - > bmp_height = le32toh ( * ( ( uint32_t * ) ( bmp_tmp + 8 ) ) ) ;
if ( t - > bmp_width < 1 | | t - > bmp_height < 1 | | t - > bmp_width > 1024 | | t - > bmp_height > 1024 ) goto fioerr ;
if ( le16toh ( * ( ( uint16_t * ) ( bmp_tmp + 12 ) ) ) ! = 1 /* biPlanes*/ )
goto fioerr ;
/* biCompression can be 0 (no compression) or 3 (BI_RGB) */
if ( ! ( le32toh ( * ( ( uint32_t * ) ( bmp_tmp + 16 ) ) ) = = 0 /* biCompression */ | |
le32toh ( * ( ( uint32_t * ) ( bmp_tmp + 16 ) ) ) = = 3 /* biCompression */ ) )
goto fioerr ;
bpp = le16toh ( * ( ( uint16_t * ) ( bmp_tmp + 14 ) ) ) ;
if ( ! ( bpp = = 1 | | bpp = = 24 | | bpp = = 32 ) ) goto fioerr ;
t - > bmp_stride = srcstride = ( ( ( t - > bmp_width * bpp ) + 31u ) & ( ~ 31u ) ) / 8u ; /* 4-byte align */
# if TARGET_MSDOS == 16
if ( ( 32768u / t - > bmp_stride ) < t - > bmp_height ) /* cannot fit into 32KB */
goto fioerr ;
# endif
/* skip anything beyond the 40 byte header we expect */
if ( bisize > 40 )
lseek ( fd , bisize - 40 , SEEK_CUR ) ;
/* palette */
if ( bpp = = 1 ) {
if ( read ( fd , bmp_tmp , 4 * 2 ) ! = ( 4 * 2 ) ) goto fioerr ;
/* in case of stupid editing programs that put the white color first */
if ( ( bmp_tmp [ 0 ] | bmp_tmp [ 1 ] | bmp_tmp [ 2 ] ) & 0x80 ) xorval = 0xFF ;
}
2020-07-09 07:39:06 +00:00
t - > bmp = ( unsigned char * ) malloc ( t - > bmp_height * t - > bmp_stride ) ;
2020-09-04 20:29:54 +00:00
if ( t - > bmp = = NULL ) {
err = bmp2arr_error_set ( t , INPUT_OUT_OF_MEMORY ) ;
goto fioerr ;
}
2020-07-06 22:14:50 +00:00
/* read bitmap bits. BMPs are upside-down */
if ( lseek ( fd , offbits , SEEK_SET ) ! = offbits ) goto fioerr ;
/* may need to convert to 1bpp from source */
# if TARGET_MSDOS == 16
if ( srcstride > = 0xFFFFul ) goto fioerr ;
# endif
2020-07-09 07:39:06 +00:00
tmprow = ( unsigned char * ) malloc ( ( unsigned int ) srcstride ) ;
2020-07-06 22:14:50 +00:00
if ( tmprow = = NULL ) goto fioerr ;
/* count: height-1 to 0 inclusive */
row = t - > bmp_height - 1u ;
do {
if ( ( uint32_t ) read ( fd , tmprow , ( unsigned int ) srcstride ) ! = srcstride ) goto fioerr ;
if ( bpp = = 1 )
memcpyxor ( t - > bmp + ( row * t - > bmp_stride ) , tmprow , ( unsigned int ) srcstride , xorval ) ;
2020-09-04 22:24:58 +00:00
else if ( bpp = = 24 ) {
int err_x = memcpy24to1 ( t - > bmp + ( row * t - > bmp_stride ) , tmprow , t - > bmp_width ) ;
if ( err_x ! = - 1 ) {
err = bmp2arr_error_set_int_int ( t , INPUT_COLOR_AT , err_x , ( int ) row ) ;
goto fioerr ;
}
}
else if ( bpp = = 32 ) {
int err_x = memcpy32to1 ( t - > bmp + ( row * t - > bmp_stride ) , tmprow , t - > bmp_width ) ;
if ( err_x ! = - 1 ) {
err = bmp2arr_error_set_int_int ( t , INPUT_COLOR_AT , err_x , ( int ) row ) ;
goto fioerr ;
}
}
2020-07-06 22:14:50 +00:00
} while ( row - - ! = 0u ) ; /* compare against post decrement to break out if it is zero */
2020-07-09 07:39:06 +00:00
if ( tmprow ) free ( ( void * ) tmprow ) ;
2020-07-06 22:14:50 +00:00
close ( fd ) ;
2020-09-04 20:29:54 +00:00
return bmp2arr_error_set ( t , SUCCESS ) ;
2020-07-06 22:14:50 +00:00
fioerr :
2020-07-09 07:39:06 +00:00
if ( tmprow ) free ( ( void * ) tmprow ) ;
2020-07-06 22:14:50 +00:00
close ( fd ) ;
2020-09-04 20:29:54 +00:00
if ( err = = SUCCESS ) {
return bmp2arr_error_set ( t , INPUT_INVALID ) ;
}
return err ;
2020-07-06 22:14:50 +00:00
}