1 #include "title.h"
2 #include "data.h"
3 #include "star.h"
4 #include "display.h"
5 #include "dirty.h"
6 
7 /* for parsing readme.txt */
8 typedef struct TEXT_LIST {
9    char *text;
10    struct TEXT_LIST *next;
11 } TEXT_LIST;
12 
13 typedef struct README_SECTION {
14    TEXT_LIST *head;
15    TEXT_LIST *tail;
16    char *flat;
17    char *desc;
18 } README_SECTION;
19 
20 /* for parsing thanks._tx and the various source files */
21 typedef struct CREDIT_NAME {
22    char *name;
23    char *text;
24    TEXT_LIST *files;
25    struct CREDIT_NAME *next;
26 } CREDIT_NAME;
27 
28 /* text messages (loaded from readme.txt) */
29 static char *title_text;
30 static int title_size;
31 static int title_alloced;
32 
33 static char *end_text;
34 
35 static PALETTE title_palette;
36 
37 /* author credits scroller */
38 static int credit_width = 0;
39 static int credit_scroll = 0;
40 static int credit_offset = 0;
41 static int credit_age = 0;
42 static int credit_speed = 32;
43 static int credit_skip = 1;
44 
45 /* text scroller at the bottom */
46 static int text_scroll = 0;
47 static BITMAP *text_bmp;
48 static int text_char;
49 static int text_pix;
50 static int text_width;
51 
52 CREDIT_NAME *credit_name = NULL;
53 
54 static CREDIT_NAME *credits = NULL;
55 
56 /* timer callback for controlling the speed of the scrolling text */
57 static volatile int scroll_count;
58 
59 
60 
scroll_counter(void)61 static void scroll_counter(void)
62 {
63    scroll_count++;
64 }
65 END_OF_STATIC_FUNCTION(scroll_counter);
66 
67 
68 
69 /* helper to find a file or directory in the Allegro tree */
find_relative_path(char buf[],size_t bufsize,const char * name)70 static int find_relative_path(char buf[], size_t bufsize, const char *name)
71 {
72    static const char *locations[] = {
73       "",
74       "../",
75       "../../",         /* Allegro root, no build directory */
76       "../../../",      /* Allegro root, with build directory */
77       "../../../../",   /* Allegro root, MSVC build configuration directory */
78       NULL
79    };
80    char exe[256];
81    char dir[256];
82    int i;
83 
84    get_executable_name(exe, sizeof(exe));
85    for (i = 0; locations[i] != NULL; i++) {
86       replace_filename(dir, exe, locations[i], sizeof(dir));
87       append_filename(buf, dir, name, bufsize);
88       if (file_exists(buf, FA_ALL, NULL)) {
89          return 1;
90       }
91    }
92 
93    return 0;
94 }
95 
96 
97 
98 /* helper to open readme.txt and thanks._tx */
open_relative_file(char buf[],size_t size,const char * name)99 static PACKFILE *open_relative_file(char buf[], size_t size, const char *name)
100 {
101    if (find_relative_path(buf, size, name))
102       return pack_fopen(buf, F_READ);
103 
104    return NULL;
105 }
106 
107 
108 
109 /* formats a list of TEXT_LIST structure into a single string */
format_text(TEXT_LIST * head,char * eol,char * gap)110 static char *format_text(TEXT_LIST * head, char *eol, char *gap)
111 {
112    TEXT_LIST *l;
113    int size = 0;
114    char *s;
115 
116    l = head;
117    while (l) {
118       if (l->text[0])
119          size += strlen(l->text) + strlen(eol);
120       else
121          size += strlen(gap) + strlen(eol);
122       l = l->next;
123    }
124 
125    s = malloc(size + 1);
126    s[0] = 0;
127 
128    l = head;
129    while (l) {
130       if (l->text[0])
131          strcat(s, l->text);
132       else
133          strcat(s, gap);
134       strcat(s, eol);
135       l = l->next;
136    }
137 
138    return s;
139 }
140 
141 
142 
143 /* loads the scroller message from readme.txt */
load_text(void)144 static void load_text(void)
145 {
146    README_SECTION sect[] = {
147       {NULL, NULL, NULL, "Introduction"},
148       {NULL, NULL, NULL, "Features"},
149       {NULL, NULL, NULL, "Copyright"},
150       {NULL, NULL, NULL, "Contact info"}
151    };
152 
153 #define SPLITTER  "                                "
154 
155    static char intro_msg[] =
156        "Welcome to the Allegro demonstration game, by Shawn Hargreaves."
157        SPLITTER
158        "Your mission: to go where no man has gone before, to seek out strange new life, and to boldly blast it to smithereens."
159        SPLITTER
160        "Your controls: the arrow keys to move left and right, the up arrow to accelerate (the faster you go, the more score you get), and the space bar to fire."
161        SPLITTER
162        "What complexity!"
163        SPLITTER
164        "What subtlety."
165        SPLITTER
166        "What originality."
167        SPLITTER "But enough of that. On to the serious stuff..." SPLITTER;
168 
169    static char splitter[] = SPLITTER;
170    static char marker[] = "--------";
171    char buf[256];
172    README_SECTION *sec = NULL;
173    TEXT_LIST *l, *p;
174    PACKFILE *f;
175    int inblank = TRUE;
176    char *s;
177    int i;
178 
179    f = open_relative_file(buf, sizeof(buf), "docs/txt/readme.txt");
180    if (!f) {
181       title_text =
182           "Can't find readme.txt, so this scroller is empty.                ";
183       title_size = strlen(title_text);
184       title_alloced = FALSE;
185       end_text = NULL;
186       return;
187    }
188 
189    while (pack_fgets(buf, sizeof(buf) - 1, f) != 0) {
190       if (buf[0] == '=') {
191          s = strchr(buf, ' ');
192          if (s) {
193             for (i = strlen(s) - 1; (uisspace(s[i])) || (s[i] == '='); i--)
194                s[i] = 0;
195 
196             s++;
197 
198             sec = NULL;
199             inblank = TRUE;
200 
201             for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
202                if (stricmp(s, sect[i].desc) == 0) {
203                   sec = &sect[i];
204                   break;
205                }
206             }
207          }
208       }
209       else if (sec) {
210          s = buf;
211 
212          while ((*s) && (uisspace(*s)))
213             s++;
214 
215          for (i = strlen(s) - 1; (i >= 0) && (uisspace(s[i])); i--)
216             s[i] = 0;
217 
218          if ((s[0]) || (!inblank)) {
219             l = malloc(sizeof(TEXT_LIST));
220             l->next = NULL;
221             l->text = malloc(strlen(s) + 1);
222             strcpy(l->text, s);
223 
224             if (sec->tail)
225                sec->tail->next = l;
226             else
227                sec->head = l;
228 
229             sec->tail = l;
230          }
231 
232          inblank = (s[0] == 0);
233       }
234    }
235 
236    pack_fclose(f);
237 
238    if (sect[2].head)
239       end_text = format_text(sect[2].head, "\n", "");
240    else
241       end_text = NULL;
242 
243    title_size = strlen(intro_msg);
244 
245    for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
246       if (sect[i].head) {
247          sect[i].flat = format_text(sect[i].head, " ", splitter);
248          title_size += strlen(sect[i].flat) + strlen(sect[i].desc) +
249              strlen(splitter) + strlen(marker) * 2 + 2;
250       }
251    }
252 
253    title_text = malloc(title_size + 1);
254    title_alloced = TRUE;
255 
256    strcpy(title_text, intro_msg);
257 
258    for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
259       if (sect[i].flat) {
260          strcat(title_text, marker);
261          strcat(title_text, " ");
262          strcat(title_text, sect[i].desc);
263          strcat(title_text, " ");
264          strcat(title_text, marker);
265          strcat(title_text, splitter);
266          strcat(title_text, sect[i].flat);
267       }
268    }
269 
270    for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
271       l = sect[i].head;
272       while (l) {
273          free(l->text);
274          p = l;
275          l = l->next;
276          free(p);
277       }
278       if (sect[i].flat)
279          free(sect[i].flat);
280    }
281 }
282 
283 
284 
285 /* reads credit info from a source file */
parse_source(AL_CONST char * filename,int attrib,void * param)286 static int parse_source(AL_CONST char *filename, int attrib, void *param)
287 {
288    char buf[256];
289    PACKFILE *f;
290    CREDIT_NAME *c;
291    TEXT_LIST *d;
292    char *p;
293 
294    if (attrib & FA_DIREC) {
295       p = get_filename(filename);
296 
297       if ((stricmp(p, ".") != 0) && (stricmp(p, "..") != 0) &&
298           (stricmp(p, "lib") != 0) && (stricmp(p, "obj") != 0)) {
299 
300          /* recurse inside a directory */
301          strcpy(buf, filename);
302          put_backslash(buf);
303          strcat(buf, "*.*");
304 
305          for_each_file_ex(buf, 0, ~(FA_ARCH | FA_RDONLY | FA_DIREC),
306                           parse_source, param);
307       }
308    }
309    else {
310       p = get_extension(filename);
311 
312       if ((stricmp(p, "c") == 0) || (stricmp(p, "cpp") == 0) ||
313           (stricmp(p, "h") == 0) || (stricmp(p, "inc") == 0) ||
314           (stricmp(p, "s") == 0) || (stricmp(p, "asm") == 0)) {
315 
316          /* parse a source file */
317          f = pack_fopen(filename, F_READ);
318          if (!f)
319             return -1;
320 
321          textprintf_centre_ex(screen, font, SCREEN_W / 2, SCREEN_H / 2 + 8,
322                               makecol(160, 160, 160), 0,
323                               "                %s                ",
324                               filename + (int)(unsigned long)param);
325 
326          while (pack_fgets(buf, sizeof(buf) - 1, f) != 0) {
327             if (strstr(buf, "*/"))
328                break;
329 
330             c = credits;
331 
332             while (c) {
333                if (strstr(buf, c->name)) {
334                   for (d = c->files; d; d = d->next) {
335                      if (strcmp
336                          (d->text,
337                           filename + (int)(unsigned long)param) == 0)
338                         break;
339                   }
340 
341                   if (!d) {
342                      d = malloc(sizeof(TEXT_LIST));
343                      d->text =
344                          malloc(strlen
345                                 (filename + (int)(unsigned long)param) +
346                                 1);
347                      strcpy(d->text, filename + (int)(unsigned long)param);
348                      d->next = c->files;
349                      c->files = d;
350                   }
351                }
352 
353                c = c->next;
354             }
355          }
356 
357          pack_fclose(f);
358       }
359    }
360 
361    return 0;
362 }
363 
364 
365 
366 /* sorts a list of text strings */
sort_text_list(TEXT_LIST ** head)367 static void sort_text_list(TEXT_LIST ** head)
368 {
369    TEXT_LIST **prev, *p;
370    int done;
371 
372    do {
373       done = TRUE;
374 
375       prev = head;
376       p = *head;
377 
378       while ((p) && (p->next)) {
379          if (stricmp(p->text, p->next->text) > 0) {
380             *prev = p->next;
381             p->next = p->next->next;
382             (*prev)->next = p;
383             p = *prev;
384 
385             done = FALSE;
386          }
387 
388          prev = &p->next;
389          p = p->next;
390       }
391 
392    } while (!done);
393 }
394 
395 
396 
397 /* sorts a list of credit strings */
sort_credit_list(void)398 static void sort_credit_list(void)
399 {
400    CREDIT_NAME **prev, *p;
401    TEXT_LIST *t;
402    int n, done;
403 
404    do {
405       done = TRUE;
406 
407       prev = &credits;
408       p = credits;
409 
410       while ((p) && (p->next)) {
411          n = 0;
412 
413          for (t = p->files; t; t = t->next)
414             n--;
415 
416          for (t = p->next->files; t; t = t->next)
417             n++;
418 
419          if (n == 0)
420             n = stricmp(p->name, p->next->name);
421 
422          if (n > 0) {
423             *prev = p->next;
424             p->next = p->next->next;
425             (*prev)->next = p;
426             p = *prev;
427 
428             done = FALSE;
429          }
430 
431          prev = &p->next;
432          p = p->next;
433       }
434 
435    } while (!done);
436 }
437 
438 
439 
440 /* reads credit info from various places */
load_credits(void)441 static void load_credits(void)
442 {
443    static int once = FALSE;
444    char buf[256], buf2[256], *p, *p2;
445    CREDIT_NAME *c = NULL;
446    PACKFILE *f;
447 
448    if (once)
449       return;
450    once = TRUE;
451 
452    textout_centre_ex(screen, font, "Scanning for author credits...",
453                      SCREEN_W / 2, SCREEN_H / 2 - 16, makecol(160, 160,
454                                                               160), 0);
455 
456    load_text();
457 
458    /* Don't load top scroller with small screens. */
459    if (SCREEN_W < 640)
460       return;
461 
462    /* parse thanks._tx */
463    f = open_relative_file(buf, sizeof(buf), "docs/src/thanks._tx");
464    if (!f)
465       return;
466 
467    while (pack_fgets(buf, sizeof(buf) - 1, f) != 0) {
468       if (stricmp(buf, "Thanks!") == 0)
469          break;
470 
471       while ((p = strstr(buf, "&lt")) != NULL) {
472          *p = '<';
473          memmove(p + 1, p + 3, strlen(p + 2));
474       }
475 
476       while ((p = strstr(buf, "&gt")) != NULL) {
477          *p = '>';
478          memmove(p + 1, p + 3, strlen(p + 2));
479       }
480 
481       p = buf;
482 
483       while ((*p) && (uisspace(*p)))
484          p++;
485 
486       p2 = p;
487 
488       while ((*p2) && ((!uisspace(*p2)) || (*(p2 + 1) != '(')))
489          p2++;
490 
491       if ((strncmp(p2, " (<email>", 9) == 0) ||
492           (strncmp(p2, " (email", 7) == 0)) {
493          *p2 = 0;
494 
495          c = malloc(sizeof(CREDIT_NAME));
496 
497          c->name = malloc(strlen(p) + 1);
498          strcpy(c->name, p);
499 
500          c->text = NULL;
501          c->files = NULL;
502 
503          c->next = credits;
504          credits = c;
505       }
506       else if (*p) {
507          if (c) {
508             p2 = p + strlen(p) - 1;
509             while ((p2 > p) && (uisspace(*p2)))
510                *(p2--) = 0;
511 
512             if (c->text) {
513                c->text = realloc(c->text, strlen(c->text) + strlen(p) + 2);
514                strcat(c->text, " ");
515                strcat(c->text, p);
516             }
517             else {
518                c->text = malloc(strlen(p) + 1);
519                strcpy(c->text, p);
520             }
521          }
522       }
523       else
524          c = NULL;
525    }
526 
527    pack_fclose(f);
528 
529    /* parse source files from root of Allegro directory down */
530    if (find_relative_path(buf2, sizeof(buf2), "src")) {
531       replace_filename(buf, buf2, "*.*", sizeof(buf));
532       for_each_file_ex(buf, 0, ~(FA_ARCH | FA_RDONLY | FA_DIREC),
533                        parse_source,
534                        (void *)(unsigned long)(strlen(buf2) - 3));
535    }
536 
537    /* sort the lists */
538    sort_credit_list();
539 
540    for (c = credits; c; c = c->next) {
541       sort_text_list(&c->files);
542    }
543 }
544 
545 
546 
scroller(void)547 static void scroller(void)
548 {
549    int c, n;
550    TEXT_LIST *tl;
551 
552    starfield_3d();
553 
554    /* move the scroller at the bottom */
555    text_scroll++;
556 
557    /* update the credits position */
558    if (credit_scroll <= 0) {
559       if (credit_name)
560          credit_name = credit_name->next;
561 
562       if (!credit_name)
563          credit_name = credits;
564 
565       if (credit_name) {
566          credit_width =
567              text_length(data[TITLE_FONT].dat, credit_name->name) + 24;
568 
569          if (credit_name->text)
570             credit_scroll = strlen(credit_name->text) * 8 + SCREEN_W -
571                credit_width + 64;
572          else
573             credit_scroll = 256;
574 
575          tl = credit_name->files;
576          n = 0;
577 
578          while (tl) {
579             n++;
580             tl = tl->next;
581          }
582 
583          credit_offset = 0;
584 
585          if (n) {
586             credit_skip = 1 + n / 50;
587             credit_speed =
588                 8 + fixtoi(fixdiv(itofix(512), itofix(n)));
589             if (credit_speed > 200)
590                credit_speed = 200;
591             c = 1024 + (n - 1) * credit_speed;
592             if (credit_scroll < c) {
593                credit_offset = credit_scroll - c;
594                credit_scroll = c;
595             }
596          }
597 
598          credit_age = 0;
599       }
600    }
601    else {
602       credit_scroll--;
603       credit_age++;
604    }
605 }
606 
607 
608 
draw_scroller(BITMAP * bmp)609 static void draw_scroller(BITMAP *bmp)
610 {
611    /* for the text scroller */
612    char buf[2] = " ";
613    TEXT_LIST *tl;
614    int n, n2, c, c2, c3;
615    char *p;
616    char cbuf[2] = " ";
617    FONT *bigfont = data[TITLE_FONT].dat;
618    int th = text_height(bigfont);
619 
620    /* draw the text scroller at the bottom */
621    blit(text_bmp, text_bmp, text_scroll, 0, 0, 0, SCREEN_W, th);
622    rectfill(text_bmp, SCREEN_W - text_scroll, 0, SCREEN_W, th, 0);
623 
624    while (text_scroll > 0) {
625       text_pix += text_scroll;
626       if (text_char >= 0)
627          buf[0] = title_text[text_char];
628       textout_ex(text_bmp, bigfont, buf,
629                  SCREEN_W - text_pix, 0, -1, 0);
630       if (text_pix >= text_width) {
631          text_scroll = text_pix - text_width;
632          text_char++;
633          if (text_char >= title_size)
634             text_char = 0;
635          buf[0] = title_text[text_char];
636          text_pix = 0;
637          text_width = text_length(data[TITLE_FONT].dat, buf);
638       }
639       else
640          text_scroll = 0;
641    }
642 
643    blit(text_bmp, bmp, 0, 0, 0, SCREEN_H - th, SCREEN_W, th);
644    if (animation_type == DIRTY_RECTANGLE)
645        dirty_rectangle(0, SCREEN_H - th, SCREEN_W, th);
646 
647    /* draw author file credits */
648    if (credit_name) {
649       int x, y, z;
650       int ix, iy;
651       tl = credit_name->files;
652       n = credit_width;
653       n2 = 0;
654 
655       while (tl) {
656          c = 1024 + n2 * credit_speed - credit_age;
657 
658          if ((c > 0) && (c < 1024) && ((n2 % credit_skip) == 0)) {
659             x = itofix(SCREEN_W / 2);
660             y = itofix(SCREEN_H / 2 - 32);
661 
662             c2 = c * ((n / 13) % 17 + 1) / 32;
663             if (n & 1)
664                c2 = -c2;
665 
666             c2 -= 96;
667 
668             c3 = (32 +
669                   fixtoi(ABS(fixsin(itofix(c / (15 + n % 42) + n))) *
670                          128)) * SCREEN_W / 640;
671 
672             x += fixsin(itofix(c2)) * c3;
673             y += fixcos(itofix(c2)) * c3;
674 
675             if (c < 512) {
676                z = fixsqrt(itofix(c) / 512);
677 
678                x = fixmul(itofix(32), itofix(1) - z) + fixmul(x, z);
679                y = fixmul(itofix(16), itofix(1) - z) + fixmul(y, z);
680             }
681             else if (c > 768) {
682                z = fixsqrt(itofix(1024 - c) / 256);
683 
684                if (n & 2)
685                   x = fixmul(itofix(128), itofix(1) - z) + fixmul(x, z);
686                else
687                   x = fixmul(itofix(SCREEN_W - 128),
688                              itofix(1) - z) + fixmul(x, z);
689 
690                y = fixmul(itofix(SCREEN_H - 128),
691                           itofix(1) - z) + fixmul(y, z);
692             }
693 
694             c = 128 + (512 - ABS(512 - c)) / 24;
695             c = MIN(255, c * 1.25);
696 
697             ix = fixtoi(x);
698             iy = fixtoi(y);
699 
700             c2 = strlen(tl->text);
701             ix -= c2 * 4;
702 
703             textout_ex(bmp, font, tl->text, ix, iy, c, 0);
704             if (animation_type == DIRTY_RECTANGLE)
705                dirty_rectangle(ix, iy, c2 * 8, 8);
706          }
707 
708          tl = tl->next;
709          n += 1234567;
710          n2++;
711       }
712    }
713 
714    draw_starfield_3d(bmp);
715 
716    /* draw author name/desc credits */
717    if (credit_name) {
718       if (credit_name->text) {
719          c = credit_scroll + credit_offset;
720          p = credit_name->text;
721          c2 = strlen(p);
722 
723          if (c > 0) {
724             if (c2 > c / 8) {
725                p += c2 - c / 8;
726                c &= 7;
727             }
728             else {
729                c -= c2 * 8;
730             }
731 
732             c += credit_width;
733 
734             while ((*p) && (c < SCREEN_W - 32)) {
735                if (c < credit_width + 96)
736                   c2 = 128 + (c - credit_width - 32) * 127 / 64;
737                else if (c > SCREEN_W - 96)
738                   c2 = 128 + (SCREEN_W - 32 - c) * 127 / 64;
739                else
740                   c2 = 255;
741 
742                if ((c2 > 128) && (c2 <= 255)) {
743                   cbuf[0] = *p;
744                   textout_ex(bmp, font, cbuf, c, 16, c2, 0);
745                }
746 
747                p++;
748                c += 8;
749             }
750          }
751       }
752 
753       c = 4;
754 
755       if (credit_age < 100)
756          c -= (100 - credit_age) * (100 -
757                                     credit_age) * credit_width / 10000;
758 
759       if (credit_scroll < 150)
760          c += (150 - credit_scroll) * (150 -
761                                        credit_scroll) * SCREEN_W /
762              22500;
763 
764       textprintf_ex(bmp, data[TITLE_FONT].dat, c, 4, -1, 0, "%s:",
765                     credit_name->name);
766       if (animation_type == DIRTY_RECTANGLE)
767          dirty_rectangle(0, 4, SCREEN_W, text_height(data[TITLE_FONT].dat));
768    }
769 
770    /* draw the Allegro logo over the top */
771    draw_sprite(bmp, data[TITLE_BMP].dat, SCREEN_W / 2 - 160,
772                SCREEN_H / 2 - 96);
773 }
774 
775 
776 
777 /* displays the title screen */
title_screen(void)778 int title_screen(void)
779 {
780    static int color = 0;
781    int c;
782    BITMAP *bmp;
783    RGB rgb;
784    int updated;
785    int scroll_pos = 0;
786 
787    text_scroll = 0;
788    credit_width = 0;
789    credit_scroll = 0;
790    credit_offset = 0;
791    credit_age = 0;
792    credit_speed = 32;
793    credit_skip = 1;
794 
795    text_char = -1;
796    text_pix = 0;
797    text_width = 0;
798    text_bmp = create_bitmap(SCREEN_W, 24);
799    clear_bitmap(text_bmp);
800 
801    play_midi(data[TITLE_MUSIC].dat, TRUE);
802    play_sample(data[WELCOME_SPL].dat, 255, 127, 1000, FALSE);
803 
804    load_credits();
805 
806    init_starfield_3d();
807 
808    for (c = 0; c < 8; c++)
809       title_palette[c] = ((RGB *) data[TITLE_PAL].dat)[c];
810 
811    /* set up the colors differently each time we display the title screen */
812    for (c = 8; c < PAL_SIZE / 2; c++) {
813       rgb = ((RGB *) data[TITLE_PAL].dat)[c];
814       switch (color) {
815          case 0:
816             rgb.b = rgb.r;
817             rgb.r = 0;
818             break;
819          case 1:
820             rgb.g = rgb.r;
821             rgb.r = 0;
822             break;
823          case 3:
824             rgb.g = rgb.r;
825             break;
826       }
827       title_palette[c] = rgb;
828    }
829 
830    for (c = PAL_SIZE / 2; c < PAL_SIZE; c++)
831       title_palette[c] = ((RGB *) data[TITLE_PAL].dat)[c];
832 
833    color++;
834    if (color > 3)
835       color = 0;
836 
837    clear_display();
838 
839    set_palette(title_palette);
840 
841    LOCK_VARIABLE(scroll_count);
842    LOCK_FUNCTION(scroll_counter);
843    scroll_count = 1;
844    install_int(scroll_counter, 5);
845 
846    while ((c = scroll_count) < 160)
847       stretch_blit(data[TITLE_BMP].dat, screen, 0, 0, 320, 128,
848                    SCREEN_W / 2 - c, SCREEN_H / 2 - c * 64 / 160 - 32,
849                    c * 2, c * 128 / 160);
850 
851    remove_int(scroll_counter);
852 
853    blit(data[TITLE_BMP].dat, screen, 0, 0, SCREEN_W / 2 - 160,
854         SCREEN_H / 2 - 96, 320, 128);
855 
856    clear_keybuf();
857 
858    scroll_count = 0;
859 
860    install_int(scroll_counter, 6);
861 
862    do {
863       updated = 0;
864       while (scroll_pos <= scroll_count) {
865          scroller();
866          scroll_pos++;
867          updated = 1;
868       }
869 
870       if (max_fps || updated) {
871          bmp = prepare_display();
872          draw_scroller(bmp);
873          flip_display();
874       }
875 
876       /* rest for a short while if we're not in CPU-hog mode and too fast */
877       if (!max_fps && !updated) {
878          rest(1);
879       }
880 
881       poll_joystick();
882 
883    } while ((!keypressed()) && (!joy[0].button[0].b)
884             && (!joy[0].button[1].b));
885 
886    remove_int(scroll_counter);
887 
888    fade_out(5);
889 
890    destroy_bitmap(text_bmp);
891 
892    while (keypressed())
893       if ((readkey() & 0xff) == 27)
894          return FALSE;
895 
896    return TRUE;
897 }
898 
899 
900 
end_title(void)901 void end_title(void)
902 {
903    CREDIT_NAME *cred;
904    TEXT_LIST *tl;
905 
906    if (end_text) {
907       allegro_message("%shttp://alleg.sourceforge.net/\n\n", end_text);
908       free(end_text);
909    }
910 
911    if ((title_text) && (title_alloced))
912       free(title_text);
913 
914    while (credits) {
915       cred = credits;
916       credits = cred->next;
917 
918       if (cred->name)
919          free(cred->name);
920 
921       if (cred->text)
922          free(cred->text);
923 
924       while (cred->files) {
925          tl = cred->files;
926          cred->files = tl->next;
927 
928          if (tl->text)
929             free(tl->text);
930 
931          free(tl);
932       }
933 
934       free(cred);
935    }
936 }
937 
938