libansilove

Library for converting ANSI, ASCII, and other formats to PNG
Log | Files | Refs | README | LICENSE

xbin.c (5333B)


      1 /*
      2  * xbin.c
      3  * libansilove 1.2.7
      4  * https://www.ansilove.org
      5  *
      6  * Copyright (c) 2011-2020 Stefan Vogt, Brian Cassidy, and Frederic Cambus
      7  * All rights reserved.
      8  *
      9  * libansilove is licensed under the BSD 2-Clause License.
     10  * See LICENSE file for details.
     11  */
     12 
     13 #include <gd.h>
     14 #include <stdint.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include "ansilove.h"
     18 #include "config.h"
     19 #include "drawchar.h"
     20 #include "fonts.h"
     21 #include "output.h"
     22 
     23 #define XBIN_HEADER_LENGTH 11 /* 4 + 1 + 2 + 2 + 1 + 1 */
     24 #define XBIN_PALETTE_LENGTH 48
     25 
     26 int
     27 ansilove_xbin(struct ansilove_ctx *ctx, struct ansilove_options *options)
     28 {
     29 	const uint8_t *font_data;
     30 	uint8_t *font_data_xbin = NULL;
     31 	uint32_t width, height;
     32 	uint32_t colors[16];
     33 	uint32_t offset = XBIN_HEADER_LENGTH;
     34 	uint32_t column = 0, row = 0, foreground, background;
     35 	int32_t character, attribute;
     36 
     37 	gdImagePtr canvas;
     38 
     39 	if (ctx == NULL || options == NULL) {
     40 		if (ctx)
     41 			ctx->error = ANSILOVE_INVALID_PARAM;
     42 
     43 		return -1;
     44 	}
     45 
     46 	if (ctx->length < XBIN_HEADER_LENGTH) {
     47 		ctx->error = ANSILOVE_FORMAT_ERROR;
     48 		return -1;
     49 	}
     50 
     51 	if (strncmp((char *)ctx->buffer, "XBIN\x1a", 5) != 0) {
     52 		ctx->error = ANSILOVE_FORMAT_ERROR;
     53 		return -1;
     54 	}
     55 
     56 	uint32_t xbin_width = (ctx->buffer[6] << 8) + ctx->buffer[5];
     57 	uint32_t xbin_height = (ctx->buffer[8] << 8) + ctx->buffer[7];
     58 	uint32_t xbin_fontsize = ctx->buffer[9];
     59 	uint32_t xbin_flags = ctx->buffer[10];
     60 
     61 	if (xbin_fontsize == 0) {
     62 		xbin_fontsize = 16;
     63 	}
     64 
     65 	if (xbin_fontsize > 32) {
     66 		ctx->error = ANSILOVE_FORMAT_ERROR;
     67 		return -1;
     68 	}
     69 
     70 	if (xbin_width < 1 || xbin_width > 4096) {
     71 		ctx->error = ANSILOVE_RANGE_ERROR;
     72 		return -1;
     73 	}
     74 
     75 	width = 8 * xbin_width;
     76 	height = xbin_fontsize * xbin_height;
     77 
     78 	if (!width || !height) {
     79 		ctx->error = ANSILOVE_FORMAT_ERROR;
     80 		return -1;
     81 	}
     82 
     83 	canvas = gdImageCreate(width, height);
     84 
     85 	if (!canvas) {
     86 		ctx->error = ANSILOVE_GD_ERROR;
     87 		return -1;
     88 	}
     89 
     90 	/* palette */
     91 	if ((xbin_flags & 1) == 1) {
     92 		size_t index, loop;
     93 
     94 		if (offset + XBIN_PALETTE_LENGTH > ctx->length) {
     95 			ctx->error = ANSILOVE_FORMAT_ERROR;
     96 			return -1;
     97 		}
     98 
     99 		for (loop = 0; loop < 16; loop++) {
    100 			index = (loop * 3) + offset;
    101 
    102 			colors[loop] = gdImageColorAllocate(canvas,
    103 			    ctx->buffer[index] << 2 | ctx->buffer[index] >> 4,
    104 			    ctx->buffer[index + 1] << 2 | ctx->buffer[index + 1] >> 4,
    105 			    ctx->buffer[index + 2] << 2 | ctx->buffer[index + 2] >> 4);
    106 		}
    107 
    108 		offset += XBIN_PALETTE_LENGTH;
    109 	} else {
    110 		for (size_t i = 0; i < 16; i++)
    111 			colors[i] = gdImageColorAllocate(canvas,
    112 			    vga_palette_red[i], vga_palette_green[i],
    113 			    vga_palette_blue[i]);
    114 	}
    115 
    116 	/* font */
    117 	if ((xbin_flags & 2) == 2) {
    118 		uint32_t numchars = (xbin_flags & 0x10 ? 512 : 256);
    119 		size_t fontsz = xbin_fontsize * numchars;
    120 
    121 		if (offset + fontsz > ctx->length) {
    122 			ctx->error = ANSILOVE_FORMAT_ERROR;
    123 			return -1;
    124 		}
    125 
    126 		/* allocate memory to contain the XBin font */
    127 		font_data_xbin = (uint8_t *)malloc(fontsz);
    128 		if (font_data_xbin == NULL) {
    129 			ctx->error = ANSILOVE_MEMORY_ERROR;
    130 			return -1;
    131 		}
    132 
    133 		memcpy(font_data_xbin, ctx->buffer+offset, fontsz);
    134 
    135 		font_data = font_data_xbin;
    136 
    137 		offset += fontsz;
    138 	} else {
    139 		/* using default 80x25 font */
    140 		font_data = font_pc_80x25;
    141 		xbin_fontsize = 16;
    142 	}
    143 
    144 	/* read compressed xbin */
    145 	if ((xbin_flags & 4) == 4) {
    146 		while (offset + 1 < ctx->length && row != xbin_height) {
    147 			uint32_t ctype = ctx->buffer[offset] & 0xC0;
    148 			uint32_t counter = (ctx->buffer[offset] & 0x3F) + 1;
    149 
    150 			character = -1;
    151 			attribute = -1;
    152 
    153 			offset++;
    154 			while (counter--) {
    155 				/* none */
    156 				if (ctype == 0) {
    157 					if (offset + 1 < ctx->length) {
    158 						character = ctx->buffer[offset];
    159 						attribute = ctx->buffer[offset + 1];
    160 						offset += 2;
    161 					} else {
    162 						ctx->error = ANSILOVE_FORMAT_ERROR;
    163 						free(font_data_xbin);
    164 						return -1;
    165 					}
    166 				}
    167 				/* char */
    168 				else if (ctype == 0x40) {
    169 					if (character == -1) {
    170 						character = ctx->buffer[offset];
    171 						offset++;
    172 					}
    173 					attribute = ctx->buffer[offset];
    174 					offset++;
    175 				}
    176 				/* attr */
    177 				else if (ctype == 0x80) {
    178 					if (attribute == -1) {
    179 						attribute = ctx->buffer[offset];
    180 						offset++;
    181 					}
    182 					character = ctx->buffer[offset];
    183 					offset++;
    184 				}
    185 				/* both */
    186 				else {
    187 					if (character == -1) {
    188 						character = ctx->buffer[offset];
    189 						offset++;
    190 					}
    191 					if (attribute == -1) {
    192 						attribute = ctx->buffer[offset];
    193 						offset++;
    194 					}
    195 				}
    196 
    197 				background = (attribute & 240) >> 4;
    198 				foreground = attribute & 15;
    199 
    200 				drawchar(canvas, font_data, 8, xbin_fontsize,
    201 				    column, row, colors[background],
    202 				    colors[foreground], character);
    203 
    204 				column++;
    205 
    206 				if (column == xbin_width) {
    207 					column = 0;
    208 					row++;
    209 				}
    210 			}
    211 		}
    212 	} else {
    213 		/* read uncompressed xbin */
    214 		while (offset + 1 < ctx->length && row != xbin_height) {
    215 			if (column == xbin_width) {
    216 				column = 0;
    217 				row++;
    218 			}
    219 
    220 			character = ctx->buffer[offset];
    221 			attribute = ctx->buffer[offset+1];
    222 
    223 			background = (attribute & 240) >> 4;
    224 			foreground = attribute & 15;
    225 
    226 			drawchar(canvas, font_data, 8, xbin_fontsize,
    227 			    column, row, colors[background],
    228 			    colors[foreground], character);
    229 
    230 			column++;
    231 			offset += 2;
    232 		}
    233 	}
    234 
    235 	/* create output file */
    236 	if (output(ctx, options, canvas) != 0) {
    237 		free(font_data_xbin);
    238 		font_data = NULL;
    239 		return -1;
    240 	}
    241 
    242 	free(font_data_xbin);
    243 
    244 	return 0;
    245 }