/* psftools: Manipulate console fonts in the .PSF format Copyright (C) 2003, 2021 John Elliott This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "cnvshell.h" #include "psflib.h" /* Compile a textual description (as generated by vfont2txt) to a vfont */ static char helpbuf[2048]; static int vversion = 1; static int big_endian = 0; static VFONT vfont; /* Program name */ char *cnv_progname = "TXT2VFONT"; /* ddash = 1 if option started with a double-dash; else 0 */ /* Return NULL if OK, else error string */ char *cnv_set_option(int ddash, char *variable, char *value) { if (!stricmp(variable, "v1")) { vversion = 1; return NULL; } if (!stricmp(variable, "v2")) { vversion = 2; return NULL; } if (!stricmp(variable, "be") || !stricmp(variable, "big-endian")) { big_endian = 1; return NULL; } if (!stricmp(variable, "le") || !stricmp(variable, "little-endian")) { big_endian = 0; return NULL; } if (strlen(variable) > 2000) variable[2000] = 0; sprintf(helpbuf, "Unknown option: %s\n", variable); return helpbuf; } /* Return help string */ char *cnv_help(void) { sprintf(helpbuf, "Syntax: %s textfile vfont { options }\n\n", cnv_progname); strcat (helpbuf, "Options: \n" " --v1 - Output in vfont version 1 format (default).\n" " --v2 - Output in proposed vfont version 2 format.\n" " --big-endian - Output a big-endian file (v1 only).\n"); return helpbuf; } #define ATSTART 0 #define INHEADER 1 #define INCHAR 2 #define INBITMAP 3 #define NOVALUE 65536L typedef struct charstate { long charleft; long charright; long charup; long chardown; long charwidth; psf_dword char_w; psf_dword char_h; psf_dword char_pitch; psf_dword char_bytes; unsigned char *bitmap; } CHARSTATE; void reset_charstate(CHARSTATE *s) { s->charleft = NOVALUE; s->charright = NOVALUE; s->charup = NOVALUE; s->chardown = NOVALUE; s->charwidth = NOVALUE; s->bitmap = NULL; } char *check_charstate(CHARSTATE *s, int line) { if (s->charleft == NOVALUE) { sprintf(helpbuf, "Error on line %d: Character 'Left' dimension not set", line); return helpbuf; } if (s->charright == NOVALUE) { sprintf(helpbuf, "Error on line %d: Character 'Right' dimension not set", line); return helpbuf; } if (s->charup == NOVALUE) { sprintf(helpbuf, "Error on line %d: Character 'Up' dimension not set", line); return helpbuf; } if (s->chardown == NOVALUE) { sprintf(helpbuf, "Error on line %d: Character 'Down' dimension not set", line); return helpbuf; } if (s->charwidth == NOVALUE) s->charwidth = s->charleft + s->charright; return NULL; } char *create_charstate(CHARSTATE *s, int line) { char *str = check_charstate(s, line); if (str) return str; s->char_w = s->charleft + s->charright; s->char_h = s->charup + s->chardown; s->char_pitch = (s->char_w + 7) / 8; s->char_bytes = s->char_h * s->char_pitch; s->bitmap = malloc(s->char_bytes); if (s->bitmap == NULL) return psf_error_string(PSF_E_NOMEM); memset(s->bitmap, 0, s->char_bytes); return NULL; } char *charstate_to_vfont(VFONT *vf, psf_dword nchar, CHARSTATE *state) { int rv; vf->vf_dispatch[nchar].vfd_addr = vf->vf_size; vf->vf_dispatch[nchar].vfd_nbytes = state->char_bytes; vf->vf_dispatch[nchar].vfd_left = state->charleft; vf->vf_dispatch[nchar].vfd_right = state->charright; vf->vf_dispatch[nchar].vfd_up = state->charup; vf->vf_dispatch[nchar].vfd_down = state->chardown; vf->vf_dispatch[nchar].vfd_width = state->charwidth; rv = vfont_realloc(vf, vf->vf_size + state->char_bytes); if (rv) return psf_error_string(rv); memcpy(&vf->vf_bitmap[vfont.vf_dispatch[nchar].vfd_addr], state->bitmap, state->char_bytes); return NULL; } char *cnv_execute(FILE *infile, FILE *outfile) { int rv, n, x = 0, y = 0; int state = 0; char linebuf[2000]; char unibuf[2000]; char *c; int version = -1, flags = -1, length = -1, extend = -1; int nchar = 0; int line = 0, havebitmap = 0; CHARSTATE chstate; reset_charstate(&chstate); if (big_endian && version == 2) { return "A version 2 vfont cannot be big-endian"; } vfont_new(&vfont); while (fgets(linebuf, sizeof(linebuf), infile)) { ++line; /* If a long line, devour the rest of it */ if (!strchr(linebuf, '\n')) { do { n = fgetc(infile); } while (n != EOF && n != '\n'); } c = strchr(linebuf, '\n'); if (c) *c = 0; c = strstr(linebuf, "//"); if (c) *c = 0; if (linebuf[0] == 0) continue; if (linebuf[0] == '%') { if (!strncmp(linebuf + 1, "VFONT2", 4) && state == ATSTART) { state = INHEADER; continue; } /* %STOP: Immediately stop parsing */ else if (!strncmp(linebuf + 1, "STOP", 4)) { break; } else if (state == INHEADER) { /* End of header. Flush it */ if (version == -1) return "Version not set"; if (flags == -1) return "Flags not set"; if (length == -1) return "Length not set"; if (version > 0) return "Versions greater than 0 are not supported"; rv = vfont_create(&vfont, length, 0, (flags & 1)); if (vversion == 2) { vfont.vf_magic = VFONT_MAGIC_V2; vfont.vf_be = 0; } else { vfont.vf_magic = VFONT_MAGIC_V1; vfont.vf_be = big_endian; } if (extend != -1) vfont.vf_xtend = extend; if (rv) return psf_error_string(rv); state = INCHAR; unibuf[0] = 0; reset_charstate(&chstate); } else if (state == INCHAR || state == INBITMAP) { if (version == -1) return "Version not set"; if (havebitmap) { c = check_charstate(&chstate, line); if (c) return c; /* Flush the character we're building */ if (nchar < vfont.vf_length) { c = charstate_to_vfont(&vfont, nchar, &chstate); if (c) return c; free(chstate.bitmap); chstate.bitmap = NULL; } } if (unibuf[0]) { rv = vfont_unicode_from_string(&vfont, nchar, unibuf); if (rv) { fprintf(stderr, "Line %d: Failed to decode %s\n", line, unibuf); return psf_error_string(rv); } } havebitmap = 0; state = INCHAR; if (chstate.bitmap) free(chstate.bitmap); chstate.bitmap = NULL; reset_charstate(&chstate); nchar++; unibuf[0] = 0; } else { fprintf(stderr, "Line %d: Unexpected %% line\n", line); return "Invalid input format"; } } else { c = strstr(linebuf, ": "); if (c) { *c = 0; c++; while (*c == ' ') c++; if (state == INHEADER) { if (!strcmp(linebuf, "Version")) version = atol(c); else if (!strcmp(linebuf, "Flags")) flags = atol(c); else if (!strcmp(linebuf, "Length")) length = atol(c); else if (!strcmp(linebuf, "Extend")) extend = atol(c); /* BitmapLength, MaxWidth and MaxHeight are output by vfont2txt, but * ignored by txt2vfont since they are calculated fields */ else if (!strcmp(linebuf, "BitmapLength")) ; else if (!strcmp(linebuf, "MaxWidth")) ; else if (!strcmp(linebuf, "MaxHeight")) ; else { fprintf(stderr, "Line %d: Unknown variable name '%s'\n", line, linebuf); return "Invalid input format"; } } else if (state == INCHAR) { if (!strcmp(linebuf, "Left")) chstate.charleft = atoi(c); else if (!strcmp(linebuf, "Right")) chstate.charright = atoi(c); else if (!strcmp(linebuf, "Up")) chstate.charup = atoi(c); else if (!strcmp(linebuf, "Down")) chstate.chardown = atoi(c); else if (!strcmp(linebuf, "Width")) chstate.charwidth = atoi(c); else if (!strcmp(linebuf, "Unicode")) strcpy(unibuf, c); else if (!strcmp(linebuf, "Bitmap")) { memmove(linebuf, c, 1 + strlen(c)); /* When starting a bitmap, we need dimensions */ c = create_charstate(&chstate, line); if (c) return c; state = INBITMAP; havebitmap = 0; x = y = 0; } else { fprintf(stderr, "Line %d: Unknown variable name '%s'\n", line, linebuf); return "Invalid input format"; } } else /* Not INCHAR and not INHEADER */ { fprintf(stderr, "Line %d: Unknown variable name '%s'\n", line, linebuf); return "Invalid input format"; } if (state != INBITMAP) continue; } /* end if (c) */ if (state == INBITMAP) { psf_byte *dest, mask; c = linebuf; while (*c) { if (!strchr("-#O@=|", *c)) { c++; continue; } dest = chstate.bitmap + (y * chstate.char_pitch) + (x/8); mask = 0x80 >> (x & 7); if (*c == '#' || *c == '@') *dest |= mask; else *dest &= ~mask; if ((*c == '@' || *c == 'O') && (x != chstate.charleft || y != chstate.charup)) { fprintf(stderr, "Warning (character %d): %c sign (base point position) does not match\n" " the position derived from left/right/up/down.\n", nchar, *c); } x++; if (x >= chstate.char_w) { x = 0; y++; } if (y >= chstate.char_h) { state = INCHAR; havebitmap = 1; break; } ++c; } continue; } fprintf(stderr, "Line %d: Not a Variable: Value pair\n", line); return "Invalid input format"; } } if (state == INCHAR && havebitmap) { c = charstate_to_vfont(&vfont, nchar, &chstate); if (c) return c; free(chstate.bitmap); chstate.bitmap = NULL; if (unibuf[0]) { rv = vfont_unicode_from_string(&vfont, nchar, unibuf); if (rv) return psf_error_string(rv); } } if (version == 1) vfont_force_v1(&vfont); if (version == 2) vfont_force_v2(&vfont); rv = vfont_write(&vfont, outfile); vfont_delete(&vfont); if (rv) return psf_error_string(rv); return NULL; }