iggy extractor - WIP

This commit is contained in:
Jindra Petřík
2016-11-22 13:44:04 +01:00
parent dc79350a97
commit 9af7247ba6
12 changed files with 1426 additions and 0 deletions
@@ -0,0 +1,106 @@
package com.jpexs.decompiler.flash.iggy;
import java.io.EOFException;
import java.io.IOException;
/**
*
* @author JPEXS
*/
public abstract class AbstractDataStream {
/**
* Available bytes
*
* @return null if unknown, long value otherwise
*/
public abstract Long available();
protected long readUI64() throws IOException {
try {
return (readUI32() + (readUI32() << 32)) & 0xffffffffffffffffL;
} catch (EOFException ex) {
return -1;
}
}
protected boolean writeUI64(long val) throws IOException {
write((int) (val & 0xff));
write((int) ((val >> 8) & 0xff));
write((int) ((val >> 16) & 0xff));
write((int) ((val >> 24) & 0xff));
write((int) ((val >> 32) & 0xff));
write((int) ((val >> 40) & 0xff));
write((int) ((val >> 48) & 0xff));
write((int) ((val >> 56) & 0xff));
return true;
}
protected long readUI32() throws IOException {
try {
return (readUI8() + (readUI8() << 8) + (readUI8() << 16) + (readUI8() << 24)) & 0xffffffff;
} catch (EOFException ex) {
return -1;
}
}
protected boolean writeUI32(long val) throws IOException {
write((int) (val & 0xff));
write((int) ((val >> 8) & 0xff));
write((int) ((val >> 16) & 0xff));
write((int) ((val >> 24) & 0xff));
return true;
}
protected int readUI16() throws IOException {
try {
return (readUI8() + (readUI8() << 8)) & 0xffff;
} catch (EOFException ex) {
return -1;
}
}
protected boolean writeUI16(int val) throws IOException {
write(val & 0xff);
write((val >> 8) & 0xff);
return true;
}
protected int readUI8() throws IOException {
try {
return read() & 0xff;
} catch (EOFException ex) {
return -1;
}
}
protected boolean writeUI8(int val) throws IOException {
write(val);
return true;
}
protected float readFloat() throws IOException {
return Float.intBitsToFloat((int) readUI32());
}
protected boolean writeFloat(float val) throws IOException {
return writeUI32(Float.floatToIntBits(val));
}
protected byte[] readBytes(int numBytes) throws IOException {
byte[] ret = new byte[numBytes];
for (int i = 0; i < numBytes; i++) {
ret[i] = (byte) read();
}
return ret;
}
protected abstract int read() throws IOException;
protected abstract void seek(long pos, SeekMode mode) throws IOException;
protected void write(int val) throws IOException {
//nothing
}
}
@@ -0,0 +1,70 @@
package com.jpexs.decompiler.flash.iggy;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
/**
*
* @author JPEXS
*/
public class ByteArrayDataStream extends AbstractDataStream {
private byte[] data;
private int pos;
public ByteArrayDataStream(int initialSize) {
this(new byte[initialSize]);
}
public ByteArrayDataStream(byte data[]) {
this.data = data;
pos = 0;
}
@Override
protected int read() throws IOException {
if (pos >= data.length) {
throw new EOFException("End of stream reached");
}
return data[pos++] & 0xff;
}
public void resize(int newsize) {
data = Arrays.copyOf(data, newsize);
if (pos > data.length) {
pos = data.length;
}
}
@Override
protected void write(int val) throws IOException {
if (pos >= data.length) {
throw new EOFException("End of stream reached");
}
data[pos++] = (byte) val;
}
@Override
protected void seek(long pos, SeekMode mode) throws IOException {
long newpos = pos;
if (mode == SeekMode.CUR) {
newpos = this.pos + pos;
} else if (mode == SeekMode.END) {
newpos = data.length - pos;
}
if (newpos > data.length) {
throw new ArrayIndexOutOfBoundsException("Position outside bounds accessed: " + pos + ". Size: " + data.length);
} else if (newpos < 0) {
throw new ArrayIndexOutOfBoundsException("Negative position accessed: " + pos);
} else {
this.pos = (int) newpos;
}
}
@Override
public Long available() {
return (long) (data.length - pos);
}
}
@@ -0,0 +1,8 @@
package com.jpexs.decompiler.flash.iggy;
/**
* @author JPEXS
*/
public enum DataType {
uint8_t, uint16_t, uint32_t, uint64_t, unknown
}
@@ -0,0 +1,647 @@
package com.jpexs.decompiler.flash.iggy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*
* Based of works of somebody called eternity.
*
*/
public class IggyExtractor extends AbstractDataStream implements AutoCloseable {
final static Logger LOGGER = Logger.getLogger(IggyExtractor.class.getName());
private RandomAccessFile raf;
private IggyHeader header;
private List<IggySubFileEntry> subFileEntries = new ArrayList<>();
public IggyExtractor(File file) throws IOException {
raf = new RandomAccessFile(file, "r");
header = new IggyHeader(this);
for (int i = 0; i < header.num_subfiles; i++) {
subFileEntries.add(new IggySubFileEntry(this));
}
List<ByteArrayDataStream> indexStreams = new ArrayList<>();
List<ByteArrayDataStream> flashStreams = new ArrayList<>();
for (int i = 0; i < subFileEntries.size(); i++) {
IggySubFileEntry entry = subFileEntries.get(i);
ByteArrayDataStream entryStream = getEntryDataStream(i);
if (entry.type == IggySubFileEntry.TYPE_INDEX) {
indexStreams.add(entryStream);
} else if (entry.type == IggySubFileEntry.TYPE_FLASH) {
flashStreams.add(entryStream);
}
}
for (ByteArrayDataStream fs : flashStreams) {
if (is64()) {
IggyFlashHeader64 fh = new IggyFlashHeader64(fs);
System.out.println("" + fh);
} else {
IggyFlashHeader32 fh = new IggyFlashHeader32(fs);
}
}
}
private boolean is64() {
return header.is64();
}
public IggyHeader getHeader() {
return header;
}
public IggySubFileEntry getSubFileEntry(int entryIndex) {
if (entryIndex < 0 || entryIndex >= subFileEntries.size()) {
throw new ArrayIndexOutOfBoundsException("No entry with index " + entryIndex + " exists");
}
return subFileEntries.get(entryIndex);
}
public int getNumEntries() {
return subFileEntries.size();
}
public byte[] getEntryData(int entryIndex) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = getEntryInputStream(entryIndex);
byte buf[] = new byte[1024];
int cnt;
while ((cnt = is.read(buf)) > 0) {
baos.write(buf, 0, cnt);
}
return baos.toByteArray();
}
public ByteArrayDataStream getEntryDataStream(int entryIndex) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = getEntryInputStream(entryIndex);
byte buf[] = new byte[1024];
int cnt;
while ((cnt = is.read(buf)) > 0) {
baos.write(buf, 0, cnt);
}
byte data[] = baos.toByteArray();
return new ByteArrayDataStream(data);
}
public InputStream getEntryInputStream(int entryIndex) {
IggySubFileEntry entry = getSubFileEntry(entryIndex);
return new InputStream() {
long offset = entry.offset;
long maxOffset = entry.offset + entry.size;
@Override
public synchronized int read() throws IOException {
if (offset < maxOffset) {
raf.seek(offset);
offset++;
return raf.read();
}
return -1;
}
};
}
protected int read() throws IOException {
int val = raf.read();
if (val == -1) {
throw new EOFException();
}
return val;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[IggyFile:").append("\r\n");
sb.append(header).append("\r\n");
sb.append("Entries:").append("\r\n");
for (IggySubFileEntry entry : subFileEntries) {
sb.append(entry).append("\r\n");
}
sb.append("]");
return sb.toString();
}
public static void extractIggyFile(File iggyFile, File extractDir) throws IOException {
final String FILENAME_FORMAT = "index%d_type%d.bin";
try (IggyExtractor ir = new IggyExtractor(iggyFile)) {
for (int i = 0; i < ir.getNumEntries(); i++) {
IggySubFileEntry entry = ir.getSubFileEntry(i);
try (FileOutputStream fos = new FileOutputStream(new File(extractDir, String.format(FILENAME_FORMAT, i, entry.type)))) {
fos.write(ir.getEntryData(i));
}
}
}
}
private static void processFile(File f) {
if (f.isDirectory()) {
System.out.println("Processing directory " + f + ":");
File iggyFiles[] = f.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".iggy");
}
});
for (File sf : iggyFiles) {
processFile(sf);
}
return;
}
System.out.print("Processing file " + f + "...");
try {
File dir = f.getParentFile();
File extractDir = new File(dir, "extracted_" + (f.getName()).replace(".iggy", ""));
extractDir.mkdir();
extractIggyFile(f, extractDir);
System.out.println("OK");
} catch (Exception ex) {
System.err.println("FAIL");
System.exit(1);
}
}
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.err.println("No file specified");
System.exit(1);
}
for (String s : args) {
File f = new File(s);
if (!f.exists()) {
System.err.println("File " + f + " does not exists");
System.exit(1);
}
processFile(f);
}
System.exit(0);
}
private static void copyStream(InputStream is, OutputStream os) {
try {
final int bufSize = 4096;
byte[] buf = new byte[bufSize];
int cnt;
while ((cnt = is.read(buf)) > 0) {
os.write(buf, 0, cnt);
}
} catch (IOException ex) {
// ignore
}
}
@Override
public void close() {
try {
raf.close();
} catch (IOException ex) {
//ignore
}
}
@Override
protected void seek(long pos, SeekMode mode) throws IOException {
long newpos = pos;
if (mode == SeekMode.CUR) {
newpos = raf.getFilePointer() + pos;
} else if (mode == SeekMode.END) {
newpos = raf.length() - pos;
}
if (newpos > raf.length()) {
throw new ArrayIndexOutOfBoundsException("Position outside bounds accessed: " + pos + ". Size: " + raf.length());
} else if (newpos < 0) {
throw new ArrayIndexOutOfBoundsException("Negative position accessed: " + pos);
} else {
raf.seek(newpos);
}
}
private static boolean updateIndex(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], long item_size_change /*int32_t*/) throws IOException {
ByteArrayDataStream stream = new ByteArrayDataStream(index_bytes);
/*
index_table:
n = UI8
for i=0..n-1
table[i] = UI8
cnt = UI8
cnt * UI16
*/
int index_table_size = stream.readUI8();
int index_table[] = new int[index_table_size];
for (int i = 0; i < index_table_size; i++) {
index_table[i] = stream.readUI8();
int num = stream.readUI8();
stream.seek(num * 2, SeekMode.CUR);
//num * UI16
}
int state = 0;
long offset = 0;//uint32_t
int code; //uint8_t
while ((code = stream.readUI8()) >= 0) {
if (state == 1) {
if (code != 0xFD) {
LOGGER.log(Level.WARNING, "We were expecting code 0xFD in state 1.");
return false;
}
} else if (state == 2) {
if (code != 0xFF) {
LOGGER.log(Level.WARNING, "We were expecting code 0xFF in state 2.");
return false;
}
}
if (code < 0x80) // 0-0x7F
{
// code is directly an index to the index_table
if (code >= index_table_size) {
LOGGER.log(Level.WARNING, String.format("< 0x80: index is greater than index_table_size. %x > %x", code, index_table_size));
return false;
}
offset += index_table[code];
} else if (code < 0xC0) // 0x80-BF
{
int index; //uint8_t
if ((index = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "< 0xC0: Cannot read index.");
return false;
}
if (index >= index_table_size) {
LOGGER.log(Level.WARNING, String.format("< 0xC0: index is greater than index_table_size. %x > %x", index, index_table_size));
return false;
}
int n = code - 0x7F;
offset += index_table[index] * n;
} else if (code < 0xD0) // 0xC0-0xCF
{
offset += ((code * 2) - 0x17E);
} else if (code < 0xE0) // 0xD0-0xDF
{
// Code here depends on plattform[0], we are assuming it is 1, as we checked in load function
int i = code & 0xF; //uint8_t
int n8; //uint8_t
int n;
if ((n8 = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "< 0xE0: Cannot read n.");
return false;
}
n = n8 + 1;
if (is_64) {
if (i <= 2) {
offset += 8 * n; // Ptr type
} else if (i <= 4) {
offset += 2 * n;
} else if (i == 5) {
offset += 4 * n;
} else if (i == 6) {
offset += 8 * n; // 64 bits type
} else {
LOGGER.log(Level.WARNING, String.format("< 0xE0: Invalid value for i (%x %x)", i, code));
}
} else {
switch (i) {
case 2:
offset += 4 * n; // Ptr type
break;
case 4:
offset += 2 * n;
break;
case 5:
offset += 4 * n; // 32 bits type
break;
case 6:
offset += 8 * n;
break;
default:
LOGGER.log(Level.WARNING, String.format("< 0xE0: invalid value for i (%x %x)", i, code));
}
}
} else if (code == 0xFC) {
stream.seek(1, SeekMode.CUR);
} else if (code == 0xFD) {
int n, m; //uint8_t
if ((n = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "0xFD: Cannot read n.");
return false;
}
if (state == 1) {
if (is_64) {
if (n != 0xF) {
LOGGER.log(Level.WARNING, String.format("We were expecting an offset of 0xF in state 1."));
return false;
}
} else if (n != 0xB) {
LOGGER.log(Level.WARNING, "We were expecting an offset of 0xB in state 1.");
return false;
}
state = 2;
}
if ((m = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "0xFD: Cannot read m.");
return false;
}
offset += n;
stream.seek(m * 2, SeekMode.CUR);
} else if (code == 0xFE) {
int n8; //uint8_t
int n;
if ((n8 = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "0xFE: Cannot read n.");
return false;
}
n = n8 + 1;
offset += n;
} else if (code == 0xFF) {
long n; //uint32_t
if ((n = stream.readUI32()) < 0) {
LOGGER.log(Level.WARNING, "0xFF: Cannot read n.");
return false;
}
if (state == 2) {
n += item_size_change;
stream.seek(-4, SeekMode.CUR);
return stream.writeUI32(n);
}
offset += n;
} else {
LOGGER.log(Level.WARNING, String.format("Unrecognized code: %x", code));
}
if (state == 0 && offset == item_offset) {
state = 1;
}
}
return false;
}
/**
* Gets length of an item.
*
* @param item_offset
* @param is_64
* @param index_bytes
* @return null when item not exists, item length otherwise
* @throws IOException
*/
private static Long getItemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[]) throws IOException {
return itemLength(item_offset, is_64, index_bytes, null);
}
/**
* Sets new length of an item.
*
* @param item_offset
* @param is_64
* @param index_bytes
* @param newLength
* @return null when item not exists, old item length otherwise
* @throws IOException
*/
private static Long setItemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], long newLength) throws IOException {
return itemLength(item_offset, is_64, index_bytes, newLength);
}
/**
* Sets/Gets length of an item
*
* @param item_offset
* @param is_64
* @param index_bytes
* @param newValue New value to set. If null then no change.
* @return null when item does not exists, old item length otherwise
* @throws IOException
*/
private static Long itemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], Long newValue) throws IOException {
ByteArrayDataStream stream = new ByteArrayDataStream(index_bytes);
/*
index_table:
n = UI8
for i=0..n-1
table[i] = UI8
cnt = UI8
cnt * UI16
*/
int index_table_size = stream.readUI8();
int index_table[] = new int[index_table_size];
for (int i = 0; i < index_table_size; i++) {
index_table[i] = stream.readUI8();
int num = stream.readUI8();
stream.seek(num * 2, SeekMode.CUR);
//num * UI16
}
int state = 0;
long offset = 0;//uint32_t
int code; //uint8_t
while ((code = stream.readUI8()) >= 0) {
if (state == 1) {
if (code != 0xFD) {
LOGGER.log(Level.WARNING, "We were expecting code 0xFD in state 1.");
return null;
}
} else if (state == 2) {
if (code != 0xFF) {
LOGGER.log(Level.WARNING, "We were expecting code 0xFF in state 2.");
return null;
}
}
if (code < 0x80) // 0-0x7F
{
// code is directly an index to the index_table
if (code >= index_table_size) {
LOGGER.log(Level.WARNING, String.format("< 0x80: index is greater than index_table_size. %x > %x", code, index_table_size));
return null;
}
offset += index_table[code];
} else if (code < 0xC0) // 0x80-BF
{
int index; //uint8_t
if ((index = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "< 0xC0: Cannot read index.");
return null;
}
if (index >= index_table_size) {
LOGGER.log(Level.WARNING, String.format("< 0xC0: index is greater than index_table_size. %x > %x", index, index_table_size));
return null;
}
int n = code - 0x7F;
offset += index_table[index] * n;
} else if (code < 0xD0) // 0xC0-0xCF
{
offset += ((code * 2) - 0x17E);
} else if (code < 0xE0) // 0xD0-0xDF
{
// Code here depends on plattform[0], we are assuming it is 1, as we checked in load function
int i = code & 0xF; //uint8_t
int n8; //uint8_t
int n;
if ((n8 = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "< 0xE0: Cannot read n.");
return null;
}
n = n8 + 1;
if (is_64) {
if (i <= 2) {
offset += 8 * n; // Ptr type
} else if (i <= 4) {
offset += 2 * n;
} else if (i == 5) {
offset += 4 * n;
} else if (i == 6) {
offset += 8 * n; // 64 bits type
} else {
LOGGER.log(Level.WARNING, String.format("< 0xE0: Invalid value for i (%x %x)", i, code));
}
} else {
switch (i) {
case 2:
offset += 4 * n; // Ptr type
break;
case 4:
offset += 2 * n;
break;
case 5:
offset += 4 * n; // 32 bits type
break;
case 6:
offset += 8 * n;
break;
default:
LOGGER.log(Level.WARNING, String.format("< 0xE0: invalid value for i (%x %x)", i, code));
}
}
} else if (code == 0xFC) {
stream.seek(1, SeekMode.CUR);
} else if (code == 0xFD) {
int n, m; //uint8_t
if ((n = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "0xFD: Cannot read n.");
return null;
}
if (state == 1) {
if (is_64) {
if (n != 0xF) {
LOGGER.log(Level.WARNING, String.format("We were expecting an offset of 0xF in state 1."));
return null;
}
} else if (n != 0xB) {
LOGGER.log(Level.WARNING, "We were expecting an offset of 0xB in state 1.");
return null;
}
state = 2;
}
if ((m = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "0xFD: Cannot read m.");
return null;
}
offset += n;
stream.seek(m * 2, SeekMode.CUR);
} else if (code == 0xFE) {
int n8; //uint8_t
int n;
if ((n8 = stream.readUI8()) < 0) {
LOGGER.log(Level.WARNING, "0xFE: Cannot read n.");
return null;
}
n = n8 + 1;
offset += n;
} else if (code == 0xFF) {
long n; //uint32_t
if ((n = stream.readUI32()) < 0) {
LOGGER.log(Level.WARNING, "0xFF: Cannot read n.");
return null;
}
if (state == 2) {
if (newValue != null) {
stream.seek(-4, SeekMode.CUR);
stream.writeUI32(newValue);
}
return n;
}
offset += n;
} else {
LOGGER.log(Level.WARNING, String.format("Unrecognized code: %x", code));
}
if (state == 0 && offset == item_offset) {
state = 1;
}
}
return null;
}
@Override
public Long available() {
try {
return raf.length() - raf.getFilePointer();
} catch (IOException ex) {
return null;
}
}
}
@@ -0,0 +1,170 @@
package com.jpexs.decompiler.flash.iggy;
import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType;
import java.io.IOException;
/**
*
* @author JPEXS
*
* Based of works of somebody called eternity.
*
* All relative offsets are relative from that specific field position All
* relative offsets can get value "1" to indicate "nothing"
*/
public class IggyFlashHeader32 implements StructureInterface {
@IggyFieldType(DataType.uint32_t)
long main_offset; // 0 Relative offset to first section (matches sizeof header)
@IggyFieldType(DataType.uint32_t)
long as3_section_offset; // 4 Relative offset to as3 file names table...
@IggyFieldType(DataType.uint32_t)
long unk_offset; // 8 relative offset to something
@IggyFieldType(DataType.uint32_t)
long unk_offset2; // 0xC relative offset to something
@IggyFieldType(DataType.uint32_t)
long unk_offset3; // 0x10 relative offset to something
@IggyFieldType(DataType.uint32_t)
long unk_offset4; // 0x14 relative offset to something
@IggyFieldType(DataType.uint32_t)
long xmin; //0x18 in pixels
@IggyFieldType(DataType.uint32_t)
long ymin; //0x0C in pixels
@IggyFieldType(DataType.uint32_t)
long xmax; // 0x20 in pixels
@IggyFieldType(DataType.uint32_t)
long ymax; // 0x24 in pixels
@IggyFieldType(DataType.uint32_t)
long unk_28; // probably numer of blocks/objects after header
@IggyFieldType(DataType.uint32_t)
long unk_2C;
@IggyFieldType(DataType.uint32_t)
long unk_30;
@IggyFieldType(DataType.uint32_t)
long unk_34;
@IggyFieldType(DataType.uint32_t)
long unk_38;
@IggyFieldType(DataType.uint32_t)
long unk_3C;
float unk_40;
@IggyFieldType(DataType.uint32_t)
long unk_44;
@IggyFieldType(DataType.uint32_t)
long unk_48;
@IggyFieldType(DataType.uint32_t)
long unk_4C;
@IggyFieldType(DataType.uint32_t)
long names_offset; // 0x50 relative offset to the names/import section of the file
@IggyFieldType(DataType.uint32_t)
long unk_offset5; // 0x54 relative offset to something
@IggyFieldType(DataType.uint64_t)
long unk_58; // Maybe number of imports/names pointed by names_offset
@IggyFieldType(DataType.uint32_t)
long last_section_offset; // 0x60 relative offset, points to the small last section of the file
@IggyFieldType(DataType.uint32_t)
long unk_offset6; // 0x64 relative offset to something
@IggyFieldType(DataType.uint32_t)
long as3_code_offset; // 0x68 relative offset to as3 code (8 bytes header + abc blob)
@IggyFieldType(DataType.uint32_t)
long as3_names_offset; // 0x6C relative offset to as3 file names table (or classes names or whatever)
@IggyFieldType(DataType.uint32_t)
long unk_70;
@IggyFieldType(DataType.uint32_t)
long unk_74;
@IggyFieldType(DataType.uint32_t)
long unk_78; // Maybe number of classes / as3 names
@IggyFieldType(DataType.uint32_t)
long unk_7C;
// Offset 0x80 (outside header): there are *unk_28* relative offsets that point to flash objects.
// The flash objects are in a format different to swf but there is probably a way to convert between them.
// After the offsets, the bodies of objects pointed above, which apparently have a code like 0xFFXX to identify the type of object, followed by a (unique?) identifier
// for the object.
// A DefineEditText-like object can be easily spotted and apparently uses type code 0x06 (or 0xFF06) but as stated above,
// it is written in a different way.
public IggyFlashHeader32(AbstractDataStream stream) throws IOException {
readFromDataStream(stream);
}
@Override
public void readFromDataStream(AbstractDataStream stream) throws IOException {
main_offset = stream.readUI32();
as3_section_offset = stream.readUI32();
unk_offset = stream.readUI32();
unk_offset2 = stream.readUI32();
unk_offset3 = stream.readUI32();
unk_offset4 = stream.readUI32();
xmin = stream.readUI32();
ymin = stream.readUI32();
xmax = stream.readUI32();
ymax = stream.readUI32();
unk_28 = stream.readUI32();
unk_2C = stream.readUI32();
unk_30 = stream.readUI32();
unk_34 = stream.readUI32();
unk_38 = stream.readUI32();
unk_3C = stream.readUI32();
unk_40 = stream.readFloat();
unk_44 = stream.readUI32();
unk_48 = stream.readUI32();
unk_4C = stream.readUI32();
unk_3C = stream.readUI32();
names_offset = stream.readUI32();
unk_offset5 = stream.readUI32();
unk_58 = stream.readUI64();
last_section_offset = stream.readUI32();
unk_offset6 = stream.readUI32();
as3_code_offset = stream.readUI32();
as3_names_offset = stream.readUI32();
unk_70 = stream.readUI32();
unk_74 = stream.readUI32();
unk_78 = stream.readUI32();
unk_7C = stream.readUI32();
}
@Override
public void writeToDataStream(AbstractDataStream stream) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[\r\n");
sb.append("main_offset ").append(main_offset).append("\r\n");
sb.append("as3_section_offset ").append(as3_section_offset).append("\r\n");
sb.append("unk_offset ").append(unk_offset).append("\r\n");
sb.append("unk_offset2 ").append(unk_offset2).append("\r\n");
sb.append("unk_offset3 ").append(unk_offset3).append("\r\n");
sb.append("unk_offset4 ").append(unk_offset4).append("\r\n");
sb.append("xmin ").append(xmin).append("\r\n");
sb.append("ymin ").append(ymin).append("\r\n");
sb.append("xmax ").append(xmax).append("\r\n");
sb.append("ymax ").append(ymax).append("\r\n");
sb.append("unk_28 ").append(unk_28).append("\r\n");
sb.append("unk_2C ").append(unk_2C).append("\r\n");
sb.append("unk_30 ").append(unk_30).append("\r\n");
sb.append("unk_34 ").append(unk_34).append("\r\n");
sb.append("unk_38 ").append(unk_38).append("\r\n");
sb.append("unk_3C ").append(unk_3C).append("\r\n");
sb.append("unk_40 ").append(unk_40).append("\r\n");
sb.append("unk_44 ").append(unk_44).append("\r\n");
sb.append("unk_48 ").append(unk_48).append("\r\n");
sb.append("unk_4C ").append(unk_4C).append("\r\n");
sb.append("names_offset ").append(names_offset).append("\r\n");
sb.append("unk_offset5 ").append(unk_offset5).append("\r\n");
sb.append("unk_58 ").append(unk_58).append("\r\n");
sb.append("last_section_offset ").append(last_section_offset).append("\r\n");
sb.append("unk_offset6 ").append(unk_offset6).append("\r\n");
sb.append("as3_code_offset ").append(as3_code_offset).append("\r\n");
sb.append("as3_names_offset ").append(as3_names_offset).append("\r\n");
sb.append("unk_70 ").append(unk_70).append("\r\n");
sb.append("unk_74 ").append(unk_74).append("\r\n");
sb.append("unk_78 ").append(unk_78).append("\r\n");
sb.append("unk_7C ").append(unk_7C).append("\r\n");
sb.append("]");
return sb.toString();
}
}
@@ -0,0 +1,167 @@
package com.jpexs.decompiler.flash.iggy;
import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType;
import java.io.IOException;
/**
*
* @author Jindra
*
* Based of works of somebody called eternity.
*/
public class IggyFlashHeader64 implements StructureInterface {
@IggyFieldType(DataType.uint64_t)
long main_offset; // 0 Relative offset to first section (matches sizeof header);
@IggyFieldType(DataType.uint64_t)
long as3_section_offset; // 8 Relative offset to as3 file names table...
@IggyFieldType(DataType.uint64_t)
long unk_offset; // 0x10 relative offset to something
@IggyFieldType(DataType.uint64_t)
long unk_offset2; // 0x18 relative offset to something
@IggyFieldType(DataType.uint64_t)
long unk_offset3; // 0x20 relative offset to something
@IggyFieldType(DataType.uint64_t)
long unk_offset4; // 0x28 names_offset; 0x50 relative pointer to the names/import section of the file
@IggyFieldType(DataType.uint32_t)
long xmin; // 0x30 in pixels
@IggyFieldType(DataType.uint32_t)
long ymin; // 0x34 in pixels
@IggyFieldType(DataType.uint32_t)
long xmax; // 0x38 in pixels
@IggyFieldType(DataType.uint32_t)
long ymax; // 0x3C in pixels
@IggyFieldType(DataType.uint32_t)
long unk_40; // probably numer of blocks/objects after header
@IggyFieldType(DataType.uint32_t)
long unk_44;
@IggyFieldType(DataType.uint32_t)
long unk_48;
@IggyFieldType(DataType.uint32_t)
long unk_4C;
@IggyFieldType(DataType.uint32_t)
long unk_50;
@IggyFieldType(DataType.uint32_t)
long unk_54;
float unk_58;
@IggyFieldType(DataType.uint32_t)
long unk_5C;
@IggyFieldType(DataType.uint64_t)
long unk_60;
@IggyFieldType(DataType.uint64_t)
long unk_68;
@IggyFieldType(DataType.uint64_t)
long names_offset; // 0x70 relative offset to the names/import section of the file
@IggyFieldType(DataType.uint64_t)
long unk_offset5; // 0x78 relative offset to something
@IggyFieldType(DataType.uint64_t)
long unk_80; // Maybe number of imports/names pointed by names_offset
@IggyFieldType(DataType.uint64_t)
long last_section_offset; // 0x88 relative offset, points to the small last section of the file
@IggyFieldType(DataType.uint64_t)
long unk_offset6; // 0x90 relative offset to something
@IggyFieldType(DataType.uint64_t)
long as3_code_offset; // 0x98 relative offset to as3 code (16 bytes header + abc blob)
@IggyFieldType(DataType.uint64_t)
long as3_names_offset; // 0xA0 relative offset to as3 file names table (or classes names or whatever)
@IggyFieldType(DataType.uint32_t)
long unk_A8;
@IggyFieldType(DataType.uint32_t)
long unk_AC;
@IggyFieldType(DataType.uint32_t)
long unk_B0; // Maybe number of classes / as3 names
@IggyFieldType(DataType.uint32_t)
long unk_B4;
// Offset 0xB8 (outside header): there are *unk_40* relative offsets that point to flash objects.
// The flash objects are in a format different to swf but there is probably a way to convert between them.
// After the offsets, the bodies of objects pointed above, which apparently have a code like 0xFFXX to identify the type of object, followed by a (unique?) identifier
// for the object.
// A DefineEditText-like object can be easily spotted and apparently uses type code 0x06 (or 0xFF06) but as stated above,
// it is written in a different way.
public IggyFlashHeader64(AbstractDataStream stream) throws IOException {
readFromDataStream(stream);
}
@Override
public void readFromDataStream(AbstractDataStream stream) throws IOException {
main_offset = stream.readUI64();
as3_section_offset = stream.readUI64();
unk_offset = stream.readUI64();
unk_offset2 = stream.readUI64();
unk_offset3 = stream.readUI64();
unk_offset4 = stream.readUI64();
xmin = stream.readUI32();
ymin = stream.readUI32();
xmax = stream.readUI32();
ymax = stream.readUI32();
unk_40 = stream.readUI32();
unk_44 = stream.readUI32();
unk_48 = stream.readUI32();
unk_4C = stream.readUI32();
unk_50 = stream.readUI32();
unk_54 = stream.readUI32();
unk_58 = stream.readFloat();
unk_5C = stream.readUI32();
unk_60 = stream.readUI64();
unk_68 = stream.readUI64();
names_offset = stream.readUI64();
unk_offset5 = stream.readUI64();
unk_80 = stream.readUI64();
last_section_offset = stream.readUI64();
unk_offset6 = stream.readUI64();
as3_code_offset = stream.readUI64();
as3_names_offset = stream.readUI64();
unk_A8 = stream.readUI32();
unk_AC = stream.readUI32();
unk_B0 = stream.readUI32();
unk_B4 = stream.readUI32();
}
@Override
public void writeToDataStream(AbstractDataStream stream) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[\r\n");
sb.append("main_offset ").append(main_offset).append("\r\n");
sb.append("as3_section_offset ").append(as3_section_offset).append("\r\n");
sb.append("unk_offset ").append(unk_offset).append("\r\n");
sb.append("unk_offset2 ").append(unk_offset2).append("\r\n");
sb.append("unk_offset3 ").append(unk_offset3).append("\r\n");
sb.append("unk_offset4 ").append(unk_offset4).append("\r\n");
sb.append("xmin ").append(xmin).append("\r\n");
sb.append("ymin ").append(ymin).append("\r\n");
sb.append("xmax ").append(ymax).append("\r\n");
sb.append("ymax ").append(ymax).append("\r\n");
sb.append("unk_40 ").append(unk_40).append("\r\n");
sb.append("unk_44 ").append(unk_44).append("\r\n");
sb.append("unk_48 ").append(unk_48).append("\r\n");
sb.append("unk_4C ").append(unk_4C).append("\r\n");
sb.append("unk_50 ").append(unk_50).append("\r\n");
sb.append("unk_54 ").append(unk_54).append("\r\n");
sb.append("unk_58 ").append(unk_58).append("\r\n");
sb.append("unk_5C ").append(unk_5C).append("\r\n");
sb.append("unk_60 ").append(unk_60).append("\r\n");
sb.append("unk_68 ").append(unk_68).append("\r\n");
sb.append("names_offset ").append(names_offset).append("\r\n");
sb.append("unk_offset5 ").append(unk_offset5).append("\r\n");
sb.append("unk_80 ").append(unk_80).append("\r\n");
sb.append("last_section_offset ").append(last_section_offset).append("\r\n");
sb.append("unk_offset6 ").append(unk_offset6).append("\r\n");
sb.append("as3_code_offset ").append(as3_code_offset).append("\r\n");
sb.append("as3_names_offset ").append(as3_names_offset).append("\r\n");
sb.append("unk_A8 ").append(unk_A8).append("\r\n");
sb.append("unk_AC ").append(unk_AC).append("\r\n");
sb.append("unk_B0 ").append(unk_B0).append("\r\n");
sb.append("unk_B4 ").append(unk_B4).append("\r\n");
sb.append("]");
return sb.toString();
}
}
@@ -0,0 +1,107 @@
package com.jpexs.decompiler.flash.iggy;
import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType;
import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType;
import java.io.IOException;
import java.math.BigInteger;
/**
*
* @author JPEXS
*
* little endian all
*
* Based of works of somebody called eternity.
*/
public class IggyHeader implements StructureInterface {
public static long MAGIC = 0xED0A6749;
//Must be 0xED0A6749
@IggyFieldType(DataType.uint32_t)
public long magic = MAGIC;
//Assume 0x900
@IggyFieldType(DataType.uint32_t)
public long version;
//Assuming: 1
@IggyFieldType(value = DataType.uint8_t)
public int platform1;
//32/64
@IggyFieldType(value = DataType.uint8_t)
public int platform2_bittness;
//Assuming: 1
@IggyFieldType(value = DataType.uint8_t)
public int platform3;
//Usually: 3
@IggyFieldType(value = DataType.uint8_t)
public int platform4;
//flags for platform 64?
@IggyFieldType(DataType.uint32_t)
public long unk_0C;
@IggyArrayFieldType(value = DataType.uint8_t, count = 12)
public byte[] reserved;
@IggyFieldType(value = DataType.uint32_t)
public long num_subfiles;
public IggyHeader(AbstractDataStream stream) throws IOException {
readFromDataStream(stream);
}
public IggyHeader(long version, int platform1, int platform2_bittness, int platform3, int platform4, long unk_0C, byte[] reserved, long num_subfiles) {
this.version = version;
this.platform1 = platform1;
this.platform2_bittness = platform2_bittness;
this.platform3 = platform3;
this.platform4 = platform4;
this.unk_0C = unk_0C;
this.reserved = reserved;
this.num_subfiles = num_subfiles;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append("version: ").append(version).append(", ");
sb.append("platform: ").append(platform1).append(" ").append(platform2_bittness).append(" ").append(platform3).append(" ").append(platform4).append(", ");
sb.append("unk_0C: ").append(String.format("%08X", unk_0C)).append(", ");
sb.append("reserved: 12 bytes").append(", ");
sb.append("num_subfiles: ").append(num_subfiles);
sb.append("]");
return sb.toString();
}
@Override
public void readFromDataStream(AbstractDataStream stream) throws IOException {
magic = stream.readUI32();
if (magic != IggyHeader.MAGIC) {
throw new IOException("Invalid Iggy file");
}
version = stream.readUI32();
platform1 = stream.readUI8();
platform2_bittness = stream.readUI8();
platform3 = stream.readUI8();
platform4 = stream.readUI8();
unk_0C = stream.readUI32();
reserved = stream.readBytes(12);
num_subfiles = stream.readUI32();
}
@Override
public void writeToDataStream(AbstractDataStream stream) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean is64() {
return platform2_bittness == 64;
}
}
@@ -0,0 +1,68 @@
package com.jpexs.decompiler.flash.iggy;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import java.io.IOException;
/**
*
* @author JPEXS
*
* Based of works of somebody called eternity.
*/
public class IggySubFileEntry implements StructureInterface {
public static final int TYPE_INDEX = 0;
public static final int TYPE_FLASH = 1;
@SWFType(BasicType.UI32)
long type;
@SWFType(BasicType.UI32)
long size;
//apparently same as size, maybe (un)compressed (?)
@SWFType(BasicType.UI32)
long size2;
//absolute offset
@SWFType(BasicType.UI32)
long offset;
public IggySubFileEntry(AbstractDataStream stream) throws IOException {
readFromDataStream(stream);
}
public IggySubFileEntry(long type, long size, long size2, long offset) {
this.type = type;
this.size = size;
this.size2 = size2;
this.offset = offset;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append("id: ").append(type).append(", ");
sb.append("size: ").append(size).append(", ");
sb.append("size2: ").append(size2).append(", ");
sb.append("offset: ").append(offset);
sb.append("]");
return sb.toString();
}
@Override
public void readFromDataStream(AbstractDataStream stream) throws IOException {
type = stream.readUI32();
size = stream.readUI32();
size2 = stream.readUI32();
offset = stream.readUI32();
}
@Override
public void writeToDataStream(AbstractDataStream stream) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}
@@ -0,0 +1,9 @@
package com.jpexs.decompiler.flash.iggy;
/**
*
* @author Jindra
*/
public enum SeekMode {
SET, CUR, END
}
@@ -0,0 +1,14 @@
package com.jpexs.decompiler.flash.iggy;
import java.io.IOException;
/**
*
* @author JPEXS
*/
public interface StructureInterface {
public void readFromDataStream(AbstractDataStream stream) throws IOException;
public void writeToDataStream(AbstractDataStream stream) throws IOException;
}
@@ -0,0 +1,26 @@
package com.jpexs.decompiler.flash.iggy.annotations;
import com.jpexs.decompiler.flash.iggy.DataType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author JPEXS
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface IggyArrayFieldType {
/// Type of value
DataType value() default DataType.unknown;
int count() default -1;
/// Field name on which Count depends
String countField() default "";
//Count to add to countField
int countAdd() default 0;
}
@@ -0,0 +1,34 @@
package com.jpexs.decompiler.flash.iggy.annotations;
import com.jpexs.decompiler.flash.iggy.DataType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author JPEXS
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface IggyFieldType {
/// Type of value
DataType value() default DataType.unknown;
/// Alternate type when condition is met
DataType alternateValue() default DataType.unknown;
/// Condition for alternate type
String alternateCondition() default "";
/// Count - used primarily for bit fields UB,SB,FB to specify number of bits
int count() default -1;
/// Field name on which Count depends
String countField() default "";
//Count to add to countField
int countAdd() default 0;
}