/* Copyright (c) 1997-2005 Miller Puckette, krzYszcz, and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* LATER think about abstractions */ /* LATER sort out escaping rules (also revisit binport.c) */ /* LATER quoting */ /* LATER rethink inlet/inlet~ case */ #ifdef UNIX #include #endif #ifdef _WIN32 #include #endif #include #include #include #include "m_pd.h" #include "g_canvas.h" #include "unstable/forky.h" #include "unstable/fragile.h" #include "unstable/fringe.h" #include "common/loud.h" #include "common/grow.h" #include "common/binport.h" #include "port.h" #ifdef KRZYSZCZ //#define PORT_DEBUG #endif #define PORT_LOG #define PORT_DUMP /* fill separate files with ignored data, e.g. pictures */ #define PORT_INISTACK 256 /* LATER rethink */ #define PORT_INISIZE 512 /* LATER rethink */ /* FIXME use messtree api */ enum { PORT_OK, /* MESSTREE_CONTINUE */ PORT_NEXT, /* next line, please: MESSTREE_OK */ PORT_UNKNOWN, PORT_CORRUPT, PORT_FATAL }; /* cf binport.c */ #define A_INT A_DEFFLOAT /* without access to sys_defaultfont, we just mimic defs from s_main.c */ #ifdef _WIN32 #define PORT_DEFFONTSIZE 12. #else #define PORT_DEFFONTSIZE 10. #endif #define PORT_XSTRETCH 1.25 #define PORT_YSTRETCH 1.1 #define PORT_WSTRETCH 1.25 typedef struct _port { t_binbuf *x_outbb; int x_messcount; int x_illmess; int x_lastunexpected; int x_lastbroken; int x_lastinconsistent; int x_nobj; int x_withbogus; int x_inatoms; t_atom *x_inmess; int x_outsize; int x_outatoms; t_atom *x_outmess; t_atom x_outini[PORT_INISIZE]; int x_stacksize; int x_stackdepth; int *x_stack; int x_stackini[PORT_INISTACK]; t_symbol *x_emstate; t_binbuf *x_embb; t_symbol *x_emname; int x_emsize; int x_emcount; int x_dumping; /* class-specifics, LATER find a better way */ FILE *x_pictfp; int x_pictno; } t_port; static t_symbol *portps_bogus; static t_symbol *portps_cleanup; static t_symbol *portps_inlet; static t_symbol *portps_outlet; static t_symbol *portps_vtable; static t_symbol *portps_coll; static t_symbol *portps_funbuff; static t_symbol *portps_prob; static t_symbol *portps_picture; static char *import_defmapping[] = { /* clashing clones */ "append", "Append", "b", "bangbang", "clip", "Clip", "clip~", "Clip~", "line~", "Line~", "scope~", "Scope~", "snapshot~", "Snapshot~", /* clashing dummies */ "biquad~", "Biquad~", "change", "Change", "key", "Key", "keyup", "Keyup", "line", "Line", "poly", "Poly", /* doomed dummies */ "appledvd", "c74.appledvd", "plugconfig", "c74.plugconfig", "plugin~", "c74.plugin~", "plugmidiin", "c74.plugmidiin", "plugmidiout", "c74.plugmidiout", "plugmod", "c74.plugmod", "plugmorph", "c74.plugmorph", "plugmultiparam", "c74.plugmultiparam", "plugout~", "c74.plugout~", "plugphasor~", "c74.plugphasor~", "plugreceive~", "c74.plugreceive~", "plugsend~", "c74.plugsend~", "plugstore", "c74.plugstore", "plugsync~", "c74.plugsync~", "pp", "c74.pp", "pptempo", "c74.pptempo", "pptime", "c74.pptime", "rewire~", "c74.rewire~", "sndmgrin~", "c74.sndmgrin~", "vdp", "c74.vdp", "vst~", "c74.vst~" }; static int import_mapsize = 0; static char **import_mapping = 0; static void import_setdefmapping(void) { import_mapsize = sizeof(import_defmapping)/(2 * sizeof(*import_defmapping)); import_mapping = import_defmapping; } void import_setmapping(int size, char **mapping) { import_mapsize = size; import_mapping = mapping; } char **import_getmapping(int *sizep) { if (!import_mapping) import_setdefmapping(); *sizep = import_mapsize; return (import_mapping); } char *port_usemapping(char *from, int mapsize, char **mapping) { while (mapsize--) { if (strcmp(*mapping, from)) mapping += 2; else return (mapping[1]); } return (0); } static t_int port_getint(t_port *x, int ndx) { if (ndx < x->x_inatoms) { t_atom *av = &x->x_inmess[ndx]; if (av->a_type == A_INT) return (av->a_w.w_index); else if (av->a_type == A_FLOAT) { loud_warning(0, "import", "[%d] float atom %d, int expected", x->x_messcount, ndx); return ((t_int)av->a_w.w_float); } } return (0); } static t_float port_getfloat(t_port *x, int ndx) { if (ndx < x->x_inatoms) { t_atom *av = &x->x_inmess[ndx]; return (av->a_type == A_FLOAT ? av->a_w.w_float : 0); } else return (0); } static t_symbol *port_getsymbol(t_port *x, int ndx) { if (ndx < x->x_inatoms) { t_atom *av = &x->x_inmess[ndx]; return (av->a_type == A_SYMBOL ? av->a_w.w_symbol : &s_); } else return (&s_); } static t_symbol *port_getanysymbol(t_port *x, int ndx) { t_symbol *sel = &s_; if (ndx < x->x_inatoms) { t_atom *av = &x->x_inmess[ndx]; if (av->a_type == A_SYMBOL) sel = av->a_w.w_symbol; else if (av->a_type == A_INT) sel = gensym("int"); else if (av->a_type == A_FLOAT) sel = &s_float; } return (sel); } static t_symbol *port_gettarget(t_port *x) { t_symbol *sel = port_getsymbol(x, 0); if (sel == &s_) loudbug_bug("port_gettarget"); return (sel); } static t_symbol *port_getselector(t_port *x) { t_symbol *sel = port_getanysymbol(x, 1); if (sel == &s_) loudbug_bug("port_getselector"); return (sel); } static int port_xstretch(float f) { return ((int)(f * PORT_XSTRETCH + 0.5)); } static int port_ystretch(float f) { return ((int)(f * PORT_YSTRETCH + 0.5)); } static int port_wstretch(float f) { return ((int)(f * PORT_WSTRETCH + 0.5)); } static t_float port_getx(t_port *x, int ndx) { return ((t_float)port_xstretch(port_getint(x, ndx))); } static t_float port_gety(t_port *x, int ndx) { return ((t_float)port_ystretch(port_getint(x, ndx))); } static t_float port_getwidth(t_port *x, int ndx) { return ((t_float)port_wstretch(port_getint(x, ndx))); } static void port_setxy(t_port *x, int ndx, t_atom *ap) { float f = port_getx(x, ndx); SETFLOAT(ap, f); ndx++; ap++; f = port_gety(x, ndx); SETFLOAT(ap, f); } static t_atom *import_copyatoms(t_atom *out, t_atom *in, int ac) { while (ac-- > 0) { if (in->a_type == A_INT) { out->a_type = A_FLOAT; out++->a_w.w_float = (float)in++->a_w.w_index; } else *out++ = *in++; } return (out); } static void import_unexpected(t_port *x) { if (x->x_lastunexpected < x->x_messcount) /* ignore redundant calls */ { x->x_lastunexpected = x->x_messcount; loud_warning(0, "import", "[%d] unexpected \"%s %s\"", x->x_messcount, port_gettarget(x)->s_name, port_getselector(x)->s_name); } } static void import_illegal(t_port *x) { x->x_illmess++; } static void import_flushillegal(t_port *x) { if (x->x_illmess) { if (x->x_illmess == 1) loud_warning(0, "import", "[%d] illegal line", x->x_messcount); else loud_warning(0, "import", "[%d] %d illegal lines", x->x_messcount, x->x_illmess); x->x_illmess = 0; } } static void import_embroken(t_port *x, char *cause) { if (x->x_lastbroken < x->x_messcount) /* ignore redundant calls */ { x->x_lastbroken = x->x_messcount; loud_warning(0, "import", "[%d] %s embedding broken by %s", x->x_messcount, x->x_emstate->s_name, cause); } } static int import_emcheck(t_port *x, t_symbol *state) { if (x->x_emstate == state) return (1); else if (x->x_emstate) import_embroken(x, state->s_name); else import_unexpected(x); return (0); } static void import_eminconsistent(t_port *x, t_symbol *state) { if (import_emcheck(x, state) && x->x_lastinconsistent < x->x_messcount) /* ignore redundant calls */ { x->x_lastinconsistent = x->x_messcount; loud_warning(0, "import", "[%d] %s embedding ended inconsistently", x->x_messcount, state->s_name); } } static int import_emcheckend(t_port *x, t_symbol *state, t_symbol *name) { if (import_emcheck(x, state)) { if (x->x_emcount /* empty ok for vtable, CHECKME other cases */ && x->x_emsize != x->x_emcount) loud_warning(0, "import", "[%d] corrupt %s (%d atoms declared, %d provided)", x->x_messcount, state->s_name, x->x_emsize, x->x_emcount); else { if (name != x->x_emname) /* warn and accept, LATER rethink */ import_eminconsistent(x, state); return (1); } } return (0); } static void import_emstart(t_port *x, t_symbol *state, t_symbol *name, int size) { if (x->x_emstate) import_embroken(x, state->s_name); x->x_emstate = state; binbuf_clear(x->x_embb); x->x_emname = name; x->x_emsize = size; x->x_emcount = 0; } static void import_emend(t_port *x, t_symbol *state, t_symbol *name) { import_emcheckend(x, state, name); x->x_emstate = 0; x->x_emname = 0; x->x_emsize = 0; x->x_emcount = 0; binbuf_clear(x->x_embb); } static void import_emflush(t_port *x, t_symbol *state, t_symbol *name) { int ac = binbuf_getnatom(x->x_embb); if (import_emcheckend(x, state, name) && ac) binbuf_add(x->x_outbb, ac, binbuf_getvec(x->x_embb)); x->x_emstate = 0; x->x_emname = 0; x->x_emsize = 0; x->x_emcount = 0; if (ac) binbuf_clear(x->x_embb); binbuf_addv(x->x_outbb, "ss;", gensym("#C"), gensym("restore")); } static int import_emcopy(t_port *x, t_symbol *state) { if (import_emcheck(x, state)) { t_atom *out = x->x_outmess; SETSYMBOL(out, gensym("#C")); out++; out = import_copyatoms(out, x->x_inmess + 1, x->x_inatoms - 1); SETSEMI(out); binbuf_add(x->x_embb, x->x_inatoms + 1, x->x_outmess); return (1); } else return (0); } static int import_emadd(t_port *x, t_symbol *state, int ac, t_atom *av) { if (import_emcheck(x, state)) { t_atom at; SETSYMBOL(&at, gensym("#C")); binbuf_add(x->x_embb, 1, &at); binbuf_add(x->x_embb, ac, av); binbuf_addsemi(x->x_embb); return (1); } else return (0); } static int import_emaddv(t_port *x, t_symbol *state, char *fmt, ...) { va_list ap; t_atom arg[64], *at = arg; int nargs = 0; char *fp = fmt; va_start(ap, fmt); SETSYMBOL(at, gensym("#C")); at++; nargs++; if (import_emcheck(x, state)) while (1) { switch(*fp++) { case 'i': SETFLOAT(at, va_arg(ap, t_int)); break; case 'f': SETFLOAT(at, va_arg(ap, double)); break; case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; case ';': SETSEMI(at); break; case 0: goto done; default: nargs = 0; goto done; } at++; nargs++; } done: va_end(ap); if (nargs > 1) { binbuf_add(x->x_embb, nargs, arg); return (1); } else return (0); } static void import_addclassname(t_port *x, char *outname, t_atom *inatom) { t_atom at; if (outname) SETSYMBOL(&at, gensym(outname)); else { t_symbol *insym = 0; if (inatom->a_type == A_SYMBOL) { /* LATER bash inatom to lowercase (CHECKME first) */ insym = inatom->a_w.w_symbol; if (import_mapping && import_mapsize) { char **fromp = import_mapping, **top = import_mapping + 1; int cnt = import_mapsize; while (cnt--) { if (strcmp(*fromp, insym->s_name)) { fromp += 2; top += 2; } else { insym = gensym(*top); inatom = 0; break; } } } if (insym != &s_bang && insym != &s_float && insym != &s_symbol && insym != &s_list && (insym == portps_inlet || insym == portps_outlet || zgetfn(&pd_objectmaker, insym) == 0)) { x->x_withbogus = 1; SETSYMBOL(&at, portps_bogus); binbuf_add(x->x_outbb, 1, &at); } } if (inatom) import_copyatoms(&at, inatom, 1); else if (insym) SETSYMBOL(&at, insym); else { loudbug_bug("import_addclassname"); SETSYMBOL(&at, gensym("???")); } } binbuf_add(x->x_outbb, 1, &at); } static int import_obj(t_port *x, char *name) { int ndx = (x->x_inmess[1].a_w.w_symbol == gensym("user") ? 3 : 2); binbuf_addv(x->x_outbb, "ssff", gensym("#X"), gensym("obj"), port_getx(x, ndx), port_gety(x, ndx + 1)); import_addclassname(x, name, &x->x_inmess[ndx == 2 ? 6 : 2]); binbuf_addsemi(x->x_outbb); x->x_nobj++; return (PORT_NEXT); } static int import_objarg(t_port *x, char *name) { int ndx = (x->x_inmess[1].a_w.w_symbol == gensym("user") ? 3 : 2); if (x->x_inatoms > 6 || (ndx == 3 && x->x_inatoms > 4)) { t_atom *out = x->x_outmess; SETSYMBOL(out, gensym("#X")); out++; SETSYMBOL(out, gensym("obj")); out++; port_setxy(x, ndx, out); binbuf_add(x->x_outbb, 4, x->x_outmess); import_addclassname(x, name, &x->x_inmess[ndx == 2 ? 6 : 2]); out = import_copyatoms(x->x_outmess, x->x_inmess + 7, x->x_inatoms - 7); SETSEMI(out); binbuf_add(x->x_outbb, x->x_inatoms - 6, x->x_outmess); x->x_nobj++; return (PORT_NEXT); } else return (PORT_CORRUPT); } static int imaction_N1_vpatcher(t_port *x, char *arg) { if (x->x_stackdepth >= x->x_stacksize) { int rqsz = x->x_stackdepth + 1; int sz = rqsz; x->x_stack = grow_withdata(&rqsz, &x->x_stackdepth, &x->x_stacksize, x->x_stack, PORT_INISTACK, x->x_stackini, sizeof(*x->x_stack)); if (rqsz != sz) { post("too many embedded patches"); return (PORT_FATAL); } } x->x_stack[x->x_stackdepth++] = x->x_nobj; x->x_nobj = 0; binbuf_addv(x->x_outbb, "ssfffff;", gensym("#N"), gensym("canvas"), port_getx(x, 2), port_gety(x, 3), (float)port_xstretch(port_getint(x, 4) - port_getint(x, 2)), (float)port_ystretch(port_getint(x, 5) - port_getint(x, 3)), PORT_DEFFONTSIZE); return (PORT_NEXT); } static int imaction_N1_vtable(t_port *x, char *arg) { int range = port_getint(x, 8), left = port_getint(x, 3), top = port_getint(x, 4), right = port_getint(x, 5), bottom = port_getint(x, 6), flags = port_getint(x, 7); import_emstart(x, portps_vtable, port_getsymbol(x, 9), port_getint(x, 2)); #ifdef PORT_DEBUG loudbug_post( "vtable \"%s\": size %d, range %d, coords %d %d %d %d, flags %d", x->x_emname->s_name, x->x_emsize, range, left, top, right, bottom, flags); #endif import_emaddv(x, portps_vtable, "si;", gensym("size"), x->x_emsize); import_emaddv(x, portps_vtable, "siiii;", gensym("flags"), /* CHECKED */ (flags & 16) != 0, (flags & 4) != 0, (flags & 8) != 0, (flags & 2) != 0); import_emaddv(x, portps_vtable, "si;", gensym("tabrange"), range); import_emaddv(x, portps_vtable, "siiiii;", gensym("_coords"), left, top, right, bottom, flags & 1); return (PORT_NEXT); } static int imaction_N1_coll(t_port *x, char *arg) { import_emstart(x, portps_coll, port_getsymbol(x, 2), 0); return (PORT_NEXT); } static int imaction_N1_funbuff(t_port *x, char *arg) { import_emstart(x, portps_funbuff, &s_, 0); import_emaddv(x, portps_funbuff, "si;", gensym("embed"), port_getint(x, 2) != 0); return (PORT_NEXT); } static int imaction_N1_prob(t_port *x, char *arg) { import_emstart(x, portps_prob, &s_, 0); return (PORT_NEXT); } static int imaction_N1_picture(t_port *x, char *arg) { import_emstart(x, portps_picture, 0, 0); if (x->x_pictfp) { import_unexpected(x); if (x->x_dumping) fclose(x->x_pictfp); x->x_pictfp = 0; } return (PORT_NEXT); } static int imaction_P6_patcher(t_port *x, char *arg) { if (x->x_withbogus) binbuf_addv(x->x_outbb, "ss;", portps_cleanup, portps_cleanup); binbuf_addv(x->x_outbb, "ssffss;", gensym("#X"), gensym("restore"), port_getx(x, 2), port_gety(x, 3), gensym("pd"), port_getsymbol(x, 7)); if (x->x_stackdepth) /* LATER consider returning PORT_FATAL otherwise */ x->x_stackdepth--; x->x_nobj = x->x_stack[x->x_stackdepth]; x->x_nobj++; return (PORT_NEXT); } static int imaction_P6_table(t_port *x, char *arg) { t_symbol *tablename = port_getsymbol(x, 7); binbuf_addv(x->x_outbb, "ssffs", gensym("#X"), gensym("obj"), port_getx(x, 2), port_gety(x, 3), gensym("Table")); if (tablename != &s_) { t_atom at; SETSYMBOL(&at, tablename); binbuf_add(x->x_outbb, 1, &at); } binbuf_addsemi(x->x_outbb); import_emflush(x, portps_vtable, tablename); x->x_nobj++; return (PORT_NEXT); } static int imaction_P6_coll(t_port *x, char *arg) { t_symbol *collname = port_getsymbol(x, 7); binbuf_addv(x->x_outbb, "ssffs", gensym("#X"), gensym("obj"), port_getx(x, 2), port_gety(x, 3), portps_coll); if (collname != &s_) { t_atom at; SETSYMBOL(&at, collname); binbuf_add(x->x_outbb, 1, &at); } binbuf_addsemi(x->x_outbb); import_emflush(x, portps_coll, collname); x->x_nobj++; return (PORT_NEXT); } static int imaction_P6_funbuff(t_port *x, char *arg) { binbuf_addv(x->x_outbb, "ssffs;", gensym("#X"), gensym("obj"), port_getx(x, 2), port_gety(x, 3), portps_funbuff); import_emflush(x, portps_funbuff, &s_); x->x_nobj++; return (PORT_NEXT); } static int imaction_P6_prob(t_port *x, char *arg) { binbuf_addv(x->x_outbb, "ssffs;", gensym("#X"), gensym("obj"), port_getx(x, 2), port_gety(x, 3), portps_prob); import_emflush(x, portps_prob, &s_); x->x_nobj++; return (PORT_NEXT); } /* LATER use hammer replacements */ static int imaction_P6_pack(t_port *x, char *arg) { int i; for (i = 7; i < x->x_inatoms; i++) { if (x->x_inmess[i].a_type == A_SYMBOL) { t_symbol *s = x->x_inmess[i].a_w.w_symbol; if (s->s_name[1]) { loud_warning(0, "import", "%s's argument '%s' bashed to 's'", port_getsymbol(x, 6)->s_name, s->s_name); x->x_inmess[i].a_w.w_symbol = gensym("s"); } else switch (*s->s_name) { case 'b': case 'f': case 's': case 'l': break; case 'i': x->x_inmess[i].a_w.w_symbol = gensym("f"); break; default: x->x_inmess[i].a_w.w_symbol = gensym("s"); } } } return (PORT_OK); } /* LATER consider using hammer replacements */ static int imaction_P6_midi(t_port *x, char *arg) { x->x_inatoms = 7; /* ugly, LATER rethink */ return (PORT_OK); } static int imaction_P2_scope(t_port *x, char *name) { if (x->x_inatoms > 6) { t_atom *out = x->x_outmess; int i, xpix, ypix; SETSYMBOL(out, gensym("#X")); out++; SETSYMBOL(out, gensym("obj")); out++; port_setxy(x, 3, out); xpix = (int)out++->a_w.w_float; ypix = (int)out->a_w.w_float; binbuf_add(x->x_outbb, 4, x->x_outmess); import_addclassname(x, name, &x->x_inmess[2]); out = x->x_outmess; port_setxy(x, 5, out); out++->a_w.w_float -= xpix; out++->a_w.w_float -= ypix; out = import_copyatoms(out, x->x_inmess + 7, x->x_inatoms - 7); SETSEMI(out); binbuf_add(x->x_outbb, x->x_inatoms - 4, x->x_outmess); x->x_nobj++; return (PORT_NEXT); } else return (PORT_CORRUPT); } /* width fontsize fontfamily encoding fontprops red green blue text... */ static int imaction_P1_comment(t_port *x, char *arg) { int outatoms; SETSYMBOL(x->x_outmess, gensym("#X")); SETSYMBOL(x->x_outmess + 1, gensym("obj")); port_setxy(x, 2, x->x_outmess + 2); SETSYMBOL(x->x_outmess + 4, gensym("comment")); if (x->x_inatoms > 5) { int i, fontsize, fontprops; float width = port_getwidth(x, 4); t_atom *ap = x->x_inmess + 5; SETFLOAT(x->x_outmess + 5, width); if (ap->a_type == A_INT) { fontsize = ap->a_w.w_index & 0x0ff; fontprops = ap->a_w.w_index >> 8; } else if (ap->a_type == A_FLOAT) /* FIXME */ { fontsize = ((int)ap->a_w.w_float) & 0x0ff; fontprops = ((int)ap->a_w.w_float) >> 8; } else fontsize = 10, fontprops = 0; SETFLOAT(x->x_outmess + 6, fontsize); SETSYMBOL(x->x_outmess + 7, gensym("helvetica")); SETSYMBOL(x->x_outmess + 8, gensym("?")); SETFLOAT(x->x_outmess + 9, fontprops); SETFLOAT(x->x_outmess + 10, 0); SETFLOAT(x->x_outmess + 11, 0); SETFLOAT(x->x_outmess + 12, 0); outatoms = x->x_inatoms + 7; import_copyatoms(x->x_outmess + 13, x->x_inmess + 6, x->x_inatoms - 6); } else outatoms = 5; SETSEMI(x->x_outmess + outatoms); binbuf_add(x->x_outbb, outatoms + 1, x->x_outmess); x->x_nobj++; return (PORT_NEXT); } static int imaction_P1_message(t_port *x, char *arg) { int i; t_atom *out; SETSYMBOL(x->x_outmess, gensym("#X")); SETSYMBOL(x->x_outmess + 1, gensym("msg")); port_setxy(x, 2, x->x_outmess + 2); out = import_copyatoms(x->x_outmess + 4, x->x_inmess + 6, x->x_inatoms - 6); SETSEMI(out); binbuf_add(x->x_outbb, x->x_inatoms - 1, x->x_outmess); x->x_nobj++; return (PORT_NEXT); } static int imaction_P1_io(t_port *x, char *arg) { binbuf_addv(x->x_outbb, "ssff", gensym("#X"), gensym("obj"), port_getx(x, 2), port_gety(x, 3)); if (x->x_inmess[1].a_w.w_symbol == portps_inlet || x->x_inmess[1].a_w.w_symbol == portps_outlet) { t_atom at; SETSYMBOL(&at, portps_bogus); binbuf_add(x->x_outbb, 1, &at); } binbuf_add(x->x_outbb, 1, &x->x_inmess[1]); binbuf_addsemi(x->x_outbb); x->x_nobj++; return (PORT_NEXT); } static int imaction_P1_number(t_port *x, char *arg) { binbuf_addv(x->x_outbb, "ssff;", gensym("#X"), gensym("floatatom"), port_getx(x, 2), port_gety(x, 3)); x->x_nobj++; return (PORT_NEXT); } static int imaction_P1_vpicture(t_port *x, char *arg) { import_emend(x, portps_picture, 0); if (x->x_pictfp) { if (x->x_dumping) fclose(x->x_pictfp); x->x_pictfp = 0; } else import_unexpected(x); binbuf_addv(x->x_outbb, "ssffs;", gensym("#X"), gensym("obj"), port_getx(x, 2), port_gety(x, 3), gensym("vpicture")); x->x_nobj++; return (PORT_NEXT); } static int imaction_P1_connect(t_port *x, char *arg) { binbuf_addv(x->x_outbb, "ssiiii;", gensym("#X"), gensym("connect"), x->x_nobj - port_getint(x, 2) - 1, port_getint(x, 3), x->x_nobj - port_getint(x, 4) - 1, port_getint(x, 5)); return (PORT_NEXT); } static int imaction_T1_int(t_port *x, char *arg) { if (x->x_emstate == portps_coll) import_emcopy(x, portps_coll); else if (x->x_emstate == portps_prob) import_emcopy(x, portps_prob); else import_unexpected(x); return (PORT_NEXT); } static int imaction_T1_flags(t_port *x, char *arg) { import_emcopy(x, portps_coll); return (PORT_NEXT); } static int imaction_T1_set(t_port *x, char *arg) { if (x->x_emstate == portps_vtable) { if (import_emcopy(x, portps_vtable)) { int count = port_getint(x, 2); if (count != x->x_emcount) loud_warning(0, "import", "[%d] bad vtable chunk index %d (%d already taken)", x->x_messcount, count, x->x_emcount); x->x_emcount += x->x_inatoms - 3; } } else if (x->x_emstate == portps_funbuff) import_emcopy(x, portps_funbuff); else import_unexpected(x); return (PORT_NEXT); } static int imaction_T1_reset(t_port *x, char *arg) { import_emcopy(x, portps_prob); return (PORT_NEXT); } static int imaction_T1_embed(t_port *x, char *arg) { import_emcopy(x, portps_prob); return (PORT_NEXT); } static int imaction_K1_replace(t_port *x, char *arg) { if (x->x_pictfp) { import_unexpected(x); if (x->x_dumping) fclose(x->x_pictfp); x->x_pictfp = 0; } else if (import_emcheck(x, portps_picture)) { char buf[32]; x->x_emsize = port_getint(x, 2); x->x_emcount = 0; sprintf(buf, "port-%02d.pict", ++x->x_pictno); if (x->x_dumping) { if (x->x_pictfp = sys_fopen(buf, "wb")) { int i; for (i = 0; i < 512; i++) fputc(0, x->x_pictfp); } } else x->x_pictfp = (FILE *)1; } return (PORT_NEXT); } static int imaction_K1_set(t_port *x, char *arg) { if (!x->x_pictfp) import_unexpected(x); else if (import_emcheck(x, portps_picture)) { int i, count = port_getint(x, 2); if (count != x->x_emcount) loud_warning(0, "import", "[%d] bad picture chunk index %d (%d already taken)", x->x_messcount, count, x->x_emcount); x->x_emcount += x->x_inatoms - 3; if (x->x_dumping) { for (i = 3; i < x->x_inatoms; i++) { int v = port_getint(x, i); fputc(v >> 24, x->x_pictfp); fputc((v >> 16) & 0x0ff, x->x_pictfp); fputc((v >> 8) & 0x0ff, x->x_pictfp); fputc(v & 0x0ff, x->x_pictfp); } } } return (PORT_NEXT); } typedef int (*t_portaction)(t_port *, char *arg); typedef struct _portslot { char *s_name; t_portaction s_action; char *s_actionarg; struct _portnode *s_subtree; t_symbol *s_symbol; } t_portslot; typedef struct _portnode /* a parser's symbol definition, sort of... */ { t_portslot *n_table; int n_nslots; int n_index; } t_portnode; #define PORT_NSLOTS(slots) (sizeof(slots)/sizeof(*(slots))) static t_portslot imslots__N[] = { { "vpatcher", imaction_N1_vpatcher, 0, 0, 0 }, { "vtable", imaction_N1_vtable, 0, 0, 0 }, { "coll", imaction_N1_coll, 0, 0, 0 }, { "funbuff", imaction_N1_funbuff, 0, 0, 0 }, { "prob", imaction_N1_prob, 0, 0, 0 }, { "picture", imaction_N1_picture, 0, 0, 0 } }; static t_portnode imnode__N = { imslots__N, PORT_NSLOTS(imslots__N), 1 }; static t_portslot imslots_newobj[] = { { "patcher", imaction_P6_patcher, 0, 0, 0 }, { "p", imaction_P6_patcher, 0, 0, 0 }, { "table", imaction_P6_table, 0, 0, 0 }, { "coll", imaction_P6_coll, 0, 0, 0 }, { "funbuff", imaction_P6_funbuff, 0, 0, 0 }, { "prob", imaction_P6_prob, 0, 0, 0 } }; static t_portnode imnode_newobj = { imslots_newobj, PORT_NSLOTS(imslots_newobj), 6 }; /* LATER consider merging newobj and newex */ static t_portslot imslots_newex[] = { { "key", import_obj, "Key", 0, 0 }, { "keyup", import_obj, "Keyup", 0, 0 }, { "pack", imaction_P6_pack, 0, 0, 0 }, { "unpack", imaction_P6_pack, 0, 0, 0 }, { "trigger", imaction_P6_pack, 0, 0, 0 }, { "t", imaction_P6_pack, 0, 0, 0 }, { "midiin", imaction_P6_midi, 0, 0, 0 }, { "midiout", imaction_P6_midi, 0, 0, 0 }, { "notein", imaction_P6_midi, 0, 0, 0 }, { "noteout", imaction_P6_midi, 0, 0, 0 }, { "pgmin", imaction_P6_midi, 0, 0, 0 }, { "pgmout", imaction_P6_midi, 0, 0, 0 }, { "ctlin", imaction_P6_midi, 0, 0, 0 }, { "ctlout", imaction_P6_midi, 0, 0, 0 }, { "bendin", imaction_P6_midi, 0, 0, 0 }, { "bendout", imaction_P6_midi, 0, 0, 0 }, /* LATER rethink */ { "Borax", import_objarg, "Borax", 0, 0 }, { "Bucket", import_objarg, "Bucket", 0, 0 }, { "Decode", import_objarg, "Decode", 0, 0 }, { "Histo", import_objarg, "Histo", 0, 0 }, { "MouseState", import_objarg, "MouseState", 0, 0 }, { "Peak", import_objarg, "Peak", 0, 0 }, { "TogEdge", import_objarg, "TogEdge", 0, 0 }, { "Trough", import_objarg, "Trough", 0, 0 }, { "Uzi", import_objarg, "Uzi", 0, 0 } }; static t_portnode imnode_newex = { imslots_newex, PORT_NSLOTS(imslots_newex), 6 }; static t_portslot imslots_user[] = { { "GSwitch", import_objarg, "Gswitch", 0, 0 }, { "GSwitch2", import_objarg, "Ggate", 0, 0 }, { "number~", import_obj, 0, 0, 0 }, { "scope~", imaction_P2_scope, "Scope~", 0, 0 }, { "uslider", import_obj, "vsl", 0, 0 } /* LATER range and offset */ }; static t_portnode imnode_user = { imslots_user, PORT_NSLOTS(imslots_user), 2 }; static t_portslot imslots__P[] = { { "comment", imaction_P1_comment, 0, 0, 0 }, { "message", imaction_P1_message, 0, 0, 0 }, { "newobj", import_objarg, 0, &imnode_newobj, 0 }, { "newex", import_objarg, 0, &imnode_newex, 0 }, { "inlet", imaction_P1_io, 0, 0, 0 }, { "inlet~", imaction_P1_io, 0, 0, 0 }, { "outlet", imaction_P1_io, 0, 0, 0 }, { "outlet~", imaction_P1_io, 0, 0, 0 }, { "number", imaction_P1_number, 0, 0, 0 }, { "flonum", imaction_P1_number, 0, 0, 0 }, { "button", import_obj, "bng", 0, 0 }, { "slider" , import_obj, "vsl", 0, 0 }, /* LATER range and offset */ { "hslider", import_obj, "hsl", 0, 0 }, /* LATER range and offset */ { "toggle", import_obj, "tgl", 0, 0 }, { "user", import_objarg, 0, &imnode_user, 0 }, /* state is embedded in #N vpreset ; #X append... */ { "preset", import_obj, "preset", 0, 0 }, /* an object created from the "Paste Picture" menu, state is embedded in #N picture; #K...; */ { "vpicture", imaction_P1_vpicture, 0, 0, 0 }, { "connect", imaction_P1_connect, 0, 0, 0 }, { "fasten", imaction_P1_connect, 0, 0, 0 } }; static t_portnode imnode__P = { imslots__P, PORT_NSLOTS(imslots__P), 1 }; static t_portslot imslots__T[] = { { "int", imaction_T1_int, 0, 0, 0 }, { "flags", imaction_T1_flags, 0, 0, 0 }, { "set", imaction_T1_set, 0, 0, 0 }, { "reset", imaction_T1_reset, 0, 0, 0 }, { "embed", imaction_T1_embed, 0, 0, 0 } }; static t_portnode imnode__T = { imslots__T, PORT_NSLOTS(imslots__T), 1 }; static t_portslot imslots__K[] = { { "replace", imaction_K1_replace, 0, 0, 0 }, { "set", imaction_K1_set, 0, 0, 0 } }; static t_portnode imnode__K = { imslots__K, PORT_NSLOTS(imslots__K), 1 }; static t_portslot imslots_[] = { { "#N", 0, 0, &imnode__N, 0 }, { "#P", 0, 0, &imnode__P, 0 }, { "#T", 0, 0, &imnode__T, 0 }, { "#K", 0, 0, &imnode__K, 0 } }; static t_portnode imnode_ = { imslots_, PORT_NSLOTS(imslots_), 0 }; static int port_doparse(t_port *x, t_portnode *node) { int nslots = node->n_nslots; if (nslots > 0) { t_portslot *slot = node->n_table; t_symbol *insym = port_getanysymbol(x, node->n_index); char *inname = 0; secondpass: while (nslots--) { if (slot->s_symbol == insym || (inname && shared_matchignorecase(inname, slot->s_name))) { if (slot->s_subtree) { int nobj = x->x_nobj; int result = port_doparse(x, slot->s_subtree); if (result == PORT_FATAL || result == PORT_CORRUPT || result == PORT_NEXT) return (result); } if (slot->s_action) return (slot->s_action(x, slot->s_actionarg)); else return (PORT_OK); /* LATER rethink */ } slot++; } if (!inname) { inname = insym->s_name; nslots = node->n_nslots; slot = node->n_table; goto secondpass; } } else loudbug_bug("port_doparse"); return (PORT_UNKNOWN); } static int port_parsemessage(t_port *x) { import_flushillegal(x); x->x_messcount++; return (port_doparse(x, &imnode_)); } static void port_startparsing(t_port *x) { #ifdef PORT_DEBUG loudbug_post("parsing..."); #endif x->x_messcount = 0; x->x_illmess = 0; x->x_lastunexpected = -1; x->x_lastbroken = -1; x->x_lastinconsistent = -1; x->x_nobj = 0; x->x_emstate = 0; binbuf_clear(x->x_embb); x->x_pictno = 0; x->x_pictfp = 0; } static void port_endparsing(t_port *x) { import_flushillegal(x); if (x->x_emstate) { import_embroken(x, "end of file"); x->x_emstate = 0; } binbuf_clear(x->x_embb); if (x->x_pictfp) { loud_warning(0, "import", "incomplete picture"); if (x->x_dumping) fclose(x->x_pictfp); x->x_pictfp = 0; } #ifdef PORT_DEBUG loudbug_post("end of parsing"); #endif } static void port_dochecksetup(t_portnode *node) { t_portslot *slots = node->n_table; int i, nslots = node->n_nslots; for (i = 0; i < nslots; i++) { t_portnode *subtree = slots[i].s_subtree; slots[i].s_symbol = gensym(slots[i].s_name); if (subtree) port_dochecksetup(subtree); } import_setdefmapping(); } #define BOGUS_NINLETS 23 #define BOGUS_NOUTLETS 24 typedef struct _bogus { t_object x_ob; t_glist *x_glist; /* used also as 'dirty' flag */ int x_bound; t_inlet *x_inlets[BOGUS_NINLETS]; t_outlet *x_outlets[BOGUS_NOUTLETS]; t_clock *x_clock; } t_bogus; typedef struct _bogushook { t_pd x_pd; t_pd *x_who; t_glist *x_glist; /* used also as 'dirty' flag */ t_clock *x_clock; } t_bogushook; static t_class *bogus_class; static t_class *bogushook_class; static void bogus_tick(t_bogus *x) { if (x->x_bound) { #ifdef PORT_DEBUG loudbug_post("bogus_tick: unbinding '%x'", (int)x); #endif pd_unbind((t_pd *)x, portps_cleanup); x->x_bound = 0; } } static void bogushook_tick(t_bogushook *x) { pd_free((t_pd *)x); } static void bogus_cleanup(t_bogus *x) { if (x->x_glist && x->x_glist == canvas_getcurrent()) { t_text *t = (t_text *)x; int ac = binbuf_getnatom(t->te_binbuf); if (ac) { t_atom *av = binbuf_getvec(t->te_binbuf); t_binbuf *bb = binbuf_new(); t_inlet **ip; t_outlet **op; int i; #ifdef PORT_DEBUG loudbug_startpost("self-adjusting "); loudbug_postbinbuf(t->te_binbuf); #endif binbuf_add(bb, ac - 1, av + 1); binbuf_free(t->te_binbuf); t->te_binbuf = bb; for (i = BOGUS_NINLETS, ip = x->x_inlets + BOGUS_NINLETS - 1; i ; i--, ip--) { if (forky_hasfeeders((t_object *)x, x->x_glist, i, 0)) break; else inlet_free(*ip); } #ifdef PORT_DEBUG loudbug_post("%d inlets deleted", BOGUS_NINLETS - i); #endif for (i = 0, op = x->x_outlets + BOGUS_NOUTLETS - 1; i < BOGUS_NOUTLETS; i++, op--) { if (fragile_outlet_connections(*op)) break; else outlet_free(*op); } #ifdef PORT_DEBUG loudbug_post("%d outlets deleted", i); #endif glist_retext(x->x_glist, t); } else loudbug_bug("bogus_cleanup"); x->x_glist = 0; clock_delay(x->x_clock, 0); } } static void bogus_free(t_bogus *x) { if (x->x_bound) pd_unbind((t_pd *)x, portps_cleanup); if (x->x_clock) clock_free(x->x_clock); } static void *bogus_new(t_symbol *s, int ac, t_atom *av) { t_bogus *x = 0; t_glist *glist; if (glist = canvas_getcurrent()) { char buf[80]; int i; if (av->a_type == A_SYMBOL) { t_pd *z; if (z = forky_newobject(av->a_w.w_symbol, ac - 1, av + 1)) { t_bogushook *y = (t_bogushook *)pd_new(bogushook_class); y->x_who = z; y->x_glist = glist; pd_bind((t_pd *)y, portps_cleanup); y->x_clock = clock_new(y, (t_method)bogushook_tick); #ifdef PORT_DEBUG loudbug_post("reclaiming %s", av->a_w.w_symbol->s_name); #endif return (z); } } x = (t_bogus *)pd_new(bogus_class); atom_string(av, buf, 80); loud_error((t_pd *)x, "unknown class '%s'", buf); x->x_glist = glist; for (i = 0; i < BOGUS_NINLETS; i++) x->x_inlets[i] = inlet_new((t_object *)x, (t_pd *)x, 0, 0); for (i = 0; i < BOGUS_NOUTLETS; i++) x->x_outlets[i] = outlet_new((t_object *)x, &s_anything); pd_bind((t_pd *)x, portps_cleanup); x->x_bound = 1; x->x_clock = clock_new(x, (t_method)bogus_tick); } return (x); } static void bogushook_cleanup(t_bogushook *x) { if (x->x_glist) { t_text *t = (t_text *)x->x_who; int ac = binbuf_getnatom(t->te_binbuf); if (ac > 1) { int dorecreate = 0; t_atom *av = binbuf_getvec(t->te_binbuf); t_binbuf *bb = binbuf_new(); #ifdef PORT_DEBUG loudbug_startpost("hook-adjusting "); loudbug_postbinbuf(t->te_binbuf); #endif ac--; av++; if (av->a_type == A_SYMBOL) { if (av->a_w.w_symbol == portps_outlet) { if (forky_hasfeeders((t_object *)x->x_who, x->x_glist, 0, &s_signal)) { t_atom at; SETSYMBOL(&at, gensym("outlet~")); binbuf_add(bb, 1, &at); ac--; av++; dorecreate = 1; } } else if (av->a_w.w_symbol == portps_inlet) { /* LATER */ } } if (ac) binbuf_add(bb, ac, av); if (dorecreate) gobj_recreate(x->x_glist, (t_gobj *)t, bb); else { binbuf_free(t->te_binbuf); t->te_binbuf = bb; glist_retext(x->x_glist, t); } } else loudbug_bug("bogushook_cleanup"); x->x_glist = 0; clock_delay(x->x_clock, 0); } } static void bogushook_free(t_bogushook *x) { #ifdef PORT_DEBUG loudbug_post("destroing the hook of '%s'", class_getname(*x->x_who)); #endif pd_unbind((t_pd *)x, portps_cleanup); if (x->x_clock) clock_free(x->x_clock); } static void port_checksetup(void) { static int done = 0; if (!done) { port_dochecksetup(&imnode_); portps_bogus = gensym("_port.bogus"); portps_cleanup = gensym("_port.cleanup"); portps_inlet = gensym("inlet"); portps_outlet = gensym("outlet"); portps_vtable = gensym("vtable"); portps_coll = gensym("coll"); portps_funbuff = gensym("funbuff"); portps_prob = gensym("prob"); portps_picture = gensym("picture"); if (zgetfn(&pd_objectmaker, portps_bogus) == 0) { bogus_class = class_new(portps_bogus, (t_newmethod)bogus_new, (t_method)bogus_free, sizeof(t_bogus), 0, A_GIMME, 0); class_addmethod(bogus_class, (t_method)bogus_cleanup, portps_cleanup, 0); bogushook_class = class_new(gensym("_port.bogushook"), 0, (t_method)bogushook_free, sizeof(t_bogushook), CLASS_PD, 0); class_addmethod(bogushook_class, (t_method)bogushook_cleanup, portps_cleanup, 0); } done = 1; } } static t_port *port_new(void) { t_port *x = (t_port *)getbytes(sizeof(*x)); x->x_outbb = 0; x->x_withbogus = 0; x->x_outsize = PORT_INISIZE; x->x_outatoms = 0; x->x_outmess = x->x_outini; x->x_stacksize = PORT_INISTACK; x->x_stackdepth = 0; x->x_stack = x->x_stackini; x->x_emstate = 0; x->x_embb = binbuf_new(); #ifdef PORT_DUMP x->x_dumping = 1; #else x->x_dumping = 0; #endif return (x); } static void port_free(t_port *x) { if (portps_cleanup->s_thing) { /* clean up toplevel glist */ typedmess(portps_cleanup->s_thing, portps_cleanup, 0, 0); /* LATER unbind all bogus objects, and destroy all bogushooks by traversing the portps_cleanup's bindlist, instead of using per-object clocks. Need to have bindlist traversal in Pd API first... Otherwise, consider fragilizing this (and fragilizing grab too). */ } if (x->x_outmess != x->x_outini) freebytes(x->x_outmess, x->x_outsize * sizeof(*x->x_outmess)); if (x->x_stack != x->x_stackini) freebytes(x->x_stack, x->x_stacksize * sizeof(*x->x_stack)); if (x->x_embb) binbuf_free(x->x_embb); freebytes(x, sizeof(*x)); } static int import_binbuf(t_port *x, t_binbuf *inbb, t_binbuf *outbb) { int result = PORT_OK; t_atom *av = binbuf_getvec(inbb); int ac = binbuf_getnatom(inbb); int startmess, endmess; x->x_outbb = outbb; port_startparsing(x); for (startmess = 0; startmess < ac; startmess = endmess + 1) { t_atom *mess = av + startmess, *ap; int i; for (endmess = startmess, ap = mess; ap->a_type != A_SEMI; endmess++, ap++) if (endmess == ac) { result = PORT_CORRUPT; /* no final semi */ goto endparsing; } if (endmess == startmess || endmess == startmess + 1 || mess->a_type != A_SYMBOL) { startmess = endmess + 1; import_illegal(x); continue; } if (mess[1].a_type != A_SYMBOL) { if (mess[1].a_type != A_INT && mess[1].a_type != A_FLOAT) { startmess = endmess + 1; import_illegal(x); continue; } } else if (mess[1].a_w.w_symbol == gensym("hidden")) { t_symbol *sel = mess[1].a_w.w_symbol; mess[1].a_w.w_symbol = mess->a_w.w_symbol; startmess++; mess++; if (endmess == startmess + 1 || mess[1].a_type != A_SYMBOL) { startmess = endmess + 1; import_illegal(x); continue; } } x->x_inatoms = endmess - startmess; x->x_inmess = mess; if ((i = x->x_inatoms + 16) > x->x_outsize) /* LATER rethink */ { int sz = i; x->x_outmess = grow_nodata(&sz, &x->x_outsize, x->x_outmess, PORT_INISIZE, x->x_outini, sizeof(*x->x_outmess)); if (sz != i) { startmess = endmess + 1; continue; /* LATER rethink */ } } /* dollar signs in file translate to symbols, LATER rethink, also #-signs */ for (i = 0, ap = x->x_inmess; i < x->x_inatoms; i++, ap++) { if (ap->a_type == A_DOLLAR) { char buf[100]; sprintf(buf, "$%d", ap->a_w.w_index); SETSYMBOL(ap, gensym(buf)); } else if (ap->a_type == A_DOLLSYM) { char buf[100]; sprintf(buf, "$%s", ap->a_w.w_symbol->s_name); SETSYMBOL(ap, gensym(buf)); } } if (port_parsemessage(x) == PORT_FATAL) { result = PORT_FATAL; goto endparsing; } } endparsing: port_endparsing(x); return (result); } int import_max(char *fn, char *dir) { int result; t_port *x; t_binbuf *inbb, *outbb; int fd; char buf[MAXPDSTRING], *bufp; t_pd *stackp = 0; int dspstate = canvas_suspend_dsp(); port_checksetup(); if ((fd = open_via_path(dir, fn, "", buf, &bufp, MAXPDSTRING, 0)) < 0) { loud_error(0, "%s: can't open", fn); return (BINPORT_NOFILE); } else close (fd); x = port_new(); inbb = binbuf_new(); glob_setfilename(0, gensym(bufp), gensym(buf)); result = binport_read(inbb, bufp, buf); if (result == BINPORT_MAXBINARY || result == BINPORT_MAXTEXT || result == BINPORT_MAXOLD) { int bbresult; #ifdef PORT_DEBUG binport_write(inbb, "import-debug.pat", ""); #endif outbb = binbuf_new(); if ((bbresult = import_binbuf(x, inbb, outbb)) != PORT_OK) { loud_error(0, "%s: import failed (%d)", fn, bbresult); if (bbresult == PORT_CORRUPT) result = BINPORT_CORRUPT; else result = BINPORT_FAILED; binbuf_free(outbb); outbb = 0; } binbuf_free(inbb); #ifdef PORT_LOG if (outbb) binbuf_write(outbb, "import-result.pd", "", 0); #endif } else if (result == BINPORT_PDFILE) outbb = inbb; else { perror(fn); /* FIXME */ binbuf_free(inbb); outbb = 0; } if (outbb) { binbuf_eval(outbb, 0, 0, 0); binbuf_free(outbb); } port_free(x); glob_setfilename(0, &s_, &s_); canvas_resume_dsp(dspstate); while ((stackp != s__X.s_thing) && (stackp = s__X.s_thing)) vmess(stackp, gensym("pop"), "i", 1); #if 0 /* LATER */ pd_doloadbang(); #endif return (result); }