diff --git a/CHANGELOG.md b/CHANGELOG.md index 9988770bc..1bb6ce601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ All notable changes to this project will be documented in this file. - [#1295], [#116] AS3 dynamic construction via MultinameL - ABC cleaner not properly fixing namespace sets - The hex view does not display bytes if there are too few of them +- SymbolClass export/import did not support obfuscated names ### Changed - Icon of "Deobfuscation options" menu from pile of pills to medkit diff --git a/libsrc/ffdec_lib/lexers/csv.flex b/libsrc/ffdec_lib/lexers/csv.flex new file mode 100644 index 000000000..4b1ebf1d7 --- /dev/null +++ b/libsrc/ffdec_lib/lexers/csv.flex @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010-2021 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +/* Flash assembler language lexer specification */ +package com.jpexs.csv; + +import java.util.List; +import java.util.ArrayList; +import java.io.StringReader; +%% + +%public +%class CsvLexer +%final +%unicode +%ignorecase +%char +%line +%column +%type CsvRow + +%{ + + StringBuilder string = new StringBuilder(); + + List currentRow = new ArrayList<>(); + + public CsvLexer(String source){ + this(new StringReader(source)); + } + +%} + +LineTerminator = \r|\n|\r\n + +InputCharacter = [^\r\n] +Separator = ";" + +%state STRING + +%% + + { + {Separator} "\"" { + currentRow.add(string.toString()); + string.setLength(0); + yybegin(STRING); + } + {Separator} { + currentRow.add(string.toString()); + string.setLength(0); + } + {LineTerminator} { + currentRow.add(string.toString()); + string.setLength(0); + CsvRow ret = new CsvRow(currentRow); + currentRow = new ArrayList<>(); + return ret; + } + . {string.append(yytext());} +} + + { + \"\" { + string.append("\""); + } + \" { + yybegin(YYINITIAL); + } + . {string.append(yytext());} +} + +/* error fallback */ +[^] { } +<> { + if (string == null) { + return null; + } + if (currentRow.isEmpty() && string.length() == 0) { + string = null; + return null; + } + currentRow.add(string.toString()); + string.setLength(0); + CsvRow ret = new CsvRow(currentRow); + currentRow = new ArrayList<>(); + string = null; + return ret; +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/csv/CsvLexer.java b/libsrc/ffdec_lib/src/com/jpexs/csv/CsvLexer.java new file mode 100644 index 000000000..da7ac2950 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/csv/CsvLexer.java @@ -0,0 +1,687 @@ +/* The following code was generated by JFlex 1.6.0 */ + +/* + * Copyright (C) 2010-2021 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +/* Flash assembler language lexer specification */ +package com.jpexs.csv; + +import java.util.List; +import java.util.ArrayList; +import java.io.StringReader; + +/** + * This class is a scanner generated by + * JFlex 1.6.0 + * from the specification file C:/Dropbox/Programovani/JavaSE/FFDec/libsrc/ffdec_lib/lexers/csv.flex + */ +public final class CsvLexer { + + /** This character denotes the end of file */ + public static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int YYINITIAL = 0; + public static final int STRING = 2; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\12\0\1\2\1\5\1\5\1\1\24\0\1\4\30\0\1\3\111\0"+ + "\1\5\u1fa2\0\1\5\1\5\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\udfe6\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\2\0\1\1\2\2\1\3\1\4\1\5\1\6\1\7"; + + private static int [] zzUnpackAction() { + int [] result = new int[10]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\6\0\14\0\22\0\14\0\30\0\14\0\36"+ + "\0\14\0\14"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[10]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int [] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 = + "\1\3\1\4\1\5\1\6\1\3\1\7\1\3\2\7"+ + "\1\3\1\10\1\7\10\0\1\5\7\0\1\11\5\0"+ + "\1\12\1\0"; + + private static int [] zzUnpackTrans() { + int [] result = new int[36]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\2\0\1\11\1\1\1\11\1\1\1\11\1\1\2\11"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[10]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean zzEOFDone; + + /** + * The number of occupied positions in zzBuffer beyond zzEndRead. + * When a lead/high surrogate has been read from the input stream + * into the final zzBuffer position, this will have a value of 1; + * otherwise, it will have a value of 0. + */ + private int zzFinalHighSurrogate = 0; + + /* user code: */ + + StringBuilder string = new StringBuilder(); + + List currentRow = new ArrayList<>(); + + public CsvLexer(String source){ + this(new StringReader(source)); + } + + + + /** + * Creates a new scanner + * + * @param in the java.io.Reader to read input from. + */ + public CsvLexer(java.io.Reader in) { + this.zzReader = in; + } + + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x110000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 62) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + zzEndRead += zzFinalHighSurrogate; + zzFinalHighSurrogate = 0; + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead-zzStartRead); + + /* translate stored positions */ + zzEndRead-= zzStartRead; + zzCurrentPos-= zzStartRead; + zzMarkedPos-= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length - zzFinalHighSurrogate) { + /* if not: blow it up */ + char newBuffer[] = new char[zzBuffer.length*2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + zzEndRead += zzFinalHighSurrogate; + zzFinalHighSurrogate = 0; + } + + /* fill the buffer with new input */ + int requested = zzBuffer.length - zzEndRead; + int totalRead = 0; + while (totalRead < requested) { + int numRead = zzReader.read(zzBuffer, zzEndRead + totalRead, requested - totalRead); + if (numRead == -1) { + break; + } + totalRead += numRead; + } + + if (totalRead > 0) { + zzEndRead += totalRead; + if (totalRead == requested) { /* possibly more input available */ + if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { + --zzEndRead; + zzFinalHighSurrogate = 1; + } + } + return false; + } + + // totalRead = 0: End of stream + return true; + } + + + /** + * Closes the input stream. + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + * + * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). + * Lexical state is set to ZZ_INITIAL. + * + * Internal scan buffer is resized down to its initial length, if it has grown. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEOFDone = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = 0; + zzFinalHighSurrogate = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + if (zzBuffer.length > ZZ_BUFFERSIZE) + zzBuffer = new char[ZZ_BUFFERSIZE]; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead+pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public CsvRow yylex() throws java.io.IOException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char [] zzBufferL = zzBuffer; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar+= zzMarkedPosL-zzStartRead; + + boolean zzR = false; + int zzCh; + int zzCharCount; + for (zzCurrentPosL = zzStartRead ; + zzCurrentPosL < zzMarkedPosL ; + zzCurrentPosL += zzCharCount ) { + zzCh = Character.codePointAt(zzBufferL, zzCurrentPosL, zzMarkedPosL); + zzCharCount = Character.charCount(zzCh); + switch (zzCh) { + case '\u000B': + case '\u000C': + case '\u0085': + case '\u2028': + case '\u2029': + yyline++; + yycolumn = 0; + zzR = false; + break; + case '\r': + yyline++; + yycolumn = 0; + zzR = true; + break; + case '\n': + if (zzR) + zzR = false; + else { + yyline++; + yycolumn = 0; + } + break; + default: + zzR = false; + yycolumn += zzCharCount; + } + } + + if (zzR) { + // peek one character ahead if it is \n (if we have counted one line too much) + boolean zzPeek; + if (zzMarkedPosL < zzEndReadL) + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + else if (zzAtEOF) + zzPeek = false; + else { + boolean eof = zzRefill(); + zzEndReadL = zzEndRead; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + if (eof) + zzPeek = false; + else + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + } + if (zzPeek) yyline--; + } + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + // set up zzAction for empty match case: + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + } + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) { + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); + zzCurrentPosL += Character.charCount(zzInput); + } + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); + zzCurrentPosL += Character.charCount(zzInput); + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 1: + { string.append(yytext()); + } + case 8: break; + case 2: + { currentRow.add(string.toString()); + string.setLength(0); + CsvRow ret = new CsvRow(currentRow); + currentRow = new ArrayList<>(); + return ret; + } + case 9: break; + case 3: + { currentRow.add(string.toString()); + string.setLength(0); + } + case 10: break; + case 4: + { + } + case 11: break; + case 5: + { yybegin(YYINITIAL); + } + case 12: break; + case 6: + { currentRow.add(string.toString()); + string.setLength(0); + yybegin(STRING); + } + case 13: break; + case 7: + { string.append("\""); + } + case 14: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + { + if (string == null) { + return null; + } + if (currentRow.isEmpty() && string.length() == 0) { + string = null; + return null; + } + currentRow.add(string.toString()); + string.setLength(0); + CsvRow ret = new CsvRow(currentRow); + currentRow = new ArrayList<>(); + string = null; + return ret; + } + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/csv/CsvRow.java b/libsrc/ffdec_lib/src/com/jpexs/csv/CsvRow.java new file mode 100644 index 000000000..459b116fe --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/csv/CsvRow.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-2025 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.csv; + +import java.util.List; + +/** + * + * @author JPEXS + */ +public class CsvRow { + public List values; + + public CsvRow(List values) { + this.values = values; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/csv/package-info.java b/libsrc/ffdec_lib/src/com/jpexs/csv/package-info.java new file mode 100644 index 000000000..f8ad648c3 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/csv/package-info.java @@ -0,0 +1,5 @@ +/** + * CSV files parsing + */ +package com.jpexs.csv; + diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java index c76dbd3b9..0a40118cd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SymbolClassExporter.java @@ -72,12 +72,12 @@ public class SymbolClassExporter { if (t instanceof ExportAssetsTag) { ExportAssetsTag eat = (ExportAssetsTag) t; for (int i = 0; i < eat.tags.size(); i++) { - writer.append(eat.tags.get(i) + ";" + eat.names.get(i) + Helper.newLine); + writer.append(eat.tags.get(i) + ";\"" + Helper.escapePCodeString(eat.names.get(i)).replace("\"", "\"\"") + "\"" + Helper.newLine); } } else if (t instanceof SymbolClassTag) { SymbolClassTag sct = (SymbolClassTag) t; for (int i = 0; i < sct.tags.size(); i++) { - writer.append(sct.tags.get(i) + ";" + sct.names.get(i) + Helper.newLine); + writer.append(sct.tags.get(i) + ";\"" + Helper.escapePCodeString(sct.names.get(i)).replace("\"", "\"\"") + "\"" + Helper.newLine); } } if (CancellableWorker.isInterrupted()) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java index e81a4a819..1de914263 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SymbolClassImporter.java @@ -16,14 +16,19 @@ */ package com.jpexs.decompiler.flash.importers; +import com.jpexs.csv.CsvLexer; +import com.jpexs.csv.CsvRow; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.helpers.Helper; import java.io.File; +import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * SymbolClass importer. @@ -34,18 +39,31 @@ public class SymbolClassImporter { /** * Imports SymbolClasses from a file. + * * @param importFile File to import from * @param swf SWF to import to */ public void importSymbolClasses(File importFile, SWF swf) { String texts = Helper.readTextFile(importFile.getPath()); - String[] lines = texts.split(Helper.newLine); + + CsvLexer lexer = new CsvLexer(texts); Map nameMap = new HashMap<>(); - for (String line : lines) { - String[] pair = line.split(";"); - int characterId = Integer.parseInt(pair[0]); - String name = pair[1]; - nameMap.put(characterId, name); + try { + while (true) { + CsvRow row = lexer.yylex(); + if (row == null) { + break; + } + if (row.values.size() >= 2) { + try { + nameMap.put(Integer.parseInt(row.values.get(0)), row.values.get(1)); + } catch (NumberFormatException nfe) { + //ignore + } + } + } + } catch (IOException ex) { + //ignore } for (Tag tag : swf.getTags()) { diff --git a/libsrc/ffdec_lib/test/com/jpexs/csv/CsvLexerTest.java b/libsrc/ffdec_lib/test/com/jpexs/csv/CsvLexerTest.java new file mode 100644 index 000000000..37446c601 --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/csv/CsvLexerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010-2025 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.csv; + +import java.util.Arrays; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * + * @author JPEXS + */ +public class CsvLexerTest { + + @Test + public void testNoNewlineAtTheEnd() throws Exception { + CsvLexer lexer = new CsvLexer("a;b;c\r\nd;e;f"); + CsvRow row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("a", "b", "c")); + row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("d", "e", "f")); + row = lexer.yylex(); + Assert.assertNull(row); + } + + @Test + public void testNewlineAtTheEnd() throws Exception { + CsvLexer lexer = new CsvLexer("a;b;c\r\nd;e;f\r\n"); + CsvRow row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("a", "b", "c")); + row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("d", "e", "f")); + row = lexer.yylex(); + Assert.assertNull(row); + } + + @Test + public void testDifferentColumns() throws Exception { + CsvLexer lexer = new CsvLexer("a;b;c\r\nd;e"); + CsvRow row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("a", "b", "c")); + row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("d", "e")); + row = lexer.yylex(); + Assert.assertNull(row); + } + + @Test + public void testQuotes() throws Exception { + CsvLexer lexer = new CsvLexer("a;\"b b2; b3\";c\r\nd;e;f"); + CsvRow row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("a", "b b2; b3", "c")); + row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("d", "e", "f")); + row = lexer.yylex(); + Assert.assertNull(row); + } + + @Test + public void testQuotesInQuotes() throws Exception { + CsvLexer lexer = new CsvLexer("a;\"b \"\"b2\"\" b3\";c\r\nd;e;f"); + CsvRow row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("a", "b \"b2\" b3", "c")); + row = lexer.yylex(); + Assert.assertEquals(row.values, Arrays.asList("d", "e", "f")); + row = lexer.yylex(); + Assert.assertNull(row); + } +}