Subversion Repositories display

Rev

Rev 360 | 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.FileNotFoundException;
import java.util.Formatter;

import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.BitReader;
import uk.me.parabola.imgfmt.app.BufferedImgFileReader;
import uk.me.parabola.imgfmt.app.ImgFileReader;
import uk.me.parabola.imgfmt.app.trergn.TREFileReader;
import uk.me.parabola.imgfmt.app.trergn.TREHeader;
import uk.me.parabola.imgfmt.fs.ImgChannel;
import uk.me.parabola.mkgmap.general.LevelInfo;

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

public class RgnDisplay extends CommonDisplay {
//      private static final Logger log = Logger.getLogger(CommonDisplay.class);

        private static final int FLAG_NETINFO = 0x800000;
        private static final int FLAG_EXTRA = 0x400000;
        private static final int MASK_LABEL = 0x3fffff;

        private int rgnStart;

        private Section levels;
        private Section subdivs;
        protected final LevelInfo[] linfo = new LevelInfo[24];
        protected final Subdiv[][] divinfo = new Subdiv[24][];

        protected void print() {
                readCommonHeader();
                openLbl();

                readHeader();
                readBody();
        }

        private void readBody() {
                openTre();

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

                for (int i = 23; i >= 0; i--) {

                        LevelInfo li = linfo[i];
                        if (li == null)
                                continue;
                        int level = li.getLevel();

                        for (Subdiv sd : divinfo[level])
                                printDiv(sd);

                }
        }

        /**
         * We probably don't need this now that tre has a method to read subdivisions.
         */

        protected void openTre() {
                ImgChannel chan;
                try {
                        chan = findFile("TRE");
                        tre = new TREFileReader(chan);

                } catch (FileNotFoundException e) {
                        System.err.println("Could not open TRE file");
                        return;
                }

                TREHeader header = (TREHeader) tre.getHeader();
                int pos = header.getMapLevelsPos();
                int size = header.getMapLevelsSize();
                levels = new Section("Levels", pos, size);
                levels.setRecordSize(4);

                pos = header.getSubdivPos();
                size = header.getSubdivSize();
                subdivs = new Section("subdivs", pos, size);
                readLevel(new BufferedImgFileReader(chan));
        }

