From a5383766efe0bc04bfb43e78e9ce65742e20f181 Mon Sep 17 00:00:00 2001 From: Vhati Date: Tue, 5 Dec 2017 22:42:31 -0500 Subject: [PATCH] Added tolerance of optional FTL 1.6.1 root tags when patching --- skel_common/readme_modders.txt | 6 ++ .../vhati/modmanager/core/ModUtilities.java | 96 +++++++++++++++---- .../vhati/modmanager/ui/ModXMLSandbox.java | 39 +++----- 3 files changed, 101 insertions(+), 40 deletions(-) diff --git a/skel_common/readme_modders.txt b/skel_common/readme_modders.txt index 41569d0..2cf97ff 100644 --- a/skel_common/readme_modders.txt +++ b/skel_common/readme_modders.txt @@ -39,6 +39,12 @@ The Append Extension When you're not overriding something, try to use unique names, so that it won't clobber another mod and vice versa. + FTL 1.6.1 introduced ... root tags wrapping XML files. If present, + Slipstream will remove them, append, and put them back afterward. Special + tags (see "Advanced XML" below) will be unaware of them. Mod files are not + required to include these root tags, though they can. Slipstream will remove + those as well. + General diff --git a/src/main/java/net/vhati/modmanager/core/ModUtilities.java b/src/main/java/net/vhati/modmanager/core/ModUtilities.java index 2cf342a..e65718f 100644 --- a/src/main/java/net/vhati/modmanager/core/ModUtilities.java +++ b/src/main/java/net/vhati/modmanager/core/ModUtilities.java @@ -35,7 +35,9 @@ import net.vhati.modmanager.core.SloppyXMLParser; import ar.com.hjg.pngj.PngReader; +import org.jdom2.Content; import org.jdom2.Document; +import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.JDOMParseException; import org.jdom2.input.SAXBuilder; @@ -85,7 +87,7 @@ public class ModUtilities { byte[] buf = new byte[4096]; int len; ByteArrayOutputStream tmpData = new ByteArrayOutputStream(); - while ( (len = is.read(buf)) >= 0 ) { + while ( (len = is.read( buf )) >= 0 ) { tmpData.write( buf, 0, len ); } byte[] allBytes = tmpData.toByteArray(); @@ -166,6 +168,10 @@ public class ModUtilities { * tacked on as-is. Any xml declaration tags will be scrubbed from both * streams, and a new one will be prepended. * + * If the mainStream had <FTL> tags (introduced in FTL 1.6.1), they + * will be scrubbed, and new ones will be added after appending. If + * appendStream has those tags, they will be scrubbed. + * * The two InputStreams are read, and the combined result is returned as a * new third InputStream. * @@ -182,13 +188,27 @@ public class ModUtilities { * the source of new content to append as the first argument). */ public static InputStream appendXMLFile( InputStream mainStream, InputStream appendStream, String encoding, String mainDescription, String appendDescription ) throws IOException { - Pattern xmlDeclPtn = Pattern.compile( "<[?]xml [^>]*?[?]>\n*" ); + // XML declaration, or root FTL tags. + Pattern comboPtn = Pattern.compile( "(<[?]xml [^>]*?[?]>\n*)|()" ); + Matcher m = null; + boolean mainHadRootTags = false; String mainText = decodeText( mainStream, mainDescription ).text; - mainText = xmlDeclPtn.matcher( mainText ).replaceFirst( "" ); + StringBuffer mainBuf = new StringBuffer( mainText.length() ); + m = comboPtn.matcher( mainText ); + while ( m.find() ) { + if ( m.group( 2 ) != null ) mainHadRootTags = true; + m.appendReplacement( mainBuf, "" ); + } + m.appendTail( mainBuf ); String appendText = decodeText( appendStream, appendDescription ).text; - appendText = xmlDeclPtn.matcher( appendText ).replaceFirst( "" ); + StringBuffer appendBuf = new StringBuffer( appendText.length() ); + m = comboPtn.matcher( appendText ); + while ( m.find() ) { + m.appendReplacement( appendBuf, "" ); + } + m.appendTail( appendBuf ); // Concatenate, filtering the stream to standardize newlines and encode. // @@ -197,10 +217,12 @@ public class ModUtilities { Writer writer = new EOLWriter( new OutputStreamWriter( tmpData, encoder ), "\r\n" ); writer.append( "\n" ); - writer.append( mainText ); + if ( mainHadRootTags ) writer.append( "\n" ); + writer.append( mainBuf ); writer.append( "\n\n\n\n" ); - writer.append( appendText ); + writer.append( appendBuf ); writer.append( "\n" ); + if ( mainHadRootTags ) writer.append( "\n" ); writer.flush(); InputStream result = new ByteArrayInputStream( tmpData.toByteArray() ); @@ -211,6 +233,10 @@ public class ModUtilities { /** * Appends and modifies mainStream, using content from appendStream. * + * If the mainStream had <FTL> tags (introduced in FTL 1.6.1), they + * will be scrubbed, and new ones will be added after appending. If + * appendStream has those tags, they will be scrubbed. + * * The two InputStreams are read, and the combined result * is returned as a new third InputStream. * @@ -227,19 +253,43 @@ public class ModUtilities { * @see net.vhati.modmanager.core.SloppyXMLOutputProcessor */ public static InputStream patchXMLFile( InputStream mainStream, InputStream appendStream, String encoding, boolean globalPanic, String mainDescription, String appendDescription ) throws IOException, JDOMException { - Pattern xmlDeclPtn = Pattern.compile( "<[?]xml [^>]*?[?]>\n*" ); + // XML declaration, or root FTL tags. + Pattern comboPtn = Pattern.compile( "(<[?]xml [^>]*?[?]>\n*)|()" ); + Matcher m = null; + boolean mainHadRootTags = false; + String wrapperOpenTag = ""; + String wrapperCloseTag = ""; + StringBuffer buf = null; String mainText = decodeText( mainStream, mainDescription ).text; - mainText = xmlDeclPtn.matcher( mainText ).replaceFirst( "" ); - mainText = ""+ mainText +""; - Document mainDoc = parseStrictOrSloppyXML( mainText, mainDescription+" (wrapped)" ); + buf = new StringBuffer( wrapperOpenTag.length() + mainText.length() + wrapperCloseTag.length() ); + buf.append( wrapperOpenTag ); + m = comboPtn.matcher( mainText ); + while ( m.find() ) { + if ( m.group( 2 ) != null ) mainHadRootTags = true; + m.appendReplacement( buf, "" ); + } + m.appendTail( buf ); + buf.append( wrapperCloseTag ); mainText = null; + Document mainDoc = parseStrictOrSloppyXML( buf, mainDescription+" (wrapped)" ); + buf.setLength( 0 ); String appendText = decodeText( appendStream, appendDescription ).text; - appendText = xmlDeclPtn.matcher( appendText ).replaceFirst( "" ); - appendText = ""+ appendText +""; - Document appendDoc = parseStrictOrSloppyXML( appendText, appendDescription+" (wrapped)" ); + buf.ensureCapacity( wrapperOpenTag.length() + appendText.length() + wrapperCloseTag.length() ); + buf.append( wrapperOpenTag ); + m = comboPtn.matcher( appendText ); + while ( m.find() ) { + m.appendReplacement( buf, "" ); + } + m.appendTail( buf ); + buf.append( wrapperCloseTag ); appendText = null; + Document appendDoc = parseStrictOrSloppyXML( buf, appendDescription+" (wrapped)" ); + buf.setLength( 0 ); + + buf.trimToSize(); // Free the buffer. + buf = null; XMLPatcher patcher = new XMLPatcher(); patcher.setGlobalPanic( globalPanic ); @@ -247,6 +297,20 @@ public class ModUtilities { mainDoc = null; appendDoc = null; + // Add FTL tags and move all content inside them. + // Collect live getContent() results in an Arraylist to avoid + // ConcurrentModificationException when detaching in the loop. + if ( mainHadRootTags ) { + Element mergedRoot = mergedDoc.getRootElement(); + Element ftlNode = new Element( "FTL" ); + List mergedContentList = new ArrayList( mergedRoot.getContent() ); + for ( Content c : mergedContentList ) { // + c.detach(); + } + ftlNode.addContent( mergedContentList ); + mergedRoot.addContent( ftlNode ); + } + // Bake XML into text, filtering the stream to standardize newlines and encode. // TODO: sloppyPrint() needs EOL normalizing!? // @@ -704,7 +768,7 @@ public class ModUtilities { "" ) ); } - m.appendReplacement( dstBuf, m.quoteReplacement(m.group( 2 ).replaceAll( "[^\n]", "" )) ); // Strip comments, but preserve line count. + m.appendReplacement( dstBuf, m.quoteReplacement( m.group( 2 ).replaceAll( "[^\n]", "" ) ) ); // Strip comments, but preserve line count. } m.appendTail( dstBuf ); tmpBuf = srcBuf; srcBuf = dstBuf; dstBuf = tmpBuf; dstBuf.setLength( 0 ); @@ -718,7 +782,7 @@ public class ModUtilities { ReportMessage.ERROR, "<"+ m.group( 1 ) +"...>..." ) ); - m.appendReplacement( dstBuf, m.quoteReplacement("<"+ m.group( 1 ) + m.group( 2 ) +">"+ m.group( 3 ) +"") ); + m.appendReplacement( dstBuf, m.quoteReplacement( "<"+ m.group( 1 ) + m.group( 2 ) +">"+ m.group( 3 ) +"" ) ); } } m.appendTail( dstBuf ); @@ -744,7 +808,7 @@ public class ModUtilities { ReportMessage.ERROR, "..." ) ); - m.appendReplacement( dstBuf, m.quoteReplacement(m.group(1) +"") ); + m.appendReplacement( dstBuf, m.quoteReplacement( m.group( 1 ) +"" ) ); } m.appendTail( dstBuf ); tmpBuf = srcBuf; srcBuf = dstBuf; dstBuf = tmpBuf; dstBuf.setLength( 0 ); diff --git a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java b/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java index bc5724d..bb25d81 100644 --- a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java +++ b/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java @@ -11,6 +11,7 @@ import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.File; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; import java.io.FileNotFoundException; @@ -63,7 +64,6 @@ import net.vhati.modmanager.core.SloppyXMLOutputProcessor; import net.vhati.modmanager.core.XMLPatcher; import net.vhati.modmanager.ui.ClipboardMenuMouseListener; -import org.jdom2.Document; import org.jdom2.JDOMException; @@ -73,7 +73,7 @@ import org.jdom2.JDOMException; public class ModXMLSandbox extends JFrame implements ActionListener { private UndoManager undoManager = new UndoManager(); - private Document mainDoc = null; + private String mainText = null; private File datsDir; @@ -344,17 +344,11 @@ public class ModXMLSandbox extends JFrame implements ActionListener { if ( innerPath == null ) return; is = pack.getInputStream( innerPath ); - String mainText = ModUtilities.decodeText( is, pack.getName()+":"+innerPath ).text; + InputStream rebuiltStream = ModUtilities.rebuildXMLFile( is, "windows-1252", pack.getName()+":"+innerPath ); + String rebuiltText = ModUtilities.decodeText( rebuiltStream, "Sandbox Main XML" ).text; is.close(); - mainText = mainText.replaceFirst( "<[?]xml [^>]*?[?]>", "" ); - mainText = ""+ mainText +""; - mainDoc = ModUtilities.parseStrictOrSloppyXML( mainText, "Sandbox Main XML" ); - - StringWriter writer = new StringWriter(); - SloppyXMLOutputProcessor.sloppyPrint( mainDoc, writer, null ); - String displayedText = writer.toString().replaceAll( "\r(?!\n)|(?]*?[?]>", "" ); - appendText = ""+ appendText +""; - Document appendDoc = ModUtilities.parseStrictOrSloppyXML( appendText, "Sandbox Append XML" ); + InputStream appendStream = new ByteArrayInputStream( appendText.getBytes( "UTF-8" ) ); - XMLPatcher patcher = new XMLPatcher(); - patcher.setGlobalPanic( false ); - Document resultDoc = patcher.patch( mainDoc, appendDoc ); + InputStream resultStream = ModUtilities.patchXMLFile( mainStream, appendStream, "windows-1252", false, "Sandbox Main XML", "Sandbox Append XML" ); + String resultText = ModUtilities.decodeText( resultStream, "Sandbox Result XML" ).text; - StringWriter writer = new StringWriter(); - SloppyXMLOutputProcessor.sloppyPrint( resultDoc, writer, null ); - String displayedText = writer.toString().replaceAll( "\r(?!\n)|(?