libansilove

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

xbin.c (6389B)


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