Added LF to CR-LF conversion, and warnings

This commit is contained in:
Vhati 2013-08-24 03:38:22 -04:00
parent 5f289a0bef
commit 9b34c5119f
5 changed files with 126 additions and 32 deletions

View file

@ -1,13 +1,17 @@
Changelog Changelog
???:
- Added LF to CR-LF conversion for *.xml.append, *.xml, and *.txt
- Added a Validate warning for text files with LF line endings
1.0: 1.0:
- Changed mod list to a table with checkboxes - Changed mod list to a table with checkboxes
- Instead of extracting to temp, mod data is transferred directly into dats - Instead of extracting to temp, mod data is transferred directly into dats
- Added a GUI progress bar during patching - Added a GUI progress bar during patching
- Added a Validate warning for paths with non-ASCII chars - Added a Validate warning for paths with non-ASCII chars
- Added support for windows-1252 ANSI and UTF-16 text in mods
Changes shared with Grognaks Mod Manager 1.8: Changes shared with Grognaks Mod Manager 1.8:
- Added support for windows-1252 ANSI and UTF-16 text in mods
- Added periodic updates to the catalog of mod metadata - Added periodic updates to the catalog of mod metadata
- Added ini setting: update_catalog - Added ini setting: update_catalog
- Added a log warning during patching if a mod gets clobbered - Added a log warning during patching if a mod gets clobbered

View file

@ -30,17 +30,22 @@ The Append Extension
to your pleasure by writing an event of the same name. Whenever multiple to your pleasure by writing an event of the same name. Whenever multiple
tags share the same name, only the last one counts. tags share the same name, only the last one counts.
When you're not overriding something, try to use unique names, so that
it won't clobber another mod and vice versa.
General General
When developing a mod, save your text files as ANSI/ASCII, or UTF-8. When developing a mod, save your text files as ANSI/ASCII, or UTF-8.
UTF-16 is tolerated. If all else fails, Slipstream will try decoding Slipstream will tolerate UTF-16 and Windows-1252 ANSI.
text as Windows-1252 ANSI.
Unless you're overriding something, try to use unique names in your xml Dos style (CR-LF) line endings are preferred. The game only partially
so that it won't clobber another mod and vice versa. File and directory accepts the unix style (LF): fine for xml, crashing for layout.txt.
names must be plain ASCII (no accents). That restriction isn't confirmed Slipstream will convert both to CR-LF as it patches.
for the game, but the mod manager enforces it just to be safe.
File and directory names must be plain ASCII (no accents). That
restriction isn't confirmed for the game, but the mod manager enforces
it just to be safe.
Images should be 32bit PNGs (24bit color + 8bit alpha transparency). Images should be 32bit PNGs (24bit color + 8bit alpha transparency).
Things that *should* be opaque rectangles like backgrounds may vary, Things that *should* be opaque rectangles like backgrounds may vary,

View file

