rnd-20140205-1-src
[rocksndiamonds.git] / src / libgame / text.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * text.c                                                   *
12 ***********************************************************/
13
14 #include <stdio.h>
15 #include <stdarg.h>
16
17 #include "text.h"
18 #include "misc.h"
19
20
21 /* ========================================================================= */
22 /* font functions                                                            */
23 /* ========================================================================= */
24
25 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
26 static GC       font_clip_gc = None;
27
28 static void InitFontClipmasks()
29 {
30   XGCValues clip_gc_values;
31   unsigned int clip_gc_valuemask;
32   GC copy_clipmask_gc;
33   int i, j;
34
35   /* This stuff is needed because X11 (XSetClipOrigin(), to be precise) is
36      often very slow when preparing a masked XCopyArea() for big Pixmaps.
37      To prevent this, create small (tile-sized) mask Pixmaps which will then
38      be set much faster with XSetClipOrigin() and speed things up a lot. */
39
40   clip_gc_values.graphics_exposures = False;
41   clip_gc_valuemask = GCGraphicsExposures;
42   font_clip_gc = XCreateGC(display, window->drawable,
43                            clip_gc_valuemask, &clip_gc_values);
44
45   /* create graphic context structures needed for clipping */
46   clip_gc_values.graphics_exposures = False;
47   clip_gc_valuemask = GCGraphicsExposures;
48   copy_clipmask_gc = XCreateGC(display,
49                                gfx.font_bitmap_info[0].bitmap->clip_mask,
50                                clip_gc_valuemask, &clip_gc_values);
51
52   /* create only those clipping Pixmaps we really need */
53   for (i = 0; i < gfx.num_fonts; i++)
54   {
55     if (gfx.font_bitmap_info[i].bitmap == NULL)
56       continue;
57
58     gfx.font_bitmap_info[i].clip_mask =
59       checked_calloc(gfx.font_bitmap_info[i].num_chars * sizeof(Pixmap));
60
61     for (j = 0; j < gfx.font_bitmap_info[i].num_chars; j++)
62     {
63       Bitmap *src_bitmap = gfx.font_bitmap_info[i].bitmap;
64       Pixmap src_pixmap = src_bitmap->clip_mask;
65       int xpos = j % gfx.font_bitmap_info[i].num_chars_per_line;
66       int ypos = j / gfx.font_bitmap_info[i].num_chars_per_line;
67       int width  = gfx.font_bitmap_info[i].width;
68       int height = gfx.font_bitmap_info[i].height;
69       int src_x = gfx.font_bitmap_info[i].src_x + xpos * width;
70       int src_y = gfx.font_bitmap_info[i].src_y + ypos * height;
71
72       gfx.font_bitmap_info[i].clip_mask[j] =
73         XCreatePixmap(display, window->drawable, width, height, 1);
74
75       XCopyArea(display, src_pixmap, gfx.font_bitmap_info[i].clip_mask[j],
76                 copy_clipmask_gc, src_x, src_y, width, height, 0, 0);
77     }
78   }
79
80   XFreeGC(display, copy_clipmask_gc);
81 }
82
83 static void FreeFontClipmasks()
84 {
85   int i, j;
86
87   if (gfx.num_fonts == 0 || gfx.font_bitmap_info[0].bitmap == NULL)
88     return;
89
90   for (i = 0; i < gfx.num_fonts; i++)
91   {
92     if (gfx.font_bitmap_info[i].clip_mask)
93     {
94       for (j = 0; j < gfx.font_bitmap_info[i].num_chars; j++)
95         XFreePixmap(display, gfx.font_bitmap_info[i].clip_mask[j]);
96       free(gfx.font_bitmap_info[i].clip_mask);
97     }
98
99     gfx.font_bitmap_info[i].clip_mask = NULL;
100     gfx.font_bitmap_info[i].num_chars = 0;
101   }
102
103   if (font_clip_gc)
104     XFreeGC(display, font_clip_gc);
105   font_clip_gc = None;
106 }
107 #endif /* TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND */
108
109 void InitFontInfo(struct FontBitmapInfo *font_bitmap_info, int num_fonts,
110                   int (*select_font_function)(int),
111                   int (*get_font_from_token_function)(char *))
112 {
113   gfx.num_fonts = num_fonts;
114   gfx.font_bitmap_info = font_bitmap_info;
115   gfx.select_font_function = select_font_function;
116   gfx.get_font_from_token_function = get_font_from_token_function;
117
118 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
119   InitFontClipmasks();
120 #endif
121 }
122
123 void FreeFontInfo(struct FontBitmapInfo *font_bitmap_info)
124 {
125   if (font_bitmap_info == NULL)
126     return;
127
128 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
129   FreeFontClipmasks();
130 #endif
131
132   free(font_bitmap_info);
133 }
134
135 struct FontBitmapInfo *getFontBitmapInfo(int font_nr)
136 {
137   int font_bitmap_id = gfx.select_font_function(font_nr);
138
139   return &gfx.font_bitmap_info[font_bitmap_id];
140 }
141
142 int getFontWidth(int font_nr)
143 {
144   int font_bitmap_id = gfx.select_font_function(font_nr);
145
146   return gfx.font_bitmap_info[font_bitmap_id].width;
147 }
148
149 int getFontHeight(int font_nr)
150 {
151   int font_bitmap_id = gfx.select_font_function(font_nr);
152
153   return gfx.font_bitmap_info[font_bitmap_id].height;
154 }
155
156 int getTextWidth(char *text, int font_nr)
157 {
158   return (text != NULL ? strlen(text) * getFontWidth(font_nr) : 0);
159 }
160
161 static int getFontCharPosition(int font_nr, char c)
162 {
163   int font_bitmap_id = gfx.select_font_function(font_nr);
164   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
165   boolean default_font = (font->num_chars == DEFAULT_NUM_CHARS_PER_FONT);
166   int font_pos = (unsigned char)c - 32;
167
168   /* map some special characters to their ascii values in default font */
169   if (default_font)
170     font_pos = MAP_FONT_ASCII(c) - 32;
171
172   /* this allows dynamic special characters together with special font */
173   if (font_pos < 0 || font_pos >= font->num_chars)
174     font_pos = 0;
175
176   return font_pos;
177 }
178
179 void getFontCharSource(int font_nr, char c, Bitmap **bitmap, int *x, int *y)
180 {
181   int font_bitmap_id = gfx.select_font_function(font_nr);
182   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
183   int font_pos = getFontCharPosition(font_nr, c);
184
185   *bitmap = font->bitmap;
186   *x = font->src_x + (font_pos % font->num_chars_per_line) * font->width;
187   *y = font->src_y + (font_pos / font->num_chars_per_line) * font->height;
188 }
189
190
191 /* ========================================================================= */
192 /* text string helper functions                                              */
193 /* ========================================================================= */
194
195 int maxWordLengthInString(char *text)
196 {
197   char *text_ptr;
198   int word_len = 0, max_word_len = 0;
199
200   for (text_ptr = text; *text_ptr; text_ptr++)
201   {
202     word_len = (*text_ptr != ' ' ? word_len + 1 : 0);
203
204     max_word_len = MAX(word_len, max_word_len);
205   }
206
207   return max_word_len;
208 }
209
210
211 /* ========================================================================= */
212 /* simple text drawing functions                                             */
213 /* ========================================================================= */
214
215 void DrawInitTextExt(char *text, int ypos, int font_nr, boolean force)
216 {
217 #if 1
218 #if 0
219   static unsigned int progress_delay = 0;
220   unsigned int progress_delay_value = 100;      /* (in milliseconds) */
221 #endif
222
223   // LimitScreenUpdates(TRUE);  // (ignore "force" for now)
224   // LimitScreenUpdates(!force);
225   LimitScreenUpdates(TRUE);
226
227   UPDATE_BUSY_STATE();
228
229 #if 0
230   if (!force && !DelayReached(&progress_delay, progress_delay_value))
231     return;
232 #endif
233
234   if (window != NULL &&
235       gfx.draw_init_text &&
236       gfx.num_fonts > 0 &&
237       gfx.font_bitmap_info[font_nr].bitmap != NULL)
238   {
239     int x = (video.width - getTextWidth(text, font_nr)) / 2;
240     int y = ypos;
241     int width = video.width;
242     int height = getFontHeight(font_nr);
243
244     ClearRectangle(drawto, 0, y, width, height);
245     DrawTextExt(drawto, x, y, text, font_nr, BLIT_OPAQUE);
246
247     BlitBitmap(drawto, window, 0, 0, video.width, video.height, 0, 0);
248   }
249 #else
250   static unsigned int progress_delay = 0;
251   unsigned int progress_delay_value = 100;      /* (in milliseconds) */
252
253   // LimitScreenUpdates(TRUE);  // (ignore "force" for now)
254   LimitScreenUpdates(!force);
255
256   UPDATE_BUSY_STATE();
257
258   if (!force && !DelayReached(&progress_delay, progress_delay_value))
259     return;
260
261   if (window != NULL &&
262       gfx.draw_init_text &&
263       gfx.num_fonts > 0 &&
264       gfx.font_bitmap_info[font_nr].bitmap != NULL)
265   {
266     int x = (video.width - getTextWidth(text, font_nr)) / 2;
267     int y = ypos;
268     int width = video.width;
269     int height = getFontHeight(font_nr);
270
271     ClearRectangle(drawto, 0, y, width, height);
272     DrawTextExt(drawto, x, y, text, font_nr, BLIT_OPAQUE);
273
274     /* this makes things significantly faster than directly drawing to window */
275     BlitBitmap(drawto, window, 0, y, width, height, 0, y);
276   }
277 #endif
278 }
279
280 void DrawInitText(char *text, int ypos, int font_nr)
281 {
282   // DrawInitTextExt(text, ypos, font_nr, TRUE);
283   DrawInitTextExt(text, ypos, font_nr, FALSE);
284 }
285
286 void DrawInitTextAlways(char *text, int ypos, int font_nr)
287 {
288   DrawInitTextExt(text, ypos, font_nr, TRUE);
289 }
290
291 void DrawInitTextIfNeeded(char *text, int ypos, int font_nr)
292 {
293   DrawInitTextExt(text, ypos, font_nr, FALSE);
294 }
295
296 void DrawTextF(int x, int y, int font_nr, char *format, ...)
297 {
298   char buffer[MAX_OUTPUT_LINESIZE + 1];
299   va_list ap;
300
301   va_start(ap, format);
302   vsprintf(buffer, format, ap);
303   va_end(ap);
304
305   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
306     Error(ERR_EXIT, "string too long in DrawTextF() -- aborting");
307
308   DrawText(gfx.sx + x, gfx.sy + y, buffer, font_nr);
309 }
310
311 void DrawTextFCentered(int y, int font_nr, char *format, ...)
312 {
313   char buffer[MAX_OUTPUT_LINESIZE + 1];
314   va_list ap;
315
316   va_start(ap, format);
317   vsprintf(buffer, format, ap);
318   va_end(ap);
319
320   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
321     Error(ERR_EXIT, "string too long in DrawTextFCentered() -- aborting");
322
323   DrawText(gfx.sx + (gfx.sxsize - getTextWidth(buffer, font_nr)) / 2,
324            gfx.sy + y, buffer, font_nr);
325 }
326
327 void DrawTextS(int x, int y, int font_nr, char *text)
328 {
329   DrawText(gfx.sx + x, gfx.sy + y, text, font_nr);
330 }
331
332 void DrawTextSCentered(int y, int font_nr, char *text)
333 {
334   DrawText(gfx.sx + (gfx.sxsize - getTextWidth(text, font_nr)) / 2,
335            gfx.sy + y, text, font_nr);
336 }
337
338 void DrawTextCentered(int y, int font_nr, char *text)
339 {
340   DrawText((gfx.sxsize - getTextWidth(text, font_nr)) / 2, y, text, font_nr);
341 }
342
343 void DrawTextSAligned(int x, int y, char *text, int font_nr, int align)
344 {
345   DrawText(gfx.sx + ALIGNED_XPOS(x, getTextWidth(text, font_nr), align),
346            gfx.sx + y, text, font_nr);
347 }
348
349 void DrawTextAligned(int x, int y, char *text, int font_nr, int align)
350 {
351   DrawText(ALIGNED_XPOS(x, getTextWidth(text, font_nr), align),
352            y, text, font_nr);
353 }
354
355 void DrawText(int x, int y, char *text, int font_nr)
356 {
357   int mask_mode = BLIT_OPAQUE;
358
359   if (DrawingOnBackground(x, y))
360     mask_mode = BLIT_ON_BACKGROUND;
361
362   DrawTextExt(drawto, x, y, text, font_nr, mask_mode);
363
364   if (x < gfx.dx)
365     redraw_mask |= REDRAW_FIELD;
366   else if (y < gfx.vy || gfx.vy == 0)
367     redraw_mask |= REDRAW_DOOR_1;
368 }
369
370 void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
371                  int font_nr, int mask_mode)
372 {
373 #if 1
374   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
375 #else
376   int font_bitmap_id = gfx.select_font_function(font_nr);
377   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
378 #endif
379   int font_width = getFontWidth(font_nr);
380   int font_height = getFontHeight(font_nr);
381 #if 0
382   int border_1 = gfx.sx + gfx.sxsize;
383   int border_2 = gfx.dx + gfx.dxsize;
384   int dst_x_start = dst_x;
385 #endif
386   Bitmap *src_bitmap;
387   int src_x, src_y;
388   char *text_ptr = text;
389
390   if (font->bitmap == NULL)
391     return;
392
393   /* skip text to be printed outside the window (left/right will be clipped) */
394   if (dst_y < 0 || dst_y + font_height > video.height)
395     return;
396
397   /* add offset for drawing font characters */
398   dst_x += font->draw_xoffset;
399   dst_y += font->draw_yoffset;
400
401   while (*text_ptr)
402   {
403     char c = *text_ptr++;
404
405     if (c == '\n')
406       c = ' ';          /* print space instead of newline */
407
408     getFontCharSource(font_nr, c, &src_bitmap, &src_x, &src_y);
409
410     /* clip text at the left side of the window */
411     if (dst_x < 0)
412     {
413       dst_x += font_width;
414
415       continue;
416     }
417
418     /* clip text at the right side of the window */
419 #if 1
420     if (dst_x + font_width > video.width)
421       break;
422 #else
423     /* (this does not work well when trying to print text to whole screen) */
424     if ((dst_x_start < border_1 && dst_x + font_width > border_1) ||
425         (dst_x_start < border_2 && dst_x + font_width > border_2))
426       break;
427 #endif
428
429     if (mask_mode == BLIT_INVERSE)      /* special mode for text gadgets */
430     {
431       /* first step: draw solid colored rectangle (use "cursor" character) */
432       if (strlen(text) == 1)    /* only one char inverted => draw cursor */
433       {
434         Bitmap *cursor_bitmap;
435         int cursor_x, cursor_y;
436
437         getFontCharSource(font_nr, FONT_ASCII_CURSOR, &cursor_bitmap,
438                           &cursor_x, &cursor_y);
439
440         BlitBitmap(cursor_bitmap, dst_bitmap, cursor_x, cursor_y,
441                    font_width, font_height, dst_x, dst_y);
442       }
443
444 #if defined(TARGET_SDL)
445       /* second step: draw masked inverted character */
446       SDLCopyInverseMasked(src_bitmap, dst_bitmap, src_x, src_y,
447                            font_width, font_height, dst_x, dst_y);
448 #else
449       /* second step: draw masked black rectangle (use "space" character) */
450       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
451                     dst_x - src_x, dst_y - src_y);
452       BlitBitmapMasked(src_bitmap, dst_bitmap, 0, 0,
453                        font_width, font_height, dst_x, dst_y);
454 #endif
455     }
456     else if (mask_mode == BLIT_MASKED || mask_mode == BLIT_ON_BACKGROUND)
457     {
458       if (mask_mode == BLIT_ON_BACKGROUND)
459       {
460         /* clear font character background */
461         ClearRectangleOnBackground(dst_bitmap, dst_x, dst_y,
462                                    font_width, font_height);
463       }
464
465 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
466       /* use special font tile clipmasks */
467       {
468         int font_pos = getFontCharPosition(font_nr, c);
469
470         SetClipMask(src_bitmap, font_clip_gc, font->clip_mask[font_pos]);
471         SetClipOrigin(src_bitmap, font_clip_gc, dst_x, dst_y);
472       }
473 #else
474       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
475                     dst_x - src_x, dst_y - src_y);
476 #endif
477
478       BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y,
479                        font_width, font_height, dst_x, dst_y);
480     }
481     else        /* normal, non-masked font blitting */
482     {
483       BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y,
484                  font_width, font_height, dst_x, dst_y);
485     }
486
487     dst_x += font_width;
488   }
489 }
490
491
492 /* ========================================================================= */
493 /* text buffer drawing functions                                             */
494 /* ========================================================================= */
495
496 #define MAX_LINES_FROM_FILE             1024
497
498 #if 1
499
500 char *GetTextBufferFromFile(char *filename, int max_lines)
501 {
502   File *file;
503   char *buffer;
504   int num_lines = 0;
505
506   if (filename == NULL)
507     return NULL;
508
509   if (!(file = openFile(filename, MODE_READ)))
510     return NULL;
511
512   buffer = checked_calloc(1);   /* start with valid, but empty text buffer */
513
514   while (!checkEndOfFile(file) && num_lines < max_lines)
515   {
516     char line[MAX_LINE_LEN];
517
518     /* read next line of input file */
519     if (!getStringFromFile(file, line, MAX_LINE_LEN))
520       break;
521
522     buffer = checked_realloc(buffer, strlen(buffer) + strlen(line) + 1);
523
524     strcat(buffer, line);
525
526     num_lines++;
527   }
528
529   closeFile(file);
530
531   return buffer;
532 }
533
534 #else
535
536 char *GetTextBufferFromFile(char *filename, int max_lines)
537 {
538   FILE *file;
539   char *buffer;
540   int num_lines = 0;
541
542   if (filename == NULL)
543     return NULL;
544
545   if (!(file = fopen(filename, MODE_READ)))
546     return NULL;
547
548   buffer = checked_calloc(1);   /* start with valid, but empty text buffer */
549
550   while (!feof(file) && num_lines < max_lines)
551   {
552     char line[MAX_LINE_LEN];
553
554     /* read next line of input file */
555     if (!fgets(line, MAX_LINE_LEN, file))
556       break;
557
558     buffer = checked_realloc(buffer, strlen(buffer) + strlen(line) + 1);
559
560     strcat(buffer, line);
561
562     num_lines++;
563   }
564
565   fclose(file);
566
567   return buffer;
568 }
569
570 #endif
571
572 void DrawTextToTextArea_OLD(int x, int y, char *text, int font_nr, int line_length,
573                             int area_xsize, int area_ysize, int mask_mode)
574 {
575   int area_line = 0;
576   int font_height = getFontHeight(font_nr);
577
578   if (text == NULL)
579     return;
580
581   while (*text && area_line < area_ysize)
582   {
583     char buffer[MAX_OUTPUT_LINESIZE + 1];
584     int i;
585
586     for (i = 0; i < line_length && *text; i++)
587       if ((buffer[i] = *text++) == '\n')
588         break;
589     buffer[MIN(i, area_xsize)] = '\0';
590
591     DrawTextExt(drawto, x, y + area_line * font_height, buffer, font_nr,
592                 mask_mode);
593
594     area_line++;
595   }
596
597   redraw_mask |= REDRAW_FIELD;
598 }
599
600 static boolean RenderLineToBuffer(char **src_buffer_ptr, char *dst_buffer,
601                                   int *dst_buffer_len, int line_length,
602                                   boolean last_line_was_empty)
603 {
604   char *text_ptr = *src_buffer_ptr;
605   char *buffer = dst_buffer;
606   int buffer_len = *dst_buffer_len;
607   boolean buffer_filled = FALSE;
608
609   while (*text_ptr)
610   {
611     char *word_ptr;
612     int word_len;
613
614     /* skip leading whitespaces */
615     while (*text_ptr == ' ' || *text_ptr == '\t')
616       text_ptr++;
617
618     word_ptr = text_ptr;
619     word_len = 0;
620
621     /* look for end of next word */
622     while (*word_ptr != ' ' && *word_ptr != '\t' && *word_ptr != '\0')
623     {
624       word_ptr++;
625       word_len++;
626     }
627
628     if (word_len == 0)
629     {
630       continue;
631     }
632     else if (*text_ptr == '\n')         /* special case: force empty line */
633     {
634       if (buffer_len == 0)
635         text_ptr++;
636
637       /* prevent printing of multiple empty lines */
638       if (buffer_len > 0 || !last_line_was_empty)
639         buffer_filled = TRUE;
640     }
641     else if (word_len < line_length - buffer_len)
642     {
643       /* word fits into text buffer -- add word */
644
645       if (buffer_len > 0)
646         buffer[buffer_len++] = ' ';
647
648       strncpy(&buffer[buffer_len], text_ptr, word_len);
649       buffer_len += word_len;
650       buffer[buffer_len] = '\0';
651       text_ptr += word_len;
652     }
653     else if (buffer_len > 0)
654     {
655       /* not enough space left for word in text buffer -- print buffer */
656
657       buffer_filled = TRUE;
658     }
659     else
660     {
661       /* word does not fit at all into empty text buffer -- cut word */
662
663       strncpy(buffer, text_ptr, line_length);
664       buffer[line_length] = '\0';
665       text_ptr += line_length;
666       buffer_filled = TRUE;
667     }
668
669     if (buffer_filled)
670       break;
671   }
672
673   *src_buffer_ptr = text_ptr;
674   *dst_buffer_len = buffer_len;
675
676   return buffer_filled;
677 }
678
679 #if 0
680 void DrawTextWrapped_OLD(int x, int y, char *text, int font_nr, int line_length,
681                          int max_lines)
682 {
683   char *text_ptr = text;
684   int current_line = 0;
685   int font_height = getFontHeight(font_nr);
686
687   while (*text_ptr && current_line < max_lines)
688   {
689     char buffer[line_length + 1];
690     int buffer_len = 0;
691
692     buffer[0] = '\0';
693
694     RenderLineToBuffer(&text_ptr, buffer, &buffer_len, line_length, TRUE);
695
696     DrawText(x, y + current_line * font_height, buffer, font_nr);
697     current_line++;
698   }
699 }
700 #endif
701
702 #if 0
703 int DrawTextFromFile_OLD(int x, int y, char *filename, int font_nr,
704                          int line_length, int max_lines, boolean wrap_text)
705 {
706   int font_height = getFontHeight(font_nr);
707   char line[MAX_LINE_LEN];
708   char buffer[line_length + 1];
709   int buffer_len;
710   int current_line = 0;
711   FILE *file;
712
713   if (current_line >= max_lines)
714     return 0;
715
716   if (filename == NULL)
717     return 0;
718
719   if (!(file = fopen(filename, MODE_READ)))
720     return 0;
721
722   buffer[0] = '\0';
723   buffer_len = 0;
724
725   while (!feof(file) && current_line < max_lines)
726   {
727     char *line_ptr;
728     boolean last_line_was_empty = TRUE;
729
730     /* read next line of input file */
731     if (!fgets(line, MAX_LINE_LEN, file))
732       break;
733
734     /* skip comments (lines directly beginning with '#') */
735     if (line[0] == '#')
736       continue;
737
738     /* cut trailing newline from input line */
739     for (line_ptr = line; *line_ptr; line_ptr++)
740     {
741       if (*line_ptr == '\n' || *line_ptr == '\r')
742       {
743         *line_ptr = '\0';
744         break;
745       }
746     }
747
748     if (strlen(line) == 0)              /* special case: force empty line */
749       strcpy(line, "\n");
750
751     line_ptr = line;
752
753     while (*line_ptr && current_line < max_lines)
754     {
755 #if 1
756       boolean buffer_filled;
757
758       if (wrap_text)
759       {
760         buffer_filled = RenderLineToBuffer(&line_ptr, buffer, &buffer_len,
761                                            line_length, last_line_was_empty);
762       }
763       else
764       {
765         if (strlen(line_ptr) <= line_length)
766         {
767           buffer_len = strlen(line_ptr);
768           strcpy(buffer, line_ptr);
769         }
770         else
771         {
772           buffer_len = line_length;
773           strncpy(buffer, line_ptr, line_length);
774         }
775
776         buffer[buffer_len] = '\0';
777         line_ptr += buffer_len;
778
779         buffer_filled = TRUE;
780       }
781 #else
782       boolean buffer_filled = RenderLineToBuffer(&line_ptr, buffer, &buffer_len,
783                                                  line_length, last_line_was_empty);
784 #endif
785
786       if (buffer_filled)
787       {
788         DrawText(x, y + current_line * font_height, buffer, font_nr);
789         current_line++;
790
791         last_line_was_empty = (buffer_len == 0);
792
793         buffer[0] = '\0';
794         buffer_len = 0;
795       }
796     }
797   }
798
799   fclose(file);
800
801   if (buffer_len > 0 && current_line < max_lines)
802   {
803     DrawText(x, y + current_line * font_height, buffer, font_nr);
804     current_line++;
805   }
806
807   return current_line;
808 }
809 #endif
810
811 static boolean getCheckedTokenValueFromString(char *string, char **token,
812                                               char **value)
813 {
814   char *ptr;
815
816   if (!getTokenValueFromString(string, token, value))
817     return FALSE;
818
819   if (**token != '.')                   /* token should begin with dot */
820     return FALSE;
821
822   for (ptr = *token; *ptr; ptr++)       /* token should contain no whitespace */
823     if (*ptr == ' ' || *ptr == '\t')
824       return FALSE;
825
826   for (ptr = *value; *ptr; ptr++)       /* value should contain no whitespace */
827     if (*ptr == ' ' || *ptr == '\t')
828       return FALSE;
829
830   return TRUE;
831 }
832
833 static void DrawTextBuffer_Flush(int x, int y, char *buffer, int font_nr,
834                                  int line_length, int cut_length,
835                                  int line_spacing, int mask_mode,
836                                  boolean centered, int current_line)
837 {
838   int buffer_len = strlen(buffer);
839   int font_width = getFontWidth(font_nr);
840   int font_height = getFontHeight(font_nr);
841   int offset_chars = (centered ? (line_length - buffer_len) / 2 : 0);
842   int offset_xsize =
843     (centered ? font_width * (line_length - buffer_len) / 2 : 0);
844   int final_cut_length = MAX(0, cut_length - offset_chars);
845   int xx = x + offset_xsize;
846   int yy = y + current_line * (font_height + line_spacing);
847
848   buffer[final_cut_length] = '\0';
849
850   if (mask_mode != -1)
851     DrawTextExt(drawto, xx, yy, buffer, font_nr, mask_mode);
852   else
853     DrawText(xx, yy, buffer, font_nr);
854 }
855
856 int DrawTextBuffer(int x, int y, char *text_buffer, int font_nr,
857                    int line_length, int cut_length, int max_lines,
858                    int line_spacing, int mask_mode, boolean autowrap,
859                    boolean centered, boolean parse_comments)
860 {
861 #if 0
862   int font_width = getFontWidth(font_nr);
863   int font_height = getFontHeight(font_nr);
864 #endif
865   char buffer[line_length + 1];
866   int buffer_len;
867   int current_line = 0;
868
869   if (text_buffer == NULL || *text_buffer == '\0')
870     return 0;
871
872   if (current_line >= max_lines)
873     return 0;
874
875   if (cut_length == -1)
876     cut_length = line_length;
877
878   buffer[0] = '\0';
879   buffer_len = 0;
880
881   while (*text_buffer && current_line < max_lines)
882   {
883     char line[MAX_LINE_LEN + 1];
884     char *line_ptr;
885     boolean last_line_was_empty = TRUE;
886 #if 1
887     int num_line_chars = MAX_LINE_LEN;
888 #else
889     int num_line_chars = (autowrap ? MAX_LINE_LEN : line_length);
890 #endif
891     int i;
892
893     /* copy next line from text buffer to line buffer (nearly fgets() style) */
894     for (i = 0; i < num_line_chars && *text_buffer; i++)
895       if ((line[i] = *text_buffer++) == '\n')
896         break;
897     line[i] = '\0';
898
899     /* prevent 'num_line_chars' sized lines to cause additional empty line */
900     if (i == num_line_chars && *text_buffer == '\n')
901       text_buffer++;
902
903     /* skip comments (lines directly beginning with '#') */
904     if (line[0] == '#' && parse_comments)
905     {
906       char *token, *value;
907
908       /* try to read generic token/value pair definition after comment sign */
909       if (getCheckedTokenValueFromString(line + 1, &token, &value))
910       {
911         /* if found, flush the current buffer, if non-empty */
912         if (buffer_len > 0 && current_line < max_lines)
913         {
914           DrawTextBuffer_Flush(x, y, buffer, font_nr, line_length, cut_length,
915                                line_spacing, mask_mode, centered, current_line);
916
917           current_line++;
918
919           buffer[0] = '\0';
920           buffer_len = 0;
921         }
922
923         if (strEqual(token, ".font"))
924           font_nr = gfx.get_font_from_token_function(value);
925         else if (strEqual(token, ".autowrap"))
926           autowrap = get_boolean_from_string(value);
927         else if (strEqual(token, ".centered"))
928           centered = get_boolean_from_string(value);
929         else if (strEqual(token, ".parse_comments"))
930           parse_comments = get_boolean_from_string(value);
931       }
932
933       continue;
934     }
935
936     /* cut trailing newline and carriage return from input line */
937     for (line_ptr = line; *line_ptr; line_ptr++)
938     {
939       if (*line_ptr == '\n' || *line_ptr == '\r')
940       {
941         *line_ptr = '\0';
942         break;
943       }
944     }
945
946     if (strlen(line) == 0)              /* special case: force empty line */
947       strcpy(line, "\n");
948
949     line_ptr = line;
950
951     while (*line_ptr && current_line < max_lines)
952     {
953       boolean buffer_filled;
954
955       if (autowrap)
956       {
957         buffer_filled = RenderLineToBuffer(&line_ptr, buffer, &buffer_len,
958                                            line_length, last_line_was_empty);
959       }
960       else
961       {
962         if (strlen(line_ptr) <= line_length)
963         {
964           buffer_len = strlen(line_ptr);
965           strcpy(buffer, line_ptr);
966         }
967         else
968         {
969           buffer_len = line_length;
970           strncpy(buffer, line_ptr, line_length);
971         }
972
973         buffer[buffer_len] = '\0';
974         line_ptr += buffer_len;
975
976         buffer_filled = TRUE;
977       }
978
979       if (buffer_filled)
980       {
981 #if 1
982         DrawTextBuffer_Flush(x, y, buffer, font_nr, line_length, cut_length,
983                              line_spacing, mask_mode, centered, current_line);
984 #else
985         int offset_chars = (centered ? (line_length - buffer_len) / 2 : 0);
986         int offset_xsize =
987           (centered ?  font_width * (line_length - buffer_len) / 2 : 0);
988         int final_cut_length = MAX(0, cut_length - offset_chars);
989         int xx = x + offset_xsize;
990
991         buffer[final_cut_length] = '\0';
992
993         if (mask_mode != -1)
994           DrawTextExt(drawto, xx, y + current_line * font_height, buffer,
995                       font_nr, mask_mode);
996         else
997           DrawText(xx, y + current_line * font_height, buffer, font_nr);
998 #endif
999
1000         current_line++;
1001
1002         last_line_was_empty = (buffer_len == 0);
1003
1004         buffer[0] = '\0';
1005         buffer_len = 0;
1006       }
1007     }
1008   }
1009
1010   if (buffer_len > 0 && current_line < max_lines)
1011   {
1012 #if 1
1013     DrawTextBuffer_Flush(x, y, buffer, font_nr, line_length, cut_length,
1014                          line_spacing, mask_mode, centered, current_line);
1015 #else
1016     int offset_chars = (centered ? (line_length - buffer_len) / 2 : 0);
1017         int offset_xsize =
1018           (centered ?  font_width * (line_length - buffer_len) / 2 : 0);
1019     int final_cut_length = MAX(0, cut_length - offset_chars);
1020     int xx = x + offset_xsize;
1021
1022     buffer[final_cut_length] = '\0';
1023
1024     if (mask_mode != -1)
1025       DrawTextExt(drawto, xx, y + current_line * font_height, buffer,
1026                   font_nr, mask_mode);
1027     else
1028       DrawText(xx, y + current_line * font_height, buffer, font_nr);
1029 #endif
1030
1031     current_line++;
1032   }
1033
1034   return current_line;
1035 }
1036
1037 int DrawTextBufferVA(int x, int y, char *format, va_list ap, int font_nr,
1038                      int line_length, int cut_length, int max_lines,
1039                      int line_spacing, int mask_mode, boolean autowrap,
1040                      boolean centered, boolean parse_comments)
1041 {
1042   char text_buffer[MAX_OUTPUT_LINESIZE];
1043   int text_length = vsnprintf(text_buffer, MAX_OUTPUT_LINESIZE, format, ap);
1044
1045   if (text_length >= MAX_OUTPUT_LINESIZE)
1046     Error(ERR_WARN, "string too long in DrawTextBufferVA() -- truncated");
1047
1048   int num_lines_printed = DrawTextBuffer(x, y, text_buffer, font_nr,
1049                                          line_length, cut_length, max_lines,
1050                                          line_spacing, mask_mode, autowrap,
1051                                          centered, parse_comments);
1052   return num_lines_printed;
1053 }
1054
1055 int DrawTextFile(int x, int y, char *filename, int font_nr,
1056                  int line_length, int cut_length, int max_lines,
1057                  int line_spacing, int mask_mode, boolean autowrap,
1058                  boolean centered, boolean parse_comments)
1059 {
1060   char *text_buffer = GetTextBufferFromFile(filename, MAX_LINES_FROM_FILE);
1061   int num_lines_printed = DrawTextBuffer(x, y, text_buffer, font_nr,
1062                                          line_length, cut_length, max_lines,
1063                                          line_spacing, mask_mode, autowrap,
1064                                          centered, parse_comments);
1065   checked_free(text_buffer);
1066
1067   return num_lines_printed;
1068 }
1069
1070 #if 0
1071 void DrawTextWrapped(int x, int y, char *text, int font_nr, int line_length,
1072                      int max_lines)
1073 {
1074   DrawTextBuffer(x, y, text, font_nr, line_length, -1, max_lines, -1, TRUE,
1075                  FALSE, FALSE);
1076 }
1077
1078 void DrawTextToTextArea(int x, int y, char *text, int font_nr, int line_length,
1079                         int cut_length, int max_lines, int mask_mode)
1080 {
1081   DrawTextBuffer(x, y, text, font_nr, line_length, cut_length, max_lines,
1082                  mask_mode, FALSE, FALSE, FALSE);
1083 }
1084 #endif