Index: src/uk/me/parabola/mkgmap/CommandArgs.java
===================================================================
--- src/uk/me/parabola/mkgmap/CommandArgs.java	(revision 4807)
+++ src/uk/me/parabola/mkgmap/CommandArgs.java	(working copy)
@@ -27,7 +27,7 @@
 		for (String listOpt : Arrays.asList("mdr7-excl", "mdr7-del", "poi-excl-index", "location-autofill",
 				"overview-levels", "levels", "name-tag-list", "polygon-size-limits", "dem", "dem-dists", "drive-on",
 				"dead-ends", "add-pois-to-lines", "coastlinefile", "generate-sea", "nearby-poi-rules",
-				"line-types-with-direction")) {
+				"line-types-with-direction", "gmapi-minimal")) {
 			stringToList(get(listOpt,  null), listOpt);
 		}
 	}
Index: src/uk/me/parabola/mkgmap/combiners/FileInfo.java
===================================================================
--- src/uk/me/parabola/mkgmap/combiners/FileInfo.java	(revision 4807)
+++ src/uk/me/parabola/mkgmap/combiners/FileInfo.java	(working copy)
@@ -32,6 +32,7 @@
 import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.BufferedImgFileReader;
 import uk.me.parabola.imgfmt.app.lbl.LBLFileReader;
+import uk.me.parabola.imgfmt.app.map.MapReader;
 import uk.me.parabola.imgfmt.app.srt.Sort;
 import uk.me.parabola.imgfmt.app.trergn.TREFileReader;
 import uk.me.parabola.imgfmt.app.trergn.TREHeader;
@@ -85,6 +86,7 @@
 	private int codePage;
 	private int sortOrderId;
 	private int demsize;
+	private MapReader mapReader;
 
 	private FileInfo(String filename, FileKind kind) {
 		this.filename = filename;
@@ -475,4 +477,17 @@
 	public void setDemsize(int demsize) {
 		this.demsize = demsize;
 	}
+	
+	public MapReader getMapReader() throws FileNotFoundException {
+		if (mapReader == null)
+			mapReader = new MapReader(filename);
+		return mapReader;
+	}
+	
+	public void closeMapReader() {
+		if (mapReader != null) {
+			Utils.closeFile(mapReader);
+			mapReader = null;
+		}
+	}
 }
Index: src/uk/me/parabola/mkgmap/combiners/GmapiBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/combiners/GmapiBuilder.java	(revision 4807)
+++ src/uk/me/parabola/mkgmap/combiners/GmapiBuilder.java	(working copy)
@@ -22,6 +22,7 @@
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -34,6 +35,7 @@
 import uk.me.parabola.imgfmt.fs.FileSystem;
 import uk.me.parabola.imgfmt.fs.ImgChannel;
 import uk.me.parabola.imgfmt.sys.ImgFS;
+import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.CommandArgs;
 
 import static java.nio.file.StandardOpenOption.*;
