tundra.c (4998B)
1 /* 2 * tundra.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 <stddef.h> 17 #include <stdint.h> 18 #include <string.h> 19 #include "ansilove.h" 20 #include "drawchar.h" 21 #include "fonts.h" 22 #include "output.h" 23 24 #define TUNDRA_VERSION 24 25 #define TUNDRA_STRING "TUNDRA24" 26 27 #define TUNDRA_HEADER_LENGTH 9 /* 8 + 1 */ 28 29 #define TUNDRA_POSITION 1 30 #define TUNDRA_COLOR_FOREGROUND 2 31 #define TUNDRA_COLOR_BACKGROUND 4 32 #define TUNDRA_COLOR_BOTH 6 33 34 int 35 ansilove_tundra(struct ansilove_ctx *ctx, struct ansilove_options *options) 36 { 37 char tundra_version; 38 int32_t column = 0, row = 1; 39 uint32_t cursor, character, background = 0, foreground = 0; 40 uint32_t width, height; 41 size_t loop = TUNDRA_HEADER_LENGTH; 42 struct fontStruct fontData; 43 44 /* libgd image pointers */ 45 gdImagePtr canvas; 46 47 if (ctx == NULL || options == NULL) { 48 if (ctx) 49 ctx->error = ANSILOVE_INVALID_PARAM; 50 51 return -1; 52 } 53 54 if (ctx->length < TUNDRA_HEADER_LENGTH) 55 goto error; 56 57 if (options->bits != 8 && options->bits !=9) { 58 ctx->error = ANSILOVE_RANGE_ERROR; 59 return -1; 60 } 61 62 options->columns = options->columns ? options->columns : 80; 63 int16_t columns = options->columns; 64 65 if (columns < 1 || columns > 4096) { 66 ctx->error = ANSILOVE_RANGE_ERROR; 67 return -1; 68 } 69 70 /* font selection */ 71 memset(&fontData, 0, sizeof(struct fontStruct)); 72 select_font(&fontData, options->font); 73 74 /* extract tundra header */ 75 tundra_version = ctx->buffer[0]; 76 77 if (tundra_version != TUNDRA_VERSION || 78 strncmp((const char *)ctx->buffer + 1, TUNDRA_STRING, 8)) 79 goto error; 80 81 /* read tundra file a first time to find the image size */ 82 while (loop < ctx->length) { 83 if (column == columns) { 84 column = 0; 85 row++; 86 } 87 88 cursor = ctx->buffer[loop]; 89 90 switch (cursor) { 91 case TUNDRA_POSITION: 92 if (loop + 8 < ctx->length) { 93 row = (ctx->buffer[loop + 1] << 24) + 94 (ctx->buffer[loop + 2] << 16) + 95 (ctx->buffer[loop + 3] << 8) + 96 ctx->buffer[loop + 4]; 97 98 column = (ctx->buffer[loop + 5] << 24) + 99 (ctx->buffer[loop + 6] << 16) + 100 (ctx->buffer[loop + 7] << 8) + 101 ctx->buffer[loop + 8]; 102 103 loop += 8; 104 } else { 105 goto error; 106 } 107 108 break; 109 110 case TUNDRA_COLOR_FOREGROUND: 111 case TUNDRA_COLOR_BACKGROUND: 112 loop += 5; 113 column++; 114 break; 115 116 case TUNDRA_COLOR_BOTH: 117 loop += 9; 118 column++; 119 break; 120 121 default: 122 column++; 123 } 124 125 loop++; 126 } 127 128 width = columns * options->bits; 129 height = row * fontData.height; 130 131 if (!width || !height) 132 goto error; 133 134 /* allocate buffer image memory */ 135 canvas = gdImageCreateTrueColor(width, height); 136 137 if (!canvas) { 138 ctx->error = ANSILOVE_GD_ERROR; 139 return -1; 140 } 141 142 /* process tundra */ 143 column = 0; 144 row = 0; 145 146 loop = TUNDRA_HEADER_LENGTH; 147 148 while (loop < ctx->length) { 149 if (column == columns) { 150 column = 0; 151 row++; 152 } 153 154 cursor = character = ctx->buffer[loop]; 155 156 switch (cursor) { 157 case TUNDRA_POSITION: 158 if (loop + 8 < ctx->length) { 159 row = (ctx->buffer[loop + 1] << 24) + 160 (ctx->buffer[loop + 2] << 16) + 161 (ctx->buffer[loop + 3] << 8) + 162 ctx->buffer[loop + 4]; 163 164 column = 165 (ctx->buffer[loop + 5] << 24) + 166 (ctx->buffer[loop + 6] << 16) + 167 (ctx->buffer[loop + 7] << 8) + 168 ctx->buffer[loop + 8]; 169 170 loop += 8; 171 } else { 172 goto error; 173 } 174 175 break; 176 177 case TUNDRA_COLOR_FOREGROUND: 178 if (loop + 5 < ctx->length) { 179 foreground = 180 (ctx->buffer[loop + 3] << 16) + 181 (ctx->buffer[loop + 4] << 8) + 182 ctx->buffer[loop + 5]; 183 184 character = ctx->buffer[loop+1]; 185 186 loop += 5; 187 } else { 188 goto error; 189 } 190 191 break; 192 193 case TUNDRA_COLOR_BACKGROUND: 194 if (loop + 5 < ctx->length) { 195 background = (ctx->buffer[loop + 3] << 16) + 196 (ctx->buffer[loop + 4] << 8) + 197 ctx->buffer[loop + 5]; 198 199 character = ctx->buffer[loop + 1]; 200 201 loop += 5; 202 } else { 203 goto error; 204 } 205 206 break; 207 208 case TUNDRA_COLOR_BOTH: 209 if (loop + 9 < ctx->length) { 210 foreground = 211 (ctx->buffer[loop + 3] << 16) + 212 (ctx->buffer[loop + 4] << 8) + 213 ctx->buffer[loop + 5]; 214 215 background = 216 (ctx->buffer[loop + 7] << 16) + 217 (ctx->buffer[loop + 8] << 8) + 218 ctx->buffer[loop + 9]; 219 220 character = ctx->buffer[loop + 1]; 221 222 loop += 9; 223 } else { 224 goto error; 225 } 226 227 break; 228 } 229 230 if (character != TUNDRA_POSITION && 231 character != TUNDRA_COLOR_BACKGROUND && 232 character != TUNDRA_COLOR_FOREGROUND && 233 character != TUNDRA_COLOR_BOTH) { 234 drawchar(canvas, fontData.font_data, options->bits, 235 fontData.height, column, row, background, 236 foreground, character); 237 238 column++; 239 } 240 241 loop++; 242 } 243 244 /* create output image */ 245 return output(ctx, options, canvas); 246 247 error: 248 ctx->error = ANSILOVE_FORMAT_ERROR; 249 return -1; 250 }