Index: src/uk/me/parabola/imgfmt/app/BitWriterLR.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/BitWriterLR.java	(nonexistent)
+++ src/uk/me/parabola/imgfmt/app/BitWriterLR.java	(working copy)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013-2021.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ */
+
+/**
+ * A class to write the bitstream from left to right.
+ *
+ * @author Gerd Petermann
+ */
+package uk.me.parabola.imgfmt.app;
+
+public class BitWriterLR {
+	private static final int INITIAL_BUF_SIZE = 20;
+
+	// The byte buffer and its current length (allocated length)
+	private byte[] buf;  // The buffer
+	private int bufsize;  // The allocated size
+	private int buflen; // The actual used length
+
+	// The bit offset into the byte array.
+	private int bitoff;
+	private static final int BUFSIZE_INC = 50;
+
+
+	public BitWriterLR() {
+		bufsize = INITIAL_BUF_SIZE;
+		buf = new byte[bufsize];
+	}
+
+	public void put1(boolean bit) {
+		if (bitoff >= 8) {
+			bitoff = 0;
+
+			buflen++;
+			if (buflen >= buf.length) {
+				reallocBuffer();
+			}
+		}
+		if (bit) {
+			int mask = (1 << (7 - bitoff));
+			buf[buflen] |= mask;
+		}
+		bitoff++;
+	}
+
+	public byte[] getBytes() {
+		return buf;
+	}
+
+	public int getBitPosition() {
+		return bitoff;
+	}
+	/**
+	 * Get the number of bytes actually used to hold the bit stream. This therefore can be and usually
+	 * is less than the length of the buffer returned by {@link #getBytes()}.
+	 * @return Number of bytes required to hold the output.
+	 */
+	public int getLength() {
+		return bitoff == 0 ? buflen : buflen + 1;
+	}
+	
+	/**
+	 * Set everything up so that the given size can be accommodated.
+	 * The buffer is re-sized if necessary.
+	 *
+	 * @param newlen The new length of the bit buffer in bits.
+	 */
+	private void ensureSize(int newlen) {
+		if (newlen/8 >= bufsize)
+			reallocBuffer();
+	}
+
+	/**
+	 * Reallocate the byte buffer.
+	 */
+	private void reallocBuffer() {
+		bufsize += BUFSIZE_INC;
+		byte[] newbuf = new byte[bufsize];
+
+		System.arraycopy(this.buf, 0, newbuf, 0, this.buf.length);
+		this.buf = newbuf;
+	}
+	
+}
Index: src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java	(revision 4839)
+++ src/uk/me/parabola/imgfmt/app/mdr/MDRFile.java	(working copy)
@@ -54,6 +54,7 @@
 	private final Mdr13 mdr13;
 	private final Mdr14 mdr14;
 	private final Mdr15 mdr15;
+	private final Mdr16 mdr16;
 	private final Mdr17 mdr17;
 	private final Mdr18 mdr18;
 	private final Mdr19 mdr19;
@@ -114,6 +115,7 @@
 		mdr13 = new Mdr13(config);
 		mdr14 = new Mdr14(config);
 		mdr15 = new Mdr15(config);
+		mdr16 = new Mdr16(config);
 		mdr17 = new Mdr17(config);
 		mdr18 = new Mdr18(config);
 		mdr19 = new Mdr19(config);
@@ -132,12 +134,19 @@
 				null,
 				mdr1, null, null, mdr4, mdr5, mdr6,
 				mdr7, mdr8, mdr9, mdr10, mdr11, mdr12,
-				mdr13, mdr14, mdr15, null, mdr17, mdr18, mdr19,
+				mdr13, mdr14, mdr15, mdr16, mdr17, mdr18, mdr19,
 				mdr20, mdr21, mdr22, mdr23, mdr24, mdr25,
 				mdr26, mdr27, mdr28, mdr29,
 		};
 
 		mdr11.setMdr10(mdr10);
