1 #include <allegro.h>
2 #include <math.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include "../include/credits.h"
6 #include "../include/global.h"
7 
8 /* for parsing readme.txt */
9 typedef struct TEXT_LIST {
10    char *text;
11    struct TEXT_LIST *next;
12 } TEXT_LIST;
13 
14 
15 typedef struct README_SECTION {
16    TEXT_LIST *head;
17    TEXT_LIST *tail;
18    char *flat;
19    char *desc;
20 } README_SECTION;
21 
22 
23 /* for parsing thanks._tx and the various source files */
24 typedef struct CREDIT_NAME {
25    char *name;
26    char *text;
27    struct CREDIT_NAME *next;
28 } CREDIT_NAME;
29 
30 
31 static CREDIT_NAME *credits = NULL;
32 
33 /* text messages (loaded from readme.txt) */
34 static char *title_text;
35 static int title_size;
36 static int title_alloced;
37 static char *end_text;
38 
39 /* for the text scroller */
40 static int text_char;
41 static int text_width;
42 static int text_pix;
43 static int text_scroll;
44 
45 /* for the credits display */
46 static CREDIT_NAME *cred;
47 static int credit_width;
48 static int credit_scroll;
49 static int credit_age;
50 static int credit_speed;
51 static int credit_skip;
52 
53 
54 
55 /* helper to find a file or directory in the Allegro tree */
find_relative_path(char buf[],size_t bufsize,const char * name)56 static int find_relative_path(char buf[], size_t bufsize, const char *name)
57 {
58    static const char *locations[] = {
59       "",
60       "../",
61       "../../",         /* Allegro root, no build directory */
62       "../../../",      /* Allegro root, with build directory */
63       "../../../../",   /* Allegro root, MSVC build configuration directory */
64       NULL
65    };
66    char exe[256];
67    char dir[256];
68    int i;
69 
70    get_executable_name(exe, sizeof(exe));
71    for (i = 0; locations[i] != NULL; i++) {
72       replace_filename(dir, exe, locations[i], sizeof(dir));
73       append_filename(buf, dir, name, bufsize);
74       if (file_exists(buf, FA_ALL, NULL)) {
75          return 1;
76       }
77    }
78 
79    return 0;
80 }
81 
82 
83 
84 /* helper to open readme.txt and thanks._tx */
open_relative_file(char buf[],size_t size,const char * name)85 static PACKFILE *open_relative_file(char buf[], size_t size, const char *name)
86 {
87    if (find_relative_path(buf, size, name))
88       return pack_fopen(buf, F_READ);
89 
90    return NULL;
91 }
92 
93 
94 
95 /* formats a list of TEXT_LIST structure into a single string */
format_text(TEXT_LIST * head,char * eol,char * gap)96 char *format_text(TEXT_LIST * head, char *eol, char *gap)
97 {
98    TEXT_LIST *l;
99    int size = 0;
100    char *s;
101 
102    l = head;
103    while (l) {
104       if (l->text[0])
105          size += strlen(l->text) + strlen(eol);
106       else
107          size += strlen(gap) + strlen(eol);
108       l = l->next;
109    }
110 
111    s = malloc(size + 1);
112    s[0] = 0;
113 
114    l = head;
115    while (l) {
116       if (l->text[0])
117          strcat(s, l->text);
118       else
119          strcat(s, gap);
120       strcat(s, eol);
121       l = l->next;
122    }
123 
124    return s;
125 }
126 
127 
128 
129 /* loads the scroller message from readme.txt */
load_text(void)130 void load_text(void)
131 {
132    README_SECTION sect[] = {
133       {NULL, NULL, NULL, "Introduction"},
134       {NULL, NULL, NULL, "Features"},
135       {NULL, NULL, NULL, "Copyright"},
136       {NULL, NULL, NULL, "Contact info"}
137    };
138 
139 #define SPLITTER  "                                "
140 
141    static char intro_msg[] =
142       "Welcome to the Allegro demonstration game, by Miran Amon, Nick Davies, Elias, Thomas Harte & Jakub Wasilewski."
143       SPLITTER
144       "Help skateboarding Ted collect various shop items - see \"About\" for more information!"
145       SPLITTER;
146 
147    static char splitter[] = SPLITTER;
148    static char marker[] = "--------";
149    char buf[256];
150    README_SECTION *sec = NULL;
151    TEXT_LIST *l, *p;
152    PACKFILE *f;
153    int inblank = TRUE;
154    char *s;
155    int i;
156 
157    f = open_relative_file(buf, sizeof(buf), "docs/txt/readme.txt");
158    if (!f) {
159       title_text =
160          "Can't find readme.txt, so this scroller is empty.                ";
161       title_size = strlen(title_text);
162       title_alloced = FALSE;
163       end_text = NULL;
164       return;
165    }
166 
167    while (pack_fgets(buf, sizeof(buf) - 1, f) != 0) {
168       if (buf[0] == '=') {
169          s = strchr(buf, ' ');
170          if (s) {
171             for (i = strlen(s) - 1; (uisspace(s[i])) || (s[i] == '='); i--)
172                s[i] = 0;
173 
174             s++;
175 
176             sec = NULL;
177             inblank = TRUE;
178 
179             for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
180                if (stricmp(s, sect[i].desc) == 0) {
181                   sec = &sect[i];
182                   break;
183                }
184             }
185          }
186       } else if (sec) {
187          s = buf;
188 
189          while ((*s) && (uisspace(*s)))
190             s++;
191 
192          for (i = strlen(s) - 1; (i >= 0) && (uisspace(s[i])); i--)
193             s[i] = 0;
194 
195          if ((s[0]) || (!inblank)) {
196             l = malloc(sizeof(TEXT_LIST));
197             l->next = NULL;
198             l->text = malloc(strlen(s) + 1);
199             strcpy(l->text, s);
200 
201             if (sec->tail)
202                sec->tail->next = l;
203             else
204                sec->head = l;
205 
206             sec->tail = l;
207          }
208 
209          inblank = (s[0] == 0);
210       }
211    }
212 
213    pack_fclose(f);
214 
215    if (sect[2].head)
216       end_text = format_text(sect[2].head, "\n", "");
217    else
218       end_text = NULL;
219 
220    title_size = strlen(intro_msg);
221 
222    for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
223       if (sect[i].head) {
224          sect[i].flat = format_text(sect[i].head, " ", splitter);
225          title_size +=
226             strlen(sect[i].flat) + strlen(sect[i].desc) +
227             strlen(splitter) + strlen(marker) * 2 + 2;
228       }
229    }
230 
231    title_text = malloc(title_size + 1);
232    title_alloced = TRUE;
233 
234    strcpy(title_text, intro_msg);
235 
236    for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
237       if (sect[i].flat) {
238          strcat(title_text, marker);
239          strcat(title_text, " ");
240          strcat(title_text, sect[i].desc);
241          strcat(title_text, " ");
242          strcat(title_text, marker);
243          strcat(title_text, splitter);
244          strcat(title_text, sect[i].flat);
245       }
246    }
247 
248    for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
249       l = sect[i].head;
250       while (l) {
251          free(l->text);
252          p = l;
253          l = l->next;
254          free(p);
255       }
256       if (sect[i].flat)
257          free(sect[i].flat);
258    }
259 }
260 
261 
262 /* sorts a list of credit strings */
sort_credit_list(void)263 void sort_credit_list(void)
264 {
265    CREDIT_NAME **prev, *p;
266    int n, done;
267 
268    do {
269       done = TRUE;
270 
271       prev = &credits;
272       p = credits;
273 
274       while ((p) && (p->next)) {
275          n = 0;
276 
277          if (n == 0) {
278             n = stricmp(p->name, p->next->name);
279          }
280 
281          if (n > 0) {
282             *prev = p->next;
283             p->next = p->next->next;
284             (*prev)->next = p;
285             p = *prev;
286 
287             done = FALSE;
288          }
289 
290          prev = &p->next;
291          p = p->next;
292       }
293    } while (!done);
294 }
295 
296 
297 /* reads credit info from various places */
load_credits(void)298 void load_credits(void)
299 {
300    char buf[256], *p, *p2;
301    CREDIT_NAME *c = NULL;
302    PACKFILE *f;
303 
304    /* parse thanks._tx */
305    f = open_relative_file(buf, sizeof(buf), "docs/src/thanks._tx");
306    if (!f)
307       return;
308 
309    while (pack_fgets(buf, sizeof(buf) - 1, f) != 0) {
310       if (stricmp(buf, "Thanks!") == 0)
311          break;
312 
313       while ((p = strstr(buf, "&lt")) != NULL) {
314          *p = '<';
315          memmove(p + 1, p + 3, strlen(p + 2));
316       }
317 
318       while ((p = strstr(buf, "&gt")) != NULL) {
319          *p = '>';
320          memmove(p + 1, p + 3, strlen(p + 2));
321       }
322 
323       p = buf;
324 
325       while ((*p) && (uisspace(*p)))
326          p++;
327 
328       p2 = p;
329 
330       while ((*p2) && ((!uisspace(*p2)) || (*(p2 + 1) != '(')))
331          p2++;
332 
333       if ((strncmp(p2, " (<email>", 9) == 0) ||
334           (strncmp(p2, " (email", 7) == 0)) {
335          *p2 = 0;
336 
337          c = malloc(sizeof(CREDIT_NAME));
338 
339          c->name = malloc(strlen(p) + 1);
340          strcpy(c->name, p);
341 
342          c->text = NULL;
343 
344          c->next = credits;
345          credits = c;
346       } else if (*p) {
347          if (c) {
348             p2 = p + strlen(p) - 1;
349             while ((p2 > p) && (uisspace(*p2)))
350                *(p2--) = 0;
351 
352             if (c->text) {
353                c->text = realloc(c->text, strlen(c->text) + strlen(p) + 2);
354                strcat(c->text, " ");
355                strcat(c->text, p);
356             } else {
357                c->text = malloc(strlen(p) + 1);
358                strcpy(c->text, p);
359             }
360          }
361       } else
362          c = NULL;
363    }
364 
365    pack_fclose(f);
366 
367    /* sort the lists */
368    sort_credit_list();
369 }
370 
371 
next_credit(CREDIT_NAME * cred)372 CREDIT_NAME *next_credit(CREDIT_NAME * cred)
373 {
374    int max, i;
375 
376    if (!cred) {
377       cred = credits;
378       if (!cred)
379          return NULL;
380    }
381 
382    max = rand() % 1000;
383    for (i = 0; i < max; i++) {
384       if (cred->next) {
385          cred = cred->next;
386       } else {
387          cred = credits;
388       }
389    }
390 
391    return cred;
392 }
393 
394 
init_credits(void)395 void init_credits(void)
396 {
397    /* for the text scroller */
398    text_char = 0xFFFF;
399    text_width = 0;
400    text_pix = 0;
401    text_scroll = 0;
402 
403    /* for the credits display */
404    cred = NULL;
405    credit_width = 0;
406    credit_scroll = 0;
407    credit_age = 0;
408    credit_speed = 32;
409    credit_skip = 1;
410 
411    load_text();
412    load_credits();
413 }
414 
415 
update_credits(void)416 void update_credits(void)
417 {
418    /* move the scroller */
419    text_scroll++;
420 
421    /* update the credits position */
422    if (credit_scroll <= 0) {
423       cred = next_credit(cred);
424 
425       if (cred) {
426          credit_width = text_length(demo_font, cred->name) + 24;
427 
428          if (cred->text) {
429             credit_scroll =
430                text_length(plain_font, cred->text) + SCREEN_W - credit_width;
431          } else {
432             credit_scroll = 256;
433          }
434 
435          credit_age = 0;
436       }
437    } else {
438       credit_scroll--;
439       credit_age++;
440    }
441 }
442 
443 
draw_credits(BITMAP * canvas)444 void draw_credits(BITMAP *canvas)
445 {
446    int c, c2;
447    int y2;
448    int col_back, col_font;
449 
450    /* for the text scroller */
451    char buf[2] = " ";
452 
453    /* for the credits display */
454    char cbuf[2] = " ";
455    char *p;
456 
457    col_back = makecol(222, 222, 222);
458    col_font = makecol(96, 96, 96);
459 
460    /* draw the text scroller */
461    y2 = SCREEN_H - text_height(demo_font);
462    text_pix = -(text_scroll / 1);
463    hline(canvas, 0, y2 - 2, SCREEN_W - 1, col_font);
464    hline(canvas, 0, y2 - 1, SCREEN_W - 1, col_font);
465    rectfill(canvas, 0, y2, SCREEN_W - 1, SCREEN_H - 1, col_back);
466    for (text_char = 0; text_char < title_size; text_char++) {
467       buf[0] = title_text[text_char];
468       c = text_length(demo_font, buf);
469 
470       if (text_pix + c > 0) {
471          demo_textout(canvas, demo_font, buf, text_pix, y2, col_font, -1);
472       }
473 
474       text_pix += c;
475 
476       if (text_pix >= SCREEN_W) {
477          break;
478       }
479    }
480 
481    /* draw author name/desc credits */
482    y2 = text_height(demo_font);
483    rectfill(canvas, 0, 0, SCREEN_W - 1, y2, col_back);
484    hline(canvas, 0, y2 + 1, SCREEN_W - 1, col_font);
485    hline(canvas, 0, y2 + 2, SCREEN_W - 1, col_font);
486    y2 = (text_height(demo_font) - 8) / 2;
487 
488    if (cred && cred->text) {
489       c = credit_scroll;
490       p = cred->text;
491       c2 = strlen(p);
492 
493       if (c > 0) {
494          if (c2 > c / 8) {
495             p += c2 - c / 8;
496             c &= 7;
497          } else {
498             c -= c2 * 8;
499          }
500 
501          c += credit_width;
502 
503          while ((*p) && (c < SCREEN_W - 32)) {
504             if (c < credit_width + 96) {
505                c2 = 128 + (c - credit_width - 32) * 127 / 64;
506             } else if (c > SCREEN_W - 96) {
507                c2 = 128 + (SCREEN_W - 32 - c) * 127 / 64;
508             } else {
509                c2 = 255;
510             }
511 
512             if ((c2 > 128) && (c2 <= 255)) {
513                c2 = 255 - c2 + 64;
514                cbuf[0] = *p;
515                demo_textout(canvas, plain_font, cbuf, c, y2, makecol(c2, c2, c2), -1);
516             }
517 
518             p++;
519             c += 8;
520          }
521       }
522    }
523 
524    c = 4;
525 
526    if (credit_age < 100) {
527       c -= (100 - credit_age) * (100 - credit_age) * credit_width / 10000;
528    }
529 
530    if (credit_scroll < 150) {
531       c += (150 - credit_scroll) * (150 - credit_scroll) * SCREEN_W / 22500;
532    }
533 
534    if (cred)
535       demo_textprintf(canvas, demo_font, c, 0, col_font, -1, "%s:", cred->name);
536    else
537       demo_textprintf(canvas, demo_font, 0, 0, col_font, -1,
538                     "thanks._tx not found!");
539 }
540 
541 
destroy_credits(void)542 void destroy_credits(void)
543 {
544    if ((title_text) && (title_alloced)) {
545       free(title_text);
546    }
547 
548    while (credits) {
549       cred = credits;
550       credits = cred->next;
551 
552       if (cred->name) {
553          free(cred->name);
554       }
555 
556       if (cred->text) {
557          free(cred->text);
558       }
559 
560       free(cred);
561    }
562 }
563