@@ -45,9 +47,11 @@
  * each .img file.
  */
 public class GmapiBuilder implements Combiner {
+	private static final Logger log = Logger.getLogger(GmapiBuilder.class);
 	private static final String NS = "http://www.garmin.com/xmlschemas/MapProduct/v1";
 
 	private final Map<String, Combiner> combinerMap;
+	private final Map<String, String> sourceMap;
 
 	private Path gmapDir;
 	private final Map<Integer, ProductInfo> productMap = new HashMap<>();
@@ -58,8 +62,13 @@
 
 	private String typFile;
 
-	public GmapiBuilder(Map<String, Combiner> combinerMap) {
+	private boolean forceWrite;
+	private List<String> mustWritePatterns;
+
+
+	public GmapiBuilder(Map<String, Combiner> combinerMap, Map<String, String> sourceMap) {
 		this.combinerMap = combinerMap;
+		this.sourceMap = sourceMap;
 	}
 
 	/**
@@ -75,6 +84,12 @@
 		productVersion = (short) args.get("product-version", 100);
 
 		gmapDir = Paths.get(args.getOutputDir(), String.format("%s.gmap", familyName));
+		forceWrite = args.exists("gmapi");
+		
+		String forceWritePatterns = args.get("gmapi-minimal", "auto");
+		if ("auto".equals(forceWritePatterns)) 
+			forceWritePatterns = null;
+		mustWritePatterns = CommandArgs.stringToList(forceWritePatterns, "gmapi-minimal");
 	}
 
 	/**
@@ -93,8 +108,10 @@
 
 		// Unzip the image into the product tile directory.
 		try {
-			if (info.isImg())
-				unzipImg(fn, mapname, productId);
+			if (info.isImg()) {
+				if (forceWrite || shouldWrite(info))
+					unzipImg(fn, mapname, productId);
+			}
 			else if (info.getKind() == FileKind.TYP_KIND)
 				typFile = info.getFilename();
 
@@ -103,6 +120,23 @@
 		}
 	}
 
+	private boolean shouldWrite(FileInfo info) {
+		String fn = info.getFilename();
+		String source = sourceMap.get(fn);
+		if (!source.equals(fn)) {
+			log.diagnostic("gmapi-minimal: Writing freshly compiled file " + fn);
+			return true;
+		}
+		for (String pattern : mustWritePatterns) {
+			if (fn.matches(pattern)) {
+				log.diagnostic("gmapi-minimal: Writing old file " + fn + " because it matches pattern " + pattern);
+				return true;
+			}
+		}
+		log.diagnostic("gmapi-minimal: Skipping file " + fn);
+		return false;
+	}
+
 	/**
 	 * The complete map set has been processed.  Finish off anything that needs
 	 * doing.
@@ -157,7 +191,7 @@
 		unzipImg(srcImgName, destDir);
 	}
 
-	private static void unzipImg(String srcImgName, Path destDir) throws IOException {
+	private void unzipImg(String srcImgName, Path destDir) throws IOException {
 		FileSystem fs = ImgFS.openFs(srcImgName);
 		for (DirectoryEntry ent : fs.list()) {
 			String fullname = ent.getFullName();
@@ -168,7 +202,8 @@
 					continue;
 
 				Files.createDirectories(destDir);
-				copyToFile(f, destDir.resolve(name));
+				Path out = destDir.resolve(name);
+				copyToFile(f, out);
 			}
 		}
 	}
Index: src/uk/me/parabola/mkgmap/combiners/MdrBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/combiners/MdrBuilder.java	(revision 4807)
+++ src/uk/me/parabola/mkgmap/combiners/MdrBuilder.java	(working copy)
@@ -155,9 +155,8 @@
 		mdrFile.addMap(info.getHexname(), info.getCodePage());
 
 		String filename = info.getFilename();
-		MapReader mr = null;
 		try {
-			mr = new MapReader(filename);
+			MapReader mr = info.getMapReader();
 
 			AreaMaps maps = new AreaMaps();
 
@@ -172,8 +171,6 @@
 			addZips(mr);
 		} catch (FileNotFoundException e) {
 			throw new ExitException("Could not open " + filename + " when creating mdr file");
-		} finally {
-			Utils.closeFile(mr);
 		}
 	}
 
Index: src/uk/me/parabola/mkgmap/combiners/TdbBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/combiners/TdbBuilder.java	(revision 4807)
+++ src/uk/me/parabola/mkgmap/combiners/TdbBuilder.java	(working copy)
@@ -135,10 +135,9 @@
 		for (String m : msgs)
 			tdb.addCopyright(m);
 
-		MapReader mapReader = null;
 		String filename = finfo.getFilename();
 		try{
-			mapReader = new MapReader(filename);
+			MapReader mapReader = finfo.getMapReader();
 
 			msgs = mapReader.getCopyrights();
 			boolean found = false;
@@ -157,8 +156,6 @@
 
 		} catch (FileNotFoundException e) {
 			throw new ExitException("Could not open " + filename + " when creating tdb file");
-		} finally {
-			Utils.closeFile(mapReader);
 		}
 
 
Index: src/uk/me/parabola/mkgmap/main/Main.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/Main.java	(revision 4807)
+++ src/uk/me/parabola/mkgmap/main/Main.java	(working copy)
@@ -104,6 +104,7 @@
 	private volatile int programRC = 0;
 
 	private final Map<String, Combiner> combinerMap = new HashMap<>();
+	private final Map<String, String> sourceMap = new HashMap<>();
 	private boolean informationDisplayed = false;
 
 	/**
@@ -297,6 +298,7 @@
 			}
 		});
 		task.setArgs(args);
+		task.setSource(filename);
 		futures.add(task);
 	}
 
@@ -618,6 +620,7 @@
 		final Map<String, Integer> nameToHex = new HashMap<>();
 		for (FilenameTask f : filenames) {
 			if (f.getFilename().endsWith(".img")) {
+				sourceMap.put(f.getFilename(), f.getSource());
 				int hex;
 				try {
 					hex = FileInfo.getFileInfo(f.getFilename() ).getHexname();
@@ -675,6 +678,7 @@
 						continue;
 					c.onMapEnd(fileInfo);
 				}
+				fileInfo.closeMapReader();
 			} catch (FileNotFoundException e) {
 				throw new MapFailedException("could not open file " + e.getMessage());
 			}
@@ -706,10 +710,13 @@
 		boolean indexOpt = args.exists("index");
 		boolean gmapsuppOpt = args.exists("gmapsupp");
 		boolean tdbOpt = args.exists("tdbfile");
-		boolean gmapiOpt = args.exists("gmapi");
+		boolean gmapiOpt = args.exists("gmapi") || args.exists("gmapi-minimal"); 
 		boolean nsisOpt = args.exists("nsis");
 
-		for (String opt : Arrays.asList("gmapi", "nsis")) {
+		if (args.exists("gmapi") && args.exists("gmapi-minimal")) {
+			throw new ExitException("Options --gmapi and --gmapi-minimal are mutually exclusive");
+		}
+		for (String opt : Arrays.asList("gmapi", "nsis", "gmapi-minimal")) {
 			if (!createTdbFiles && args.exists(opt)) {
 				throw new ExitException("Options --" + opt  + " and --no-tdbfiles are mutually exclusive");
 			}
@@ -729,7 +736,7 @@
 			addCombiner("mdx", new MdxBuilder());
 		}
 		if (gmapiOpt) {
-			addCombiner("gmapi", new GmapiBuilder(combinerMap));
+			addCombiner("gmapi", new GmapiBuilder(combinerMap, sourceMap));
 		}
 		if (nsisOpt) {
 			addCombiner("nsis", new NsisBuilder(combinerMap));
@@ -772,6 +779,7 @@
 	private static class FilenameTask extends FutureTask<String> {
 		private CommandArgs args;
 		private String filename;
+		private String source;
 
 		private FilenameTask(Callable<String> callable) {
 			super(callable);
@@ -794,7 +802,16 @@
 		}
 
 		public String toString() {
-			return filename;
+			return source + " -> " + filename;
 		}
+		
+		public void setSource(String source) {
+			this.source = source;
+		}
+
+		public String getSource() {
+			return source;
+		}
+
 	}
 }