+		if (mdr16.canEncode()) {
+			mdr15.setMdr16(mdr16);
+			for (MdrSection s : sections) {
+				if (s != null)
+					s.setMdr15(mdr15);
+			}
+		}
 	}
 
 	/**
@@ -289,7 +298,9 @@
 	 */
 	private void writeSections(ImgFileWriter writer) {
 		sizes = new MdrMapSection.PointerSizes(sections);
-
+		writeSection(writer, 16, mdr16);
+		writeSection(writer, 15, mdr15);
+		
 		mdr7.trim();
 		// Deal with the dependencies between the sections. The order of the following
 		// statements is sometimes important.
@@ -377,7 +388,6 @@
 		}
 		writeSection(writer, 13, mdr13);
 		writeSection(writer, 14, mdr14);
-		writeSection(writer, 15, mdr15);
 
 		writeSection(writer, 23, mdr23);
 		writeSection(writer, 24, mdr24);
@@ -408,7 +418,7 @@
 	private void writeSection(ImgFileWriter writer, int sectionNumber, MdrSection section) {
 
 		// Some sections are just not written in the device config
-		if (forDevice && Arrays.asList(13, 14, 15, 21, 23, 26, 27, 28).contains(sectionNumber))
+		if (forDevice && Arrays.asList(13, 14, 15, 16, 21, 23, 26, 27, 28).contains(sectionNumber))
 			return;
 
 		section.setSizes(sizes);
Index: src/uk/me/parabola/imgfmt/app/mdr/MDRHeader.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/MDRHeader.java	(revision 4839)
+++ src/uk/me/parabola/imgfmt/app/mdr/MDRHeader.java	(working copy)
@@ -76,7 +76,8 @@
 		sections[13].writeSectionInfo(writer, true, true);
 		sections[14].writeSectionInfo(writer, true, true);
 		sections[15].writeSectionInfo(writer);
-		writer.put1u(0);
+		// compressed/ uncompressed flag is a single byte 
+		writer.put1u(sections[16].getSize() > 0 ? 1 : 0); 
 
 		sections[16].writeSectionInfo(writer, true, true);
 		sections[17].writeSectionInfo(writer, false, true);
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr15.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr15.java	(revision 4839)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr15.java	(working copy)
@@ -19,6 +19,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.charset.Charset;
 import java.util.HashMap;
@@ -44,6 +45,9 @@
 	private Map<String, Integer> strings = new HashMap<>();
 	private final Charset charset;
 	private final File tempFile;
+	int[] freqencies = new int[256];
+	IntBuffer offsets = IntBuffer.allocate(32 * 1024); 
+	Mdr16 mdr16;
 
 	public Mdr15(MdrConfig config) {
 		setConfig(config);
@@ -59,7 +63,8 @@
 			// reserve the string at offset 0 to be the empty string.
 			stringFile.write(0);
 			nextOffset = 1;
-
+			freqencies[0]++;
+			offsets.put(-1);
 		} catch (IOException e) {
 			throw new ExitException("Could not create temporary file");
 		}
@@ -77,7 +82,13 @@
 			byte[] bytes = str.getBytes(charset);
 			stringFile.write(bytes);
 			stringFile.write(0);
-
+			if (mdr16 != null) {
+				for (int i = 0; i < bytes.length; i++) {
+					freqencies[bytes[i] & 0xff]++;
+				}
+				freqencies[0]++;
+			}
+			
 			// Increase offset for the length of the string and the null byte
 			nextOffset += bytes.length + 1;
 		} catch (IOException e) {
@@ -84,7 +95,16 @@
 			// Can't convert, return empty string instead.
 			off = 0;
 		}
-
+		if (mdr16 != null && off != 0) {
+			if (offsets.position() + 1 >= offsets.capacity()) {
+				IntBuffer tmp = offsets;
+				offsets = IntBuffer.allocate(tmp.capacity() * 2);
+				tmp.flip();
+				offsets.put(tmp);
+			}
+			offsets.put(off);
+			off = offsets.position() - 1;
+		}
 		strings.put(str, off);
 		return off;
 	}
@@ -102,15 +122,46 @@
 		} catch (IOException e) {
 			throw new MapFailedException("Could not close string temporary file");
 		}
