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
???:
- 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:
- Changed mod list to a table with checkboxes
- Instead of extracting to temp, mod data is transferred directly into dats
- Added a GUI progress bar during patching
- 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:
- Added support for windows-1252 ANSI and UTF-16 text in mods
- Added periodic updates to the catalog of mod metadata
- Added ini setting: update_catalog
- 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
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
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
text as Windows-1252 ANSI.
Slipstream will tolerate UTF-16 and Windows-1252 ANSI.
Unless you're overriding something, try to use unique names in your xml
so that it won't clobber another mod and vice versa. 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.
Dos style (CR-LF) line endings are preferred. The game only partially
accepts the unix style (LF): fine for xml, crashing for layout.txt.
Slipstream will convert both to CR-LF as it patches.
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).
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 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_AUTHOR = "Vhati";
@ -34,8 +34,8 @@ public class FTLModManager {
public static void main( String[] args ) {
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( System.getProperty("java.vm.name") +", "+ System.getProperty("java.version") );
log.debug( String.format( "%s %s", System.getProperty("os.name"), System.getProperty("os.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" );
@ -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 += " 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 ) {
log.error( "Error saving config to "+ configFile.getPath(), e );

View file

@ -233,6 +233,21 @@ public class ModPatchThread extends Thread {
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 {
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" );
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
* 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.
*/
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;
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();
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( tmpData, "UTF-8" ) );
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.write( mergedString );
bw.flush();
InputStream result = new ByteArrayInputStream( tmpData.toByteArray() );
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.
*
@ -215,7 +256,7 @@ public class ModUtilities {
) );
modValid = false;
}
else if ( innerPath.endsWith( ".png" ) ) {
else if ( innerPath.endsWith( "[.]png" ) ) {
try {
PngReader pngr = new PngReader( zis );
@ -249,9 +290,7 @@ public class ModUtilities {
modValid = false;
}
}
else if ( innerPath.matches( "^.*(?:.xml.append|.append.xml|.xml)$" ) ) {
if ( innerPath.matches( "^.*(?:.xml.append|.append.xml)$" ) )
seenAppend = true;
else if ( innerPath.matches( "^.*(?:[.]xml[.]append|[.]append[.]xml|[.]xml|[.]txt)$" ) ) {
DecodeResult decodeResult = ModUtilities.decodeText( zis, modFile.getName()+":"+innerPath );
@ -263,6 +302,15 @@ public class ModUtilities {
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>();
Map<Pattern,String> oddCharSuggestions = new HashMap<Pattern,String>();
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.
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 ) {
pendingMsgs.add( new ReportMessage(
ReportMessage.NESTED_BLOCK,
xmlReport.text
) );
Report xmlReport = validateModXML( decodeResult.text, formatter );
if ( xmlReport.text.length() > 0 ) {
pendingMsgs.add( new ReportMessage(
ReportMessage.NESTED_BLOCK,
xmlReport.text
) );
}
if ( xmlReport.outcome == false )
modValid = false;
}
if ( xmlReport.outcome == false )
modValid = false;
}
if ( !pendingMsgs.isEmpty() ) {
@ -644,17 +697,32 @@ public class ModUtilities {
*
* text - The decoded string.
* encoding - The encoding used.
* eol - A constant describing the original line endings.
* bom - The BOM bytes found, or null.
*/
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 encoding;
public final int eol;
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.encoding = encoding;
this.eol = eol;
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";
}
}
}