Reverse engineering SWF to fix limitation in SWFTools

This post will be in English as you can see. And it’s very specific, maybe it won’t interest most of the usual readers.

A couple days ago I was hitting my head against an almost impossible to solve problem as I described in my cry for help at StackOverflow: swftools and events creating internal link

The solution was far from easy because debug in Flash is a great way to pay for your sins to save purgatory time. So I decided to take the way of reverse engineering.

Getting an working example and using swfdump -D in it I figured out that the swf dump should look like it:

                [022]       227 DEFINEBUTTON2 defines id 0011
                 condition 00a1
                 (   18 bytes) action: Push int:2 int:1 String:"butMC4"
                 (    0 bytes) action: GetVariable
                 (   13 bytes) action: Push String:"gotoAndPlay"
                 (    0 bytes) action: CallMethod
                 (    0 bytes) action: Pop
                 (    7 bytes) action: Push String:"_root"
                 (    0 bytes) action: GetVariable
                 (   11 bytes) action: Push String:"canflip" bool:false
                 (    0 bytes) action: SetMember
                 (    0 bytes) action: End
                 condition 0152
                 (   18 bytes) action: Push int:11 int:1 String:"butMC4"
                 (    0 bytes) action: GetVariable
                 (   13 bytes) action: Push String:"gotoAndPlay"
                 (    0 bytes) action: CallMethod
                 (    0 bytes) action: Pop
                 (    7 bytes) action: Push String:"_root"
                 (    0 bytes) action: GetVariable
                 (   11 bytes) action: Push String:"canflip" bool:true
                 (    0 bytes) action: SetMember
                 (    0 bytes) action: End
                 condition 0008
                 (   25 bytes) action: Constantpool(3 entries) String:"_root" String:"canflip" String:"gotoPage"
                 (    2 bytes) action: Push Lookup:0 ("_root")
                 (    0 bytes) action: GetVariable
                 (    4 bytes) action: Push Lookup:1 ("canflip") bool:true
                 (    0 bytes) action: SetMember
                 (   14 bytes) action: Push bool:true int:10 int:2 Lookup:0 ("_root")
                 (    0 bytes) action: GetVariable
                 (    2 bytes) action: Push Lookup:2 ("gotoPage")
                 (    0 bytes) action: CallMethod
                 (    0 bytes) action: Pop
                 (    0 bytes) action: End

To have the events implemented.

I changed the swftools, to get the pdf2swf producing the correct code to the internal links. It was an iterative process. Change C code, compile, run, compare against correct dump.

The final form of the function

void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
{
    swfoutput_internal*i = (swfoutput_internal*)dev->internal;
    ActionTAG* actions = 0;
    ActionTAG* actionOvOn = 0;
    ActionTAG* actionOvOff = 0;
 
    if(i->shapeid>=0)
	endshape(dev);
    if(i->textmode)
	endtext(dev);
        /* 
            _root.canflip=true; // flipping enabled
            _root.gotoPage(4,true); // go to page
        */
	actions = action_PushString(actions, "_root"); //function name
        actions = action_GetVariable(actions);
	actions = action_PushString(actions, "canFlip"); //function name
	actions = action_PushBoolean(actions,1);
        actions = action_SetMember(actions);
	actions = action_PushBoolean(actions,0);
	actions = action_PushInt(actions, page); //parameter
	actions = action_PushInt(actions, 2); //number of parameters (2)
	actions = action_PushString(actions, "_root"); //function name
        actions = action_GetVariable(actions);
        actions = action_PushString(actions,"gotoPage");
	actions = action_CallMethod(actions);
        actions = action_Pop(actions);
	actions = action_End(actions);
 
        /* 
            _root.canflip=false; // flipping enabled
        */
        actionOvOn = action_PushString(actionOvOn, "_root"); //function name
        actionOvOn = action_GetVariable(actionOvOn);
        actionOvOn = action_PushString(actionOvOn, "canFlip"); //function name
        actionOvOn = action_PushBoolean(actionOvOn,0);
        actionOvOn = action_SetMember(actionOvOn);
	actionOvOn = action_End(actionOvOn);
 
        /* 
            _root.canflip=true; // flipping enabled
        */
        actionOvOff = action_PushString(actionOvOff, "_root"); //function name
        actionOvOff = action_GetVariable(actionOvOff);
        actionOvOff = action_PushString(actionOvOff, "canFlip"); //function name
        actionOvOff = action_PushBoolean(actionOvOff,1);
        actionOvOff = action_SetMember(actionOvOff);
	actionOvOff = action_End(actionOvOff);
 
        printf("Debug: Actions GotoPage %d\n",page);
        char name[80];
        sprintf(name, "page%d", page);
 
	drawlink3 (dev,actions,actionOvOff,actionOvOn,points,page,name);
        swf_ActionFree(actions);
	swf_ActionFree(actionOvOn);
	swf_ActionFree(actionOvOff);
}

