Subversion Repositories display

Rev

Rev 611 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2007 Steve Ratcliffe
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License 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.
 *
 *
 * Author: Steve Ratcliffe
 * Create date: Dec 16, 2007
 */

package test.display;

import java.io.ByteArrayOutputStream;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import test.files.RoadData;
import test.util.BitReaderLR;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.BitReader;
import uk.me.parabola.imgfmt.app.trergn.Subdivision;

/**
 * Standalone program to display the RGN file.
 *
 * @author Steve Ratcliffe
 */

public class RgnDisplay extends CommonDisplay {
        private static final int FLAG_NETINFO = 0x800000;
        private static final int FLAG_EXTRA = 0x400000;
        private static final int MASK_LABEL = 0x3fffff;

        private static final int NUM_LINK_SIZES = 3;

        private int rgnStart;

        List<HuffmanDecoder> decoders = new ArrayList<>();

        private Section extPolygons;
        private Section extLines;
        private Section extPoints;
        private int polygonsGlbFlags;
        private int linesGlbFlags;
        private int pointsGlbFlags;
        private int[] polygonsLclFlags;
        private int[] linesLclFlags;
        private int[] pointsLclFlags;
        private Section rgn5;
        private HuffmanDecoder huffmanDecoder;
       
        private Map<Integer, HuffmanDecoder> huffmanTables = new HashMap<>();

        @Override
        protected void print() {
                readCommonHeader();
                readHeader();
                if (rgn5 != null) {
                        // read and display Huffman table(s)
                        displayRgn5();
                }
                openLbl();
                openNet();
                if (reader.getGMPOffset() > 0)
                        openNod();
                readBody();

        }

        private void readBody() {
                openTre();

                Displayer d = new Displayer(reader);
                d.setTitle("RGN body");
                d.print(outStream);

                for (int i = 23; i >= 0; i--) {
                        Subdivision[] divInfo = tre.subdivForLevel(i);
                        for (Subdivision sd : divInfo)
                                printDiv(sd);
                }
        }

        private static int numberOfPointers(Subdivision sd) {
                int count = 0;
                if (sd.hasPoints()) count++;
                if (sd.hasIndPoints()) count++;
                if (sd.hasPolylines()) count++;
                if (sd.hasPolygons()) count++;
                if (sd.hasRoadRefs()) count++;
                if (count > 0)
                        count--;
                return count;
        }

        /**
         * Print the section of the rgn file that belongs to the given subdiv.
         *
         * @param subdiv The subdiv. This will contain start and end points in the rgn
         *               file.
         */

        private void printDiv(Subdivision subdiv) {
                Displayer d = new Displayer(reader);
                d.setTitle("Level " + subdiv.getZoom().getLevel() + ", Subdiv " + subdiv.getNumber());

                int offset = subdiv.getStartRgnPointer();
                reader.position(rgnStart + offset);
                d.setSectStart(rgnStart + offset);

                // Although we have start and end points, this is further split into areas
                // for points, lines and polygons etc.
                int n = numberOfPointers(subdiv);
                int[] pointers = new int[n + 2];
                pointers[0] = rgnStart + offset + (2 * n);
                for (int i = 0; i < n; i++) {
                        pointers[i + 1] = rgnStart + offset + d.charValue("Pointer 0x%x");
                }
                pointers[n + 1] = rgnStart + subdiv.getEndRgnPointer();
                d.item().addText("final at %x, (end %x)", pointers[n + 1], subdiv.getEndRgnPointer());

                int count = 0;
                if (subdiv.hasPoints()) {
                        displayPoints(d, subdiv, pointers, count);
                        count++;
                }

                if (subdiv.hasIndPoints()) {
                        displayPoints(d, subdiv, pointers, count);
                        count++;
                }

                if (subdiv.hasPolylines()) {
                        displayLines(d, subdiv, pointers, count, "Line");
                        count++;
                }

                if (subdiv.hasPolygons()) {
                        displayLines(d, subdiv, pointers, count, "Polygon");
                        count++;
                }
                if (subdiv.hasRoadRefs()) {
                        displayRoadRefs(d, subdiv, pointers, count, "RoadRef");
                }
                if (subdiv.getExtTypeAreasSize() > 0) {
                        long start = extPolygons.getStart() + subdiv.getExtTypeAreasOffset();
                        long end = start + subdiv.getExtTypeAreasSize();
                        displayExtLines(d, subdiv, "ext-polygon", start, end);
                }
                if (subdiv.getExtTypeLinesSize() > 0) {
                        long start = extLines.getStart() + subdiv.getExtTypeLinesOffset();
                        long end = start + subdiv.getExtTypeLinesSize();
                        displayExtLines(d, subdiv, "ext-line", start, end);
                }
                if (subdiv.getExtTypePointsSize() > 0) {
                        displayExtPoints(d, subdiv);
                }
                d.print(outStream);
        }

