package com.jpexs.decompiler.flash.iggy; import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; import com.jpexs.decompiler.flash.iggy.streams.IggyIndexBuilder; import com.jpexs.decompiler.flash.iggy.streams.ReadDataStreamInterface; import com.jpexs.decompiler.flash.iggy.streams.SeekMode; import com.jpexs.decompiler.flash.iggy.streams.StructureInterface; import com.jpexs.decompiler.flash.iggy.streams.WriteDataStreamInterface; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * * @author JPEXS */ public class IggySwf implements StructureInterface { final static int NO_OFFSET = 1; @IggyFieldType(value = DataType.wchar_t, count = 48) String name; private List<IggyFont> fonts = new ArrayList<>(); // private List<Long> font_data_addresses = new ArrayList<>(); private List<IggyFont> add_fonts = new ArrayList<>(); // private List<Long> add_font_data_addresses = new ArrayList<>(); private IggyFlashHeader64 hdr; public IggySwf(ReadDataStreamInterface stream) throws IOException { readFromDataStream(stream); } private List<IggyText> texts = new ArrayList<>(); //private List<Long> text_data_addresses = new ArrayList<>(); private List<IggyText> add_texts = new ArrayList<>(); //private List<Long> add_text_data_addresses = new ArrayList<>(); //private byte font_add_data[]; //private List<Long> font_additional_size = new ArrayList<>(); private IggyFontBinInfo font_bin_info[]; private List<String> sequenceNames = new ArrayList<>(); //private List<Long> sequenceValues = new ArrayList<>(); private IggyFontTypeInfo type_info[]; private String type_info_name[]; private IggyDeclStrings decl_strings; private long ofs_additional; private long additional_address; public IggyFlashHeader64 getHdr() { return hdr; } public List<IggyFont> getFonts() { return fonts; } public List<IggyFont> getAddFonts() { return add_fonts; } public List<IggyText> getTexts() { return texts; } public List<IggyText> getAddTexts() { return add_texts; } @Override public void readFromDataStream(ReadDataStreamInterface s) throws IOException { this.hdr = new IggyFlashHeader64(s); //Save all font bytes to buffer for later easy modification //here is offset[0] - 184 name = s.readWChar(); //here is offset[1] - 230 int pad8 = 8 - (int) (s.position() % 8); if (pad8 > 8) { s.seek(pad8, SeekMode.CUR); } //here is offset [2] - 232 s.seek(hdr.getBaseAddress(), SeekMode.SET); s.readUI64(); //pad 1 List<Long> itemsAddresses = new ArrayList<>(); while (true) { long offset = s.readUI64(); if (offset == 1) { break; } itemsAddresses.add(offset + s.position() - 8); } if (hdr.getImported_guid() != 0) { ofs_additional = s.readUI64(); additional_address = ofs_additional == 1 ? 0 : ofs_additional + s.position() - 8; } for (Long addr : itemsAddresses) { s.seek(addr, SeekMode.SET); int kind = s.readUI8(); s.seek(-1, SeekMode.CUR); switch (kind) { case 22 /*FONT*/: IggyFont font = new IggyFont(s); //font_data_addresses.add(addr); fonts.add(font); break; case 6 /*TEXT*/: IggyText text = new IggyText(s); //text_data_addresses.add(addr); texts.add(text); break; default: throw new RuntimeException("Unknown item kind: " + kind); } } if (additional_address != 0) { s.seek(additional_address, SeekMode.SET); List<Long> additionalItemsAddresses = new ArrayList<>(); while (true) { long offset = s.readUI64(); if (offset == 1) { break; } additionalItemsAddresses.add(offset + s.position() - 8); } for (Long addr : additionalItemsAddresses) { s.seek(addr, SeekMode.SET); int kind = s.readUI8(); s.seek(-1, SeekMode.CUR); switch (kind) { case 22 /*FONT*/: IggyFont font = new IggyFont(s); //add_font_data_addresses.add(addr); add_fonts.add(font); break; case 6 /*TEXT*/: IggyText text = new IggyText(s); //add_text_data_addresses.add(addr); add_texts.add(text); break; default: throw new RuntimeException("Unknown imported item kind: " + kind); } } } s.seek(hdr.getFontEndAddress(), SeekMode.SET); //here is offset [4] - 856 ? font_bin_info = new IggyFontBinInfo[(int) hdr.font_count]; for (int i = 0; i < hdr.font_count; i++) { font_bin_info[i] = new IggyFontBinInfo(s); } sequenceNames = new ArrayList<>(); long seq_addresses[] = new long[]{hdr.getSequenceStartAddress1(), hdr.getSequenceStartAddress2(), hdr.getSequenceStartAddress3()}; long seq_name_addresses[] = new long[3]; for (int i = 0; i < 3; i++) { if (seq_addresses[i] == 0) { seq_name_addresses[i] = 0; //0 } else { s.seek(seq_addresses[i], SeekMode.SET); long ofs_seq_name = s.readUI64(); seq_name_addresses[i] = ofs_seq_name == 1 ? 0 : ofs_seq_name + s.position() - 8; s.readUI64(); //is this crucial? } } for (int i = 0; i < 3; i++) { if (seq_name_addresses[i] > 0) { s.seek(seq_name_addresses[i], SeekMode.SET); sequenceNames.add(s.readWChar()); } else { sequenceNames.add(null); } } s.pad8bytes(); //sequence = new IggySequence(s); s.seek(hdr.getTypeFontsAddress(), SeekMode.SET); type_info = new IggyFontTypeInfo[(int) hdr.font_count]; type_info_name = new String[(int) hdr.font_count]; for (int i = 0; i < hdr.font_count; i++) { type_info[i] = new IggyFontTypeInfo(s); } for (int i = 0; i < hdr.font_count; i++) { s.seek(type_info[i].getLocal_name_ofs_pos() + type_info[i].ofs_local_name, SeekMode.SET); type_info_name[i] = s.readWChar(); } s.seek(hdr.getDeclStringsAddress(), SeekMode.SET); decl_strings = new IggyDeclStrings(s); /*WriteDataStreamInterface outs = new TemporaryDataStream(); writeToDataStream(outs); Helper.writeFile("d:\\Dropbox\\jpexs-laptop\\iggi\\parts\\swf_out.bin", outs.getAllBytes());*/ } public String getName() { return name; } @Override public void writeToDataStream(WriteDataStreamInterface s) throws IOException { IggyIndexBuilder ib = s.getIndexing(); hdr.writeToDataStream(s); s.writeWChar(name); s.pad8bytes(); s.writeUI64(1); ib.write16bitArray(name.length() + 1); ib.writeTwoPaddingBytes(); ib.write64bitPointerArray(64); long posBeforeOffsets = s.position(); final int FILL_LATER = 1; List<Long> fontPosFillLater = new ArrayList<>(); for (int i = 0; i < fonts.size(); i++) { fontPosFillLater.add(s.position()); s.writeUI64(FILL_LATER); } List<Long> textPosFillLater = new ArrayList<>(); for (int i = 0; i < texts.size(); i++) { textPosFillLater.add(s.position()); s.writeUI64(FILL_LATER); } s.writeUI64(1); long addPosFillLater = s.position(); s.writeUI64(FILL_LATER); long posAfter = posBeforeOffsets + 64 * 8; long curPos = s.position(); long numLeft = posAfter - curPos; long ofsLeft = numLeft / 8; for (int i = 0; i < ofsLeft - 1; i++) { s.writeUI64(FILL_LATER); } for (int i = 0; i < fonts.size(); i++) { s.setOlderOffsetToThisPos(fontPosFillLater.get(i)); fonts.get(i).writeToDataStream(s); } for (int i = 0; i < texts.size(); i++) { s.setOlderOffsetToThisPos(textPosFillLater.get(i)); texts.get(i).writeToDataStream(s); } if (!add_fonts.isEmpty() || !add_texts.isEmpty()) { s.setOlderOffsetToThisPos(addPosFillLater); List<Long> addFontPosFillLater = new ArrayList<>(); for (int i = 0; i < add_fonts.size(); i++) { addFontPosFillLater.add(s.position()); s.writeUI64(FILL_LATER); } List<Long> addTextPosFillLater = new ArrayList<>(); for (int i = 0; i < add_texts.size(); i++) { addTextPosFillLater.add(s.position()); s.writeUI64(FILL_LATER); } s.writeUI64(FILL_LATER); for (int i = 0; i < add_fonts.size(); i++) { s.setOlderOffsetToThisPos(addFontPosFillLater.get(i)); add_fonts.get(i).writeToDataStream(s); } for (int i = 0; i < add_texts.size(); i++) { s.setOlderOffsetToThisPos(addTextPosFillLater.get(i)); add_texts.get(i).writeToDataStream(s); } } s.setOlderOffsetToThisPos(hdr.getFont_end_ofs_pos()); ib.writeConstLengthArray(IggyIndexBuilder.CONST_BIN_INFO_SIZE, hdr.font_count); for (int i = 0; i < hdr.font_count; i++) { font_bin_info[i].writeToDataStream(s); } long seq_ofs_pos[] = new long[]{hdr.getSequence_start1_ofs_pos(), hdr.getSequence_start2_ofs_pos(), hdr.getSequence_start3_ofs_pos()}; long off_seq_expected[] = new long[]{hdr.off_sequence_start1, hdr.off_sequence_start2, hdr.off_sequence_start3}; long seq_name_fill_later[] = new long[3]; s.setOlderOffsetToThisPos(seq_ofs_pos[0]); s.writeUI64(1); s.writeUI64(1); ib.writeLengthCustom(16, new int[]{0}, new int[]{2}); seq_name_fill_later[2] = s.position(); s.setOlderOffsetToThisPos(seq_ofs_pos[2]); s.writeUI64(FILL_LATER); s.writeUI64(0); ib.writeConstLength(IggyIndexBuilder.CONST_SEQUENCE_SIZE); for (int i = 0; i < 3; i++) { if (sequenceNames.get(i) != null) { s.setOlderOffsetToThisPos(seq_name_fill_later[i]); ib.write16bitArray(sequenceNames.get(i).length() + 1); s.writeWChar(sequenceNames.get(i)); } } s.setOlderOffsetToThisPos(hdr.getSequence_end_ofs_pos()); s.pad8bytes(); ib.pad8bytes(); ib.writeConstLengthArray(IggyIndexBuilder.CONST_TYPE_INFO_SIZE, hdr.font_count); s.setOlderOffsetToThisPos(hdr.getType_fonts_ofs_pos()); //s.seek(hdr.getTypeFontsAddress(), SeekMode.SET); for (int i = 0; i < hdr.font_count; i++) { type_info[i].writeToDataStream(s); } for (int i = 0; i < hdr.font_count; i++) { ib.write16bitArray(type_info_name[i].length() + 1); s.setOlderOffsetToThisPos(type_info[i].getLocal_name_ofs_pos()); s.writeWChar(type_info_name[i]); } s.pad8bytes(); ib.pad8bytes(); s.setOlderOffsetToThisPos(hdr.getDecl_strings_ofs_pos()); //s.seek(hdr.getDeclStringsAddress(), SeekMode.SET); decl_strings.writeToDataStream(s); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[\r\n"); sb.append("name ").append(name).append("\r\n"); return sb.toString(); } public IggyDeclStrings getDeclStrings() { return decl_strings; } }