Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2009.
 *
 * 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 uk.me.parabola.imgfmt.app.ImgFileWriter;

/**
 * Super class of all sections.
 * @author Steve Ratcliffe
 */

public abstract class MdrSection extends ConfigBase {
        private PointerSizes sizes;
        private boolean released;
        protected int nItems;
        private boolean sizeValid;

        /**
         * Write out the contents of this section.
         * @param writer Where to write it.
         */

        public abstract void writeSectData(ImgFileWriter writer);

        /**
         * The size of a record in the section.  This is not a constant and
         * might vary on various factors, such as the file version, if we are
         * preparing for a device, the number of maps etc.
         *
         * @return The size of a record in this section.
         */

        public abstract int getItemSize();

        protected PointerSizes getSizes() {
                return sizes;
        }

        public void setSizes(PointerSizes sizes) {
                this.sizes = sizes;
        }

        protected void putMapIndex(ImgFileWriter writer, int mapIndex) {
                putN(writer, sizes.getMapSize(), mapIndex);
        }

        protected void putStringOffset(ImgFileWriter writer, int strOff) {
                putN(writer, sizes.getStrOffSize(), strOff);
        }

        protected void putN(ImgFileWriter writer, int n, int value) {
                switch (n) {
                case 1:
                        writer.put((byte) value);
                        break;
                case 2:
                        writer.putChar((char) value);
                        break;
                case 3:
                        writer.put3(value);
                        break;
                case 4:
                        writer.putInt(value);
                        break;
                default: // Don't write anything.
                        assert false;
                        break;
                }
        }

        protected static int numberToPointerSize(int n) {
                if (n > 0xffffff)
                        return 4;
                else if (n > 0xffff)
                        return 3;
                else if (n > 0xff)
                        return 2;
                else
                        return 1;
        }

        /**
         * The number of records in this section.
         * @return The number of items in the section.
         */

        public final int getNumberOfItems() {
                assert sizeValid;
                if (released)
                        return nItems;
                else
                        return numberOfItems();
        }

        /**
         * Method to be implemented by subclasses to return the number of items in the section.
         * This will only be valid after the section is completely finished etc.
         * @return The number of items in the section.
         */

        protected abstract int numberOfItems();

        /**
         * Get the size of an integer that is sufficient to store a record number
         * from this section.  If the pointer has a flag(s) then this must be
         * taken into account too.
         * @return A number between 1 and 4 giving the number of bytes required
         * to store the largest record number in this section.
         */

        public int getSizeForRecord() {
                return numberToPointerSize(getNumberOfItems());
        }

        /**
         * Called before the section is written and before the actual size of the section
         * is required.
         *
         * Calling it more than once is ok.
         *
         * The actual work is implemented in the subclass via the {@link #preWriteImpl()} method.
         */

        public final void preWrite() {
                if (!sizeValid)
                        preWriteImpl();
                sizeValid = true;
        }

        /**
         * Prepare the final list of items to be written.
         * Used to de-duplicate or remove invalid entries from the raw data that was
         * saved.
         *
         * In particular after this call the number of items must not change.
         */

        protected void preWriteImpl() {
        }

        public final void release() {
                nItems = numberOfItems();
                releaseMemory();
                released = true;
        }

        protected void releaseMemory() {
                throw new UnsupportedOperationException();
        }

        /**
         * Provides the pointer sizes required to hold record of offset values
         * in the various sections.
         */

        static class PointerSizes {

                private final MdrSection[] sections;

                public PointerSizes(MdrSection[] sections) {
                        this.sections = sections;
                }

                public int getMapSize() {
                        return sections[1].getSizeForRecord();
                }

                public int getCitySize() {
                        return sections[5].getSizeForRecord();
                }

                /**
                 * Get the number of bytes required to represent a city when there is
                 * one bit reserved for a flag.
                 * There is a minimum size of 2.
                 * @return Number of bytes to represent a city record number and a
                 * one bit flag.
                 */

                public int getCitySizeFlagged() {
                        return Math.max(2, numberToPointerSize(sections[5].getNumberOfItems() << 1));
                }

                public int getCityFlag() {
                        return flagForSize(getCitySizeFlagged());
                }

                public int getStreetSize() {
                        return sections[7].getSizeForRecord();
                }

                public int getStreetSizeFlagged() {
                        return numberToPointerSize(sections[7].getNumberOfItems() << 1);
                }

                public int getPoiSize() {
                        return sections[11].getSizeForRecord();
                }

                public int getZipSize() {
                        //return Math.max(2, sections[6].getSizeForRecord());
                        return sections[6].getSizeForRecord();
                }

                /**
                 * The number of bytes required to represent a POI (mdr11) record number
                 * and a flag bit.
                 */

                public int getPoiSizeFlagged() {
                        return numberToPointerSize(sections[11].getNumberOfItems() << 1);
                }

                public int getPoiFlag() {
                        return flagForSize(getPoiSizeFlagged());
                }

                /**
                 * Size of the pointer required to index a byte offset into mdr15 (strings).
                 * There is a minimum of 3 for this value.
                 * @return Pointer size required for the string offset value.
                 */

                public int getStrOffSize() {
                        return Math.max(3, sections[15].getSizeForRecord());
                }

                public int getMdr20Size() {
                        return sections[20].getSizeForRecord();
                }

                private int flagForSize(int size) {
                        int flag;
                        if (size == 1)
                                flag = 0x80;
                        else if (size == 2)
                                flag = 0x8000;
                        else if (size == 3)
                                flag = 0x800000;
                        else if (size == 4)
                                flag = 0x80000000;
                        else
                                flag = 0;
                        return flag;
                }

                public int getSize(int sect) {
                        return sections[sect].getSizeForRecord();
                }
        }
}