        private void displayRoadRefs(Displayer d, Subdivision subdiv, int[] pointers, int count, String kind) {
                d.item().addText("%s start %x to %x", kind.toLowerCase(), pointers[count], pointers[count + 1]);
                reader.position(pointers[count]);
                if (nod == null)
                        d.rawValue((int) (pointers[count + 1] - reader.position()), "???");
                int roadNo = 0;
                while (reader.position() < pointers[count + 1]) {
                        AtomicInteger varLength = new AtomicInteger();
                        roadNo++;
                        int size = d.varUInt32("size %d", varLength);
                        long pos = reader.position();
                        DisplayItem item = d.byteItem();
                        int flags = item.getValue();
                        int numBits = 0;
                        int[] bits = new int[NUM_LINK_SIZES];
                        for (int i = 0; i < NUM_LINK_SIZES; i++) {
                                switch (flags >> (2 * i) & 0x3) {
                                case 1:
                                        bits[i] = 4;
                                        break;
                                case 2:
                                        bits[i] = 8;
                                        break;
                                case 3:
                                        bits[i] = 10;
                                        break;
                                default:
                                }
                                numBits += bits[i];
                        }
                        item.addText("road %d flags=0x%x -> %d+%d+%d bits", roadNo, flags, bits[0], bits[1], bits[2]);
                       
                        int blockIndexId = d.intValue(nod.getIndexIdSize(), "NOD-blockIndexId %d");
                        item = d.rawItem((numBits + 7) / 8);
                        BitReader br = new BitReader(item.getBytes());
                        int numb8 = bits[0] == 0 ? 0 : br.get(bits[0]) + 1;
                        int numb10 = bits[1] == 0 ? 0 : br.get(bits[1]) + 1;
                        int numb16 = bits[2] == 0 ? 0 : br.get(bits[2]) + 1;

                        int countData = item.getValue();
                        item.addText("counter data 0x%x -> %d x 8-bit, %d x 10-bit, %d x 16-bit entries", countData, numb8, numb10,
                                        numb16);
                        int linkId;
                        int lineId;

                        // read the items, first come 8-bit entries, then 16-bit entries, finally the 10
                        // bit entries
                        int numIds = numb8 + numb10 + numb16;
                        int shift = 0;
                        for (int i = 0; i < numIds; i++) {
                                long posIds = reader.position();
                                if (i < numb8) {
                                        item = d.byteItem();
                                        linkId = item.getValue();
                                        lineId = 0;
                                } else {
                                        int v16 = reader.get2u();
                                        if (i >= numb8 + numb16) {
                                                linkId = (v16 >> shift) & 0xff;
                                                lineId = ((v16 >> (shift + 8)) & 0x3) + 1;
                                                if (shift < 6 && i < numIds - 1)
                                                        reader.position(reader.position() - 1); // reuse for next
                                                shift += 2;
                                                shift &= 7;
                                        } else {
                                                linkId = v16 & 0xff;
                                                lineId = v16 >> 8;
                                                assert lineId > 4; // else a 10 bit entry would be enough
                                        }
                                        int bytesForData = (int) (reader.position() - posIds);
                                        reader.position(posIds);
                                        item = d.rawItem(bytesForData);
                                }
                                item.addText("linkId = %d, lineId = %d", linkId, lineId);
                                // TODO:
//                              blockInfo = nod.getBlockInfo(blockIndexId);
                                // net->link(subdiv, shift, netHdl, nod, nodHdl, nodHdl2, lbl, lblHdl,
                                // blockInfo, linkId, lineId, lines);
                        }

                        assert reader.position() - pos == size;
                }
                d.rawValue((int) (pointers[count + 1] - reader.position()), "???");
                d.print(outStream);
                d.setTitle(null);
        }

