/*
* OptimizedActionBlockReader.java
* @Author Oleg Gorobets
* Created: 24.07.2007
* CVS-ID: $Id:
*************************************************************************/
package com.jswiff.swfrecords.actions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.swfparser.util.PrintfFormat;
import org.swfparser.util.UnsignedByte;
import org.apache.log4j.Logger;
import com.jswiff.io.InputBitStream;
import com.jswiff.io.OutputBitStream;
public class OptimizedActionBlockReader extends ActionBlock {
/**
*
*/
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(OptimizedActionBlockReader.class);
private static PrintfFormat actionFormat = new PrintfFormat("%02X");
private byte[] data;
private boolean skipGarbage = false;
public void setSkipGarbage(boolean skipGarbage) {
this.skipGarbage = skipGarbage;
}
private void saveBinaryData(InputBitStream stream) {
int offset = (int)stream.getOffset();
try {
data = stream.readBytes(stream.available());
stream.reset();
stream.readBytes(offset);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public byte[] getData() {
return data;
}
@Override
public void read(InputBitStream stream) throws IOException {
// saveBinaryData(stream);
// int startOffset = (int) stream.getOffset();
readBlock(stream);
}
public void readBlock(InputBitStream stream) throws IOException {
int xStartOffset = (int) stream.getOffset();
saveBinaryData(stream);
int startOffset = (int) stream.getOffset();
logger.debug("===============================================");
logger.debug("Starting optimized action block with offset "+startOffset);
logger.debug("===============================================");
boolean hasEndAction = false;
logger.debug("########");
logger.debug("Pass 1");
logger.debug("########");
// this will handle all JUMP or IF pointers in absolute
// from start of action block
Set<Integer> absoluteLabelOffsets = new HashSet<Integer>();
while (stream.available() > 0) {
Action record = ActionReader.readRecord(stream);
String actionInfo = "(1)ACTION:"+UnsignedByte.hex(record.getOffset()-startOffset)+":"+actionFormat.sprintf(record.getCode())+"("+ActionConstants.getActionName(record.getCode());
if (record.code != ActionConstants.END) {
actions.add(record);
} else {
hasEndAction = true;
break;
}
if (record instanceof Branch) {
int branchOffset = ((Branch)record).getBranchOffset();
int absoluteLabelOffset = (record.getOffset()-startOffset) + record.getSize()+ branchOffset ;
actionInfo+=",branchOffset="+branchOffset+"("+UnsignedByte.hex(absoluteLabelOffset)+")";
logger.debug("ALO:"+UnsignedByte.hex(absoluteLabelOffset));
absoluteLabelOffsets.add(absoluteLabelOffset);
}
logger.debug(actionInfo);
if (record instanceof Jump) {
short branchOffset = ((Jump) record).getBranchOffset();
if (branchOffset > 0) {
int absoluteLabelOffset = (record.getOffset()-startOffset) + record.getSize()+ branchOffset;
boolean writeGarbageAction = true;
int garbageActionSize = branchOffset;
// check whether there is a label in interval
// (record.getOffset()-startOffset) + record.getSize() <----> absoluteLabelOffset
Integer attemptedBranch = null;
logger.debug("Checking range: "+UnsignedByte.hex((record.getOffset()-startOffset) + record.getSize())+" --- "+UnsignedByte.hex(absoluteLabelOffset));
for (int j=(record.getOffset()-startOffset) + record.getSize(); j<absoluteLabelOffset; j++) {
if (absoluteLabelOffsets.contains(new Integer(j))) {
attemptedBranch=j;
break;
}
}
if (skipGarbage) {
logger.debug("Skipping next " + branchOffset + "("+UnsignedByte.hex(absoluteLabelOffset)+") bytes as Garbage action");
if (attemptedBranch!=null) {
int relativeSkip = (attemptedBranch - (record.getOffset()-startOffset) - record.getSize());
logger.debug("Attempt to branch is "+attemptedBranch+". Relative skip = "+relativeSkip);
logger.debug("Finally skipping "+relativeSkip+" byres");
garbageActionSize = relativeSkip;
if (relativeSkip==0) {
logger.debug("Just proceeding to next action...");
writeGarbageAction=false;
}
}
if (writeGarbageAction) {
logger.debug("Adding garbage action...");
int garbageActionOffset = (int)stream.getOffset();
byte[] b = stream.readBytes(garbageActionSize);
Action garbageAction = new GarbageAction(b);
garbageAction.setOffset(garbageActionOffset);
actions.add(garbageAction);
}
}
}
}
}
if (actions.size() == 0) {
return;
}
logger.debug("########");
logger.debug("Pass 2 - "+UnsignedByte.hex(stream.getOffset()));
logger.debug("########");
// end offset (relative to start offset, end action ignored)
int relativeEndOffset = (int) stream.getOffset() - startOffset - (hasEndAction ? 1 : 0);
logger.debug("relativeEndOffset = "+UnsignedByte.hex(relativeEndOffset));
// correct offsets, setting to relative to first action (not to start of
// stream)
// also, populate the label map with integers containing the
// corresponding offsets
int labelCounter = 0;
Map actionMap = new HashMap(); // contains offset->action mapping
for (int i = 0; i < actions.size(); i++) {
Action action = (Action) actions.get(i);
int newOffset = action.getOffset() - startOffset;
action.setOffset(newOffset);
actionMap.put(new Integer(newOffset), action);
String actionInfo = "(2)ACTION:"+UnsignedByte.hex(newOffset)+":"+actionFormat.sprintf(action.getCode())+"("+ActionConstants.getActionName(action.getCode());
// collect labels from Jump and If actions
if ((action.getCode() == ActionConstants.IF) || (action.getCode() == ActionConstants.JUMP)) {
Branch branchAction = (Branch) action;
// temporarily put the offset into the label map
// later on, the offset will be replaced with the corresponding
// action instance
int branchOffset = getBranchOffset(branchAction);
actionInfo+=",branchOffset="+branchAction.getBranchOffset()+"("+UnsignedByte.hex(branchOffset)+")";
String branchLabel;
if (branchOffset < relativeEndOffset) {
Integer branchOffsetObj = new Integer(branchOffset);
// check if branch target isn't already assigned a label
String oldLabel = (String) inverseLabelMap.get(branchOffsetObj);
if (oldLabel == null) {
branchLabel = "L_" + instCounter + "_" + labelCounter++;
actionInfo+=" branchLabel:"+branchLabel+",totalBranchOffset="+branchOffsetObj;
labelMap.put(branchLabel, branchOffsetObj);
inverseLabelMap.put(branchOffsetObj, branchLabel);
} else {
branchLabel = oldLabel;
}
} else if (branchOffset == relativeEndOffset) {
branchLabel = LABEL_END;
} else {
branchLabel = LABEL_OUT;
}
branchAction.setBranchLabel(branchLabel);
}
logger.debug(actionInfo);
}
logger.debug("########");
logger.debug("Replacing offsets from label map with corresponding actions");
logger.debug("########");
// now replace offsets from label map with corresponding actions
Set keys = labelMap.keySet();
for (Iterator i = keys.iterator(); i.hasNext();) {
String label = (String) i.next();
Object branchOffset = labelMap.get(label);
Action action = (Action) actionMap.get(branchOffset);
if (action != null) {
logger.debug("LABEL:"+label+",branchOffset="+branchOffset+",action="+UnsignedByte.hex(action.getOffset())+" "+action);
// action == null when label == LABEL_OUT
action.setLabel(label);
labelMap.put(label, action);
}
}
instCounter++;
}
@Override
public void write(OutputBitStream stream, boolean writeEndAction) throws IOException {
// Remove garabage actions
logger.debug("Removing garabage actions before writing... "+actions.size());
List<Action> cleanActions = new ArrayList<Action>();
for (Object actionObj : actions) {
Action action = (Action) actionObj;
if (!(action instanceof GarbageAction)) {
cleanActions.add(action);
}
}
actions = cleanActions;
// TODO: remove it
// logger.debug("~~~ Reading statements ~~~");
// StatementBlock statementBlock = CodeUtil.getStatementBlockReader();
//
// try {
// statementBlock.setExecutionContext(CodeUtil.getExecutionContext());
// statementBlock.read(actions);
// } catch (StatementBlockException e) {
// throw new RuntimeException("ZZZ");
// }
super.write(stream, writeEndAction);
}
}