@ -26,7 +26,7 @@ public class FTLModManager {
private static final Logger log = LogManager.getLogger(FTLModManager.class); private static final Logger log = LogManager.getLogger(FTLModManager.class);
private static final String APP_NAME = "Slipstream Mod Manager"; private static final String APP_NAME = "Slipstream Mod Manager";
private static final ComparableVersion APP_VERSION = new ComparableVersion( "1.0" ); private static final ComparableVersion APP_VERSION = new ComparableVersion( "???" );
private static final String APP_URL = "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=17102"; private static final String APP_URL = "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=17102";
private static final String APP_AUTHOR = "Vhati"; private static final String APP_AUTHOR = "Vhati";
@ -34,8 +34,8 @@ public class FTLModManager {
public static void main( String[] args ) { public static void main( String[] args ) {
log.debug( String.format( "%s v%s", APP_NAME, APP_VERSION ) ); log.debug( String.format( "%s v%s", APP_NAME, APP_VERSION ) );
log.debug( System.getProperty("os.name") +" "+ System.getProperty("os.version") +" "+ System.getProperty("os.arch") ); log.debug( String.format( "%s %s", System.getProperty("os.name"), System.getProperty("os.version") ) );
log.debug( System.getProperty("java.vm.name") +", "+ System.getProperty("java.version") ); log.debug( String.format( "%s, %s, %s", System.getProperty("java.vm.name"), System.getProperty("java.version"), System.getProperty("os.arch") ) );
File configFile = new File( "modman.cfg" ); File configFile = new File( "modman.cfg" );
@ -154,7 +154,9 @@ public class FTLModManager {
configComments += " update_catalog - If true, periodically download descriptions for the latest mods. If invalid, you'll be prompted.\n"; configComments += " update_catalog - If true, periodically download descriptions for the latest mods. If invalid, you'll be prompted.\n";
configComments += " use_default_ui - If true, no attempt will be made to resemble a native GUI. Default: false.\n"; configComments += " use_default_ui - If true, no attempt will be made to resemble a native GUI. Default: false.\n";
config.store( new OutputStreamWriter( out, "UTF-8" ), configComments ); OutputStreamWriter writer = new OutputStreamWriter( out, "UTF-8" );
config.store( writer, configComments );
writer.flush();
} }
catch ( IOException e ) { catch ( IOException e ) {
log.error( "Error saving config to "+ configFile.getPath(), e ); log.error( "Error saving config to "+ configFile.getPath(), e );

View file

@ -233,6 +233,21 @@ public class ModPatchThread extends Thread {
moddedItems.add( innerPath ); moddedItems.add( innerPath );
} }
} }
else if ( fileName.endsWith( ".xml" ) || fileName.endsWith( ".txt" ) ) {
String innerPath = checkCase( item.getName(), knownPaths, knownPathsLower );
// Normalize line endings for other text files to CR-LF.
InputStream fixedStream = ModUtilities.setLineEndings( zis, "\r\n", modFile.getName()+":"+parentPath+fileName );
if ( !moddedItems.contains(innerPath) )
moddedItems.add( innerPath );
else
log.warn( String.format( "Clobbering earlier mods: %s", innerPath ) );
if ( ftlP.contains( innerPath ) )
ftlP.remove( innerPath );
ftlP.add( innerPath, fixedStream );
}
else { else {
String innerPath = checkCase( item.getName(), knownPaths, knownPathsLower ); String innerPath = checkCase( item.getName(), knownPaths, knownPathsLower );

View file

@ -112,8 +112,17 @@ public class ModUtilities {
} }
} }
// Determine the original line endings.
int eol = DecodeResult.EOL_NONE;
Matcher m = Pattern.compile( "(\r(?!\n))|((?<!\r)\n)|(\r\n)" ).matcher( result );
if ( m.find() ) {
if ( m.group(3) != null ) eol = DecodeResult.EOL_CRLF;
else if ( m.group(2) != null ) eol = DecodeResult.EOL_LF;
else if ( m.group(1) != null ) eol = DecodeResult.EOL_CR;
}
result = result.replaceAll( "\r(?!\n)|\r\n", "\n" ); result = result.replaceAll( "\r(?!\n)|\r\n", "\n" );
return new DecodeResult( result, encoding, bom ); return new DecodeResult( result, encoding, eol, bom );
} }
@ -126,6 +135,8 @@ public class ModUtilities {
* The returned stream is a ByteArrayInputStream * The returned stream is a ByteArrayInputStream
* which doesn't need closing. * which doesn't need closing.
* *
* The result will be UTF-8 with CR-LF line endings.
*
* The description arguments identify the streams for log messages. * The description arguments identify the streams for log messages.
*/ */
public static InputStream appendXMLFile( InputStream srcStream, InputStream dstStream, String srcDescription, String dstDescription ) throws IOException { public static InputStream appendXMLFile( InputStream srcStream, InputStream dstStream, String srcDescription, String dstDescription ) throws IOException {
@ -137,20 +148,50 @@ public class ModUtilities {
String dstText = decodeText( dstStream, dstDescription ).text; String dstText = decodeText( dstStream, dstDescription ).text;
dstText = xmlDeclPtn.matcher(dstText).replaceFirst( "" ); dstText = xmlDeclPtn.matcher(dstText).replaceFirst( "" );
StringBuilder buf = new StringBuilder( srcText.length() +100+ dstText.length() );
buf.append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" );
buf.append( dstText );
buf.append( "\n\n<!-- Appended by GMM -->\n\n" );
buf.append( srcText );
buf.append( "\n" );
String mergedString = Pattern.compile("\n").matcher( buf ).replaceAll("\r\n");
ByteArrayOutputStream tmpData = new ByteArrayOutputStream(); ByteArrayOutputStream tmpData = new ByteArrayOutputStream();
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( tmpData, "UTF-8" ) ); BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( tmpData, "UTF-8" ) );
bw.write( mergedString );
bw.write( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" );
bw.write( dstText );
bw.write( "\n\n<!-- Appended by GMM -->\n\n");
bw.write( srcText );
bw.write( "\n" );
bw.flush(); bw.flush();
InputStream result = new ByteArrayInputStream( tmpData.toByteArray() ); InputStream result = new ByteArrayInputStream( tmpData.toByteArray() );
return result; return result;
} }
/**
* Calls decodeText() on a stream, replaces line endings, and re-encodes.
*
* The returned stream is a ByteArrayInputStream
* which doesn't need closing.
*
* The result will be UTF-8 with the desired line endings.
*
* The description argument identifies the stream for log messages.
*/
public static InputStream setLineEndings( InputStream srcStream, String eol, String srcDescription ) throws IOException {
// decodeText() returns a LF string.
String srcText = decodeText( srcStream, srcDescription ).text;
String fixedText = Pattern.compile("\n").matcher( srcText ).replaceAll( Matcher.quoteReplacement(eol) );
ByteArrayOutputStream tmpData = new ByteArrayOutputStream();
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( tmpData, "UTF-8" ) );
bw.write( fixedText );
bw.flush();
InputStream result = new ByteArrayInputStream( tmpData.toByteArray() );
return result;
}
/** /**
* Checks a mod file for common problems. * Checks a mod file for common problems.
* *
@ -215,7 +256,7 @@ public class ModUtilities {
) ); ) );
modValid = false; modValid = false;
} }
else if ( innerPath.endsWith( ".png" ) ) { else if ( innerPath.endsWith( "[.]png" ) ) {
try { try {
PngReader pngr = new PngReader( zis ); PngReader pngr = new PngReader( zis );
@ -249,9 +290,7 @@ public class ModUtilities {
modValid = false; modValid = false;
} }
} }
else if ( innerPath.matches( "^.*(?:.xml.append|.append.xml|.xml)$" ) ) { else if ( innerPath.matches( "^.*(?:[.]xml[.]append|[.]append[.]xml|[.]xml|[.]txt)$" ) ) {
if ( innerPath.matches( "^.*(?:.xml.append|.append.xml)$" ) )
seenAppend = true;
DecodeResult decodeResult = ModUtilities.decodeText( zis, modFile.getName()+":"+innerPath ); DecodeResult decodeResult = ModUtilities.decodeText( zis, modFile.getName()+":"+innerPath );
@ -263,6 +302,15 @@ public class ModUtilities {
modValid = false; modValid = false;
} }
if ( decodeResult.eol != DecodeResult.EOL_CRLF &&
decodeResult.eol != DecodeResult.EOL_NONE ) {
pendingMsgs.add( new ReportMessage(
ReportMessage.ERROR,
String.format( "%s line endings (CR-LF is safest)", decodeResult.getEOLName() )
) );
modValid = false;
}
List<Pattern> oddCharPtns = new ArrayList<Pattern>(); List<Pattern> oddCharPtns = new ArrayList<Pattern>();
Map<Pattern,String> oddCharSuggestions = new HashMap<Pattern,String>(); Map<Pattern,String> oddCharSuggestions = new HashMap<Pattern,String>();
Map<Pattern,List<Character>> oddCharLists = new HashMap<Pattern,List<Character>>(); Map<Pattern,List<Character>> oddCharLists = new HashMap<Pattern,List<Character>>();
@ -310,17 +358,22 @@ public class ModUtilities {
// TODO: Nag if there are chars FTL can't show. // TODO: Nag if there are chars FTL can't show.
Report xmlReport = validateModXML( decodeResult.text, formatter ); if ( innerPath.matches( "^.*(?:[.]xml[.]append|[.]append[.]xml|[.]xml)$" ) ) {
if ( innerPath.matches( "^.*(?:[.]xml[.]append|[.]append[.]xml)$" ) )
seenAppend = true;
if ( xmlReport.text.length() > 0 ) { Report xmlReport = validateModXML( decodeResult.text, formatter );
pendingMsgs.add( new ReportMessage(
ReportMessage.NESTED_BLOCK, if ( xmlReport.text.length() > 0 ) {
xmlReport.text pendingMsgs.add( new ReportMessage(
) ); ReportMessage.NESTED_BLOCK,
xmlReport.text
) );
}
if ( xmlReport.outcome == false )
modValid = false;
} }
if ( xmlReport.outcome == false )
modValid = false;
} }
if ( !pendingMsgs.isEmpty() ) { if ( !pendingMsgs.isEmpty() ) {
@ -644,17 +697,32 @@ public class ModUtilities {
* *
* text - The decoded string. * text - The decoded string.
* encoding - The encoding used. * encoding - The encoding used.
* eol - A constant describing the original line endings.
* bom - The BOM bytes found, or null. * bom - The BOM bytes found, or null.
*/ */
public static class DecodeResult { public static class DecodeResult {
public static final int EOL_NONE = 0;
public static final int EOL_CRLF = 1;
public static final int EOL_LF = 2;
public static final int EOL_CR = 3;
public final String text; public final String text;
public final String encoding; public final String encoding;
public final int eol;
public final byte[] bom; public final byte[] bom;
public DecodeResult( String text, String encoding, byte[] bom ) { public DecodeResult( String text, String encoding, int eol, byte[] bom ) {
this.text = text; this.text = text;
this.encoding = encoding; this.encoding = encoding;
this.eol = eol;
this.bom = bom; this.bom = bom;
} }
public String getEOLName() {
if ( eol == EOL_CRLF ) return "CR-LF";
if ( eol == EOL_LF ) return "LF";
if ( eol == EOL_CR ) return "CR";
return "None";
}
} }
} }