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 }