1 /*
2 scripting.c:
3
4 XML-based scripting for lessons and practice.
5 Copyright 2003, 2004, 2007, 2008, 2009, 2010.
6 Authors: Jesse Andrews, David Bruce, Matthew Trey.
7
8 Project email: <tux4kids-tuxtype-dev@lists.alioth.debian.org>
9 Project website: http://tux4kids.alioth.debian.org
10
11 scripting.c is part of Tux Typing, a.k.a "tuxtype".
12
13 Tux Typing is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 3 of the License, or
16 (at your option) any later version.
17
18 Tux Typing is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27
28
29
30 #include "scripting.h"
31 #define MAX_LESSONS 100
32 #include "SDL_extras.h"
33 #include "convert_utf.h"
34 #include "scandir.h"
35
36 /* Local function prototypes: */
37 static void clear_items(itemType* i);
38 static void clear_pages(pageType* p);
39 static void close_script(void);
40 static SDL_Color* get_color(const char* in);
41 static int get_int(const char* in);
42 static char* get_quote(const char* in);
43 static char hex2int(char b, char s);
44 static int load_script(const char* fn);
45 static void run_script(void);
46 static int is_xml_file(const struct dirent* xml_dirent);
47 /************************************************************************/
48 /* */
49 /* "Public" functions (callable throughout program) */
50 /* */
51 /************************************************************************/
52
53
InstructCascade(void)54 void InstructCascade(void)
55 {
56 char fn[FNLEN];
57
58 /* Try theme script first: */
59 if (!settings.use_english)
60 sprintf( fn, "%s/scripts/cascade.xml", settings.theme_data_path);
61
62 if (load_script( fn ) == 0) /* meaning successful load */
63 {
64 run_script();
65 return;
66 }
67
68 /* If unsuccessful, fall back to default (English) script: */
69 sprintf( fn, "%s/scripts/cascade.xml", settings.default_data_path);
70 if (load_script( fn ) != 0)
71 return; // bail if any errors occur
72
73 run_script();
74 }
75
76
InstructLaser(void)77 void InstructLaser(void)
78 {
79 char fn[FNLEN];
80
81 /* Try theme script first: */
82 if (!settings.use_english)
83 sprintf( fn, "%s/scripts/laser.xml", settings.theme_data_path);
84
85 if (load_script( fn ) == 0) /* meaning successful load */
86 {
87 run_script();
88 return;
89 }
90
91
92 /* If unsuccessful, fall back to default (English) script: */
93 sprintf( fn, "%s/scripts/laser.xml", settings.default_data_path);
94 if (load_script( fn ) != 0)
95 return; // bail if any errors occur
96
97 run_script();
98 }
99
ProjectInfo(void)100 void ProjectInfo(void)
101 {
102 char fn[FNLEN];
103
104 /* Try theme script first: */
105 if (!settings.use_english)
106 sprintf( fn, "%s/scripts/projectInfo.xml", settings.theme_data_path);
107
108 if (load_script( fn ) == 0) /* meaning successful load */
109 {
110 run_script();
111 return;
112 }
113
114 /* If unsuccessful, fall back to default (English) script: */
115 sprintf( fn, "%s/scripts/projectInfo.xml", settings.default_data_path);
116 if (load_script( fn ) != 0)
117 return; // bail if any errors occur
118
119 run_script();
120 }
121
122 /* This is the function that allows the user */
123 /* to select a lesson from the menu. */
124 /* FIXME we ought to display descriptive titles from the lessons, */
125 /* rather than just the filenames. We also should consider a */
126 /* "gold stars" system like in TuxMath - DSB */
XMLLesson(void)127 int XMLLesson(void)
128 {
129 SDL_Surface* titles[MAX_LESSONS] = {NULL};
130 SDL_Surface* select[MAX_LESSONS] = {NULL};
131 SDL_Surface* left = NULL, *right = NULL;
132 SDL_Rect leftRect, rightRect;
133 SDL_Rect titleRects[8];
134
135 int nchars;
136 struct dirent **script_list_dirents = NULL;
137 int i = 0;
138 int scriptIterator = 0; //Iterator over matching files in script dir
139 int scripts = 0; //Iterator over accepted (& parsed) script files
140 int num_scripts = 0;
141 char script_path[FNLEN];
142 char script_filenames[MAX_LESSONS][FNLEN];
143 char fn[FNLEN];
144
145 int stop = 0;
146 int loc = 0;
147 int old_loc = 1;
148 int found = 0;
149
150
151 LOG("Entering XMLLesson():\n");
152
153 /* First look in theme path, if desired: */
154 if (!settings.use_english)
155 {
156 sprintf(script_path, "%s/scripts", settings.theme_data_path);
157 if (CheckFile(script_path))
158 {
159 DEBUGCODE {fprintf(stderr, "Using theme script dir: %s\n", script_path);}
160 found = 1;
161 }
162 }
163
164 /* Now look in default path if desired or needed: */
165 if (!found)
166 {
167 sprintf( script_path, "%s/scripts", settings.default_data_path);
168 if (CheckFile(script_path))
169 {
170 DEBUGCODE { fprintf(stderr, "Using theme script dir: %s\n", script_path); }
171 found = 1;
172 }
173 }
174
175 if (!found)
176 {
177 fprintf(stderr, "XMLLesson(): Error finding script directory!\n");
178 return 0;
179 }
180
181
182 /* If we get to here, we know there is at least a lesson script directory */
183 /* but not necessarily any valid files. */
184
185 DEBUGCODE { fprintf(stderr, "script_path is: %s\n", script_path); }
186
187
188 /* create a list of all the .xml files */
189 num_scripts = scandir(script_path, &script_list_dirents, is_xml_file, alphasort);
190
191 for (scriptIterator = 0, scripts = 0;
192 scriptIterator < num_scripts && scripts < MAX_LESSONS;
193 scriptIterator++)
194 {
195 /* Copy over the filename: */
196 nchars = snprintf(script_filenames[scripts], FNLEN, "%s",
197 script_list_dirents[scriptIterator]->d_name);
198
199 /* Skip (actually clobber) any invalid or undesired files: */
200 if (nchars < 0 || nchars >= FNLEN)
201 continue;
202 /* Don't show project info file or instructions files */
203 if (strcmp(script_filenames[scripts], "projectInfo.xml") == 0 ||
204 strcmp(script_filenames[scripts], "laser.xml") == 0 ||
205 strcmp(script_filenames[scripts], "cascade.xml") == 0)
206 continue;
207
208 DEBUGCODE
209 {
210 fprintf(stderr, "Found script file %d:\t%s\n", scripts, script_filenames[scripts]);
211 }
212
213 /* Increment the iterator for correctly-parsed lesson files */
214 scripts++;
215 }
216
217 // DEBUGCODE
218 {
219 fprintf(stderr, "Before undesired files screened out:\n");
220 for(i = 0; i < num_scripts; i++)
221 fprintf(stderr, "script %d filename: %s\n", i,
222 script_list_dirents[i]->d_name);
223 fprintf(stderr, "After undesired files screened out:\n");
224 for(i = 0; i < scripts; i++)
225 fprintf(stderr, "script %d filename: %s\n", i,
226 script_filenames[i]);
227 }
228
229
230 /* Now free the individual dirents. We do this on a second pass */
231 /* because of the "continue" approach used to error handling. */
232 for (scriptIterator = 0; scriptIterator < num_scripts; scriptIterator++)
233 free(script_list_dirents[scriptIterator]);
234 free(script_list_dirents);
235
236 /* Adjust num_scripts for any skipped files: */
237 num_scripts = scripts;
238
239 //START OF OLD IMPLEMENTATION
240 // num_scripts = 0;
241 // script_dir = opendir(script_path);
242 // do
243 // {
244 // script_file = readdir(script_dir);
245 // if (!script_file)
246 // break;
247 //
248 // /* must have at least '.xml' at the end */
249 // if (strlen(script_file->d_name) < 5)
250 // continue;
251 //
252 // /* Don't show project info file or instructions files */
253 // if (strcmp(script_file->d_name, "projectInfo.xml") == 0 ||
254 // strcmp(script_file->d_name, "laser.xml") == 0 ||
255 // strcmp(script_file->d_name, "cascade.xml") == 0)
256 // continue;
257 //
258 //
259 // if (strcmp(&script_file->d_name[strlen(script_file->d_name) - 4],".xml"))
260 // continue;
261 //
262 // sprintf(script_filenames[num_scripts], "%s", script_file->d_name);
263 // num_scripts++;
264 // DEBUGCODE { fprintf(stderr, "Adding XML file no. %d: %s\n",
265 // num_scripts, script_filenames[num_scripts]); }
266 //
267 // } while (1); /* Leave loop when readdir() returns NULL */
268 //
269 // closedir(script_dir);
270
271 // END OF OLD IMPLEMENTATION
272
273
274
275 DEBUGCODE { fprintf(stderr, "Found %d . xml file(s) in script dir\n", num_scripts); }
276
277
278 /* let the user pick the lesson script */
279 for (i = 0; i < num_scripts; i++)
280 {
281 titles[i] = BlackOutline( script_filenames[i], DEFAULT_MENU_FONT_SIZE, &white );
282 select[i] = BlackOutline( script_filenames[i], DEFAULT_MENU_FONT_SIZE, &yellow);
283 }
284
285 left = LoadImage("left.png", IMG_ALPHA);
286 right = LoadImage("right.png", IMG_ALPHA);
287 LoadBothBkgds("main_bkg.png");
288
289 /* Get out if needed surface not loaded successfully: */
290 if (!CurrentBkgd() || !left || !right)
291 {
292 fprintf(stderr, "XMLLesson(): needed image not available\n");
293
294 for (i = 0; i < num_scripts; i++)
295 {
296 SDL_FreeSurface(titles[i]);
297 SDL_FreeSurface(select[i]);
298 titles[i] = select[i] = NULL;
299 }
300
301 SDL_FreeSurface(left);
302 SDL_FreeSurface(right);
303 left = right = NULL;
304
305 return 0;
306 }
307
308
309 leftRect.w = left->w;
310 leftRect.h = left->h;
311 leftRect.x = screen->w/2 - 80 - (leftRect.w/2);
312 leftRect.y = screen->h - 50;
313
314 rightRect.w = right->w;
315 rightRect.h = right->h;
316 rightRect.x = screen->w/2 + 80 - (rightRect.w/2);
317 rightRect.y = screen->h - 50;
318
319 /* set initial rect sizes */
320 titleRects[0].y = 30;
321 titleRects[0].w = titleRects[0].h = titleRects[0].x = 0;
322
323 for (i = 1; i < 8; i++)
324 {
325 titleRects[i].y = titleRects[i - 1].y + 50;
326 titleRects[i].w = titleRects[i].h = titleRects[i].x = 0;
327 }
328
329 /* Main event loop for this screen: */
330 while (!stop)
331 {
332 while (SDL_PollEvent(&event))
333 {
334 switch (event.type)
335 {
336 case SDL_QUIT:
337 return 0; /* Return control to the main program so we can exit cleanly */
338 break;
339
340 case SDL_MOUSEMOTION:
341 for (i = 0; (i < 8) && (loc - (loc % 8) + i < num_scripts); i++)
342 if (inRect(titleRects[i], event.motion.x, event.motion.y ))
343 {
344 loc = loc - (loc % 8) + i;
345 break;
346 }
347 break;
348
349 case SDL_MOUSEBUTTONDOWN:
350 if (inRect( leftRect, event.button.x, event.button.y ))
351 {
352 if (loc - (loc % 8) - 8 >= 0)
353 {
354 loc = loc - (loc % 8) - 8;
355 break;
356 }
357 }
358
359 if (inRect(rightRect, event.button.x, event.button.y))
360 {
361 if (loc - (loc % 8) + 8 < num_scripts)
362 {
363 loc = loc - (loc % 8) + 8;
364 break;
365 }
366 }
367
368 for (i = 0; (i < 8) && (loc - (loc % 8) + i < num_scripts); i++)
369 {
370 if (inRect(titleRects[i], event.button.x, event.button.y))
371 {
372 loc = loc - (loc % 8) + i;
373 if(settings.use_english)
374 sprintf(fn, "%s/scripts/%s", settings.default_data_path, script_filenames[loc]);
375 else
376 sprintf(fn, "%s/scripts/%s", settings.theme_data_path, script_filenames[loc]);
377 stop = 1;
378 break;
379 }
380 }
381
382 break;
383
384 case SDL_KEYDOWN:
385 if (event.key.keysym.sym == SDLK_ESCAPE)
386 {
387 stop = 2;
388 break;
389 }
390
391 if (event.key.keysym.sym == SDLK_RETURN)
392 {
393 if(settings.use_english)
394 sprintf(fn, "%s/scripts/%s", settings.default_data_path, script_filenames[loc]);
395 else
396 sprintf(fn, "%s/scripts/%s", settings.theme_data_path, script_filenames[loc]); stop = 1;
397 break;
398 }
399
400 if ((event.key.keysym.sym == SDLK_LEFT)
401 || (event.key.keysym.sym == SDLK_PAGEUP))
402 {
403 if (loc - (loc % 8) - 8 >= 0)
404 loc = loc - (loc % 8) - 8;
405 }
406
407 if ((event.key.keysym.sym == SDLK_RIGHT)
408 || (event.key.keysym.sym == SDLK_PAGEDOWN))
409 {
410 if (loc - (loc % 8) + 8 < num_scripts)
411 loc = (loc - (loc % 8) + 8);
412 }
413
414 if ((event.key.keysym.sym == SDLK_UP)
415 ||
416 (event.key.keysym.sym == SDLK_k))
417 {
418 if (loc > 0)
419 loc--;
420 }
421
422 if ((event.key.keysym.sym == SDLK_DOWN)
423 ||
424 (event.key.keysym.sym == SDLK_j))
425 {
426 if (loc + 1 < num_scripts)
427 loc++;
428 }
429 }
430 }
431
432 /* Redraw if we have changed location: */
433 if (old_loc != loc)
434 {
435 int start;
436
437 SDL_BlitSurface(CurrentBkgd(), NULL, screen, NULL );
438
439 start = loc - (loc % 8);
440
441 for (i = start; i < MIN(start + 8, num_scripts); i++)
442 {
443 titleRects[i % 8].x = screen->w/2 - (titles[i]->w/2);
444 if (i == loc) /* Draw selected text in yellow: */
445 SDL_BlitSurface(select[loc], NULL, screen, &titleRects[i%8]);
446 else /* Draw unselected text in white: */
447 SDL_BlitSurface(titles[i], NULL, screen, &titleRects[i%8]);
448 }
449
450 /* --- draw arrow buttons --- */
451 if (start > 0)
452 SDL_BlitSurface(left, NULL, screen, &leftRect);
453
454 if (start + 8 < num_scripts)
455 SDL_BlitSurface(right, NULL, screen, &rightRect);
456
457 SDL_UpdateRect(screen, 0, 0, 0 ,0);
458 }
459
460 SDL_Delay(40);
461 old_loc = loc;
462 }
463
464 /* --- clear graphics before leaving function --- */
465 for (i = 0; i < num_scripts; i++)
466 {
467 if (titles[i])
468 SDL_FreeSurface(titles[i]);
469 if (select[i])
470 SDL_FreeSurface(select[i]);
471 titles[i] = select[i] = NULL;
472 }
473
474 SDL_FreeSurface(left);
475 SDL_FreeSurface(right);
476 left = right = NULL; /* Maybe overkill - about to be destroyed anyway */
477
478 FreeBothBkgds();
479
480
481 if (stop == 2)
482 {
483 SDL_ShowCursor(1);
484 return 0;
485 }
486
487 /* Getting to here means "stop == 1", try to run chosen script: */
488 if (load_script(fn) != 0)
489 {
490 fprintf(stderr, "load_script() failed to load '%s'\n",fn);
491 SDL_ShowCursor(1);
492 return 0; // bail if any errors occur
493 }
494
495 DEBUGCODE { fprintf(stderr, "Attempting to run script: %s\n", fn); }
496
497 run_script();
498 SDL_ShowCursor(1);
499
500 LOG("Leave XMLLesson()\n");
501
502 return 1;
503 }
504
505
506
507 /************************************************************************/
508 /* */
509 /* "Private" functions (local to scripting.c) */
510 /* */
511 /************************************************************************/
512
513
get_quote(const char * in)514 static char* get_quote(const char* in)
515 {
516 int start, finish;
517 char *out;
518
519 for (start=0; start<strlen(in) && in[start] != '"'; start++); // find the first "
520
521 if (start >= strlen(in)) return 0; // return null string if no " found
522
523 start++; // move past the "
524
525 for (finish=start; finish<strlen(in) && in[finish] != '"'; finish++); // find the next "
526
527 if (finish >= strlen(in)) return 0; // return null string if no " found
528
529 out = malloc(finish - start + 2);
530
531 snprintf(out, finish - start + 1, "%s", &in[start]);
532 out[finish-start] = 0;
533
534 return out;
535 }
536
537
get_int(const char * in)538 static int get_int(const char* in)
539 {
540 char *t = get_quote(in);
541 int ans=-1;
542 if (t) {
543 ans = atoi(t);
544 free(t);
545 }
546 return ans;
547 }
548
549
hex2int(char b,char s)550 static char hex2int(char b, char s)
551 {
552 char ans=0;
553
554 if ((b>='0') && (b<='9')) ans=16*(b-'0');
555 else if ((b>='A') && (b<='F')) ans=16*(b-'A'+10);
556 else if ((b>='a') && (b<='f')) ans=16*(b-'a'+10);
557
558 if ((s>='0') && (s<='9')) ans+=(s-'0');
559 else if ((s>='A') && (s<='F')) ans+=(s-'A'+10);
560 else if ((s>='a') && (s<='f')) ans+=(s-'a'+10);
561
562 return ans;
563 }
564
565
get_color(const char * in)566 static SDL_Color* get_color(const char* in)
567 {
568 char* col;
569 SDL_Color* out=malloc(sizeof(SDL_Color));
570 col = get_quote(in);
571
572 if ((strlen(col)==7) && (col[0] == '#')) {
573 out->r = hex2int( col[1], col[2] );
574 out->g = hex2int( col[3], col[4] );
575 out->b = hex2int( col[5], col[6] );
576 }
577
578 free(col);
579
580 return out;
581 }
582
583 scriptType* curScript = NULL;
584 pageType* curPage = NULL;
585 itemType* curItem = NULL;
586
load_script(const char * fn)587 static int load_script(const char* fn)
588 {
589 int i;
590 char str[FNLEN];
591 FILE* f = NULL;
592
593 DEBUGCODE
594 {
595 fprintf(stderr, "\nEnter load_script() - attempt to load '%s'\n", fn);
596 }
597
598 if (curScript)
599 {
600 LOG( "previous script in memory, removing now!\n");
601 close_script();
602 }
603
604
605 f = fopen(fn, "r");
606
607 if (f == NULL)
608 {
609 fprintf(stderr, "error loading script %s\n", fn);
610 return -1;
611 }
612
613 do
614 {
615 /* Compiler complains if we don't inspect result of fscanf() */
616 int fscanf_result = fscanf(f, "%[^\n]\n", str);
617 if (fscanf_result == EOF)
618 break;
619
620 if (strncmp("<!--", str, 4) == 0)
621 {
622 /* -- comment section found, ignore everything until comment close -- */
623 int found = 0;
624 char* tmpStr;
625
626 do
627 {
628 // search the current line for comment end
629 for ( tmpStr = str; strlen(tmpStr) >= 3 && !found; tmpStr++ )
630 {
631 if (strncmp("-->",tmpStr, 3) == 0)
632 {
633 // move past the comment end tag
634 tmpStr += 2;
635 found = 1;
636 }
637 }
638
639 // if the comment end was not found get another line
640 if (!found)
641 {
642 fscanf_result = fscanf(f, "%[^\n]\n", str);
643 tmpStr = str;
644 }
645
646 // we did find the end of the comment
647 else
648 {
649
650 if (strlen(tmpStr) > 0)
651 {
652 // copy the rest of the line into str for processing
653 strncpy(str, tmpStr, strlen(tmpStr));
654 str[strlen(tmpStr)] = '\0';
655 }
656 else
657 {
658 // str needs another line, this one is used up
659 fscanf_result = fscanf(f, "%[^\n]\n", str);
660 tmpStr = str;
661 }
662
663 // if the next line is a comment, start all over again
664 if (fscanf_result != EOF && strncmp("<!--", str, 4) == 0)
665 {
666 found = 0;
667 }
668 }
669
670 } while ( fscanf_result != EOF && !found );
671
672 /* -- if we reached the end of the file and saw no close to the comment, generate a warning -- */
673 if ( !found && fscanf_result == EOF )
674 {
675 fprintf(stderr, "XML Warning: End of file reached looking for the end of a comment.\n");
676 break;
677 }
678
679 /* -- don't continue processing if at EOF -- */
680 if (fscanf_result == EOF)
681 {
682 break;
683 }
684 }
685
686 if (strncmp("<script", str, 7) == 0)
687 {
688 /* -- allocate space for the lesson info -- */
689 curScript = (scriptType*)calloc(1, sizeof(scriptType));
690 for (i = 7; i < strlen(str) && str[i] != '>'; i++)
691 {
692 if ((str[i] == 't') && strncmp("title", &str[i], 5) == 0)
693 curScript->title = get_quote(&str[i + 5]);
694
695 if ((str[i]=='b') && strncmp("bgcolor", &str[i], 7) == 0)
696 curScript->bgcolor = get_color(&str[i + 7]);
697
698 if ((str[i]=='b') && strncmp("background", &str[i], 10) == 0)
699 curScript->background = get_quote(&str[i + 10]);
700
701 if ((str[i]=='f') && strncmp("fgcolor", &str[i], 7) == 0)
702 curScript->fgcolor = get_color(&str[i + 7]);
703 }
704 }
705 else if (strncmp("<page", str, 5)==0)
706 {
707 if (curScript==NULL)
708 {
709 fprintf(stderr, "CRITICAL XML ERROR: <page> should be in a <script> in file %s line (todo)", fn);
710 close_script();
711 return 0;
712 }
713
714 if (curScript->pages==NULL)
715 {
716 curPage = (pageType *)calloc(1,sizeof(pageType));
717 curPage->prev = curPage;
718 curScript->pages = curPage;
719 }
720 else
721 {
722 curPage->next = (pageType*)calloc(1, sizeof(pageType));
723 curPage->next->prev = curPage;
724 curPage = curPage->next;
725 }
726
727 for (i = 5; i < strlen(str) && str[i]!='>'; i++)
728 {
729 if ((str[i] == 'b') && strncmp("background", &str[i], 10) == 0)
730 curPage->background = get_quote(&str[i + 10]);
731
732 if ((str[i]== 't') && strncmp("title", &str[i], 5) == 0)
733 curPage->title = get_quote(&str[i + 5]);
734
735 if ((str[i] == 'b') && strncmp("bgcolor", &str[i], 7) == 0)
736 curPage->bgcolor = get_color(&str[i + 7]);
737
738 if ((str[i] == 'f') && strncmp("fgcolor", &str[i], 7) == 0)
739 curPage->fgcolor = get_color(&str[i + 7]);
740 }
741 }
742 else if (strncmp("<text", str, 5) == 0)
743 {
744 if (curPage == NULL)
745 {
746 fprintf(stderr, "CRITICAL XML ERROR: <text> should be in a <page> in file %s line (todo)", fn);
747 close_script();
748 return 0;
749 }
750
751 if (curPage->items == NULL)
752 {
753 curItem = (itemType*)calloc(1, sizeof(itemType));
754 curPage->items = curItem;
755 }
756 else
757 {
758 curItem->next = (itemType*)calloc(1, sizeof(itemType));
759 curItem = curItem->next;
760 }
761
762 curItem->type = itemTEXT;
763 curItem->x = curItem->y = -1;
764
765 for (i = 5; i<strlen(str) && str[i]!='>'; i++)
766 {
767 if ((str[i] == 's') && strncmp("size", &str[i], 4) == 0)
768 curItem->size = (char)get_int(&str[i + 4]);
769
770 if ((str[i] == 'a') && strncmp("align", &str[i], 5) == 0)
771 {
772 char* t = get_quote(&str[i+5]);
773
774 if (strlen(t)>=1)
775 {
776 if ((t[0] == 'l') || (t[0]=='L'))
777 curItem->align='l'; // left
778 if ((t[0] == 'c') || (t[0]=='C'))
779 curItem->align='c'; // center
780 if ((t[0] == 'r') || (t[0]=='R'))
781 curItem->align='r'; // right
782 if ((t[0] == 'm') || (t[0]=='M'))
783 curItem->align='c'; // let 'm'iddle work as "center"
784 }
785 free(t);
786 }
787
788 if ((str[i] == 'c') && strncmp("color", &str[i], 5) == 0)
789 curItem->color = get_color(&str[i + 5]);
790
791 if ((str[i]== 'x') && strncmp(" x=", &str[i - 1], 3) == 0)
792 curItem->x = get_int(&str[i + 2]);
793
794 if ((str[i] == 'y') && strncmp(" y=", &str[i - 1], 3) == 0)
795 curItem->y = get_int(&str[i + 2]);
796 }
797
798 /* --- grab the text between <text> and </text> --- */
799 {
800 int start, finish;
801
802 for (start = 5; start < strlen(str) - 5 && str[start] != '>'; start++);
803
804 start++; // advance passed the '>'
805
806 for (finish = strlen(str) - 6; finish > 5; finish--)
807 if (strncmp( "</text>", &str[finish], 7) == 0)
808 break;
809
810 finish--; // advance passed the '<'
811
812 if (start <= finish)
813 {
814 curItem->data = (char*)calloc(1, finish - start + 2);
815 strncpy(curItem->data, &str[start], finish - start + 1);
816 }
817 else
818 {
819 if (start == finish + 1)
820 {
821 curItem->data = (char*)calloc(1, 2);
822 curItem->data[0]=' ';
823 }
824 }
825 }
826
827 }
828 else if (strncmp("<img", str, 4) == 0)
829 {
830 if (curPage == NULL)
831 {
832 fprintf(stderr,
833 "CRITICAL XML ERROR: <img> should be in a <page> in file %s line (todo)",
834 fn);
835 close_script();
836 return 0; //Return control to the main program for a clean exit
837 }
838
839 if (curPage->items == NULL)
840 {
841 curItem = (itemType*)calloc(1, sizeof(itemType));
842 curPage->items = curItem;
843 }
844 else
845 {
846 curItem->next = (itemType*)calloc(1, sizeof(itemType));
847 curItem = curItem->next;
848 }
849
850 curItem->type = itemIMG;
851 curItem->x = curItem->y = -1;
852
853 for (i = 5; i < strlen(str); i++)
854 {
855 if ((str[i] == 'o') && strncmp("onclickplay", &str[i], 11) == 0)
856 curItem->onclick = get_quote(&str[i + 3]);
857
858 if ((str[i] == 'x') && strncmp(" x=", &str[i - 1], 3) == 0)
859 curItem->x = get_int(&str[i + 2]);
860
861 if ((str[i] == 'y') && strncmp(" y=", &str[i - 1], 3) == 0)
862 curItem->y = get_int(&str[i + 2]);
863
864 if ((str[i] == 's') && strncmp("src", &str[i], 3) == 0)
865 curItem->data = get_quote(&str[i + 3]);
866
867 if ((str[i] == 'a') && strncmp("align", &str[i], 5) == 0)
868 {
869 char* t = get_quote(&str[i + 5]);
870
871 if (strlen(t) >= 1)
872 {
873 if ((t[0] == 'l') || (t[0] == 'L'))
874 curItem->align='l'; // left
875
876 if ((t[0] == 'c') || (t[0]=='C'))
877 curItem->align='c'; // center
878
879 if ((t[0] == 'r') || (t[0]=='R'))
880 curItem->align='r'; // right
881
882 if ((t[0] == 'm') || (t[0]=='M'))
883 curItem->align='c'; // let 'm'iddle work as "center"
884 }
885 free(t);
886 }
887 }
888 }
889 else if (strncmp("<bkgd", str, 5) == 0)
890 {
891 if (curPage == NULL)
892 {
893 fprintf(stderr,
894 "CRITICAL XML ERROR: <bkgd> should be in a <page> in file %s line (todo)",
895 fn);
896 close_script();
897 return 0; //Return control to the main program for a clean exit
898 }
899
900 if (curPage->items == NULL)
901 {
902 curItem = (itemType*)calloc(1, sizeof(itemType));
903 curPage->items = curItem;
904 }
905 else
906 {
907 curItem->next = (itemType*)calloc(1, sizeof(itemType));
908 curItem = curItem->next;
909 }
910
911 curItem->type = itemBKGD;
912 curItem->x = curItem->y = -1;
913
914
915 for (i = 6; i < strlen(str); i++)
916 {
917 if ((str[i] == 's') && strncmp("src", &str[i], 3) == 0)
918 curItem->data = get_quote(&str[i + 3]);
919 }
920 }
921
922 else if (strncmp("<wav", str, 4) == 0)
923 {
924 if (curPage == NULL)
925 {
926 fprintf(stderr,
927 "CRITICAL XML ERROR: <wav> should be in a <page> in file %s line (todo)",
928 fn);
929 close_script();
930 return 0; /* Return control to main program for a clean exit */
931 }
932
933 if (curPage->items == NULL)
934 {
935 curItem = (itemType*)calloc(1, sizeof(itemType));
936 curPage->items = curItem;
937 }
938 else
939 {
940 curItem->next = (itemType*)calloc(1, sizeof(itemType));
941 curItem = curItem->next;
942 }
943
944 curItem->type = itemWAV;
945 curItem->loop = 0;
946
947 for (i =5 ; i < strlen(str); i++)
948 {
949 if ((str[i] == 's') && strncmp("src", &str[i], 3) ==0 )
950 curItem->data = get_quote(&str[i + 3]);
951
952 if ((str[i] == 'l') && strncmp("loop", &str[i], 4) == 0)
953 {
954 char* t = get_quote(&str[i + 4]);
955
956 if (strlen(t) >= 1)
957 {
958 if ((t[0] == 't') || (t[0]=='T'))
959 curItem->loop = 1;
960 }
961 free(t);
962 }
963 }
964 }
965 else if (strncmp("<prac", str, 5) == 0)
966 {
967 if (curPage == NULL)
968 {
969 fprintf(stderr,
970 "CRITICAL XML ERROR: <prac> should be in a <page> in file %s line (todo)",
971
972 fn);
973 close_script();
974 return 0; /* Return control to the main program for a clean exit */
975 }
976
977 if (curPage->items == NULL)
978 {
979 curItem = (itemType*)calloc(1, sizeof(itemType));
980 curPage->items = curItem;
981 }
982 else
983 {
984 curItem->next = (itemType*)calloc(1, sizeof(itemType));
985 curItem = curItem->next;
986 }
987
988 curItem->type = itemPRAC;
989
990 for (i = 5; i < strlen(str) && str[i] != '>'; i++)
991 {
992 if ((str[i] == 's') && strncmp("size", &str[i], 4) == 0)
993 curItem->size = (char)get_int( &str[i + 4]);
994
995 if ((str[i] == 'g') && strncmp("goal", &str[i], 4) == 0)
996 curItem->goal = (char)get_int(&str[i + 4]);
997
998 if ((str[i] == 'a') && strncmp("align", &str[i], 5) == 0)
999 {
1000 char* t = get_quote(&str[i + 5]);
1001
1002 if (strlen(t) >= 1)
1003 {
1004 if ((t[0] == 'l') || (t[0]=='L'))
1005 curItem->align = 'l'; // left
1006 if ((t[0] == 'c') || (t[0]=='C'))
1007 curItem->align = 'c'; // center
1008 if ((t[0] == 'r') || (t[0]=='R'))
1009 curItem->align = 'r'; // right
1010 if ((t[0] == 'm') || (t[0]=='M'))
1011 curItem->align = 'c'; // let 'm'iddle work as "center"
1012 }
1013 free(t);
1014 }
1015
1016 if ((str[i] == 'c') && strncmp("color", &str[i], 5) == 0)
1017 curItem->color = get_color(&str[i + 5]);
1018 }
1019
1020 { /* --- grab the text between <prac> and </prac> --- */
1021 int start, finish;
1022
1023 for (start = 5; start < strlen(str) - 5 && str[start] != '>'; start++);
1024
1025 start++; // advance passed the '>/* --- grab the text between <prac> and </prac> --- */'
1026
1027 for (finish = strlen(str) - 6; finish > 5; finish--)
1028 if (strncmp("</prac>", &str[finish], 7) == 0)
1029 break;
1030
1031 finish--; // advance passed the '<'
1032
1033 if (start <= finish)
1034 {
1035 curItem->data = (char*)calloc(1, finish - start + 2);
1036 strncpy(curItem->data, &str[start], finish - start + 1);
1037 }
1038 else
1039 {
1040 if (start == finish + 1)
1041 {
1042 curItem->data = (char*)calloc(1, 2);
1043 curItem->data[0]=' ';
1044 }
1045 }
1046 }
1047 }
1048 else if (strncmp("<waitforinput", str, 13) == 0)
1049 {
1050 if (curPage == NULL)
1051 {
1052 fprintf(stderr,
1053 "CRITICAL XML ERROR: <waitforinput> should be in a <page> in file %s line (todo)",
1054 fn);
1055 close_script();
1056 return 0;
1057 }
1058
1059 if (curPage->items == NULL)
1060 {
1061 curItem = (itemType*)calloc(1, sizeof(itemType));
1062 curPage->items = curItem;
1063 }
1064 else
1065 {
1066 curItem->next = (itemType*)calloc(1, sizeof(itemType));
1067 curItem = curItem->next;
1068 }
1069
1070 curItem->type = itemWFIN;
1071
1072 }
1073 else if (strncmp("<waitforchar", str, 12) == 0)
1074 {
1075 if (curPage == NULL)
1076 {
1077 fprintf(stderr,
1078 "CRITICAL XML ERROR: <waitforchar> should be in a <page> in file %s line (todo)",
1079 fn);
1080 close_script();
1081 return 0;
1082 }
1083
1084 if (curPage->items == NULL)
1085 {
1086 curItem = (itemType*)calloc(1, sizeof(itemType));
1087 curPage->items = curItem;
1088 }
1089 else
1090 {
1091 curItem->next = (itemType*)calloc(1, sizeof(itemType));
1092 curItem = curItem->next;
1093 }
1094
1095 curItem->type = itemWFCH;
1096 }
1097 else if (strncmp("</",str, 2) == 0)
1098 {
1099 /* do nothing */
1100 }
1101 else
1102 fprintf(stderr, "not recognized: %s\n", str);
1103
1104 } while(!feof(f));
1105
1106 fclose(f);
1107
1108 LOG("Leave load_script()\n");
1109
1110 return 0;
1111 }
1112
1113
1114
1115
run_script(void)1116 static void run_script(void)
1117 {
1118 /* FIXME FNLEN doesn't make sense for size of these arrays */
1119 Mix_Chunk* sounds[FNLEN] = {NULL};
1120
1121 /* --- for on mouse click on an image --- */
1122 Mix_Chunk* clickWavs[FNLEN] = {NULL};
1123 SDL_Rect clickRects[FNLEN];
1124
1125 LOG("\nEnter run_script()\n");
1126
1127 if (!curScript)
1128 {
1129 fprintf(stderr, "run_script() - Error: curScript is NULL\n");
1130 return;
1131 }
1132
1133 curPage = curScript->pages;
1134
1135 while (curPage)
1136 {
1137 int y = 0;
1138 int skip = 0;
1139 int numWavs = 0;
1140 int numClicks = 0;
1141
1142 curItem = curPage->items;
1143
1144 /* --- setup background color --- */
1145 if (curPage->bgcolor)
1146 SDL_FillRect( screen, NULL, COL2RGB(curPage->bgcolor));
1147 else if (curScript->bgcolor)
1148 SDL_FillRect(screen, NULL, COL2RGB(curScript->bgcolor));
1149
1150 /* --- setup background image --- */
1151 if (curPage->background)
1152 {
1153 SDL_Surface* img = LoadImage(curPage->background, IMG_ALPHA|IMG_NOT_REQUIRED);
1154
1155 /* hack: since this is the background it needs to scale when in fullscreen
1156 * but shouldn't every image scale when in fullscreen? assuming svg is for that... -MDT */
1157 if (settings.fullscreen)
1158 {
1159 SDL_Surface* fsimg = zoom(img, fs_res_x, fs_res_y);
1160 SDL_BlitSurface(fsimg, NULL, screen, NULL);
1161 SDL_FreeSurface(fsimg);
1162 }
1163 else
1164 {
1165 SDL_BlitSurface(img, NULL, screen, NULL);
1166 }
1167
1168 SDL_FreeSurface(img);
1169
1170 }
1171 else if (curScript->background)
1172 {
1173 SDL_Surface* img = LoadImage(curScript->background, IMG_ALPHA|IMG_NOT_REQUIRED);
1174
1175 /* hack: since this is the background it needs to scale when in fullscreen -MDT */
1176 if (settings.fullscreen)
1177 {
1178 SDL_Surface* fsimg = zoom(img, fs_res_x, fs_res_y);
1179 SDL_BlitSurface(fsimg, NULL, screen, NULL);
1180 SDL_FreeSurface(fsimg);
1181 }
1182 else
1183 {
1184 SDL_BlitSurface(img, NULL, screen, NULL);
1185 }
1186
1187 SDL_FreeSurface(img);
1188 }
1189
1190 /* --- go through all the items in the page --- */
1191 while (curItem)
1192 {
1193 switch (curItem->type)
1194 {
1195 case itemIMG:
1196 {
1197 SDL_Surface* img = LoadImage(curItem->data, IMG_ALPHA|IMG_NOT_REQUIRED);
1198 if (img)
1199 {
1200 /* --- figure out where to put it! --- */
1201 SDL_Rect loc;
1202 loc.w = img->w;
1203 loc.h = img->h;
1204
1205 /* --- if user specifies y location, use it --- */
1206 if (curItem->y >= 0)
1207 {
1208 loc.y = curItem->y;
1209 }
1210 else
1211 {
1212 loc.y = y;
1213 y += loc.h;
1214 }
1215
1216 /* --- if user specifies x location, use it --- */
1217 if (curItem->x >= 0)
1218 {
1219 loc.x = curItem->x;
1220 }
1221 else
1222 {
1223 switch (curItem->align)
1224 {
1225 case 'r':
1226 loc.x = (screen->w) - (loc.w);
1227 break;
1228 case 'c':
1229 loc.x = ((screen->w) - (loc.w))/2;
1230 break;
1231 default:
1232 loc.x = 0;
1233 break;
1234 }
1235 }
1236
1237 /* --- and blit! --- */
1238 SDL_BlitSurface(img, NULL, screen, &loc);
1239
1240 /* --- does it do click and play --- */
1241 if (curItem->onclick)
1242 {
1243 if (settings.sys_sound)
1244 clickWavs[numClicks] = LoadSound(curItem->onclick);
1245 clickRects[numClicks].x = loc.x;
1246 clickRects[numClicks].y = loc.y;
1247 clickRects[numClicks].w = loc.w;
1248 clickRects[numClicks].h = loc.h;
1249 numClicks++;
1250 }
1251
1252 SDL_FreeSurface(img);
1253 }
1254 break;
1255 }
1256
1257 case itemBKGD:
1258 {
1259 SDL_Surface* img = LoadImage(curItem->data, IMG_ALPHA|IMG_NOT_REQUIRED);
1260 if (img)
1261 {
1262 /* hack: since this is the background it needs to scale when in fullscreen -MDT */
1263 if (settings.fullscreen)
1264 {
1265 SDL_Surface* fsimg = zoom(img, fs_res_x, fs_res_y);
1266 SDL_BlitSurface(fsimg, NULL, screen, NULL);
1267 SDL_FreeSurface(fsimg);
1268 }
1269 else
1270 {
1271 SDL_BlitSurface(img, NULL, screen, NULL);
1272 }
1273 SDL_FreeSurface(img);
1274 }
1275 break;
1276 }
1277
1278 case itemTEXT:
1279 {
1280 SDL_Surface* img;
1281 SDL_Color* col;
1282
1283 int shown, toshow; // used to wrap text
1284 char tmp[FNLEN]; // used to hold temp text for wrapping
1285
1286 /* Sanity check - use default font size if size not specified: */
1287 if ((curItem->size < 8) || (curItem->size > 80))
1288 curItem->size = DEFAULT_SCRIPT_FONT_SIZE; //currently 24
1289
1290 if (curItem->color)
1291 col = curItem->color;
1292 else if (curPage->fgcolor)
1293 col = curPage->fgcolor;
1294 else if (curScript->fgcolor)
1295 col = curScript->fgcolor;
1296 else
1297 col = (SDL_Color*)&white;
1298
1299 shown = 0;
1300
1301
1302 do
1303 {
1304 int ok = 0;
1305
1306 if ((shown > 0) && (curItem->data[shown] == ' '))
1307 shown++;
1308 strncpy(tmp, &curItem->data[shown], FNLEN - 1);
1309 tmp[FNLEN - 1] = 0;
1310 tmp[strlen(curItem->data) - shown] = 0;
1311
1312 for (toshow = strlen(&curItem->data[shown]); !ok; toshow--)
1313 {
1314 if (toshow + 1 > FNLEN)
1315 continue;
1316
1317 tmp[toshow] = 0;
1318
1319 img = SimpleText(tmp, (int)curItem->size, col);
1320
1321 if (img)
1322 {
1323 if (img->w + 20 < screen->w)
1324 ok = 1;
1325 SDL_FreeSurface(img);
1326 img = NULL;
1327 }
1328 }
1329
1330 shown += toshow + 1;
1331
1332 img = SimpleText(tmp, (int)curItem->size, col);
1333
1334 if (img)
1335 {
1336 SDL_Rect loc;
1337 /* --- figure out where to put it! --- */
1338 loc.w = img->w;
1339 loc.h = img->h;
1340
1341 /* --- if user specifies y location, use it --- */
1342 if (curItem->y >= 0)
1343 loc.y = curItem->y;
1344 else
1345 loc.y=y; y+=loc.h;
1346
1347 /* --- if user specifies x location, use it --- */
1348 if (curItem->x >= 0)
1349 loc.x = curItem->x;
1350 else
1351 {
1352 switch (curItem->align)
1353 {
1354 case 'r':
1355 loc.x = (screen->w) - (loc.w);
1356 break;
1357 case 'c':
1358 loc.x = ((screen->w) - (loc.w))/2;
1359 break;
1360 default:
1361 loc.x = 0;
1362 break;
1363 }
1364 }
1365
1366 /* --- and blit! --- */
1367 SDL_BlitSurface(img, NULL, screen, &loc);
1368 SDL_FreeSurface(img);
1369 }
1370
1371 } while (shown + 1 < strlen(curItem->data));
1372
1373 break;
1374 }
1375
1376
1377 case itemWAV:
1378 {
1379 // HACK, we need to make sure no more than 8 sounds or so..
1380 sounds[numWavs] = LoadSound( curItem->data );
1381
1382 // let audio.c handle calls to SDL_mixer
1383 //Mix_PlayChannel( numWavs, sounds[numWavs], -curItem->loop );
1384
1385 PlaySoundLoop( sounds[numWavs], -curItem->loop );
1386 numWavs++;
1387 break;
1388 }
1389
1390
1391 case itemWFIN:
1392 {
1393 int done = 0;
1394
1395 // Make sure everything is on screen
1396 SDL_Flip(screen);
1397
1398 while (!done)
1399 {
1400 SDL_Delay(100);
1401
1402 while (SDL_PollEvent(&event))
1403 {
1404 switch (event.type)
1405 {
1406 case SDL_MOUSEBUTTONDOWN:
1407 {
1408 int j;
1409
1410 for (j=0; j<numClicks; j++)
1411 {
1412 if (inRect(clickRects[j], event.button.x, event.button.y))
1413 PlaySound( clickWavs[j] );
1414 // let audio.c handle calls to SDL_mixer
1415 //Mix_PlayChannel(numWavs + j, clickWavs[j], 0);
1416 }
1417 break;
1418 }
1419
1420 case SDL_QUIT:
1421 {
1422 curPage = NULL;
1423 done = 1;
1424 break;
1425 }
1426
1427 case SDL_KEYDOWN:
1428 {
1429 switch (event.key.keysym.sym)
1430 {
1431 case SDLK_ESCAPE:
1432 curPage = NULL;
1433 done = 1;
1434 break; // quit
1435 case SDLK_LEFT:
1436 curPage = curPage->prev;
1437 done = 1;
1438 break;
1439 case SDLK_RIGHT:
1440 case SDLK_SPACE:
1441 case SDLK_RETURN:
1442 curPage = curPage->next;
1443 skip = 1;
1444 done = 1;
1445 break;
1446 default:
1447 break;
1448 };
1449
1450 break;
1451 }
1452 }
1453 }
1454 }
1455 }
1456 break;
1457
1458
1459 case itemWFCH:
1460 {
1461 int done = 0;
1462 // Make sure everything is on screen
1463 SDL_Flip(screen);
1464
1465 while (!done)
1466 {
1467 SDL_Delay(100);
1468 while (SDL_PollEvent(&event))
1469 {
1470 switch (event.type)
1471 {
1472 case SDL_QUIT:
1473 {
1474 curPage = NULL;
1475 done = 1;
1476 break;
1477 }
1478
1479 case SDL_KEYDOWN:
1480 {
1481 switch (event.key.keysym.sym)
1482 {
1483 case SDLK_ESCAPE:
1484 {
1485 curPage = NULL;
1486 done = 1;
1487 break; // quit
1488 }
1489 case SDLK_p:
1490 {
1491 curPage = curPage->next;
1492 done = 1;
1493 break;
1494 }
1495 default:
1496 break;
1497 };
1498 break;
1499 }
1500 }
1501 }
1502 }
1503 }
1504 break;
1505
1506 case itemPRAC:
1507 {
1508 wchar_t wide_buf[FNLEN];
1509 ConvertFromUTF8(wide_buf, curItem->data, FNLEN);
1510 if (curItem->goal > 0)
1511 {
1512 //printf( "goal is %d\n", curItem->goal );
1513 Phrases(wide_buf);
1514 }
1515 else
1516 {
1517 //printf( "No goal \n" );
1518 Phrases(wide_buf);
1519 }
1520 break;
1521 }
1522
1523 default:
1524 {
1525 /* do nothing */
1526 }
1527 }
1528
1529 if (curItem->next == NULL && curPage != NULL)
1530 {
1531 if (!skip)
1532 {
1533 curPage = curPage->next;
1534 skip = 0;
1535 }
1536 break;
1537 }
1538 else
1539 curItem = curItem->next;
1540 }
1541 SDL_Flip(screen);
1542 SDL_Delay(30);
1543
1544
1545 /* --- cleanup memory --- changing pages --- */
1546 {
1547 int i;
1548
1549 if (settings.sys_sound)
1550 {
1551 // halt all the channels before we try to free the sounds
1552 audioHaltChannel(-1);
1553
1554 for (i=0; i<numWavs; i++)
1555 {
1556 // let audio.c handle calls to SDL_mixer
1557 //Mix_HaltChannel(i);
1558
1559 Mix_FreeChunk(sounds[i]);
1560 }
1561
1562 for (i = 0; i < numClicks; i++)
1563 {
1564 // let audio.c handle calls to SDL_mixer
1565 //Mix_HaltChannel(i + numWavs);
1566
1567 Mix_FreeChunk(clickWavs[i]);
1568 }
1569 }
1570 }
1571
1572 } /* --- End of "while (curPage)" loop ----*/
1573
1574 LOG("Leave run_script()\n");
1575 }
1576
1577
clear_items(itemType * i)1578 static void clear_items(itemType* i)
1579 {
1580 itemType* n;
1581
1582 /* if i is null, will return harmlessly: */
1583 while (i) {
1584 n = i->next; // remember the next guy
1585
1586 /* -- remove any data we are pointing to -- */
1587 free(i->data);
1588 free(i->onclick);
1589 free(i->color);
1590
1591 /* -- remove ourselves --*/
1592 free(i);
1593
1594 /* -- on to the next guy! -- */
1595 i = n;
1596 }
1597 }
1598
clear_pages(pageType * p)1599 static void clear_pages(pageType* p)
1600 {
1601 pageType* n;
1602
1603 /* if p is null, will return harmlessly: */
1604 while (p) {
1605 n = p->next; // remember the next guy
1606
1607 /* -- remove all of our sub elements -- */
1608 clear_items(p->items);
1609
1610 /* -- free anything we are pointing to --- */
1611 free(p->background);
1612 free(p->title);
1613 free(p->bgcolor);
1614 free(p->fgcolor);
1615
1616 /* -- free ourselves -- */
1617 free(p);
1618
1619 /* -- on to the next --*/
1620 p = n;
1621 }
1622 }
1623
1624
close_script(void)1625 static void close_script(void)
1626 {
1627 if (curScript)
1628 {
1629 /* -- remove all the pages we have --*/
1630 clear_pages(curScript->pages);
1631
1632 /* -- remove attributes we are pointing to -- */
1633 free(curScript->title);
1634 free(curScript->bgcolor);
1635 free(curScript->fgcolor);
1636 free(curScript->background);
1637
1638 /* -- free yourself -- */
1639 free(curScript);
1640
1641 /* -- and remember you did -- */
1642 curScript = NULL;
1643 }
1644 }
1645
1646
1647 /* NOTE we just check to see if the name ends in ".xml", but we don't */
1648 /* verify that it really contains XML. */
is_xml_file(const struct dirent * xml_dirent)1649 static int is_xml_file(const struct dirent* xml_dirent)
1650 {
1651 const char* ending = &xml_dirent->d_name[strlen(xml_dirent->d_name) - 4];
1652 return (0 == strncasecmp(ending, ".xml", 4));
1653 }
1654