        private void displayLines(Displayer d, Subdivision subdiv, int[] pointers, int count, String kind) {
                d.item().addText("%s start %x to %x", kind.toLowerCase(), pointers[count], pointers[count + 1]);
                reader.position(pointers[count]);
                int lineNo = 1;
                while (reader.position() < pointers[count + 1]) {
                        DisplayItem item = d.item();
                        int type = item.setBytes1(reader.get1u());
                        item.addText("%s %d", kind, lineNo++);
                        if ("Line".equals(kind)) {
                                item.addText("Type 0x%x %sdirection", type & 0x3f, (type & 0x40) != 0 ? "has" : "no ");
                        } else
                                item.addText("Type 0x%x", type & 0x7f);
                        boolean twoByteBitLength = false;
                        if ((type & 0x80) != 0)
                                twoByteBitLength = true;

                        item = d.item();
                        int labval = reader.get3u();
                        item.setBytes3(labval);
                        int off = labval & MASK_LABEL;
                        boolean extra = (labval & FLAG_EXTRA) != 0;
                        boolean netinfo = (labval & FLAG_NETINFO) != 0;
                        if (netinfo) {
                                String name = "invalid";
                                RoadData road = net.getRoad(off);
                                if (road != null) {
                                        name = road.getLabel(0).getText();
                                }
                                item.addText("NET offset: 0x%06x (%s)", off, name);
                        } else
                                item.addText("Label offset: 0x%06x (%s)", off, fetchLabel(off));
                        if (extra)
                                d.item().addText("extra bit");

                        short dlon = d.shortValue("Long delta %d");
                        short dlat = d.shortValue("Lat delta %d");
                        int currLat = fixDelta(subdiv, subdiv.getLatitude(), dlat);
                        int currLon = fixDelta(subdiv, subdiv.getLongitude(), dlon);
                        d.item().addText("Start point %.5f,%.5f %d/%d", Utils.toDegrees(currLat), Utils.toDegrees(currLon), currLat,
                                        currLon);

                        int bslen = twoByteBitLength ? d.charValue("Bitstream length (2 byte) %d")
                                        : d.byteValue("Bitstream length %d") & 0xff;

                        displayBitStream(d, bslen + 1, subdiv.getShift(), kind, extra, currLat, currLon);
                        d.gap();
                        d.print(outStream);
                        d.setTitle(null);
                }

                d.print(outStream);
                d.setTitle(null);
        }

        private void displayPoints(Displayer d, Subdivision subdiv, int[] pointers, int count) {
                d.item().addText("point start %x to %x", pointers[count], pointers[count + 1]);
                reader.position(pointers[count]);
                int num = 1;
                while (reader.position() < pointers[count + 1]) {
                        d.item().addText("Point #%d", num++);
                        d.byteValue("Type %x");
                        int val = reader.get3u();
                        DisplayItem item = d.item();
                        item.setBytes3(val);
                        boolean hasSubtype = (val & 0x800000) != 0;
                        boolean hasPoi = (val & 0x400000) != 0;

                        val &= 0x3fffff;
                        if (hasPoi) {
                                item.addText("Poi offset 0x%x", val);
                        }
                        else
                                item.addText("Label offset 0x%x (%s)", val, fetchLabel(val));

                        short dlon = d.shortValue("Long delta %d");
                        short dlat = d.shortValue("Lat delta %d");
                        double startLat = Utils.toDegrees(fixDelta(subdiv, subdiv.getLatitude(), dlat));
                        double startLon = Utils.toDegrees(fixDelta(subdiv, subdiv.getLongitude(), dlon));

                        d.item().addText("Start point %.5f,%.5f", startLat, startLon);
                        if (hasSubtype)
                                d.byteValue("subtype %d");
                        d.gap();
                }
                d.print(outStream);
                d.setTitle(null);

        }

        void displayExtraBytes(Displayer d) {
                ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                byte b1 = reader.get();
                bytes.write(b1);

                if ((b1 & 0xe0) != 0) {
                        // varying length, search for 0x01 as this seems to be the terminator
                        do {
                                b1 = reader.get();
                                bytes.write(b1);
                        } while (b1 != 0x01);
                } else if ((b1 & 0xa0) != 0) {
                        bytes.write(reader.get());
                        bytes.write(reader.get());
                } else if ((b1 & 0x80) != 0) {
                        bytes.write(reader.get());
                }
                DisplayItem item = d.item();
                item.setBytes(bytes.toByteArray());
                item.addText("extra bytes");
        }