+		// frequencies from adria topo
+//		int[] fakeFreq = { 108543, 0, 0, 1, 19, 242, 842, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//				0, 0, 0, 195, 139214, 18, 614, 0, 0, 1, 153, 95, 2537, 2558, 1905, 7, 2126, 6588, 10184, 64, 6539,
+//				13281, 8713, 7478, 7203, 6778, 6634, 5972, 5832, 5314, 1, 1, 0, 0, 0, 2, 0, 181900, 33286, 66619, 37462,
+//				99070, 4481, 24930, 12992, 113012, 44778, 63856, 63057, 40220, 79383, 101405, 34866, 80, 95298, 71047,
+//				53719, 31985, 63113, 294, 422, 719, 23284, 0, 0, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 49, 0, 0, 0, 3, 1, 0,
+//				0, 2, 4, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//				0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 2, 0, 43, 0, 34, 4, 0, 51, 0, 0, 0, 0, 5, 0, 0, 0, 0,
+//				0, 5, 0, 2, 0, 1, 2, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//				0, 0, 0, 0, 0, 0, 0, 0 };
+//		freqencies = fakeFreq;
+		if (mdr16 != null)
+			mdr16.calc(freqencies);
 	}
 
 	public void writeSectData(ImgFileWriter writer) {
+		offsets.clear();
+		offsets.put(-1);
 		try (FileInputStream stream = new FileInputStream(tempFile)) {
 			FileChannel channel = stream.getChannel();
 			ByteBuffer buf = ByteBuffer.allocate(32 * 1024);
+			ByteBuffer encoderBuf = ByteBuffer.allocate(1024);
+			int start = writer.position();
 			while (channel.read(buf) > 0) {
 				buf.flip();
-				writer.put(buf);
+				if (mdr16 == null)
+					writer.put(buf);
+				else {
+					while (buf.remaining() > 0) {
+						byte b = buf.get();
+						encoderBuf.put(b);
+						if (b == 0) {
+							offsets.put(writer.position() - start);
+							encoderBuf.flip();
+							writer.put(mdr16.encode(encoderBuf));
+							encoderBuf.clear();
+						}
+					}
+				}
 				buf.compact();
 			}
 		} catch (IOException e) {
@@ -141,4 +192,19 @@
 	protected int numberOfItems() {
 		return 0;
 	}
+
+	/**
+	 * Set reference to compression table
+	 * @param mdr16 reference to compression table
+	 */
+	public void setMdr16(Mdr16 mdr16) {
+		this.mdr16 = mdr16;
+	}
+	
+	public int translateOffset(int off) {
+		if (mdr16 == null)
+			return off;
+		return offsets.array()[off];
+	}
+
 }
Index: src/uk/me/parabola/imgfmt/app/mdr/Mdr16.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/Mdr16.java	(nonexistent)
+++ src/uk/me/parabola/imgfmt/app/mdr/Mdr16.java	(working copy)
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2013-2021.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ */
+package uk.me.parabola.imgfmt.app.mdr;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.sql.PseudoColumnUsage;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.PriorityQueue;
+
+import func.lib.ArrayImgWriter;
+import uk.me.parabola.imgfmt.MapFailedException;
+import uk.me.parabola.imgfmt.app.BitWriterLR;
+import uk.me.parabola.imgfmt.app.ImgFileWriter;
+import uk.me.parabola.imgfmt.app.srt.Sort;
+import uk.me.parabola.log.Logger;
+
+
+public class Mdr16 extends MdrSection implements HasHeaderFlags {
+	private final Sort sort;
+	private final Charset charset;
+	
+	private HuffmanNode root;
+	private final Code[] codes = new Code[256];
+	private final int initBits = 5; // TODO: when do we have/want to use 6 bits?
+
+	// initialize with 
+	byte[] data = new byte[0];
+//	data = {
+//			  (byte) 0xeb , (byte) 0x15 , (byte) 0x0d , (byte) 0x07 , (byte) 0x08 , (byte) 0x29 , (byte) 0x00 , (byte) 0x00
+//			  , (byte) 0x0d , (byte) 0x00 , (byte) 0x04 , (byte) 0x00 , (byte) 0x0c , (byte) 0x04 , (byte) 0x08 , (byte) 0x00
+//			  , (byte) 0x0a , (byte) 0x06 , (byte) 0x10 , (byte) 0x00 , (byte) 0x09 , (byte) 0x07 , (byte) 0x20 , (byte) 0x00
+//			  , (byte) 0x08 , (byte) 0x08 , (byte) 0x80 , (byte) 0x00 , (byte) 0x07 , (byte) 0x0b , (byte) 0x80 , (byte) 0x01
+//			  , (byte) 0x06 , (byte) 0x0f , (byte) 0x00 , (byte) 0x05 , (byte) 0x0a , (byte) 0x06 , (byte) 0x0c , (byte) 0x06
+//			  , (byte) 0x0c , (byte) 0x06 , (byte) 0x0b , (byte) 0x47 , (byte) 0x0b , (byte) 0x54 , (byte) 0x0b , (byte) 0x55
+//			  , (byte) 0x0b , (byte) 0x4d , (byte) 0x0b , (byte) 0x48 , (byte) 0x0b , (byte) 0x2d , (byte) 0x09 , (byte) 0x4c
+//			  , (byte) 0x09 , (byte) 0x4c , (byte) 0x09 , (byte) 0x52 , (byte) 0x09 , (byte) 0x52 , (byte) 0x09 , (byte) 0x53
+//			  , (byte) 0x09 , (byte) 0x53 , (byte) 0x09 , (byte) 0x4e , (byte) 0x09 , (byte) 0x4e , (byte) 0x09 , (byte) 0x49
+//			  , (byte) 0x09 , (byte) 0x49 , (byte) 0x09 , (byte) 0x41 , (byte) 0x09 , (byte) 0x41 , (byte) 0x09 , (byte) 0x4f
+//			  , (byte) 0x09 , (byte) 0x4f , (byte) 0x07 , (byte) 0x45 , (byte) 0x07 , (byte) 0x45 , (byte) 0x07 , (byte) 0x45
+//			  , (byte) 0x07 , (byte) 0x45 , (byte) 0x07 , (byte) 0x00 , (byte) 0x07 , (byte) 0x00 , (byte) 0x07 , (byte) 0x00
+//			  , (byte) 0x07 , (byte) 0x00 , (byte) 0x27 , (byte) 0x28 , (byte) 0x29 , (byte) 0xc7 , (byte) 0x8c , (byte) 0xcb
+//			  , (byte) 0x20 , (byte) 0x51 , (byte) 0x4a , (byte) 0x58 , (byte) 0x59 , (byte) 0x46 , (byte) 0x50 , (byte) 0x57
+//			  , (byte) 0x5a , (byte) 0x43 , (byte) 0x4b , (byte) 0x42 , (byte) 0x56 , (byte) 0x44
+//	};
+	
+
+	/* sample from old Garmin map:
+--------- MDR 16 (decompression codebook Huffman tree) -------------------------
+         |        |                         | Unknown 118 bytes:
+00000238 | 000000 | eb 15 0d 07 08 29 00 00 | ├½....)..
+00000240 | 000008 | 0d 00 04 00 0c 04 08 00 | ........
+00000248 | 000010 | 0a 06 10 00 09 07 20 00 | ...... .
+00000250 | 000018 | 08 08 80 00 07 0b 80 01 | ..┬Ç...┬Ç.
+00000258 | 000020 | 06 0f 00 05 0a 06 0c 06 | ........
+00000260 | 000028 | 0c 06 0b 47 0b 54 0b 55 | ...G.T.U
+00000268 | 000030 | 0b 4d 0b 48 0b 2d 09 4c | .M.H.-.L
+00000270 | 000038 | 09 4c 09 52 09 52 09 53 | .L.R.R.S
+00000278 | 000040 | 09 53 09 4e 09 4e 09 49 | .S.N.N.I
+00000280 | 000048 | 09 49 09 41 09 41 09 4f | .I.A.A.O
+00000288 | 000050 | 09 4f 07 45 07 45 07 45 | .O.E.E.E
+00000290 | 000058 | 07 45 07 00 07 00 07 00 | .E......
+00000298 | 000060 | 07 00 27 28 29 c7 8c cb | ..'()├ç┬î├ï
+000002a0 | 000068 | 20 51 4a 58 59 46 50 57 |  QJXYFPW
+000002a8 | 000070 | 5a 43 4b 42 56 44       | ZCKBVD
+*/	
+	
+	public Mdr16(MdrConfig config) {
+		setConfig(config);
+		sort = config.getSort();
+		charset = sort.getCharset();
+	}
+
+	@Override
+	public void writeSectData(ImgFileWriter writer) {
+		writer.put(data);
+	}
+	
+	@Override
+	public int getItemSize() {
+		return data.length;
+	}
+
+	@Override
+	protected int numberOfItems() {
+		return 1;
+	}
+
+	@Override
+	public int getExtraValue() {
+		return 0;
+	}
+
+	public void calc(int[] freqencies) {
+		root = buildTree(freqencies);
+		printHuffmanTree(0, root);
+		System.out.println();
+		
+		int maxDepth = 0;
+		int code = 0;
+		ByteBuffer remSymbols = ByteBuffer.allocate(256);
+		List<Mdr16Tab> tab1 = new ArrayList<>();
+		for (int depth = 32; depth > initBits; depth--) {
+			ByteBuffer vals = ByteBuffer.allocate(256);
+			displayHuffman(0, depth, vals, root);
+			if (vals.position() > 0) {
+				if (depth > maxDepth)
+					maxDepth = depth;
+				Mdr16Tab tabEntry = new Mdr16Tab();
+				tabEntry.depth = depth;
+				tabEntry.offset = remSymbols.position();
+//				tabEntry.minCode = code;
+				tabEntry.vals = Arrays.copyOf(vals.array(), vals.position());
+				tab1.add(tabEntry);
+				vals.flip();
+				remSymbols.put(vals);
+//				vals.rewind();
+//				while (vals.hasRemaining()) {
+//					addCode(vals.get(), depth, code++);
+//				}
+//				code =Integer.highestOneBit(code) * 2;
+			}
+		}
+		byte[] tab2 = calcTab2(tab1, initBits);
+		recalcCodes(tab1,tab2);
+		
+		
+		ByteBuffer mdr16Bytes = ByteBuffer.allocate(1028);
+		int tab1Width = 5;
+		int neededBytes = remSymbols.position()  + tab2.length + tab1.size()*tab1Width;
+		int remSymbolsSizeBytes = remSymbols.position() >= 128 ? 2 : 1;
+		int headerBytes = neededBytes + remSymbolsSizeBytes + 4; 
+		if (headerBytes >= 128) {
+			mdr16Bytes.put((byte) (headerBytes << 2 & 0xff));
+			mdr16Bytes.put((byte) (headerBytes >> 6));
+		} else {
+			mdr16Bytes.put((byte) (headerBytes * 2 + 1));
+		}
+		mdr16Bytes.put((byte) (0x10 | initBits));
+		mdr16Bytes.put((byte) maxDepth);
+		mdr16Bytes.put((byte) tab1.size());
+		mdr16Bytes.put((byte) 8); // symbol width
+		final int remSymboulsSize = remSymbols.position();
+		if (remSymboulsSize >= 128) {
+			mdr16Bytes.put((byte) (remSymboulsSize << 2 & 0xff));
+			mdr16Bytes.put((byte) (remSymboulsSize >> 6));
+		} else {
+			mdr16Bytes.put((byte) (remSymboulsSize * 2 + 1));
+		}
+		try (ArrayImgWriter writer = new ArrayImgWriter()) {
+			for (Mdr16Tab e : tab1) {
+				writer.putNu(tab1Width - 2, e.minCode);
+				writer.put1u(e.depth);
+				writer.put1u(e.offset);
+			}
+			mdr16Bytes.put(writer.getBytes());
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		mdr16Bytes.put(tab2);
+		remSymbols.flip();
+		mdr16Bytes.put(remSymbols);
+		mdr16Bytes.flip();
+		data = Arrays.copyOf(mdr16Bytes.array(), mdr16Bytes.remaining());
+	}
+
+	private void recalcCodes(List<Mdr16Tab> tab1, byte[] tab2) {
+		int code = 0;
+		for (int i = 0; i < tab2.length; i += 2) {
+			if (tab2[i] % 2 != 0) {
+				code = i/2;
+				break;
+			}
+		}
+		
+		final int lastDepth = tab1.get(0).depth;
+		for (int i = tab1.size() - 1; i >= 0; i--) {
+			Mdr16Tab e = tab1.get(i);
+
+			code = ((code - 1) * 2) + 1;
+			
+			if (i > 0 && code <= e.vals.length) {
+				throw new MapFailedException("too many vals for code");	
+			}
+			
+			for (int k = e.vals.length - 1; k >= 0; k--) {
+				addCode(e.vals[k], e.depth, code--);
+			}
+			code++;
+			e.minCode = code << (lastDepth - e.depth); // why the shifting?
+		}
+	}
+
+	private byte[] calcTab2(List<Mdr16Tab> tab1, int initBits) {
+		int tab2Rows = 1 << 5; 
+		int pos = tab2Rows - 1;
+		byte[] tab2 = new byte[tab2Rows * 2];
+		int idx1 = tab1.size() + 1;
+		int lastIndex = idx1;
+		int carry = 0;
+		
+		for (int depth = 0; depth <= 32; depth++) {
+			ByteBuffer vals = ByteBuffer.allocate(256);
+			displayHuffman(0, depth, vals, root);
+			if (vals.position() == 0)
+				continue;
+			
+			int repeat = 1;
+			if (depth < initBits)
+				repeat = 1 << (initBits - depth);
+			else {
+				idx1--;
+			}
+			if (carry == 0)
+				lastIndex = idx1;
+
+			vals.flip();
+			if (depth <= initBits) {
+				while (vals.remaining() > 0) {
+					byte ch = vals.get();
+					for (int i = 0; i < repeat; i++) {
+						byte v0 = (byte) (depth * 2 + 1);
+						byte v1 = ch;
+						tab2[pos * 2] = v0;
+						tab2[pos * 2 + 1] = v1;
+
+						String prefix = Integer.toBinaryString(pos);
+						// add leading 0 to make it 5 bytes long
+						prefix = "0000000000".substring(0, initBits - prefix.length()) + prefix;
+						Logger.defaultLogger.error(pos, prefix, v0, displayChar(v1));
+						addCode(ch, depth, pos);
+						pos--;
+						if (pos <= 0) {
+							tab2[0] = 0;
+							tab2[1] = (byte) depth;
+							return tab2;
+						}
+					}
+				}
+			} else {
+				int numVals = 2 * (depth - initBits) - carry;
+				while (vals.remaining() >= numVals) {
+					for (int i = 0; i < numVals; i++)
+						vals.get();
+					byte v0 = (byte) (idx1 * 2);
+					byte v1 = (byte) lastIndex;
+					tab2[pos * 2] = v0;
+					tab2[pos * 2 + 1] = v1;
+					carry = 0;
+					String prefix = Integer.toBinaryString(pos);
+					prefix = "0000000000".substring(0, initBits - prefix.length()) + prefix;
+					Logger.defaultLogger.error(pos, prefix, v0, v1);
+					pos--;
+					if (pos <= 0) {
+						tab2[0] = 0;
+						tab2[1] = (byte) idx1;
+						return tab2;
+					}
+
+				}
+				if (carry == 0)
+					lastIndex = idx1;
+
+				carry += vals.remaining();
+				
+			}
+		}
+		// we should not get here
+		Logger.defaultLogger.error("Possibly failed to calculate Mdr16");
+		return tab2; 
+	}
+
+	private void addCode(byte ch, int len, int code) {
+		if (len > initBits) {
+		String prefix = Integer.toBinaryString(code);
+		// add leading 0 to make it 5 bytes long
+		if (prefix.length() < len)
+			prefix = "00000000000000000000000000000".substring(0, len - prefix.length()) + prefix;
+		Logger.defaultLogger.error(prefix, displayChar(ch));
+		}
+		Code coded = new Code();
+		if (len < initBits)
+			coded.bits = code >> initBits - len;
+		else 
+			coded.bits = code;
+		coded.len = len;
+		
+		codes[ch & 0xff] = coded;
+	}
+
+	private HuffmanNode buildTree(int[] freqencies) {
+		PriorityQueue<HuffmanNode> q = new PriorityQueue<>(128, (o1, o2) -> Integer.compare(o1.freq, o2.freq));
+		for (int i = 0; i < freqencies.length; i++) {
+			if (freqencies[i] > 0) {
+				q.add(new HuffmanNode((byte) i, freqencies[i], null, null));
+			}
+		}
+		HuffmanNode parent = null;
+		while (q.size() > 1) {
+			HuffmanNode left = q.remove();
+			HuffmanNode right = q.remove();
+			parent = new HuffmanNode(null, left.freq + right.freq, left, right);
+			q.add(parent);
+		}
+		parent = q.remove();
+		return parent;
+	}
+
+	private void printHuffmanTree(int depth, HuffmanNode node) {
+		if (node == null)
+			return;
+		if (node.ch != null) {
+			Logger.defaultLogger.error(String.format(
+			"depth:%d %s freq: %d", depth, displayChar((byte) (node.ch & 0xff)), node.freq));
+		}
+		printHuffmanTree(depth + 1, node.left);
+		printHuffmanTree(depth + 1, node.right);
+	}
+	
+	private void displayHuffman(int depth, int wantedDepth, ByteBuffer vals, HuffmanNode node) {
+		if (node.ch != null && wantedDepth == depth) {
+			vals.put(node.ch);
+		}
+		if (node.left != null)
+			displayHuffman(depth + 1, wantedDepth, vals, node.left);
+		if (node.right != null)
+			displayHuffman(depth + 1, wantedDepth, vals, node.right);
+	}			
+
+	private String displayChar(byte val) {
+		byte[] bb = { val };
+		CharBuffer cbuf = charset.decode(ByteBuffer.wrap(bb));
+		char v = cbuf.get();
+
+		return String.format("'%c' 0x%02x", v == 0 ? '.' : v, val & 0xff);
+	}
+
+	private static class HuffmanNode {
+		Byte ch;
+		int freq;
+		HuffmanNode left;
+		HuffmanNode right;
+		public HuffmanNode(Byte b, int freq, HuffmanNode l, HuffmanNode r) {
+			this.ch = b;
+			this.freq = freq;
+			this.left = l;
+			this.right = r;
+		}
+	}
+	
+	private static class Mdr16Tab {
+		int depth;
+		int offset;
+		byte[] vals;
+		int minCode;
+		
+		@Override
+		public String toString() {
+			return "depth=" + depth + ", offset=" + offset + ", vals=" + Arrays.toString(vals);
+		}
+	}
+
+	private static class Code {
+		int len;
+		int bits;
+	}
+	
+	public boolean canEncode() {
+//		return false;
+		// TODO: refuse to encode codepage with more than 8 bit like 932    
+		return sort.getCodepage() == 65001 || (sort.getCodepage() >= 1250 && sort.getCodepage() <= 1258); 
+	}
+	
+	public byte[] encode(ByteBuffer buf) {
+		BitWriterLR bw = new BitWriterLR();
+		byte b;
+		while (buf.remaining() > 0) {
+			b = buf.get();
+			Code code = codes[b & 0xff];
+			if (code == null)
+				throw new MapFailedException("invalid code found");
+
+			int bitToWrite = 1 << (code.len - 1);
+			while (bitToWrite != 0) {
+				bw.put1((bitToWrite & code.bits) != 0);
+				bitToWrite >>= 1;
+			}
+		}
+		return Arrays.copyOf(bw.getBytes(), bw.getLength());
+	}
+	
+}
Index: src/uk/me/parabola/imgfmt/app/mdr/MdrSection.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/mdr/MdrSection.java	(revision 4839)
+++ src/uk/me/parabola/imgfmt/app/mdr/MdrSection.java	(working copy)
@@ -24,6 +24,7 @@
 	private boolean released;
 	protected int nItems;
 	private boolean sizeValid;
+	private Mdr15 mdr15;
 
 	/**
 	 * Write out the contents of this section.
@@ -53,6 +54,8 @@
 	}
 
 	protected void putStringOffset(ImgFileWriter writer, int strOff) {
+		if (mdr15 != null)
+			strOff = mdr15.translateOffset(strOff);
 		writer.putNu(sizes.getStrOffSize(), strOff);
 	}
 
@@ -220,4 +223,8 @@
 			return sections[sect].getNumberOfItems();
 		}
 	}
+
+	public void setMdr15(Mdr15 mdr15) {
+		this.mdr15 = mdr15;
+	}
 }
