package com.jpexs.decompiler.flash.iggy; import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType; 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.WriteDataStreamInterface; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * * @author JPEXS */ public class IggyFont extends IggyTag { public static final int ID = 0xFF16; @IggyFieldType(DataType.uint16_t) int type; //stejny pro rozdilne fonty @IggyFieldType(DataType.uint16_t) int fontId; @IggyArrayFieldType(value = DataType.uint8_t, count = 28) byte[] zeroone; // stejny pro rozdilne fonty @IggyFieldType(DataType.uint16_t) int char_count2; @IggyFieldType(value = DataType.uint16_t) int ascent; @IggyFieldType(value = DataType.uint16_t) int descent; @IggyFieldType(value = DataType.uint16_t) int leading; @IggyFieldType(DataType.uint64_t) long flags; //@IggyFieldType(DataType.uint64_t) //long start_of_char_struct; //@IggyFieldType(DataType.uint64_t) //long start_of_char_index; //@IggyFieldType(DataType.uint64_t) //long start_of_scale; @IggyFieldType(DataType.uint32_t) long kern_count; @IggyArrayFieldType(value = DataType.float_t, count = 5) float[] unk_float; //@IggyFieldType(DataType.uint64_t) //long start_of_kern; @IggyFieldType(DataType.uint64_t) long zero_padd; @IggyFieldType(DataType.uint64_t) long what_2; @IggyFieldType(DataType.uint64_t) long zero_padd_2; //@IggyFieldType(DataType.uint64_t) //long start_of_name; @IggyFieldType(DataType.uint64_t) long one_padd; @IggyFieldType(DataType.uint16_t) int xscale; @IggyFieldType(DataType.uint16_t) int yscale; @IggyFieldType(DataType.uint64_t) long zero_padd_3; @IggyFieldType(DataType.float_t) float ssr1; @IggyFieldType(DataType.float_t) float ssr2; @IggyFieldType(DataType.uint32_t) long char_count; @IggyFieldType(DataType.uint64_t) long zero_padd_4; @IggyFieldType(DataType.uint64_t) long what_3; @IggyFieldType(value = DataType.wchar_t, count = 40) String subName = ""; @IggyFieldType(value = DataType.uint8_t, count = 48) byte[] zeroes48a; @IggyFieldType(value = DataType.uint8_t, count = 48) byte[] zeroes48b; @IggyFieldType(DataType.float_t) float sss1; @IggyFieldType(DataType.uint32_t) long one_padd2; @IggyFieldType(DataType.float_t) float sss2; @IggyFieldType(DataType.uint32_t) long one_padd3; @IggyFieldType(DataType.float_t) float sss3; @IggyFieldType(DataType.uint32_t) long one_padd4; @IggyFieldType(DataType.float_t) float sss4; @IggyFieldType(DataType.uint32_t) long one_padd5; @IggyFieldType(value = DataType.wchar_t, count = 16) String name; List<IggyCharOffset> charOffsets; List<IggyShape> glyphs; IggyCharIndices codePoints; IggyCharAdvances charScales; IggyCharKerning charKernings; public IggyFont(int type, int fontId, byte[] zeroone, int char_count2, int ascent, int descent, int leading, long flags, long kern_count, float[] unk_float, long zero_padd, long what_2, long zero_padd_2, long one_padd, int xscale, int yscale, long zero_padd_3, float ssr1, float ssr2, long char_count, long zero_padd_4, long what_3, byte[] zeroes48a, byte[] zeroes48b, float sss1, long one_padd2, float sss2, long one_padd3, float sss3, long one_padd4, float sss4, long one_padd5, String name, List<IggyCharOffset> charOffsets, List<IggyShape> glyphs, IggyCharIndices codePoints, IggyCharAdvances charScales, IggyCharKerning charKernings) { this.type = type; this.fontId = fontId; this.zeroone = zeroone; this.char_count2 = char_count2; this.ascent = ascent; this.descent = descent; this.leading = leading; this.flags = flags; this.kern_count = kern_count; this.unk_float = unk_float; this.zero_padd = zero_padd; this.what_2 = what_2; this.zero_padd_2 = zero_padd_2; this.one_padd = one_padd; this.xscale = xscale; this.yscale = yscale; this.zero_padd_3 = zero_padd_3; this.ssr1 = ssr1; this.ssr2 = ssr2; this.char_count = char_count; this.zero_padd_4 = zero_padd_4; this.what_3 = what_3; this.zeroes48a = zeroes48a; this.zeroes48b = zeroes48b; this.sss1 = sss1; this.one_padd2 = one_padd2; this.sss2 = sss2; this.one_padd3 = one_padd3; this.sss3 = sss3; this.one_padd4 = one_padd4; this.sss4 = sss4; this.one_padd5 = one_padd5; this.name = name; this.charOffsets = charOffsets; this.glyphs = glyphs; this.codePoints = codePoints; this.charScales = charScales; this.charKernings = charKernings; } public IggyFont(ReadDataStreamInterface stream) throws IOException { readFromDataStream(stream); } private long readAbsoluteOffset(ReadDataStreamInterface stream) throws IOException { long offset = stream.readUI64(); if (offset == 1) { return 0; } return stream.position() - 8 + offset; } private void writeAbsoluteOffset(WriteDataStreamInterface stream, long offset) throws IOException { if (offset == 0) { stream.writeUI64(1); } else { stream.writeUI64(offset - stream.position()); } } private void writeRelativeOffset(WriteDataStreamInterface stream, long offset) throws IOException { if (offset == 0) { stream.writeUI64(1); } else { stream.writeUI64(offset); } } private long makeAbsOffset(ReadDataStreamInterface s, long offset) { return offset == 1 ? 0 : offset + s.position() - 8; } private long makeAbsOffset(WriteDataStreamInterface s, long offset) { return offset == 1 ? 0 : offset + s.position() - 8; } @Override public void readFromDataStream(ReadDataStreamInterface s) throws IOException { type = s.readUI16(); fontId = s.readUI16(); zeroone = s.readBytes(28); char_count2 = s.readUI16(); ascent = s.readUI16(); descent = s.readUI16(); leading = s.readUI16(); flags = s.readUI64(); long start_of_char_struct = s.readUI64(); long abs_start_of_char_struct = makeAbsOffset(s, start_of_char_struct); long start_of_char_index = s.readUI64(); long abs_start_of_char_index = makeAbsOffset(s, start_of_char_index); long start_of_scale = s.readUI64(); long abs_start_of_scale = makeAbsOffset(s, start_of_scale); kern_count = s.readUI32(); unk_float = new float[5]; for (int i = 0; i < unk_float.length; i++) { unk_float[i] = s.readFloat(); } long start_of_kern = s.readUI64(); long abs_start_of_kern = makeAbsOffset(s, start_of_kern); zero_padd = s.readUI64(); //-------------------------------------- what_2 = s.readUI64(); zero_padd_2 = s.readUI64(); long start_of_name = s.readUI64(); long abs_start_of_name = makeAbsOffset(s, start_of_name); one_padd = s.readUI64(); xscale = s.readUI16(); yscale = s.readUI16(); zero_padd_3 = s.readUI64(); ssr1 = s.readFloat(); ssr2 = s.readFloat(); char_count = s.readUI32(); zero_padd_4 = s.readUI64(); what_3 = s.readUI64(); StringBuilder subNameBuilder = new StringBuilder(); boolean snFinish = false; for (int i = 0; i < 20; i++) { char c = (char) s.readUI16(); if (c == '\0') { snFinish = true; } if (!snFinish) { subNameBuilder.append(c); } } subName = subNameBuilder.toString(); zeroes48a = s.readBytes(48); sss1 = s.readFloat(); one_padd2 = s.readUI32(); sss2 = s.readFloat(); one_padd3 = s.readUI32(); sss3 = s.readFloat(); one_padd4 = s.readUI32(); sss4 = s.readFloat(); one_padd5 = s.readUI32(); zeroes48b = s.readBytes(48); if (abs_start_of_name != 0) { //here is offset [5] - 1096 s.seek(abs_start_of_name, SeekMode.SET); name = s.readWChar(); //here is offset [6] - 1130 s.pad8bytes(); } if (abs_start_of_char_struct != 0) { //here is offset [7] - 1136 s.seek(abs_start_of_char_struct, SeekMode.SET); charOffsets = new ArrayList<>(); List<Long> charAddresses = new ArrayList<>(); for (int i = 0; i < char_count; i++) { IggyCharOffset iggyOffset = new IggyCharOffset(s); charOffsets.add(iggyOffset); if (iggyOffset.offset == 1) { charAddresses.add(0L); } else { charAddresses.add(iggyOffset.offset + s.position() - 8); } } glyphs = new ArrayList<>(); for (int i = 0; i < char_count; i++) { long addr = charAddresses.get(i); if (addr != 0) { s.seek(addr, SeekMode.SET); glyphs.add(new IggyShape(s)); } else { glyphs.add(null); } } } if (abs_start_of_char_index != 0) { s.seek(abs_start_of_char_index, SeekMode.SET); codePoints = new IggyCharIndices(s, char_count); } if (abs_start_of_scale != 0) { s.seek(abs_start_of_scale, SeekMode.SET); charScales = new IggyCharAdvances(s, char_count); } if (abs_start_of_kern != 0) { s.seek(abs_start_of_kern, SeekMode.SET); charKernings = new IggyCharKerning(s, kern_count); } } @Override public void writeToDataStream(WriteDataStreamInterface s) throws IOException { final int FILL_LATER_IF_AVAILABLE = 1; IggyIndexBuilder ib = s.getIndexing(); ib.writeConstLength(IggyIndexBuilder.CONST_GENERAL_FONT_INFO_SIZE); s.writeUI16(type); s.writeUI16(fontId); s.writeBytes(zeroone); s.writeUI16(char_count2); s.writeUI16(ascent); s.writeUI16(descent); s.writeUI16(leading); s.writeUI64(flags); long start_of_char_struct_ofs_pos = s.position(); s.writeUI64(FILL_LATER_IF_AVAILABLE); long start_of_char_index_ofs_pos = s.position(); s.writeUI64(FILL_LATER_IF_AVAILABLE); long start_of_scale_ofs_pos = s.position(); s.writeUI64(FILL_LATER_IF_AVAILABLE); s.writeUI32(kern_count); for (int i = 0; i < unk_float.length; i++) { s.writeFloat(unk_float[i]); } long start_of_kern_ofs_pos = s.position(); s.writeUI64(FILL_LATER_IF_AVAILABLE); s.writeUI64(zero_padd); ib.writeConstLength(IggyIndexBuilder.CONST_GENERAL_FONT_INFO2_SIZE); s.writeUI64(what_2); s.writeUI64(zero_padd_2); long start_of_name_ofs_pos = s.position(); s.writeUI64(FILL_LATER_IF_AVAILABLE); s.writeUI64(one_padd); s.writeUI16(xscale); s.writeUI16(yscale); s.writeUI64(zero_padd_3); s.writeFloat(ssr1); s.writeFloat(ssr2); s.writeUI32(char_count); s.writeUI64(zero_padd_4); s.writeUI64(what_3); for (int i = 0; i < 20; i++) { if (i < subName.length()) { s.writeUI16((char) subName.charAt(i)); } else { s.writeUI16(0); } } s.writeBytes(zeroes48a); s.writeFloat(sss1); s.writeUI32(one_padd2); s.writeFloat(sss2); s.writeUI32(one_padd3); s.writeFloat(sss3); s.writeUI32(one_padd4); s.writeFloat(sss4); s.writeUI32(one_padd5); s.writeBytes(zeroes48b); if (name != null) { s.setOlderOffsetToThisPos(start_of_name_ofs_pos); for (char c : name.toCharArray()) { s.writeUI16(c); } s.writeUI16(0); //align to 8 bytes boundary int len = name.length() * 2 + 2; ib.write16bitArray(name.length() + 1); ib.pad8bytes(); int pad8 = 8 - (len % 8); if (pad8 < 8) { for (int i = 0; i < pad8; i++) { s.write(0); } } } //s.writeUI64(0); //pad zero if (charOffsets != null) { s.setOlderOffsetToThisPos(start_of_char_struct_ofs_pos); ib.writeConstLengthArray(IggyIndexBuilder.CONST_CHAR_OFFSET_SIZE, charOffsets.size()); List<Long> toFixOffsets = new ArrayList<>(); //offsets of shapes for (IggyCharOffset ofs : charOffsets) { ofs.offset = FILL_LATER_IF_AVAILABLE; ofs.writeToDataStream(s); toFixOffsets.add(s.position() - 8); } for (int i = 0; i < glyphs.size(); i++) { IggyShape shp = glyphs.get(i); if (shp != null) { s.setOlderOffsetToThisPos(toFixOffsets.get(i)); shp.writeToDataStream(s); } } } if (codePoints != null) { s.setOlderOffsetToThisPos(start_of_char_index_ofs_pos); for (char c : codePoints.chars) { s.writeUI16(c); } ib.write16bitArray(codePoints.chars.size()); ib.pad8bytes(); s.pad8bytes(); } if (charScales != null) { s.setOlderOffsetToThisPos(start_of_scale_ofs_pos); charScales.writeToDataStream(s); ib.write32bitArray(charScales.advances.size()); ib.pad8bytes(); s.pad8bytes(); } if (charKernings != null) { s.setOlderOffsetToThisPos(start_of_kern_ofs_pos); ib.writeConstLengthArray(IggyIndexBuilder.CONST_KERNING_RECORD_SIZE, kern_count); charKernings.writeToDataStream(s); ib.pad8bytes(); s.pad8bytes(); } /*if ((s.position() - abs_start_of_char_index) % 8 != 0) { byte padd[] = new byte[(int) (((s.position() - abs_start_of_char_index) / 8 + 1) * 8 - (s.position() - abs_start_of_char_index))]; s.writeBytes(padd); } ib.writePaddingTo8(s.position());*/ } public int getType() { return type; } public long getFlags() { return flags; } public int getXscale() { return xscale; } public int getYscale() { return yscale; } public long getCharacterCount() { return char_count; } public String getName() { return name; } public List<IggyShape> getChars() { return glyphs; } public IggyCharIndices getCharIndices() { return codePoints; } public IggyCharAdvances getCharAdvances() { return charScales; } public IggyCharKerning getCharKernings() { return charKernings; } public float[] getUnk_float() { return unk_float; } public int getAscent() { return ascent; } public int getDescent() { return descent; } public int getLeading() { return leading; } public long getWhat_2() { return what_2; } public long getWhat_3() { return what_3; } public List<IggyCharOffset> getCharOffsets() { return charOffsets; } @Override public int getTagType() { return ID; } @Override public String toString() { return String.format("IggyFontTag (%04X)", ID); } public void setType(int type) { this.type = type; } public void setFontId(int fontId) { this.fontId = fontId; } public void setCharCount2(int char_count2) { this.char_count2 = char_count2; } public void setAscent(int ascent) { this.ascent = ascent; } public void setDescent(int descent) { this.descent = descent; } public void setLeading(int leading) { this.leading = leading; } public void setFlags(long flags) { this.flags = flags; } public void setUnkFloat(float[] unk_float) { this.unk_float = unk_float; } public void setWhat2(long what_2) { this.what_2 = what_2; } public void setXScale(int xscale) { this.xscale = xscale; } public void setYScale(int yscale) { this.yscale = yscale; } public void setSsr1(float ssr1) { this.ssr1 = ssr1; } public void setSsr2(float ssr2) { this.ssr2 = ssr2; } public void setCharCount(long char_count) { this.char_count = char_count; } public void setWhat3(long what_3) { this.what_3 = what_3; } public void setSubName(String subName) { this.subName = subName; } public void setSss1(float sss1) { this.sss1 = sss1; } public void setSss2(float sss2) { this.sss2 = sss2; } public void setSss3(float sss3) { this.sss3 = sss3; } public void setSss4(float sss4) { this.sss4 = sss4; } public void setName(String name) { this.name = name; } public void setCharOffsets(List<IggyCharOffset> charOffsets) { this.charOffsets = charOffsets; } public void setGlyphs(List<IggyShape> glyphs) { this.glyphs = glyphs; } public void setCodePoints(IggyCharIndices codePoints) { this.codePoints = codePoints; } public void setCharScales(IggyCharAdvances charScales) { this.charScales = charScales; } public void setCharKernings(IggyCharKerning charKernings) { this.charKernings = charKernings; } }