        private void displayExtPoints(Displayer d, Subdivision subdiv) {
                long start = extPoints.getStart() + subdiv.getExtTypePointsOffset();
                long end = start + subdiv.getExtTypePointsSize();
                reader.position(start);
                d.item().addText("ext-point start %x to %x", start, end);
                int num = 1;
                while (reader.position() < end) {
                        d.item().addText("Ext-Point #%d", num++);
                        DisplayItem item = d.charItem();
                        int typeData = item.getValue();
                        int type = (typeData & 0xff) << 8;
                        int b1 = (typeData >> 8) & 0xff;
                        boolean hasExtra = (b1 & 0x80) != 0;
                        boolean hasLcl = (b1 & 0x40) != 0;
                        boolean hasLabel = (b1 & 0x20) != 0;
                        type |= 0x10000 + (b1 & 0x1f);
                       
                       
                        item.addText("Type %x hasExtra=%b hasLcl=%b", type, hasExtra, hasLcl);
                       
                        short dlon = d.shortValue("Long delta %d");
                        short dlat = d.shortValue("Lat delta %d");
                        double startLat = Utils.toDegrees(fixDelta(subdiv, subdiv.getLatitude(), dlat));
                        double startLon = Utils.toDegrees(fixDelta(subdiv, subdiv.getLongitude(), dlon));
                       

                        d.item().addText("Start point %.5f,%.5f", startLat, startLon);
                        if (hasLabel) {
                                int val = reader.get3u();
                                boolean hasPoi = (val & 0x400000) != 0;
                                item = d.item();
                                item.setBytes3(val);

                                val &= 0x3fffff;
                                if (hasPoi) {
                                        item.addText("Poi offset 0x%x", val);
                                } else
                                        item.addText("Label offset 0x%x (%s)", val, fetchLabel(val));
                        }
                        if (hasExtra) {
                                displayClassFields(d, "ext-point");
                        }
                        if (hasLcl) {
                                skipLclFields(d, pointsLclFlags);
                        }
                        if (pointsGlbFlags != 0)
                                skipGblFields(d, pointsGlbFlags);
                        d.gap();
                }
                d.print(outStream);
                d.setTitle(null);
               
        }
       
        private void skipLclFields(Displayer d, int[] flags) {
                if (flags[0] == 0)
                        return;
                int mask = 0xFFFFFFFF;
                if ((flags[0] & 0x20000000) != 0) {
                        long pos = reader.position();
                        mask = readVBitMask();
                        int len = (int) (reader.position() - pos);
                        reader.position(pos);
                        DisplayItem item = d.rawItem(len);
                        item.addText(String.format("var length mask 0x%x", mask));
                }
                int j = 0;
                for (int i = 0; i < 29; i++) {
                        if (((flags[0] >> i) & 0x1) != 0) {
                                if ((mask & 0x1) != 0) {
                                        int m = flags[(j >> 4) + 1] >> ((j * 2) & 0x1e) & 3;

                                        int skip = 0;
                                        if (m == 3) {
                                                AtomicInteger varLength = new AtomicInteger();
                                                skip = d.varLength("Lcl length %d", varLength);
                                        } else
                                                skip = m + 1;
                                        d.rawValue(skip, "Lcl data ???");
                                }
                                mask >>= 1;
                                if (mask == 0)
                                        break;
                                j++;
                        }
                }
                d.print(outStream);
                d.setTitle(null);
        }

        /**
         * Read a bit mask stored with varying number of bytes.  
         * @return an int containing the mask
         */

        private int readVBitMask() {
                int peek = reader.get1u();
                int mask;
               
                if ((peek & 0x1) != 0)
                        return (peek >> 1);
                reader.position(reader.position() - 1);
                if ((peek & 0x2) != 0) {
                        mask = reader.get2u();
                        return mask >> 2;
                }
                if ((peek & 0x4) != 0) {
                        mask = reader.get3u();
                } else {
                        mask = reader.get4();
                }
                return mask >> 3;
        }

        public static double toDegrees32(long val) {
                return ((double) val / (double) (1l << 31)) * 180.0;
        }

