mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-09-27 04:20:45 +00:00
iggy extractor - WIP
This commit is contained in:
@@ -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;
|
||||
}
|
||||
+26
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user