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