        private void displayExtLines(Displayer d, Subdivision subdiv, String kind, long start, long end) {
                reader.position(start);
                int num = 0;
                d.item().addText("%s start %x to %x", kind.toLowerCase(), start, end);
                boolean isLine = "ext-line".equals(kind);
                Section section = isLine ? extLines : extPolygons;
                int extraShift = subdiv.getZoom().getLevel() == 0 ? (tre.getTre7Magic() >> 11) & 0x7 : 0;
                final long baseLat32 = ((long) subdiv.getLatitude()) << 8;
                final long baseLon32 = ((long) subdiv.getLongitude()) << 8;
                while (reader.position() < end) {
                        d.item().addText("%s %d", kind, ++num);
                        DisplayItem item = d.charItem();
                        int typeData = item.getValue();
                        int type = (typeData & 0xff) << 8;
                        int b1 = (typeData >> 8) & 0xff;
                        boolean hasExtra = (b1 & 0x80) != 0;
                        boolean hasLcl = (b1 & 0x40) != 0;
                        boolean hasLabel = (b1 & 0x20) != 0;
                        type |= 0x10000 + (b1 & 0x1f);
                       
                        item.addText("Type %x hasExtra=%b hasLcl=%b", type, hasExtra, hasLcl);

                        int dlon = d.shortValue("Long delta %d");
                        int dlat = d.shortValue("Lat delta %d");

                        AtomicInteger varLength = new AtomicInteger();
                        int bslen = d.varLength("Bitstream length %d", varLength);
                        if (reader.position() + bslen > section.getEnd()) {
                                d.item().addText("bitstream length too high?");
                                break;
                        }
                       
                        if (huffmanDecoder != null) {
                                long lat32 = baseLat32 + (dlat << 32 - subdiv.getResolution());
                                long lon32 = baseLon32 + (dlon << 32 - subdiv.getResolution());
                                int dx;
                                int dy;
                               
                                long bspos = reader.position();
                                d.rawValue(bslen, "Huffman encoded delta stream");
                                assert reader.position() == bspos + bslen;
                                reader.position(bspos);
                                BitReaderLR br = new BitReaderLR(reader, bslen);

                                int xSign = 0;
                                int ySign = 0;

                                if (isLine) {
                                        boolean xsame = br.get1() == 0;
                                        boolean xneg = false;
                                        if (!xsame) {
                                                xneg = br.get1() != 0;
                                                xSign = xneg ? -1 : 1;
                                        }
                                        boolean ysame = br.get1() == 0;
                                        boolean yneg = false;
                                        if (!ysame) {
                                                yneg = br.get1() != 0;
                                                ySign = yneg ? -1 : 1;
                                        }
                                        d.item().addText("xsame %b, xneg %b", xsame, xneg);
                                        d.item().addText("ysame %b, yneg %b", ysame, yneg);
                                }
                               
                                int extra = br.get1(); // always 0?
                                if (extra != 0)
                                        assert extra == 0;
                                if (extraShift != 0) {
                                        dx = readOffset(br);
                                        dy = readOffset(br);
                                        lon32 |= dx << (32 - subdiv.getResolution() - extraShift);
                                        lat32 |= dy << (32 - subdiv.getResolution() - extraShift);
                                }
                                d.item().addText("Start point %.6f,%.6f", toDegrees32(lat32), toDegrees32(lon32));
                                while (br.hasRemaining()) {
                                        try {
                                                dx = readDelta(br, xSign);
                                                dy = readDelta(br, ySign);
                                        } catch (BufferUnderflowException e) {
                                                break;
                                        }
                                        lon32 += dx << (32 - subdiv.getResolution() - extraShift);
                                        if (lon32 < 0 && subdiv.getLongitude() >= 0)
                                                lon32 = 0x7fffffff;
                                        lat32 += dy << (32 - subdiv.getResolution() - extraShift);
                                        d.item().addText("Coord %.6f,%.6f [%+d,%+d]", toDegrees32(lat32), toDegrees32(lon32), dx, dy);
                                        d.print(outStream);
                                        d.setTitle(null);
                                }
                                reader.position(bspos + bslen);

                        } else {
                                int currLat = fixDelta(subdiv, subdiv.getLatitude(), dlat);
                                int currLon = fixDelta(subdiv, subdiv.getLongitude(), dlon);
                                d.item().addText("Start point %.5f,%.5f %d/%d", Utils.toDegrees(currLat), Utils.toDegrees(currLon), currLat,
                                                currLon);
                                displayBitStream(d, bslen, subdiv.getShift(), kind, false /* no extra */, currLat, currLon);
                        }
                        if (hasLabel){
                                item = d.item();
                                int labval = reader.get3u();
                                item.setBytes3(labval);
                                int off = labval & MASK_LABEL;
                                item.addText("Label offset: 0x%06x (%s)", off, fetchLabel(off));
                        }
                        if (hasExtra){
                                displayClassFields(d, kind);
                        }
                        if (hasLcl) {
                                skipLclFields(d, isLine ? linesLclFlags : polygonsLclFlags);
                        }
                        int gblFlags = isLine ? linesGlbFlags : polygonsGlbFlags;
                        if (gblFlags != 0)
                                skipGblFields(d, gblFlags);
                       
                        d.gap();
                }
                d.print(outStream);
                d.setTitle(null);
        }

