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