nuclear@9: #include nuclear@9: #include nuclear@9: #include nuclear@9: #include nuclear@9: #include nuclear@9: #include nuclear@9: #include "dsys.h" nuclear@9: #include "dsys_impl.h" nuclear@9: nuclear@9: static int read_script(struct dsys_demo *demo, FILE *fp, const char *fname); nuclear@9: static char *strip_ws(char *buf); nuclear@9: static void dbg_print_events(struct dsys_event *ev); nuclear@9: nuclear@9: static void proc_event(struct dsys_event *ev, demotime_t tm); nuclear@9: static void link_callback(struct dsys_event *ev, void *cls); nuclear@9: static void free_event(struct dsys_event *ev); nuclear@9: nuclear@9: static struct dsys_event *sort_evlist(struct dsys_event *list, int num_ev); nuclear@9: static struct dsys_event *merge_evlists(struct dsys_event *list1, struct dsys_event *list2); nuclear@9: nuclear@9: nuclear@9: struct dsys_demo *dsys_open(const char *fname) nuclear@9: { nuclear@9: FILE *fp; nuclear@9: struct dsys_demo *demo; nuclear@9: nuclear@9: if(!(fp = fopen(fname, "r"))) { nuclear@9: fprintf(stderr, "failed to open demoscript: %s: %s\n", fname, strerror(errno)); nuclear@9: return 0; nuclear@9: } nuclear@9: nuclear@9: if(!(demo = malloc(sizeof *demo))) { nuclear@9: perror("failed to allocate memory"); nuclear@9: fclose(fp); nuclear@9: return 0; nuclear@9: } nuclear@9: memset(demo, 0, sizeof *demo); nuclear@9: nuclear@9: demo->src_tm = demo->start_tm = -1; nuclear@9: nuclear@9: if(read_script(demo, fp, fname) == -1) { nuclear@9: free(demo); nuclear@9: fclose(fp); nuclear@9: return 0; nuclear@9: } nuclear@9: nuclear@9: fclose(fp); nuclear@9: return demo; nuclear@9: } nuclear@9: nuclear@9: struct dsys_demo *dsys_open_stream(FILE *fp) nuclear@9: { nuclear@9: struct dsys_demo *demo; nuclear@9: nuclear@9: if(!(demo = malloc(sizeof *demo))) { nuclear@9: perror("failed to allocate memory"); nuclear@9: return 0; nuclear@9: } nuclear@9: memset(demo, 0, sizeof *demo); nuclear@9: nuclear@9: demo->src_tm = demo->start_tm = -1; nuclear@9: nuclear@9: if(read_script(demo, fp, 0) == -1) { nuclear@9: free(demo); nuclear@9: return 0; nuclear@9: } nuclear@9: nuclear@9: return demo; nuclear@9: } nuclear@9: nuclear@9: void dsys_close(struct dsys_demo *demo) nuclear@9: { nuclear@9: while(demo->evlist) { nuclear@9: struct dsys_event *ev = demo->evlist; nuclear@9: demo->evlist = demo->evlist->next; nuclear@9: free_event(ev); nuclear@9: } nuclear@9: nuclear@9: free(demo); nuclear@9: } nuclear@9: nuclear@9: nuclear@9: #define SEP " \t\n\r" nuclear@9: nuclear@9: static int read_script(struct dsys_demo *demo, FILE *fp, const char *fname) nuclear@9: { nuclear@9: int nline = 0; nuclear@9: char buf[512], *line, *tok, *endp; nuclear@9: unsigned int t0, t1; nuclear@9: struct dsys_event *ev; nuclear@9: nuclear@9: if(!fname) { nuclear@9: fname = ""; nuclear@9: } nuclear@9: nuclear@9: demo->duration = dsys_msec_to_dtime(0); nuclear@9: nuclear@9: while(fgets(buf, sizeof buf, fp)) { nuclear@9: nline++; nuclear@9: nuclear@9: line = strip_ws(buf); nuclear@9: nuclear@9: if(!line || !*line) { nuclear@9: continue; nuclear@9: } nuclear@9: nuclear@9: if(!(tok = strtok(line, SEP)) || (t0 = strtol(tok, &endp, 10), endp == tok)) { nuclear@9: fprintf(stderr, "%s line: %d, error: expected timestamp t0\n", fname, nline); nuclear@9: return -1; nuclear@9: } nuclear@9: nuclear@9: if(!(tok = strtok(0, SEP))) { nuclear@9: fprintf(stderr, "%s line: %d, error: expected second timestamp or event name\n", fname, nline); nuclear@9: return -1; nuclear@9: } nuclear@9: nuclear@9: t1 = strtol(tok, &endp, 10); nuclear@9: if(endp == tok) { nuclear@9: t1 = t0; nuclear@9: } else { nuclear@9: if(!(tok = strtok(0, SEP))) { nuclear@9: fprintf(stderr, "%s line: %d, error: expected event name\n", fname, nline); nuclear@9: return -1; nuclear@9: } nuclear@9: } nuclear@9: nuclear@9: if(!(ev = malloc(sizeof *ev))) { nuclear@9: perror("read_script: failed to allocate memory for an event\n"); nuclear@9: return -1; nuclear@9: } nuclear@9: memset(ev, 0, sizeof *ev); nuclear@9: ev->t0 = dsys_msec_to_dtime(t0); nuclear@9: ev->t1 = dsys_msec_to_dtime(t1); nuclear@9: nuclear@9: if(!(ev->name = malloc(strlen(tok) + 1))) { nuclear@9: free(ev); nuclear@9: fprintf(stderr, "read_script: failed to allocate memory for the event name: %s\n", tok); nuclear@9: return -1; nuclear@9: } nuclear@9: strcpy(ev->name, tok); nuclear@9: nuclear@9: ev->eval = t0 == t1 ? dsys_eval_step : dsys_eval_lerp; nuclear@9: nuclear@9: ev->next = demo->evlist; nuclear@9: ev->prev = 0; nuclear@9: if(demo->evlist) { nuclear@9: demo->evlist->prev = ev; nuclear@9: } nuclear@9: demo->evlist = ev; nuclear@9: demo->num_ev++; nuclear@9: nuclear@9: if(ev->t1 > demo->duration) { nuclear@9: demo->duration = ev->t1; nuclear@9: } nuclear@9: } nuclear@9: nuclear@9: demo->evlist = sort_evlist(demo->evlist, demo->num_ev); nuclear@9: nuclear@9: /*dbg_print_events(demo->evlist);*/ nuclear@9: nuclear@9: return 0; nuclear@9: } nuclear@9: nuclear@9: static char *strip_ws(char *buf) nuclear@9: { nuclear@9: char *ptr; nuclear@9: nuclear@9: while(isspace(*buf)) { nuclear@9: buf++; nuclear@9: } nuclear@9: nuclear@9: ptr = buf; nuclear@9: while(*ptr) { nuclear@9: if(*ptr == '\n' || *ptr == '\r' || *ptr == '#') { nuclear@9: *ptr = 0; nuclear@9: break; nuclear@9: } nuclear@9: ptr++; nuclear@9: } nuclear@9: nuclear@9: return buf; nuclear@9: } nuclear@9: nuclear@9: static void dbg_print_events(struct dsys_event *ev) nuclear@9: { nuclear@9: int i; nuclear@9: nuclear@9: for(i=0; ev; i++) { nuclear@9: printf("%02d - %s (%f -> %f) [%s]\n", i, ev->eval == dsys_eval_step ? "step" : "lerp", nuclear@9: ev->t0, ev->t1, ev->name); nuclear@9: ev = ev->next; nuclear@9: } nuclear@9: } nuclear@9: nuclear@9: void dsys_update(struct dsys_demo *demo, demotime_t tm) nuclear@9: { nuclear@9: struct dsys_event *ev; nuclear@9: nuclear@9: demo->src_tm = tm; nuclear@9: nuclear@9: if(demo->start_tm == -1) { nuclear@9: dsys_start(demo); nuclear@9: } nuclear@9: nuclear@9: if(!demo->running) { nuclear@9: return; /* nothing changes */ nuclear@9: } nuclear@9: nuclear@9: demo->tm = tm - demo->start_tm - demo->stoppage_tm; nuclear@9: nuclear@9: if(demo->tm < 0) { nuclear@9: demo->tm = 0; nuclear@9: } nuclear@9: if(demo->tm > demo->duration) { nuclear@9: demo->tm = demo->duration; nuclear@9: } nuclear@9: nuclear@9: while(demo->active && demo->active->t1 <= demo->tm) { nuclear@9: proc_event(demo->active, demo->tm); nuclear@9: demo->active = demo->active->next; nuclear@9: } nuclear@9: nuclear@9: ev = demo->active; nuclear@9: while(ev && ev->t0 <= demo->tm) { nuclear@9: proc_event(ev, demo->tm); nuclear@9: ev = ev->next; nuclear@9: } nuclear@9: demo->nextev = ev; nuclear@9: nuclear@9: nuclear@9: if(demo->tm >= demo->duration) { nuclear@9: dsys_stop(demo); nuclear@9: } nuclear@9: } nuclear@9: nuclear@9: static void proc_event(struct dsys_event *ev, demotime_t tm) nuclear@9: { nuclear@9: float val = ev->eval(ev, tm); nuclear@9: nuclear@9: if(ev->val != val) { nuclear@9: struct callback *cb = ev->cblist; nuclear@9: nuclear@9: ev->val = val; nuclear@9: nuclear@9: while(cb) { nuclear@9: cb->func(ev, cb->cls); nuclear@9: cb = cb->next; nuclear@9: } nuclear@9: } nuclear@9: } nuclear@9: nuclear@9: void dsys_start(struct dsys_demo *demo) nuclear@9: { nuclear@9: if(demo->running) { nuclear@9: return; nuclear@9: } nuclear@9: nuclear@9: if(demo->start_tm == -1) { nuclear@9: demo->start_tm = demo->src_tm; nuclear@9: demo->nextev = demo->active = demo->evlist; nuclear@9: } else { nuclear@9: demo->stoppage_tm += demo->src_tm - demo->stop_tm; nuclear@9: } nuclear@9: nuclear@9: demo->running = 1; nuclear@9: } nuclear@9: nuclear@9: void dsys_stop(struct dsys_demo *demo) nuclear@9: { nuclear@9: if(!demo->running) { nuclear@9: return; nuclear@9: } nuclear@9: nuclear@9: demo->stop_tm = demo->src_tm; nuclear@9: demo->running = 0; nuclear@9: } nuclear@9: nuclear@9: int dsys_is_running(struct dsys_demo *demo) nuclear@9: { nuclear@9: return demo->running; nuclear@9: } nuclear@9: nuclear@9: nuclear@9: demotime_t dsys_duration(struct dsys_demo *demo) nuclear@9: { nuclear@9: return demo->duration; nuclear@9: } nuclear@9: nuclear@9: demotime_t dsys_time(struct dsys_demo *demo) nuclear@9: { nuclear@9: return demo->tm; nuclear@9: } nuclear@9: nuclear@9: float dsys_progress(struct dsys_demo *demo) nuclear@9: { nuclear@9: return demo->tm / demo->duration; nuclear@9: } nuclear@9: nuclear@9: /* seek without continuity */ nuclear@9: void dsys_seek(struct dsys_demo *demo, demotime_t tm) nuclear@9: { nuclear@9: struct dsys_event *ev; nuclear@9: nuclear@9: if(tm < 0) { nuclear@9: tm = 0; nuclear@9: } nuclear@9: if(tm > demo->duration) { nuclear@9: tm = demo->duration; nuclear@9: } nuclear@9: nuclear@9: if(tm < demo->tm) { nuclear@9: /* on backwards seek, invalidate the sliding window */ nuclear@9: demo->nextev = demo->active = demo->evlist; nuclear@9: } nuclear@9: nuclear@9: demo->start_tm = demo->src_tm - tm; nuclear@9: demo->stoppage_tm = 0; nuclear@9: demo->stop_tm = demo->src_tm; nuclear@9: demo->tm = tm; nuclear@9: nuclear@9: /* recalculate events */ nuclear@9: ev = demo->evlist; nuclear@9: while(ev) { nuclear@9: proc_event(ev, tm); nuclear@9: ev = ev->next; nuclear@9: } nuclear@9: } nuclear@9: nuclear@9: void dsys_seek_norm(struct dsys_demo *demo, float t) nuclear@9: { nuclear@9: dsys_seek(demo, t * demo->duration); nuclear@9: } nuclear@9: nuclear@9: /* seek by accelerating time */ nuclear@9: void dsys_warp(struct dsys_demo *demo, demotime_t tm) nuclear@9: { nuclear@9: fprintf(stderr, "dsys_warp not implemented yet\n"); nuclear@9: } nuclear@9: nuclear@9: void dsys_warp_norm(struct dsys_demo *demo, float t) nuclear@9: { nuclear@9: dsys_warp(demo, t * demo->duration); nuclear@9: } nuclear@9: nuclear@9: nuclear@9: /* events */ nuclear@9: struct dsys_event *dsys_event(struct dsys_demo *demo, const char *name) nuclear@9: { nuclear@9: struct dsys_event *iter = demo->evlist; nuclear@9: nuclear@9: while(iter) { nuclear@9: if(strcmp(iter->name, name) == 0) { nuclear@9: return iter; nuclear@9: } nuclear@9: iter = iter->next; nuclear@9: } nuclear@9: return 0; nuclear@9: } nuclear@9: nuclear@11: void dsys_set_event_eval(struct dsys_event *ev, float (*eval)(struct dsys_event*, demotime_t)) nuclear@11: { nuclear@11: ev->eval = eval; nuclear@11: } nuclear@11: nuclear@9: enum dsys_evtype dsys_event_type(struct dsys_event *ev) nuclear@9: { nuclear@9: return ev->type; nuclear@9: } nuclear@9: nuclear@9: float dsys_event_value(struct dsys_event *ev) nuclear@9: { nuclear@9: return ev->val; nuclear@9: } nuclear@9: nuclear@9: int dsys_event_callback(struct dsys_event *ev, void (*func)(struct dsys_event*, void*), void *cls) nuclear@9: { nuclear@9: struct callback *cb; nuclear@9: nuclear@9: if(!(cb = malloc(sizeof *cb))) { nuclear@9: perror("failed to allocate memory"); nuclear@9: return -1; nuclear@9: } nuclear@9: cb->func = func; nuclear@9: cb->cls = cls; nuclear@9: cb->next = ev->cblist; nuclear@9: ev->cblist = cb; nuclear@9: return 0; nuclear@9: } nuclear@9: nuclear@9: int dsys_event_link(struct dsys_event *ev, float *link) nuclear@9: { nuclear@9: return dsys_event_callback(ev, link_callback, link); nuclear@9: } nuclear@9: nuclear@9: static void link_callback(struct dsys_event *ev, void *cls) nuclear@9: { nuclear@9: *(float*)cls = ev->val; nuclear@9: } nuclear@9: nuclear@9: nuclear@9: /* time conversion */ nuclear@9: demotime_t dsys_sec_to_dtime(float sec) nuclear@9: { nuclear@9: return sec; nuclear@9: } nuclear@9: nuclear@9: demotime_t dsys_msec_to_dtime(unsigned long msec) nuclear@9: { nuclear@9: return (demotime_t)msec / 1000.0; nuclear@9: } nuclear@9: nuclear@9: float dsys_dtime_to_sec(demotime_t tm) nuclear@9: { nuclear@9: return tm; nuclear@9: } nuclear@9: nuclear@9: unsigned long dsys_dtime_to_msec(demotime_t tm) nuclear@9: { nuclear@9: return (unsigned long)(tm * 1000.0); nuclear@9: } nuclear@9: nuclear@9: nuclear@9: float dsys_eval_step(struct dsys_event *ev, demotime_t t) nuclear@9: { nuclear@9: return t >= ev->t1 ? 1.0 : 0.0; nuclear@9: } nuclear@9: nuclear@9: #define CLAMP(x, low, high) ((x) < (low) ? (low) : ((x) > (high) ? (high) : (x))) nuclear@9: nuclear@9: float dsys_eval_lerp(struct dsys_event *ev, demotime_t t) nuclear@9: { nuclear@9: float res = (t - ev->t0) / (ev->t1 - ev->t0); nuclear@9: return CLAMP(res, 0.0, 1.0); nuclear@9: } nuclear@9: nuclear@9: float dsys_eval_sigmoid(struct dsys_event *ev, demotime_t t) nuclear@9: { nuclear@9: t = dsys_eval_lerp(ev, t); nuclear@9: return 1.0 - (cos(t * M_PI) * 0.5 + 0.5); nuclear@9: } nuclear@9: nuclear@9: static void free_event(struct dsys_event *ev) nuclear@9: { nuclear@9: while(ev->cblist) { nuclear@9: struct callback *cb = ev->cblist; nuclear@9: ev->cblist = ev->cblist->next; nuclear@9: free(cb); nuclear@9: } nuclear@9: } nuclear@9: nuclear@9: static struct dsys_event *sort_evlist(struct dsys_event *list, int num_ev) nuclear@9: { nuclear@9: int i, num_left, num_right; nuclear@9: struct dsys_event *left, *right, *node = list; nuclear@9: nuclear@9: if(num_ev < 2) { nuclear@9: return list; nuclear@9: } nuclear@9: nuclear@9: num_left = num_ev / 2; nuclear@9: num_right = num_ev - num_left; nuclear@9: nuclear@9: for(i=0; inext; nuclear@9: } nuclear@9: nuclear@9: if(node->prev) { nuclear@9: node->prev->next = 0; nuclear@9: node->prev = 0; nuclear@9: } nuclear@9: nuclear@9: left = sort_evlist(list, num_left); nuclear@9: right = sort_evlist(node, num_right); nuclear@9: nuclear@9: return merge_evlists(left, right); nuclear@9: } nuclear@9: nuclear@9: static struct dsys_event *merge_evlists(struct dsys_event *list1, struct dsys_event *list2) nuclear@9: { nuclear@9: struct dsys_event *head, *tail, *node; nuclear@9: nuclear@9: if(!list1) { nuclear@9: return list2; nuclear@9: } nuclear@9: if(!list2) { nuclear@9: return list1; nuclear@9: } nuclear@9: nuclear@9: head = tail = 0; nuclear@9: nuclear@9: while(list1 && list2) { nuclear@9: if(list1->t0 < list2->t0) { nuclear@9: node = list1; nuclear@9: list1 = list1->next; nuclear@9: } else { nuclear@9: node = list2; nuclear@9: list2 = list2->next; nuclear@9: } nuclear@9: nuclear@9: node->next = 0; nuclear@9: node->prev = tail; nuclear@9: nuclear@9: if(!head) { nuclear@9: head = node; nuclear@9: } else { nuclear@9: tail->next = node; nuclear@9: } nuclear@9: tail = node; nuclear@9: } nuclear@9: nuclear@9: if(list1) { nuclear@9: tail->next = list1; nuclear@9: list1->prev = tail; nuclear@9: } else if(list2) { nuclear@9: tail->next = list2; nuclear@9: list2->prev = tail; nuclear@9: } nuclear@9: nuclear@9: return head; nuclear@9: }