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