        private int readDelta(BitReaderLR br, int sign) {
                int symbol = huffmanDecoder.readSymbol(br);
                if (symbol != 0 && sign == 0) {
                        int bit = br.get1();
                        sign = bit == 1 ? -1 : 1;
                }
                return sign * symbol;
        }

        private int readOffset(BitReaderLR br) {
                return readDelta(br, 1);
        }

        private static void skipGblFields(Displayer d, int flags) {
                int cnt = 0;

                do {
                        cnt = cnt + (flags & 3);
                        flags = flags >> 2;
                } while (flags != 0);
                d.rawValue(cnt, "gbl data");
        }
       
        private void displayClassFields(Displayer d, String kind) {
                byte b1 = d.byteValue("class field flag?");
                final int rs;
                switch ((b1 & 0xff) >> 5) {
                case 4:
                        rs = 1;
                        break;
                case 5:
                        rs = 2;
                        break;
                case 6:
                        rs = 3;
                        break;
                case 7:
                        AtomicInteger varLength = new AtomicInteger();
                        rs = reader.readVarLength(varLength);
                        break;
                default:
                        rs = 0;
                        break;
                }
                if (rs != 0) {
                        d.rawValue(rs, "class fields data");
                }

        }

        private static boolean displayBitStream(Displayer d, final int bslen, final int shift, final String kind,
                        final boolean extra, final int _currLat, final int _currLon) {
                int base = d.byteValue("Base %x");
                byte[] bytes = d.rawValue(bslen - 1, "Bitstream");
                BitReader br = new BitReader(bytes);

                int currLat = _currLat;
                int currLon = _currLon;

                int xbase = 2;
                int n = base & 0xf;
                if (n <= 9)
                        xbase += n;
                else
                        xbase += (2 * n) - 9;

                n = (base >>> 4) & 0xf;
                int ybase = 2;
                if (n <= 9)
                        ybase += n;
                else
                        ybase += (2 * n) - 9;
               
                if (br.getNumberOfBits() == 0) {
                        d.item().addText("bitstream is empty!");
                        d.gap();
                        return false;
                }

                boolean xneg = false;
                boolean xsame = br.get1();
                if (xsame) {
                        xneg = br.get1();
                } else
                        xbase++;

                boolean ysame = br.get1();
                boolean yneg = false;
                if (ysame) {
                        yneg = br.get1();
                } else
                        ybase++;

                d.item().addText("xsame %b, xneg %b", xsame, xneg);
                d.item().addText("ysame %b, yneg %b", ysame, yneg);
                d.item().addText("xbase %d, ybase %d", xbase, ybase);

                if (extra) {
                        boolean firstextra = br.get1();
                        d.item().addText("first extra bit %b", firstextra);
                }
                if (kind.startsWith("ext-")) {
                        br.get1();
                }
                int needed = xbase + ybase + (extra ? 1 : 0);
                int lastUsedPos = br.getBitPosition();
                while (needed <= br.getNumberOfBits() - br.getBitPosition()) {
                        br.getBitPosition();

                        int dx;
                        if (xsame) {
                                dx = br.get(xbase);
                                if (xneg)
                                        dx = -dx;
                        } else {
                                dx = br.sget2(xbase);
                        }

                        int dy;
                        if (ysame) {
                                dy = br.get(ybase);
                                if (yneg)
                                        dy = -dy;
                        } else {
                                dy = br.sget2(ybase);
                        }
                        boolean isnode = extra && br.get1();
                        if (!isnode && dx == 0 && dy == 0 && br.getNumberOfBits() - br.getBitPosition() < 8 + needed)
                                continue;
                        lastUsedPos = br.getBitPosition();
                        currLat += dy << shift;
                        currLon += dx << shift;
                        double latDeg = Utils.toDegrees(currLat);
                        double lonDeg = Utils.toDegrees(currLon);
                        assert latDeg >= -90 && latDeg <= 90 : "invalid lat in bitstream";
                        assert lonDeg >= -180 && latDeg <= 180 : "invalid lon in bitstream";
                        String extraInf = extra ? String.format(", node=%b", isnode) : "";
                        d.item().addText(String.format(Locale.ENGLISH, "Coord %.5f,%.5f %d/%d%s [%+d,%+d]",
                                        latDeg, lonDeg, currLat, currLon, extraInf, dx,dy));
                }
                int bleft = br.getNumberOfBits() - br.getBitPosition();
               
                d.item().addText((bleft + br.getBitPosition() - lastUsedPos)  + " bits left, " + br.get(bleft));
                return true;
        }

