1 /*! \file
2  * \brief Animation string parser
3  * \details Functions and structs for reading, writing and modifying OMF:2097 animation strings.
4  * \copyright MIT license.
5  * \date 2013-2014
6  * \author Andrew Thompson
7  * \author Tuomas Virtanen
8  */
9 
10 #ifndef _SD_SCRIPT_H
11 #define _SD_SCRIPT_H
12 
13 #include "shadowdive/taglist.h"
14 
15 #ifdef __cplusplus
16 extern "C" {
17 #endif
18 
19 /*! \brief Animation tag
20  *
21  * Describes a single tag in animation frame.
22  */
23 typedef struct {
24     const char* key;  ///< Tag name
25     const char* desc; ///< Tag description
26     int has_param;    ///< Tells if the tag has a parameter
27     int value;        ///< Tag parameter value. Only valid if has_param = 1.
28 } sd_script_tag;
29 
30 /*! \brief Animation frame
31  *
32  * Describes a single frame in animation string.
33  */
34 typedef struct {
35     int sprite;          ///< Sprite ID that the frame relates to
36     int tick_len;        ///< Length of the frame in ticks
37     int tag_count;       ///< Amount of tags in this frame
38     sd_script_tag *tags; ///< A list of tags in this frame
39 } sd_script_frame;
40 
41 /*! \brief Animation script
42  *
43  * A single animation string. Contains multiple frames, which then contain tags.
44  * A valid string must contain at least a single frame.
45  */
46 typedef struct {
47     int frame_count;          ///< Amount of frames in the string
48     sd_script_frame *frames;  ///< List of frames in this string
49 } sd_script;
50 
51 /*! \brief Initialize script parser
52  *
53  * Initializes the script parser with empty values.
54  *
55  * \retval SD_INVALID_INPUT Script struct pointer was NULL
56  * \retval SD_SUCCESS Success.
57  *
58  * \param script Allocated script struct pointer.
59  */
60 int sd_script_create(sd_script *script);
61 
62 /*! \brief Free script parser
63  *
64  * Frees up all memory reserved by the script parser structure.
65  * All contents will be freed, all pointers to contents will be invalid.
66  *
67  * \param script Script struct to free.
68  */
69 void sd_script_free(sd_script *script);
70 
71 /*! \brief Decode animation string
72  *
73  * Decodes an animation string to frames and tags. There must be at least
74  * a single full frame, which means a frame number and length in ticks.
75  *
76  * \retval SD_ANIM_INVALID_STRING String is invalid; eg. doesn't have enough complete frames.
77  * \retval SD_INVALID_TAG There was an invalid tag in string. invalid_pos will contain problematic position.
78  * \retval SD_SUCCESS Decoding was successful.
79  *
80  * \param script Script structure to fill. Must be formatted using sd_script_create().
81  * \param str Animation string to parse
82  * \param invalid_pos Will contain problematic position in string if SD_INVALID_TAG is returned. Will be ignored if set to NULL.
83  */
84 int sd_script_decode(sd_script *script, const char* str, int *invalid_pos);
85 
86 /*! \brief Encode animation string
87  *
88  * Encodes the animation script structure back to an animation string.
89  * Note that if the decoded string contained oddities like prefixed zeroes in
90  * front of numbers, those will be automatically fixed. Therefore the output
91  * string may not be exactly like the input string. Filler tags (u,c,p,o,z)
92  * will also be removed.
93  *
94  * Note that the target str buffer needs to be large enough for the encoding operation.
95  * It is possible to find the encoded string buffer size by using sd_script_encoded_lenghth() function.
96  *
97  * \sa sd_script_encoded_length
98  *
99  * \retval SD_INVALID_INPUT Script or str parameter was NULL
100  * \retval SD_SUCCESS Successful operation
101  *
102  * \param script Script structure to encode
103  * \param str Target string buffer. Make sure it's large enough!
104  */
105 int sd_script_encode(const sd_script *script, char* str);
106 
107 /*! \brief Find the encoded length of a script
108  *
109  * Returns the encoded length of the animation script. The length will be the EXACT size
110  * of the string, so you may need to add +1 to compensate for a trailing zero.
111  *
112  * The function will return 0 if there are no frames in the script, the frames are invalid,
113  * or the script variable is NULL.
114  *
115  * \sa sd_script_decode
116  *
117  * \param script Script structure to check
118  * \return Length of the encoded animation string.
119  */
120 int sd_script_encoded_length(const sd_script *script);
121 
122 /*! \brief Find the total duration of the script
123  *
124  * Finds the total duration of the script in game ticks. Essentially
125  * just adds up all the frame lengths.
126  *
127  * \param script Script structure to check
128  * \return The total duration of the script in game ticks
129  */
130 int sd_script_get_total_ticks(const sd_script *script);
131 
132 /*! \brief Find the tick position at the start of the given frame.
133  *
134  * \param script Script structure to check
135  * \param frame_id Frame ID to search
136  * \return Tick position at the frame
137  */
138 int sd_script_get_tick_pos_at_frame(const sd_script *script, int frame_id);
139 
140 /*! \brief Find the tick length of the given frame
141  *
142  * \param script Script structure to check
143  * \param frame_id Frame ID to search
144  * \return Tick length of the frame, 0 if frame_id does not exist.
145  */
146 int sd_script_get_tick_len_at_frame(const sd_script *script, int frame_id);
147 
148 /*! \brief Find the sprite ID of the given frame
149  *
150  * \param script Script structure to check
151  * \param frame_id Frame ID to search
152  * \return Sprite ID of the given frame, 0 if frame_id does not exist.
153  */
154 int sd_script_get_sprite_at_frame(const sd_script *script, int frame_id);
155 
156 /*! \brief Returns the frame at a given tick
157  *
158  * Finds the frame at the given moment in time (tick). If the ticks value is either
159  * larger than the total duration of the script or a negative value, the function will return NULL.
160  * If script variable is NULL, the function will also return NULL. Otherwise the function will
161  * return the frame at the moment of the given tick.
162  *
163  * The tick values will start at 0, so if the tick length of a frame is 140, the last tick will be 139.
164  *
165  * The returned frame is owned by the library; do not attempt to free it.
166  *
167  * \param script The script structure to read
168  * \param ticks A position in time in game ticks
169  * \return Frame pointer or NULL
170  */
171 const sd_script_frame* sd_script_get_frame_at(const sd_script *script, int ticks);
172 
173 /*! \brief Returns the frame at a given position
174  *
175  * Returns the frame at a given position. If the frame number given is less than 0
176  * or larger or equal than the number of frames, NULL will be returned. A NULL value
177  * for the script pointer will also cause a NULL to be returned. Otherwise the function
178  * will return the frame at the given position.
179  *
180  * The returned frame is owned by the library; do not attempt to free it.
181  *
182  * \param script The script structure to read
183  * \param frame_number Frame number to get
184  * \return Frame pointer or NULL
185  */
186 const sd_script_frame* sd_script_get_frame(const sd_script *script, int frame_number);
187 
188 /*! \brief Returns the information of a tag in a frame.
189  *
190  * Returns the tag descriptor for the instance of a tag in frame. A NULL for either
191  * parameter will result in a NULL being returned. If the requested tag does not
192  * exist in the frame, a NULL will be also returned.
193  *
194  * The returned tag information struct is owned by the library; do not attempt to free it.
195  *
196  * \param frame Frame structure to read
197  * \param tag Tag to look for. Must have a trailing zero.
198  * \return Tag descriptor pointer or NULL
199  */
200 const sd_script_tag* sd_script_get_tag(const sd_script_frame* frame, const char* tag);
201 
202 /*! \brief Tells if the frame changed between two points in time
203  *
204  * Tells if the frame changed between two game ticks. If the tick times are equal,
205  * if frame struct pointer is null, or if the frame didn't change, 0 will be returned.
206  * If the frame changed, 1 will be returned.
207  *
208  * Note that a change between zero and negative value will also count as a frame change.
209  * Eg. switching between "animation not started" and first frame wil lcause 1.
210  * Also, change from a tick in animation range to a tick outside of animation will cause
211  * 1 to be returned. However, a change from "not started" (-1) to "finished" (tick > total_ticks)
212  * will cause 0 to be returned.
213  *
214  * \param script The script structure to inspect
215  * \param tick_start Position 1
216  * \param tick_stop Position 2
217  * \return 1 or 0, whether the frame changed or not.
218  */
219 int sd_script_frame_changed(const sd_script *script, int tick_start, int tick_stop);
220 
221 /*! \brief Returns the array index of frame
222  *
223  * This simply returns the array index of the given frame. Comparison to the frames
224  * in the script will be done by pointer. If the frame does not exist in the animation,
225  * -1 will be returned.
226  *
227  * \param script The script structure to inspect
228  * \param frame Frame structure to find the index of
229  * \return Index of the frame
230  */
231 int sd_script_get_frame_index(const sd_script *script, const sd_script_frame *frame);
232 
233 /*! \brief Returns the array index of frame at given tick position
234  *
235  * Returns the array index of the frame at a given tick position. If the tick is valid
236  * and in the range of the animation, an index will be returned. If the tick is outside
237  * the animation range, -1 will be returned.
238  *
239  * \param script The script structure to inspect
240  * \param ticks Tick position to find
241  * \return Index of the frame
242  */
243 int sd_script_get_frame_index_at(const sd_script *script, int ticks);
244 
245 /*! \brief Tells if the frame is the last frame in animation.
246  *
247  * Tells if the given frame pointer is the last frame in the given animation.
248  * If script or frame pointer is null, or frame is not the last frame, 0 will be returned.
249  * Otherwise 1 will be returned.
250  *
251  * \param script The script structure to inspect
252  * \param frame Frame structure to find
253  * \return 1 or 0
254  */
255 int sd_script_is_last_frame(const sd_script *script, const sd_script_frame *frame);
256 
257 /*! \brief Tells if the frame index is the last frame in animation.
258  *
259  * Tells if the given frame index is the last frame in the given animation.
260  * If script pointer is null, or frame is not the last frame, 0 will be returned.
261  * Otherwise 1 will be returned.
262  *
263  * \param script The script structure to inspect
264  * \param ticks Tick position to find
265  * \return 1 or 0
266  */
267 int sd_script_is_last_frame_at(const sd_script *script, int ticks);
268 
269 /*! \brief Tells if the frame is the first frame in animation.
270  *
271  * Tells if the given frame pointer is the first frame in the given animation.
272  * If script or frame pointer is null, or frame is not the first frame, 0 will be returned.
273  * Otherwise 1 will be returned.
274  *
275  * \param script The script structure to inspect
276  * \param frame Frame structure to find
277  * \return 1 or 0
278  */
279 int sd_script_is_first_frame(const sd_script *script, const sd_script_frame *frame);
280 
281 /*! \brief Tells if the frame index is the first frame in animation.
282  *
283  * Tells if the given frame index is the first frame in the given animation.
284  * If script pointer is null, or frame is not the first frame, 0 will be returned.
285  * Otherwise 1 will be returned.
286  *
287  * \param script The script structure to inspect
288  * \param ticks Tick position to find
289  * \return 1 or 0
290  */
291 int sd_script_is_first_frame_at(const sd_script *script, int ticks);
292 
293 /*! \brief Tells if the tag is set in frame
294  *
295  * Tells if the given tag is set in the given frame, Returns 1 if tag is set,
296  * otherwise 0.
297  *
298  * \param frame The frame structure to inspect
299  * \param tag Tag name to find
300  * \return 1 or 0
301  */
302 int sd_script_isset(const sd_script_frame *frame, const char* tag);
303 
304 /*! \brief Returns the tag value in frame
305  *
306  * Returns the parameter value of a tag in a given frame. Note that if the tag doesn't
307  * exist in frame, or tag doesn't have a parameter value, 0 will be returned. Otherwise
308  * the tag parameter value will be returned.
309  *
310  * \param frame The frame structure to inspect
311  * \param tag Tag name to find
312  * \return Tag parameter value or 0.
313  */
314 int sd_script_get(const sd_script_frame *frame, const char* tag);
315 
316 /*! \brief Returns the next frame number with a given sprite ID
317  *
318  * Returns the next frame number with the given sprite number. Sprite numbers start from 0 and go to
319  * the amount of sprites in the frame.
320  *
321  * If script is NULL, sprite_id is smaller than 0 or larger than the amount of sprites in the
322  * script, or current_tick is larger than the length of the script animation, or sprite frame
323  * with the sprite_id does not exist in script, -1 value will be returned. Otherwise
324  * the frame number will be returned.
325  *
326  * The search will start from the next frame from the one that current_tick is pointing at.
327  *
328  * \param script Script structure to search through
329  * \param sprite_id Sprite ID to search for
330  * \param current_tick Current tick time
331  * \return Frame ID or -1 on error
332  */
333 int sd_script_next_frame_with_sprite(const sd_script *script, int sprite_id, int current_tick);
334 
335 /*! \brief Returns the next frame number with a given tag
336  *
337  * Returns the next frame number with the given tag.
338  *
339  * If script or tag is NULL, current_tick is larger than the length of the script animation,
340  * or sprite frame with the tag does not exist in script, -1 value will be returned. Otherwise
341  * the frame number will be returned.
342  *
343  * The search will start from the next frame from the one that current_tick is pointing at.
344  *
345  * \param script Script structure to search through
346  * \param tag Tag to search for
347  * \param current_tick Current tick time
348  * \return Frame ID or -1 on error
349  */
350 int sd_script_next_frame_with_tag(const sd_script *script, const char* tag, int current_tick);
351 
352 /*! \brief Sets a tag for the given frame
353  *
354  * Sets the tag for the given frame. If the tag has not been set previously, a new tag
355  * entry will be created. If the tag has been set previously, and the tag accepts value
356  * parameters, a new parameter will be set for the tag. Otherwise this function does nothing.
357  *
358  * \retval SD_SUCCESS On succesful tag set operation
359  * \retval SD_INVALID_INPUT if script or tag is NULL, tag does not exist or frame does not exist.
360  *
361  * \param script Script to modify
362  * \param frame_id Frame ID to modify
363  * \param tag Tag to add
364  * \param value Value to set, if tag allows values.
365  */
366 int sd_script_set_tag(sd_script *script, int frame_id, const char* tag, int value);
367 
368 /*! \brief Deletes a tag from the given frame
369  *
370  * Deletes the tag from the given frame. If the tag does not exist, does nothing.
371  *
372  * \retval SD_SUCCESS On succesful tag set operation
373  * \retval SD_INVALID_INPUT if script or tag is NULL or the frame does not exist.
374  *
375  * \param script Script to modify
376  * \param frame_id Frame ID to modify
377  * \param tag Tag to delete
378  */
379 int sd_script_delete_tag(sd_script *script, int frame_id, const char* tag);
380 
381 /*! \brief Append a new frame to the end of the frame list
382  *
383  * Appends a new frame to the end of the frame list, and sets its tick length and sprite ID.
384  * Tag list will be empty after creation.
385  *
386  * \retval SD_SUCCESS on successful frame creation
387  * \retval SD_INVALID_INPUT if script is NULL
388  *
389  * \param script Script to modify
390  * \param tick_len Tick length of the new frame
391  * \param sprite_id Sprite ID for the new frame
392  */
393 int sd_script_append_frame(sd_script *script, int tick_len, int sprite_id);
394 
395 /*! \brief Clear frame tags
396  *
397  * Clears all tags from the given frame.
398  *
399  * \retval SD_SUCCESS on successful removal
400  * \retval SD_INVALID_INPUT if script is NULL or if frame_id is nonexistent
401  *
402  * \param script Script to modify
403  * \param frame_id Valid frame ID with tags to clear
404  */
405 int sd_script_clear_tags(sd_script *script, int frame_id);
406 
407 /*! \brief Set frame tick length
408  *
409  * Sets a new tick length for the given frame.
410  *
411  * \retval SD_SUCCESS on successful operation
412  * \retval SD_INVALID_INPUT if script is NULL or frame_id is nonexistent
413  *
414  * \param script Script to modify
415  * \param frame_id Frame ID to set tick length to
416  * \param duration Tick length to set
417  */
418 int sd_script_set_tick_len_at_frame(sd_script *script, int frame_id, int duration);
419 
420 /*! \brief Set frame sprite ID
421  *
422  * Sets the sprite ID of the given frame.
423  *
424  * \retval SD_SUCCESS on successful operation
425  * \retval SD_INVALID_INPUT if script is NULL, frame_id is invalid or sprite_id is invalid.
426  *
427  * \param script Script to modify
428  * \param frame_id Frame ID to set sprite ID to
429  * \param sprite_id Sprite ID to set
430  */
431 int sd_script_set_sprite_at_frame(sd_script *script, int frame_id, int sprite_id);
432 
433 /** \brief Returns the frame ID by frame letter.
434  *
435  * \param letter Frame letter ('A', 'B', etc.)
436  * \return Frame ID ('A' => 0, ...)
437  */
438 int sd_script_letter_to_frame(char letter);
439 
440 /** \brief Returns the frame letter by frame ID.
441  *
442  * \param frame_id Frame ID (0 ... n)
443  * \return Frame letter (0 => 'A', ...)
444  */
445 char sd_script_frame_to_letter(int frame_id);
446 
447 #ifdef __cplusplus
448 }
449 #endif
450 
451 #endif // _SD_SCRIPT_H