Issue #1118 Loading characters through ImportAssets - show as readonly

icon
This commit is contained in:
Jindra Petřík
2016-01-03 22:25:54 +01:00
parent c069d5e0e4
commit c2aba5fc08
21 changed files with 2325 additions and 2183 deletions

View File

@@ -107,12 +107,14 @@ import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.Exportable;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.ImportTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.timeline.AS2Package;
@@ -161,6 +163,7 @@ import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -214,6 +217,9 @@ public final class SWF implements SWFContainerItem, Timelined {
@Internal
public ReadOnlyTagList readOnlyTags;
@Internal
public ReadOnlyTagList readOnlyLocalTags;
public boolean hasEndTag = true;
/**
@@ -494,6 +500,20 @@ public final class SWF implements SWFContainerItem, Timelined {
return exportName;
}
public FontTag getFontByClass(String fontClass) {
if (fontClass == null) {
return null;
}
for (Tag t : getTags()) {
if (t instanceof FontTag) {
if (fontClass.equals(((FontTag) t).getClassName())) {
return (FontTag) t;
}
}
}
return null;
}
public FontTag getFont(int fontId) {
CharacterTag characterTag = getCharacters().get(fontId);
if (characterTag instanceof FontTag) {
@@ -593,7 +613,13 @@ public final class SWF implements SWFContainerItem, Timelined {
public int getNextCharacterId() {
int max = 0;
for (int characterId : getCharacters().keySet()) {
Set<Integer> ids = new HashSet<>(getCharacters().keySet());
for (Tag t : tags) {
if (t instanceof ImportTag) {
ids.addAll(((ImportTag) t).getAssets().keySet());
}
}
for (int characterId : ids) {
if (characterId > max) {
max = characterId;
}
@@ -824,7 +850,7 @@ public final class SWF implements SWFContainerItem, Timelined {
sos.writeFIXED8(frameRate);
sos.writeUI16(frameCount);
sos.writeTags(getTags());
sos.writeTags(getLocalTags());
if (hasEndTag) {
sos.writeUI16(0);
}
@@ -1066,6 +1092,10 @@ public final class SWF implements SWFContainerItem, Timelined {
decompress(is, new NulStream(), true);
}
public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy) throws IOException, InterruptedException {
this(is, file, fileTitle, listener, parallelRead, checkOnly, lazy, null);
}
/**
* Construct SWF from stream
*
@@ -1076,10 +1106,11 @@ public final class SWF implements SWFContainerItem, Timelined {
* @param parallelRead Use parallel threads?
* @param checkOnly Check only file validity
* @param lazy
* @param resolver Resolver for imported tags
* @throws IOException
* @throws java.lang.InterruptedException
*/
public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy) throws IOException, InterruptedException {
public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy, UrlResolver resolver) throws IOException, InterruptedException {
this.file = file;
this.fileTitle = fileTitle;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -1112,11 +1143,15 @@ public final class SWF implements SWFContainerItem, Timelined {
}
this.tags = tags;
readOnlyTags = null;
readOnlyLocalTags = null;
if (!checkOnly) {
checkInvalidSprites();
updateCharacters();
assignExportNamesToSymbols();
assignClassesToSymbols();
if (resolver != null) {
resolveImported(resolver);
}
SWFDecompilerPlugin.fireSwfParsed(this);
} else {
boolean hasNonUnknownTag = false;
@@ -1139,6 +1174,94 @@ public final class SWF implements SWFContainerItem, Timelined {
getASMs(true); // Add scriptNames to ASMs
}
private void resolveImported(UrlResolver resolver) {
for (int p = 0; p < tags.size(); p++) {
Tag t = tags.get(p);
if (t instanceof ImportTag) {
ImportTag importTag = (ImportTag) t;
SWF iSwf = resolver.resolveUrl(importTag.getUrl());
if (iSwf != null) {
Map<Integer, String> exportedMap1 = new HashMap<>();
Map<Integer, String> classesMap1 = new HashMap<>();
for (Tag t2 : iSwf.tags) {
if (t2 instanceof ExportAssetsTag) {
ExportAssetsTag sc = (ExportAssetsTag) t2;
Map<Integer, String> m2 = sc.getTagToNameMap();
for (int key : m2.keySet()) {
if (!exportedMap1.containsKey(key)) {
exportedMap1.put(key, m2.get(key));
}
}
}
if (t2 instanceof SymbolClassTag) {
SymbolClassTag sc = (SymbolClassTag) t2;
Map<Integer, String> m2 = sc.getTagToNameMap();
for (int key : m2.keySet()) {
if (!classesMap1.containsKey(key)) {
classesMap1.put(key, m2.get(key));
}
}
}
}
Map<String, Integer> exportedMap2 = new HashMap<>();
for (int k : exportedMap1.keySet()) {
exportedMap2.put(exportedMap1.get(k), k);
}
Map<String, Integer> classesMap2 = new HashMap<>();
for (int k : classesMap1.keySet()) {
classesMap2.put(classesMap1.get(k), k);
}
Map<Integer, String> importedMap1 = importTag.getAssets();
Map<String, Integer> importedMap2 = new HashMap<>();
for (int k : importedMap1.keySet()) {
importedMap2.put(importedMap1.get(k), k);
}
int pos = 0;
for (String key : importedMap2.keySet()) {
if (!exportedMap2.containsKey(key)) {
continue; //?
}
int exportedId = exportedMap2.get(key);
int importedId = importedMap2.get(key);
for (Tag cht : iSwf.tags) {
if ((cht instanceof CharacterIdTag) && (((CharacterIdTag) cht).getCharacterId() == exportedId) && !(cht instanceof PlaceObjectTypeTag) && !(cht instanceof RemoveTag)) {
CharacterIdTag ch = (CharacterIdTag) cht;
cht.setSwf(this);
ch.setCharacterId(importedId);
cht.setImported(true);
tags.add(p + 1 + pos, cht);
pos++;
}
}
}
int newId = getNextCharacterId();
pos = 0;
for (String key : classesMap2.keySet()) {
int exportedId = classesMap2.get(key);
int importedId = newId++;
for (Tag cht : iSwf.tags) {
if ((cht instanceof CharacterIdTag) && (((CharacterIdTag) cht).getCharacterId() == exportedId) && !(cht instanceof PlaceObjectTypeTag) && !(cht instanceof RemoveTag)) {
CharacterIdTag ch = (CharacterIdTag) cht;
cht.setSwf(this);
ch.setCharacterId(importedId);
cht.setImported(true);
tags.add(p + 1 + pos, cht);
pos++;
}
}
}
updateCharacters();
}
}
}
}
@Override
public SWF getSwf() {
return this;
@@ -2338,6 +2461,7 @@ public final class SWF implements SWFContainerItem, Timelined {
public void clearReadOnlyListCache() {
readOnlyTags = null;
readOnlyLocalTags = null;
for (Tag tag : tags) {
if (tag instanceof DefineSpriteTag) {
((DefineSpriteTag) tag).clearReadOnlyListCache();
@@ -3059,13 +3183,15 @@ public final class SWF implements SWFContainerItem, Timelined {
timelined.setModified(true);
timelined.resetTimeline();
} else // timeline should be always the swf here
if (removeDependencies) {
removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline());
timelined.setModified(true);
} else {
boolean modified = removeTagFromTimeline(tag, timelined.getTimeline());
if (modified) {
{
if (removeDependencies) {
removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline());
timelined.setModified(true);
} else {
boolean modified = removeTagFromTimeline(tag, timelined.getTimeline());
if (modified) {
timelined.setModified(true);
}
}
}
}
@@ -3079,6 +3205,20 @@ public final class SWF implements SWFContainerItem, Timelined {
return readOnlyTags;
}
public ReadOnlyTagList getLocalTags() {
if (readOnlyLocalTags == null) {
List<Tag> localTags = new ArrayList<>();
for (Tag t : tags) {
if (!t.isImported()) {
localTags.add(t);
}
}
readOnlyLocalTags = new ReadOnlyTagList(localTags);
}
return readOnlyLocalTags;
}
/**
* Adds a tag to the SWF
*

View File

@@ -0,0 +1,10 @@
package com.jpexs.decompiler.flash;
/**
*
* @author JPEXS
*/
public interface UrlResolver {
public SWF resolveUrl(String url);
}

View File

@@ -27,7 +27,7 @@ public class Stage extends DisplayObject {
startTime = System.currentTimeMillis();
this.timelined = timelined;
this.timeline = timelined != null ? timelined.getTimeline() : null;
this.frame = timelined != null ? this.timeline.getFrame(0) : null;
this.frame = timelined != null && this.timeline.getFrameCount() > 0 ? this.timeline.getFrame(0) : null;
}
@Override

View File

@@ -359,7 +359,11 @@ public class DefineEditTextTag extends TextTag {
private List<CharacterWithStyle> getTextWithStyle() {
String str = "";
TextStyle style = new TextStyle();
style.font = swf.getFont(fontId);
if (fontClass != null) {
style.font = swf.getFontByClass(fontClass);
} else {
style.font = swf.getFont(fontId);
}
style.fontHeight = fontHeight;
style.fontLeading = leading;
if (hasTextColor) {
@@ -991,10 +995,8 @@ public class DefineEditTextTag extends TextTag {
if (Character.isWhitespace(c)) {
lastWasWhiteSpace = true;
}
} else {
if (multiline) {
textModel.newParagraph();
}
} else if (multiline) {
textModel.newParagraph();
}
prevChar = c;
}
@@ -1096,8 +1098,15 @@ public class DefineEditTextTag extends TextTag {
}
for (SameStyleTextRecord tr : line) {
TEXTRECORD tr2 = new TEXTRECORD();
tr2.styleFlagsHasFont = fontId != 0;
tr2.fontId = fontId;
int fid = fontId;
if (fontClass != null) {
FontTag ft = swf.getFontByClass(fontClass);
if (ft != null) {
fid = ft.getFontId();
}
}
tr2.styleFlagsHasFont = fid != 0;
tr2.fontId = fid;
tr2.textHeight = tr.style.fontHeight;
if (tr.style.textColor != null) {
tr2.styleFlagsHasColor = true;

View File

@@ -28,7 +28,9 @@ import com.jpexs.decompiler.flash.types.annotations.Table;
import com.jpexs.helpers.ByteArrayRange;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Makes portions of a SWF file available for import by other SWF files
@@ -65,6 +67,19 @@ public class ExportAssetsTag extends SymbolClassTypeTag {
names = new ArrayList<>();
}
@Override
public Map<Integer, String> getTagToNameMap() {
Map<Integer, String> exportNames = new HashMap<>();
for (int i = 0; i < tags.size(); i++) {
int tagId = tags.get(i);
String name = names.get(i);
if ((!exportNames.containsKey(tagId)) && (!exportNames.containsValue(name))) {
exportNames.put(tagId, name);
}
}
return exportNames;
}
/**
* Constructor
*

View File

@@ -28,7 +28,9 @@ import com.jpexs.decompiler.flash.types.annotations.Table;
import com.jpexs.helpers.ByteArrayRange;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
@@ -50,6 +52,19 @@ public class SymbolClassTag extends SymbolClassTypeTag {
@Table(value = "symbols", itemName = "symbol")
public List<String> names;
@Override
public Map<Integer, String> getTagToNameMap() {
Map<Integer, String> exportNames = new HashMap<>();
for (int i = 0; i < tags.size(); i++) {
int tagId = tags.get(i);
String name = names.get(i);
if ((!exportNames.containsKey(tagId)) && (!exportNames.containsValue(name))) {
exportNames.put(tagId, name);
}
}
return exportNames;
}
/**
* Constructor
*

View File

@@ -84,6 +84,17 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable {
@Internal
private boolean modified;
@Internal
protected boolean imported = false;
public void setImported(boolean imported) {
this.imported = imported;
}
public boolean isImported() {
return imported;
}
/**
* Original tag data
*/
@@ -573,6 +584,10 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable {
return modified;
}
public boolean isReadOnly() {
return isImported();
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
}

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags.base;
import java.util.Map;

View File

@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.helpers.ByteArrayRange;
import java.util.Map;
/**
*
@@ -29,4 +30,6 @@ public abstract class SymbolClassTypeTag extends Tag {
public SymbolClassTypeTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract Map<Integer, String> getTagToNameMap();
}

View File

@@ -109,6 +109,9 @@ public class Timeline {
public Frame getFrame(int index) {
ensureInitialized();
if (index >= frames.size()) {
return null;
}
return frames.get(index);
}