mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-09-25 15:41:12 +00:00
jump to resources view from hex view
This commit is contained in:
+111
-111
@@ -1,111 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache;
|
||||
|
||||
import com.jpexs.helpers.LimitedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public abstract class CacheEntry {
|
||||
|
||||
public abstract String getRequestURL();
|
||||
|
||||
public abstract Map<String, String> getResponseHeaders();
|
||||
|
||||
public abstract String getStatusLine();
|
||||
|
||||
public abstract String getRequestMethod();
|
||||
|
||||
public abstract InputStream getResponseRawDataStream();
|
||||
|
||||
public InputStream getResponseDataStream() {
|
||||
String contentLengthStr = getHeader("Content-Length");
|
||||
int contentLength = -1;
|
||||
if (contentLengthStr != null) {
|
||||
try {
|
||||
contentLength = Integer.parseInt(contentLengthStr);
|
||||
} catch (NumberFormatException nex) {
|
||||
}
|
||||
}
|
||||
final InputStream rawIs = getResponseRawDataStream();
|
||||
InputStream is = rawIs;
|
||||
if (contentLength > -1) {
|
||||
is = new LimitedInputStream(is, contentLength);
|
||||
}
|
||||
|
||||
String encoding = getHeader("Content-Encoding");
|
||||
if (encoding != null) {
|
||||
switch (encoding) {
|
||||
case "gzip":
|
||||
try {
|
||||
is = new GZIPInputStream(is);
|
||||
} catch (IOException ex) {
|
||||
is = null;
|
||||
//ignore
|
||||
}
|
||||
break;
|
||||
case "deflate":
|
||||
is = new InflaterInputStream(is);
|
||||
break;
|
||||
default: //unknown
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ("chunked".equals(getHeader("Transfer-Encoding"))) {
|
||||
is = new ChunkedInputStream(is);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getRequestURL();
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
String st = getStatusLine();
|
||||
if (st == null) {
|
||||
return 0;
|
||||
}
|
||||
String parts[] = st.split(" ");
|
||||
try {
|
||||
return Integer.parseInt(parts[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String getHeader(String header) {
|
||||
Map<String, String> m = getResponseHeaders();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
for (String k : m.keySet()) {
|
||||
if (k.toLowerCase().equals(header.toLowerCase())) {
|
||||
return m.get(k);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache;
|
||||
|
||||
import com.jpexs.helpers.LimitedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public abstract class CacheEntry {
|
||||
|
||||
public abstract String getRequestURL();
|
||||
|
||||
public abstract Map<String, String> getResponseHeaders();
|
||||
|
||||
public abstract String getStatusLine();
|
||||
|
||||
public abstract String getRequestMethod();
|
||||
|
||||
public abstract InputStream getResponseRawDataStream();
|
||||
|
||||
public InputStream getResponseDataStream() {
|
||||
String contentLengthStr = getHeader("Content-Length");
|
||||
int contentLength = -1;
|
||||
if (contentLengthStr != null) {
|
||||
try {
|
||||
contentLength = Integer.parseInt(contentLengthStr);
|
||||
} catch (NumberFormatException nex) {
|
||||
}
|
||||
}
|
||||
final InputStream rawIs = getResponseRawDataStream();
|
||||
InputStream is = rawIs;
|
||||
if (contentLength > -1) {
|
||||
is = new LimitedInputStream(is, contentLength);
|
||||
}
|
||||
|
||||
String encoding = getHeader("Content-Encoding");
|
||||
if (encoding != null) {
|
||||
switch (encoding) {
|
||||
case "gzip":
|
||||
try {
|
||||
is = new GZIPInputStream(is);
|
||||
} catch (IOException ex) {
|
||||
is = null;
|
||||
//ignore
|
||||
}
|
||||
break;
|
||||
case "deflate":
|
||||
is = new InflaterInputStream(is);
|
||||
break;
|
||||
default: //unknown
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ("chunked".equals(getHeader("Transfer-Encoding"))) {
|
||||
is = new ChunkedInputStream(is);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getRequestURL();
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
String st = getStatusLine();
|
||||
if (st == null) {
|
||||
return 0;
|
||||
}
|
||||
String[] parts = st.split(" ");
|
||||
try {
|
||||
return Integer.parseInt(parts[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String getHeader(String header) {
|
||||
Map<String, String> m = getResponseHeaders();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
for (String k : m.keySet()) {
|
||||
if (k.toLowerCase().equals(header.toLowerCase())) {
|
||||
return m.get(k);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+83
-83
@@ -1,83 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class BlockFileHeader {
|
||||
|
||||
protected static final int kBlockHeaderSize = 8192; // Two pages: almost 64k entries
|
||||
|
||||
private static final int kMaxBlocks = (kBlockHeaderSize - 80) * 8;
|
||||
|
||||
private final long magic; // c3 ca 04 c1
|
||||
|
||||
private final long version; // 00 00 02 00
|
||||
|
||||
private final int this_file;
|
||||
|
||||
private final int next_file;
|
||||
|
||||
private final int entry_size;
|
||||
|
||||
private final int num_entries;
|
||||
|
||||
private final int max_entries;
|
||||
|
||||
private int empty[] = new int[4];
|
||||
|
||||
private int hints[] = new int[4];
|
||||
|
||||
private final int updating;
|
||||
|
||||
private int user[] = new int[5];
|
||||
|
||||
private final long allocation_map[];
|
||||
|
||||
public BlockFileHeader(InputStream is) throws IOException {
|
||||
this.allocation_map = new long[kMaxBlocks / 32];
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
magic = iis.readUInt32();
|
||||
version = iis.readUInt32();
|
||||
this_file = iis.readInt16();
|
||||
next_file = iis.readInt16();
|
||||
entry_size = iis.readInt32();
|
||||
num_entries = iis.readInt32();
|
||||
max_entries = iis.readInt32();
|
||||
empty = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
empty[i] = iis.readInt32();
|
||||
}
|
||||
hints = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
hints[i] = iis.readInt32();
|
||||
}
|
||||
updating = iis.readInt32();
|
||||
user = new int[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
user[i] = iis.readInt32();
|
||||
}
|
||||
for (int i = 0; i < allocation_map.length; i++) {
|
||||
allocation_map[i] = iis.readUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class BlockFileHeader {
|
||||
|
||||
protected static final int kBlockHeaderSize = 8192; // Two pages: almost 64k entries
|
||||
|
||||
private static final int kMaxBlocks = (kBlockHeaderSize - 80) * 8;
|
||||
|
||||
private final long magic; // c3 ca 04 c1
|
||||
|
||||
private final long version; // 00 00 02 00
|
||||
|
||||
private final int this_file;
|
||||
|
||||
private final int next_file;
|
||||
|
||||
private final int entry_size;
|
||||
|
||||
private final int num_entries;
|
||||
|
||||
private final int max_entries;
|
||||
|
||||
private int[] empty = new int[4];
|
||||
|
||||
private int[] hints = new int[4];
|
||||
|
||||
private final int updating;
|
||||
|
||||
private int[] user = new int[5];
|
||||
|
||||
private final long allocation_map[];
|
||||
|
||||
public BlockFileHeader(InputStream is) throws IOException {
|
||||
this.allocation_map = new long[kMaxBlocks / 32];
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
magic = iis.readUInt32();
|
||||
version = iis.readUInt32();
|
||||
this_file = iis.readInt16();
|
||||
next_file = iis.readInt16();
|
||||
entry_size = iis.readInt32();
|
||||
num_entries = iis.readInt32();
|
||||
max_entries = iis.readInt32();
|
||||
empty = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
empty[i] = iis.readInt32();
|
||||
}
|
||||
hints = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
hints[i] = iis.readInt32();
|
||||
}
|
||||
updating = iis.readInt32();
|
||||
user = new int[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
user[i] = iis.readInt32();
|
||||
}
|
||||
for (int i = 0; i < allocation_map.length; i++) {
|
||||
allocation_map[i] = iis.readUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+151
-151
@@ -1,151 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import com.jpexs.browsers.cache.RafInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class CacheAddr {
|
||||
|
||||
private static final int EXTERNAL = 0;
|
||||
|
||||
private static final int RANKINGS = 1;
|
||||
|
||||
private static final int BLOCK_256 = 2;
|
||||
|
||||
private static final int BLOCK_1K = 3;
|
||||
|
||||
private static final int BLOCK_4K = 4;
|
||||
|
||||
private static final int BLOCK_FILES = 5;
|
||||
|
||||
private static final int BLOCK_ENTRIES = 6;
|
||||
|
||||
private static final int BLOCK_EVICTED = 7;
|
||||
|
||||
private static final String blockNames[] = new String[]{"EXTERNAL", "RANKINGS", "BLOCK_256", "BLOCK_1K", "BLOCK_4K", "BLOCK_FILES", "BLOCK_ENTRIES", "BLOCK_EVICTED"};
|
||||
|
||||
private static final int blockSizes[] = new int[]{0, 36, 256, 1024, 4096, 8, 104, 48};
|
||||
|
||||
private static final long kInitializedMask = 0x80000000L;
|
||||
|
||||
private static final long kFileTypeMask = 0x70000000L;
|
||||
|
||||
private static final int kFileTypeOffset = 28;
|
||||
|
||||
private static final long kReservedBitsMask = 0x0c000000L;
|
||||
|
||||
private static final long kNumBlocksMask = 0x03000000L;
|
||||
|
||||
private static final int kNumBlocksOffset = 24;
|
||||
|
||||
private static final long kFileSelectorMask = 0x00ff0000L;
|
||||
|
||||
private static final int FileSelectorOffset = 16;
|
||||
|
||||
private static final long kStartBlockMask = 0x0000FFFFL;
|
||||
|
||||
private static final long kFileNameMask = 0x0FFFFFFFL;
|
||||
|
||||
private final boolean initialized;
|
||||
|
||||
private final int fileType;
|
||||
|
||||
private int numBlocks;
|
||||
|
||||
private int fileSelector;
|
||||
|
||||
private int startBlock;
|
||||
|
||||
private int fileName;
|
||||
|
||||
private final long val;
|
||||
|
||||
private final File rootPath;
|
||||
|
||||
private final Map<Integer, RandomAccessFile> dataFiles;
|
||||
|
||||
private final File externalFilesDir;
|
||||
|
||||
public CacheAddr(InputStream is, File rootPath, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
this.dataFiles = dataFiles;
|
||||
this.rootPath = rootPath;
|
||||
this.externalFilesDir = externalFilesDir;
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
val = iis.readUInt32();
|
||||
initialized = (val & kInitializedMask) == kInitializedMask;
|
||||
fileType = (int) ((val & kFileTypeMask) >> kFileTypeOffset);
|
||||
if (fileType == EXTERNAL) {
|
||||
fileName = (int) (val & kFileNameMask);
|
||||
} else {
|
||||
numBlocks = (int) ((val & kNumBlocksMask) >> kNumBlocksOffset);
|
||||
fileSelector = (int) ((val & kFileSelectorMask) >> FileSelectorOffset);
|
||||
startBlock = (int) (val & kStartBlockMask);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String ft = blockNames[fileType];
|
||||
if (fileType == EXTERNAL) {
|
||||
return ft + ":" + fileName;
|
||||
}
|
||||
if (!initialized) {
|
||||
return "uninitialized";
|
||||
}
|
||||
return ft + ": numBlocks " + numBlocks + " fileSelector " + fileSelector + " startBlock " + startBlock;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (!initialized) {
|
||||
return null;
|
||||
}
|
||||
switch (fileType) {
|
||||
case EXTERNAL:
|
||||
String fileNameStr = Long.toHexString(fileName);
|
||||
while (fileNameStr.length() < 6) {
|
||||
fileNameStr = "0" + fileNameStr;
|
||||
}
|
||||
fileNameStr = "f_" + fileNameStr;
|
||||
return new RafInputStream(new RandomAccessFile(new File(externalFilesDir, fileNameStr), "r"));
|
||||
case BLOCK_1K:
|
||||
case BLOCK_256:
|
||||
case BLOCK_4K:
|
||||
|
||||
RandomAccessFile raf;
|
||||
|
||||
if (dataFiles.containsKey(fileSelector)) {
|
||||
raf = dataFiles.get(fileSelector);
|
||||
} else {
|
||||
raf = new RandomAccessFile(rootPath + "\\data_" + fileSelector, "r");
|
||||
dataFiles.put(fileSelector, raf);
|
||||
}
|
||||
raf.seek(BlockFileHeader.kBlockHeaderSize + startBlock * blockSizes[fileType]);
|
||||
return new RafInputStream(raf);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import com.jpexs.browsers.cache.RafInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class CacheAddr {
|
||||
|
||||
private static final int EXTERNAL = 0;
|
||||
|
||||
private static final int RANKINGS = 1;
|
||||
|
||||
private static final int BLOCK_256 = 2;
|
||||
|
||||
private static final int BLOCK_1K = 3;
|
||||
|
||||
private static final int BLOCK_4K = 4;
|
||||
|
||||
private static final int BLOCK_FILES = 5;
|
||||
|
||||
private static final int BLOCK_ENTRIES = 6;
|
||||
|
||||
private static final int BLOCK_EVICTED = 7;
|
||||
|
||||
private static final String[] blockNames = new String[]{"EXTERNAL", "RANKINGS", "BLOCK_256", "BLOCK_1K", "BLOCK_4K", "BLOCK_FILES", "BLOCK_ENTRIES", "BLOCK_EVICTED"};
|
||||
|
||||
private static final int[] blockSizes = new int[]{0, 36, 256, 1024, 4096, 8, 104, 48};
|
||||
|
||||
private static final long kInitializedMask = 0x80000000L;
|
||||
|
||||
private static final long kFileTypeMask = 0x70000000L;
|
||||
|
||||
private static final int kFileTypeOffset = 28;
|
||||
|
||||
private static final long kReservedBitsMask = 0x0c000000L;
|
||||
|
||||
private static final long kNumBlocksMask = 0x03000000L;
|
||||
|
||||
private static final int kNumBlocksOffset = 24;
|
||||
|
||||
private static final long kFileSelectorMask = 0x00ff0000L;
|
||||
|
||||
private static final int FileSelectorOffset = 16;
|
||||
|
||||
private static final long kStartBlockMask = 0x0000FFFFL;
|
||||
|
||||
private static final long kFileNameMask = 0x0FFFFFFFL;
|
||||
|
||||
private final boolean initialized;
|
||||
|
||||
private final int fileType;
|
||||
|
||||
private int numBlocks;
|
||||
|
||||
private int fileSelector;
|
||||
|
||||
private int startBlock;
|
||||
|
||||
private int fileName;
|
||||
|
||||
private final long val;
|
||||
|
||||
private final File rootPath;
|
||||
|
||||
private final Map<Integer, RandomAccessFile> dataFiles;
|
||||
|
||||
private final File externalFilesDir;
|
||||
|
||||
public CacheAddr(InputStream is, File rootPath, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
this.dataFiles = dataFiles;
|
||||
this.rootPath = rootPath;
|
||||
this.externalFilesDir = externalFilesDir;
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
val = iis.readUInt32();
|
||||
initialized = (val & kInitializedMask) == kInitializedMask;
|
||||
fileType = (int) ((val & kFileTypeMask) >> kFileTypeOffset);
|
||||
if (fileType == EXTERNAL) {
|
||||
fileName = (int) (val & kFileNameMask);
|
||||
} else {
|
||||
numBlocks = (int) ((val & kNumBlocksMask) >> kNumBlocksOffset);
|
||||
fileSelector = (int) ((val & kFileSelectorMask) >> FileSelectorOffset);
|
||||
startBlock = (int) (val & kStartBlockMask);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String ft = blockNames[fileType];
|
||||
if (fileType == EXTERNAL) {
|
||||
return ft + ":" + fileName;
|
||||
}
|
||||
if (!initialized) {
|
||||
return "uninitialized";
|
||||
}
|
||||
return ft + ": numBlocks " + numBlocks + " fileSelector " + fileSelector + " startBlock " + startBlock;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (!initialized) {
|
||||
return null;
|
||||
}
|
||||
switch (fileType) {
|
||||
case EXTERNAL:
|
||||
String fileNameStr = Long.toHexString(fileName);
|
||||
while (fileNameStr.length() < 6) {
|
||||
fileNameStr = "0" + fileNameStr;
|
||||
}
|
||||
fileNameStr = "f_" + fileNameStr;
|
||||
return new RafInputStream(new RandomAccessFile(new File(externalFilesDir, fileNameStr), "r"));
|
||||
case BLOCK_1K:
|
||||
case BLOCK_256:
|
||||
case BLOCK_4K:
|
||||
|
||||
RandomAccessFile raf;
|
||||
|
||||
if (dataFiles.containsKey(fileSelector)) {
|
||||
raf = dataFiles.get(fileSelector);
|
||||
} else {
|
||||
raf = new RandomAccessFile(rootPath + "\\data_" + fileSelector, "r");
|
||||
dataFiles.put(fileSelector, raf);
|
||||
}
|
||||
raf.seek(BlockFileHeader.kBlockHeaderSize + startBlock * blockSizes[fileType]);
|
||||
return new RafInputStream(raf);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+174
-174
@@ -1,174 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import com.jpexs.browsers.cache.CacheEntry;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class EntryStore extends CacheEntry {
|
||||
|
||||
public static final int ENTRY_NORMAL = 0;
|
||||
|
||||
public static final int ENTRY_EVICTED = 1; // The entry was recently evicted from the cache.
|
||||
|
||||
public static final int ENTRY_DOOMED = 2; // The entry was doomed
|
||||
|
||||
public static final int PARENT_ENTRY = 1; // This entry has children (sparse) entries.
|
||||
|
||||
public static final int CHILD_ENTRY = 1 << 1;
|
||||
|
||||
public long hash; // Full hash of the key.
|
||||
|
||||
public CacheAddr next; // Next entry with the same hash or bucket.
|
||||
|
||||
public CacheAddr rankings_node; // Rankings node for this entry.
|
||||
|
||||
public int reuse_count; // How often is this entry used.
|
||||
|
||||
public int refetch_count; // How often is this fetched from the net.
|
||||
|
||||
public int state; // Current state.
|
||||
|
||||
public long creation_time;
|
||||
|
||||
public int key_len;
|
||||
|
||||
public CacheAddr long_key; // Optional address of a long key.
|
||||
|
||||
public int data_size[] = new int[4]; // We can store up to 4 data streams for each
|
||||
|
||||
public CacheAddr data_addr[] = new CacheAddr[4]; // entry.
|
||||
|
||||
public long flags; // Any combination of EntryFlags.
|
||||
|
||||
public int pad[] = new int[4];
|
||||
|
||||
public long self_hash; // The hash of EntryStore up to this point.
|
||||
|
||||
public byte key[] = new byte[256 - 24 * 4]; // null terminated
|
||||
|
||||
public EntryStore(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
hash = iis.readUInt32();
|
||||
next = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
rankings_node = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
reuse_count = iis.readInt32();
|
||||
refetch_count = iis.readInt32();
|
||||
state = iis.readInt32();
|
||||
creation_time = iis.readUInt64();
|
||||
key_len = iis.readInt32();
|
||||
long_key = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
data_size = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
data_size[i] = iis.readInt32();
|
||||
}
|
||||
data_addr = new CacheAddr[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
data_addr[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
flags = iis.readUInt32();
|
||||
pad = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pad[i] = iis.readInt32();
|
||||
}
|
||||
self_hash = iis.readUInt32();
|
||||
key = new byte[256 - 24 * 4];
|
||||
if (iis.read(key) != key.length) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponseInfo getResponseInfo() {
|
||||
try {
|
||||
InputStream is = data_addr[0].getInputStream();
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
return new HttpResponseInfo(is);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(EntryStore.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getResponseDataSize() {
|
||||
return data_size[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResponseRawDataStream() {
|
||||
try {
|
||||
return data_addr[1].getInputStream();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(EntryStore.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
if (key_len < 0) {
|
||||
return null;
|
||||
}
|
||||
return new String(key, 0, key_len > key.length ? key.length : key_len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestURL() {
|
||||
return getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getResponseHeaders() {
|
||||
HttpResponseInfo ri = getResponseInfo();
|
||||
if (ri == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
List<String> headers = ri.headers;
|
||||
Map<String, String> ret = new HashMap<>();
|
||||
for (int h = 1; h < headers.size(); h++) {
|
||||
String hs = headers.get(h);
|
||||
if (hs.contains(":")) {
|
||||
String hp[] = hs.split(":");
|
||||
ret.put(hp[0].trim(), hp[1].trim());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusLine() {
|
||||
HttpResponseInfo ri = getResponseInfo();
|
||||
return ri.headers.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestMethod() {
|
||||
return "GET";
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import com.jpexs.browsers.cache.CacheEntry;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class EntryStore extends CacheEntry {
|
||||
|
||||
public static final int ENTRY_NORMAL = 0;
|
||||
|
||||
public static final int ENTRY_EVICTED = 1; // The entry was recently evicted from the cache.
|
||||
|
||||
public static final int ENTRY_DOOMED = 2; // The entry was doomed
|
||||
|
||||
public static final int PARENT_ENTRY = 1; // This entry has children (sparse) entries.
|
||||
|
||||
public static final int CHILD_ENTRY = 1 << 1;
|
||||
|
||||
public long hash; // Full hash of the key.
|
||||
|
||||
public CacheAddr next; // Next entry with the same hash or bucket.
|
||||
|
||||
public CacheAddr rankings_node; // Rankings node for this entry.
|
||||
|
||||
public int reuse_count; // How often is this entry used.
|
||||
|
||||
public int refetch_count; // How often is this fetched from the net.
|
||||
|
||||
public int state; // Current state.
|
||||
|
||||
public long creation_time;
|
||||
|
||||
public int key_len;
|
||||
|
||||
public CacheAddr long_key; // Optional address of a long key.
|
||||
|
||||
public int[] data_size = new int[4]; // We can store up to 4 data streams for each
|
||||
|
||||
public CacheAddr[] data_addr = new CacheAddr[4]; // entry.
|
||||
|
||||
public long flags; // Any combination of EntryFlags.
|
||||
|
||||
public int[] pad = new int[4];
|
||||
|
||||
public long self_hash; // The hash of EntryStore up to this point.
|
||||
|
||||
public byte[] key = new byte[256 - 24 * 4]; // null terminated
|
||||
|
||||
public EntryStore(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
hash = iis.readUInt32();
|
||||
next = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
rankings_node = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
reuse_count = iis.readInt32();
|
||||
refetch_count = iis.readInt32();
|
||||
state = iis.readInt32();
|
||||
creation_time = iis.readUInt64();
|
||||
key_len = iis.readInt32();
|
||||
long_key = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
data_size = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
data_size[i] = iis.readInt32();
|
||||
}
|
||||
data_addr = new CacheAddr[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
data_addr[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
flags = iis.readUInt32();
|
||||
pad = new int[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pad[i] = iis.readInt32();
|
||||
}
|
||||
self_hash = iis.readUInt32();
|
||||
key = new byte[256 - 24 * 4];
|
||||
if (iis.read(key) != key.length) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponseInfo getResponseInfo() {
|
||||
try {
|
||||
InputStream is = data_addr[0].getInputStream();
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
return new HttpResponseInfo(is);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(EntryStore.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getResponseDataSize() {
|
||||
return data_size[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResponseRawDataStream() {
|
||||
try {
|
||||
return data_addr[1].getInputStream();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(EntryStore.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
if (key_len < 0) {
|
||||
return null;
|
||||
}
|
||||
return new String(key, 0, key_len > key.length ? key.length : key_len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestURL() {
|
||||
return getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getResponseHeaders() {
|
||||
HttpResponseInfo ri = getResponseInfo();
|
||||
if (ri == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
List<String> headers = ri.headers;
|
||||
Map<String, String> ret = new HashMap<>();
|
||||
for (int h = 1; h < headers.size(); h++) {
|
||||
String hs = headers.get(h);
|
||||
if (hs.contains(":")) {
|
||||
String[] hp = hs.split(":");
|
||||
ret.put(hp[0].trim(), hp[1].trim());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusLine() {
|
||||
HttpResponseInfo ri = getResponseInfo();
|
||||
return ri.headers.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestMethod() {
|
||||
return "GET";
|
||||
}
|
||||
}
|
||||
|
||||
+127
-127
@@ -1,127 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class HttpResponseInfo {
|
||||
|
||||
public long flags;
|
||||
|
||||
public int version;
|
||||
|
||||
public long request_time;
|
||||
|
||||
public long response_time;
|
||||
|
||||
public long payload_size;
|
||||
|
||||
public List<String> headers;
|
||||
|
||||
// The version of the response info used when persisting response info.
|
||||
public static final int RESPONSE_INFO_VERSION = 3;
|
||||
|
||||
// The minimum version supported for deserializing response info.
|
||||
public static final int RESPONSE_INFO_MINIMUM_VERSION = 1;
|
||||
|
||||
// We reserve up to 8 bits for the version number.
|
||||
public static final int RESPONSE_INFO_VERSION_MASK = 0xFF;
|
||||
|
||||
// This bit is set if the response info has a cert at the end.
|
||||
// Version 1 serialized only the end-entity certificate, while subsequent
|
||||
// versions include the available certificate chain.
|
||||
public static final int RESPONSE_INFO_HAS_CERT = 1 << 8;
|
||||
|
||||
// This bit is set if the response info has a security-bits field (security
|
||||
// strength, in bits, of the SSL connection) at the end.
|
||||
public static final int RESPONSE_INFO_HAS_SECURITY_BITS = 1 << 9;
|
||||
|
||||
// This bit is set if the response info has a cert status at the end.
|
||||
public static final int RESPONSE_INFO_HAS_CERT_STATUS = 1 << 10;
|
||||
|
||||
// This bit is set if the response info has vary header data.
|
||||
public static final int RESPONSE_INFO_HAS_VARY_DATA = 1 << 11;
|
||||
|
||||
// This bit is set if the request was cancelled before completion.
|
||||
public static final int RESPONSE_INFO_TRUNCATED = 1 << 12;
|
||||
|
||||
// This bit is set if the response was received via SPDY.
|
||||
public static final int RESPONSE_INFO_WAS_SPDY = 1 << 13;
|
||||
|
||||
// This bit is set if the request has NPN negotiated.
|
||||
public static final int RESPONSE_INFO_WAS_NPN = 1 << 14;
|
||||
|
||||
// This bit is set if the request was fetched via an explicit proxy.
|
||||
public static final int RESPONSE_INFO_WAS_PROXY = 1 << 15;
|
||||
|
||||
// This bit is set if the response info has an SSL connection status field.
|
||||
// This contains the ciphersuite used to fetch the resource as well as the
|
||||
// protocol version, compression method and whether SSLv3 fallback was used.
|
||||
public static final int RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS = 1 << 16;
|
||||
|
||||
// This bit is set if the response info has protocol version.
|
||||
public static final int RESPONSE_INFO_HAS_NPN_NEGOTIATED_PROTOCOL = 1 << 17;
|
||||
|
||||
// This bit is set if the response info has connection info.
|
||||
public static final int RESPONSE_INFO_HAS_CONNECTION_INFO = 1 << 18;
|
||||
|
||||
// This bit is set if the request has http authentication.
|
||||
public static final int RESPONSE_INFO_USE_HTTP_AUTHENTICATION = 1 << 19;
|
||||
|
||||
public String getHeaderValue(String header) {
|
||||
for (String h : headers) {
|
||||
if (h.contains(":")) {
|
||||
String keyval[] = h.split(":");
|
||||
String key = keyval[0].trim().toLowerCase();
|
||||
String val = keyval[1].trim();
|
||||
if (header.toLowerCase().equals(key)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpResponseInfo(InputStream is) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
payload_size = iis.readUInt32();
|
||||
flags = iis.readInt();
|
||||
version = (int) (flags & RESPONSE_INFO_VERSION_MASK);
|
||||
if (version < RESPONSE_INFO_MINIMUM_VERSION || version > RESPONSE_INFO_VERSION) {
|
||||
throw new RuntimeException("unexpected response info version: " + version);
|
||||
}
|
||||
request_time = iis.readInt64();
|
||||
response_time = iis.readInt64();
|
||||
String headersStr = iis.readString();
|
||||
headers = new ArrayList<>();
|
||||
int nulpos;
|
||||
while ((nulpos = headersStr.indexOf(0)) > 0) {
|
||||
String h = headersStr.substring(0, nulpos);
|
||||
headersStr = headersStr.substring(nulpos + 1);
|
||||
headers.add(h);
|
||||
}
|
||||
|
||||
//TODO: Read SSL info
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class HttpResponseInfo {
|
||||
|
||||
public long flags;
|
||||
|
||||
public int version;
|
||||
|
||||
public long request_time;
|
||||
|
||||
public long response_time;
|
||||
|
||||
public long payload_size;
|
||||
|
||||
public List<String> headers;
|
||||
|
||||
// The version of the response info used when persisting response info.
|
||||
public static final int RESPONSE_INFO_VERSION = 3;
|
||||
|
||||
// The minimum version supported for deserializing response info.
|
||||
public static final int RESPONSE_INFO_MINIMUM_VERSION = 1;
|
||||
|
||||
// We reserve up to 8 bits for the version number.
|
||||
public static final int RESPONSE_INFO_VERSION_MASK = 0xFF;
|
||||
|
||||
// This bit is set if the response info has a cert at the end.
|
||||
// Version 1 serialized only the end-entity certificate, while subsequent
|
||||
// versions include the available certificate chain.
|
||||
public static final int RESPONSE_INFO_HAS_CERT = 1 << 8;
|
||||
|
||||
// This bit is set if the response info has a security-bits field (security
|
||||
// strength, in bits, of the SSL connection) at the end.
|
||||
public static final int RESPONSE_INFO_HAS_SECURITY_BITS = 1 << 9;
|
||||
|
||||
// This bit is set if the response info has a cert status at the end.
|
||||
public static final int RESPONSE_INFO_HAS_CERT_STATUS = 1 << 10;
|
||||
|
||||
// This bit is set if the response info has vary header data.
|
||||
public static final int RESPONSE_INFO_HAS_VARY_DATA = 1 << 11;
|
||||
|
||||
// This bit is set if the request was cancelled before completion.
|
||||
public static final int RESPONSE_INFO_TRUNCATED = 1 << 12;
|
||||
|
||||
// This bit is set if the response was received via SPDY.
|
||||
public static final int RESPONSE_INFO_WAS_SPDY = 1 << 13;
|
||||
|
||||
// This bit is set if the request has NPN negotiated.
|
||||
public static final int RESPONSE_INFO_WAS_NPN = 1 << 14;
|
||||
|
||||
// This bit is set if the request was fetched via an explicit proxy.
|
||||
public static final int RESPONSE_INFO_WAS_PROXY = 1 << 15;
|
||||
|
||||
// This bit is set if the response info has an SSL connection status field.
|
||||
// This contains the ciphersuite used to fetch the resource as well as the
|
||||
// protocol version, compression method and whether SSLv3 fallback was used.
|
||||
public static final int RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS = 1 << 16;
|
||||
|
||||
// This bit is set if the response info has protocol version.
|
||||
public static final int RESPONSE_INFO_HAS_NPN_NEGOTIATED_PROTOCOL = 1 << 17;
|
||||
|
||||
// This bit is set if the response info has connection info.
|
||||
public static final int RESPONSE_INFO_HAS_CONNECTION_INFO = 1 << 18;
|
||||
|
||||
// This bit is set if the request has http authentication.
|
||||
public static final int RESPONSE_INFO_USE_HTTP_AUTHENTICATION = 1 << 19;
|
||||
|
||||
public String getHeaderValue(String header) {
|
||||
for (String h : headers) {
|
||||
if (h.contains(":")) {
|
||||
String[] keyval = h.split(":");
|
||||
String key = keyval[0].trim().toLowerCase();
|
||||
String val = keyval[1].trim();
|
||||
if (header.toLowerCase().equals(key)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpResponseInfo(InputStream is) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
payload_size = iis.readUInt32();
|
||||
flags = iis.readInt();
|
||||
version = (int) (flags & RESPONSE_INFO_VERSION_MASK);
|
||||
if (version < RESPONSE_INFO_MINIMUM_VERSION || version > RESPONSE_INFO_VERSION) {
|
||||
throw new RuntimeException("unexpected response info version: " + version);
|
||||
}
|
||||
request_time = iis.readInt64();
|
||||
response_time = iis.readInt64();
|
||||
String headersStr = iis.readString();
|
||||
headers = new ArrayList<>();
|
||||
int nulpos;
|
||||
while ((nulpos = headersStr.indexOf(0)) > 0) {
|
||||
String h = headersStr.substring(0, nulpos);
|
||||
headersStr = headersStr.substring(nulpos + 1);
|
||||
headers.add(h);
|
||||
}
|
||||
|
||||
//TODO: Read SSL info
|
||||
}
|
||||
}
|
||||
|
||||
+76
-76
@@ -1,76 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class IndexHeader {
|
||||
|
||||
long magic; //c3 ca 03 c1
|
||||
|
||||
long version; //01 00 02 00
|
||||
|
||||
int num_entries;
|
||||
|
||||
int num_bytes;
|
||||
|
||||
int last_file;
|
||||
|
||||
int this_id;
|
||||
|
||||
CacheAddr stats;
|
||||
|
||||
int table_len;
|
||||
|
||||
int crash;
|
||||
|
||||
int experiment;
|
||||
|
||||
long create_time;
|
||||
|
||||
int pad[] = new int[52];
|
||||
|
||||
LruData lru;
|
||||
|
||||
public IndexHeader(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
magic = iis.readUInt32();
|
||||
version = iis.readUInt32();
|
||||
num_entries = iis.readInt32();
|
||||
num_bytes = iis.readInt32();
|
||||
last_file = iis.readInt32();
|
||||
this_id = iis.readInt32();
|
||||
stats = new CacheAddr(iis, rootDir, dataFiles, externalFilesDir);
|
||||
table_len = iis.readInt32();
|
||||
crash = iis.readInt32();
|
||||
experiment = iis.readInt32();
|
||||
create_time = iis.readUInt64();
|
||||
pad = new int[52];
|
||||
for (int i = 0; i < 52; i++) {
|
||||
pad[i] = iis.readInt32();
|
||||
}
|
||||
lru = new LruData(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class IndexHeader {
|
||||
|
||||
long magic; //c3 ca 03 c1
|
||||
|
||||
long version; //01 00 02 00
|
||||
|
||||
int num_entries;
|
||||
|
||||
int num_bytes;
|
||||
|
||||
int last_file;
|
||||
|
||||
int this_id;
|
||||
|
||||
CacheAddr stats;
|
||||
|
||||
int table_len;
|
||||
|
||||
int crash;
|
||||
|
||||
int experiment;
|
||||
|
||||
long create_time;
|
||||
|
||||
int[] pad = new int[52];
|
||||
|
||||
LruData lru;
|
||||
|
||||
public IndexHeader(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
magic = iis.readUInt32();
|
||||
version = iis.readUInt32();
|
||||
num_entries = iis.readInt32();
|
||||
num_bytes = iis.readInt32();
|
||||
last_file = iis.readInt32();
|
||||
this_id = iis.readInt32();
|
||||
stats = new CacheAddr(iis, rootDir, dataFiles, externalFilesDir);
|
||||
table_len = iis.readInt32();
|
||||
crash = iis.readInt32();
|
||||
experiment = iis.readInt32();
|
||||
create_time = iis.readUInt64();
|
||||
pad = new int[52];
|
||||
for (int i = 0; i < 52; i++) {
|
||||
pad[i] = iis.readInt32();
|
||||
}
|
||||
lru = new LruData(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
}
|
||||
|
||||
+83
-83
@@ -1,83 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class IndexInputStream extends InputStream {
|
||||
|
||||
private final InputStream is;
|
||||
|
||||
public long pos = 0;
|
||||
|
||||
public long getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public IndexInputStream(InputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int r = is.read();
|
||||
//System.out.print("$"+Integer.toHexString(r));
|
||||
pos++;
|
||||
return r;
|
||||
}
|
||||
|
||||
public long readUInt32() throws IOException {
|
||||
long r = (((long) read()) + ((long) (read() << 8)) + ((long) (read() << 16)) + ((long) (read() << 24))) & 0xffffffffL;
|
||||
//System.out.println("");
|
||||
return r;
|
||||
}
|
||||
|
||||
public long readUInt64() throws IOException {
|
||||
return readUInt32() + (readUInt32() << 32);
|
||||
}
|
||||
|
||||
public long readInt64() throws IOException {
|
||||
return readUInt64(); //FIXME
|
||||
}
|
||||
|
||||
public int readInt32() throws IOException {
|
||||
return (int) readUInt32();
|
||||
}
|
||||
|
||||
public int readInt16() throws IOException {
|
||||
return (short) ((int) read() + (int) (read() << 8));
|
||||
}
|
||||
|
||||
public long readInt() throws IOException {
|
||||
return readInt32();
|
||||
}
|
||||
|
||||
public String readString() throws IOException {
|
||||
int len = (int) readInt();
|
||||
byte data[] = new byte[len];
|
||||
if (read(data) != len) {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
return new String(data);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class IndexInputStream extends InputStream {
|
||||
|
||||
private final InputStream is;
|
||||
|
||||
public long pos = 0;
|
||||
|
||||
public long getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public IndexInputStream(InputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int r = is.read();
|
||||
//System.out.print("$"+Integer.toHexString(r));
|
||||
pos++;
|
||||
return r;
|
||||
}
|
||||
|
||||
public long readUInt32() throws IOException {
|
||||
long r = (((long) read()) + ((long) (read() << 8)) + ((long) (read() << 16)) + ((long) (read() << 24))) & 0xffffffffL;
|
||||
//System.out.println("");
|
||||
return r;
|
||||
}
|
||||
|
||||
public long readUInt64() throws IOException {
|
||||
return readUInt32() + (readUInt32() << 32);
|
||||
}
|
||||
|
||||
public long readInt64() throws IOException {
|
||||
return readUInt64(); //FIXME
|
||||
}
|
||||
|
||||
public int readInt32() throws IOException {
|
||||
return (int) readUInt32();
|
||||
}
|
||||
|
||||
public int readInt16() throws IOException {
|
||||
return (short) ((int) read() + (int) (read() << 8));
|
||||
}
|
||||
|
||||
public long readInt() throws IOException {
|
||||
return readInt32();
|
||||
}
|
||||
|
||||
public String readString() throws IOException {
|
||||
int len = (int) readInt();
|
||||
byte[] data = new byte[len];
|
||||
if (read(data) != len) {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
return new String(data);
|
||||
}
|
||||
}
|
||||
|
||||
+79
-79
@@ -1,79 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class LruData {
|
||||
|
||||
int pad1[] = new int[2];
|
||||
|
||||
int filled;
|
||||
|
||||
int sizes[] = new int[5];
|
||||
|
||||
CacheAddr heads[] = new CacheAddr[5];
|
||||
|
||||
CacheAddr tails[] = new CacheAddr[5];
|
||||
|
||||
CacheAddr transaction;
|
||||
|
||||
int operation;
|
||||
|
||||
int operation_list;
|
||||
|
||||
int pad2[] = new int[7];
|
||||
|
||||
public LruData(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
pad1 = new int[2];
|
||||
pad1[0] = iis.readInt32();
|
||||
pad1[1] = iis.readInt32();
|
||||
filled = iis.readInt32();
|
||||
sizes = new int[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sizes[i] = iis.readInt32();
|
||||
}
|
||||
sizes = new int[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sizes[i] = iis.readInt32();
|
||||
}
|
||||
heads = new CacheAddr[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
heads[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
tails = new CacheAddr[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
tails[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
transaction = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
operation = iis.readInt32();
|
||||
operation_list = iis.readInt32();
|
||||
pad2 = new int[7];
|
||||
for (int i = 0; i < 7; i++) {
|
||||
pad2[i] = iis.readInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.chrome;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class LruData {
|
||||
|
||||
int[] pad1 = new int[2];
|
||||
|
||||
int filled;
|
||||
|
||||
int[] sizes = new int[5];
|
||||
|
||||
CacheAddr[] heads = new CacheAddr[5];
|
||||
|
||||
CacheAddr[] tails = new CacheAddr[5];
|
||||
|
||||
CacheAddr transaction;
|
||||
|
||||
int operation;
|
||||
|
||||
int operation_list;
|
||||
|
||||
int[] pad2 = new int[7];
|
||||
|
||||
public LruData(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles, File externalFilesDir) throws IOException {
|
||||
IndexInputStream iis = new IndexInputStream(is);
|
||||
pad1 = new int[2];
|
||||
pad1[0] = iis.readInt32();
|
||||
pad1[1] = iis.readInt32();
|
||||
filled = iis.readInt32();
|
||||
sizes = new int[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sizes[i] = iis.readInt32();
|
||||
}
|
||||
sizes = new int[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sizes[i] = iis.readInt32();
|
||||
}
|
||||
heads = new CacheAddr[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
heads[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
tails = new CacheAddr[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
tails[i] = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
}
|
||||
transaction = new CacheAddr(is, rootDir, dataFiles, externalFilesDir);
|
||||
operation = iis.readInt32();
|
||||
operation_list = iis.readInt32();
|
||||
pad2 = new int[7];
|
||||
for (int i = 0; i < 7; i++) {
|
||||
pad2[i] = iis.readInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+167
-167
@@ -1,167 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.firefox;
|
||||
|
||||
import com.jpexs.browsers.cache.CacheEntry;
|
||||
import com.jpexs.browsers.cache.CacheImplementation;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class FirefoxCache implements CacheImplementation {
|
||||
|
||||
private static volatile FirefoxCache instance;
|
||||
|
||||
private FirefoxCache() {
|
||||
}
|
||||
|
||||
public static FirefoxCache getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (FirefoxCache.class) {
|
||||
if (instance == null) {
|
||||
instance = new FirefoxCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
private CacheMap map;
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
File dir = getCacheDirectory();
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
File cacheMapFile = new File(dir, "_CACHE_MAP_");
|
||||
try {
|
||||
map = new CacheMap(cacheMapFile);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(FirefoxCache.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CacheEntry> getEntries() {
|
||||
if (!loaded) {
|
||||
refresh();
|
||||
}
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
List<CacheEntry> ret = new ArrayList<>();
|
||||
|
||||
ret.addAll(map.mapBuckets);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private enum OSId {
|
||||
|
||||
WINDOWS, OSX, UNIX
|
||||
}
|
||||
|
||||
private static OSId getOSId() {
|
||||
PrivilegedAction<String> doGetOSName = new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
return System.getProperty("os.name");
|
||||
}
|
||||
};
|
||||
OSId id = OSId.UNIX;
|
||||
String osName = AccessController.doPrivileged(doGetOSName);
|
||||
if (osName != null) {
|
||||
if (osName.toLowerCase().startsWith("mac os x")) {
|
||||
id = OSId.OSX;
|
||||
} else if (osName.contains("Windows")) {
|
||||
id = OSId.WINDOWS;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public static File getProfileDirectory() {
|
||||
File profilesDir = getProfilesDirectory();
|
||||
if (profilesDir == null) {
|
||||
return null;
|
||||
}
|
||||
File profiles[] = profilesDir.listFiles();
|
||||
File profileDir = null;
|
||||
for (File f : profiles) {
|
||||
if (f.isDirectory()) {
|
||||
if (f.getName().matches("[a-z0-9]+\\.default")) {
|
||||
profileDir = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return profileDir;
|
||||
}
|
||||
|
||||
public static File getCacheDirectory() {
|
||||
File profileDir = getProfileDirectory();
|
||||
File cacheDir = null;
|
||||
if (profileDir != null) {
|
||||
cacheDir = new File(profileDir, "Cache");
|
||||
}
|
||||
if (cacheDir == null) {
|
||||
return null;
|
||||
}
|
||||
if (!cacheDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
public static File getProfilesDirectory() {
|
||||
String userHome = null;
|
||||
File profilesDir = null;
|
||||
try {
|
||||
userHome = System.getProperty("user.home");
|
||||
} catch (SecurityException ignore) {
|
||||
}
|
||||
if (userHome != null) {
|
||||
OSId osId = getOSId();
|
||||
if (osId == OSId.WINDOWS) {
|
||||
profilesDir = new File(userHome + "\\AppData\\Local\\Mozilla\\Firefox\\Profiles");
|
||||
if (!profilesDir.exists()) {
|
||||
profilesDir = new File(userHome + "\\Local Settings\\Application Data\\Mozilla\\Firefox\\Profiles");
|
||||
}
|
||||
} else if (osId == OSId.OSX) {
|
||||
profilesDir = new File(userHome + "/Library/Caches/Firefox/Profiles");
|
||||
} else {
|
||||
profilesDir = new File(userHome + "/.mozilla/firefox");
|
||||
}
|
||||
}
|
||||
if ((profilesDir == null) || !profilesDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
return profilesDir;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.firefox;
|
||||
|
||||
import com.jpexs.browsers.cache.CacheEntry;
|
||||
import com.jpexs.browsers.cache.CacheImplementation;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class FirefoxCache implements CacheImplementation {
|
||||
|
||||
private static volatile FirefoxCache instance;
|
||||
|
||||
private FirefoxCache() {
|
||||
}
|
||||
|
||||
public static FirefoxCache getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (FirefoxCache.class) {
|
||||
if (instance == null) {
|
||||
instance = new FirefoxCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
private CacheMap map;
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
File dir = getCacheDirectory();
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
File cacheMapFile = new File(dir, "_CACHE_MAP_");
|
||||
try {
|
||||
map = new CacheMap(cacheMapFile);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(FirefoxCache.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CacheEntry> getEntries() {
|
||||
if (!loaded) {
|
||||
refresh();
|
||||
}
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
List<CacheEntry> ret = new ArrayList<>();
|
||||
|
||||
ret.addAll(map.mapBuckets);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private enum OSId {
|
||||
|
||||
WINDOWS, OSX, UNIX
|
||||
}
|
||||
|
||||
private static OSId getOSId() {
|
||||
PrivilegedAction<String> doGetOSName = new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
return System.getProperty("os.name");
|
||||
}
|
||||
};
|
||||
OSId id = OSId.UNIX;
|
||||
String osName = AccessController.doPrivileged(doGetOSName);
|
||||
if (osName != null) {
|
||||
if (osName.toLowerCase().startsWith("mac os x")) {
|
||||
id = OSId.OSX;
|
||||
} else if (osName.contains("Windows")) {
|
||||
id = OSId.WINDOWS;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public static File getProfileDirectory() {
|
||||
File profilesDir = getProfilesDirectory();
|
||||
if (profilesDir == null) {
|
||||
return null;
|
||||
}
|
||||
File[] profiles = profilesDir.listFiles();
|
||||
File profileDir = null;
|
||||
for (File f : profiles) {
|
||||
if (f.isDirectory()) {
|
||||
if (f.getName().matches("[a-z0-9]+\\.default")) {
|
||||
profileDir = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return profileDir;
|
||||
}
|
||||
|
||||
public static File getCacheDirectory() {
|
||||
File profileDir = getProfileDirectory();
|
||||
File cacheDir = null;
|
||||
if (profileDir != null) {
|
||||
cacheDir = new File(profileDir, "Cache");
|
||||
}
|
||||
if (cacheDir == null) {
|
||||
return null;
|
||||
}
|
||||
if (!cacheDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
public static File getProfilesDirectory() {
|
||||
String userHome = null;
|
||||
File profilesDir = null;
|
||||
try {
|
||||
userHome = System.getProperty("user.home");
|
||||
} catch (SecurityException ignore) {
|
||||
}
|
||||
if (userHome != null) {
|
||||
OSId osId = getOSId();
|
||||
if (osId == OSId.WINDOWS) {
|
||||
profilesDir = new File(userHome + "\\AppData\\Local\\Mozilla\\Firefox\\Profiles");
|
||||
if (!profilesDir.exists()) {
|
||||
profilesDir = new File(userHome + "\\Local Settings\\Application Data\\Mozilla\\Firefox\\Profiles");
|
||||
}
|
||||
} else if (osId == OSId.OSX) {
|
||||
profilesDir = new File(userHome + "/Library/Caches/Firefox/Profiles");
|
||||
} else {
|
||||
profilesDir = new File(userHome + "/.mozilla/firefox");
|
||||
}
|
||||
}
|
||||
if ((profilesDir == null) || !profilesDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
return profilesDir;
|
||||
}
|
||||
}
|
||||
|
||||
+132
-132
@@ -1,132 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.firefox;
|
||||
|
||||
import com.jpexs.browsers.cache.CacheEntry;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class MapBucket extends CacheEntry {
|
||||
|
||||
public long hash;
|
||||
|
||||
public long enviction;
|
||||
|
||||
public Location dataLocation;
|
||||
|
||||
public Location metadataLocation;
|
||||
|
||||
private MetaData metadata;
|
||||
|
||||
public MapBucket(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles) throws IOException {
|
||||
CacheInputStream cis = new CacheInputStream(is);
|
||||
hash = cis.readInt32();
|
||||
enviction = cis.readInt32();
|
||||
dataLocation = new Location(cis.readInt32(), false, hash, rootDir, dataFiles);
|
||||
metadataLocation = new Location(cis.readInt32(), true, hash, rootDir, dataFiles);
|
||||
}
|
||||
|
||||
public InputStream getMetaDataStream() throws IOException {
|
||||
return metadataLocation.getInputStream();
|
||||
}
|
||||
|
||||
public MetaData getMetaData() {
|
||||
if (metadata == null) {
|
||||
try {
|
||||
metadata = new MetaData(getMetaDataStream());
|
||||
} catch (IncompatibleVersionException ie) {
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MapBucket.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestURL() {
|
||||
MetaData m = getMetaData();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
String req = m.request;
|
||||
if (req == null) {
|
||||
return null;
|
||||
}
|
||||
if (req.startsWith("HTTP:")) {
|
||||
req = req.substring("HTTP:".length());
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getResponseHeaders() {
|
||||
MetaData m = getMetaData();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
String responseHead = m.response.get("response-head");
|
||||
if (responseHead == null) {
|
||||
return null;
|
||||
}
|
||||
String headers[] = responseHead.split("\r\n");
|
||||
Map<String, String> ret = new HashMap<>();
|
||||
for (int h = 1; h < headers.length; h++) {
|
||||
String hs = headers[h];
|
||||
if (hs.contains(":")) {
|
||||
String hp[] = hs.split(":");
|
||||
ret.put(hp[0].trim(), hp[1].trim());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusLine() {
|
||||
MetaData m = getMetaData();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
String responseHead = m.response.get("response-head");
|
||||
String headers[] = responseHead.split("\r\n");
|
||||
return headers[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestMethod() {
|
||||
return "GET"; //No POST caching in Firefox
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResponseRawDataStream() {
|
||||
try {
|
||||
return dataLocation.getInputStream();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MapBucket.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.firefox;
|
||||
|
||||
import com.jpexs.browsers.cache.CacheEntry;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class MapBucket extends CacheEntry {
|
||||
|
||||
public long hash;
|
||||
|
||||
public long enviction;
|
||||
|
||||
public Location dataLocation;
|
||||
|
||||
public Location metadataLocation;
|
||||
|
||||
private MetaData metadata;
|
||||
|
||||
public MapBucket(InputStream is, File rootDir, Map<Integer, RandomAccessFile> dataFiles) throws IOException {
|
||||
CacheInputStream cis = new CacheInputStream(is);
|
||||
hash = cis.readInt32();
|
||||
enviction = cis.readInt32();
|
||||
dataLocation = new Location(cis.readInt32(), false, hash, rootDir, dataFiles);
|
||||
metadataLocation = new Location(cis.readInt32(), true, hash, rootDir, dataFiles);
|
||||
}
|
||||
|
||||
public InputStream getMetaDataStream() throws IOException {
|
||||
return metadataLocation.getInputStream();
|
||||
}
|
||||
|
||||
public MetaData getMetaData() {
|
||||
if (metadata == null) {
|
||||
try {
|
||||
metadata = new MetaData(getMetaDataStream());
|
||||
} catch (IncompatibleVersionException ie) {
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MapBucket.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestURL() {
|
||||
MetaData m = getMetaData();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
String req = m.request;
|
||||
if (req == null) {
|
||||
return null;
|
||||
}
|
||||
if (req.startsWith("HTTP:")) {
|
||||
req = req.substring("HTTP:".length());
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getResponseHeaders() {
|
||||
MetaData m = getMetaData();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
String responseHead = m.response.get("response-head");
|
||||
if (responseHead == null) {
|
||||
return null;
|
||||
}
|
||||
String[] headers = responseHead.split("\r\n");
|
||||
Map<String, String> ret = new HashMap<>();
|
||||
for (int h = 1; h < headers.length; h++) {
|
||||
String hs = headers[h];
|
||||
if (hs.contains(":")) {
|
||||
String[] hp = hs.split(":");
|
||||
ret.put(hp[0].trim(), hp[1].trim());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusLine() {
|
||||
MetaData m = getMetaData();
|
||||
if (m == null) {
|
||||
return null;
|
||||
}
|
||||
String responseHead = m.response.get("response-head");
|
||||
String[] headers = responseHead.split("\r\n");
|
||||
return headers[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestMethod() {
|
||||
return "GET"; //No POST caching in Firefox
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResponseRawDataStream() {
|
||||
try {
|
||||
return dataLocation.getInputStream();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MapBucket.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+93
-93
@@ -1,93 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.firefox;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class MetaData {
|
||||
|
||||
public int majorVersion;
|
||||
|
||||
public int minorVersion;
|
||||
|
||||
public long location;
|
||||
|
||||
public long fetchCount;
|
||||
|
||||
public long firstFetchTime;
|
||||
|
||||
public long lastFetchTime;
|
||||
|
||||
public long expireTime;
|
||||
|
||||
public long dataSize;
|
||||
|
||||
public long requestSize;
|
||||
|
||||
public long infoSize;
|
||||
|
||||
public String request;
|
||||
|
||||
public Map<String, String> response;
|
||||
|
||||
public MetaData(InputStream is) throws IOException, IncompatibleVersionException {
|
||||
CacheInputStream cis = new CacheInputStream(is);
|
||||
majorVersion = cis.readInt16();
|
||||
if (majorVersion != 1) {
|
||||
throw new IncompatibleVersionException(majorVersion);
|
||||
}
|
||||
minorVersion = cis.readInt16();
|
||||
location = cis.readInt32();
|
||||
fetchCount = cis.readInt32();
|
||||
firstFetchTime = cis.readInt32();
|
||||
lastFetchTime = cis.readInt32();
|
||||
expireTime = cis.readInt32();
|
||||
dataSize = cis.readInt32();
|
||||
requestSize = cis.readInt32();
|
||||
infoSize = cis.readInt32();
|
||||
byte req[] = new byte[(int) requestSize];
|
||||
if (cis.read(req) != req.length) {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
request = new String(req, 0, (int) requestSize - 1/*Ends with char 0*/);
|
||||
byte res[] = new byte[(int) infoSize];
|
||||
cis.read(res);
|
||||
String responseStr = new String(res);
|
||||
int nulpos;
|
||||
boolean inKey = true;
|
||||
String key = null;
|
||||
response = new HashMap<>();
|
||||
while ((nulpos = responseStr.indexOf(0)) > 0) {
|
||||
String v = responseStr.substring(0, nulpos);
|
||||
responseStr = responseStr.substring(nulpos + 1);
|
||||
if (inKey) {
|
||||
key = v;
|
||||
} else {
|
||||
response.put(key, v);
|
||||
}
|
||||
inKey = !inKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.browsers.cache.firefox;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class MetaData {
|
||||
|
||||
public int majorVersion;
|
||||
|
||||
public int minorVersion;
|
||||
|
||||
public long location;
|
||||
|
||||
public long fetchCount;
|
||||
|
||||
public long firstFetchTime;
|
||||
|
||||
public long lastFetchTime;
|
||||
|
||||
public long expireTime;
|
||||
|
||||
public long dataSize;
|
||||
|
||||
public long requestSize;
|
||||
|
||||
public long infoSize;
|
||||
|
||||
public String request;
|
||||
|
||||
public Map<String, String> response;
|
||||
|
||||
public MetaData(InputStream is) throws IOException, IncompatibleVersionException {
|
||||
CacheInputStream cis = new CacheInputStream(is);
|
||||
majorVersion = cis.readInt16();
|
||||
if (majorVersion != 1) {
|
||||
throw new IncompatibleVersionException(majorVersion);
|
||||
}
|
||||
minorVersion = cis.readInt16();
|
||||
location = cis.readInt32();
|
||||
fetchCount = cis.readInt32();
|
||||
firstFetchTime = cis.readInt32();
|
||||
lastFetchTime = cis.readInt32();
|
||||
expireTime = cis.readInt32();
|
||||
dataSize = cis.readInt32();
|
||||
requestSize = cis.readInt32();
|
||||
infoSize = cis.readInt32();
|
||||
byte[] req = new byte[(int) requestSize];
|
||||
if (cis.read(req) != req.length) {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
request = new String(req, 0, (int) requestSize - 1/*Ends with char 0*/);
|
||||
byte[] res = new byte[(int) infoSize];
|
||||
cis.read(res);
|
||||
String responseStr = new String(res);
|
||||
int nulpos;
|
||||
boolean inKey = true;
|
||||
String key = null;
|
||||
response = new HashMap<>();
|
||||
while ((nulpos = responseStr.indexOf(0)) > 0) {
|
||||
String v = responseStr.substring(0, nulpos);
|
||||
responseStr = responseStr.substring(nulpos + 1);
|
||||
if (inKey) {
|
||||
key = v;
|
||||
} else {
|
||||
response.put(key, v);
|
||||
}
|
||||
inKey = !inKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1532,7 +1532,7 @@ public class CommandLineArgumentParser {
|
||||
Integer min = null;
|
||||
Integer max = null;
|
||||
if (r.contains("-")) {
|
||||
String ps[] = r.split("\\-");
|
||||
String[] ps = r.split("\\-");
|
||||
if (ps.length != 2) {
|
||||
System.err.println("invalid range");
|
||||
badArguments("select");
|
||||
|
||||
@@ -1,235 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.console;
|
||||
|
||||
import com.jpexs.helpers.utf8.Utf8Helper;
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.WString;
|
||||
import com.sun.jna.platform.win32.Advapi32Util;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.SHELLEXECUTEINFO;
|
||||
import com.sun.jna.platform.win32.Shell32;
|
||||
import com.sun.jna.platform.win32.Win32Exception;
|
||||
import com.sun.jna.platform.win32.WinReg;
|
||||
import com.sun.jna.platform.win32.WinUser;
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ContextMenuTools {
|
||||
|
||||
public static String getAppDir() {
|
||||
String path = Utf8Helper.urlDecode(ContextMenuTools.class.getProtectionDomain().getCodeSource().getLocation().getPath());
|
||||
String appDir = new File(path).getParentFile().getAbsolutePath();
|
||||
if (!appDir.endsWith("\\")) {
|
||||
appDir += "\\";
|
||||
}
|
||||
return appDir;
|
||||
}
|
||||
|
||||
public static boolean isAddedToContextMenu() {
|
||||
if (!Platform.isWindows()) {
|
||||
return false;
|
||||
}
|
||||
final WinReg.HKEY REG_CLASSES_HKEY = WinReg.HKEY_LOCAL_MACHINE;
|
||||
final String REG_CLASSES_PATH = "Software\\Classes\\";
|
||||
try {
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + ".swf")) {
|
||||
return false;
|
||||
}
|
||||
String clsName = Advapi32Util.registryGetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + ".swf", "");
|
||||
if (clsName == null) {
|
||||
return false;
|
||||
}
|
||||
return Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\ffdec");
|
||||
} catch (Win32Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean addToContextMenu(boolean add, boolean fromCommandLine) {
|
||||
if (add == isAddedToContextMenu()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String exeName = "ffdec.exe";
|
||||
|
||||
if (add) {
|
||||
return addToContextMenu(add, fromCommandLine, exeName);
|
||||
} else {
|
||||
// remove both 32 and 64 bit references
|
||||
return addToContextMenu(add, fromCommandLine, exeName)
|
||||
&& addToContextMenu(add, fromCommandLine, "ffdec64.exe"); //remove 64 exe from previous versions
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean addToContextMenu(boolean add, boolean fromCommandLine, String exeName) {
|
||||
final String extensions[] = new String[]{"swf", "gfx"};
|
||||
|
||||
final WinReg.HKEY REG_CLASSES_HKEY = WinReg.HKEY_LOCAL_MACHINE;
|
||||
final String REG_CLASSES_PATH = "Software\\Classes\\";
|
||||
|
||||
String appDir = getAppDir();
|
||||
String verb = "ffdec";
|
||||
String verbName = "Open with FFDec";
|
||||
boolean exists;
|
||||
try {
|
||||
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
if ((!exists) && add) { //add
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell");
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open", "", verbName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open\\command");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open\\command", "", "\"" + appDir + exeName + "\" \"%1\"");
|
||||
|
||||
}
|
||||
|
||||
for (String ext : extensions) {
|
||||
|
||||
// 1) Add to context menu of SWF
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext)) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext, "", "ShockwaveFlash.ShockwaveFlash");
|
||||
}
|
||||
|
||||
if (Advapi32Util.registryValueExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext, "")) {
|
||||
String clsName = Advapi32Util.registryGetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext, "");
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName)) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName, "", "Flash Movie");
|
||||
}
|
||||
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell")) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell");
|
||||
}
|
||||
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb);
|
||||
|
||||
if ((!exists) && add) { //add
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb, "", verbName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb + "\\command");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb + "\\command", "", "\"" + appDir + exeName + "\" \"%1\"");
|
||||
}
|
||||
if (exists && (!add)) { //remove
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb + "\\command");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb);
|
||||
}
|
||||
}
|
||||
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
|
||||
if (exists && (!add)) { //remove
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open\\command");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
}
|
||||
//2) Add to OpenWith list
|
||||
if (Advapi32Util.registryValueExists(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList")) {
|
||||
String mruList = Advapi32Util.registryGetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList");
|
||||
if (mruList != null) {
|
||||
exists = false;
|
||||
char appChar = 0;
|
||||
for (int i = 0; i < mruList.length(); i++) {
|
||||
String app = Advapi32Util.registryGetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "" + mruList.charAt(i));
|
||||
if (app.equals(exeName)) {
|
||||
appChar = mruList.charAt(i);
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((!exists) && add) { //add
|
||||
for (int c = 'a'; c <= 'z'; c++) {
|
||||
if (mruList.indexOf(c) == -1) {
|
||||
mruList += (char) c;
|
||||
Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "" + (char) c, exeName);
|
||||
Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList", mruList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exists && (!add)) { //remove
|
||||
mruList = mruList.replace("" + appChar, "");
|
||||
Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList", mruList);
|
||||
registryDeleteValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "" + appChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//On some systems, file must be associated in SystemFileAssociations too
|
||||
if (Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations")) {
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb);
|
||||
if ((!exists) && add) { //add
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "")) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "");
|
||||
}
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell")) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell");
|
||||
}
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb, "", verbName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb + "\\Command");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb + "\\Command", "", "\"" + appDir + exeName + "\" \"%1\"");
|
||||
}
|
||||
if (exists && (!add)) { //remove
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb + "\\Command");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
if (!fromCommandLine) {
|
||||
//Updating registry failed, try elevating rights
|
||||
SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO();
|
||||
sei.fMask = 0x00000040;
|
||||
sei.lpVerb = new WString("runas");
|
||||
sei.lpFile = new WString(appDir + exeName);
|
||||
sei.lpParameters = new WString(add ? "-addtocontextmenu" : "-removefromcontextmenu");
|
||||
sei.nShow = WinUser.SW_NORMAL;
|
||||
Shell32.INSTANCE.ShellExecuteEx(sei);
|
||||
//Wait till exit
|
||||
Kernel32.INSTANCE.WaitForSingleObject(sei.hProcess, 1000 * 60 * 60 * 24 /*1 day max*/);
|
||||
Kernel32.INSTANCE.CloseHandle(sei.hProcess);
|
||||
} else {
|
||||
Logger.getLogger(ContextMenuTools.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void registryDeleteKey(WinReg.HKEY hKey, String keyName) {
|
||||
boolean exists = Advapi32Util.registryKeyExists(hKey, keyName);
|
||||
if (exists) {
|
||||
Advapi32Util.registryDeleteKey(hKey, keyName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registryDeleteValue(WinReg.HKEY root, String keyPath, String valueName) {
|
||||
boolean exists = Advapi32Util.registryValueExists(root, keyPath, valueName);
|
||||
if (exists) {
|
||||
Advapi32Util.registryDeleteValue(root, keyPath, valueName);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.console;
|
||||
|
||||
import com.jpexs.helpers.utf8.Utf8Helper;
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.WString;
|
||||
import com.sun.jna.platform.win32.Advapi32Util;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.SHELLEXECUTEINFO;
|
||||
import com.sun.jna.platform.win32.Shell32;
|
||||
import com.sun.jna.platform.win32.Win32Exception;
|
||||
import com.sun.jna.platform.win32.WinReg;
|
||||
import com.sun.jna.platform.win32.WinUser;
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ContextMenuTools {
|
||||
|
||||
public static String getAppDir() {
|
||||
String path = Utf8Helper.urlDecode(ContextMenuTools.class.getProtectionDomain().getCodeSource().getLocation().getPath());
|
||||
String appDir = new File(path).getParentFile().getAbsolutePath();
|
||||
if (!appDir.endsWith("\\")) {
|
||||
appDir += "\\";
|
||||
}
|
||||
return appDir;
|
||||
}
|
||||
|
||||
public static boolean isAddedToContextMenu() {
|
||||
if (!Platform.isWindows()) {
|
||||
return false;
|
||||
}
|
||||
final WinReg.HKEY REG_CLASSES_HKEY = WinReg.HKEY_LOCAL_MACHINE;
|
||||
final String REG_CLASSES_PATH = "Software\\Classes\\";
|
||||
try {
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + ".swf")) {
|
||||
return false;
|
||||
}
|
||||
String clsName = Advapi32Util.registryGetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + ".swf", "");
|
||||
if (clsName == null) {
|
||||
return false;
|
||||
}
|
||||
return Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\ffdec");
|
||||
} catch (Win32Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean addToContextMenu(boolean add, boolean fromCommandLine) {
|
||||
if (add == isAddedToContextMenu()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String exeName = "ffdec.exe";
|
||||
|
||||
if (add) {
|
||||
return addToContextMenu(add, fromCommandLine, exeName);
|
||||
} else {
|
||||
// remove both 32 and 64 bit references
|
||||
return addToContextMenu(add, fromCommandLine, exeName)
|
||||
&& addToContextMenu(add, fromCommandLine, "ffdec64.exe"); //remove 64 exe from previous versions
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean addToContextMenu(boolean add, boolean fromCommandLine, String exeName) {
|
||||
final String[] extensions = new String[]{"swf", "gfx"};
|
||||
|
||||
final WinReg.HKEY REG_CLASSES_HKEY = WinReg.HKEY_LOCAL_MACHINE;
|
||||
final String REG_CLASSES_PATH = "Software\\Classes\\";
|
||||
|
||||
String appDir = getAppDir();
|
||||
String verb = "ffdec";
|
||||
String verbName = "Open with FFDec";
|
||||
boolean exists;
|
||||
try {
|
||||
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
if ((!exists) && add) { //add
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell");
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open", "", verbName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open\\command");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open\\command", "", "\"" + appDir + exeName + "\" \"%1\"");
|
||||
|
||||
}
|
||||
|
||||
for (String ext : extensions) {
|
||||
|
||||
// 1) Add to context menu of SWF
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext)) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext, "", "ShockwaveFlash.ShockwaveFlash");
|
||||
}
|
||||
|
||||
if (Advapi32Util.registryValueExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext, "")) {
|
||||
String clsName = Advapi32Util.registryGetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "." + ext, "");
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName)) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName, "", "Flash Movie");
|
||||
}
|
||||
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell")) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell");
|
||||
}
|
||||
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb);
|
||||
|
||||
if ((!exists) && add) { //add
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb, "", verbName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb + "\\command");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb + "\\command", "", "\"" + appDir + exeName + "\" \"%1\"");
|
||||
}
|
||||
if (exists && (!add)) { //remove
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb + "\\command");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + clsName + "\\shell\\" + verb);
|
||||
}
|
||||
}
|
||||
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
|
||||
if (exists && (!add)) { //remove
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open\\command");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell\\open");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName + "\\shell");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "Applications\\" + exeName);
|
||||
}
|
||||
//2) Add to OpenWith list
|
||||
if (Advapi32Util.registryValueExists(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList")) {
|
||||
String mruList = Advapi32Util.registryGetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList");
|
||||
if (mruList != null) {
|
||||
exists = false;
|
||||
char appChar = 0;
|
||||
for (int i = 0; i < mruList.length(); i++) {
|
||||
String app = Advapi32Util.registryGetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "" + mruList.charAt(i));
|
||||
if (app.equals(exeName)) {
|
||||
appChar = mruList.charAt(i);
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((!exists) && add) { //add
|
||||
for (int c = 'a'; c <= 'z'; c++) {
|
||||
if (mruList.indexOf(c) == -1) {
|
||||
mruList += (char) c;
|
||||
Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "" + (char) c, exeName);
|
||||
Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList", mruList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exists && (!add)) { //remove
|
||||
mruList = mruList.replace("" + appChar, "");
|
||||
Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "MRUList", mruList);
|
||||
registryDeleteValue(WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + ext + "\\OpenWithList", "" + appChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//On some systems, file must be associated in SystemFileAssociations too
|
||||
if (Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations")) {
|
||||
exists = Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb);
|
||||
if ((!exists) && add) { //add
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "")) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "");
|
||||
}
|
||||
if (!Advapi32Util.registryKeyExists(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell")) {
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell");
|
||||
}
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb);
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb, "", verbName);
|
||||
Advapi32Util.registryCreateKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb + "\\Command");
|
||||
Advapi32Util.registrySetStringValue(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb + "\\Command", "", "\"" + appDir + exeName + "\" \"%1\"");
|
||||
}
|
||||
if (exists && (!add)) { //remove
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb + "\\Command");
|
||||
registryDeleteKey(REG_CLASSES_HKEY, REG_CLASSES_PATH + "SystemFileAssociations\\." + ext + "\\Shell\\" + verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
if (!fromCommandLine) {
|
||||
//Updating registry failed, try elevating rights
|
||||
SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO();
|
||||
sei.fMask = 0x00000040;
|
||||
sei.lpVerb = new WString("runas");
|
||||
sei.lpFile = new WString(appDir + exeName);
|
||||
sei.lpParameters = new WString(add ? "-addtocontextmenu" : "-removefromcontextmenu");
|
||||
sei.nShow = WinUser.SW_NORMAL;
|
||||
Shell32.INSTANCE.ShellExecuteEx(sei);
|
||||
//Wait till exit
|
||||
Kernel32.INSTANCE.WaitForSingleObject(sei.hProcess, 1000 * 60 * 60 * 24 /*1 day max*/);
|
||||
Kernel32.INSTANCE.CloseHandle(sei.hProcess);
|
||||
} else {
|
||||
Logger.getLogger(ContextMenuTools.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void registryDeleteKey(WinReg.HKEY hKey, String keyName) {
|
||||
boolean exists = Advapi32Util.registryKeyExists(hKey, keyName);
|
||||
if (exists) {
|
||||
Advapi32Util.registryDeleteKey(hKey, keyName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registryDeleteValue(WinReg.HKEY root, String keyPath, String valueName) {
|
||||
boolean exists = Advapi32Util.registryValueExists(root, keyPath, valueName);
|
||||
if (exists) {
|
||||
Advapi32Util.registryDeleteValue(root, keyPath, valueName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ public class AdvancedSettingsDialog extends AppDialog {
|
||||
Map<String, Component> tabs = new HashMap<>();
|
||||
getCategories(componentsMap, tabs, skinComboBox, getResourceBundle());
|
||||
|
||||
String catOrder[] = new String[]{"ui", "display", "decompilation", "script", "format", "export", "import", "paths", "limit", "update", "debug", "other"};
|
||||
String[] catOrder = new String[]{"ui", "display", "decompilation", "script", "format", "export", "import", "paths", "limit", "update", "debug", "other"};
|
||||
|
||||
for (String cat : catOrder) {
|
||||
if (!tabs.containsKey(cat)) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,366 +1,366 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.TextExportMode;
|
||||
import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.FontTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ImageTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.SoundTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.TextTag;
|
||||
import com.jpexs.decompiler.flash.timeline.Frame;
|
||||
import com.jpexs.decompiler.flash.treeitems.TreeItem;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ExportDialog extends AppDialog {
|
||||
|
||||
private int result = ERROR_OPTION;
|
||||
|
||||
String[] optionNames = {
|
||||
TagTreeModel.FOLDER_SHAPES,
|
||||
TagTreeModel.FOLDER_TEXTS,
|
||||
TagTreeModel.FOLDER_IMAGES,
|
||||
TagTreeModel.FOLDER_MOVIES,
|
||||
TagTreeModel.FOLDER_SOUNDS,
|
||||
TagTreeModel.FOLDER_SCRIPTS,
|
||||
TagTreeModel.FOLDER_BINARY_DATA,
|
||||
TagTreeModel.FOLDER_FRAMES,
|
||||
TagTreeModel.FOLDER_SPRITES,
|
||||
TagTreeModel.FOLDER_BUTTONS,
|
||||
TagTreeModel.FOLDER_FONTS,
|
||||
TagTreeModel.FOLDER_MORPHSHAPES,
|
||||
"symbolclass"
|
||||
};
|
||||
|
||||
//Display options only when these classes found
|
||||
Class[][] objClasses = {
|
||||
{ShapeTag.class},
|
||||
{TextTag.class},
|
||||
{ImageTag.class},
|
||||
{DefineVideoStreamTag.class},
|
||||
{SoundTag.class},
|
||||
{ASMSource.class, ScriptPack.class},
|
||||
{DefineBinaryDataTag.class},
|
||||
{Frame.class},
|
||||
{Frame.class},
|
||||
{ButtonTag.class},
|
||||
{FontTag.class},
|
||||
{MorphShapeTag.class},
|
||||
{SymbolClassTypeTag.class}
|
||||
};
|
||||
|
||||
//Enum classes for values
|
||||
Class[] optionClasses = {
|
||||
ShapeExportMode.class,
|
||||
TextExportMode.class,
|
||||
ImageExportMode.class,
|
||||
MovieExportMode.class,
|
||||
SoundExportMode.class,
|
||||
ScriptExportMode.class,
|
||||
BinaryDataExportMode.class,
|
||||
FrameExportMode.class,
|
||||
SpriteExportMode.class,
|
||||
ButtonExportMode.class,
|
||||
FontExportMode.class,
|
||||
MorphShapeExportMode.class,
|
||||
SymbolClassExportMode.class
|
||||
};
|
||||
|
||||
Class[] zoomClasses = {
|
||||
ShapeExportMode.class,
|
||||
TextExportMode.class,
|
||||
FrameExportMode.class,
|
||||
MorphShapeExportMode.class
|
||||
};
|
||||
|
||||
private final JComboBox[] combos;
|
||||
|
||||
private final JCheckBox[] checkBoxes;
|
||||
|
||||
private final JCheckBox selectAllCheckBox;
|
||||
|
||||
private JTextField zoomTextField = new JTextField();
|
||||
|
||||
public <E> E getValue(Class<E> option) {
|
||||
for (int i = 0; i < optionClasses.length; i++) {
|
||||
if (option == optionClasses[i]) {
|
||||
E values[] = option.getEnumConstants();
|
||||
return values[combos[i].getSelectedIndex()];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isOptionEnabled(Class<?> option) {
|
||||
for (int i = 0; i < optionClasses.length; i++) {
|
||||
if (option == optionClasses[i]) {
|
||||
return checkBoxes[i].isSelected();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public double getZoom() {
|
||||
return Double.parseDouble(zoomTextField.getText()) / 100;
|
||||
}
|
||||
|
||||
private void saveConfig() {
|
||||
StringBuilder cfg = new StringBuilder();
|
||||
for (int i = 0; i < optionNames.length; i++) {
|
||||
int selIndex = combos[i].getSelectedIndex();
|
||||
Class c = optionClasses[i];
|
||||
Object vals[] = c.getEnumConstants();
|
||||
String key = optionNames[i] + "." + vals[selIndex].toString().toLowerCase();
|
||||
if (i > 0) {
|
||||
cfg.append(",");
|
||||
}
|
||||
cfg.append(key);
|
||||
}
|
||||
|
||||
Configuration.lastSelectedExportZoom.set(Double.parseDouble(zoomTextField.getText()) / 100);
|
||||
Configuration.lastSelectedExportFormats.set(cfg.toString());
|
||||
}
|
||||
|
||||
public ExportDialog(List<TreeItem> exportables) {
|
||||
setTitle(translate("dialog.title"));
|
||||
setResizable(false);
|
||||
|
||||
Container cnt = getContentPane();
|
||||
cnt.setLayout(new BorderLayout());
|
||||
JPanel comboPanel = new JPanel(null);
|
||||
int labWidth = 0;
|
||||
boolean[] exportableExistsArray = new boolean[optionNames.length];
|
||||
for (int i = 0; i < optionNames.length; i++) {
|
||||
boolean exportableExists = false;
|
||||
if (exportables == null) {
|
||||
exportableExists = true;
|
||||
} else {
|
||||
for (TreeItem e : exportables) {
|
||||
for (int j = 0; j < objClasses[i].length; j++) {
|
||||
if (objClasses[i][j].isInstance(e)) {
|
||||
exportableExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!exportableExists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
exportableExistsArray[i] = true;
|
||||
|
||||
JLabel label = new JLabel(translate(optionNames[i]));
|
||||
if (label.getPreferredSize().width > labWidth) {
|
||||
labWidth = label.getPreferredSize().width;
|
||||
}
|
||||
}
|
||||
|
||||
String exportFormatsStr = Configuration.lastSelectedExportFormats.get();
|
||||
if ("".equals(exportFormatsStr)) {
|
||||
exportFormatsStr = null;
|
||||
}
|
||||
|
||||
String exportFormatsArr[] = new String[0];
|
||||
if (exportFormatsStr != null) {
|
||||
if (exportFormatsStr.contains(",")) {
|
||||
exportFormatsArr = exportFormatsStr.split(",");
|
||||
} else {
|
||||
exportFormatsArr = new String[]{exportFormatsStr};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int comboWidth = 200;
|
||||
int checkBoxWidth;
|
||||
int top = 10;
|
||||
|
||||
List<String> exportFormats = Arrays.asList(exportFormatsArr);
|
||||
combos = new JComboBox[optionNames.length];
|
||||
checkBoxes = new JCheckBox[optionNames.length];
|
||||
selectAllCheckBox = new JCheckBox();
|
||||
checkBoxWidth = selectAllCheckBox.getPreferredSize().width;
|
||||
selectAllCheckBox.setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, selectAllCheckBox.getPreferredSize().height);
|
||||
selectAllCheckBox.setSelected(true);
|
||||
selectAllCheckBox.addActionListener((ActionEvent e) -> {
|
||||
boolean selected = selectAllCheckBox.isSelected();
|
||||
for (JCheckBox checkBox : checkBoxes) {
|
||||
if (checkBox != null) {
|
||||
checkBox.setSelected(selected);
|
||||
}
|
||||
}
|
||||
});
|
||||
comboPanel.add(selectAllCheckBox);
|
||||
top += selectAllCheckBox.getHeight();
|
||||
|
||||
boolean zoomable = false;
|
||||
for (int i = 0; i < optionNames.length; i++) {
|
||||
Class c = optionClasses[i];
|
||||
Object vals[] = c.getEnumConstants();
|
||||
String names[] = new String[vals.length];
|
||||
int itemIndex = -1;
|
||||
for (int j = 0; j < vals.length; j++) {
|
||||
|
||||
String key = optionNames[i] + "." + vals[j].toString().toLowerCase();
|
||||
if (exportFormats.contains(key)) {
|
||||
itemIndex = j;
|
||||
}
|
||||
names[j] = translate(key);
|
||||
}
|
||||
|
||||
combos[i] = new JComboBox<>(names);
|
||||
if (itemIndex > -1) {
|
||||
combos[i].setSelectedIndex(itemIndex);
|
||||
}
|
||||
|
||||
combos[i].setBounds(10 + labWidth + 10, top, comboWidth, combos[i].getPreferredSize().height);
|
||||
|
||||
checkBoxes[i] = new JCheckBox();
|
||||
checkBoxes[i].setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, checkBoxes[i].getPreferredSize().height);
|
||||
checkBoxes[i].setSelected(true);
|
||||
|
||||
if (!exportableExistsArray[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Arrays.asList(zoomClasses).contains(c)) {
|
||||
zoomable = true;
|
||||
}
|
||||
|
||||
JLabel lab = new JLabel(translate(optionNames[i]));
|
||||
lab.setBounds(10, top, lab.getPreferredSize().width, lab.getPreferredSize().height);
|
||||
comboPanel.add(lab);
|
||||
comboPanel.add(checkBoxes[i]);
|
||||
comboPanel.add(combos[i]);
|
||||
top += combos[i].getHeight();
|
||||
}
|
||||
|
||||
int zoomWidth = 50;
|
||||
if (zoomable) {
|
||||
top += 2;
|
||||
JLabel zlab = new JLabel(translate("zoom"));
|
||||
zlab.setBounds(10, top + 4, zlab.getPreferredSize().width, zlab.getPreferredSize().height);
|
||||
zoomTextField.setBounds(10 + labWidth + 10, top, zoomWidth, zoomTextField.getPreferredSize().height);
|
||||
JLabel pctLabel = new JLabel(translate("zoom.percent"));
|
||||
pctLabel.setBounds(10 + labWidth + 10 + zoomWidth + 5, top + 4, 20, pctLabel.getPreferredSize().height);
|
||||
|
||||
comboPanel.add(zlab);
|
||||
comboPanel.add(zoomTextField);
|
||||
comboPanel.add(pctLabel);
|
||||
top += zoomTextField.getHeight();
|
||||
}
|
||||
|
||||
Dimension dim = new Dimension(10 + labWidth + 10 + checkBoxWidth + 10 + comboWidth + 10, top + 10);
|
||||
comboPanel.setMinimumSize(dim);
|
||||
comboPanel.setPreferredSize(dim);
|
||||
cnt.add(comboPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel buttonsPanel = new JPanel(new FlowLayout());
|
||||
JButton okButton = new JButton(translate("button.ok"));
|
||||
okButton.addActionListener(this::okButtonActionPerformed);
|
||||
|
||||
JButton cancelButton = new JButton(translate("button.cancel"));
|
||||
cancelButton.addActionListener(this::cancelButtonActionPerformed);
|
||||
|
||||
buttonsPanel.add(okButton);
|
||||
buttonsPanel.add(cancelButton);
|
||||
|
||||
cnt.add(buttonsPanel, BorderLayout.SOUTH);
|
||||
pack();
|
||||
View.centerScreen(this);
|
||||
View.setWindowIcon(this);
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
setModal(true);
|
||||
String pct = "" + Configuration.lastSelectedExportZoom.get() * 100;
|
||||
if (pct.endsWith(".0")) {
|
||||
pct = pct.substring(0, pct.length() - 2);
|
||||
}
|
||||
|
||||
zoomTextField.setText(pct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
if (b) {
|
||||
result = ERROR_OPTION;
|
||||
}
|
||||
super.setVisible(b);
|
||||
}
|
||||
|
||||
private void okButtonActionPerformed(ActionEvent evt) {
|
||||
result = OK_OPTION;
|
||||
try {
|
||||
saveConfig();
|
||||
} catch (NumberFormatException nfe) {
|
||||
JOptionPane.showMessageDialog(ExportDialog.this, translate("zoom.invalid"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
zoomTextField.requestFocusInWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void cancelButtonActionPerformed(ActionEvent evt) {
|
||||
result = CANCEL_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
public int showExportDialog() {
|
||||
setVisible(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.TextExportMode;
|
||||
import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.FontTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ImageTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.SoundTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.TextTag;
|
||||
import com.jpexs.decompiler.flash.timeline.Frame;
|
||||
import com.jpexs.decompiler.flash.treeitems.TreeItem;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ExportDialog extends AppDialog {
|
||||
|
||||
private int result = ERROR_OPTION;
|
||||
|
||||
String[] optionNames = {
|
||||
TagTreeModel.FOLDER_SHAPES,
|
||||
TagTreeModel.FOLDER_TEXTS,
|
||||
TagTreeModel.FOLDER_IMAGES,
|
||||
TagTreeModel.FOLDER_MOVIES,
|
||||
TagTreeModel.FOLDER_SOUNDS,
|
||||
TagTreeModel.FOLDER_SCRIPTS,
|
||||
TagTreeModel.FOLDER_BINARY_DATA,
|
||||
TagTreeModel.FOLDER_FRAMES,
|
||||
TagTreeModel.FOLDER_SPRITES,
|
||||
TagTreeModel.FOLDER_BUTTONS,
|
||||
TagTreeModel.FOLDER_FONTS,
|
||||
TagTreeModel.FOLDER_MORPHSHAPES,
|
||||
"symbolclass"
|
||||
};
|
||||
|
||||
//Display options only when these classes found
|
||||
Class[][] objClasses = {
|
||||
{ShapeTag.class},
|
||||
{TextTag.class},
|
||||
{ImageTag.class},
|
||||
{DefineVideoStreamTag.class},
|
||||
{SoundTag.class},
|
||||
{ASMSource.class, ScriptPack.class},
|
||||
{DefineBinaryDataTag.class},
|
||||
{Frame.class},
|
||||
{Frame.class},
|
||||
{ButtonTag.class},
|
||||
{FontTag.class},
|
||||
{MorphShapeTag.class},
|
||||
{SymbolClassTypeTag.class}
|
||||
};
|
||||
|
||||
//Enum classes for values
|
||||
Class[] optionClasses = {
|
||||
ShapeExportMode.class,
|
||||
TextExportMode.class,
|
||||
ImageExportMode.class,
|
||||
MovieExportMode.class,
|
||||
SoundExportMode.class,
|
||||
ScriptExportMode.class,
|
||||
BinaryDataExportMode.class,
|
||||
FrameExportMode.class,
|
||||
SpriteExportMode.class,
|
||||
ButtonExportMode.class,
|
||||
FontExportMode.class,
|
||||
MorphShapeExportMode.class,
|
||||
SymbolClassExportMode.class
|
||||
};
|
||||
|
||||
Class[] zoomClasses = {
|
||||
ShapeExportMode.class,
|
||||
TextExportMode.class,
|
||||
FrameExportMode.class,
|
||||
MorphShapeExportMode.class
|
||||
};
|
||||
|
||||
private final JComboBox[] combos;
|
||||
|
||||
private final JCheckBox[] checkBoxes;
|
||||
|
||||
private final JCheckBox selectAllCheckBox;
|
||||
|
||||
private JTextField zoomTextField = new JTextField();
|
||||
|
||||
public <E> E getValue(Class<E> option) {
|
||||
for (int i = 0; i < optionClasses.length; i++) {
|
||||
if (option == optionClasses[i]) {
|
||||
E[] values = option.getEnumConstants();
|
||||
return values[combos[i].getSelectedIndex()];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isOptionEnabled(Class<?> option) {
|
||||
for (int i = 0; i < optionClasses.length; i++) {
|
||||
if (option == optionClasses[i]) {
|
||||
return checkBoxes[i].isSelected();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public double getZoom() {
|
||||
return Double.parseDouble(zoomTextField.getText()) / 100;
|
||||
}
|
||||
|
||||
private void saveConfig() {
|
||||
StringBuilder cfg = new StringBuilder();
|
||||
for (int i = 0; i < optionNames.length; i++) {
|
||||
int selIndex = combos[i].getSelectedIndex();
|
||||
Class c = optionClasses[i];
|
||||
Object[] vals = c.getEnumConstants();
|
||||
String key = optionNames[i] + "." + vals[selIndex].toString().toLowerCase();
|
||||
if (i > 0) {
|
||||
cfg.append(",");
|
||||
}
|
||||
cfg.append(key);
|
||||
}
|
||||
|
||||
Configuration.lastSelectedExportZoom.set(Double.parseDouble(zoomTextField.getText()) / 100);
|
||||
Configuration.lastSelectedExportFormats.set(cfg.toString());
|
||||
}
|
||||
|
||||
public ExportDialog(List<TreeItem> exportables) {
|
||||
setTitle(translate("dialog.title"));
|
||||
setResizable(false);
|
||||
|
||||
Container cnt = getContentPane();
|
||||
cnt.setLayout(new BorderLayout());
|
||||
JPanel comboPanel = new JPanel(null);
|
||||
int labWidth = 0;
|
||||
boolean[] exportableExistsArray = new boolean[optionNames.length];
|
||||
for (int i = 0; i < optionNames.length; i++) {
|
||||
boolean exportableExists = false;
|
||||
if (exportables == null) {
|
||||
exportableExists = true;
|
||||
} else {
|
||||
for (TreeItem e : exportables) {
|
||||
for (int j = 0; j < objClasses[i].length; j++) {
|
||||
if (objClasses[i][j].isInstance(e)) {
|
||||
exportableExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!exportableExists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
exportableExistsArray[i] = true;
|
||||
|
||||
JLabel label = new JLabel(translate(optionNames[i]));
|
||||
if (label.getPreferredSize().width > labWidth) {
|
||||
labWidth = label.getPreferredSize().width;
|
||||
}
|
||||
}
|
||||
|
||||
String exportFormatsStr = Configuration.lastSelectedExportFormats.get();
|
||||
if ("".equals(exportFormatsStr)) {
|
||||
exportFormatsStr = null;
|
||||
}
|
||||
|
||||
String[] exportFormatsArr = new String[0];
|
||||
if (exportFormatsStr != null) {
|
||||
if (exportFormatsStr.contains(",")) {
|
||||
exportFormatsArr = exportFormatsStr.split(",");
|
||||
} else {
|
||||
exportFormatsArr = new String[]{exportFormatsStr};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int comboWidth = 200;
|
||||
int checkBoxWidth;
|
||||
int top = 10;
|
||||
|
||||
List<String> exportFormats = Arrays.asList(exportFormatsArr);
|
||||
combos = new JComboBox[optionNames.length];
|
||||
checkBoxes = new JCheckBox[optionNames.length];
|
||||
selectAllCheckBox = new JCheckBox();
|
||||
checkBoxWidth = selectAllCheckBox.getPreferredSize().width;
|
||||
selectAllCheckBox.setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, selectAllCheckBox.getPreferredSize().height);
|
||||
selectAllCheckBox.setSelected(true);
|
||||
selectAllCheckBox.addActionListener((ActionEvent e) -> {
|
||||
boolean selected = selectAllCheckBox.isSelected();
|
||||
for (JCheckBox checkBox : checkBoxes) {
|
||||
if (checkBox != null) {
|
||||
checkBox.setSelected(selected);
|
||||
}
|
||||
}
|
||||
});
|
||||
comboPanel.add(selectAllCheckBox);
|
||||
top += selectAllCheckBox.getHeight();
|
||||
|
||||
boolean zoomable = false;
|
||||
for (int i = 0; i < optionNames.length; i++) {
|
||||
Class c = optionClasses[i];
|
||||
Object[] vals = c.getEnumConstants();
|
||||
String[] names = new String[vals.length];
|
||||
int itemIndex = -1;
|
||||
for (int j = 0; j < vals.length; j++) {
|
||||
|
||||
String key = optionNames[i] + "." + vals[j].toString().toLowerCase();
|
||||
if (exportFormats.contains(key)) {
|
||||
itemIndex = j;
|
||||
}
|
||||
names[j] = translate(key);
|
||||
}
|
||||
|
||||
combos[i] = new JComboBox<>(names);
|
||||
if (itemIndex > -1) {
|
||||
combos[i].setSelectedIndex(itemIndex);
|
||||
}
|
||||
|
||||
combos[i].setBounds(10 + labWidth + 10, top, comboWidth, combos[i].getPreferredSize().height);
|
||||
|
||||
checkBoxes[i] = new JCheckBox();
|
||||
checkBoxes[i].setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, checkBoxes[i].getPreferredSize().height);
|
||||
checkBoxes[i].setSelected(true);
|
||||
|
||||
if (!exportableExistsArray[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Arrays.asList(zoomClasses).contains(c)) {
|
||||
zoomable = true;
|
||||
}
|
||||
|
||||
JLabel lab = new JLabel(translate(optionNames[i]));
|
||||
lab.setBounds(10, top, lab.getPreferredSize().width, lab.getPreferredSize().height);
|
||||
comboPanel.add(lab);
|
||||
comboPanel.add(checkBoxes[i]);
|
||||
comboPanel.add(combos[i]);
|
||||
top += combos[i].getHeight();
|
||||
}
|
||||
|
||||
int zoomWidth = 50;
|
||||
if (zoomable) {
|
||||
top += 2;
|
||||
JLabel zlab = new JLabel(translate("zoom"));
|
||||
zlab.setBounds(10, top + 4, zlab.getPreferredSize().width, zlab.getPreferredSize().height);
|
||||
zoomTextField.setBounds(10 + labWidth + 10, top, zoomWidth, zoomTextField.getPreferredSize().height);
|
||||
JLabel pctLabel = new JLabel(translate("zoom.percent"));
|
||||
pctLabel.setBounds(10 + labWidth + 10 + zoomWidth + 5, top + 4, 20, pctLabel.getPreferredSize().height);
|
||||
|
||||
comboPanel.add(zlab);
|
||||
comboPanel.add(zoomTextField);
|
||||
comboPanel.add(pctLabel);
|
||||
top += zoomTextField.getHeight();
|
||||
}
|
||||
|
||||
Dimension dim = new Dimension(10 + labWidth + 10 + checkBoxWidth + 10 + comboWidth + 10, top + 10);
|
||||
comboPanel.setMinimumSize(dim);
|
||||
comboPanel.setPreferredSize(dim);
|
||||
cnt.add(comboPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel buttonsPanel = new JPanel(new FlowLayout());
|
||||
JButton okButton = new JButton(translate("button.ok"));
|
||||
okButton.addActionListener(this::okButtonActionPerformed);
|
||||
|
||||
JButton cancelButton = new JButton(translate("button.cancel"));
|
||||
cancelButton.addActionListener(this::cancelButtonActionPerformed);
|
||||
|
||||
buttonsPanel.add(okButton);
|
||||
buttonsPanel.add(cancelButton);
|
||||
|
||||
cnt.add(buttonsPanel, BorderLayout.SOUTH);
|
||||
pack();
|
||||
View.centerScreen(this);
|
||||
View.setWindowIcon(this);
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
setModal(true);
|
||||
String pct = "" + Configuration.lastSelectedExportZoom.get() * 100;
|
||||
if (pct.endsWith(".0")) {
|
||||
pct = pct.substring(0, pct.length() - 2);
|
||||
}
|
||||
|
||||
zoomTextField.setText(pct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
if (b) {
|
||||
result = ERROR_OPTION;
|
||||
}
|
||||
super.setVisible(b);
|
||||
}
|
||||
|
||||
private void okButtonActionPerformed(ActionEvent evt) {
|
||||
result = OK_OPTION;
|
||||
try {
|
||||
saveConfig();
|
||||
} catch (NumberFormatException nfe) {
|
||||
JOptionPane.showMessageDialog(ExportDialog.this, translate("zoom.invalid"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
zoomTextField.requestFocusInWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void cancelButtonActionPerformed(ActionEvent evt) {
|
||||
result = CANCEL_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
public int showExportDialog() {
|
||||
setVisible(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,401 +1,401 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui;
|
||||
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.tags.font.CharacterRanges;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class FontEmbedDialog extends AppDialog {
|
||||
|
||||
private static final int SAMPLE_MAX_LENGTH = 50;
|
||||
|
||||
private final JComboBox<FontFamily> familyNamesSelection;
|
||||
|
||||
private final JComboBox<FontFace> faceSelection;
|
||||
|
||||
private final JCheckBox[] rangeCheckboxes;
|
||||
|
||||
private final String rangeNames[];
|
||||
|
||||
private final JLabel[] rangeSamples;
|
||||
|
||||
private final JTextField individualCharsField;
|
||||
|
||||
private int result = ERROR_OPTION;
|
||||
|
||||
private JLabel individialSample;
|
||||
|
||||
private Font customFont;
|
||||
|
||||
private final JCheckBox allCheckbox;
|
||||
|
||||
public Font getSelectedFont() {
|
||||
if (ttfFileRadio.isSelected() && customFont != null) {
|
||||
return customFont;
|
||||
}
|
||||
return ((FontFace) faceSelection.getSelectedItem()).font;
|
||||
}
|
||||
|
||||
public Set<Integer> getSelectedChars() {
|
||||
Set<Integer> chars = new TreeSet<>();
|
||||
Font f = getSelectedFont();
|
||||
if (allCheckbox.isSelected()) {
|
||||
for (int i = 0; i < rangeCheckboxes.length; i++) {
|
||||
int codes[] = CharacterRanges.rangeCodes(i);
|
||||
for (int c : codes) {
|
||||
if (f.canDisplay(c)) {
|
||||
chars.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < rangeCheckboxes.length; i++) {
|
||||
if (rangeCheckboxes[i].isSelected()) {
|
||||
int codes[] = CharacterRanges.rangeCodes(i);
|
||||
for (int c : codes) {
|
||||
if (f.canDisplay(c)) {
|
||||
chars.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String indStr = individualCharsField.getText();
|
||||
for (int i = 0; i < indStr.length(); i++) {
|
||||
if (f.canDisplay(indStr.codePointAt(i))) {
|
||||
chars.add(indStr.codePointAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
private JRadioButton ttfFileRadio;
|
||||
|
||||
private JRadioButton installedRadio;
|
||||
|
||||
private void updateFaceSelection() {
|
||||
faceSelection.setModel(FontPanel.getFaceModel((FontFamily) familyNamesSelection.getSelectedItem()));
|
||||
}
|
||||
|
||||
public FontEmbedDialog(FontFace selectedFace, String selectedChars) {
|
||||
setSize(900, 600);
|
||||
setDefaultCloseOperation(HIDE_ON_CLOSE);
|
||||
setTitle(translate("dialog.title"));
|
||||
|
||||
Container cnt = getContentPane();
|
||||
cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS));
|
||||
|
||||
JPanel selFontPanel = new JPanel(new FlowLayout());
|
||||
|
||||
installedRadio = new JRadioButton(translate("installed"));
|
||||
ttfFileRadio = new JRadioButton(translate("ttffile.noselection"));
|
||||
|
||||
ButtonGroup bg = new ButtonGroup();
|
||||
bg.add(installedRadio);
|
||||
bg.add(ttfFileRadio);
|
||||
|
||||
installedRadio.setSelected(true);
|
||||
|
||||
individialSample = new JLabel();
|
||||
familyNamesSelection = new JComboBox<>(FontPanel.getFamilyModel());
|
||||
familyNamesSelection.setSelectedItem(new FontFamily(selectedFace.font));
|
||||
faceSelection = new JComboBox<>();
|
||||
updateFaceSelection();
|
||||
faceSelection.setSelectedItem(selectedFace);
|
||||
JButton loadFromDiskButton = new JButton(View.getIcon("open16"));
|
||||
loadFromDiskButton.setToolTipText(translate("button.loadfont"));
|
||||
loadFromDiskButton.addActionListener(this::loadFromDiscButtonActionPerformed);
|
||||
selFontPanel.add(installedRadio);
|
||||
selFontPanel.add(familyNamesSelection);
|
||||
selFontPanel.add(faceSelection);
|
||||
selFontPanel.add(ttfFileRadio);
|
||||
selFontPanel.add(loadFromDiskButton);
|
||||
|
||||
installedRadio.addItemListener(new ItemListener() {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
updateCheckboxes();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ttfFileRadio.addItemListener(new ItemListener() {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
if (ttfFileRadio.isSelected()) {
|
||||
if (customFont == null) {
|
||||
if (loadFromDisk()) {
|
||||
updateCheckboxes();
|
||||
} else {
|
||||
installedRadio.setSelected(true);
|
||||
}
|
||||
} else {
|
||||
updateCheckboxes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cnt.add(selFontPanel);
|
||||
JPanel rangesPanel = new JPanel();
|
||||
rangesPanel.setLayout(new BoxLayout(rangesPanel, BoxLayout.Y_AXIS));
|
||||
final int rc = CharacterRanges.rangeCount();
|
||||
rangeCheckboxes = new JCheckBox[rc];
|
||||
rangeSamples = new JLabel[rc];
|
||||
rangeNames = new String[rc];
|
||||
allCheckbox = new JCheckBox(translate("allcharacters"));
|
||||
allCheckbox.addItemListener(new ItemListener() {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeCheckboxes[i].setEnabled(false);
|
||||
}
|
||||
individualCharsField.setEnabled(false);
|
||||
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeCheckboxes[i].setEnabled(true);
|
||||
}
|
||||
individualCharsField.setEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
JPanel rangeRowPanel = new JPanel();
|
||||
rangeRowPanel.setLayout(new BorderLayout());
|
||||
rangeRowPanel.add(allCheckbox, BorderLayout.WEST);
|
||||
rangeRowPanel.setAlignmentX(0);
|
||||
rangesPanel.add(rangeRowPanel);
|
||||
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeNames[i] = CharacterRanges.rangeName(i);
|
||||
rangeSamples[i] = new JLabel("");
|
||||
rangeCheckboxes[i] = new JCheckBox(rangeNames[i]);
|
||||
rangeRowPanel = new JPanel();
|
||||
rangeRowPanel.setLayout(new BoxLayout(rangeRowPanel, BoxLayout.X_AXIS));
|
||||
rangeRowPanel.add(rangeCheckboxes[i]);
|
||||
rangeRowPanel.add(Box.createHorizontalGlue());
|
||||
rangeRowPanel.add(rangeSamples[i]);
|
||||
rangeRowPanel.setAlignmentX(0);
|
||||
rangesPanel.add(rangeRowPanel);
|
||||
}
|
||||
cnt.add(new JScrollPane(rangesPanel));
|
||||
|
||||
JPanel specialPanel = new JPanel();
|
||||
specialPanel.setLayout(new BoxLayout(specialPanel, BoxLayout.X_AXIS));
|
||||
specialPanel.add(new JLabel(translate("label.individual")));
|
||||
individualCharsField = new JTextField();
|
||||
individualCharsField.setPreferredSize(new Dimension(100, individualCharsField.getPreferredSize().height));
|
||||
individialSample = new JLabel();
|
||||
specialPanel.add(individualCharsField);
|
||||
|
||||
cnt.add(specialPanel);
|
||||
cnt.add(individialSample);
|
||||
|
||||
JPanel buttonsPanel = new JPanel(new FlowLayout());
|
||||
JButton okButton = new JButton(AppStrings.translate("button.ok"));
|
||||
okButton.addActionListener(this::okButtonActionPerformed);
|
||||
JButton cancelButton = new JButton(AppStrings.translate("button.cancel"));
|
||||
cancelButton.addActionListener(this::cancelButtonActionPerformed);
|
||||
buttonsPanel.add(okButton);
|
||||
buttonsPanel.add(cancelButton);
|
||||
cnt.add(buttonsPanel);
|
||||
View.setWindowIcon(this);
|
||||
View.centerScreen(this);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
individualCharsField.setText(selectedChars);
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
familyNamesSelection.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
updateFaceSelection();
|
||||
updateCheckboxes();
|
||||
}
|
||||
});
|
||||
faceSelection.addItemListener((ItemEvent e) -> {
|
||||
updateCheckboxes();
|
||||
});
|
||||
updateCheckboxes();
|
||||
individualCharsField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
updateIndividual();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
updateIndividual();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
updateIndividual();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateIndividual() {
|
||||
String chars = individualCharsField.getText();
|
||||
Font f = getSelectedFont();
|
||||
StringBuilder visibleChars = new StringBuilder();
|
||||
for (int i = 0; i < chars.length(); i++) {
|
||||
if (f.canDisplay(chars.codePointAt(i))) {
|
||||
visibleChars.append(chars.charAt(i));
|
||||
}
|
||||
}
|
||||
individialSample.setText(visibleChars.toString());
|
||||
}
|
||||
|
||||
private void updateCheckboxes() {
|
||||
Font f = getSelectedFont().deriveFont(12f);
|
||||
int rc = CharacterRanges.rangeCount();
|
||||
|
||||
Set<Integer> allChars = new HashSet<>();
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeNames[i] = CharacterRanges.rangeName(i);
|
||||
int codes[] = CharacterRanges.rangeCodes(i);
|
||||
int avail = 0;
|
||||
StringBuilder sample = new StringBuilder();
|
||||
for (int c = 0; c < codes.length; c++) {
|
||||
if (f.canDisplay(codes[c])) {
|
||||
allChars.add(codes[c]);
|
||||
if (avail < SAMPLE_MAX_LENGTH) {
|
||||
sample.append((char) codes[c]);
|
||||
}
|
||||
avail++;
|
||||
}
|
||||
}
|
||||
rangeSamples[i].setText(sample.toString());
|
||||
rangeSamples[i].setFont(f);
|
||||
rangeCheckboxes[i].setText(translate("range.description").replace("%available%", Integer.toString(avail)).replace("%name%", rangeNames[i]).replace("%total%", Integer.toString(codes.length)));
|
||||
}
|
||||
allCheckbox.setText(translate("allcharacters").replace("%available%", Integer.toString(allChars.size())));
|
||||
individialSample.setFont(f);
|
||||
updateIndividual();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
if (b) {
|
||||
result = ERROR_OPTION;
|
||||
}
|
||||
|
||||
super.setVisible(b);
|
||||
}
|
||||
|
||||
private void okButtonActionPerformed(ActionEvent evt) {
|
||||
result = OK_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void cancelButtonActionPerformed(ActionEvent evt) {
|
||||
result = CANCEL_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void loadFromDiscButtonActionPerformed(ActionEvent evt) {
|
||||
if (customFont != null) {
|
||||
if (loadFromDisk()) {
|
||||
updateCheckboxes();
|
||||
}
|
||||
}
|
||||
|
||||
ttfFileRadio.setSelected(true);
|
||||
}
|
||||
|
||||
private boolean loadFromDisk() {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get()));
|
||||
FileFilter ttfFilter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File f) {
|
||||
return (f.getName().toLowerCase().endsWith(".ttf")) || (f.isDirectory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return translate("filter.ttf");
|
||||
}
|
||||
};
|
||||
fc.setFileFilter(ttfFilter);
|
||||
fc.setAcceptAllFileFilterUsed(true);
|
||||
JFrame f = new JFrame();
|
||||
View.setWindowIcon(f);
|
||||
int returnVal = fc.showOpenDialog(f);
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath());
|
||||
File selfile = Helper.fixDialogFile(fc.getSelectedFile());
|
||||
try {
|
||||
customFont = Font.createFont(Font.TRUETYPE_FONT, selfile);
|
||||
ttfFileRadio.setText(translate("ttffile.selection").replace("%fontname%", customFont.getName()).replace("%filename%", selfile.getName()));
|
||||
return true;
|
||||
} catch (FontFormatException ex) {
|
||||
JOptionPane.showMessageDialog(this, translate("error.invalidfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
} catch (IOException ex) {
|
||||
JOptionPane.showMessageDialog(this, translate("error.cannotreadfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int showDialog() {
|
||||
setVisible(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui;
|
||||
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.tags.font.CharacterRanges;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class FontEmbedDialog extends AppDialog {
|
||||
|
||||
private static final int SAMPLE_MAX_LENGTH = 50;
|
||||
|
||||
private final JComboBox<FontFamily> familyNamesSelection;
|
||||
|
||||
private final JComboBox<FontFace> faceSelection;
|
||||
|
||||
private final JCheckBox[] rangeCheckboxes;
|
||||
|
||||
private final String rangeNames[];
|
||||
|
||||
private final JLabel[] rangeSamples;
|
||||
|
||||
private final JTextField individualCharsField;
|
||||
|
||||
private int result = ERROR_OPTION;
|
||||
|
||||
private JLabel individialSample;
|
||||
|
||||
private Font customFont;
|
||||
|
||||
private final JCheckBox allCheckbox;
|
||||
|
||||
public Font getSelectedFont() {
|
||||
if (ttfFileRadio.isSelected() && customFont != null) {
|
||||
return customFont;
|
||||
}
|
||||
return ((FontFace) faceSelection.getSelectedItem()).font;
|
||||
}
|
||||
|
||||
public Set<Integer> getSelectedChars() {
|
||||
Set<Integer> chars = new TreeSet<>();
|
||||
Font f = getSelectedFont();
|
||||
if (allCheckbox.isSelected()) {
|
||||
for (int i = 0; i < rangeCheckboxes.length; i++) {
|
||||
int[] codes = CharacterRanges.rangeCodes(i);
|
||||
for (int c : codes) {
|
||||
if (f.canDisplay(c)) {
|
||||
chars.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < rangeCheckboxes.length; i++) {
|
||||
if (rangeCheckboxes[i].isSelected()) {
|
||||
int[] codes = CharacterRanges.rangeCodes(i);
|
||||
for (int c : codes) {
|
||||
if (f.canDisplay(c)) {
|
||||
chars.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String indStr = individualCharsField.getText();
|
||||
for (int i = 0; i < indStr.length(); i++) {
|
||||
if (f.canDisplay(indStr.codePointAt(i))) {
|
||||
chars.add(indStr.codePointAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
private JRadioButton ttfFileRadio;
|
||||
|
||||
private JRadioButton installedRadio;
|
||||
|
||||
private void updateFaceSelection() {
|
||||
faceSelection.setModel(FontPanel.getFaceModel((FontFamily) familyNamesSelection.getSelectedItem()));
|
||||
}
|
||||
|
||||
public FontEmbedDialog(FontFace selectedFace, String selectedChars) {
|
||||
setSize(900, 600);
|
||||
setDefaultCloseOperation(HIDE_ON_CLOSE);
|
||||
setTitle(translate("dialog.title"));
|
||||
|
||||
Container cnt = getContentPane();
|
||||
cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS));
|
||||
|
||||
JPanel selFontPanel = new JPanel(new FlowLayout());
|
||||
|
||||
installedRadio = new JRadioButton(translate("installed"));
|
||||
ttfFileRadio = new JRadioButton(translate("ttffile.noselection"));
|
||||
|
||||
ButtonGroup bg = new ButtonGroup();
|
||||
bg.add(installedRadio);
|
||||
bg.add(ttfFileRadio);
|
||||
|
||||
installedRadio.setSelected(true);
|
||||
|
||||
individialSample = new JLabel();
|
||||
familyNamesSelection = new JComboBox<>(FontPanel.getFamilyModel());
|
||||
familyNamesSelection.setSelectedItem(new FontFamily(selectedFace.font));
|
||||
faceSelection = new JComboBox<>();
|
||||
updateFaceSelection();
|
||||
faceSelection.setSelectedItem(selectedFace);
|
||||
JButton loadFromDiskButton = new JButton(View.getIcon("open16"));
|
||||
loadFromDiskButton.setToolTipText(translate("button.loadfont"));
|
||||
loadFromDiskButton.addActionListener(this::loadFromDiscButtonActionPerformed);
|
||||
selFontPanel.add(installedRadio);
|
||||
selFontPanel.add(familyNamesSelection);
|
||||
selFontPanel.add(faceSelection);
|
||||
selFontPanel.add(ttfFileRadio);
|
||||
selFontPanel.add(loadFromDiskButton);
|
||||
|
||||
installedRadio.addItemListener(new ItemListener() {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
updateCheckboxes();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ttfFileRadio.addItemListener(new ItemListener() {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
if (ttfFileRadio.isSelected()) {
|
||||
if (customFont == null) {
|
||||
if (loadFromDisk()) {
|
||||
updateCheckboxes();
|
||||
} else {
|
||||
installedRadio.setSelected(true);
|
||||
}
|
||||
} else {
|
||||
updateCheckboxes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cnt.add(selFontPanel);
|
||||
JPanel rangesPanel = new JPanel();
|
||||
rangesPanel.setLayout(new BoxLayout(rangesPanel, BoxLayout.Y_AXIS));
|
||||
final int rc = CharacterRanges.rangeCount();
|
||||
rangeCheckboxes = new JCheckBox[rc];
|
||||
rangeSamples = new JLabel[rc];
|
||||
rangeNames = new String[rc];
|
||||
allCheckbox = new JCheckBox(translate("allcharacters"));
|
||||
allCheckbox.addItemListener(new ItemListener() {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeCheckboxes[i].setEnabled(false);
|
||||
}
|
||||
individualCharsField.setEnabled(false);
|
||||
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeCheckboxes[i].setEnabled(true);
|
||||
}
|
||||
individualCharsField.setEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
JPanel rangeRowPanel = new JPanel();
|
||||
rangeRowPanel.setLayout(new BorderLayout());
|
||||
rangeRowPanel.add(allCheckbox, BorderLayout.WEST);
|
||||
rangeRowPanel.setAlignmentX(0);
|
||||
rangesPanel.add(rangeRowPanel);
|
||||
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeNames[i] = CharacterRanges.rangeName(i);
|
||||
rangeSamples[i] = new JLabel("");
|
||||
rangeCheckboxes[i] = new JCheckBox(rangeNames[i]);
|
||||
rangeRowPanel = new JPanel();
|
||||
rangeRowPanel.setLayout(new BoxLayout(rangeRowPanel, BoxLayout.X_AXIS));
|
||||
rangeRowPanel.add(rangeCheckboxes[i]);
|
||||
rangeRowPanel.add(Box.createHorizontalGlue());
|
||||
rangeRowPanel.add(rangeSamples[i]);
|
||||
rangeRowPanel.setAlignmentX(0);
|
||||
rangesPanel.add(rangeRowPanel);
|
||||
}
|
||||
cnt.add(new JScrollPane(rangesPanel));
|
||||
|
||||
JPanel specialPanel = new JPanel();
|
||||
specialPanel.setLayout(new BoxLayout(specialPanel, BoxLayout.X_AXIS));
|
||||
specialPanel.add(new JLabel(translate("label.individual")));
|
||||
individualCharsField = new JTextField();
|
||||
individualCharsField.setPreferredSize(new Dimension(100, individualCharsField.getPreferredSize().height));
|
||||
individialSample = new JLabel();
|
||||
specialPanel.add(individualCharsField);
|
||||
|
||||
cnt.add(specialPanel);
|
||||
cnt.add(individialSample);
|
||||
|
||||
JPanel buttonsPanel = new JPanel(new FlowLayout());
|
||||
JButton okButton = new JButton(AppStrings.translate("button.ok"));
|
||||
okButton.addActionListener(this::okButtonActionPerformed);
|
||||
JButton cancelButton = new JButton(AppStrings.translate("button.cancel"));
|
||||
cancelButton.addActionListener(this::cancelButtonActionPerformed);
|
||||
buttonsPanel.add(okButton);
|
||||
buttonsPanel.add(cancelButton);
|
||||
cnt.add(buttonsPanel);
|
||||
View.setWindowIcon(this);
|
||||
View.centerScreen(this);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
individualCharsField.setText(selectedChars);
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
familyNamesSelection.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
updateFaceSelection();
|
||||
updateCheckboxes();
|
||||
}
|
||||
});
|
||||
faceSelection.addItemListener((ItemEvent e) -> {
|
||||
updateCheckboxes();
|
||||
});
|
||||
updateCheckboxes();
|
||||
individualCharsField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
updateIndividual();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
updateIndividual();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
updateIndividual();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateIndividual() {
|
||||
String chars = individualCharsField.getText();
|
||||
Font f = getSelectedFont();
|
||||
StringBuilder visibleChars = new StringBuilder();
|
||||
for (int i = 0; i < chars.length(); i++) {
|
||||
if (f.canDisplay(chars.codePointAt(i))) {
|
||||
visibleChars.append(chars.charAt(i));
|
||||
}
|
||||
}
|
||||
individialSample.setText(visibleChars.toString());
|
||||
}
|
||||
|
||||
private void updateCheckboxes() {
|
||||
Font f = getSelectedFont().deriveFont(12f);
|
||||
int rc = CharacterRanges.rangeCount();
|
||||
|
||||
Set<Integer> allChars = new HashSet<>();
|
||||
for (int i = 0; i < rc; i++) {
|
||||
rangeNames[i] = CharacterRanges.rangeName(i);
|
||||
int[] codes = CharacterRanges.rangeCodes(i);
|
||||
int avail = 0;
|
||||
StringBuilder sample = new StringBuilder();
|
||||
for (int c = 0; c < codes.length; c++) {
|
||||
if (f.canDisplay(codes[c])) {
|
||||
allChars.add(codes[c]);
|
||||
if (avail < SAMPLE_MAX_LENGTH) {
|
||||
sample.append((char) codes[c]);
|
||||
}
|
||||
avail++;
|
||||
}
|
||||
}
|
||||
rangeSamples[i].setText(sample.toString());
|
||||
rangeSamples[i].setFont(f);
|
||||
rangeCheckboxes[i].setText(translate("range.description").replace("%available%", Integer.toString(avail)).replace("%name%", rangeNames[i]).replace("%total%", Integer.toString(codes.length)));
|
||||
}
|
||||
allCheckbox.setText(translate("allcharacters").replace("%available%", Integer.toString(allChars.size())));
|
||||
individialSample.setFont(f);
|
||||
updateIndividual();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
if (b) {
|
||||
result = ERROR_OPTION;
|
||||
}
|
||||
|
||||
super.setVisible(b);
|
||||
}
|
||||
|
||||
private void okButtonActionPerformed(ActionEvent evt) {
|
||||
result = OK_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void cancelButtonActionPerformed(ActionEvent evt) {
|
||||
result = CANCEL_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void loadFromDiscButtonActionPerformed(ActionEvent evt) {
|
||||
if (customFont != null) {
|
||||
if (loadFromDisk()) {
|
||||
updateCheckboxes();
|
||||
}
|
||||
}
|
||||
|
||||
ttfFileRadio.setSelected(true);
|
||||
}
|
||||
|
||||
private boolean loadFromDisk() {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get()));
|
||||
FileFilter ttfFilter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File f) {
|
||||
return (f.getName().toLowerCase().endsWith(".ttf")) || (f.isDirectory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return translate("filter.ttf");
|
||||
}
|
||||
};
|
||||
fc.setFileFilter(ttfFilter);
|
||||
fc.setAcceptAllFileFilterUsed(true);
|
||||
JFrame f = new JFrame();
|
||||
View.setWindowIcon(f);
|
||||
int returnVal = fc.showOpenDialog(f);
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath());
|
||||
File selfile = Helper.fixDialogFile(fc.getSelectedFile());
|
||||
try {
|
||||
customFont = Font.createFont(Font.TRUETYPE_FONT, selfile);
|
||||
ttfFileRadio.setText(translate("ttffile.selection").replace("%fontname%", customFont.getName()).replace("%filename%", selfile.getName()));
|
||||
return true;
|
||||
} catch (FontFormatException ex) {
|
||||
JOptionPane.showMessageDialog(this, translate("error.invalidfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
} catch (IOException ex) {
|
||||
JOptionPane.showMessageDialog(this, translate("error.cannotreadfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int showDialog() {
|
||||
setVisible(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,7 +642,7 @@ public class FontPanel extends JPanel {
|
||||
Set<Integer> selChars = new HashSet<>();
|
||||
try {
|
||||
Font f = Font.createFont(Font.TRUETYPE_FONT, selfile);
|
||||
int required[] = new int[]{0x0001, 0x0000, 0x000D, 0x0020};
|
||||
int[] required = new int[]{0x0001, 0x0000, 0x000D, 0x0020};
|
||||
loopi:
|
||||
for (char i = 0; i < Character.MAX_VALUE; i++) {
|
||||
for (int r : required) {
|
||||
|
||||
@@ -290,7 +290,7 @@ public class GenericTagPanel extends JPanel implements ChangeListener {
|
||||
final int val = sb.getValue(); //save scroll top
|
||||
SWFType swfType = field.getAnnotation(SWFType.class);
|
||||
if (swfType != null && !swfType.countField().isEmpty()) { //Fields with same countField must be removed from too
|
||||
Field fields[] = obj.getClass().getDeclaredFields();
|
||||
Field[] fields = obj.getClass().getDeclaredFields();
|
||||
for (int f = 0; f < fields.length; f++) {
|
||||
SWFType fieldSwfType = fields[f].getAnnotation(SWFType.class);
|
||||
if (fieldSwfType != null && fieldSwfType.countField().equals(swfType.countField())) {
|
||||
@@ -338,7 +338,7 @@ public class GenericTagPanel extends JPanel implements ChangeListener {
|
||||
final int val = sb.getValue(); //save scroll top
|
||||
SWFType swfType = field.getAnnotation(SWFType.class);
|
||||
if (swfType != null && !swfType.countField().isEmpty()) { //Fields with same countField must be enlarged too
|
||||
Field fields[] = obj.getClass().getDeclaredFields();
|
||||
Field[] fields = obj.getClass().getDeclaredFields();
|
||||
for (int f = 0; f < fields.length; f++) {
|
||||
SWFType fieldSwfType = fields[f].getAnnotation(SWFType.class);
|
||||
if (fieldSwfType != null && fieldSwfType.countField().equals(swfType.countField())) {
|
||||
|
||||
@@ -1068,7 +1068,7 @@ public class GenericTagTreePanel extends GenericTagPanel {
|
||||
List<Field> ret = fieldCache.get(cls);
|
||||
if (ret == null) {
|
||||
ret = new ArrayList<>();
|
||||
Field fields[] = cls.getFields();
|
||||
Field[] fields = cls.getFields();
|
||||
for (Field f : fields) {
|
||||
if (Modifier.isStatic(f.getModifiers())) {
|
||||
continue;
|
||||
@@ -1092,7 +1092,7 @@ public class GenericTagTreePanel extends GenericTagPanel {
|
||||
private void addItem(Object obj, Field field, int index, Class<?> cls) {
|
||||
SWFArray swfArray = field.getAnnotation(SWFArray.class);
|
||||
if (swfArray != null && !swfArray.countField().isEmpty()) { //Fields with same countField must be enlarged too
|
||||
Field fields[] = obj.getClass().getDeclaredFields();
|
||||
Field[] fields = obj.getClass().getDeclaredFields();
|
||||
List<Integer> sameFlds = new ArrayList<>();
|
||||
for (int f = 0; f < fields.length; f++) {
|
||||
SWFArray fieldSwfArray = fields[f].getAnnotation(SWFArray.class);
|
||||
@@ -1154,7 +1154,7 @@ public class GenericTagTreePanel extends GenericTagPanel {
|
||||
private void removeItem(Object obj, Field field, int index) {
|
||||
SWFArray swfArray = field.getAnnotation(SWFArray.class);
|
||||
if (swfArray != null && !swfArray.countField().isEmpty()) { //Fields with same countField must be removed from too
|
||||
Field fields[] = obj.getClass().getDeclaredFields();
|
||||
Field[] fields = obj.getClass().getDeclaredFields();
|
||||
for (int f = 0; f < fields.length; f++) {
|
||||
SWFArray fieldSwfArray = fields[f].getAnnotation(SWFArray.class);
|
||||
if (fieldSwfArray != null && fieldSwfArray.countField().equals(swfArray.countField())) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,293 +1,293 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Menu Builder. Creates menu.
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public interface MenuBuilder {
|
||||
|
||||
public static class HotKey {
|
||||
|
||||
private static Map<Integer, String> keyCodesToNames = new HashMap<>();
|
||||
|
||||
private static Map<String, Integer> keyNamesToCodes = new HashMap<>();
|
||||
|
||||
{
|
||||
|
||||
Field[] fields = KeyEvent.class.getFields();
|
||||
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
|
||||
String fieldName = fields[i].getName();
|
||||
|
||||
// We only care about the field names corresponding to key codes
|
||||
if (fieldName.startsWith("VK")) {
|
||||
try {
|
||||
int keyCode = fields[i].getInt(null);
|
||||
String keyName = fieldName.substring(3);
|
||||
keyCodesToNames.put(keyCode, keyName);
|
||||
keyNamesToCodes.put(keyName, keyCode);
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int key;
|
||||
|
||||
public boolean shiftDown;
|
||||
|
||||
public boolean ctrlDown;
|
||||
|
||||
public boolean altDown;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 41 * hash + this.key;
|
||||
hash = 41 * hash + (this.shiftDown ? 1 : 0);
|
||||
hash = 41 * hash + (this.ctrlDown ? 1 : 0);
|
||||
hash = 41 * hash + (this.altDown ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final HotKey other = (HotKey) obj;
|
||||
if (this.key != other.key) {
|
||||
return false;
|
||||
}
|
||||
if (this.shiftDown != other.shiftDown) {
|
||||
return false;
|
||||
}
|
||||
if (this.ctrlDown != other.ctrlDown) {
|
||||
return false;
|
||||
}
|
||||
return (this.altDown == other.altDown);
|
||||
}
|
||||
|
||||
public boolean matches(KeyEvent ev) {
|
||||
return ev.getKeyCode() == key && ev.isControlDown() == ctrlDown && ev.isShiftDown() == shiftDown && ev.isAltDown() == altDown;
|
||||
}
|
||||
|
||||
public int getModifier() {
|
||||
return (shiftDown ? KeyEvent.SHIFT_MASK : 0) + (ctrlDown ? KeyEvent.CTRL_MASK : 0) + (altDown ? KeyEvent.ALT_MASK : 0);
|
||||
}
|
||||
|
||||
public HotKey(String h) {
|
||||
String parts[] = h.contains("+") ? h.split("\\+") : new String[]{h};
|
||||
for (String s : parts) {
|
||||
switch (s) {
|
||||
case "SHIFT":
|
||||
shiftDown = true;
|
||||
break;
|
||||
case "CTRL":
|
||||
ctrlDown = true;
|
||||
break;
|
||||
case "ALT":
|
||||
altDown = true;
|
||||
break;
|
||||
default:
|
||||
if (keyNamesToCodes.containsKey(s)) {
|
||||
key = keyNamesToCodes.get(s);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Key " + s + " not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HotKey(KeyEvent ev) {
|
||||
this(ev.getKeyCode(), ev.isShiftDown(), ev.isControlDown(), ev.isAltDown());
|
||||
}
|
||||
|
||||
public HotKey(int key) {
|
||||
this(key, false, false, false);
|
||||
}
|
||||
|
||||
public HotKey(int key, boolean shiftDown, boolean ctrlDown, boolean altDown) {
|
||||
this.key = key;
|
||||
this.shiftDown = shiftDown;
|
||||
this.ctrlDown = ctrlDown;
|
||||
this.altDown = altDown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = "";
|
||||
if (shiftDown) {
|
||||
s += "SHIFT";
|
||||
}
|
||||
if (ctrlDown) {
|
||||
if (!s.isEmpty()) {
|
||||
s += "+";
|
||||
}
|
||||
s += "CTRL";
|
||||
}
|
||||
if (altDown) {
|
||||
if (!s.isEmpty()) {
|
||||
s += "+";
|
||||
}
|
||||
s += "ALT";
|
||||
}
|
||||
if (!s.isEmpty()) {
|
||||
s += "+";
|
||||
}
|
||||
s += keyCodesToNames.get(key);
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int PRIORITY_LOW = 1;
|
||||
|
||||
public static final int PRIORITY_MEDIUM = 2;
|
||||
|
||||
public static final int PRIORITY_TOP = 3;
|
||||
|
||||
/**
|
||||
* Initializes menuBuilder
|
||||
*/
|
||||
public void initMenu();
|
||||
|
||||
/**
|
||||
* Adds separator
|
||||
*
|
||||
* @param parentPath Parent menu path
|
||||
*/
|
||||
public void addSeparator(String parentPath);
|
||||
|
||||
/**
|
||||
* Adds menu item
|
||||
*
|
||||
* @param path Path
|
||||
* @param title Title
|
||||
* @param icon Icon - resource name
|
||||
* @param action Action for clicking
|
||||
* @param priority Priority
|
||||
* @param subloader Action which loads menu inside
|
||||
* @param isLeaf Has no subitems?
|
||||
* @param key
|
||||
* @param isOptional
|
||||
*/
|
||||
public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subloader, boolean isLeaf, HotKey key, boolean isOptional);
|
||||
|
||||
/**
|
||||
* Adds toggle item (radio/checkbox)
|
||||
*
|
||||
* @param path Path
|
||||
* @param title Title
|
||||
* @param group Group for toggling. Null = checkbox
|
||||
* @param icon Icon - resource name
|
||||
* @param action Action for clicking
|
||||
* @param priority Priority
|
||||
* @param key
|
||||
*/
|
||||
public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority, HotKey key);
|
||||
|
||||
/**
|
||||
* Test menu checked (toggle)
|
||||
*
|
||||
* @param path Menu path
|
||||
* @return True when checked
|
||||
*/
|
||||
public boolean isMenuChecked(String path);
|
||||
|
||||
/**
|
||||
* Hotkey for menu
|
||||
*
|
||||
* @param path Menu path
|
||||
* @return
|
||||
*/
|
||||
public HotKey getMenuHotkey(String path);
|
||||
|
||||
/**
|
||||
* Sets menu checked (toggle)
|
||||
*
|
||||
* @param path Menu path
|
||||
* @param checked Checked?
|
||||
*/
|
||||
public void setMenuChecked(String path, boolean checked);
|
||||
|
||||
/**
|
||||
* Sets checked item from group
|
||||
*
|
||||
* @param group Group name
|
||||
* @param selected Selected path
|
||||
*/
|
||||
public void setGroupSelection(String group, String selected);
|
||||
|
||||
/**
|
||||
* Gets selected path from group
|
||||
*
|
||||
* @param group Group name
|
||||
* @return Path
|
||||
*/
|
||||
public String getGroupSelection(String group);
|
||||
|
||||
/**
|
||||
* Clears menu
|
||||
*
|
||||
* @param path Path of menu
|
||||
*/
|
||||
public void clearMenu(String path);
|
||||
|
||||
/**
|
||||
* Sets enabled property of menu
|
||||
*
|
||||
* @param path Menu path
|
||||
* @param enabled Enabled?
|
||||
*/
|
||||
public void setMenuEnabled(String path, boolean enabled);
|
||||
|
||||
/**
|
||||
* Finished creating menu
|
||||
*
|
||||
* @param path Menu path
|
||||
*/
|
||||
public void finishMenu(String path);
|
||||
|
||||
/**
|
||||
* Does this builder support actions for menus? (not menuitems)
|
||||
*
|
||||
* @return True if supports
|
||||
*/
|
||||
public boolean supportsMenuAction();
|
||||
|
||||
/**
|
||||
* Does this builder support application menu (Path "_/...")
|
||||
*
|
||||
* @return True if supports
|
||||
*/
|
||||
public boolean supportsAppMenu();
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Menu Builder. Creates menu.
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public interface MenuBuilder {
|
||||
|
||||
public static class HotKey {
|
||||
|
||||
private static Map<Integer, String> keyCodesToNames = new HashMap<>();
|
||||
|
||||
private static Map<String, Integer> keyNamesToCodes = new HashMap<>();
|
||||
|
||||
{
|
||||
|
||||
Field[] fields = KeyEvent.class.getFields();
|
||||
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
|
||||
String fieldName = fields[i].getName();
|
||||
|
||||
// We only care about the field names corresponding to key codes
|
||||
if (fieldName.startsWith("VK")) {
|
||||
try {
|
||||
int keyCode = fields[i].getInt(null);
|
||||
String keyName = fieldName.substring(3);
|
||||
keyCodesToNames.put(keyCode, keyName);
|
||||
keyNamesToCodes.put(keyName, keyCode);
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int key;
|
||||
|
||||
public boolean shiftDown;
|
||||
|
||||
public boolean ctrlDown;
|
||||
|
||||
public boolean altDown;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 41 * hash + this.key;
|
||||
hash = 41 * hash + (this.shiftDown ? 1 : 0);
|
||||
hash = 41 * hash + (this.ctrlDown ? 1 : 0);
|
||||
hash = 41 * hash + (this.altDown ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final HotKey other = (HotKey) obj;
|
||||
if (this.key != other.key) {
|
||||
return false;
|
||||
}
|
||||
if (this.shiftDown != other.shiftDown) {
|
||||
return false;
|
||||
}
|
||||
if (this.ctrlDown != other.ctrlDown) {
|
||||
return false;
|
||||
}
|
||||
return (this.altDown == other.altDown);
|
||||
}
|
||||
|
||||
public boolean matches(KeyEvent ev) {
|
||||
return ev.getKeyCode() == key && ev.isControlDown() == ctrlDown && ev.isShiftDown() == shiftDown && ev.isAltDown() == altDown;
|
||||
}
|
||||
|
||||
public int getModifier() {
|
||||
return (shiftDown ? KeyEvent.SHIFT_MASK : 0) + (ctrlDown ? KeyEvent.CTRL_MASK : 0) + (altDown ? KeyEvent.ALT_MASK : 0);
|
||||
}
|
||||
|
||||
public HotKey(String h) {
|
||||
String[] parts = h.contains("+") ? h.split("\\+") : new String[]{h};
|
||||
for (String s : parts) {
|
||||
switch (s) {
|
||||
case "SHIFT":
|
||||
shiftDown = true;
|
||||
break;
|
||||
case "CTRL":
|
||||
ctrlDown = true;
|
||||
break;
|
||||
case "ALT":
|
||||
altDown = true;
|
||||
break;
|
||||
default:
|
||||
if (keyNamesToCodes.containsKey(s)) {
|
||||
key = keyNamesToCodes.get(s);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Key " + s + " not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HotKey(KeyEvent ev) {
|
||||
this(ev.getKeyCode(), ev.isShiftDown(), ev.isControlDown(), ev.isAltDown());
|
||||
}
|
||||
|
||||
public HotKey(int key) {
|
||||
this(key, false, false, false);
|
||||
}
|
||||
|
||||
public HotKey(int key, boolean shiftDown, boolean ctrlDown, boolean altDown) {
|
||||
this.key = key;
|
||||
this.shiftDown = shiftDown;
|
||||
this.ctrlDown = ctrlDown;
|
||||
this.altDown = altDown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = "";
|
||||
if (shiftDown) {
|
||||
s += "SHIFT";
|
||||
}
|
||||
if (ctrlDown) {
|
||||
if (!s.isEmpty()) {
|
||||
s += "+";
|
||||
}
|
||||
s += "CTRL";
|
||||
}
|
||||
if (altDown) {
|
||||
if (!s.isEmpty()) {
|
||||
s += "+";
|
||||
}
|
||||
s += "ALT";
|
||||
}
|
||||
if (!s.isEmpty()) {
|
||||
s += "+";
|
||||
}
|
||||
s += keyCodesToNames.get(key);
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int PRIORITY_LOW = 1;
|
||||
|
||||
public static final int PRIORITY_MEDIUM = 2;
|
||||
|
||||
public static final int PRIORITY_TOP = 3;
|
||||
|
||||
/**
|
||||
* Initializes menuBuilder
|
||||
*/
|
||||
public void initMenu();
|
||||
|
||||
/**
|
||||
* Adds separator
|
||||
*
|
||||
* @param parentPath Parent menu path
|
||||
*/
|
||||
public void addSeparator(String parentPath);
|
||||
|
||||
/**
|
||||
* Adds menu item
|
||||
*
|
||||
* @param path Path
|
||||
* @param title Title
|
||||
* @param icon Icon - resource name
|
||||
* @param action Action for clicking
|
||||
* @param priority Priority
|
||||
* @param subloader Action which loads menu inside
|
||||
* @param isLeaf Has no subitems?
|
||||
* @param key
|
||||
* @param isOptional
|
||||
*/
|
||||
public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subloader, boolean isLeaf, HotKey key, boolean isOptional);
|
||||
|
||||
/**
|
||||
* Adds toggle item (radio/checkbox)
|
||||
*
|
||||
* @param path Path
|
||||
* @param title Title
|
||||
* @param group Group for toggling. Null = checkbox
|
||||
* @param icon Icon - resource name
|
||||
* @param action Action for clicking
|
||||
* @param priority Priority
|
||||
* @param key
|
||||
*/
|
||||
public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority, HotKey key);
|
||||
|
||||
/**
|
||||
* Test menu checked (toggle)
|
||||
*
|
||||
* @param path Menu path
|
||||
* @return True when checked
|
||||
*/
|
||||
public boolean isMenuChecked(String path);
|
||||
|
||||
/**
|
||||
* Hotkey for menu
|
||||
*
|
||||
* @param path Menu path
|
||||
* @return
|
||||
*/
|
||||
public HotKey getMenuHotkey(String path);
|
||||
|
||||
/**
|
||||
* Sets menu checked (toggle)
|
||||
*
|
||||
* @param path Menu path
|
||||
* @param checked Checked?
|
||||
*/
|
||||
public void setMenuChecked(String path, boolean checked);
|
||||
|
||||
/**
|
||||
* Sets checked item from group
|
||||
*
|
||||
* @param group Group name
|
||||
* @param selected Selected path
|
||||
*/
|
||||
public void setGroupSelection(String group, String selected);
|
||||
|
||||
/**
|
||||
* Gets selected path from group
|
||||
*
|
||||
* @param group Group name
|
||||
* @return Path
|
||||
*/
|
||||
public String getGroupSelection(String group);
|
||||
|
||||
/**
|
||||
* Clears menu
|
||||
*
|
||||
* @param path Path of menu
|
||||
*/
|
||||
public void clearMenu(String path);
|
||||
|
||||
/**
|
||||
* Sets enabled property of menu
|
||||
*
|
||||
* @param path Menu path
|
||||
* @param enabled Enabled?
|
||||
*/
|
||||
public void setMenuEnabled(String path, boolean enabled);
|
||||
|
||||
/**
|
||||
* Finished creating menu
|
||||
*
|
||||
* @param path Menu path
|
||||
*/
|
||||
public void finishMenu(String path);
|
||||
|
||||
/**
|
||||
* Does this builder support actions for menus? (not menuitems)
|
||||
*
|
||||
* @return True if supports
|
||||
*/
|
||||
public boolean supportsMenuAction();
|
||||
|
||||
/**
|
||||
* Does this builder support application menu (Path "_/...")
|
||||
*
|
||||
* @return True if supports
|
||||
*/
|
||||
public boolean supportsAppMenu();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,184 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.abc;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.types.Namespace;
|
||||
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
|
||||
import com.jpexs.decompiler.flash.gui.AppDialog;
|
||||
import com.jpexs.decompiler.flash.gui.AppStrings;
|
||||
import com.jpexs.decompiler.flash.gui.View;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.AncestorEvent;
|
||||
import javax.swing.event.AncestorListener;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class NewTraitDialog extends AppDialog {
|
||||
|
||||
private static final int modifiers[] = new int[]{
|
||||
Namespace.KIND_PACKAGE,
|
||||
Namespace.KIND_PRIVATE,
|
||||
Namespace.KIND_PROTECTED,
|
||||
Namespace.KIND_NAMESPACE,
|
||||
Namespace.KIND_PACKAGE_INTERNAL,
|
||||
Namespace.KIND_EXPLICIT,
|
||||
Namespace.KIND_STATIC_PROTECTED
|
||||
};
|
||||
|
||||
private static final int types[] = new int[]{
|
||||
Trait.TRAIT_METHOD,
|
||||
Trait.TRAIT_GETTER,
|
||||
Trait.TRAIT_SETTER,
|
||||
Trait.TRAIT_CONST,
|
||||
Trait.TRAIT_SLOT
|
||||
};
|
||||
|
||||
private final JComboBox<String> accessComboBox;
|
||||
|
||||
private final JComboBox<String> typeComboBox;
|
||||
|
||||
private final JCheckBox staticCheckbox;
|
||||
|
||||
private final JTextField nameField;
|
||||
|
||||
private int result = ERROR_OPTION;
|
||||
|
||||
public boolean getStatic() {
|
||||
return staticCheckbox.isSelected();
|
||||
}
|
||||
|
||||
public int getNamespaceKind() {
|
||||
return modifiers[accessComboBox.getSelectedIndex()];
|
||||
}
|
||||
|
||||
public int getTraitType() {
|
||||
return types[typeComboBox.getSelectedIndex()];
|
||||
}
|
||||
|
||||
public String getTraitName() {
|
||||
return nameField.getText();
|
||||
}
|
||||
|
||||
public NewTraitDialog() {
|
||||
setSize(500, 300);
|
||||
setTitle(translate("dialog.title"));
|
||||
View.centerScreen(this);
|
||||
View.setWindowIcon(this);
|
||||
Container cnt = getContentPane();
|
||||
cnt.setLayout(new BorderLayout());
|
||||
JPanel optionsPanel = new JPanel(new FlowLayout());
|
||||
//optionsPanel.add(new JLabel(translate("label.type")));
|
||||
typeComboBox = new JComboBox<>(new String[]{
|
||||
translate("type.method"),
|
||||
translate("type.getter"),
|
||||
translate("type.setter"),
|
||||
translate("type.const"),
|
||||
translate("type.slot"),});
|
||||
staticCheckbox = new JCheckBox(translate("checkbox.static"));
|
||||
optionsPanel.add(staticCheckbox);
|
||||
String accessStrings[] = new String[modifiers.length];
|
||||
for (int i = 0; i < accessStrings.length; i++) {
|
||||
String pref = Namespace.kindToPrefix(modifiers[i]);
|
||||
String name = Namespace.kindToStr(modifiers[i]);
|
||||
accessStrings[i] = (pref.isEmpty() ? "" : pref + " ") + "(" + name + ")";
|
||||
}
|
||||
|
||||
//optionsPanel.add(new JLabel(translate("label.access")));
|
||||
accessComboBox = new JComboBox<>(accessStrings);
|
||||
optionsPanel.add(accessComboBox);
|
||||
|
||||
optionsPanel.add(typeComboBox);
|
||||
|
||||
//optionsPanel.add(new JLabel(translate("label.name")));
|
||||
nameField = new JTextField();
|
||||
nameField.setPreferredSize(new Dimension(300, nameField.getPreferredSize().height));
|
||||
optionsPanel.add(nameField);
|
||||
|
||||
cnt.add(optionsPanel, BorderLayout.CENTER);
|
||||
JPanel buttonsPanel = new JPanel(new FlowLayout());
|
||||
JButton buttonOk = new JButton(AppStrings.translate("button.ok"));
|
||||
buttonOk.addActionListener(this::okButtonActionPerformed);
|
||||
JButton buttonCancel = new JButton(AppStrings.translate("button.cancel"));
|
||||
buttonCancel.addActionListener(this::cancelButtonActionPerformed);
|
||||
buttonsPanel.add(buttonOk);
|
||||
buttonsPanel.add(buttonCancel);
|
||||
cnt.add(buttonsPanel, BorderLayout.SOUTH);
|
||||
pack();
|
||||
setDefaultCloseOperation(HIDE_ON_CLOSE);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
|
||||
nameField.addAncestorListener(new AncestorListener() {
|
||||
@Override
|
||||
public void ancestorAdded(AncestorEvent event) {
|
||||
JComponent component = event.getComponent();
|
||||
component.requestFocusInWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ancestorRemoved(AncestorEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ancestorMoved(AncestorEvent event) {
|
||||
}
|
||||
});
|
||||
getRootPane().setDefaultButton(buttonOk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
if (b) {
|
||||
result = ERROR_OPTION;
|
||||
nameField.setText("");
|
||||
}
|
||||
|
||||
super.setVisible(b);
|
||||
}
|
||||
|
||||
private void okButtonActionPerformed(ActionEvent evt) {
|
||||
result = OK_OPTION;
|
||||
if (nameField.getText().trim().isEmpty()) {
|
||||
View.showMessageDialog(null, translate("error.name"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void cancelButtonActionPerformed(ActionEvent evt) {
|
||||
result = CANCEL_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
public int showDialog() {
|
||||
setVisible(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.abc;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.types.Namespace;
|
||||
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
|
||||
import com.jpexs.decompiler.flash.gui.AppDialog;
|
||||
import com.jpexs.decompiler.flash.gui.AppStrings;
|
||||
import com.jpexs.decompiler.flash.gui.View;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.AncestorEvent;
|
||||
import javax.swing.event.AncestorListener;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class NewTraitDialog extends AppDialog {
|
||||
|
||||
private static final int[] modifiers = new int[]{
|
||||
Namespace.KIND_PACKAGE,
|
||||
Namespace.KIND_PRIVATE,
|
||||
Namespace.KIND_PROTECTED,
|
||||
Namespace.KIND_NAMESPACE,
|
||||
Namespace.KIND_PACKAGE_INTERNAL,
|
||||
Namespace.KIND_EXPLICIT,
|
||||
Namespace.KIND_STATIC_PROTECTED
|
||||
};
|
||||
|
||||
private static final int[] types = new int[]{
|
||||
Trait.TRAIT_METHOD,
|
||||
Trait.TRAIT_GETTER,
|
||||
Trait.TRAIT_SETTER,
|
||||
Trait.TRAIT_CONST,
|
||||
Trait.TRAIT_SLOT
|
||||
};
|
||||
|
||||
private final JComboBox<String> accessComboBox;
|
||||
|
||||
private final JComboBox<String> typeComboBox;
|
||||
|
||||
private final JCheckBox staticCheckbox;
|
||||
|
||||
private final JTextField nameField;
|
||||
|
||||
private int result = ERROR_OPTION;
|
||||
|
||||
public boolean getStatic() {
|
||||
return staticCheckbox.isSelected();
|
||||
}
|
||||
|
||||
public int getNamespaceKind() {
|
||||
return modifiers[accessComboBox.getSelectedIndex()];
|
||||
}
|
||||
|
||||
public int getTraitType() {
|
||||
return types[typeComboBox.getSelectedIndex()];
|
||||
}
|
||||
|
||||
public String getTraitName() {
|
||||
return nameField.getText();
|
||||
}
|
||||
|
||||
public NewTraitDialog() {
|
||||
setSize(500, 300);
|
||||
setTitle(translate("dialog.title"));
|
||||
View.centerScreen(this);
|
||||
View.setWindowIcon(this);
|
||||
Container cnt = getContentPane();
|
||||
cnt.setLayout(new BorderLayout());
|
||||
JPanel optionsPanel = new JPanel(new FlowLayout());
|
||||
//optionsPanel.add(new JLabel(translate("label.type")));
|
||||
typeComboBox = new JComboBox<>(new String[]{
|
||||
translate("type.method"),
|
||||
translate("type.getter"),
|
||||
translate("type.setter"),
|
||||
translate("type.const"),
|
||||
translate("type.slot"),});
|
||||
staticCheckbox = new JCheckBox(translate("checkbox.static"));
|
||||
optionsPanel.add(staticCheckbox);
|
||||
String[] accessStrings = new String[modifiers.length];
|
||||
for (int i = 0; i < accessStrings.length; i++) {
|
||||
String pref = Namespace.kindToPrefix(modifiers[i]);
|
||||
String name = Namespace.kindToStr(modifiers[i]);
|
||||
accessStrings[i] = (pref.isEmpty() ? "" : pref + " ") + "(" + name + ")";
|
||||
}
|
||||
|
||||
//optionsPanel.add(new JLabel(translate("label.access")));
|
||||
accessComboBox = new JComboBox<>(accessStrings);
|
||||
optionsPanel.add(accessComboBox);
|
||||
|
||||
optionsPanel.add(typeComboBox);
|
||||
|
||||
//optionsPanel.add(new JLabel(translate("label.name")));
|
||||
nameField = new JTextField();
|
||||
nameField.setPreferredSize(new Dimension(300, nameField.getPreferredSize().height));
|
||||
optionsPanel.add(nameField);
|
||||
|
||||
cnt.add(optionsPanel, BorderLayout.CENTER);
|
||||
JPanel buttonsPanel = new JPanel(new FlowLayout());
|
||||
JButton buttonOk = new JButton(AppStrings.translate("button.ok"));
|
||||
buttonOk.addActionListener(this::okButtonActionPerformed);
|
||||
JButton buttonCancel = new JButton(AppStrings.translate("button.cancel"));
|
||||
buttonCancel.addActionListener(this::cancelButtonActionPerformed);
|
||||
buttonsPanel.add(buttonOk);
|
||||
buttonsPanel.add(buttonCancel);
|
||||
cnt.add(buttonsPanel, BorderLayout.SOUTH);
|
||||
pack();
|
||||
setDefaultCloseOperation(HIDE_ON_CLOSE);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
|
||||
nameField.addAncestorListener(new AncestorListener() {
|
||||
@Override
|
||||
public void ancestorAdded(AncestorEvent event) {
|
||||
JComponent component = event.getComponent();
|
||||
component.requestFocusInWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ancestorRemoved(AncestorEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ancestorMoved(AncestorEvent event) {
|
||||
}
|
||||
});
|
||||
getRootPane().setDefaultButton(buttonOk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
if (b) {
|
||||
result = ERROR_OPTION;
|
||||
nameField.setText("");
|
||||
}
|
||||
|
||||
super.setVisible(b);
|
||||
}
|
||||
|
||||
private void okButtonActionPerformed(ActionEvent evt) {
|
||||
result = OK_OPTION;
|
||||
if (nameField.getText().trim().isEmpty()) {
|
||||
View.showMessageDialog(null, translate("error.name"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void cancelButtonActionPerformed(ActionEvent evt) {
|
||||
result = CANCEL_OPTION;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
public int showDialog() {
|
||||
setVisible(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,308 +1,308 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.debugger;
|
||||
|
||||
import com.jpexs.helpers.utf8.Utf8Helper;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Debugger {
|
||||
|
||||
private static final Set<DebugListener> listeners = new HashSet<>();
|
||||
|
||||
public synchronized void addMessageListener(DebugListener l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
public synchronized void removeMessageListener(DebugListener l) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
private static class DebugHandler extends Thread {
|
||||
|
||||
private final Socket s;
|
||||
|
||||
private final int serverPort;
|
||||
|
||||
private static int maxid = 0;
|
||||
|
||||
private final int id;
|
||||
|
||||
public boolean finished = false;
|
||||
|
||||
private final Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
public static final int MSG_STRING = 0;
|
||||
|
||||
public static final int MSG_LOADER_URL = 1;
|
||||
|
||||
public static final int MSG_LOADER_BYTES = 2;
|
||||
|
||||
public String getParameter(String name, String defValue) {
|
||||
if (parameters.containsKey(name)) {
|
||||
return parameters.get(name);
|
||||
}
|
||||
return defValue;
|
||||
}
|
||||
|
||||
public int getVersionMajor() {
|
||||
return Integer.parseInt(getParameter("debug.version.major", "1"));
|
||||
}
|
||||
|
||||
public int getVersionMinor() {
|
||||
return Integer.parseInt(getParameter("debug.version.major", "0"));
|
||||
}
|
||||
|
||||
public boolean hasMsgType() {
|
||||
return getVersionMajor() > 1 || getVersionMinor() > 0;
|
||||
}
|
||||
|
||||
public DebugHandler(int serverPort, Socket s) {
|
||||
this.s = s;
|
||||
id = maxid++;
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
try {
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
private int readType(InputStream is) throws IOException {
|
||||
int type = is.read();
|
||||
if (type == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private byte[] readBytes(InputStream is) throws IOException {
|
||||
int len = is.read();
|
||||
if (len == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len2 = is.read();
|
||||
if (len2 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len3 = is.read();
|
||||
if (len3 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len4 = is.read();
|
||||
if (len4 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
len = (len << 24) + (len2 << 16) + (len3 << 8) + len4;
|
||||
byte data[] = new byte[len];
|
||||
int cnt;
|
||||
int off = 0;
|
||||
while (len > 0 && (cnt = is.read(data, off, len)) > 0) {
|
||||
len -= cnt;
|
||||
off += cnt;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private String readString(InputStream is) throws IOException {
|
||||
int len = is.read();
|
||||
if (len == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len2 = is.read();
|
||||
if (len2 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
len = (len << 8) + len2;
|
||||
|
||||
byte buf[] = new byte[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
int rd = is.read();
|
||||
if (rd == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
buf[i] = (byte) rd;
|
||||
}
|
||||
return new String(buf, Utf8Helper.charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String clientName = Integer.toString(id);
|
||||
try (InputStream is = s.getInputStream()) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
int c;
|
||||
do {
|
||||
c = is.read();
|
||||
if (c != 0) {
|
||||
baos.write(c);
|
||||
}
|
||||
|
||||
} while (c > 0);
|
||||
String ret = baos.toString("UTF-8");
|
||||
|
||||
if (ret.equals("<policy-file-request/>")) {
|
||||
try (OutputStream os = s.getOutputStream()) {
|
||||
os.write(("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"" + serverPort + "\" /></cross-domain-policy>").getBytes("UTF-8"));
|
||||
}
|
||||
} else {
|
||||
if (!ret.isEmpty()) {
|
||||
String param[] = (ret.contains(";") ? ret.split(";") : new String[]{ret});
|
||||
for (String p : param) {
|
||||
if (p.contains("=")) {
|
||||
String key = p.substring(0, p.indexOf('='));
|
||||
String val = p.substring(p.indexOf('=') + 1);
|
||||
parameters.put(key, val);
|
||||
} else {
|
||||
parameters.put(p, "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean hasType = hasMsgType();
|
||||
String name = readString(is);
|
||||
if (!name.isEmpty()) {
|
||||
clientName = name;
|
||||
}
|
||||
while (true) {
|
||||
int type = 0;
|
||||
if (hasType) {
|
||||
type = readType(is);
|
||||
}
|
||||
switch (type) {
|
||||
case MSG_STRING:
|
||||
ret = readString(is);
|
||||
for (DebugListener l : listeners) {
|
||||
l.onMessage(clientName, ret);
|
||||
}
|
||||
break;
|
||||
case MSG_LOADER_URL:
|
||||
ret = readString(is);
|
||||
for (DebugListener l : listeners) {
|
||||
l.onLoaderURL(clientName, ret);
|
||||
}
|
||||
break;
|
||||
case MSG_LOADER_BYTES:
|
||||
byte retB[] = readBytes(is);
|
||||
for (DebugListener l : listeners) {
|
||||
l.onLoaderBytes(clientName, retB);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
try {
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
finished = true;
|
||||
for (DebugListener l : listeners) {
|
||||
l.onFinish(clientName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DebugServerThread extends Thread {
|
||||
|
||||
private final int port;
|
||||
|
||||
private ServerSocket ss;
|
||||
|
||||
private final Map<Integer, DebugHandler> handlers = new WeakHashMap<>();
|
||||
|
||||
public DebugServerThread(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ss = new ServerSocket(port, 50, InetAddress.getByName("localhost"));
|
||||
ss.setReuseAddress(true);
|
||||
while (true) {
|
||||
Socket s = ss.accept();
|
||||
DebugHandler h = new DebugHandler(port, s);
|
||||
handlers.put(h.id, h);
|
||||
h.start();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final int port;
|
||||
|
||||
public Debugger(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
private DebugServerThread server = null;
|
||||
|
||||
public synchronized void start() {
|
||||
if (server == null) {
|
||||
server = new DebugServerThread(port);
|
||||
server.start();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isRunning() {
|
||||
return server != null;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
if (server != null) {
|
||||
try {
|
||||
server.ss.close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
for (DebugHandler h : server.handlers.values()) {
|
||||
h.cancel();
|
||||
}
|
||||
server.handlers.clear();
|
||||
server = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.debugger;
|
||||
|
||||
import com.jpexs.helpers.utf8.Utf8Helper;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Debugger {
|
||||
|
||||
private static final Set<DebugListener> listeners = new HashSet<>();
|
||||
|
||||
public synchronized void addMessageListener(DebugListener l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
public synchronized void removeMessageListener(DebugListener l) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
private static class DebugHandler extends Thread {
|
||||
|
||||
private final Socket s;
|
||||
|
||||
private final int serverPort;
|
||||
|
||||
private static int maxid = 0;
|
||||
|
||||
private final int id;
|
||||
|
||||
public boolean finished = false;
|
||||
|
||||
private final Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
public static final int MSG_STRING = 0;
|
||||
|
||||
public static final int MSG_LOADER_URL = 1;
|
||||
|
||||
public static final int MSG_LOADER_BYTES = 2;
|
||||
|
||||
public String getParameter(String name, String defValue) {
|
||||
if (parameters.containsKey(name)) {
|
||||
return parameters.get(name);
|
||||
}
|
||||
return defValue;
|
||||
}
|
||||
|
||||
public int getVersionMajor() {
|
||||
return Integer.parseInt(getParameter("debug.version.major", "1"));
|
||||
}
|
||||
|
||||
public int getVersionMinor() {
|
||||
return Integer.parseInt(getParameter("debug.version.major", "0"));
|
||||
}
|
||||
|
||||
public boolean hasMsgType() {
|
||||
return getVersionMajor() > 1 || getVersionMinor() > 0;
|
||||
}
|
||||
|
||||
public DebugHandler(int serverPort, Socket s) {
|
||||
this.s = s;
|
||||
id = maxid++;
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
try {
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
private int readType(InputStream is) throws IOException {
|
||||
int type = is.read();
|
||||
if (type == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private byte[] readBytes(InputStream is) throws IOException {
|
||||
int len = is.read();
|
||||
if (len == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len2 = is.read();
|
||||
if (len2 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len3 = is.read();
|
||||
if (len3 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len4 = is.read();
|
||||
if (len4 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
len = (len << 24) + (len2 << 16) + (len3 << 8) + len4;
|
||||
byte[] data = new byte[len];
|
||||
int cnt;
|
||||
int off = 0;
|
||||
while (len > 0 && (cnt = is.read(data, off, len)) > 0) {
|
||||
len -= cnt;
|
||||
off += cnt;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private String readString(InputStream is) throws IOException {
|
||||
int len = is.read();
|
||||
if (len == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
int len2 = is.read();
|
||||
if (len2 == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
len = (len << 8) + len2;
|
||||
|
||||
byte[] buf = new byte[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
int rd = is.read();
|
||||
if (rd == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
buf[i] = (byte) rd;
|
||||
}
|
||||
return new String(buf, Utf8Helper.charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String clientName = Integer.toString(id);
|
||||
try (InputStream is = s.getInputStream()) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
int c;
|
||||
do {
|
||||
c = is.read();
|
||||
if (c != 0) {
|
||||
baos.write(c);
|
||||
}
|
||||
|
||||
} while (c > 0);
|
||||
String ret = baos.toString("UTF-8");
|
||||
|
||||
if (ret.equals("<policy-file-request/>")) {
|
||||
try (OutputStream os = s.getOutputStream()) {
|
||||
os.write(("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"" + serverPort + "\" /></cross-domain-policy>").getBytes("UTF-8"));
|
||||
}
|
||||
} else {
|
||||
if (!ret.isEmpty()) {
|
||||
String[] param = (ret.contains(";") ? ret.split(";") : new String[]{ret});
|
||||
for (String p : param) {
|
||||
if (p.contains("=")) {
|
||||
String key = p.substring(0, p.indexOf('='));
|
||||
String val = p.substring(p.indexOf('=') + 1);
|
||||
parameters.put(key, val);
|
||||
} else {
|
||||
parameters.put(p, "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean hasType = hasMsgType();
|
||||
String name = readString(is);
|
||||
if (!name.isEmpty()) {
|
||||
clientName = name;
|
||||
}
|
||||
while (true) {
|
||||
int type = 0;
|
||||
if (hasType) {
|
||||
type = readType(is);
|
||||
}
|
||||
switch (type) {
|
||||
case MSG_STRING:
|
||||
ret = readString(is);
|
||||
for (DebugListener l : listeners) {
|
||||
l.onMessage(clientName, ret);
|
||||
}
|
||||
break;
|
||||
case MSG_LOADER_URL:
|
||||
ret = readString(is);
|
||||
for (DebugListener l : listeners) {
|
||||
l.onLoaderURL(clientName, ret);
|
||||
}
|
||||
break;
|
||||
case MSG_LOADER_BYTES:
|
||||
byte[] retB = readBytes(is);
|
||||
for (DebugListener l : listeners) {
|
||||
l.onLoaderBytes(clientName, retB);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
try {
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
finished = true;
|
||||
for (DebugListener l : listeners) {
|
||||
l.onFinish(clientName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DebugServerThread extends Thread {
|
||||
|
||||
private final int port;
|
||||
|
||||
private ServerSocket ss;
|
||||
|
||||
private final Map<Integer, DebugHandler> handlers = new WeakHashMap<>();
|
||||
|
||||
public DebugServerThread(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ss = new ServerSocket(port, 50, InetAddress.getByName("localhost"));
|
||||
ss.setReuseAddress(true);
|
||||
while (true) {
|
||||
Socket s = ss.accept();
|
||||
DebugHandler h = new DebugHandler(port, s);
|
||||
handlers.put(h.id, h);
|
||||
h.start();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final int port;
|
||||
|
||||
public Debugger(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
private DebugServerThread server = null;
|
||||
|
||||
public synchronized void start() {
|
||||
if (server == null) {
|
||||
server = new DebugServerThread(port);
|
||||
server.start();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isRunning() {
|
||||
return server != null;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
if (server != null) {
|
||||
try {
|
||||
server.ss.close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
for (DebugHandler h : server.handlers.values()) {
|
||||
h.cancel();
|
||||
}
|
||||
server.handlers.clear();
|
||||
server = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +1,247 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.debugger;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.abc.types.Multiname;
|
||||
import com.jpexs.decompiler.flash.abc.types.Namespace;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.gui.DebugLogDialog;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class DebuggerTools {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DebuggerTools.class.getName());
|
||||
|
||||
public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger";
|
||||
|
||||
private static volatile Debugger debugger;
|
||||
|
||||
private static ScriptPack getDebuggerScriptPack(SWF swf) {
|
||||
List<ABC> allAbcList = new ArrayList<>();
|
||||
for (ABCContainerTag ac : swf.getAbcList()) {
|
||||
allAbcList.add(ac.getABC());
|
||||
}
|
||||
for (ABCContainerTag ac : swf.getAbcList()) {
|
||||
ABC a = ac.getABC();
|
||||
for (ScriptPack m : a.getScriptPacks(DEBUGGER_PACKAGE, allAbcList)) {
|
||||
if (isDebuggerClass(m.getClassPath().packageStr.toRawString(), null)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean hasDebugger(SWF swf) {
|
||||
return getDebuggerScriptPack(swf) != null;
|
||||
}
|
||||
|
||||
private static boolean isDebuggerClass(String tested, String cls) {
|
||||
if (tested == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fast check, because dynamic regex compile and match is expensive
|
||||
if (!tested.startsWith(DEBUGGER_PACKAGE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls == null) {
|
||||
cls = "";
|
||||
} else {
|
||||
cls = "\\." + Pattern.quote(cls);
|
||||
}
|
||||
|
||||
return tested.matches(Pattern.quote(DEBUGGER_PACKAGE) + "(\\.pkg[a-f0-9]+)?" + cls);
|
||||
}
|
||||
|
||||
public static void injectDebugLoader(SWF swf) {
|
||||
if (hasDebugger(swf)) {
|
||||
ScriptPack dsp = getDebuggerScriptPack(swf);
|
||||
String debuggerPkg = dsp.getClassPath().packageStr.toRawString();
|
||||
for (ABCContainerTag ct : swf.getAbcList()) {
|
||||
ABC a = ct.getABC();
|
||||
if (dsp.abc == a) { //do not replace Loader in debugger itself
|
||||
continue;
|
||||
}
|
||||
for (int i = 1; i < a.constants.getMultinameCount(); i++) {
|
||||
Multiname m = a.constants.getMultiname(i);
|
||||
if ("flash.display.Loader".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("DebugLoader", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.getDefinitionByName".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugGetDefinitionByName", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.getQualifiedClassName".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugGetQualifiedClassName", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.getQualifiedSuperclassName".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugGetQualifiedSuperclassName", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.describeType".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugDescribeType", true);
|
||||
((Tag) ct).setModified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void replaceTraceCalls(SWF swf, String fname) {
|
||||
if (hasDebugger(swf)) {
|
||||
String debuggerPkg = getDebuggerScriptPack(swf).getClassPath().packageStr.toRawString();
|
||||
//change trace to fname
|
||||
for (ABCContainerTag ct : swf.getAbcList()) {
|
||||
ABC a = ct.getABC();
|
||||
for (int i = 1; i < a.constants.getMultinameCount(); i++) {
|
||||
Multiname m = a.constants.getMultiname(i);
|
||||
if ("trace".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId(fname, true);
|
||||
((Tag) ct).setModified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void switchDebugger(SWF swf) {
|
||||
int port = Configuration.debuggerPort.get();
|
||||
ScriptPack found = getDebuggerScriptPack(swf);
|
||||
if (found != null) {
|
||||
ABCContainerTag tag = found.abc.parentTag;
|
||||
swf.removeTag((Tag) tag);
|
||||
swf.getAbcList().remove(tag);
|
||||
|
||||
//Change all debugger calls to normal trace / Loader
|
||||
for (ABCContainerTag ct : swf.getAbcList()) {
|
||||
ABC a = ct.getABC();
|
||||
for (int i = 1; i < a.constants.getMultinameCount(); i++) {
|
||||
Multiname m = a.constants.getMultiname(i);
|
||||
String packageStr = m.getNameWithNamespace(a.constants).toString();
|
||||
if (isDebuggerClass(packageStr, "debugTrace")
|
||||
|| isDebuggerClass(packageStr, "debugAlert")
|
||||
|| isDebuggerClass(packageStr, "debugSocket")
|
||||
|| isDebuggerClass(packageStr, "debugConsole")) {
|
||||
m.name_index = a.constants.getStringId("trace", true);
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if (isDebuggerClass(packageStr, "DebugLoader")) {
|
||||
m.name_index = a.constants.getStringId("Loader", true);
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "flash.display", 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Random rnd = new Random();
|
||||
byte rb[] = new byte[16];
|
||||
rnd.nextBytes(rb);
|
||||
String rhex = Helper.byteArrayToHex(rb);
|
||||
try {
|
||||
//load debug swf
|
||||
SWF debugSWF = new SWF(Main.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/gui/debugger/debug.swf"), false);
|
||||
|
||||
List<ABCContainerTag> al = swf.getAbcList();
|
||||
ABCContainerTag firstAbc = al.isEmpty() ? null : al.get(0);
|
||||
if (firstAbc == null) { //nothing to instrument?
|
||||
return;
|
||||
}
|
||||
String newdebuggerpkg = DEBUGGER_PACKAGE;
|
||||
|
||||
if (Configuration.randomDebuggerPackage.get()) {
|
||||
newdebuggerpkg += ".pkg" + rhex;
|
||||
}
|
||||
|
||||
//add debug ABC tags to main SWF
|
||||
for (ABCContainerTag ds : debugSWF.getAbcList()) {
|
||||
ABC a = ds.getABC();
|
||||
//Append random hex to Debugger package name
|
||||
for (int i = 1; i < a.constants.getNamespaceCount(); i++) {
|
||||
if (a.constants.getNamespace(i).hasName(DEBUGGER_PACKAGE, a.constants)) {
|
||||
a.constants.getNamespace(i).name_index = a.constants.getStringId(newdebuggerpkg, true);
|
||||
}
|
||||
}
|
||||
//Set debugger port to actually set port
|
||||
for (int i = 0; i < a.constants.getIntCount(); i++) {
|
||||
if (a.constants.getInt(i) == 123456L) {
|
||||
a.constants.setInt(i, (long) port);
|
||||
}
|
||||
}
|
||||
//Add to target SWF
|
||||
((Tag) ds).setSwf(swf);
|
||||
swf.addTag((Tag) ds, (Tag) firstAbc);
|
||||
swf.getAbcList().add(swf.getAbcList().indexOf(firstAbc), ds);
|
||||
((Tag) ds).setModified(true);
|
||||
|
||||
//To allow socket connection to FFDec. Is this safe?
|
||||
FileAttributesTag ft = swf.getFileAttributes();
|
||||
ft.useNetwork = true;
|
||||
ft.setModified(true);
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error while attaching debugger", ex);
|
||||
//ignore
|
||||
}
|
||||
|
||||
}
|
||||
initDebugger();
|
||||
}
|
||||
|
||||
public static Debugger initDebugger() {
|
||||
if (debugger == null) {
|
||||
synchronized (Main.class) {
|
||||
if (debugger == null) {
|
||||
Debugger dbg = new Debugger(Configuration.debuggerPort.get());
|
||||
dbg.start();
|
||||
debugger = dbg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return debugger;
|
||||
}
|
||||
|
||||
public static void debuggerShowLog() {
|
||||
initDebugger();
|
||||
if (Main.debugDialog == null) {
|
||||
Main.debugDialog = new DebugLogDialog(debugger);
|
||||
}
|
||||
Main.debugDialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.debugger;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.abc.types.Multiname;
|
||||
import com.jpexs.decompiler.flash.abc.types.Namespace;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.gui.DebugLogDialog;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class DebuggerTools {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DebuggerTools.class.getName());
|
||||
|
||||
public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger";
|
||||
|
||||
private static volatile Debugger debugger;
|
||||
|
||||
private static ScriptPack getDebuggerScriptPack(SWF swf) {
|
||||
List<ABC> allAbcList = new ArrayList<>();
|
||||
for (ABCContainerTag ac : swf.getAbcList()) {
|
||||
allAbcList.add(ac.getABC());
|
||||
}
|
||||
for (ABCContainerTag ac : swf.getAbcList()) {
|
||||
ABC a = ac.getABC();
|
||||
for (ScriptPack m : a.getScriptPacks(DEBUGGER_PACKAGE, allAbcList)) {
|
||||
if (isDebuggerClass(m.getClassPath().packageStr.toRawString(), null)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean hasDebugger(SWF swf) {
|
||||
return getDebuggerScriptPack(swf) != null;
|
||||
}
|
||||
|
||||
private static boolean isDebuggerClass(String tested, String cls) {
|
||||
if (tested == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fast check, because dynamic regex compile and match is expensive
|
||||
if (!tested.startsWith(DEBUGGER_PACKAGE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls == null) {
|
||||
cls = "";
|
||||
} else {
|
||||
cls = "\\." + Pattern.quote(cls);
|
||||
}
|
||||
|
||||
return tested.matches(Pattern.quote(DEBUGGER_PACKAGE) + "(\\.pkg[a-f0-9]+)?" + cls);
|
||||
}
|
||||
|
||||
public static void injectDebugLoader(SWF swf) {
|
||||
if (hasDebugger(swf)) {
|
||||
ScriptPack dsp = getDebuggerScriptPack(swf);
|
||||
String debuggerPkg = dsp.getClassPath().packageStr.toRawString();
|
||||
for (ABCContainerTag ct : swf.getAbcList()) {
|
||||
ABC a = ct.getABC();
|
||||
if (dsp.abc == a) { //do not replace Loader in debugger itself
|
||||
continue;
|
||||
}
|
||||
for (int i = 1; i < a.constants.getMultinameCount(); i++) {
|
||||
Multiname m = a.constants.getMultiname(i);
|
||||
if ("flash.display.Loader".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("DebugLoader", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.getDefinitionByName".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugGetDefinitionByName", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.getQualifiedClassName".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugGetQualifiedClassName", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.getQualifiedSuperclassName".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugGetQualifiedSuperclassName", true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if ("flash.utils.describeType".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId("debugDescribeType", true);
|
||||
((Tag) ct).setModified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void replaceTraceCalls(SWF swf, String fname) {
|
||||
if (hasDebugger(swf)) {
|
||||
String debuggerPkg = getDebuggerScriptPack(swf).getClassPath().packageStr.toRawString();
|
||||
//change trace to fname
|
||||
for (ABCContainerTag ct : swf.getAbcList()) {
|
||||
ABC a = ct.getABC();
|
||||
for (int i = 1; i < a.constants.getMultinameCount(); i++) {
|
||||
Multiname m = a.constants.getMultiname(i);
|
||||
if ("trace".equals(m.getNameWithNamespace(a.constants).toRawString())) {
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true);
|
||||
m.name_index = a.constants.getStringId(fname, true);
|
||||
((Tag) ct).setModified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void switchDebugger(SWF swf) {
|
||||
int port = Configuration.debuggerPort.get();
|
||||
ScriptPack found = getDebuggerScriptPack(swf);
|
||||
if (found != null) {
|
||||
ABCContainerTag tag = found.abc.parentTag;
|
||||
swf.removeTag((Tag) tag);
|
||||
swf.getAbcList().remove(tag);
|
||||
|
||||
//Change all debugger calls to normal trace / Loader
|
||||
for (ABCContainerTag ct : swf.getAbcList()) {
|
||||
ABC a = ct.getABC();
|
||||
for (int i = 1; i < a.constants.getMultinameCount(); i++) {
|
||||
Multiname m = a.constants.getMultiname(i);
|
||||
String packageStr = m.getNameWithNamespace(a.constants).toString();
|
||||
if (isDebuggerClass(packageStr, "debugTrace")
|
||||
|| isDebuggerClass(packageStr, "debugAlert")
|
||||
|| isDebuggerClass(packageStr, "debugSocket")
|
||||
|| isDebuggerClass(packageStr, "debugConsole")) {
|
||||
m.name_index = a.constants.getStringId("trace", true);
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true);
|
||||
((Tag) ct).setModified(true);
|
||||
} else if (isDebuggerClass(packageStr, "DebugLoader")) {
|
||||
m.name_index = a.constants.getStringId("Loader", true);
|
||||
m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "flash.display", 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Random rnd = new Random();
|
||||
byte[] rb = new byte[16];
|
||||
rnd.nextBytes(rb);
|
||||
String rhex = Helper.byteArrayToHex(rb);
|
||||
try {
|
||||
//load debug swf
|
||||
SWF debugSWF = new SWF(Main.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/gui/debugger/debug.swf"), false);
|
||||
|
||||
List<ABCContainerTag> al = swf.getAbcList();
|
||||
ABCContainerTag firstAbc = al.isEmpty() ? null : al.get(0);
|
||||
if (firstAbc == null) { //nothing to instrument?
|
||||
return;
|
||||
}
|
||||
String newdebuggerpkg = DEBUGGER_PACKAGE;
|
||||
|
||||
if (Configuration.randomDebuggerPackage.get()) {
|
||||
newdebuggerpkg += ".pkg" + rhex;
|
||||
}
|
||||
|
||||
//add debug ABC tags to main SWF
|
||||
for (ABCContainerTag ds : debugSWF.getAbcList()) {
|
||||
ABC a = ds.getABC();
|
||||
//Append random hex to Debugger package name
|
||||
for (int i = 1; i < a.constants.getNamespaceCount(); i++) {
|
||||
if (a.constants.getNamespace(i).hasName(DEBUGGER_PACKAGE, a.constants)) {
|
||||
a.constants.getNamespace(i).name_index = a.constants.getStringId(newdebuggerpkg, true);
|
||||
}
|
||||
}
|
||||
//Set debugger port to actually set port
|
||||
for (int i = 0; i < a.constants.getIntCount(); i++) {
|
||||
if (a.constants.getInt(i) == 123456L) {
|
||||
a.constants.setInt(i, (long) port);
|
||||
}
|
||||
}
|
||||
//Add to target SWF
|
||||
((Tag) ds).setSwf(swf);
|
||||
swf.addTag((Tag) ds, (Tag) firstAbc);
|
||||
swf.getAbcList().add(swf.getAbcList().indexOf(firstAbc), ds);
|
||||
((Tag) ds).setModified(true);
|
||||
|
||||
//To allow socket connection to FFDec. Is this safe?
|
||||
FileAttributesTag ft = swf.getFileAttributes();
|
||||
ft.useNetwork = true;
|
||||
ft.setModified(true);
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error while attaching debugger", ex);
|
||||
//ignore
|
||||
}
|
||||
|
||||
}
|
||||
initDebugger();
|
||||
}
|
||||
|
||||
public static Debugger initDebugger() {
|
||||
if (debugger == null) {
|
||||
synchronized (Main.class) {
|
||||
if (debugger == null) {
|
||||
Debugger dbg = new Debugger(Configuration.debuggerPort.get());
|
||||
dbg.start();
|
||||
debugger = dbg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return debugger;
|
||||
}
|
||||
|
||||
public static void debuggerShowLog() {
|
||||
initDebugger();
|
||||
if (Main.debugDialog == null) {
|
||||
Main.debugDialog = new DebugLogDialog(debugger);
|
||||
}
|
||||
Main.debugDialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,458 +1,541 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.dumpview;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.SWFInputStream;
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.abc.ABCInputStream;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
|
||||
import com.jpexs.decompiler.flash.action.Action;
|
||||
import com.jpexs.decompiler.flash.action.ActionListReader;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
|
||||
import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import com.jpexs.decompiler.flash.gui.MainPanel;
|
||||
import com.jpexs.decompiler.flash.gui.TreeNodeType;
|
||||
import com.jpexs.decompiler.flash.gui.View;
|
||||
import com.jpexs.decompiler.flash.gui.tagtree.TagTree;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineButton2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineButtonTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineEditTextTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFont2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFont3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFontTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShape2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShape3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShape4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineText2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineTextTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
|
||||
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DoABCTag;
|
||||
import com.jpexs.decompiler.flash.tags.DoActionTag;
|
||||
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
|
||||
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
|
||||
import com.jpexs.decompiler.flash.tags.MetadataTag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObject3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObject4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObjectTag;
|
||||
import com.jpexs.decompiler.flash.tags.RemoveObject2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.RemoveObjectTag;
|
||||
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
|
||||
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
|
||||
import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag;
|
||||
import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.MemoryInputStream;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.plaf.basic.BasicLabelUI;
|
||||
import javax.swing.plaf.basic.BasicTreeUI;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class DumpTree extends JTree {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DumpTree.class.getName());
|
||||
|
||||
private final MainPanel mainPanel;
|
||||
|
||||
public class DumpTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
|
||||
public DumpTreeCellRenderer() {
|
||||
setUI(new BasicLabelUI());
|
||||
setOpaque(false);
|
||||
setBackgroundNonSelectionColor(Color.white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
||||
if (ret instanceof JLabel) {
|
||||
JLabel lab = (JLabel) ret;
|
||||
if (value instanceof DumpInfo) {
|
||||
DumpInfo di = (DumpInfo) value;
|
||||
TreeNodeType nodeType = null;
|
||||
if ("".equals(di.type)) {
|
||||
nodeType = TreeNodeType.FLASH;
|
||||
} else if ("TAG".equals(di.type)) {
|
||||
String name = di.name;
|
||||
if (name.contains(" ")) {
|
||||
name = name.substring(0, name.indexOf(' ')).trim();
|
||||
}
|
||||
switch (name) {
|
||||
case DefineFontTag.NAME:
|
||||
case DefineFont2Tag.NAME:
|
||||
case DefineFont3Tag.NAME:
|
||||
case DefineFont4Tag.NAME:
|
||||
case DefineCompactedFont.NAME:
|
||||
nodeType = TreeNodeType.FONT;
|
||||
break;
|
||||
case DefineTextTag.NAME:
|
||||
case DefineText2Tag.NAME:
|
||||
case DefineEditTextTag.NAME:
|
||||
nodeType = TreeNodeType.TEXT;
|
||||
break;
|
||||
case DefineBitsTag.NAME:
|
||||
case DefineBitsJPEG2Tag.NAME:
|
||||
case DefineBitsJPEG3Tag.NAME:
|
||||
case DefineBitsJPEG4Tag.NAME:
|
||||
case DefineBitsLosslessTag.NAME:
|
||||
case DefineBitsLossless2Tag.NAME:
|
||||
nodeType = TreeNodeType.IMAGE;
|
||||
break;
|
||||
case DefineShapeTag.NAME:
|
||||
case DefineShape2Tag.NAME:
|
||||
case DefineShape3Tag.NAME:
|
||||
case DefineShape4Tag.NAME:
|
||||
nodeType = TreeNodeType.SHAPE;
|
||||
break;
|
||||
case DefineMorphShapeTag.NAME:
|
||||
case DefineMorphShape2Tag.NAME:
|
||||
nodeType = TreeNodeType.MORPH_SHAPE;
|
||||
break;
|
||||
case DefineSpriteTag.NAME:
|
||||
nodeType = TreeNodeType.SPRITE;
|
||||
break;
|
||||
case DefineButtonTag.NAME:
|
||||
case DefineButton2Tag.NAME:
|
||||
nodeType = TreeNodeType.BUTTON;
|
||||
break;
|
||||
case DefineVideoStreamTag.NAME:
|
||||
nodeType = TreeNodeType.MOVIE;
|
||||
break;
|
||||
|
||||
case DefineSoundTag.NAME:
|
||||
case SoundStreamHeadTag.NAME:
|
||||
case SoundStreamHead2Tag.NAME:
|
||||
nodeType = TreeNodeType.SOUND;
|
||||
break;
|
||||
case DefineBinaryDataTag.NAME:
|
||||
nodeType = TreeNodeType.BINARY_DATA;
|
||||
break;
|
||||
case DoActionTag.NAME:
|
||||
case DoInitActionTag.NAME:
|
||||
case DoABCTag.NAME:
|
||||
case DoABC2Tag.NAME:
|
||||
nodeType = TreeNodeType.AS;
|
||||
break;
|
||||
case ShowFrameTag.NAME:
|
||||
nodeType = TreeNodeType.FRAME; //show_frame?
|
||||
break;
|
||||
case SetBackgroundColorTag.NAME:
|
||||
nodeType = TreeNodeType.SET_BACKGROUNDCOLOR;
|
||||
break;
|
||||
case FileAttributesTag.NAME:
|
||||
nodeType = TreeNodeType.FILE_ATTRIBUTES;
|
||||
break;
|
||||
case MetadataTag.NAME:
|
||||
nodeType = TreeNodeType.METADATA;
|
||||
break;
|
||||
case PlaceObjectTag.NAME:
|
||||
case PlaceObject2Tag.NAME:
|
||||
case PlaceObject3Tag.NAME:
|
||||
case PlaceObject4Tag.NAME:
|
||||
nodeType = TreeNodeType.PLACE_OBJECT;
|
||||
break;
|
||||
case RemoveObjectTag.NAME:
|
||||
case RemoveObject2Tag.NAME:
|
||||
nodeType = TreeNodeType.REMOVE_OBJECT;
|
||||
break;
|
||||
default:
|
||||
nodeType = TreeNodeType.OTHER_TAG;
|
||||
}
|
||||
}
|
||||
if (nodeType != null) {
|
||||
lab.setIcon(TagTree.getIconForType(nodeType));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public DumpTree(DumpTreeModel treeModel, MainPanel mainPanel) {
|
||||
super(treeModel);
|
||||
this.mainPanel = mainPanel;
|
||||
setCellRenderer(new DumpTreeCellRenderer());
|
||||
setRootVisible(false);
|
||||
setBackground(Color.white);
|
||||
setUI(new BasicTreeUI() {
|
||||
{
|
||||
setHashColor(Color.gray);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void createContextMenu() {
|
||||
final JPopupMenu contextPopupMenu = new JPopupMenu();
|
||||
|
||||
final JMenuItem expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll"));
|
||||
expandRecursiveMenuItem.addActionListener(this::expandRecursiveButtonActionPerformed);
|
||||
contextPopupMenu.add(expandRecursiveMenuItem);
|
||||
|
||||
final JMenuItem saveToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveToFile"));
|
||||
saveToFileMenuItem.addActionListener(this::saveToFileButtonActionPerformed);
|
||||
contextPopupMenu.add(saveToFileMenuItem);
|
||||
|
||||
final JMenuItem saveUncompressedToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveUncompressedToFile"));
|
||||
saveUncompressedToFileMenuItem.addActionListener(this::saveUncompressedToFileButtonActionPerformed);
|
||||
contextPopupMenu.add(saveUncompressedToFileMenuItem);
|
||||
|
||||
final JMenuItem closeSelectionMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf"));
|
||||
closeSelectionMenuItem.addActionListener(this::closeSwfButtonActionPerformed);
|
||||
contextPopupMenu.add(closeSelectionMenuItem);
|
||||
|
||||
final JMenuItem parseActionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseActions"));
|
||||
parseActionsMenuItem.addActionListener(this::parseActionsButtonActionPerformed);
|
||||
contextPopupMenu.add(parseActionsMenuItem);
|
||||
|
||||
final JMenuItem parseAbcMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseABC"));
|
||||
parseAbcMenuItem.addActionListener(this::parseAbcButtonActionPerformed);
|
||||
contextPopupMenu.add(parseAbcMenuItem);
|
||||
|
||||
final JMenuItem parseInstructionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseInstructions"));
|
||||
parseInstructionsMenuItem.addActionListener(this::parseInstructionsButtonActionPerformed);
|
||||
contextPopupMenu.add(parseInstructionsMenuItem);
|
||||
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (SwingUtilities.isRightMouseButton(e)) {
|
||||
|
||||
int row = getClosestRowForLocation(e.getX(), e.getY());
|
||||
int[] selectionRows = getSelectionRows();
|
||||
if (!Helper.contains(selectionRows, row)) {
|
||||
setSelectionRow(row);
|
||||
}
|
||||
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
if (paths == null || paths.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeSelectionMenuItem.setVisible(false);
|
||||
expandRecursiveMenuItem.setVisible(false);
|
||||
saveToFileMenuItem.setVisible(false);
|
||||
saveUncompressedToFileMenuItem.setVisible(false);
|
||||
parseActionsMenuItem.setVisible(false);
|
||||
parseAbcMenuItem.setVisible(false);
|
||||
parseInstructionsMenuItem.setVisible(false);
|
||||
|
||||
if (paths.length == 1) {
|
||||
DumpInfo treeNode = (DumpInfo) paths[0].getLastPathComponent();
|
||||
|
||||
if (treeNode instanceof DumpInfoSwfNode) {
|
||||
closeSelectionMenuItem.setVisible(true);
|
||||
}
|
||||
|
||||
if (treeNode.getEndByte() - treeNode.startByte > 3) {
|
||||
saveToFileMenuItem.setVisible(true);
|
||||
if (treeNode.name.equals("bitmapAlphaData") || treeNode.name.equals("zlibBitmapData")) {
|
||||
saveUncompressedToFileMenuItem.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
// todo honfika: do not use string names, because it has conflicts e.g with DefineFont.code
|
||||
if (treeNode.name.equals("actionBytes") && treeNode.getChildCount() == 0) {
|
||||
parseActionsMenuItem.setVisible(true);
|
||||
}
|
||||
|
||||
if (treeNode.name.equals("abcBytes") && treeNode.getChildCount() == 0) {
|
||||
parseAbcMenuItem.setVisible(true);
|
||||
}
|
||||
|
||||
if (treeNode.name.equals("code") && treeNode.parent.name.equals("method_body") && treeNode.getChildCount() == 0) {
|
||||
parseInstructionsMenuItem.setVisible(true);
|
||||
}
|
||||
|
||||
TreeModel model = getModel();
|
||||
expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0);
|
||||
}
|
||||
|
||||
contextPopupMenu.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void expandRecursiveButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath path = getSelectionPath();
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
View.expandTreeNodes(this, path, true);
|
||||
}
|
||||
|
||||
private void saveToFileButtonActionPerformed(ActionEvent evt) {
|
||||
saveToFileButtonActionPerformed(false);
|
||||
}
|
||||
|
||||
private void saveUncompressedToFileButtonActionPerformed(ActionEvent evt) {
|
||||
saveToFileButtonActionPerformed(true);
|
||||
}
|
||||
|
||||
private void saveToFileButtonActionPerformed(boolean decompress) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
JFileChooser fc = new JFileChooser();
|
||||
String selDir = Configuration.lastOpenDir.get();
|
||||
fc.setCurrentDirectory(new File(selDir));
|
||||
JFrame f = new JFrame();
|
||||
View.setWindowIcon(f);
|
||||
if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) {
|
||||
File sf = Helper.fixDialogFile(fc.getSelectedFile());
|
||||
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(sf))) {
|
||||
if (decompress) {
|
||||
byte[] data = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().originalUncompressedData;
|
||||
fos.write(SWFInputStream.uncompressByteArray(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1)));
|
||||
} else {
|
||||
byte[] data = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().originalUncompressedData;
|
||||
fos.write(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseActionsButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
byte[] data = swf.originalUncompressedData;
|
||||
int prevLength = (int) dumpInfo.startByte;
|
||||
try {
|
||||
SWFInputStream rri = new SWFInputStream(swf, data);
|
||||
if (prevLength != 0) {
|
||||
rri.seek(prevLength);
|
||||
}
|
||||
List<Action> actions = ActionListReader.getOriginalActions(rri, prevLength, (int) dumpInfo.getEndByte());
|
||||
for (Action action : actions) {
|
||||
DumpInfo di = new DumpInfo(action.toString(), "Action", null, action.getAddress(), action.getTotalActionLength());
|
||||
di.parent = dumpInfo;
|
||||
rri.dumpInfo = di;
|
||||
rri.seek(action.getAddress());
|
||||
rri.readAction();
|
||||
dumpInfo.getChildInfos().add(di);
|
||||
}
|
||||
repaint();
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseAbcButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
byte[] data = swf.originalUncompressedData;
|
||||
int prevLength = (int) dumpInfo.startByte;
|
||||
try {
|
||||
ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes));
|
||||
ais.seek(prevLength);
|
||||
ais.dumpInfo = dumpInfo;
|
||||
new ABC(ais, swf, null);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void parseInstructionsButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
byte[] data = swf.originalUncompressedData;
|
||||
int prevLength = (int) dumpInfo.startByte;
|
||||
try {
|
||||
ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes));
|
||||
ais.seek(prevLength);
|
||||
ais.dumpInfo = dumpInfo;
|
||||
new AVM2Code(ais, null /*FIXME! Pass correct body!*/);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void closeSwfButtonActionPerformed(ActionEvent evt) {
|
||||
Main.closeFile(mainPanel.getCurrentSwfList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DumpTreeModel getModel() {
|
||||
return (DumpTreeModel) super.getModel();
|
||||
}
|
||||
|
||||
public void expandRoot() {
|
||||
DumpTreeModel dtm = getModel();
|
||||
DumpInfo root = dtm.getRoot();
|
||||
expandPath(new TreePath(new Object[]{root}));
|
||||
}
|
||||
|
||||
public void expandFirstLevelNodes() {
|
||||
DumpTreeModel dtm = getModel();
|
||||
DumpInfo root = dtm.getRoot();
|
||||
int childCount = dtm.getChildCount(root);
|
||||
expandPath(new TreePath(new Object[]{root}));
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
expandPath(new TreePath(new Object[]{root, dtm.getChild(root, i)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.dumpview;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.SWFInputStream;
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.abc.ABCInputStream;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
|
||||
import com.jpexs.decompiler.flash.action.Action;
|
||||
import com.jpexs.decompiler.flash.action.ActionListReader;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
|
||||
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial;
|
||||
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType;
|
||||
import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode;
|
||||
import com.jpexs.decompiler.flash.gui.Main;
|
||||
import com.jpexs.decompiler.flash.gui.MainPanel;
|
||||
import com.jpexs.decompiler.flash.gui.TreeNodeType;
|
||||
import com.jpexs.decompiler.flash.gui.View;
|
||||
import com.jpexs.decompiler.flash.gui.tagtree.TagTree;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineBitsTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineButton2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineButtonTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineEditTextTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFont2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFont3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineFontTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShape2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShape3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShape4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineShapeTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineText2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineTextTag;
|
||||
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
|
||||
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.DoABCTag;
|
||||
import com.jpexs.decompiler.flash.tags.DoActionTag;
|
||||
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
|
||||
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
|
||||
import com.jpexs.decompiler.flash.tags.MetadataTag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObject3Tag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObject4Tag;
|
||||
import com.jpexs.decompiler.flash.tags.PlaceObjectTag;
|
||||
import com.jpexs.decompiler.flash.tags.RemoveObject2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.RemoveObjectTag;
|
||||
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
|
||||
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
|
||||
import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.MemoryInputStream;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.plaf.basic.BasicLabelUI;
|
||||
import javax.swing.plaf.basic.BasicTreeUI;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class DumpTree extends JTree {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DumpTree.class.getName());
|
||||
|
||||
private final MainPanel mainPanel;
|
||||
|
||||
public class DumpTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
|
||||
public DumpTreeCellRenderer() {
|
||||
setUI(new BasicLabelUI());
|
||||
setOpaque(false);
|
||||
setBackgroundNonSelectionColor(Color.white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
||||
if (ret instanceof JLabel) {
|
||||
JLabel lab = (JLabel) ret;
|
||||
if (value instanceof DumpInfo) {
|
||||
DumpInfo di = (DumpInfo) value;
|
||||
TreeNodeType nodeType = null;
|
||||
if ("".equals(di.type)) {
|
||||
nodeType = TreeNodeType.FLASH;
|
||||
} else if ("TAG".equals(di.type)) {
|
||||
String name = di.name;
|
||||
if (name.contains(" ")) {
|
||||
name = name.substring(0, name.indexOf(' ')).trim();
|
||||
}
|
||||
switch (name) {
|
||||
case DefineFontTag.NAME:
|
||||
case DefineFont2Tag.NAME:
|
||||
case DefineFont3Tag.NAME:
|
||||
case DefineFont4Tag.NAME:
|
||||
case DefineCompactedFont.NAME:
|
||||
nodeType = TreeNodeType.FONT;
|
||||
break;
|
||||
case DefineTextTag.NAME:
|
||||
case DefineText2Tag.NAME:
|
||||
case DefineEditTextTag.NAME:
|
||||
nodeType = TreeNodeType.TEXT;
|
||||
break;
|
||||
case DefineBitsTag.NAME:
|
||||
case DefineBitsJPEG2Tag.NAME:
|
||||
case DefineBitsJPEG3Tag.NAME:
|
||||
case DefineBitsJPEG4Tag.NAME:
|
||||
case DefineBitsLosslessTag.NAME:
|
||||
case DefineBitsLossless2Tag.NAME:
|
||||
nodeType = TreeNodeType.IMAGE;
|
||||
break;
|
||||
case DefineShapeTag.NAME:
|
||||
case DefineShape2Tag.NAME:
|
||||
case DefineShape3Tag.NAME:
|
||||
case DefineShape4Tag.NAME:
|
||||
nodeType = TreeNodeType.SHAPE;
|
||||
break;
|
||||
case DefineMorphShapeTag.NAME:
|
||||
case DefineMorphShape2Tag.NAME:
|
||||
nodeType = TreeNodeType.MORPH_SHAPE;
|
||||
break;
|
||||
case DefineSpriteTag.NAME:
|
||||
nodeType = TreeNodeType.SPRITE;
|
||||
break;
|
||||
case DefineButtonTag.NAME:
|
||||
case DefineButton2Tag.NAME:
|
||||
nodeType = TreeNodeType.BUTTON;
|
||||
break;
|
||||
case DefineVideoStreamTag.NAME:
|
||||
nodeType = TreeNodeType.MOVIE;
|
||||
break;
|
||||
|
||||
case DefineSoundTag.NAME:
|
||||
case SoundStreamHeadTag.NAME:
|
||||
case SoundStreamHead2Tag.NAME:
|
||||
nodeType = TreeNodeType.SOUND;
|
||||
break;
|
||||
case DefineBinaryDataTag.NAME:
|
||||
nodeType = TreeNodeType.BINARY_DATA;
|
||||
break;
|
||||
case DoActionTag.NAME:
|
||||
case DoInitActionTag.NAME:
|
||||
case DoABCTag.NAME:
|
||||
case DoABC2Tag.NAME:
|
||||
nodeType = TreeNodeType.AS;
|
||||
break;
|
||||
case ShowFrameTag.NAME:
|
||||
nodeType = TreeNodeType.FRAME; //show_frame?
|
||||
break;
|
||||
case SetBackgroundColorTag.NAME:
|
||||
nodeType = TreeNodeType.SET_BACKGROUNDCOLOR;
|
||||
break;
|
||||
case FileAttributesTag.NAME:
|
||||
nodeType = TreeNodeType.FILE_ATTRIBUTES;
|
||||
break;
|
||||
case MetadataTag.NAME:
|
||||
nodeType = TreeNodeType.METADATA;
|
||||
break;
|
||||
case PlaceObjectTag.NAME:
|
||||
case PlaceObject2Tag.NAME:
|
||||
case PlaceObject3Tag.NAME:
|
||||
case PlaceObject4Tag.NAME:
|
||||
nodeType = TreeNodeType.PLACE_OBJECT;
|
||||
break;
|
||||
case RemoveObjectTag.NAME:
|
||||
case RemoveObject2Tag.NAME:
|
||||
nodeType = TreeNodeType.REMOVE_OBJECT;
|
||||
break;
|
||||
default:
|
||||
nodeType = TreeNodeType.OTHER_TAG;
|
||||
}
|
||||
}
|
||||
if (nodeType != null) {
|
||||
lab.setIcon(TagTree.getIconForType(nodeType));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public DumpTree(DumpTreeModel treeModel, MainPanel mainPanel) {
|
||||
super(treeModel);
|
||||
this.mainPanel = mainPanel;
|
||||
setCellRenderer(new DumpTreeCellRenderer());
|
||||
setRootVisible(false);
|
||||
setBackground(Color.white);
|
||||
setUI(new BasicTreeUI() {
|
||||
{
|
||||
setHashColor(Color.gray);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void createContextMenu() {
|
||||
final JPopupMenu contextPopupMenu = new JPopupMenu();
|
||||
|
||||
final JMenuItem expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll"));
|
||||
expandRecursiveMenuItem.addActionListener(this::expandRecursiveButtonActionPerformed);
|
||||
contextPopupMenu.add(expandRecursiveMenuItem);
|
||||
|
||||
final JMenuItem saveToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveToFile"));
|
||||
saveToFileMenuItem.addActionListener(this::saveToFileButtonActionPerformed);
|
||||
contextPopupMenu.add(saveToFileMenuItem);
|
||||
|
||||
final JMenuItem saveUncompressedToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveUncompressedToFile"));
|
||||
saveUncompressedToFileMenuItem.addActionListener(this::saveUncompressedToFileButtonActionPerformed);
|
||||
contextPopupMenu.add(saveUncompressedToFileMenuItem);
|
||||
|
||||
final JMenuItem closeSelectionMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf"));
|
||||
closeSelectionMenuItem.addActionListener(this::closeSwfButtonActionPerformed);
|
||||
contextPopupMenu.add(closeSelectionMenuItem);
|
||||
|
||||
final JMenuItem parseActionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseActions"));
|
||||
parseActionsMenuItem.addActionListener(this::parseActionsButtonActionPerformed);
|
||||
contextPopupMenu.add(parseActionsMenuItem);
|
||||
|
||||
final JMenuItem parseAbcMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseABC"));
|
||||
parseAbcMenuItem.addActionListener(this::parseAbcButtonActionPerformed);
|
||||
contextPopupMenu.add(parseAbcMenuItem);
|
||||
|
||||
final JMenuItem parseInstructionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseInstructions"));
|
||||
parseInstructionsMenuItem.addActionListener(this::parseInstructionsButtonActionPerformed);
|
||||
contextPopupMenu.add(parseInstructionsMenuItem);
|
||||
|
||||
final JMenuItem gotoTagMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources"));
|
||||
gotoTagMenuItem.addActionListener(this::gotoTagButtonActionPerformed);
|
||||
contextPopupMenu.add(gotoTagMenuItem);
|
||||
|
||||
final JMenuItem gotoActionListMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources"));
|
||||
gotoActionListMenuItem.addActionListener(this::gotoActionListButtonActionPerformed);
|
||||
contextPopupMenu.add(gotoActionListMenuItem);
|
||||
|
||||
final JMenuItem gotoMethodMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources"));
|
||||
gotoMethodMenuItem.addActionListener(this::gotoMethodButtonActionPerformed);
|
||||
contextPopupMenu.add(gotoMethodMenuItem);
|
||||
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (SwingUtilities.isRightMouseButton(e)) {
|
||||
|
||||
int row = getClosestRowForLocation(e.getX(), e.getY());
|
||||
int[] selectionRows = getSelectionRows();
|
||||
if (!Helper.contains(selectionRows, row)) {
|
||||
setSelectionRow(row);
|
||||
}
|
||||
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
if (paths == null || paths.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeSelectionMenuItem.setVisible(false);
|
||||
expandRecursiveMenuItem.setVisible(false);
|
||||
saveToFileMenuItem.setVisible(false);
|
||||
saveUncompressedToFileMenuItem.setVisible(false);
|
||||
parseActionsMenuItem.setVisible(false);
|
||||
parseAbcMenuItem.setVisible(false);
|
||||
parseInstructionsMenuItem.setVisible(false);
|
||||
gotoTagMenuItem.setVisible(false);
|
||||
gotoActionListMenuItem.setVisible(false);
|
||||
gotoMethodMenuItem.setVisible(false);
|
||||
|
||||
if (paths.length == 1) {
|
||||
DumpInfo treeNode = (DumpInfo) paths[0].getLastPathComponent();
|
||||
DumpInfoSpecialType specialType = getSpecialType(treeNode);
|
||||
|
||||
if (treeNode instanceof DumpInfoSwfNode) {
|
||||
closeSelectionMenuItem.setVisible(true);
|
||||
}
|
||||
|
||||
if (treeNode.getEndByte() - treeNode.startByte > 3) {
|
||||
saveToFileMenuItem.setVisible(true);
|
||||
if (specialType == DumpInfoSpecialType.ZLIB_DATA) {
|
||||
saveUncompressedToFileMenuItem.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
boolean noChild = treeNode.getChildCount() == 0;
|
||||
|
||||
if (noChild) {
|
||||
switch (specialType) {
|
||||
case ACTION_BYTES:
|
||||
parseActionsMenuItem.setVisible(true);
|
||||
break;
|
||||
case ABC_BYTES:
|
||||
parseAbcMenuItem.setVisible(true);
|
||||
break;
|
||||
case ABC_CODE:
|
||||
parseInstructionsMenuItem.setVisible(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (specialType) {
|
||||
case TAG:
|
||||
gotoTagMenuItem.setVisible(true);
|
||||
break;
|
||||
case ACTION_BYTES:
|
||||
gotoActionListMenuItem.setVisible(true);
|
||||
break;
|
||||
case ABC_CODE:
|
||||
case ABC_METHOD_BODY:
|
||||
gotoMethodMenuItem.setVisible(true);
|
||||
break;
|
||||
}
|
||||
|
||||
TreeModel model = getModel();
|
||||
expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0);
|
||||
}
|
||||
|
||||
contextPopupMenu.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DumpInfoSpecialType getSpecialType(DumpInfo dumpInfo) {
|
||||
DumpInfoSpecialType specialType = dumpInfo instanceof DumpInfoSpecial
|
||||
? ((DumpInfoSpecial) dumpInfo).specialType
|
||||
: DumpInfoSpecialType.NONE;
|
||||
return specialType;
|
||||
}
|
||||
|
||||
private void expandRecursiveButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath path = getSelectionPath();
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
View.expandTreeNodes(this, path, true);
|
||||
}
|
||||
|
||||
private void saveToFileButtonActionPerformed(ActionEvent evt) {
|
||||
saveToFileButtonActionPerformed(false);
|
||||
}
|
||||
|
||||
private void saveUncompressedToFileButtonActionPerformed(ActionEvent evt) {
|
||||
saveToFileButtonActionPerformed(true);
|
||||
}
|
||||
|
||||
private void saveToFileButtonActionPerformed(boolean decompress) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
JFileChooser fc = new JFileChooser();
|
||||
String selDir = Configuration.lastOpenDir.get();
|
||||
fc.setCurrentDirectory(new File(selDir));
|
||||
JFrame f = new JFrame();
|
||||
View.setWindowIcon(f);
|
||||
if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) {
|
||||
File sf = Helper.fixDialogFile(fc.getSelectedFile());
|
||||
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(sf))) {
|
||||
byte[] data = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().originalUncompressedData;
|
||||
if (decompress) {
|
||||
fos.write(SWFInputStream.uncompressByteArray(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1)));
|
||||
} else {
|
||||
fos.write(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseActionsButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
byte[] data = swf.originalUncompressedData;
|
||||
int prevLength = (int) dumpInfo.startByte;
|
||||
try {
|
||||
SWFInputStream rri = new SWFInputStream(swf, data);
|
||||
if (prevLength != 0) {
|
||||
rri.seek(prevLength);
|
||||
}
|
||||
List<Action> actions = ActionListReader.getOriginalActions(rri, prevLength, (int) dumpInfo.getEndByte());
|
||||
for (Action action : actions) {
|
||||
DumpInfo di = new DumpInfo(action.toString(), "Action", null, action.getAddress(), action.getTotalActionLength());
|
||||
di.parent = dumpInfo;
|
||||
rri.dumpInfo = di;
|
||||
rri.seek(action.getAddress());
|
||||
rri.readAction();
|
||||
dumpInfo.getChildInfos().add(di);
|
||||
}
|
||||
repaint();
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseAbcButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
byte[] data = swf.originalUncompressedData;
|
||||
int prevLength = (int) dumpInfo.startByte;
|
||||
try {
|
||||
ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes));
|
||||
ais.seek(prevLength);
|
||||
ais.dumpInfo = dumpInfo;
|
||||
new ABC(ais, swf, null);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void parseInstructionsButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent();
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
byte[] data = swf.originalUncompressedData;
|
||||
int prevLength = (int) dumpInfo.startByte;
|
||||
try {
|
||||
ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes));
|
||||
ais.seek(prevLength);
|
||||
ais.dumpInfo = dumpInfo;
|
||||
new AVM2Code(ais, null /*FIXME! Pass correct body!*/);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void gotoTagButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent();
|
||||
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
long address = (long) (Long) dumpInfo.specialValue;
|
||||
Tag foundTag = null;
|
||||
for (Tag tag : swf.getTags()) {
|
||||
if (tag.getOriginalRange().getPos() == address) {
|
||||
foundTag = tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundTag != null) {
|
||||
mainPanel.getMainFrame().getMenu().showResourcesView();
|
||||
mainPanel.setTagTreeSelectedNode(foundTag);
|
||||
}
|
||||
}
|
||||
|
||||
private void gotoActionListButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent();
|
||||
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
long address = (long) (Long) dumpInfo.specialValue;
|
||||
mainPanel.getMainFrame().getMenu().showResourcesView();
|
||||
//mainPanel.setTagTreeSelectedNode(asm);
|
||||
}
|
||||
|
||||
private void gotoMethodButtonActionPerformed(ActionEvent evt) {
|
||||
TreePath[] paths = getSelectionPaths();
|
||||
DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent();
|
||||
if (dumpInfo.specialType == DumpInfoSpecialType.ABC_CODE) {
|
||||
dumpInfo = (DumpInfoSpecial) dumpInfo.parent; // method_body
|
||||
}
|
||||
|
||||
SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
|
||||
int method_info = (int) dumpInfo.specialValue;
|
||||
// todo
|
||||
}
|
||||
|
||||
private void closeSwfButtonActionPerformed(ActionEvent evt) {
|
||||
Main.closeFile(mainPanel.getCurrentSwfList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DumpTreeModel getModel() {
|
||||
return (DumpTreeModel) super.getModel();
|
||||
}
|
||||
|
||||
public void expandRoot() {
|
||||
DumpTreeModel dtm = getModel();
|
||||
DumpInfo root = dtm.getRoot();
|
||||
expandPath(new TreePath(new Object[]{root}));
|
||||
}
|
||||
|
||||
public void expandFirstLevelNodes() {
|
||||
DumpTreeModel dtm = getModel();
|
||||
DumpInfo root = dtm.getRoot();
|
||||
int childCount = dtm.getChildCount(root);
|
||||
expandPath(new TreePath(new Object[]{root}));
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
expandPath(new TreePath(new Object[]{root, dtm.getChild(root, i)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,97 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.pipes;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.WinNT.HANDLE;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class PipeInputStream extends InputStream {
|
||||
|
||||
protected HANDLE pipe;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
public PipeInputStream(String pipeName, boolean newpipe) throws IOException {
|
||||
if (!Platform.isWindows()) {
|
||||
throw new IOException("Cannot create Pipe on nonWindows OS");
|
||||
}
|
||||
String fullPipePath = "\\\\.\\pipe\\" + pipeName;
|
||||
if (newpipe) {
|
||||
pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_INBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null);
|
||||
if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) {
|
||||
throw new IOException("Cannot connect to the pipe");
|
||||
}
|
||||
} else {
|
||||
pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_READ, Kernel32.FILE_SHARE_READ, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null);
|
||||
}
|
||||
if (pipe == null) {
|
||||
throw new IOException("Cannot connect to the pipe");
|
||||
}
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!closed) {
|
||||
Kernel32.INSTANCE.CloseHandle(pipe);
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
byte d[] = new byte[1];
|
||||
if (readPipe(d) == 0) {
|
||||
return -1;
|
||||
}
|
||||
return d[0];
|
||||
}
|
||||
|
||||
private int readPipe(byte res[]) throws IOException {
|
||||
final IntByReference ibr = new IntByReference();
|
||||
int read = 0;
|
||||
while (read < res.length) {
|
||||
byte[] data = new byte[res.length - read];
|
||||
boolean result = Kernel32.INSTANCE.ReadFile(pipe, data, data.length, ibr, null);
|
||||
if (!result) {
|
||||
throw new IOException("Cannot read pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
int readNow = ibr.getValue();
|
||||
System.arraycopy(data, 0, res, read, readNow);
|
||||
read += readNow;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.pipes;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.WinNT.HANDLE;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class PipeInputStream extends InputStream {
|
||||
|
||||
protected HANDLE pipe;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
public PipeInputStream(String pipeName, boolean newpipe) throws IOException {
|
||||
if (!Platform.isWindows()) {
|
||||
throw new IOException("Cannot create Pipe on nonWindows OS");
|
||||
}
|
||||
String fullPipePath = "\\\\.\\pipe\\" + pipeName;
|
||||
if (newpipe) {
|
||||
pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_INBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null);
|
||||
if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) {
|
||||
throw new IOException("Cannot connect to the pipe");
|
||||
}
|
||||
} else {
|
||||
pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_READ, Kernel32.FILE_SHARE_READ, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null);
|
||||
}
|
||||
if (pipe == null) {
|
||||
throw new IOException("Cannot connect to the pipe");
|
||||
}
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!closed) {
|
||||
Kernel32.INSTANCE.CloseHandle(pipe);
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
byte[] d = new byte[1];
|
||||
if (readPipe(d) == 0) {
|
||||
return -1;
|
||||
}
|
||||
return d[0];
|
||||
}
|
||||
|
||||
private int readPipe(byte res[]) throws IOException {
|
||||
final IntByReference ibr = new IntByReference();
|
||||
int read = 0;
|
||||
while (read < res.length) {
|
||||
byte[] data = new byte[res.length - read];
|
||||
boolean result = Kernel32.INSTANCE.ReadFile(pipe, data, data.length, ibr, null);
|
||||
if (!result) {
|
||||
throw new IOException("Cannot read pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
int readNow = ibr.getValue();
|
||||
System.arraycopy(data, 0, res, read, readNow);
|
||||
read += readNow;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,85 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.pipes;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.WinNT.HANDLE;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class PipeOutputStream extends OutputStream {
|
||||
|
||||
protected HANDLE pipe;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
public PipeOutputStream(String pipeName, boolean newPipe) throws IOException {
|
||||
if (!Platform.isWindows()) {
|
||||
throw new IOException("Cannot create Pipe on nonWindows OS");
|
||||
}
|
||||
String fullPipePath = "\\\\.\\pipe\\" + pipeName;
|
||||
if (newPipe) {
|
||||
pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_OUTBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null);
|
||||
if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) {
|
||||
throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
} else {
|
||||
pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_WRITE, Kernel32.FILE_SHARE_WRITE, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null);
|
||||
if (pipe == null) {
|
||||
throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
}
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!closed) {
|
||||
Kernel32.INSTANCE.CloseHandle(pipe);
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
byte data[] = new byte[]{(byte) b};
|
||||
IntByReference ibr = new IntByReference();
|
||||
boolean result = Kernel32.INSTANCE.WriteFile(pipe, data, data.length, ibr, null);
|
||||
if (!result) {
|
||||
throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
if (ibr.getValue() != data.length) {
|
||||
throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2016 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.gui.pipes;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.WinNT.HANDLE;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class PipeOutputStream extends OutputStream {
|
||||
|
||||
protected HANDLE pipe;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
public PipeOutputStream(String pipeName, boolean newPipe) throws IOException {
|
||||
if (!Platform.isWindows()) {
|
||||
throw new IOException("Cannot create Pipe on nonWindows OS");
|
||||
}
|
||||
String fullPipePath = "\\\\.\\pipe\\" + pipeName;
|
||||
if (newPipe) {
|
||||
pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_OUTBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null);
|
||||
if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) {
|
||||
throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
} else {
|
||||
pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_WRITE, Kernel32.FILE_SHARE_WRITE, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null);
|
||||
if (pipe == null) {
|
||||
throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
}
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!closed) {
|
||||
Kernel32.INSTANCE.CloseHandle(pipe);
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
byte[] data = new byte[]{(byte) b};
|
||||
IntByReference ibr = new IntByReference();
|
||||
boolean result = Kernel32.INSTANCE.WriteFile(pipe, data, data.length, ibr, null);
|
||||
if (!result) {
|
||||
throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
if (ibr.getValue() != data.length) {
|
||||
throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user