mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-28 12:36:09 +00:00
java code format
This commit is contained in:
@@ -321,7 +321,7 @@ public class SWFInputStream implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setPercentMax(long percentMax) {
|
||||
this.percentMax = percentMax;
|
||||
}
|
||||
@@ -403,7 +403,7 @@ public class SWFInputStream implements AutoCloseable {
|
||||
dumpInfo = dumpInfo.parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void endDumpLevelUntil(DumpInfo di) {
|
||||
if (di != null) {
|
||||
while (dumpInfo != null && dumpInfo != di) {
|
||||
@@ -706,7 +706,7 @@ public class SWFInputStream implements AutoCloseable {
|
||||
if (count <= 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
|
||||
informListeners();
|
||||
bitPos = 0;
|
||||
byte[] ret = new byte[(int) count];
|
||||
@@ -980,7 +980,7 @@ public class SWFInputStream implements AutoCloseable {
|
||||
DumpInfo di = dumpInfo;
|
||||
try {
|
||||
Tag t = resolveTag(tag, level, parallel, skipUnusualTags, gfx);
|
||||
if (dumpInfo!= null && t != null) {
|
||||
if (dumpInfo != null && t != null) {
|
||||
dumpInfo.name = t.getName();
|
||||
}
|
||||
return t;
|
||||
@@ -1436,7 +1436,7 @@ public class SWFInputStream implements AutoCloseable {
|
||||
if (tagLength > available) {
|
||||
tagLength = available;
|
||||
}
|
||||
|
||||
|
||||
ByteArrayRange dataRange = new ByteArrayRange(swf.uncompressedData, (int) pos, (int) (tagLength + headerLength));
|
||||
TagStub tagStub = new TagStub(swf, tagID, "Unresolved", dataRange, tagDataStream);
|
||||
tagStub.forceWriteAsLong = readLong;
|
||||
|
||||
@@ -257,8 +257,8 @@ public class ConstantPool {
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,11 +44,11 @@ public class PopIns extends InstructionDefinition {
|
||||
@Override
|
||||
public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, Stack<GraphTargetItem> stack, Stack<GraphTargetItem> scopeStack, ConstantPool constants, AVM2Instruction ins, List<MethodInfo> method_info, List<GraphTargetItem> output, MethodBody body, ABC abc, HashMap<Integer, String> localRegNames, List<String> fullyQualifiedNames, String path, HashMap<Integer, Integer> localRegsAssignmentIps, int ip, HashMap<Integer, List<Integer>> refs, AVM2Code code) {
|
||||
if (stack.size() > 0) {
|
||||
GraphTargetItem top = stack.pop();
|
||||
GraphTargetItem top = stack.pop();
|
||||
//Note: Commands like "5;" - numbers are unsupported as it collide with try..finally block decompilation. TODO: allow this somehow
|
||||
if(!(top instanceof IntegerValueAVM2Item)){
|
||||
if (!(top instanceof IntegerValueAVM2Item)) {
|
||||
output.add(top);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public class NewFunctionAVM2Item extends AVM2Item {
|
||||
methodInfo.get(methodIndex).getParamStr(writer, constants, body, abc, fullyQualifiedNames);
|
||||
writer.appendNoHilight("):");
|
||||
methodInfo.get(methodIndex).getReturnTypeStr(writer, constants, fullyQualifiedNames);
|
||||
writer.endMethod();
|
||||
writer.endMethod();
|
||||
writer.startBlock();
|
||||
if (body != null) {
|
||||
if (writer instanceof NulWriter) {
|
||||
|
||||
@@ -1,86 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.abc.avm2.model;
|
||||
|
||||
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.graph.CompilationException;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItem;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.decompiler.graph.SourceGenerator;
|
||||
import com.jpexs.decompiler.graph.TypeItem;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NewObjectAVM2Item extends AVM2Item {
|
||||
|
||||
public List<NameValuePair> pairs;
|
||||
|
||||
public NewObjectAVM2Item(AVM2Instruction instruction, List<NameValuePair> pairs) {
|
||||
super(instruction, PRECEDENCE_PRIMARY);
|
||||
this.pairs = pairs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
|
||||
boolean singleLine = pairs.size() < 2;
|
||||
//no new line before as it may break return clause (#593)
|
||||
writer.append("{");
|
||||
if (!singleLine) {
|
||||
writer.newLine();
|
||||
writer.indent();
|
||||
}
|
||||
for (int n = 0; n < pairs.size(); n++) {
|
||||
if (n > 0) {
|
||||
writer.append(",").newLine();
|
||||
}
|
||||
pairs.get(n).toString(writer, localData);
|
||||
}
|
||||
if (!singleLine) {
|
||||
writer.newLine();
|
||||
writer.unindent();
|
||||
}
|
||||
writer.append("}");
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphTargetItem returnType() {
|
||||
return new TypeItem("Object");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReturnValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
|
||||
List<GraphTargetItem> args = new ArrayList<>();
|
||||
for (NameValuePair p : pairs) {
|
||||
args.add(p.name);
|
||||
args.add(p.value);
|
||||
}
|
||||
return toSourceMerge(localData, generator, args,
|
||||
new AVM2Instruction(0, new NewObjectIns(), new int[]{pairs.size()}, new byte[0])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.abc.avm2.model;
|
||||
|
||||
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.graph.CompilationException;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItem;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.decompiler.graph.SourceGenerator;
|
||||
import com.jpexs.decompiler.graph.TypeItem;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NewObjectAVM2Item extends AVM2Item {
|
||||
|
||||
public List<NameValuePair> pairs;
|
||||
|
||||
public NewObjectAVM2Item(AVM2Instruction instruction, List<NameValuePair> pairs) {
|
||||
super(instruction, PRECEDENCE_PRIMARY);
|
||||
this.pairs = pairs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
|
||||
boolean singleLine = pairs.size() < 2;
|
||||
//no new line before as it may break return clause (#593)
|
||||
writer.append("{");
|
||||
if (!singleLine) {
|
||||
writer.newLine();
|
||||
writer.indent();
|
||||
}
|
||||
for (int n = 0; n < pairs.size(); n++) {
|
||||
if (n > 0) {
|
||||
writer.append(",").newLine();
|
||||
}
|
||||
pairs.get(n).toString(writer, localData);
|
||||
}
|
||||
if (!singleLine) {
|
||||
writer.newLine();
|
||||
writer.unindent();
|
||||
}
|
||||
writer.append("}");
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphTargetItem returnType() {
|
||||
return new TypeItem("Object");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReturnValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
|
||||
List<GraphTargetItem> args = new ArrayList<>();
|
||||
for (NameValuePair p : pairs) {
|
||||
args.add(p.name);
|
||||
args.add(p.value);
|
||||
}
|
||||
return toSourceMerge(localData, generator, args,
|
||||
new AVM2Instruction(0, new NewObjectIns(), new int[]{pairs.size()}, new byte[0])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class WithAVM2Item extends AVM2Item {
|
||||
scope.toString(writer, localData);
|
||||
writer.append(")").startBlock();
|
||||
//NOTE: endBlock is added with WithEndAVM2Item
|
||||
return writer;
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -66,12 +66,12 @@ public class TryAVM2Item extends AVM2Item implements Block {
|
||||
|
||||
@Override
|
||||
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
|
||||
writer.append("try").startBlock();
|
||||
writer.append("try").startBlock();
|
||||
for (GraphTargetItem ti : tryCommands) {
|
||||
if (!ti.isEmpty()) {
|
||||
ti.toStringSemicoloned(writer, localData).newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.endBlock();
|
||||
for (int e = 0; e < catchExceptions.size(); e++) {
|
||||
writer.newLine();
|
||||
|
||||
@@ -2032,7 +2032,7 @@ public class AVM2SourceGenerator implements SourceGenerator {
|
||||
continue;
|
||||
}
|
||||
TraitMethodGetterSetter tmgs = new TraitMethodGetterSetter();
|
||||
tmgs.kindType = (item instanceof GetterAVM2Item)?Trait.TRAIT_GETTER:((item instanceof SetterAVM2Item) ? Trait.TRAIT_SETTER : Trait.TRAIT_METHOD);
|
||||
tmgs.kindType = (item instanceof GetterAVM2Item) ? Trait.TRAIT_GETTER : ((item instanceof SetterAVM2Item) ? Trait.TRAIT_SETTER : Trait.TRAIT_METHOD);
|
||||
//tmgs.name_index = traitName(((MethodAVM2Item) item).namespace, ((MethodAVM2Item) item).functionName);
|
||||
tmgs.disp_id = mai.isStatic() ? disp_id++ : 0; //For a reason, there is disp_id only for static methods (or not?)
|
||||
if (mai.isFinal() || mai.isStatic()) {
|
||||
|
||||
@@ -99,7 +99,7 @@ public class TraitMethodGetterSetter extends Trait {
|
||||
writer.startBlock();
|
||||
if (bodyIndex != -1) {
|
||||
abc.bodies.get(bodyIndex).toString(path, exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack<GraphTargetItem>(), false, writer, fullyQualifiedNames, null);
|
||||
}
|
||||
}
|
||||
writer.endBlock();
|
||||
}
|
||||
writer.newLine();
|
||||
|
||||
@@ -128,7 +128,6 @@ public class Action implements GraphSourceItem {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Names of ActionScript properties
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,14 +65,14 @@ public class DumpInfo {
|
||||
public int getChildCount() {
|
||||
return childInfos == null ? 0 : childInfos.size();
|
||||
}
|
||||
|
||||
|
||||
public List<DumpInfo> getChildInfos() {
|
||||
if (childInfos == null) {
|
||||
childInfos = new ArrayList<>();
|
||||
}
|
||||
return childInfos;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String value = previewValue == null ? "" : previewValue.toString();
|
||||
|
||||
@@ -25,16 +25,16 @@ import com.jpexs.decompiler.flash.SWF;
|
||||
public class DumpInfoSwfNode extends DumpInfo {
|
||||
|
||||
private final SWF swf;
|
||||
|
||||
|
||||
public DumpInfoSwfNode(SWF swf, String name, String type, Object value, long startByte, long lengthBytes) {
|
||||
super(name, type, value, startByte, lengthBytes);
|
||||
this.swf = swf;
|
||||
}
|
||||
|
||||
|
||||
public SWF getSwf() {
|
||||
return swf;
|
||||
}
|
||||
|
||||
|
||||
public static DumpInfoSwfNode getSwfNode(DumpInfo dumpInfo) {
|
||||
while (!(dumpInfo instanceof DumpInfoSwfNode)) {
|
||||
dumpInfo = dumpInfo.parent;
|
||||
|
||||
@@ -60,8 +60,7 @@ public class HeaderInfoPanel extends JPanel {
|
||||
add(displayRectTwipsLabel);
|
||||
add(new JLabel(""));
|
||||
add(displayRectPixelsLabel);
|
||||
|
||||
|
||||
|
||||
SpringUtilities.makeCompactGrid(this,
|
||||
9, 2, //rows, cols
|
||||
6, 6, //initX, initY
|
||||
@@ -87,22 +86,23 @@ public class HeaderInfoPanel extends JPanel {
|
||||
frameRateLabel.setText("" + swf.frameRate);
|
||||
frameCountLabel.setText("" + swf.frameCount);
|
||||
displayRectTwipsLabel.setText(AppStrings.translate("header.displayrect.value.twips")
|
||||
.replace("%xmin%", ""+swf.displayRect.Xmin)
|
||||
.replace("%ymin%", ""+swf.displayRect.Ymin)
|
||||
.replace("%xmax%", ""+swf.displayRect.Xmax)
|
||||
.replace("%ymax%", ""+swf.displayRect.Ymax));
|
||||
.replace("%xmin%", "" + swf.displayRect.Xmin)
|
||||
.replace("%ymin%", "" + swf.displayRect.Ymin)
|
||||
.replace("%xmax%", "" + swf.displayRect.Xmax)
|
||||
.replace("%ymax%", "" + swf.displayRect.Ymax));
|
||||
displayRectPixelsLabel.setText(AppStrings.translate("header.displayrect.value.pixels")
|
||||
.replace("%xmin%", ""+fmtDouble(swf.displayRect.Xmin/SWF.unitDivisor))
|
||||
.replace("%ymin%", ""+fmtDouble(swf.displayRect.Ymin/SWF.unitDivisor))
|
||||
.replace("%xmax%", ""+fmtDouble(swf.displayRect.Xmax/SWF.unitDivisor))
|
||||
.replace("%ymax%", ""+fmtDouble(swf.displayRect.Ymax/SWF.unitDivisor)));
|
||||
.replace("%xmin%", "" + fmtDouble(swf.displayRect.Xmin / SWF.unitDivisor))
|
||||
.replace("%ymin%", "" + fmtDouble(swf.displayRect.Ymin / SWF.unitDivisor))
|
||||
.replace("%xmax%", "" + fmtDouble(swf.displayRect.Xmax / SWF.unitDivisor))
|
||||
.replace("%ymax%", "" + fmtDouble(swf.displayRect.Ymax / SWF.unitDivisor)));
|
||||
}
|
||||
private String fmtDouble(double d){
|
||||
String r = ""+d;
|
||||
if(r.endsWith(".0")){
|
||||
r = r.substring(0,r.length()-2);
|
||||
|
||||
private String fmtDouble(double d) {
|
||||
String r = "" + d;
|
||||
if (r.endsWith(".0")) {
|
||||
r = r.substring(0, r.length() - 2);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import javax.swing.table.TableColumn;
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class HexView extends JTable {
|
||||
|
||||
|
||||
private final int bytesInRow = 16;
|
||||
private long[] highlightStarts;
|
||||
private long[] highlightEnds;
|
||||
@@ -51,7 +51,7 @@ public class HexView extends JTable {
|
||||
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
|
||||
int level = -1;
|
||||
if (col > 0 && highlightStarts != null) {
|
||||
if(col!=bytesInRow+1){
|
||||
if (col != bytesInRow + 1) {
|
||||
int idx = row * bytesInRow + ((col > bytesInRow + 1) ? (col - bytesInRow - 2) : (col - 1));
|
||||
for (int i = 0; i < highlightStarts.length; i++) {
|
||||
if (highlightStarts[i] <= idx && highlightEnds[i] >= idx) {
|
||||
@@ -73,14 +73,14 @@ public class HexView extends JTable {
|
||||
|
||||
return l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HexView() {
|
||||
highlightColors = new Color[highlightColorsStr.length];
|
||||
for (int i = 0; i < highlightColors.length; i++) {
|
||||
highlightColors[i] = Color.decode("#" + highlightColorsStr[i]);
|
||||
}
|
||||
|
||||
|
||||
setModel(new AbstractTableModel() {
|
||||
|
||||
@Override
|
||||
@@ -137,7 +137,7 @@ public class HexView extends JTable {
|
||||
setFont(new Font("Monospaced", Font.PLAIN, 12));
|
||||
setTableHeader(new JTableHeader());
|
||||
setMaximumSize(new Dimension(200, 200));
|
||||
|
||||
|
||||
setShowHorizontalLines(false);
|
||||
setShowVerticalLines(false);
|
||||
setRowSelectionAllowed(false);
|
||||
@@ -152,10 +152,10 @@ public class HexView extends JTable {
|
||||
column.setMaxWidth(25);
|
||||
column.setCellRenderer(cellRenderer);
|
||||
}
|
||||
|
||||
|
||||
column = columnModel.getColumn(bytesInRow + 1);
|
||||
column.setMaxWidth(10);
|
||||
|
||||
|
||||
for (int i = 0; i < bytesInRow; i++) {
|
||||
column = columnModel.getColumn(i + bytesInRow + 1 + 1);
|
||||
column.setMaxWidth(10);
|
||||
@@ -164,28 +164,28 @@ public class HexView extends JTable {
|
||||
}
|
||||
|
||||
public void setData(byte[] data, long[] highlightStarts, long[] highlightEnds) {
|
||||
|
||||
|
||||
if ((highlightStarts == null) ^ (highlightEnds == null)) {
|
||||
throw new Error("highlightStarts and highlightEnds should be both null or not null.");
|
||||
}
|
||||
|
||||
|
||||
if (highlightStarts != null && highlightStarts.length != highlightEnds.length) {
|
||||
throw new Error("highlightStarts and highlightEnds should have the same number of elements.");
|
||||
}
|
||||
|
||||
|
||||
this.data = data;
|
||||
this.highlightStarts = highlightStarts;
|
||||
this.highlightEnds = highlightEnds;
|
||||
}
|
||||
|
||||
|
||||
public void scrollToByte(long byteNum) {
|
||||
int row = (int) (byteNum / bytesInRow);
|
||||
|
||||
//final int pageSize = (int) (getParent().getSize().getHeight() / getRowHeight());
|
||||
getSelectionModel().setSelectionInterval(row, row);
|
||||
scrollRectToVisible(new Rectangle(getCellRect(row, 0, true)));
|
||||
scrollRectToVisible(new Rectangle(getCellRect(row, 0, true)));
|
||||
}
|
||||
|
||||
|
||||
public void scrollToByte(long[] byteNumStarts, long[] byteNumEnds) {
|
||||
for (int i = 0; i < byteNumStarts.length; i++) {
|
||||
scrollToByte(byteNumStarts[i]);
|
||||
|
||||
@@ -329,29 +329,27 @@ public class MainFrameRibbonMenu implements MainFrameMenu, ActionListener {
|
||||
assignListener(importTextCommandButton, ACTION_IMPORT_TEXT);
|
||||
|
||||
importBand.addCommandButton(importTextCommandButton, RibbonElementPriority.TOP);
|
||||
|
||||
|
||||
|
||||
JRibbonBand viewBand = new JRibbonBand(translate("menu.view"), null);
|
||||
viewBand.setResizePolicies(getResizePolicies(viewBand));
|
||||
|
||||
CommandToggleButtonGroup grp=new CommandToggleButtonGroup();
|
||||
|
||||
|
||||
CommandToggleButtonGroup grp = new CommandToggleButtonGroup();
|
||||
|
||||
viewModeResourcesToggleButton = new JCommandToggleButton(fixCommandTitle(translate("menu.file.view.resources")), View.getResizableIcon("viewresources16"));
|
||||
assignListener(viewModeResourcesToggleButton, ACTION_VIEWMODE_RESOURCES);
|
||||
|
||||
assignListener(viewModeResourcesToggleButton, ACTION_VIEWMODE_RESOURCES);
|
||||
|
||||
viewModeHexToggleButton = new JCommandToggleButton(fixCommandTitle(translate("menu.file.view.hex")), View.getResizableIcon("viewhex16"));
|
||||
assignListener(viewModeHexToggleButton, ACTION_VIEWMODE_HEX);
|
||||
|
||||
|
||||
grp.add(viewModeResourcesToggleButton);
|
||||
grp.add(viewModeHexToggleButton);
|
||||
|
||||
if(Configuration.dumpView.get()){
|
||||
|
||||
if (Configuration.dumpView.get()) {
|
||||
grp.setSelected(viewModeHexToggleButton, true);
|
||||
}else{
|
||||
} else {
|
||||
grp.setSelected(viewModeResourcesToggleButton, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
viewBand.addCommandButton(viewModeResourcesToggleButton, RibbonElementPriority.MEDIUM);
|
||||
viewBand.addCommandButton(viewModeHexToggleButton, RibbonElementPriority.MEDIUM);
|
||||
|
||||
@@ -460,10 +458,9 @@ public class MainFrameRibbonMenu implements MainFrameMenu, ActionListener {
|
||||
miAutoRenameIdentifiers.addActionListener(this);
|
||||
|
||||
/*miDumpView = new JCheckBox(translate("menu.settings.dumpView"));
|
||||
miDumpView.setSelected(Configuration.dumpView.get());
|
||||
miDumpView.setActionCommand(ACTION_DUMP_VIEW_SWITCH);
|
||||
miDumpView.addActionListener(this);*/
|
||||
|
||||
miDumpView.setSelected(Configuration.dumpView.get());
|
||||
miDumpView.setActionCommand(ACTION_DUMP_VIEW_SWITCH);
|
||||
miDumpView.addActionListener(this);*/
|
||||
settingsBand.addRibbonComponent(new JRibbonComponent(miAutoDeobfuscation));
|
||||
settingsBand.addRibbonComponent(new JRibbonComponent(miInternalViewer));
|
||||
settingsBand.addRibbonComponent(new JRibbonComponent(miParallelSpeedUp));
|
||||
@@ -652,7 +649,7 @@ public class MainFrameRibbonMenu implements MainFrameMenu, ActionListener {
|
||||
Configuration.internalFlashViewer.set(miInternalViewer.isSelected());
|
||||
mainFrame.panel.reload(true);
|
||||
break;
|
||||
|
||||
|
||||
case ACTION_VIEWMODE_RESOURCES:
|
||||
Configuration.dumpView.set(false);
|
||||
mainFrame.panel.showDumpView(false);
|
||||
|
||||
@@ -485,10 +485,10 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
|
||||
displayPanel.add(previewPanel, CARDPREVIEWPANEL);
|
||||
displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL);
|
||||
displayPanel.add(createDumpPreviewCard(), CARDDUMPVIEW);
|
||||
|
||||
headerPanel = new HeaderInfoPanel();
|
||||
displayPanel.add(headerPanel,CARDHEADER);
|
||||
|
||||
|
||||
headerPanel = new HeaderInfoPanel();
|
||||
displayPanel.add(headerPanel, CARDHEADER);
|
||||
|
||||
displayPanel.add(new JPanel(), CARDEMPTYPANEL);
|
||||
showCard(CARDEMPTYPANEL);
|
||||
|
||||
@@ -1133,7 +1133,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
|
||||
|
||||
return DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1859,7 +1859,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
|
||||
setDumpTreeSelectedNode(dumpInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void refreshDecompiled() {
|
||||
clearCache();
|
||||
if (abcPanel != null) {
|
||||
@@ -2153,18 +2153,18 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
|
||||
showCard(CARDEMPTYPANEL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
dumpViewPanel.setData(DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().uncompressedData, dumpInfo);
|
||||
dumpViewPanel.revalidate();
|
||||
showCard(CARDDUMPVIEW);
|
||||
}
|
||||
|
||||
|
||||
public void reload(boolean forceReload) {
|
||||
if (Configuration.dumpView.get()) {
|
||||
dumpViewReload(forceReload);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent();
|
||||
if (treeNode == null) {
|
||||
return;
|
||||
@@ -2249,9 +2249,9 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
|
||||
|
||||
previewPanel.setImageReplaceButtonVisible(false);
|
||||
|
||||
if(treeNode instanceof HeaderNode) {
|
||||
if (treeNode instanceof HeaderNode) {
|
||||
showCard(CARDHEADER);
|
||||
headerPanel.load(((HeaderNode)treeNode).getItem().getSwf());
|
||||
headerPanel.load(((HeaderNode) treeNode).getItem().getSwf());
|
||||
} else if (treeNode instanceof StringNode) {
|
||||
showCard(CARDFOLDERPREVIEWPANEL);
|
||||
showFolderPreview(treeNode);
|
||||
@@ -2543,7 +2543,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
|
||||
ds.matrix = new MATRIX();
|
||||
f.layers.put(1, ds);
|
||||
tim.frames.add(f);
|
||||
}
|
||||
}
|
||||
tim.displayRect = getRect(new HashSet<BoundedTag>());
|
||||
return tim;
|
||||
}
|
||||
|
||||
@@ -187,11 +187,11 @@ public class TagTree extends JTree implements ActionListener {
|
||||
}
|
||||
|
||||
public static TreeNodeType getTreeNodeType(TreeItem t) {
|
||||
|
||||
if(t instanceof HeaderItem){
|
||||
|
||||
if (t instanceof HeaderItem) {
|
||||
return TreeNodeType.HEADER;
|
||||
}
|
||||
|
||||
|
||||
if ((t instanceof DefineFontTag)
|
||||
|| (t instanceof DefineFont2Tag)
|
||||
|| (t instanceof DefineFont3Tag)
|
||||
@@ -498,7 +498,7 @@ public class TagTree extends JTree implements ActionListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean hasExportableNodes() {
|
||||
return !getSelection(mainPanel.getCurrentSwf()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -244,9 +244,8 @@ public class TagTreeModel implements TreeModel {
|
||||
}
|
||||
swfNode.scriptsNode = actionScriptNode;
|
||||
|
||||
|
||||
ret.add(new HeaderNode(new HeaderItem(swf)));
|
||||
|
||||
|
||||
if (!shapesNode.subNodes.isEmpty()) {
|
||||
ret.add(shapesNode);
|
||||
}
|
||||
@@ -341,7 +340,7 @@ public class TagTreeModel implements TreeModel {
|
||||
return newPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (obj instanceof StringItem && n.getItem() instanceof StringItem) {
|
||||
// StringItems are always recreated, so compare them by name
|
||||
StringItem nds = (StringItem) n.getItem();
|
||||
|
||||
@@ -156,7 +156,7 @@ public class DumpTree extends JTree implements ActionListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setModel(TreeModel tm) {
|
||||
super.setModel(tm);
|
||||
|
||||
@@ -59,7 +59,7 @@ public class DumpViewPanel extends JPanel {
|
||||
}
|
||||
return end - 1;
|
||||
}
|
||||
|
||||
|
||||
public void setData(byte[] data, DumpInfo dumpInfo) {
|
||||
List<DumpInfo> dumpInfos = new ArrayList<>();
|
||||
DumpInfo di = dumpInfo;
|
||||
@@ -81,7 +81,7 @@ public class DumpViewPanel extends JPanel {
|
||||
int selectionEnd = getEndIndex(dumpInfo);
|
||||
|
||||
dumpViewHexTable.scrollToByte(highlightStarts, highlightEnds);
|
||||
|
||||
|
||||
setLabelText("startByte: " + dumpInfo.startByte
|
||||
+ " startBit: " + dumpInfo.startBit
|
||||
+ " lengthBytes: " + dumpInfo.lengthBytes
|
||||
@@ -89,7 +89,7 @@ public class DumpViewPanel extends JPanel {
|
||||
+ " selectionStart: " + selectionStart
|
||||
+ " selectionEnd: " + selectionEnd);
|
||||
}
|
||||
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ public class DefineButtonTag extends ButtonTag implements ASMSource {
|
||||
CharacterTag ch = swf.characters.get(r.characterId);
|
||||
if (ch instanceof BoundedTag) {
|
||||
BoundedTag bt = (BoundedTag) ch;
|
||||
if (!added.contains(bt)){
|
||||
if (!added.contains(bt)) {
|
||||
added.add(bt);
|
||||
RECT r2 = bt.getRect(added);
|
||||
MATRIX mat = r.placeMatrix;
|
||||
|
||||
@@ -125,7 +125,7 @@ public class DefineSpriteTag extends CharacterTag implements Container, Drawable
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
private static final Cache<RECT> rectCache = Cache.getInstance(true);
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,7 +36,7 @@ public class EndTag extends Tag {
|
||||
public byte[] getData() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
|
||||
public static final int ID = 0;
|
||||
|
||||
/**
|
||||
|
||||
@@ -228,7 +228,7 @@ public class PlaceObject2Tag extends CharacterIdTag implements Container, PlaceO
|
||||
placeFlagHasMatrix = sis.readUB(1, "placeFlagHasMatrix") == 1;
|
||||
placeFlagHasCharacter = sis.readUB(1, "placeFlagHasCharacter") == 1;
|
||||
placeFlagMove = sis.readUB(1, "placeFlagMove") == 1;
|
||||
|
||||
|
||||
depth = sis.readUI16("depth");
|
||||
if (placeFlagHasCharacter) {
|
||||
characterId = sis.readUI16("characterId");
|
||||
|
||||
@@ -397,7 +397,7 @@ public class PlaceObject3Tag extends CharacterIdTag implements Container, PlaceO
|
||||
}
|
||||
return clipActions.clipActionRecords.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void getNeededCharacters(Set<Integer> needed) {
|
||||
if (placeFlagHasCharacter) {
|
||||
|
||||
@@ -249,7 +249,7 @@ public class PlaceObject4Tag extends CharacterIdTag implements Container, PlaceO
|
||||
sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0);
|
||||
sos.writeUB(1, placeFlagHasFilterList ? 1 : 0);
|
||||
sos.writeUI16(depth);
|
||||
|
||||
|
||||
if (placeFlagHasClassName) {
|
||||
sos.writeString(className);
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, ContainerItem,
|
||||
* @return Bytes of data
|
||||
*/
|
||||
public abstract byte[] getData();
|
||||
|
||||
|
||||
public final ByteArrayRange getOriginalRange() {
|
||||
return originalRange;
|
||||
}
|
||||
@@ -369,7 +369,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, ContainerItem,
|
||||
if (originalRange == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return originalRange.length - (isLongOriginal() ? 6 : 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag {
|
||||
shape.shapeRecords.add(new EndShapeRecord());
|
||||
return shape;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RECT getRect(Set<BoundedTag> added) {
|
||||
SerializableImage image = getImage();
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.jpexs.decompiler.flash.treeitems;
|
||||
|
||||
import com.jpexs.decompiler.flash.AppStrings;
|
||||
@@ -31,9 +30,7 @@ public class HeaderItem implements TreeItem {
|
||||
public HeaderItem(SWF swf) {
|
||||
this.swf = swf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public SWF getSwf() {
|
||||
return swf;
|
||||
@@ -43,7 +40,5 @@ public class HeaderItem implements TreeItem {
|
||||
public String toString() {
|
||||
return AppStrings.translate("node.header");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.jpexs.decompiler.flash.treenodes;
|
||||
|
||||
import com.jpexs.decompiler.flash.treeitems.HeaderItem;
|
||||
@@ -28,10 +27,10 @@ public class HeaderNode extends TreeNode {
|
||||
public HeaderNode(HeaderItem item) {
|
||||
super(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HeaderItem getItem() {
|
||||
return (HeaderItem) item;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ package com.jpexs.helpers;
|
||||
public class ByteArrayRange {
|
||||
|
||||
public final byte[] array;
|
||||
public final int pos;
|
||||
public final int length;
|
||||
|
||||
public final int pos;
|
||||
public final int length;
|
||||
|
||||
public ByteArrayRange() {
|
||||
this(new byte[0]);
|
||||
}
|
||||
|
||||
|
||||
public ByteArrayRange(byte[] array) {
|
||||
this.array = array;
|
||||
this.pos = 0;
|
||||
|
||||
@@ -87,10 +87,10 @@ public class MemoryInputStream extends SeekableInputStream {
|
||||
pos += toRead;
|
||||
return toRead;
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return maxLength - (int) pos;
|
||||
|
||||
@@ -1,56 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.ABCInputStream;
|
||||
import com.jpexs.decompiler.flash.abc.ABCOutputStream;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ABCStreamTest {
|
||||
@BeforeClass
|
||||
public void init(){
|
||||
Main.initLogging(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testU30() {
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ABCOutputStream aos = new ABCOutputStream(baos);) {
|
||||
long number = 1531;
|
||||
aos.writeU30(number);
|
||||
aos.close();
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ABCInputStream ais = new ABCInputStream(bais);) {
|
||||
assertEquals(number, ais.readU30());
|
||||
assertEquals(0, bais.available());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.ABCInputStream;
|
||||
import com.jpexs.decompiler.flash.abc.ABCOutputStream;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ABCStreamTest {
|
||||
|
||||
@BeforeClass
|
||||
public void init() {
|
||||
Main.initLogging(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testU30() {
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ABCOutputStream aos = new ABCOutputStream(baos);) {
|
||||
long number = 1531;
|
||||
aos.writeU30(number);
|
||||
aos.close();
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ABCInputStream ais = new ABCInputStream(bais);) {
|
||||
assertEquals(number, ais.readU30());
|
||||
assertEquals(0, bais.available());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,122 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash;
|
||||
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import static org.testng.Assert.fail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ExportTest {
|
||||
|
||||
@BeforeClass
|
||||
public void init(){
|
||||
Main.initLogging(false);
|
||||
}
|
||||
|
||||
public static final String TESTDATADIR = "testdata/decompile";
|
||||
|
||||
@BeforeClass
|
||||
public void addLogger() {
|
||||
Configuration.autoDeobfuscate.set(true);
|
||||
Logger logger = Logger.getLogger("");
|
||||
logger.addHandler(new Handler() {
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
if (record.getLevel() == Level.SEVERE) {
|
||||
fail("Error during decompilation", record.getThrown());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@DataProvider(name = "swfFiles")
|
||||
public Object[][] createData() {
|
||||
File dir = new File(TESTDATADIR);
|
||||
if (!dir.exists()) {
|
||||
return new Object[0][0];
|
||||
}
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(".swf");
|
||||
}
|
||||
});
|
||||
Object[][] ret = new Object[files.length][1];
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
ret[i][0] = files[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Test(dataProvider = "swfFiles")
|
||||
public void testDecompileAS(File f) {
|
||||
testDecompile(f, ScriptExportMode.AS);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "swfFiles")
|
||||
public void testDecompilePcode(File f) {
|
||||
testDecompile(f, ScriptExportMode.PCODE);
|
||||
}
|
||||
|
||||
public void testDecompile(File f, ScriptExportMode exportMode) {
|
||||
try {
|
||||
SWF swf = new SWF(new FileInputStream(f), false);
|
||||
Configuration.debugCopy.set(true);
|
||||
String folderName = exportMode == ScriptExportMode.AS ? "output" : "outputp";
|
||||
File fdir = new File(TESTDATADIR + File.separator + folderName + File.separator + f.getName());
|
||||
fdir.mkdirs();
|
||||
|
||||
swf.exportActionScript(new AbortRetryIgnoreHandler() {
|
||||
@Override
|
||||
public int handle(Throwable thrown) {
|
||||
return AbortRetryIgnoreHandler.ABORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbortRetryIgnoreHandler getNewInstance() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}, fdir.getAbsolutePath(), exportMode, false);
|
||||
} catch (Exception ex) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash;
|
||||
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import static org.testng.Assert.fail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ExportTest {
|
||||
|
||||
@BeforeClass
|
||||
public void init() {
|
||||
Main.initLogging(false);
|
||||
}
|
||||
|
||||
public static final String TESTDATADIR = "testdata/decompile";
|
||||
|
||||
@BeforeClass
|
||||
public void addLogger() {
|
||||
Configuration.autoDeobfuscate.set(true);
|
||||
Logger logger = Logger.getLogger("");
|
||||
logger.addHandler(new Handler() {
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
if (record.getLevel() == Level.SEVERE) {
|
||||
fail("Error during decompilation", record.getThrown());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@DataProvider(name = "swfFiles")
|
||||
public Object[][] createData() {
|
||||
File dir = new File(TESTDATADIR);
|
||||
if (!dir.exists()) {
|
||||
return new Object[0][0];
|
||||
}
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(".swf");
|
||||
}
|
||||
});
|
||||
Object[][] ret = new Object[files.length][1];
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
ret[i][0] = files[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Test(dataProvider = "swfFiles")
|
||||
public void testDecompileAS(File f) {
|
||||
testDecompile(f, ScriptExportMode.AS);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "swfFiles")
|
||||
public void testDecompilePcode(File f) {
|
||||
testDecompile(f, ScriptExportMode.PCODE);
|
||||
}
|
||||
|
||||
public void testDecompile(File f, ScriptExportMode exportMode) {
|
||||
try {
|
||||
SWF swf = new SWF(new FileInputStream(f), false);
|
||||
Configuration.debugCopy.set(true);
|
||||
String folderName = exportMode == ScriptExportMode.AS ? "output" : "outputp";
|
||||
File fdir = new File(TESTDATADIR + File.separator + folderName + File.separator + f.getName());
|
||||
fdir.mkdirs();
|
||||
|
||||
swf.exportActionScript(new AbortRetryIgnoreHandler() {
|
||||
@Override
|
||||
public int handle(Throwable thrown) {
|
||||
return AbortRetryIgnoreHandler.ABORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbortRetryIgnoreHandler getNewInstance() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}, fdir.getAbsolutePath(), exportMode, false);
|
||||
} catch (Exception ex) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,184 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.abc.ClassPath;
|
||||
import com.jpexs.decompiler.flash.abc.NotSameException;
|
||||
import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.action.Action;
|
||||
import com.jpexs.decompiler.flash.action.parser.ParseException;
|
||||
import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import com.jpexs.decompiler.flash.helpers.CodeFormatting;
|
||||
import com.jpexs.decompiler.flash.helpers.HilightedTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.flash.tags.base.ContainerItem;
|
||||
import com.jpexs.decompiler.flash.treeitems.TreeItem;
|
||||
import com.jpexs.decompiler.flash.treenodes.TagNode;
|
||||
import com.jpexs.decompiler.flash.treenodes.TreeNode;
|
||||
import com.jpexs.decompiler.graph.CompilationException;
|
||||
import com.jpexs.decompiler.graph.TranslateException;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import static org.testng.Assert.fail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class RecompileTest {
|
||||
|
||||
@BeforeClass
|
||||
public void init() {
|
||||
Main.initLogging(false);
|
||||
}
|
||||
|
||||
public static final String TESTDATADIR = "testdata/recompile";
|
||||
|
||||
@Test(dataProvider = "provideFiles")
|
||||
public void testRecompile(String filename) {
|
||||
try {
|
||||
SWF swf = new SWF(new BufferedInputStream(new FileInputStream(TESTDATADIR + File.separator + filename)), false);
|
||||
Configuration.debugCopy.set(true);
|
||||
swf.saveTo(new ByteArrayOutputStream());
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
fail();
|
||||
} catch (NotSameException ex) {
|
||||
fail("File is different after recompiling: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
private void testAS2DirectEditingOneRecursive(int swfVersion,List<TreeNode> nodeList) {
|
||||
for (TreeNode node : nodeList) {
|
||||
if (node.subNodes.isEmpty()) {
|
||||
TreeItem item = node.getItem();
|
||||
if ((item instanceof ASMSource) && (node.export)) {
|
||||
try {
|
||||
ASMSource asm = ((ASMSource) item);
|
||||
HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
Action.actionsToSource(asm, asm.getActions(), asm.toString()/*FIXME?*/, writer);
|
||||
String as = writer.toString();
|
||||
as = asm.removePrefixAndSuffix(as);
|
||||
ActionScriptParser par = new ActionScriptParser(swfVersion);
|
||||
try {
|
||||
asm.setActions(par.actionsFromString(as));
|
||||
} catch (ParseException | CompilationException ex) {
|
||||
fail("Unable to parse: " + item.getSwf().getShortFileName() + "/" + item.toString());
|
||||
}
|
||||
writer = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
Action.actionsToSource(asm, asm.getActions(), asm.toString()/*FIXME?*/, writer);
|
||||
String as2 = writer.toString();
|
||||
as2 = asm.removePrefixAndSuffix(as2);
|
||||
try {
|
||||
asm.setActions(par.actionsFromString(as2));
|
||||
} catch (ParseException | CompilationException ex) {
|
||||
fail("Unable to parse: " + item.getSwf().getShortFileName() + "/" + item.toString());
|
||||
}
|
||||
writer = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
Action.actionsToSource(asm, asm.getActions(), asm.toString()/*FIXME?*/, writer);
|
||||
String as3 = writer.toString();
|
||||
as3 = asm.removePrefixAndSuffix(as3);
|
||||
if (!as3.equals(as2)) {
|
||||
fail("ActionScript is different: " + item.getSwf().getShortFileName() + "/" + item.toString());
|
||||
}
|
||||
} catch (InterruptedException | IOException | OutOfMemoryError | TranslateException | StackOverflowError ex) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
testAS2DirectEditingOneRecursive(swfVersion,node.subNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "provideFiles")
|
||||
public void testDirectEditing(String filename) throws IOException, InterruptedException, com.jpexs.decompiler.flash.abc.avm2.parser.ParseException, CompilationException {
|
||||
Configuration.autoDeobfuscate.set(false);
|
||||
try{SWF swf = new SWF(new BufferedInputStream(new FileInputStream(TESTDATADIR + File.separator + filename)), false);
|
||||
if (swf.fileAttributes.actionScript3) {
|
||||
boolean dotest=false;
|
||||
List<ABC> allAbcs = new ArrayList<>();
|
||||
for (ABCContainerTag ct : swf.abcList) {
|
||||
allAbcs.add(ct.getABC());
|
||||
}
|
||||
for (ABC abc : allAbcs) {
|
||||
for (int s = 0; s < abc.script_info.size(); s++) {
|
||||
|
||||
String startAfter=null;
|
||||
HilightedTextWriter htw = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
MyEntry<ClassPath, ScriptPack> en = abc.script_info.get(s).getPacks(abc, s).get(0);
|
||||
if(startAfter==null || en.key.toString().equals(startAfter)){
|
||||
dotest = true;
|
||||
}
|
||||
if(!dotest){
|
||||
System.out.println("Skipped:"+en.key.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.println("Recompiling:"+en.key.toString()+"...");
|
||||
en.value.toSource(htw, swf.abcList, abc.script_info.get(s).traits.traits, ScriptExportMode.AS, false);
|
||||
String original = htw.toString();
|
||||
ABC nabc = new ABC(swf);
|
||||
com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser.compile(original, nabc,allAbcs, false, en.key.className + ".as");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<ContainerItem> list2 = new ArrayList<>();
|
||||
list2.addAll(swf.tags);
|
||||
List<TreeNode> list = SWF.createASTagList(list2, null);
|
||||
|
||||
TagNode.setExport(list, true);
|
||||
testAS2DirectEditingOneRecursive(swf.version,list);
|
||||
}
|
||||
}catch(Exception ex){
|
||||
System.out.println("FAIL");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "provideFiles")
|
||||
public Object[][] provideFiles() {
|
||||
File dir = new File(TESTDATADIR);
|
||||
if (!dir.exists()) {
|
||||
return new Object[0][];
|
||||
}
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(".swf");
|
||||
}
|
||||
});
|
||||
Object[][] ret = new Object[files.length][1];
|
||||
for (int f = 0; f < files.length; f++) {
|
||||
ret[f][0] = files[f].getName();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2014 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.abc.ClassPath;
|
||||
import com.jpexs.decompiler.flash.abc.NotSameException;
|
||||
import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.action.Action;
|
||||
import com.jpexs.decompiler.flash.action.parser.ParseException;
|
||||
import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import com.jpexs.decompiler.flash.helpers.CodeFormatting;
|
||||
import com.jpexs.decompiler.flash.helpers.HilightedTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.flash.tags.base.ContainerItem;
|
||||
import com.jpexs.decompiler.flash.treeitems.TreeItem;
|
||||
import com.jpexs.decompiler.flash.treenodes.TagNode;
|
||||
import com.jpexs.decompiler.flash.treenodes.TreeNode;
|
||||
import com.jpexs.decompiler.graph.CompilationException;
|
||||
import com.jpexs.decompiler.graph.TranslateException;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import static org.testng.Assert.fail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class RecompileTest {
|
||||
|
||||
@BeforeClass
|
||||
public void init() {
|
||||
Main.initLogging(false);
|
||||
}
|
||||
|
||||
public static final String TESTDATADIR = "testdata/recompile";
|
||||
|
||||
@Test(dataProvider = "provideFiles")
|
||||
public void testRecompile(String filename) {
|
||||
try {
|
||||
SWF swf = new SWF(new BufferedInputStream(new FileInputStream(TESTDATADIR + File.separator + filename)), false);
|
||||
Configuration.debugCopy.set(true);
|
||||
swf.saveTo(new ByteArrayOutputStream());
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
fail();
|
||||
} catch (NotSameException ex) {
|
||||
fail("File is different after recompiling: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
private void testAS2DirectEditingOneRecursive(int swfVersion, List<TreeNode> nodeList) {
|
||||
for (TreeNode node : nodeList) {
|
||||
if (node.subNodes.isEmpty()) {
|
||||
TreeItem item = node.getItem();
|
||||
if ((item instanceof ASMSource) && (node.export)) {
|
||||
try {
|
||||
ASMSource asm = ((ASMSource) item);
|
||||
HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
Action.actionsToSource(asm, asm.getActions(), asm.toString()/*FIXME?*/, writer);
|
||||
String as = writer.toString();
|
||||
as = asm.removePrefixAndSuffix(as);
|
||||
ActionScriptParser par = new ActionScriptParser(swfVersion);
|
||||
try {
|
||||
asm.setActions(par.actionsFromString(as));
|
||||
} catch (ParseException | CompilationException ex) {
|
||||
fail("Unable to parse: " + item.getSwf().getShortFileName() + "/" + item.toString());
|
||||
}
|
||||
writer = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
Action.actionsToSource(asm, asm.getActions(), asm.toString()/*FIXME?*/, writer);
|
||||
String as2 = writer.toString();
|
||||
as2 = asm.removePrefixAndSuffix(as2);
|
||||
try {
|
||||
asm.setActions(par.actionsFromString(as2));
|
||||
} catch (ParseException | CompilationException ex) {
|
||||
fail("Unable to parse: " + item.getSwf().getShortFileName() + "/" + item.toString());
|
||||
}
|
||||
writer = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
Action.actionsToSource(asm, asm.getActions(), asm.toString()/*FIXME?*/, writer);
|
||||
String as3 = writer.toString();
|
||||
as3 = asm.removePrefixAndSuffix(as3);
|
||||
if (!as3.equals(as2)) {
|
||||
fail("ActionScript is different: " + item.getSwf().getShortFileName() + "/" + item.toString());
|
||||
}
|
||||
} catch (InterruptedException | IOException | OutOfMemoryError | TranslateException | StackOverflowError ex) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
testAS2DirectEditingOneRecursive(swfVersion, node.subNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "provideFiles")
|
||||
public void testDirectEditing(String filename) throws IOException, InterruptedException, com.jpexs.decompiler.flash.abc.avm2.parser.ParseException, CompilationException {
|
||||
Configuration.autoDeobfuscate.set(false);
|
||||
try {
|
||||
SWF swf = new SWF(new BufferedInputStream(new FileInputStream(TESTDATADIR + File.separator + filename)), false);
|
||||
if (swf.fileAttributes.actionScript3) {
|
||||
boolean dotest = false;
|
||||
List<ABC> allAbcs = new ArrayList<>();
|
||||
for (ABCContainerTag ct : swf.abcList) {
|
||||
allAbcs.add(ct.getABC());
|
||||
}
|
||||
for (ABC abc : allAbcs) {
|
||||
for (int s = 0; s < abc.script_info.size(); s++) {
|
||||
|
||||
String startAfter = null;
|
||||
HilightedTextWriter htw = new HilightedTextWriter(new CodeFormatting(), false);
|
||||
MyEntry<ClassPath, ScriptPack> en = abc.script_info.get(s).getPacks(abc, s).get(0);
|
||||
if (startAfter == null || en.key.toString().equals(startAfter)) {
|
||||
dotest = true;
|
||||
}
|
||||
if (!dotest) {
|
||||
System.out.println("Skipped:" + en.key.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.println("Recompiling:" + en.key.toString() + "...");
|
||||
en.value.toSource(htw, swf.abcList, abc.script_info.get(s).traits.traits, ScriptExportMode.AS, false);
|
||||
String original = htw.toString();
|
||||
ABC nabc = new ABC(swf);
|
||||
com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser.compile(original, nabc, allAbcs, false, en.key.className + ".as");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<ContainerItem> list2 = new ArrayList<>();
|
||||
list2.addAll(swf.tags);
|
||||
List<TreeNode> list = SWF.createASTagList(list2, null);
|
||||
|
||||
TagNode.setExport(list, true);
|
||||
testAS2DirectEditingOneRecursive(swf.version, list);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
System.out.println("FAIL");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "provideFiles")
|
||||
public Object[][] provideFiles() {
|
||||
File dir = new File(TESTDATADIR);
|
||||
if (!dir.exists()) {
|
||||
return new Object[0][];
|
||||
}
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(".swf");
|
||||
}
|
||||
});
|
||||
Object[][] ret = new Object[files.length][1];
|
||||
for (int f = 0; f < files.length; f++) {
|
||||
ret[f][0] = files[f].getName();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ import org.testng.annotations.Test;
|
||||
public class SWFStreamTest {
|
||||
|
||||
@BeforeClass
|
||||
public void init(){
|
||||
public void init() {
|
||||
Main.initLogging(false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNeededBits() {
|
||||
assertEquals(SWFOutputStream.getNeededBitsU(3), 2);
|
||||
|
||||
Reference in New Issue
Block a user