        /**
         * 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(Subdiv subdiv) {
                Displayer d = new Displayer(reader);
                d.setTitle("Level " + subdiv.getLevel() + ", Subdiv " + subdiv.getNumber());

                int offset = subdiv.getRgnOffset();
                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 = subdiv.numberOfPointers();
                int[] pointers = new int[n+2];
                pointers[0] = rgnStart + offset + (2 * n);
                for (int i = 0; i < n; i++) {
                        pointers[i + 1] = rgnStart + offset + (int) d.charValue("Pointer 0x%x");
                }
                pointers[n + 1] = rgnStart + subdiv.getEndRgnOffset();
                d.item().addText("final at %x, (end %x)", pointers[n + 1], subdiv.getEndRgnOffset());

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

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

                if (subdiv.hasLine()) {
                        displayLines(d, subdiv, pointers, count);
                        count++;
                }

                if (subdiv.hasShape()) {
                        displayLines(d, subdiv, pointers, count);
                }
                d.print(outStream);
        }

        private void displayLines(Displayer d, Subdiv subdiv, int[] pointers, int count) {
                d.item().addText("line start %x to %x", 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.setBytes(reader.get());
                        item.addText("Line %d", lineNo++);
                        item.addText("Type 0x%x", type & 0x7f);
                        //int type = d.byteValue("Type %x");
                        boolean twoByteBitLength = false;
                        if ((type & 0x80) != 0)
                                twoByteBitLength = true;
                       
                        item = d.item();
                        int labval = reader.get3();
                        item.setBytes3(labval);
                        int off = labval & MASK_LABEL;
                        boolean extra = (labval & FLAG_EXTRA) != 0;
                        boolean netinfo = (labval & FLAG_NETINFO) != 0;
                        if (netinfo)
                                item.addText("NET offset: 0x%06x", off);
                        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;
                        int base = d.byteValue("Base %x");

                        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 (bslen == 0) {
                                d.item().addText("bitstream length 0!");
                                d.gap();
                                break;
                        }

                        byte[] bytes = d.rawValue(bslen, "Bitstream");
                        BitReader br = new BitReader(bytes);

                        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);
                        }
                        while (br.getBitPosition() <= 8* bslen - ((extra ? 1:0) + xbase + ybase)) {
                                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);
                                }

                                currLat += dy << (24 - subdiv.getBits());
                                currLon += dx << (24 - subdiv.getBits());
                                Formatter fmt = new Formatter();
                                fmt.format("Coord %.5f,%.5f %d/%d", Utils.toDegrees(currLat), Utils.toDegrees(currLon), currLat, currLon);

                                if (extra) {
                                        boolean isnode = br.get1();
                                        fmt.format(", node=%b", isnode);
                                }

                                fmt.format(" [%+d,%+d]", dx, dy);

                                d.item().addText(fmt.toString());
                        }
                        int bleft = 8*bslen - br.getBitPosition();
                        d.item().addText(bleft + " bits left, " + br.get(bleft));

                        d.gap();
                }

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

        private void displayPoints(Displayer d, Subdiv 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.get3();
                        DisplayItem item = d.item();
                        item.setBytes3(val);
                        boolean hasSubtype = false;
                        if ((val & 0x800000) != 0)
                                hasSubtype = true;
                        boolean hasPoi = false;
                        if ((val & 0x400000) != 0)
                                hasPoi = true;

                        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();
                }
        }

        private int fixDelta(Subdiv subdiv, int base, short dlat) {
                return base + (dlat << (24 - subdiv.getBits()));
        }

        private void readHeader() {
                int remain = getHeaderLen() - (int) reader.position();
                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);

                remain -= 8;
                if (getHeaderLen() > 29) {
                        int rgn2 = d.intValue("rgn2 %x");
                        len = d.intValue("rgn2 size %d");
                        d.item().addText("rgn2 end 0x%06x", rgn2 + len);
                        remain -= 8;

                        d.rawValue(20);
                        remain -= 20;

                        int rgn3 = d.intValue("rgn3 0x%06x");
                        len = d.intValue("rgn3 size %d");
                        d.item().addText("rgn3 end 0x%06x", rgn3 + len);
                        remain -= 8;

                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        remain -= 20;

                        int rgn4 = d.intValue("rgn4 0x%06x");
                        len = d.intValue("rgn4 end %d");
                        d.item().addText("rgn4 end 0x%06x", rgn4 + len);
                        remain -= 8;

                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        d.intValue("unknown %d");
                        remain -= 32;
                }
                assert remain == 0 : remain;
                d.rawValue(remain);
                d.print(outStream);
        }

        private void readLevel(ImgFileReader treader) {
                treader.position(levels.getStart());
                long end = levels.getEnd();
                int lowestLevel = 24;
                while (treader.position() < end) {
                        int level = treader.get();
                        int nbits = treader.get();
                        int ndivs = treader.getChar();
                        level &= 0x7f;

                        LevelInfo li = new LevelInfo(level, nbits);
                        linfo[level] = li;

                        Subdiv[] divs = new Subdiv[ndivs];
                        divinfo[level] = divs;
                        lowestLevel = level;
                }

                treader.position(subdivs.getStart());

                int subdivNumber = 1;
                int lastRgnOffset = treader.get3();
                for (int level = 23; level >=0; level--) {
                        Subdiv[] divs = divinfo[level];
                        if (divs == null)
                                continue;

                        int count = divs.length;
                        while (count-- > 0) {
                                int elem = treader.get();
                                int lon = treader.get3();
                                int lat = treader.get3();
                                treader.getChar();
                                treader.getChar();

                                if (level > lowestLevel)
                                        treader.getChar();

                                int endRgnOffset = treader.getu3();

                                Subdiv subdiv = new Subdiv();
                                divs[divs.length - count - 1] = subdiv;

                                subdiv.setLevel(level);
                                subdiv.setNumber(subdivNumber++);
                                subdiv.setRgnOffset(lastRgnOffset);
                                subdiv.setRgnEnd(endRgnOffset);
                                subdiv.setBits(linfo[level].getBits());
                                subdiv.setLocation(lat, lon);
                                subdiv.setTypes(elem);

                                lastRgnOffset = endRgnOffset;
                        }
                }
        }

        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");
        }
}