fixed: was potentially storing stack-allocated name strings in the trackmap
[andemo] / src / demosys.c
1 #include <stdio.h>
2 #include <string.h>
3 #include "demo.h"
4 #include "demosys.h"
5 #include "treestore.h"
6 #include "assfile.h"
7 #include "rbtree.h"
8 #include "darray.h"
9
10 void regscr_testa(void);
11 void regscr_testb(void);
12
13 static void proc_screen_script(struct demoscreen *scr, struct ts_node *node);
14 static void proc_track(struct ts_node *node, const char *pname);
15 static long io_read(void *buf, size_t bytes, void *uptr);
16 static void del_rbnode(struct rbnode *node, void *cls);
17
18
19 int dsys_init(const char *fname)
20 {
21         int i;
22         struct ts_io io = {0};
23         struct ts_node *ts, *tsnode;
24         struct demoscreen *scr;
25
26         memset(&dsys, 0, sizeof dsys);
27         if(!(dsys.trackmap = rb_create(RB_KEY_STRING))) {
28                 return -1;
29         }
30         rb_set_delete_func(dsys.trackmap, del_rbnode, 0);
31
32         dsys.track = darr_alloc(0, sizeof *dsys.track);
33         dsys.value = darr_alloc(0, sizeof *dsys.value);
34
35         regscr_testa();
36         regscr_testb();
37
38         for(i=0; i<dsys.num_screens; i++) {
39                 if(dsys.screens[i]->init() == -1) {
40                         fprintf(stderr, "failed to initialize demo screen: %s\n", dsys.screens[i]->name);
41                         return -1;
42                 }
43         }
44
45         if(!fname || !(io.data = ass_fopen(fname, "rb"))) {
46                 dsys_run_screen(dsys.screens[0]);
47                 return 0;
48         }
49         io.read = io_read;
50
51         if(!(ts = ts_load_io(&io)) || strcmp(ts->name, "demo") != 0) {
52                 ass_fclose(io.data);
53                 fprintf(stderr, "failed to read demoscript\n");
54                 return -1;
55         }
56
57         tsnode = ts->child_list;
58         while(tsnode) {
59                 if(strcmp(tsnode->name, "screen") == 0 &&
60                                 (scr = dsys_find_screen(ts_get_attr_str(tsnode, "name", 0)))) {
61                         proc_screen_script(scr, tsnode);
62
63                 } else if(strcmp(tsnode->name, "track") == 0) {
64                         proc_track(tsnode, 0);
65                 }
66                 tsnode = tsnode->next;
67         }
68
69         ass_fclose(io.data);
70         return 0;
71 }
72
73 static void proc_screen_script(struct demoscreen *scr, struct ts_node *node)
74 {
75         struct ts_node *sub;
76         struct ts_attr *attr;
77         long tm;
78
79         attr = node->attr_list;
80         while(attr) {
81                 if(sscanf(attr->name, "key_%ld", &tm) == 1 && attr->val.type == TS_NUMBER) {
82                         anm_set_value(&scr->track, tm, attr->val.fnum);
83                 }
84                 attr = attr->next;
85         }
86
87         sub = node->child_list;
88         while(sub) {
89                 if(strcmp(sub->name, "track") == 0) {
90                         proc_track(sub, scr->name);
91                 }
92                 sub = sub->next;
93         }
94 }
95
96 static void proc_track(struct ts_node *node, const char *pname)
97 {
98         char *name, *buf;
99         struct ts_attr *attr;
100         long tm;
101         int tidx;
102         struct anm_track *trk;
103
104         if(!(name = (char*)ts_get_attr_str(node, "name", 0))) {
105                 return;
106         }
107         if(pname) {
108                 buf = alloca(strlen(name) + strlen(pname) + 2);
109                 sprintf(buf, "%s.%s", pname, name);
110                 name = buf;
111         }
112
113         if((tidx = dsys_add_track(name)) == -1) {
114                 return;
115         }
116         trk = dsys.track + tidx;
117
118         attr = node->attr_list;
119         while(attr) {
120                 if(sscanf(attr->name, "key_%ld", &tm) == 1 && attr->val.type == TS_NUMBER) {
121                         anm_set_value(trk, tm, attr->val.fnum);
122                 }
123                 attr = attr->next;
124         }
125 }
126
127 static long io_read(void *buf, size_t bytes, void *uptr)
128 {
129         return ass_fread(buf, 1, bytes, uptr);
130 }
131
132
133 void dsys_destroy(void)
134 {
135         int i;
136
137         for(i=0; i<dsys.num_screens; i++) {
138                 anm_destroy_track(&dsys.screens[i]->track);
139                 if(dsys.screens[i]->destroy) {
140                         dsys.screens[i]->destroy();
141                 }
142         }
143         dsys.num_screens = 0;
144
145         darr_free(dsys.track);
146         darr_free(dsys.value);
147         rb_free(dsys.trackmap);
148 }
149
150 void dsys_update(void)
151 {
152         int i, j, sort_needed = 0;
153         struct demoscreen *scr;
154
155         dsys.tmsec = time_msec;
156
157         dsys.num_act = 0;
158         for(i=0; i<dsys.num_screens; i++) {
159                 scr = dsys.screens[i];
160                 scr->vis = anm_get_value(&scr->track, dsys.tmsec);
161
162                 if(scr->vis > 0.0f) {
163                         if(!scr->active) {
164                                 if(scr->start) scr->start();
165                                 scr->active = 1;
166                         }
167                         if(scr->update) scr->update(dsys.tmsec);
168
169                         if(dsys.num_act && scr->prio != dsys.act[dsys.num_act - 1]->prio) {
170                                 sort_needed = 1;
171                         }
172                         dsys.act[dsys.num_act++] = scr;
173                 } else {
174                         if(scr->active) {
175                                 if(scr->stop) scr->stop();
176                                 scr->active = 0;
177                         }
178                 }
179         }
180
181         if(sort_needed) {
182                 for(i=0; i<dsys.num_act; i++) {
183                         for(j=i+1; j<dsys.num_act; j++) {
184                                 if(dsys.act[j]->prio > dsys.act[j - 1]->prio) {
185                                         void *tmp = dsys.act[j];
186                                         dsys.act[j] = dsys.act[j - 1];
187                                         dsys.act[j - 1] = tmp;
188                                 }
189                         }
190                 }
191         }
192
193         /* evaluate tracks */
194         for(i=0; i<dsys.num_tracks; i++) {
195                 dsys.value[i] = anm_get_value(dsys.track + i, dsys.tmsec);
196         }
197 }
198
199 /* TODO: do something about draw ordering of the active screens */
200 void dsys_draw(void)
201 {
202         int i;
203         for(i=0; i<dsys.num_act; i++) {
204                 dsys.act[i]->draw();
205         }
206 }
207
208 void dsys_run(void)
209 {
210 }
211
212 void dsys_stop(void)
213 {
214 }
215
216 void dsys_seek_abs(long tm)
217 {
218 }
219
220 void dsys_seek_rel(long dt)
221 {
222 }
223
224 void dsys_seek_norm(float t)
225 {
226 }
227
228
229 struct demoscreen *dsys_find_screen(const char *name)
230 {
231         int i;
232
233         if(!name) return 0;
234
235         for(i=0; i<dsys.num_screens; i++) {
236                 if(strcmp(dsys.screens[i]->name, name) == 0) {
237                         return dsys.screens[i];
238                 }
239         }
240         return 0;
241 }
242
243 void dsys_run_screen(struct demoscreen *scr)
244 {
245         int i;
246
247         if(!scr) return;
248         if(dsys.num_act == 1 && dsys.act[0] == scr) return;
249
250         for(i=0; i<dsys.num_act; i++) {
251                 if(dsys.act[i]->stop) dsys.act[i]->stop();
252                 dsys.act[i]->active = 0;
253         }
254
255         dsys.act[0] = scr;
256         dsys.num_act = 1;
257
258         if(scr->start) scr->start();
259         scr->active = 1;
260 }
261
262
263 int dsys_add_screen(struct demoscreen *scr)
264 {
265         if(!scr->name || !scr->init || !scr->draw) {
266                 fprintf(stderr, "dsys_add_screen: invalid screen\n");
267                 return -1;
268         }
269         if(anm_init_track(&scr->track) == -1) {
270                 fprintf(stderr, "dsys_add_screen: failed to initialize keyframe track\n");
271                 return -1;
272         }
273         anm_set_track_interpolator(&scr->track, ANM_INTERP_LINEAR);
274         anm_set_track_extrapolator(&scr->track, ANM_EXTRAP_CLAMP);
275         anm_set_track_default(&scr->track, 0);
276
277         dsys.screens[dsys.num_screens++] = scr;
278         return 0;
279 }
280
281 int dsys_add_track(const char *name)
282 {
283         struct anm_track trk;
284         int idx;
285
286         if(rb_find(dsys.trackmap, (char*)name)) {
287                 fprintf(stderr, "ignoring duplicate track: %s\n", name);
288                 return -1;
289         }
290
291         idx = darr_size(dsys.track);
292         darr_push(dsys.track, &trk);
293         darr_pushf(dsys.value, 0);
294
295         anm_init_track(dsys.track + idx);
296
297         if(rb_insert(dsys.trackmap, (char*)strdup_nf(name), (void*)(intptr_t)idx) == -1) {
298                 fprintf(stderr, "failed to insert to track map: %s\n", name);
299                 abort();
300         }
301         dsys.num_tracks = idx + 1;
302         return idx;
303 }
304
305 int dsys_find_track(const char *name)
306 {
307         struct rbnode *n = rb_find(dsys.trackmap, (char*)name);
308         if(!n) return -1;
309
310         return (intptr_t)n->data;
311 }
312
313 float dsys_value(const char *name)
314 {
315         int idx = dsys_find_track(name);
316         return idx == -1 ? 0.0f : dsys.value[idx];
317 }
318
319 static void del_rbnode(struct rbnode *node, void *cls)
320 {
321         free(node->key);
322 }