1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include "shadowdive/script.h"
5 #include "shadowdive/error.h"
6 #include "shadowdive/taglist.h"
7 
8 static void _create_frame(sd_script *script, int number);
9 static void _create_tag(sd_script_frame *frame, int number);
10 
read_next_int(const char * str,int * pos)11 static int read_next_int(const char *str, int *pos) {
12     int opos = 0;
13     char buf[20];
14     memset(buf, 0, 20);
15     if(str[*pos] == '-' && str[(*pos)+1] >= '0' && str[(*pos)+1] <= '9') {
16         buf[opos] = str[*pos];
17         (*pos)++;
18         opos++;
19     }
20     if(str[*pos] == '+') {
21         (*pos)++;
22     }
23     while(str[*pos] >= '0' && str[*pos] <= '9') {
24         buf[opos] = str[*pos];
25         (*pos)++;
26         opos++;
27     }
28 
29     if(opos == 0)
30         return 0;
31     return atoi(buf);
32 }
33 
sd_script_create(sd_script * script)34 int sd_script_create(sd_script *script) {
35     if(script == NULL) {
36         return SD_INVALID_INPUT;
37     }
38     memset(script, 0, sizeof(sd_script));
39     return SD_SUCCESS;
40 }
41 
sd_script_free(sd_script * script)42 void sd_script_free(sd_script *script) {
43     if(script == NULL)
44         return;
45     for(int i = 0; i < script->frame_count; i++) {
46         free(script->frames[i].tags);
47     }
48     free(script->frames);
49 }
50 
sd_script_append_frame(sd_script * script,int tick_len,int sprite_id)51 int sd_script_append_frame(sd_script *script, int tick_len, int sprite_id) {
52     if(script == NULL) {
53         return SD_INVALID_INPUT;
54     }
55 
56     _create_frame(script, script->frame_count);
57     script->frames[script->frame_count].tick_len = tick_len;
58     script->frames[script->frame_count].sprite = sprite_id;
59     script->frame_count++;
60     return SD_SUCCESS;
61 }
62 
sd_script_clear_tags(sd_script * script,int frame_id)63 int sd_script_clear_tags(sd_script *script, int frame_id) {
64     if(script == NULL || frame_id < 0 || frame_id >= script->frame_count) {
65         return SD_INVALID_INPUT;
66     }
67 
68     // Clear out old tags
69     free(script->frames[frame_id].tags);
70     script->frames[frame_id].tags = NULL;
71     script->frames[frame_id].tag_count = 0;
72     return SD_SUCCESS;
73 }
74 
sd_script_set_tick_len_at_frame(sd_script * script,int frame_id,int duration)75 int sd_script_set_tick_len_at_frame(sd_script *script, int frame_id, int duration) {
76     if(script == NULL || frame_id < 0 || frame_id >= script->frame_count) {
77         return SD_INVALID_INPUT;
78     }
79 
80     script->frames[frame_id].tick_len = duration;
81     return SD_SUCCESS;
82 }
83 
sd_script_set_sprite_at_frame(sd_script * script,int frame_id,int sprite_id)84 int sd_script_set_sprite_at_frame(sd_script *script, int frame_id, int sprite_id) {
85     if(script == NULL || frame_id < 0 || frame_id >= script->frame_count || sprite_id < 0 || sprite_id > 25) {
86         return SD_INVALID_INPUT;
87     }
88 
89     script->frames[frame_id].sprite = sprite_id;
90     return SD_SUCCESS;
91 }
92 
sd_script_get_total_ticks(const sd_script * script)93 int sd_script_get_total_ticks(const sd_script *script) {
94     return sd_script_get_tick_pos_at_frame(script, script->frame_count);
95 }
96 
sd_script_get_tick_pos_at_frame(const sd_script * script,int frame_id)97 int sd_script_get_tick_pos_at_frame(const sd_script *script, int frame_id) {
98     if(script == NULL)
99         return 0;
100     int len = 0;
101     for(int i = 0; i < frame_id; i++) {
102         len += script->frames[i].tick_len;
103     }
104     return len;
105 }
106 
sd_script_get_tick_len_at_frame(const sd_script * script,int frame_id)107 int sd_script_get_tick_len_at_frame(const sd_script *script, int frame_id) {
108     if(script == NULL || frame_id >= script->frame_count)
109         return 0;
110     return script->frames[frame_id].tick_len;
111 }
112 
sd_script_get_sprite_at_frame(const sd_script * script,int frame_id)113 int sd_script_get_sprite_at_frame(const sd_script *script, int frame_id) {
114     if(script == NULL || frame_id >= script->frame_count)
115         return 0;
116     return script->frames[frame_id].sprite;
117 }
118 
_create_tag(sd_script_frame * frame,int number)119 static void _create_tag(sd_script_frame *frame, int number) {
120     size_t newsize = sizeof(sd_script_tag) * (number + 1);
121     frame->tags = realloc(frame->tags, newsize);
122     memset(&frame->tags[number], 0, sizeof(sd_script_tag));
123 }
124 
_create_frame(sd_script * script,int number)125 static void _create_frame(sd_script *script, int number) {
126     size_t newsize = sizeof(sd_script_frame) * (number + 1);
127     script->frames = realloc(script->frames, newsize);
128     memset(&script->frames[number], 0, sizeof(sd_script_frame));
129 }
130 
sd_script_decode(sd_script * script,const char * str,int * inv_pos)131 int sd_script_decode(sd_script *script, const char* str, int *inv_pos) {
132     if(script == NULL || str == NULL)
133         return SD_INVALID_INPUT;
134 
135     char test[4];
136     int len = strlen(str);
137     int i = 0;
138     int req_param;
139     int has_end = 0;
140     const char *desc = NULL;
141     const char *tag = NULL;
142 
143     int frame_number = 0;
144     int tag_number = 0;
145     _create_frame(script, frame_number);
146     while(i < len) {
147         if(str[i] >= 'A' && str[i] <= 'Z') {
148             // Set the length and id for this frame
149             script->frames[frame_number].sprite = str[i++] - 65;
150             script->frames[frame_number].tick_len = read_next_int(str, &i);
151             tag_number = 0;
152 
153             // Create a new frame if necessary
154             i++;
155             if(i < len) {
156                 frame_number++;
157                 _create_frame(script, frame_number);
158                 has_end = 0;
159             } else {
160                 has_end = 1;
161             }
162             script->frame_count++;
163             continue;
164         }
165         if(str[i] >= 'a' && str[i] <= 'z') {
166             int found = 0;
167             for(int k = 3; k > 0; k--) {
168                 memcpy(test, str+i, k);
169                 test[k] = 0;
170 
171                 // See if the current tag matches with anything.
172                 if(sd_tag_info(test, &req_param, &tag, &desc) == 0) {
173                     has_end = 0;
174                     i += k;
175                     found = 1;
176 
177                     // Add entry for tag
178                     sd_script_frame *frame = &script->frames[frame_number];
179                     _create_tag(frame, tag_number);
180                     frame->tag_count++;
181 
182                     // Set values
183                     frame->tags[tag_number].key = tag;
184                     frame->tags[tag_number].desc = desc;
185                     frame->tags[tag_number].has_param = req_param;
186                     if(req_param) {
187                         frame->tags[tag_number].value = read_next_int(str, &i);
188                     }
189                     tag_number++;
190                     k = 0; // Stop here.
191                 }
192             }
193             if(!found) {
194                 // Handle known filler tags
195                 if(strcmp(test, "u") == 0) { i++; continue; }
196                 if(strcmp(test, "c") == 0) { i++; continue; }
197                 if(strcmp(test, "p") == 0) { i++; continue; }
198                 if(strcmp(test, "o") == 0) { i++; continue; }
199                 if(strcmp(test, "z") == 0) { i++; continue; }
200 
201                 // Could do nothing about it.
202                 if(inv_pos != NULL)
203                     *inv_pos = i;
204                 return SD_INVALID_TAG;
205             }
206             continue;
207         }
208 
209         // Should never get here
210         i++;
211     }
212     // Make sure the last frame is complete.
213     // If we don't, do some catastrophe management
214     if(!has_end) {
215         script->frame_count += 1; // Make sure our last entry is freed
216         return SD_ANIM_INVALID_STRING;
217     }
218     return SD_SUCCESS;
219 }
220 
sd_script_encode(const sd_script * script,char * str)221 int sd_script_encode(const sd_script *script, char* str) {
222     if(script == NULL || str == NULL)
223         return SD_INVALID_INPUT;
224 
225     int s = 0;
226     for(int i = 0; i < script->frame_count; i++) {
227         sd_script_frame *frame = &script->frames[i];
228         for(int k = 0; k < frame->tag_count; k++) {
229             sd_script_tag *tag = &frame->tags[k];
230             s += sprintf(str + s, "%s", tag->key);
231             if(tag->has_param) {
232                 s += sprintf(str + s, "%d", tag->value);
233             }
234         }
235         s += sprintf(str + s, "%c%d-", frame->sprite + 65, frame->tick_len);
236     }
237 
238     // Overwrite the last '-'
239     str[--s] = 0;
240 
241     return SD_SUCCESS;
242 }
243 
sd_script_encoded_length(const sd_script * script)244 int sd_script_encoded_length(const sd_script *script) {
245     if(script == NULL)
246         return 0;
247 
248     int s = 0;
249     char tmp[20];
250     for(int i = 0; i < script->frame_count; i++) {
251         sd_script_frame *frame = &script->frames[i];
252         for(int k = 0; k < frame->tag_count; k++) {
253             sd_script_tag *tag = &frame->tags[k];
254             s += strlen(tag->key); // Tag length
255             if(tag->has_param) {
256                 s += sprintf(tmp, "%d", tag->value); // Tag value length
257             }
258         }
259         s += 2; // sprite key and the '-' char
260         s += sprintf(tmp, "%d", frame->tick_len); // Tick length char count
261     }
262     s--; // Minus the last '-'
263     return s;
264 }
265 
sd_script_get_frame_at(const sd_script * script,int ticks)266 const sd_script_frame* sd_script_get_frame_at(const sd_script *script, int ticks) {
267     if(script == NULL)
268         return NULL;
269     if(ticks < 0)
270         return NULL;
271 
272     int pos = 0;
273     int next = 0;
274     for(int i = 0; i < script->frame_count; i++) {
275         next = pos + script->frames[i].tick_len;
276         if(pos <= ticks && ticks < next) {
277             return &script->frames[i];
278         }
279         pos = next;
280     }
281     return NULL;
282 }
283 
sd_script_get_frame(const sd_script * script,int frame_number)284 const sd_script_frame* sd_script_get_frame(const sd_script *script, int frame_number) {
285     if(script == NULL || frame_number < 0 || frame_number >= script->frame_count) {
286         return NULL;
287     }
288     return &script->frames[frame_number];
289 }
290 
sd_script_frame_changed(const sd_script * script,int tick_start,int tick_stop)291 int sd_script_frame_changed(const sd_script *script, int tick_start, int tick_stop) {
292     if(script == NULL)
293         return 0;
294     if(tick_start == tick_stop)
295         return 0;
296     const sd_script_frame *frame_a = sd_script_get_frame_at(script, tick_start);
297     const sd_script_frame *frame_b = sd_script_get_frame_at(script, tick_stop);
298     return (frame_a != frame_b);
299 }
300 
sd_script_get_frame_index(const sd_script * script,const sd_script_frame * frame)301 int sd_script_get_frame_index(const sd_script *script, const sd_script_frame *frame) {
302     if(script == NULL || frame == NULL)
303         return -1;
304     for(int i = 0; i < script->frame_count; i++) {
305         if(&script->frames[i] == frame) {
306             return i;
307         }
308     }
309     return -1;
310 }
311 
sd_script_get_frame_index_at(const sd_script * script,int ticks)312 int sd_script_get_frame_index_at(const sd_script *script, int ticks) {
313     if(script == NULL || ticks < 0)
314         return -1;
315 
316     int pos = 0;
317     int next = 0;
318     for(int i = 0; i < script->frame_count; i++) {
319         next = pos + script->frames[i].tick_len;
320         if(pos <= ticks && ticks < next) {
321             return i;
322         }
323         pos = next;
324     }
325     return -1;
326 }
327 
sd_script_is_last_frame(const sd_script * script,const sd_script_frame * frame)328 int sd_script_is_last_frame(const sd_script *script, const sd_script_frame *frame) {
329     if(script == NULL)
330         return 0;
331     int index = sd_script_get_frame_index(script, frame);
332     if(index == script->frame_count-1)
333         return 1;
334     return 0;
335 }
336 
sd_script_is_last_frame_at(const sd_script * script,int ticks)337 int sd_script_is_last_frame_at(const sd_script *script, int ticks) {
338     const sd_script_frame *frame = sd_script_get_frame_at(script, ticks);
339     return sd_script_is_last_frame(script, frame);
340 }
341 
sd_script_is_first_frame(const sd_script * script,const sd_script_frame * frame)342 int sd_script_is_first_frame(const sd_script *script, const sd_script_frame *frame) {
343     if(script == NULL)
344         return 0;
345     int index = sd_script_get_frame_index(script, frame);
346     if(index == 0)
347         return 1;
348     return 0;
349 }
350 
sd_script_is_first_frame_at(const sd_script * script,int ticks)351 int sd_script_is_first_frame_at(const sd_script *script, int ticks) {
352     const sd_script_frame *frame = sd_script_get_frame_at(script, ticks);
353     return sd_script_is_first_frame(script, frame);
354 }
355 
_sd_script_get_tag(const sd_script_frame * frame,const char * tag)356 sd_script_tag* _sd_script_get_tag(const sd_script_frame* frame, const char* tag) {
357     for(int i = 0; i < frame->tag_count; i++) {
358         if(strcmp(tag, frame->tags[i].key) == 0) {
359             return &frame->tags[i];
360         }
361     }
362     return NULL;
363 }
364 
sd_script_get_tag(const sd_script_frame * frame,const char * tag)365 const sd_script_tag* sd_script_get_tag(const sd_script_frame* frame, const char* tag) {
366     if(frame == NULL || tag == NULL) {
367         return NULL;
368     }
369     return _sd_script_get_tag(frame, tag);
370 }
371 
sd_script_isset(const sd_script_frame * frame,const char * tag)372 int sd_script_isset(const sd_script_frame *frame, const char* tag) {
373     if(sd_script_get_tag(frame, tag) != NULL) {
374         return 1;
375     }
376     return 0;
377 }
378 
sd_script_get(const sd_script_frame * frame,const char * tag)379 int sd_script_get(const sd_script_frame *frame, const char* tag) {
380     const sd_script_tag *stag = sd_script_get_tag(frame, tag);
381     if(stag == NULL) {
382         return 0;
383     }
384     return stag->value;
385 }
386 
387 
sd_script_next_frame_with_sprite(const sd_script * script,int sprite_id,int current_tick)388 int sd_script_next_frame_with_sprite(const sd_script *script, int sprite_id, int current_tick) {
389     if(script == NULL)
390         return -1;
391     if(sprite_id < 0)
392         return -1;
393     if(current_tick > sd_script_get_total_ticks(script))
394         return -1;
395 
396     int pos = 0;
397     int next = 0;
398     for(int i = 0; i < script->frame_count; i++) {
399         next = pos + script->frames[i].tick_len;
400         if(current_tick < pos && sprite_id == script->frames[i].sprite) {
401             return i;
402         }
403         pos = next;
404     }
405 
406     return -1;
407 }
408 
sd_script_next_frame_with_tag(const sd_script * script,const char * tag,int current_tick)409 int sd_script_next_frame_with_tag(const sd_script *script, const char* tag, int current_tick) {
410     if(script == NULL || tag == NULL)
411         return -1;
412     if(current_tick > sd_script_get_total_ticks(script))
413         return -1;
414 
415     int pos = 0;
416     int next = 0;
417     for(int i = 0; i < script->frame_count; i++) {
418         next = pos + script->frames[i].tick_len;
419         if(current_tick < pos && sd_script_isset(&script->frames[i], tag)) {
420             return i;
421         }
422         pos = next;
423     }
424 
425     return -1;
426 }
427 
sd_script_delete_tag(sd_script * script,int frame_id,const char * tag)428 int sd_script_delete_tag(sd_script *script, int frame_id, const char* tag) {
429     if(script == NULL || tag == NULL)
430         return SD_INVALID_INPUT;
431     if(frame_id < 0 || frame_id >= script->frame_count)
432         return SD_INVALID_INPUT;
433 
434     sd_script_frame *frame = &script->frames[frame_id];
435     if(frame->tag_count <= 0) {
436         return SD_SUCCESS;
437     }
438 
439     // Find the tag.
440     int tag_num = -1;
441     for(int i = 0; i < frame->tag_count; i++) {
442         if(strcmp(frame->tags[i].key, tag) == 0) {
443             tag_num = i;
444             break;
445         }
446     }
447     if(tag_num < 0) {
448         return SD_SUCCESS; // No tag, stop here
449     }
450 
451     // Move if this is not the last entry
452     if(tag_num + 1 < frame->tag_count) {
453         void *dst = frame->tags + tag_num;
454         void *src = frame->tags + tag_num + 1;
455         size_t len = (frame->tag_count - tag_num - 1) * sizeof(sd_script_tag);
456         memmove(dst, src, len);
457     }
458     frame->tag_count--;
459     return SD_SUCCESS;
460 }
461 
sd_script_set_tag(sd_script * script,int frame_id,const char * tag,int value)462 int sd_script_set_tag(sd_script *script, int frame_id, const char* tag, int value) {
463     if(script == NULL || tag == NULL)
464         return SD_INVALID_INPUT;
465     if(frame_id < 0 || frame_id >= script->frame_count)
466         return SD_INVALID_INPUT;
467 
468     // Get frame
469     sd_script_frame *frame = &script->frames[frame_id];
470 
471     // Get tag information
472     const char *r_tag;
473     const char *r_desc;
474     int req_param;
475     if(sd_tag_info(tag, &req_param, &r_tag, &r_desc) != SD_SUCCESS) {
476         return SD_INVALID_INPUT;
477     }
478 
479     // See if old tag has been set
480     sd_script_tag *old_tag = _sd_script_get_tag(frame, r_tag);
481     if(old_tag == NULL) {
482         // If the tag does not exist in the frame, set it and its value (if value is required.)
483         _create_tag(frame, frame->tag_count);
484         frame->tags[frame->tag_count].key = r_tag;
485         frame->tags[frame->tag_count].desc = r_desc;
486         frame->tags[frame->tag_count].has_param = req_param;
487         frame->tags[frame->tag_count].value = value;
488         frame->tag_count++;
489     } else if(req_param) {
490         // If the tag exists and requires a value, set it.
491         old_tag->value = value;
492         old_tag->has_param = 1;
493     }
494 
495     return SD_SUCCESS;
496 }
497 
sd_script_letter_to_frame(char letter)498 int sd_script_letter_to_frame(char letter) {
499     return (int)(letter - 'A');
500 }
501 
sd_script_frame_to_letter(int frame_id)502 char sd_script_frame_to_letter(int frame_id) {
503     return (char)(frame_id + 'A');
504 }
505