        private static int fixDelta(Subdivision subdiv, int base, int dlat) {
                return base + (dlat << subdiv.getShift());
        }

        private void readHeader() {
                Displayer d = new Displayer(reader);
                d.setTitle("RGN Header");

                Section sect = readSection(d, "RGN 1", 1, false, false);
                rgnStart = (int) sect.getStart();
                int len = sect.getLen();
                d.item().addText("rgn end 0x%06x", rgnStart + len);

                if (getHeaderLen() > 29) {
                        extPolygons = readSection(d, "RGN 2 (extended polygons)", 2, false, true);
                        polygonsGlbFlags = d.intValue("polygons Glb Flags %x");
                        polygonsLclFlags = new int[3];
                        polygonsLclFlags[0] = d.intValue("polygonsLclFlags[0] %x");
                        polygonsLclFlags[1] = d.intValue("polygonsLclFlags[1] %x");
                        polygonsLclFlags[2] = d.intValue("polygonsLclFlags[2] %x");

                        extLines = readSection(d, "RGN 3 (extended lines)", 3, false, true);
                        linesGlbFlags = d.intValue("lines Glb Flags %x");
                        linesLclFlags = new int[3];
                        linesLclFlags[0] = d.intValue("linesLclFlags[0] %x");
                        linesLclFlags[1] = d.intValue("linesLclFlags[1] %x");
                        linesLclFlags[2] = d.intValue("linesLclFlags[2] %x");

                        extPoints = readSection(d, "RGN 4 (extended points)", 4, false, true);
                        pointsGlbFlags = d.intValue("points Glb Flags %x");
                        pointsLclFlags = new int[3];
                        pointsLclFlags[0] = d.intValue("pointsLclFlags[0] %x");
                        pointsLclFlags[1] = d.intValue("pointsLclFlags[1] %x");
                        pointsLclFlags[2] = d.intValue("pointsLclFlags[2] %x");
                }

                if (getHeaderLen() > 101) {
                        rgn5 = readSection(d, "RGN 5", 5, false, true);
                }

                d.rawValue((int) (getHeaderLen() - reader.position() + reader.getGMPOffset()));
                d.print(outStream);
        }

        private void displayRgn5() {

                int r5start = (int) rgn5.getStart();
                int r5len = rgn5.getLen();
                if (r5len == 0)
                        return;

                Displayer d = new Displayer(reader);
                d.setTitle("RGN5 (decompression codebook Huffman trees)");

                reader.position(r5start);
                while (reader.position() < rgn5.getEnd()) {
                        HuffmanDecoder decoder = readHuffmanTable(d, reader.position());
                        decoders.add(decoder);
                }
                d.rawValue((int) (rgn5.getEnd() - reader.position()));
                if ((rgn5.getMagic() & 0x1e) != 0) {
                        int tableId = ((rgn5.getMagic() >> 1) & 0xf) - 1;
                        huffmanDecoder = decoders.get(0);
                        huffmanTables.put(tableId, huffmanDecoder);
                }
                d.print(outStream);

        }

        public static void main(String[] args) {
                if (args.length < 1) {
                        System.err.println("Usage: rgndisplay <filename>");
                        System.exit(1);
                }

                String name = args[0];

                CommonDisplay nd = new RgnDisplay();
                nd.display(name, "RGN");
        }
}