xbin.c (5333B)
1 /* 2 * xbin.c 3 * libansilove 1.2.8 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 }