From 73168a7f7ca3a242980071be43b5454456374aae Mon Sep 17 00:00:00 2001 From: kramm Date: Thu, 6 Feb 2003 12:11:32 +0000 Subject: [PATCH] swfc initial revision. --- src/parser.h | 27 + src/parser.lex | 193 ++++++ src/q.c | 358 +++++++++++ src/q.h | 90 +++ src/swfc.c | 1861 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 2529 insertions(+) create mode 100644 src/parser.h create mode 100644 src/parser.lex create mode 100644 src/q.c create mode 100644 src/q.h create mode 100644 src/swfc.c diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..32d468f --- /dev/null +++ b/src/parser.h @@ -0,0 +1,27 @@ +#ifndef __parser_h__ +#define __parser_h__ + +enum type_t { + TWIP=0, + NUMBER=1, + COMMAND=2, + STRING=3, + ASSIGNMENT=4, + IDENTIFIER=5, + LABEL=6, + END=7 +}; + +extern char*type_names[]; + +struct token_t { + enum type_t type; + char* text; + int line; + int column; +}; + +extern struct token_t* generateTokens(char*filename); +extern void freeTokens(struct token_t*); + +#endif diff --git a/src/parser.lex b/src/parser.lex new file mode 100644 index 0000000..f29926f --- /dev/null +++ b/src/parser.lex @@ -0,0 +1,193 @@ +%{ + +#include +#include "q.h" +#include "parser.h" + +//RVALUE {NUMBER}|{PERCENT}|{NAME}|\"{STRING}\"|{DIM} +//. {printf("%s\n", yytext);} +// %x: exclusive, %s: inclusive +char*type_names[] = {"twip","number","command","string","assignment","identifier","label","end"}; +static int line=1; +static int column=1; + +mem_t strings; +mem_t tokens; + +static void count(char*text, int len, int condition) +{ + int t; + for(t=0;t%d(%s) %s\n", type, type_names[type], text);fflush(stdout); + + token.text = 0; + switch(type) { + case END: + string_set2(&tmp, "", 0); + token.text = (char*)mem_putstring(&strings, tmp); + break; + case STRING: + string_set2(&tmp, text+1, length-2); + token.text = (char*)mem_putstring(&strings, tmp); + break; + case TWIP: + case NUMBER: + case IDENTIFIER: + string_set2(&tmp, text, length); + if(prefix) { + //strcat + token.text = (char*)mem_put(&strings, prefix, strlen(prefix)); + mem_putstring(&strings, tmp); + } else { + token.text = (char*)mem_putstring(&strings, tmp); + } + prefix = 0; + break; + case LABEL: + string_set2(&tmp, text, length-1); + token.text = (char*)mem_putstring(&strings, tmp); + break; + case COMMAND: + string_set2(&tmp, text+1, length-1); + token.text = (char*)mem_putstring(&strings, tmp); + break; + case ASSIGNMENT: { + char*x = &text[length-1]; + if(x[-1] == '-' || x[-1] == '+') + x--; + do{x--;} while(*x==32 || *x==10 || *x==13 || *x=='\t'); + x++; //first space + string_set2(&tmp, text, x-text); + token.text = (char*)mem_putstring(&strings, tmp); + /*char*y,*x = strchr(text, '='); + if(!x) exit(1); + y=x; + do{y--;} while(*y==32 || *y==10 || *y==13 || *y=='\t'); + do{x++;} while(*x==32 || *x==10 || *x==13 || *x=='\t'); + token.text1 = (char*)put(&strings, text, y-text + 1, 1); + token.text2 = (char*)put(&strings, x, length-(x-text), 1);*/ + } break; + } + + mem_put(&tokens, &token, sizeof(struct token_t)); + prefix = 0; +} + +#define c() {count(yytext, yyleng, YY_START);} +#define s(type) {store(type, line, column, yytext, yyleng);} +%} + +%s R +%x BINARY + +NAME [a-zA-Z_./](-*[a-zA-Z0-9_./])* +TWIP ([0-9]+(\.([0-9]([05])?)?)?) +NUMBER [0-9]+(\.[0-9]*)? +PERCENT {NUMBER}% +STRING (\\.|[^\\"\n])* +S [ \n\r\t] +RVALUE \"{STRING}\"|([^ \n\r\t]+) + +%% + +\] {c();BEGIN(0);} +. {c();} +\n {c();} +{TWIP}/[ \n\r\t] {s(TWIP);c();BEGIN(0);} +{NUMBER}/[ \n\r\t] {s(NUMBER);c();BEGIN(0);} +^#[^\n]*\n {c();} +[ \t\r]#[^\n]*\n {c();} +\"{STRING}\" {s(STRING);c();BEGIN(0);} +\"{STRING}$ {c();printf("unterminated string in line %d: %s\n", line, yytext);exit(1);yyterminate();} +{NAME}{S}*\+= {s(ASSIGNMENT);prefix="";c();BEGIN(R);} +{NAME}{S}*-= {s(ASSIGNMENT);prefix="";c();BEGIN(R);} +{NAME}{S}*= {s(ASSIGNMENT);c();BEGIN(R);} +{ /* values which appear only on the right-hand side of assignments, like: x=50% */ + [^ \n\t\r]* {s(IDENTIFIER);c();BEGIN(0);} +} +\.{NAME} {s(COMMAND);c();} +{NAME}{S}*: {s(LABEL);c();} +{NAME} {s(IDENTIFIER);c();} +"[" {c();BEGIN(BINARY);} +{S} {c();} +. {char c,c1=0; + printf("Syntax error in line %d, %d: %s", line, column, yytext); + while(1) { + c=input(); + if(!c1) c1=c; + if(c=='\n' || c==EOF) + break; + printf("%c", c); + } + if(c1>='0' && c1<='9') + printf(" (identifiers must not start with a digit)"); + printf("\n"); + exit(1); + yyterminate(); + } +<> {c();s(END);yyterminate();} +%% + +int yywrap() +{ + return 1; +} + +void freeTokens(struct token_t*file) +{ + mem_clear(&strings); + mem_clear(&tokens); +} + +struct token_t* generateTokens(char*filename) +{ + FILE*fi = fopen(filename, "rb"); + int t; + struct token_t*result; + int num; + if(!fi) { + printf("Couldn't find file %s\n", filename); + return 0; + } + yyin = fi; + + mem_init(&strings); + mem_init(&tokens); + mem_put(&strings, &t, 1); //hack- make all valid strings start at position >0 + + line=1; + column=1; + + yylex(); + yy_delete_buffer(yy_current_buffer); + + result = (struct token_t*)tokens.buffer; + num = tokens.pos/sizeof(struct token_t); + + for(t=0;t + + This file is distributed under the GPL, see file COPYING for details */ +#include +#include +#include +#include +#include "q.h" + +// ------------------------------- malloc, alloc routines --------------------- + +#ifndef STRNDUP +char* strndup(const char*str, int size) +{ + char*m = (char*)malloc(size+1); + memcpy(m, str, size); + m[size] = 0; + return m; +} +#endif +void* qmalloc_internal(int len) +{ + void*val = malloc(len); + if(!val) { + printf("memory error! Couldn't reserve %d bytes\n", len); + fprintf(stderr, "memory error! Couldn't reserve %d bytes\n", len); + exit(1); + } + return val; +} +void* qrealloc_internal(void*old, int len) +{ + void*val = realloc(old, len); + if(!val) { + printf("memory error! Couldn't reserve %d bytes\n", len); + fprintf(stderr, "memory error! Couldn't reserve %d bytes\n", len); + exit(1); + } + return val; +} +void qfree_internal(void*old) +{ + free(old); +} +char*qstrdup(const char*string) +{ + return strdup(string); +} +char*qstrndup(const char*string, int len) +{ + return strndup(string, len); +} + +// ------------------------------- mem_t -------------------------------------- + +void mem_init(mem_t*mem) +{ + memset(mem, 0, sizeof(mem_t)); +} +void mem_clear(mem_t*mem) +{ + free(mem->buffer); +} +void mem_destroy(mem_t*mem) +{ + mem_clear(mem); + free(mem); +} +static int mem_put_(mem_t*m,void*data, int length, int null) +{ + int n = m->pos; + m->pos += length + (null?1:0); + if(m->pos > m->len) + { + //m->len += 1024>length?1024:(null?length*2:length); + + m->len *= 2; + while(m->len < m->pos) + m->len += 64; + + m->buffer = m->buffer?realloc(m->buffer,m->len):malloc(m->len); + } + memcpy(&m->buffer[n], data, length); + if(null) + m->buffer[n + length] = 0; + return n; +} +int mem_put(mem_t*m,void*data, int length) +{ + return mem_put_(m, data, length, 0); +} +int mem_putstring(mem_t*m,string_t str) +{ + return mem_put_(m, str.str, str.len, 1); +} + +// ------------------------------- string_t ------------------------------------ + +void string_set2(string_t*str, char*text, int len) +{ + str->len = len; + str->str = text; +} +void string_set(string_t*str, char*text) +{ + str->len = strlen(text); + str->str = text; +} +void string_dup2(string_t*str, const char*text, int len) +{ + str->len = len; + str->str = strndup(text, len); +} +void string_dup(string_t*str, const char*text) +{ + str->len = strlen(text); + str->str = strdup(text); +} +int string_equals(string_t*str, const char*text) +{ + int l = strlen(text); + if(str->len == l && !strncmp(str->str, text, l)) + return 1; + return 0; +} +int string_equals2(string_t*str, string_t*str2) +{ + if(str->len == str2->len && !strncmp(str->str, str2->str, str->len)) + return 1; + return 0; +} +char* string_cstr(string_t*str) +{ + return strndup(str->str, str->len); +} + +// ------------------------------- stringarray_t ------------------------------ + +typedef struct _stringarray_internal_t +{ + mem_t data; + mem_t pos; + int num; +} stringarray_internal_t; +void stringarray_init(stringarray_t*sa) +{ + stringarray_internal_t*s; + sa->internal = (stringarray_internal_t*)malloc(sizeof(stringarray_internal_t)); + memset(sa->internal, 0, sizeof(stringarray_internal_t)); + s = (stringarray_internal_t*)sa->internal; + mem_init(&s->data); + mem_init(&s->pos); +} +void stringarray_put(stringarray_t*sa, string_t str) +{ + stringarray_internal_t*s = (stringarray_internal_t*)sa->internal; + int pos; + pos = mem_putstring(&s->data, str); + mem_put(&s->pos, &pos, sizeof(int)); + s->num++; +} +char* stringarray_at(stringarray_t*sa, int pos) +{ + stringarray_internal_t*s = (stringarray_internal_t*)sa->internal; + int p; + if(pos<0 || pos>=s->num) + return 0; + p = *(int*)&s->pos.buffer[pos*sizeof(int)]; + if(p<0) + return 0; + return &s->data.buffer[p]; +} +string_t stringarray_at2(stringarray_t*sa, int pos) +{ + string_t s; + s.str = stringarray_at(sa, pos); + s.len = s.str?strlen(s.str):0; + return s; +} +void stringarray_del(stringarray_t*sa, int pos) +{ + stringarray_internal_t*s = (stringarray_internal_t*)sa->internal; + *(int*)&s->pos.buffer[pos*sizeof(int)] = -1; +} +int stringarray_find(stringarray_t*sa, string_t* str) +{ + stringarray_internal_t*s = (stringarray_internal_t*)sa->internal; + int t; + for(t=0;tnum;t++) { + string_t s = stringarray_at2(sa, t); + if(s.str && string_equals2(&s, str)) { + return t; + } + } + return -1; +} +void stringarray_clear(stringarray_t*sa) +{ + stringarray_internal_t*s = (stringarray_internal_t*)sa->internal; + mem_clear(&s->data); + mem_clear(&s->pos); + free(s); +} +void stringarray_destroy(stringarray_t*sa) +{ + stringarray_clear(sa); + free(sa); +} + + +// ------------------------------- map_t -------------------------------------- + +typedef struct _map_internal_t +{ + stringarray_t keys; + stringarray_t values; + int num; +} map_internal_t; + +void map_init(map_t*map) +{ + map_internal_t*m; + map->internal = (map_internal_t*)malloc(sizeof(map_internal_t)); + memset(map->internal, 0, sizeof(map_internal_t)); + m = (map_internal_t*)map->internal; + stringarray_init(&m->keys); + stringarray_init(&m->values); +} +void map_put(map_t*map, string_t t1, string_t t2) +{ + map_internal_t*m = (map_internal_t*)map->internal; + stringarray_put(&m->keys, t1); + stringarray_put(&m->values, t2); + m->num++; +} +char* map_lookup(map_t*map, const char*name) +{ + int s; + map_internal_t*m = (map_internal_t*)map->internal; + string_t str; + string_set(&str, (char*)name); + s = stringarray_find(&m->keys, &str); + if(s>=0) { + string_t s2 = stringarray_at2(&m->values, s); + return s2.str; + } + return 0; +} +void map_dump(map_t*map, FILE*fi, const char*prefix) +{ + int t; + map_internal_t*m = (map_internal_t*)map->internal; + for(t=0;tnum;t++) { + string_t s1 = stringarray_at2(&m->keys, t); + string_t s2 = stringarray_at2(&m->values, t); + fprintf(fi, "%s%s=%s\n", prefix, s1.str, s2.str); + } +} +void map_clear(map_t*map) +{ + map_internal_t*m = (map_internal_t*)map->internal; + stringarray_clear(&m->keys); + stringarray_clear(&m->values); + free(m); +} +void map_destroy(map_t*map) +{ + map_clear(map); + free(map); +} + +// ------------------------------- dictionary_t -------------------------------------- + +typedef struct _dictionary_internal_t +{ + stringarray_t keys; + mem_t values; + int num; +} dictionary_internal_t; + +void dictionary_init(dictionary_t*dict) +{ + dictionary_internal_t*d; + dict->internal = (dictionary_internal_t*)malloc(sizeof(dictionary_internal_t)); + memset(dict->internal, 0, sizeof(dictionary_internal_t)); + d = (dictionary_internal_t*)dict->internal; + stringarray_init(&d->keys); + mem_init(&d->values); +} +void dictionary_put(dictionary_t*dict, string_t t1, void* t2) +{ + dictionary_internal_t*d = (dictionary_internal_t*)dict->internal; + int s=0; + s = stringarray_find(&d->keys, &t1); + if(s>=0) { + /* replace */ + *(void**)(&d->values.buffer[s*sizeof(void*)]) = t2; + } else { + stringarray_put(&d->keys, t1); + mem_put(&d->values, &t2, sizeof(void*)); + d->num++; + } +} +void dictionary_put2(dictionary_t*dict, const char*t1, void* t2) +{ + string_t s; + string_set(&s, (char*)t1); + dictionary_put(dict, s, t2); +} +void* dictionary_lookup(dictionary_t*dict, const char*name) +{ + int s; + dictionary_internal_t*d = (dictionary_internal_t*)dict->internal; + string_t str; + string_set(&str, (char*)name); + s = stringarray_find(&d->keys, &str); + if(s>=0) { + return *(void**)&d->values.buffer[sizeof(void*)*s]; + } + return 0; +} +void dictionary_dump(dictionary_t*dict, FILE*fi, const char*prefix) +{ + dictionary_internal_t*d = (dictionary_internal_t*)dict->internal; + int t; + for(t=0;tnum;t++) { + string_t s1 = stringarray_at2(&d->keys, t); + fprintf(fi, "%s%s=%08x\n", prefix, s1.str, *(void**)&d->values.buffer[sizeof(void*)*t]); + } +} +void dictionary_del(dictionary_t*dict, const char* name) +{ + dictionary_internal_t*d = (dictionary_internal_t*)dict->internal; + int s; + string_t str; + string_set(&str, (char*)name); + s = stringarray_find(&d->keys, &str); + if(s>=0) { + *(void**)(&d->values.buffer[s*sizeof(void*)]) = 0; + stringarray_del(&d->keys, s); + } +} +void dictionary_clear(dictionary_t*dict) +{ + dictionary_internal_t*d = (dictionary_internal_t*)dict->internal; + stringarray_clear(&d->keys); + mem_clear(&d->values); + free(d); +} +void dictionary_destroy(dictionary_t*dict) +{ + dictionary_clear(dict); + free(dict); +} diff --git a/src/q.h b/src/q.h new file mode 100644 index 0000000..cffd63b --- /dev/null +++ b/src/q.h @@ -0,0 +1,90 @@ +/* q.h + Header file for q.c. + + Part of the swftools package. + + Copyright (c) 2001 Matthias Kramm + + This file is distributed under the GPL, see file COPYING for details */ + +#ifndef __q_h__ +#define __q_h__ + +#include + +/* dynamically growing mem section */ +typedef struct _mem_t { + char*buffer; + int len; + int pos; +} mem_t; + +/* non-nul terminated string */ +typedef struct _string_t { + char*str; + int len; +} string_t; + +/* key/value pairs of strings */ +typedef struct _map_t { + void*internal; +} map_t; + +/* (void*)s referenced by strings */ +typedef struct _dictionary_t { + void*internal; +} dictionary_t; + +/* array of strings */ +typedef struct stringarray_t +{ + void*internal; +} stringarray_t; + +void mem_init(mem_t*mem); +int mem_put(mem_t*m, void*data, int length); +int mem_putstring(mem_t*m, string_t str); +void mem_clear(mem_t*mem); +void mem_destroy(mem_t*mem); + +void string_set(string_t*str, char*text); +void string_set2(string_t*str, char*text, int len); +void string_dup(string_t*str, const char*text); +void string_dup2(string_t*str, const char*text, int len); +int string_equals(string_t*str, const char*text); + +void stringarray_init(stringarray_t*sa); +void stringarray_put(stringarray_t*sa, string_t str); +char* stringarray_at(stringarray_t*sa, int pos); +string_t stringarray_at2(stringarray_t*sa, int pos); +int stringarray_find(stringarray_t*sa, string_t*str); +void stringarray_clear(stringarray_t*sa); +void stringarray_destroy(stringarray_t*sa); + +void map_init(map_t*map); +void map_put(map_t*map, string_t t1, string_t t2); +char* map_lookup(map_t*map, const char*name); +void map_dump(map_t*map, FILE*fi, const char*prefix); +void map_clear(map_t*map); +void map_destroy(map_t*map); + +void dictionary_init(dictionary_t*dict); +void dictionary_put(dictionary_t*dict, string_t t1, void* t2); +void dictionary_put2(dictionary_t*dict, const char* t1, void* t2); +void* dictionary_lookup(dictionary_t*dict, const char*name); +void dictionary_dump(dictionary_t*dict, FILE*fi, const char*prefix); +void dictionary_del(dictionary_t*dict, const char* name); +void dictionary_clear(dictionary_t*dict); +void dictionary_destroy(dictionary_t*dict); + +char* strndup(const char*str, int size); + +void* qmalloc_internal(int len); +void* qrealloc_internal(void*old, int len); +void qfree_internal(void*old); + +#define qmalloc(len) qmalloc_internal(len) +#define qrealloc(old, len) qmalloc_internal(old, len) +#define qfree(old) qmalloc_internal(old) + +#endif //__q_h__ diff --git a/src/swfc.c b/src/swfc.c new file mode 100644 index 0000000..c0e8ec1 --- /dev/null +++ b/src/swfc.c @@ -0,0 +1,1861 @@ +/* swfc.c + Compiles swf code (.sc) files into .swf files. + + Part of the swftools package. + + Copyright (c) 2001 Matthias Kramm + + This file is distributed under the GPL, see file COPYING for details */ + +#include +#include +#include +#include +#include +#include +#define logf logarithmf // logf is also used by ../lib/log.h +#include +#undef logf +#include "../config.h" +#include "../lib/rfxswf.h" +#include "../lib/log.h" +#include "../lib/args.h" +#include "q.h" +#include "parser.h" + +//#define DEBUG + +static char * filename = 0; +static char * outputname = "output.swf"; +static int verbose = 2; + +static struct options_t options[] = +{ + {"o","output"}, + {"v","verbose"}, + {"V","version"}, + {0,0} +}; + +int args_callback_option(char*name,char*val) +{ + if(!strcmp(name, "V")) { + printf("swfc - part of %s %s\n", PACKAGE, VERSION); + exit(0); + } + else if(!strcmp(name, "o")) { + outputname = val; + return 1; + } + else if(!strcmp(name, "v")) { + verbose ++; + return 0; + } + else { + printf("Unknown option: -%s\n", name); + exit(1); + } + return 0; +} +int args_callback_longoption(char*name,char*val) +{ + return args_long2shortoption(options, name, val); +} +void args_callback_usage(char*name) +{ + printf("Usage: %s [-o filename] file.wav\n", name); + printf("\t-v , --verbose\t\t\t Be more verbose\n"); + printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n"); + printf("\t-V , --version\t\t\t Print program version and exit\n"); +} +int args_callback_command(char*name,char*val) +{ + if(filename) { + fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n", + filename, name); + } + filename = name; + return 0; +} + +static struct token_t* file; + +static int pos; +static char*text; +static int textlen; +static int type; +static int line; +static int column; + +static void syntaxerror(char*format, ...) +{ + char buf[1024]; + va_list arglist; + va_start(arglist, format); + vsprintf(buf, format, arglist); + va_end(arglist); + printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf); + exit(1); +} + +static void warning(char*format, ...) +{ + char buf[1024]; + va_list arglist; + va_start(arglist, format); + vsprintf(buf, format, arglist); + va_end(arglist); + printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf); +} + +static void readToken() +{ + type = file[pos].type; + if(type == END) { + syntaxerror("unexpected end of file"); + } + text = file[pos].text; + textlen = strlen(text); + line = file[pos].line; + column = file[pos].column; + pos++; + //printf("---> %d(%s) %s\n", type, type_names[type], text); +} + +static void pushBack() +{ + int p; + if(!pos) syntaxerror("internal error 3"); + pos--; + p = pos; + if(p) p--; + text = file[p].text; + textlen = strlen(text); + type = file[p].type; + line = file[p].line; + column = file[p].column; +} + +static int noMoreTokens() +{ + if(file[pos].type == END) + return 1; + return 0; +} + +// ------------------------------ swf routines ---------------------------- +struct _character; +static struct level +{ + int type; //0=swf, 1=sprite, 2=clip + + /* for swf (0): */ + SWF*swf; + char*filename; + + /* for sprites (1): */ + TAG*tag; + U16 id; + char*name; + U16 olddepth; + int oldframe; + dictionary_t oldinstances; + SRECT oldrect; + +} stack[256]; +static int stackpos = 0; + +static dictionary_t characters; +static char idmap[65536]; +static TAG*tag = 0; //current tag + +static int id; //current character id +static int currentframe; //current frame in current level +static SRECT currentrect; //current bounding box in current level +static U16 currentdepth; +static dictionary_t instances; +static dictionary_t fonts; + +typedef struct _parameters { + int x,y; + float scalex, scaley; + CXFORM cxform; + float rotate; + SPOINT pivot; + SPOINT pin; +} parameters_t; + +typedef struct _character { + TAG*definingTag; + U16 id; + SRECT size; +} character_t; + +typedef struct _instance { + character_t*character; + U16 depth; + parameters_t parameters; + TAG* lastTag; //last tag which set the object + U16 lastFrame; //frame lastTag is in +} instance_t; + +static void character_init(character_t*c) +{ + memset(c, 0, sizeof(character_t)); +} +static character_t* character_new() +{ + character_t*c; + c = (character_t*)malloc(sizeof(character_t)); + character_init(c); + return c; +} +static void instance_init(instance_t*i) +{ + memset(i, 0, sizeof(instance_t)); +} +static instance_t* instance_new() +{ + instance_t*c; + c = (instance_t*)malloc(sizeof(instance_t)); + instance_init(c); + return c; +} + +static void incrementid() +{ + while(idmap[++id]) { + if(id==65535) + syntaxerror("Out of character ids."); + } + idmap[id] = 1; +} + +static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r) +{ + character_t* c = character_new(); + + c->definingTag = ctag; + c->id = id; + c->size = r; + if(dictionary_lookup(&characters, name)) + syntaxerror("character %s defined twice", name); + dictionary_put2(&characters, name, c); + + tag = swf_InsertTag(tag, ST_NAMECHARACTER); + swf_SetU16(tag, id); + swf_SetString(tag, name); + tag = swf_InsertTag(tag, ST_EXPORTASSETS); + swf_SetU16(tag, 1); + swf_SetU16(tag, id); + swf_SetString(tag, name); +} +static instance_t* s_addinstance(char*name, character_t*c, U16 depth) +{ + instance_t* i = instance_new(); + i->character = c; + i->depth = depth; + //swf_GetMatrix(0, &i->matrix); + if(dictionary_lookup(&instances, name)) + syntaxerror("object %s defined twice", name); + dictionary_put2(&instances, name, i); + return i; +} + +static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, SPOINT pivot, SPOINT pin, CXFORM cxform) +{ + p->x = x; p->y = y; + p->scalex = scalex; p->scaley = scaley; + p->pin = pin; p->pivot = pivot; + p->rotate = rotate; p->cxform = cxform; +} + +static void parameters_clear(parameters_t*p) +{ + p->x = 0; p->y = 0; + p->scalex = 1.0; p->scaley = 1.0; + p->pin.x = 1; p->pin.y = 0; + p->pivot.x = 0; p->pivot.y = 0; + p->rotate = 0; + swf_GetCXForm(0, &p->cxform, 1); +} + +static void makeMatrix(MATRIX*m, parameters_t*p) +{ + SPOINT h; + m->sx = p->scalex*cos(p->rotate/360*2*3.14159265358979)*65535; + m->r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)*65535; + m->r0 = p->scaley*sin(p->rotate/360*2*3.14159265358979)*65535; + m->sy = p->scaley*cos(p->rotate/360*2*3.14159265358979)*65535; + m->tx = m->ty = 0; + + h = swf_TurnPoint(p->pin, m); + m->tx = p->x - h.x; + m->ty = p->y - h.y; +} + +static MATRIX s_instancepos(instance_t*i, parameters_t*p) +{ + MATRIX m; + SRECT r; + makeMatrix(&m, p); + r = swf_TurnRect(i->character->size, &m); + if(currentrect.xmin == 0 && currentrect.ymin == 0 && + currentrect.xmax == 0 && currentrect.ymax == 0) + currentrect = r; + else + swf_ExpandRect2(¤trect, &r); + return m; +} + +void s_swf(char*name, SRECT r, int version, int fps, int compress) +{ + SWF*swf = (SWF*)malloc(sizeof(SWF)); + RGBA rgb; + + if(stackpos) + syntaxerror(".swf blocks can't be nested"); + + memset(swf, 0, sizeof(swf)); + swf->fileVersion = version; + swf->movieSize = r; + swf->frameRate = fps; + swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR); + swf->compressed = compress; + rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00; + swf_SetRGB(tag,&rgb); + + if(stackpos==sizeof(stack)/sizeof(stack[0])) + syntaxerror("too many levels of recursion"); + + dictionary_init(&characters); + dictionary_init(&instances); + dictionary_init(&fonts); + + memset(&stack[stackpos], 0, sizeof(stack[0])); + stack[stackpos].type = 0; + stack[stackpos].filename = strdup(name); + stack[stackpos].swf = swf; + stack[stackpos].oldframe = -1; + stackpos++; + id = 0; + + currentframe = 0; + memset(¤trect, 0, sizeof(currentrect)); + currentdepth = 1; + + memset(idmap, 0, sizeof(idmap)); + incrementid(); +} + +void s_sprite(char*name) +{ + tag = swf_InsertTag(tag, ST_DEFINESPRITE); + swf_SetU16(tag, id); //id + swf_SetU16(tag, 0); //frames + + memset(&stack[stackpos], 0, sizeof(stack[0])); + stack[stackpos].type = 1; + stack[stackpos].oldframe = currentframe; + stack[stackpos].olddepth = currentdepth; + stack[stackpos].oldrect = currentrect; + stack[stackpos].oldinstances = instances; + stack[stackpos].tag = tag; + stack[stackpos].id = id; + stack[stackpos].name = strdup(name); + + /* FIXME: those four fields should be bundled together */ + dictionary_init(&instances); + currentframe = 0; + currentdepth = 1; + memset(¤trect, 0, sizeof(currentrect)); + + stackpos++; + incrementid(); +} + +static void s_endSprite() +{ + SRECT r = currentrect; + stackpos--; + + /* TODO: before clearing, prepend "." to names and + copy into old instances dict */ + dictionary_clear(&instances); + + currentframe = stack[stackpos].oldframe; + currentrect = stack[stackpos].oldrect; + currentdepth = stack[stackpos].olddepth; + instances = stack[stackpos].oldinstances; + tag = swf_InsertTag(tag, ST_END); + + tag = stack[stackpos].tag; + swf_FoldSprite(tag); + if(tag->next != 0) + syntaxerror("internal error(7)"); + + s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r); + free(stack[stackpos].name); +} + +static void s_endSWF() +{ + int fi; + SWF* swf; + char*filename; + stackpos--; + + swf = stack[stackpos].swf; + filename = stack[stackpos].filename; + + tag = swf_InsertTag(tag, ST_SHOWFRAME); + tag = swf_InsertTag(tag, ST_END); + + swf_OptimizeTagOrder(swf); + + if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) + swf->movieSize = currentrect; /* "autocrop" */ + + fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if(fi<0) { + syntaxerror("couldn't create output file %s", filename); + } + if(swf->compressed) + {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");} + else + {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");} + + close(fi); + + dictionary_clear(&instances); + dictionary_clear(&characters); + dictionary_clear(&fonts); + + swf_FreeTags(swf); + free(swf); + free(filename); +} +void s_close() +{ + if(stackpos) { + if(stack[stackpos-1].type == 0) + syntaxerror("End of file encountered in .swf block"); + if(stack[stackpos-1].type == 1) + syntaxerror("End of file encountered in .sprite block"); + if(stack[stackpos-1].type == 2) + syntaxerror("End of file encountered in .clip block"); + } +} + +int s_getframe() +{ + return currentframe; +} + +void s_frame(int nr) +{ + int t; + for(t=currentframe;t= font->maxascii || font->ascii2glyph[text[0]]<0) { + warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname); + s_box(name, 0, 0, black, 20, black, 0); + return; + } + g = font->ascii2glyph[text[0]]; + rect = font->layout->bounds[g]; + + swf_ShapeNew(&s); + ls1 = swf_ShapeAddLineStyle(s,linewidth,&color); + if(dofill) + fs1 = swf_ShapeAddSolidFillStyle(s,&fill); + + tag = swf_InsertTag(tag, ST_DEFINESHAPE); + swf_SetU16(tag,id); + swf_SetRect(tag, &rect); + swf_SetShapeStyles(tag, s); + swf_SetSimpleShape(tag, font->glyph[g].shape); + swf_ShapeFree(s); + + s_addcharacter(name, id, tag, rect); + incrementid(); +} +void s_text(char*name, char*fontname, char*text, int size, RGBA color) +{ + SRECT r; + + SWFFONT*font; + font = dictionary_lookup(&fonts, fontname); + if(!font) + syntaxerror("font \"%s\" not known!", fontname); + + tag = swf_InsertTag(tag, ST_DEFINETEXT2); + swf_SetU16(tag, id); + if(!font->numchars) { + s_box(name, 0, 0, black, 20, black, 0); + return; + } + r = swf_SetDefineText(tag, font, &color, text, size); + + s_addcharacter(name, id, tag, r); + incrementid(); +} + +void dumpSWF(SWF*swf) +{ + TAG* tag = swf->firstTag; + printf("vvvvvvvvvvvvvvvvvvvvv\n"); + while(tag) { + printf("%8d %s\n", tag->len, swf_TagGetName(tag)); + tag = tag->next; + } + printf("^^^^^^^^^^^^^^^^^^^^^\n"); +} + +void s_font(char*name, char*filename) +{ + int f; + SWF swf; + SWFFONT* font; + f = open(filename,O_RDONLY); + if (f<0) { + warning("Couldn't open file \"%s\": %s", filename, strerror(errno)); + font = (SWFFONT*)malloc(sizeof(SWFFONT)); + memset(font, 0, sizeof(SWFFONT)); + dictionary_put2(&fonts, name, font); + return; + } + font = 0; + if (swf_ReadSWF(f,&swf)>=0) { + swf_FontExtract(&swf, 0x4e46, &font); + swf_FreeTags(&swf); + } + close(f); + if (font==0) { + syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename); + } + + if(0) + { + /* fix the layout. Only needed for old fonts */ + int t; + for(t=0;tnumchars;t++) { + font->glyph[t].advance = 0; + } + font->layout = 0; + swf_FontCreateLayout(font); + } + + font->id = id; + tag = swf_InsertTag(tag, ST_DEFINEFONT2); + swf_FontSetDefine2(tag, font); + incrementid(); + + if(dictionary_lookup(&fonts, name)) + syntaxerror("font %s defined twice", name); + dictionary_put2(&fonts, name, font); +} + +void s_shape(char*name, char*filename) +{ + int f; + SWF swf; + TAG* ftag; + SRECT r; + TAG* s; + int level = 0; + U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX}; + f = open(filename,O_RDONLY); + if (f<0) { + warning("Couldn't open file \"%s\": %s", filename, strerror(errno)); + s_box(name, 0, 0, black, 20, black, 0); + return; + } + if (swf_ReadSWF(f,&swf)<0) { + warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename); + s_box(name, 0, 0, black, 20, black, 0); + return; + } + close(f); + + /* FIXME: The following sets the bounding Box for the character. + It is wrong for two reasons: + a) It may be too small (in case objects in the movie clip at the borders) + b) it may be too big (because the poor movie never got autocropped) + */ + r = swf.movieSize; + + s = tag = swf_InsertTag(tag, ST_DEFINESPRITE); + swf_SetU16(tag, id); + swf_SetU16(tag, 0); + + swf_Relocate(&swf, idmap); + + ftag = swf.firstTag; + level = 1; + while(ftag) { + int t; + for(t=0;tid) { + ftag = ftag->next; + continue; + } + if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag)) + level++; + if(ftag->id == ST_END) + level--; + if(!level) + break; + /* We simply dump all tags right after the sprite + header, relying on the fact that swf_OptimizeTagOrder() will + sort things out for us later. + We also rely on the fact that the imported SWF is well-formed. + */ + tag = swf_InsertTag(tag, ftag->id); + swf_SetBlock(tag, ftag->data, ftag->len); + ftag = ftag->next; + } + if(!ftag) + syntaxerror("Included file %s contains errors", filename); + tag = swf_InsertTag(tag, ST_END); + + swf_FreeTags(&swf); + + s_addcharacter(name, id, tag, r); + incrementid(); +} +SRECT s_getCharBBox(char*name) +{ + character_t* c = dictionary_lookup(&characters, name); + if(!c) syntaxerror("character '%s' unknown(2)", name); + return c->size; +} +SRECT s_getInstanceBBox(char*name) +{ + instance_t * i = dictionary_lookup(&instances, name); + character_t * c; + if(!i) syntaxerror("instance '%s' unknown(4)", name); + c = i->character; + if(!c) syntaxerror("internal error(5)"); + return c->size; +} +parameters_t s_getParameters(char*name) +{ + instance_t * i = dictionary_lookup(&instances, name); + if(!i) syntaxerror("instance '%s' unknown(10)", name); + return i->parameters; +} +void s_startclip(char*instance, char*character, parameters_t p) +{ + character_t* c = dictionary_lookup(&characters, character); + instance_t* i; + MATRIX m; + if(!c) { + syntaxerror("character %s not known", character); + } + i = s_addinstance(instance, c, currentdepth); + i->parameters = p; + m = s_instancepos(i, &p); + + tag = swf_InsertTag(tag, ST_PLACEOBJECT2); + /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */ + swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance); + i->lastTag = tag; + i->lastFrame= currentframe; + + stack[stackpos].tag = tag; + stack[stackpos].type = 2; + stackpos++; + + currentdepth++; +} +void s_endClip() +{ + SWFPLACEOBJECT p; + stackpos--; + swf_SetTagPos(stack[stackpos].tag, 0); + swf_GetPlaceObject(stack[stackpos].tag, &p); + p.clipdepth = currentdepth; + swf_ClearTag(stack[stackpos].tag); + swf_SetPlaceObject(stack[stackpos].tag, &p); + currentdepth++; +} + +void s_put(char*instance, char*character, parameters_t p) +{ + character_t* c = dictionary_lookup(&characters, character); + instance_t* i; + MATRIX m; + if(!c) { + syntaxerror("character %s not known (in .put %s=%s)", character, instance, character); + } + + i = s_addinstance(instance, c, currentdepth); + i->parameters = p; + m = s_instancepos(i, &p); + + tag = swf_InsertTag(tag, ST_PLACEOBJECT2); + swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance); + i->lastTag = tag; + i->lastFrame = currentframe; + currentdepth++; +} + +void s_jump(char*instance, parameters_t p) +{ + instance_t* i = dictionary_lookup(&instances, instance); + MATRIX m; + if(!i) { + syntaxerror("instance %s not known", instance); + } + + i->parameters = p; + m = s_instancepos(i, &p); + + tag = swf_InsertTag(tag, ST_PLACEOBJECT2); + swf_ObjectMove(tag, i->depth, &m, &p.cxform); + i->lastTag = tag; + i->lastFrame = currentframe; +} + +parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num) +{ + parameters_t p; + float ratio; + if(num==0 || num==1) + return *p1; + ratio = (float)pos/(float)num; + + p.x = (p2->x-p1->x)*ratio + p1->x; + p.y = (p2->y-p1->y)*ratio + p1->y; + p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex; + p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley; + p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate; + + p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0; + p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0; + p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0; + p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0; + + p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1; + p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1; + p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1; + p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1; + + p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x; + p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y; + p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x; + p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y; + return p; +} + +void s_change(char*instance, parameters_t p2) +{ + instance_t* i = dictionary_lookup(&instances, instance); + MATRIX m; + parameters_t p1; + TAG*t; + int frame, allframes; + if(!i) { + syntaxerror("instance %s not known", instance); + } + p1 = i->parameters; + + allframes = currentframe - i->lastFrame - 1; + if(!allframes) { + warning(".change ignored. can only .put/.change an object once per frame."); + return; + } + + m = s_instancepos(i, &p2); + tag = swf_InsertTag(tag, ST_PLACEOBJECT2); + swf_ObjectMove(tag, i->depth, &m, &p2.cxform); + i->parameters = p2; + + /* o.k., we got the start and end point set. Now iterate though all the + tags in between, inserting object changes after each new frame */ + t = i->lastTag; + i->lastTag = tag; + if(!t) syntaxerror("internal error(6)"); + frame = 0; + while(frame < allframes) { + if(t->id == ST_SHOWFRAME) { + parameters_t p; + MATRIX m; + TAG*lt; + frame ++; + p = s_interpolate(&p1, &p2, frame, allframes); + m = s_instancepos(i, &p); //needed? + lt = swf_InsertTag(t, ST_PLACEOBJECT2); + i->lastFrame = currentframe; + swf_ObjectMove(lt, i->depth, &m, &p.cxform); + t = lt; + if(frame == allframes) + break; + } + t = t->next; + if(!t) + syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes); + } +} + +void s_delinstance(char*instance) +{ + instance_t* i = dictionary_lookup(&instances, instance); + if(!i) { + syntaxerror("instance %s not known", instance); + } + tag = swf_InsertTag(tag, ST_REMOVEOBJECT2); + swf_SetU16(tag, i->depth); + dictionary_del(&instances, instance); +} + +void s_qchange(char*instance, parameters_t p) +{ +} + +void s_end() +{ + if(!stackpos) + syntaxerror(".end unexpected"); + if(stack[stackpos-1].type == 0) + s_endSWF(); + else if(stack[stackpos-1].type == 1) + s_endSprite(); + else if(stack[stackpos-1].type == 2) + s_endClip(); + else syntaxerror("internal error 1"); +} + +// ------------------------------------------------------------------------ + +typedef int command_func_t(map_t*args); + +SRECT parseBox(char*str) +{ + SRECT r; + float xmin, xmax, ymin, ymax; + char*x = strchr(str, 'x'); + char*d1=0,*d2=0; + if(!strcmp(str, "autocrop")) { + r.xmin = r.ymin = r.xmax = r.ymax = 0; + return r; + } + if(!x) goto error; + d1 = strchr(x+1, ':'); + if(d1) + d2 = strchr(d1+1, ':'); + if(!d1) { + if(sscanf(str, "%fx%f", &xmax, &ymax) < 2) + goto error; + xmin = ymin = 0; + } + else if(d1 && !d2) { + if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3) + goto error; + xmax += xmin; + ymin = 0; + } + else { + if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4) + goto error; + xmax += xmin; + ymax += ymin; + } + r.xmin = (SCOORD)(xmin*20); + r.ymin = (SCOORD)(ymin*20); + r.xmax = (SCOORD)(xmax*20); + r.ymax = (SCOORD)(ymax*20); + return r; +error: + syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str); + return r; +} +float parseFloat(char*str) +{ + return atof(str); +} +int parseInt(char*str) +{ + int t; + int l=strlen(str); + int s=0; + if(str[0]=='+' || str[0]=='-') + s++; + + for(t=s;t'9') + syntaxerror("Not an Integer: \"%s\"", str); + return atoi(str); +} +int parseTwip(char*str) +{ + char*dot = strchr(str, '.'); + if(!dot) { + int l=strlen(str); + int t; + return parseInt(str)*20; + } else { + int l=strlen(++dot); + char*s; + for(s=str;s'9') + syntaxerror("Not a coordinate: \"%s\"", str); + for(s=dot;*s;s++) { + if(*s<'0' || *s>'9') + syntaxerror("Not a coordinate: \"%s\"", str); + } + if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) { + warning("precision loss: %s converted to twip", str); + dot[2] = 0; + l=2; + } + if(l==0) + return atoi(str)*20; + if(l==1) + return atoi(str)*20+atoi(dot)*2; + if(l==2) + return atoi(str)*20+atoi(dot)/5; + } + return 0; +} + +int isPoint(char*str) +{ + if(strchr(str, '(')) + return 1; + else + return 0; +} + +SPOINT parsePoint(char*str) +{ + SPOINT p; + char tmp[80]; + int l = strlen(str); + char*comma = strchr(str, ','); + if(str[0]!='(' || str[l-1]!=')' || !comma || l>70) + syntaxerror("\"%s\" is not a valid point of the form (x,y)", str); + strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0; + p.x = parseTwip(tmp); + strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0; + p.y = parseTwip(tmp); + return p; +} + +int parseColor2(char*str, RGBA*color) +{ + int l = strlen(str); + int r,g,b,a; + int t; + char*names[8] = {"black", "blue", "green", "cyan", + "red", "violet", "yellow", "white"}; + a=255; + r=g=b=0; + if(str[0]=='#' && (l==7 || l==9)) { + if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b))) + return 0; + if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a))) + return 0; + color->r = r; color->g = g; color->b = b; color->a = a; + return 1; + } + for(t=0;t<8;t++) + if(!strcmp(str, names[t])) { + if(t&1) + b = 255; + if(t&2) + g = 255; + if(t&4) + r = 255; + color->r = r; color->g = g; color->b = b; color->a = a; + return 1; + } + return 0; + +} +RGBA parseColor(char*str) +{ + RGBA c; + if(!parseColor2(str, &c)) + syntaxerror("Expression '%s' is not a color", str); + return c; +} + +typedef struct _muladd { + S16 mul; + S16 add; +} MULADD; + +MULADD parseMulAdd(char*str) +{ + float add, mul; + char* str2 = (char*)malloc(strlen(str)+5); + int i; + MULADD m; + strcpy(str2, str); + strcat(str2, " 0"); + add = 0; + mul = 1.0; + if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;} + else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;} + else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;} + else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;} + else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;} + else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;} + else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;} + else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;} + else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;} + else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;} + else { + syntaxerror("'%s' is not a valid color transform expression", str); + } + m.add = (int)(add*256); + m.mul = (int)(mul*256); + free(str2); + return m; +} + +MULADD mergeMulAdd(MULADD m1, MULADD m2) +{ + int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0); + double m = ((double)m1.mul*(double)m2.mul)/256.0; + MULADD r; + if(a<-32768) a=-32768; + if(a>32767) a=32767; + if(m<-32768) m=-32768; + if(m>32767) m=32767; + r.add = a; + r.mul = (int)m; + return r; +} + +float parsePercent(char*str) +{ + int l = strlen(str); + if(!l) + return 1.0; + if(str[l-1]=='%') { + return atoi(str)/100.0; + } + syntaxerror("Expression '%s' is not a percentage", str); + return 0; +} +int isPercent(char*str) +{ + return str[strlen(str)-1]=='%'; +} +int parseNewSize(char*str, int size) +{ + if(isPercent(str)) + return parsePercent(str)*size; + else + return (int)(atof(str)*20); +} + +int isColor(char*str) +{ + RGBA c; + return parseColor2(str, &c); +} + +static char* lu(map_t* args, char*name) +{ + char* value = map_lookup(args, name); + if(!value) { + syntaxerror("internal error 2: value %s should be set", name); + } + return value; +} + +static int c_swf(map_t*args) +{ + char* name = lu(args, "name"); + char* compressstr = lu(args, "compress"); + SRECT bbox = parseBox(lu(args, "bbox")); + int version = parseInt(lu(args, "version")); + int fps = (int)(parseFloat(lu(args, "fps"))*256); + int compress = 0; + if(!strcmp(name, "!default!")) + name = outputname; + + if(!strcmp(compressstr, "default")) + compress = version==6; + else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress")) + compress = 1; + else if(!strcmp(compressstr, "no")) + compress = 0; + else syntaxerror("value \"%s\" not supported for the compress argument", compressstr); + + s_swf(name, bbox, version, fps, compress); + return 0; +} +int isRelative(char*str) +{ + return !strncmp(str, "", 6) || + !strncmp(str, "", 7); +} +char* getOffset(char*str) +{ + if(!strncmp(str, "", 6)) + return str+6; + if(!strncmp(str, "", 7)) + return str+7; + syntaxerror("internal error (347)"); + return 0; +} +int getSign(char*str) +{ + if(!strncmp(str, "", 6)) + return 1; + if(!strncmp(str, "", 7)) + return -1; + syntaxerror("internal error (348)"); + return 0; +} +static dictionary_t points; +static mem_t mpoints; +int points_initialized = 0; + +SPOINT getPoint(SRECT r, char*name) +{ + int l; + if(!strcmp(name, "center")) { + SPOINT p; + p.x = (r.xmin + r.xmax)/2; + p.y = (r.ymin + r.ymax)/2; + return p; + } + + l = (int)dictionary_lookup(&points, name); + if(l==0) { + syntaxerror("Couldn't find point \"%s\".", name); + } + l--; + return *(SPOINT*)&mpoints.buffer[l]; +} +static int c_point(map_t*args) +{ + char*name = lu(args, "name"); + int pos; + string_t s1; + SPOINT p; + if(!points_initialized) { + dictionary_init(&points); + mem_init(&mpoints); + points_initialized = 1; + } + p.x = parseTwip(lu(args, "x")); + p.y = parseTwip(lu(args, "y")); + pos = mem_put(&mpoints, &p, sizeof(p)); + string_set(&s1, name); + pos++; + dictionary_put(&points, s1, (void*)pos); + return 0; +} +static int c_placement(map_t*args, int type) +{ + char*instance = lu(args, (type==0||type==4)?"instance":"name"); + char*character = 0; + + char* luminancestr = lu(args, "luminance"); + char* scalestr = lu(args, "scale"); + char* scalexstr = lu(args, "scalex"); + char* scaleystr = lu(args, "scaley"); + char* rotatestr = lu(args, "rotate"); + char* xstr="", *pivotstr=""; + char* ystr="", *anglestr=""; + char*above = lu(args, "above"); /*FIXME*/ + char*below = lu(args, "below"); + char* rstr = lu(args, "red"); + char* gstr = lu(args, "green"); + char* bstr = lu(args, "blue"); + char* astr = lu(args, "alpha"); + char* pinstr = lu(args, "pin"); + MULADD r,g,b,a; + float oldwidth; + float oldheight; + SRECT oldbbox; + MULADD luminance; + parameters_t p; + + if(type==5) { + pivotstr = lu(args, "pivot"); + anglestr = lu(args, "angle"); + } else { + xstr = lu(args, "x"); + ystr = lu(args, "y"); + } + if(luminancestr[0]) + luminance = parseMulAdd(luminancestr); + else { + luminance.add = 0; + luminance.mul = 256; + } + + if(scalestr[0]) { + if(scalexstr[0]||scaleystr[0]) + syntaxerror("scalex/scaley and scale cannot both be set"); + scalexstr = scaleystr = scalestr; + } + + if(type == 0 || type == 4) { + // put or startclip + character = lu(args, "character"); + parameters_clear(&p); + } else { + p = s_getParameters(instance); + } + + /* x,y position */ + if(xstr[0]) { + if(isRelative(xstr)) { + if(type == 0 || type == 4) + syntaxerror("relative x values not allowed for initial put or startclip"); + p.x += parseTwip(getOffset(xstr))*getSign(xstr); + } else { + p.x = parseTwip(xstr); + } + } + if(ystr[0]) { + if(isRelative(ystr)) { + if(type == 0 || type == 4) + syntaxerror("relative y values not allowed for initial put or startclip"); + p.y += parseTwip(getOffset(ystr))*getSign(ystr); + } else { + p.y = parseTwip(ystr); + } + } + + /* scale, scalex, scaley */ + if(character) { + oldbbox = s_getCharBBox(character); + } else { + oldbbox = s_getInstanceBBox(instance); + } + oldwidth = oldbbox.xmax - oldbbox.xmin; + oldheight = oldbbox.ymax - oldbbox.ymin; + if(scalexstr[0]) { + if(oldwidth==0) p.scalex = 1.0; + else { + if(scalexstr[0]) + p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth; + } + } + if(scaleystr[0]) { + if(oldheight==0) p.scaley = 1.0; + else { + if(scaleystr[0]) + p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight; + } + } + + /* rotation */ + if(rotatestr[0]) { + if(isRelative(rotatestr)) { + p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr); + } else { + p.rotate = parseFloat(rotatestr); + } + } + + if(pivotstr[0]) { + if(isPoint(pivotstr)) + p.pivot = parsePoint(pivotstr); + else + p.pivot = getPoint(oldbbox, pivotstr); + } + if(pinstr[0]) { + if(isPoint(pinstr)) + p.pin = parsePoint(pinstr); + else + p.pin = getPoint(oldbbox, pinstr); + } + + /* color transform */ + + if(rstr[0] || luminancestr[0]) { + MULADD r; + if(rstr[0]) + r = parseMulAdd(rstr); + else { + r.add = p.cxform.r0; + r.mul = p.cxform.r1; + } + r = mergeMulAdd(r, luminance); + p.cxform.r0 = r.mul;p.cxform.r1 = r.add; + } + if(gstr[0] || luminancestr[0]) { + MULADD g; + if(gstr[0]) + g = parseMulAdd(gstr); + else { + g.add = p.cxform.g0; + g.mul = p.cxform.g1; + } + g = mergeMulAdd(g, luminance); + p.cxform.g0 = g.mul;p.cxform.g1 = g.add; + } + if(bstr[0] || luminancestr[0]) { + MULADD b; + if(bstr[0]) + b = parseMulAdd(bstr); + else { + b.add = p.cxform.b0; + b.mul = p.cxform.b1; + } + b = mergeMulAdd(b, luminance); + p.cxform.b0 = b.mul;p.cxform.b1 = b.add; + } + if(astr[0]) { + MULADD a = parseMulAdd(astr); + p.cxform.a0 = a.mul;p.cxform.a1 = a.add; + } + + if(type == 0) + s_put(instance, character, p); + if(type == 1) + s_change(instance, p); + if(type == 2) + s_qchange(instance, p); + if(type == 3) + s_jump(instance, p); + if(type == 4) + s_startclip(instance, character, p); + return 0; +} +static int c_put(map_t*args) +{ + c_placement(args, 0); + return 0; +} +static int c_change(map_t*args) +{ + c_placement(args, 1); + return 0; +} +static int c_qchange(map_t*args) +{ + c_placement(args, 2); + return 0; +} +static int c_arcchange(map_t*args) +{ + c_placement(args, 2); + return 0; +} +static int c_jump(map_t*args) +{ + c_placement(args, 3); + return 0; +} +static int c_startclip(map_t*args) +{ + c_placement(args, 4); + return 0; +} +static int c_del(map_t*args) +{ + char*instance = lu(args, "name"); + s_delinstance(instance); + return 0; +} +static int c_end(map_t*args) +{ + s_end(); + return 0; +} +static int c_sprite(map_t*args) +{ + char* name = lu(args, "name"); + s_sprite(name); + return 0; +} +static int c_frame(map_t*args) +{ + char*framestr = lu(args, "n"); + int frame; + if(framestr[0]=='+') { + frame = s_getframe(); + frame += parseInt(framestr+1); + } + else { + frame = parseInt(framestr); + if(s_getframe() >= frame + && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0 + syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr); + } + s_frame(frame); + return 0; +} +static int c_primitive(map_t*args) +{ + char*name = lu(args, "name"); + char*command = lu(args, "commandname"); + int width=0, height=0, r=0; + int linewidth = parseTwip(lu(args, "line")); + char*colorstr = lu(args, "color"); + RGBA color = parseColor(colorstr); + char*fillstr = lu(args, "fill"); + int dofill = 1; + int type=0; + char* font; + char* text; + RGBA fill; + if(!strcmp(command, "circle")) + type = 1; + else if(!strcmp(command, "textshape")) + type = 2; + + if(type==0) { + width = parseTwip(lu(args, "width")); + height = parseTwip(lu(args, "height")); + } else if (type==1) { + r = parseTwip(lu(args, "r")); + } else if (type==2) { + text = lu(args, "text"); + font = lu(args, "font"); + } + + if(!strcmp(fillstr, "fill")) + fillstr = colorstr; + if(!strcmp(fillstr, "none")) + dofill = 0; + if(width<0 || height<0 || linewidth<0 || r<0) + syntaxerror("values width, height, line, r must be positive"); + if(!dofill || isColor(fillstr)) { + if(dofill) + fill = parseColor(fillstr); + } else { + /* FIXME - texture fill */ + fill.r = fill.g = 0; + fill.b = fill.a = 255; + warning("texture fill not supported yet. Filling with black."); + } + if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill); + else if(type==1) s_circle(name, r, color, linewidth, fill, dofill); + else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill); + return 0; +} + +static int c_shape(map_t*args) +{ + char*name = lu(args, "name"); + char*filename = lu(args, "filename"); + s_shape(name, filename); + return 0; +} + +static int c_font(map_t*args) +{ + char*name = lu(args, "name"); + char*filename = lu(args, "filename"); + s_font(name, filename); + return 0; +} + +static int c_text(map_t*args) +{ + char*name = lu(args, "name"); + char*text = lu(args, "text"); + char*font = lu(args, "font"); + float size = parsePercent(lu(args, "size")); + RGBA color = parseColor(lu(args, "color")); + s_text(name, font, text, (int)(size*100), color); + return 0; +} + +int fakechar(map_t*args) +{ + char*name = lu(args, "name"); + s_box(name, 0, 0, black, 20, black, 0); + return 0; +} +static int c_circle(map_t*args) {return fakechar(args);} + +static int c_egon(map_t*args) {return fakechar(args);} +static int c_button(map_t*args) {return fakechar(args);} +static int c_edittext(map_t*args) {return fakechar(args);} + +static int c_morphshape(map_t*args) {return fakechar(args);} +static int c_image(map_t*args) {return fakechar(args);} +static int c_movie(map_t*args) {return fakechar(args);} +static int c_sound(map_t*args) {return fakechar(args);} + +static int c_play(map_t*args) {return 0;} +static int c_stop(map_t*args) {return 0;} + +static int c_soundtrack(map_t*args) {return 0;} +static int c_buttonsounds(map_t*args) {return 0;} +static int c_buttonput(map_t*args) {return 0;} +static int c_texture(map_t*args) {return 0;} +static int c_action(map_t*args) {return 0;} + +static struct { + char*command; + command_func_t* func; + char*arguments; +} arguments[] = +{{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"}, + {"frame", c_frame, "n=+1"}, + + // "import" type stuff + {"shape", c_shape, "name filename"}, + {"morphshape", c_morphshape, "name start end"}, + {"jpeg", c_image, "name filename quality=80%"}, + {"png", c_image, "name filename"}, + {"movie", c_movie, "name filename"}, + {"sound", c_sound, "name filename"}, + {"font", c_font, "name filename"}, + {"soundtrack", c_soundtrack, "filename"}, + + // character generators + {"box", c_primitive, "name width height color=white line=1 @fill=none"}, + {"circle", c_primitive, "name r color=white line=1 @fill=none"}, + {"textshape", c_primitive, "name text font color=white line=1 @fill=none"}, + {"egon", c_egon, "name vertices color=white line=1 @fill=none"}, + {"button", c_button, "name shape over=*shape press=*shape area=*shape"}, + {"text", c_text, "name text font size=100% color=white"}, + {"edittext", c_edittext, "name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"}, + + {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"}, + + // control tags + {"play", c_play, "sound loop=0 @nomultiple=0"}, + {"stop", c_stop, "sound"}, + + // object placement tags + {"put", c_put, " x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="}, + {"startclip", c_startclip, " x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="}, + {"change", c_change, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="}, + {"arcchange", c_arcchange, "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="}, + {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="}, + {"jump", c_jump, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= rotate= above= below="}, + {"del", c_del, "name"}, + // virtual object placement + {"buttonput", c_buttonput, " x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% rotate=0 above= below="}, + {"texture", c_texture, " x=0 y=0 scale= scalex=100% scaley=100% rotate=0"}, + {"point", c_point, "name x=0 y=0"}, + + // commands which start a block +//startclip (see above) + {"sprite", c_sprite, "name"}, + {"action", c_action, ""}, + + {"end", c_end, ""} +}; + + +static map_t parseArguments(char*command, char*pattern) +{ + char*x; + char*d,*e; + + string_t name[64]; + string_t value[64]; + int set[64]; + int isboolean[64]; + int pos; + int len; + int t; + string_t t1,t2; + map_t result; + map_init(&result); + + string_set(&t1, "commandname"); + string_set(&t2, command); + map_put(&result, t1, t2); + + if(!pattern || !*pattern) + return result; + + x = pattern; + + pos = 0; + + if(!strncmp(" ", x, 3)) { + readToken(); + if(type == COMMAND || type == LABEL) { + pushBack(); + syntaxerror("character name expected"); + } + name[pos].str = "instance"; + name[pos].len = 8; + value[pos].str = text; + value[pos].len = strlen(text); + set[pos] = 1; + pos++; + + if(type == ASSIGNMENT) + readToken(); + + name[pos].str = "character"; + name[pos].len = 9; + value[pos].str = text; + value[pos].len = strlen(text); + set[pos] = 1; + pos++; + + x+=4; + } + + while(*x) { + isboolean[pos] = (x[0] =='@'); + if(isboolean[pos]) + x++; + + d = strchr(x, ' '); + e = strchr(x, '='); + if(!d) + d=&x[strlen(x)]; + set[pos] = 0; + + if(!e || dtextlen?name[pos].len:textlen)) { + set[pos] = 1; + if(type == ASSIGNMENT) + readToken(); + value[pos].str = text; + value[pos].len = strlen(text); + /*printf("setting boolean parameter %s (to %s)\n", + strndup(name[pos], namelen[pos]), + strndup(value[pos], valuelen[pos]));*/ + break; + } + } + + // second, search for normal arguments + if(pos==len) + for(pos=0;postextlen?name[pos].len:textlen)) || + (type != ASSIGNMENT && !set[pos])) { + if(set[pos]) { + syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len)); + } + if(type == ASSIGNMENT) + readToken(); + set[pos] = 1; + value[pos].str = text; + value[pos].len = strlen(text); +#if 0//def DEBUG + printf("setting parameter %s (to %s)\n", + strndup(name[pos].str, name[pos].len), + strndup(value[pos].str, value[pos].len)); +#endif + break; + } + } + if(pos==len) { + syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command); + } + } +#if 0//def DEBUG + for(t=0;t