I had to create a new function to attach 3 events to a button. The optimal solution should have a variable number of events, but I didn’t have time for that.

static void drawlink3(gfxdevice_t*dev, ActionTAG*actions1, 
                      ActionTAG*actions2, ActionTAG*actions3, 
                      gfxline_t*points, char*type, const char*url)
{
    swfoutput_internal*i = (swfoutput_internal*)dev->internal;
    RGBA rgb;
    SRECT r;
    int lsid=0;
    int fsid;
    int myshapeid;
    int myshapeid2;
    double posx = 0;
    double posy = 0;
    int buttonid = getNewID(dev);
    gfxbbox_t bbox = gfxline_getbbox(points);
 
    i->hasbuttons = 1;
 
    /* shape */
    myshapeid = getNewID(dev);
    i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
    swf_ShapeNew(&i->shape);
    rgb.r = rgb.b = rgb.a = rgb.g = 0; 
    fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
    swf_SetU16(i->tag, myshapeid);
    r.xmin = (int)(bbox.xmin*20);
    r.ymin = (int)(bbox.ymin*20);
    r.xmax = (int)(bbox.xmax*20);
    r.ymax = (int)(bbox.ymax*20);
    r = swf_ClipRect(i->pagebbox, r);
    swf_SetRect(i->tag,&r);
    swf_SetShapeStyles(i->tag,i->shape);
    swf_ShapeCountBits(i->shape,NULL,NULL);
    swf_SetShapeBits(i->tag,i->shape);
    swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
    i->swflastx = i->swflasty = 0;
    drawgfxline(dev, points, 1);
    swf_ShapeSetEnd(i->tag);
    swf_ShapeFree(i->shape);
 
    /* shape2 */
    myshapeid2 = getNewID(dev);
    i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
    swf_ShapeNew(&i->shape);
 
    rgb = i->config_linkcolor;
 
    fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
    swf_SetU16(i->tag, myshapeid2);
    r.xmin = (int)(bbox.xmin*20);
    r.ymin = (int)(bbox.ymin*20);
    r.xmax = (int)(bbox.xmax*20);
    r.ymax = (int)(bbox.ymax*20);
    r = swf_ClipRect(i->pagebbox, r);
    swf_SetRect(i->tag,&r);
    swf_SetShapeStyles(i->tag,i->shape);
    swf_ShapeCountBits(i->shape,NULL,NULL);
    swf_SetShapeBits(i->tag,i->shape);
    swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
    i->swflastx = i->swflasty = 0;
    drawgfxline(dev, points, 1);
    swf_ShapeSetEnd(i->tag);
    swf_ShapeFree(i->shape);
 
    // ST_DEFINEBUTTON2 support multiple conditions
    // ST_DEFINEBUTTON Don't!!!
    i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2); 
    swf_SetU16(i->tag,buttonid); //id
    swf_ButtonSetFlags(i->tag, 0); //menu=no
    swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
    swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
    swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
    swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
    swf_SetU8(i->tag,0);
 
    // on(rollOver, dragOver)
    swf_ButtonSetCondition(i->tag,0x00a1);
    swf_ActionSet(i->tag,actions3);
 
    //on(rollOut, dragOut, releaseOutside)
    swf_ButtonSetCondition(i->tag,0x0152);
    swf_ActionSet(i->tag,actions2);
 
    // on(release)
    swf_ButtonSetCondition(i->tag,0x0008);
    swf_ActionSet(i->tag,actions1);
 
    swf_SetU8(i->tag,0);
    swf_ButtonPostProcess(i->tag,3);
 
    char buf[80];
    char*buf2 = 0;
    const char* name = 0;
    if(i->config_linknameurl) {
        buf2 = malloc(strlen(type)+strlen(url)+2);
        sprintf(buf2, "%s:%s", type, url);
        name = buf2;
    } else {
        name = buf;
        sprintf(buf, "button%d", buttonid);
    }
 
    i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
 
    if(posx!=0 || posy!=0) {
        SPOINT p;
        p.x = (int)(posx*20);
        p.y = (int)(posy*20);
        p = swf_TurnPoint(p, &i->page_matrix);
	MATRIX m;
        m = i->page_matrix;
        m.tx = p.x;
        m.ty = p.y;
	swf_ObjectPlace(i->tag, buttonid, 
                  getNewDepth(dev),&m,0,(U8*)name);
    } else {
	swf_ObjectPlace(i->tag, buttonid, 
                  getNewDepth(dev),&i->page_matrix,0,(U8*)name);
    }
 
    if(buf2)
	free(buf2);
}

Deu trabalho …

Comments on this entry are closed.