Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2006 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: 03-Dec-2006
 * Change: Thomas Lußnig <gps@suche.org>
 */

package uk.me.parabola.imgfmt.app.typ;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.BufferedImgFileWriter;
import uk.me.parabola.imgfmt.app.ImgFile;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.imgfmt.app.Section;
import uk.me.parabola.imgfmt.app.SectionWriter;
import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.imgfmt.app.srt.SortKey;
import uk.me.parabola.imgfmt.fs.ImgChannel;
import uk.me.parabola.log.Logger;

/**
 * The TYP file.
 *
 * @author Thomas Lußnig
 * @author Steve Ratcliffe
 */

public class TYPFile extends ImgFile {
        private static final Logger log = Logger.getLogger(TYPFile.class);

        private final TYPHeader header = new TYPHeader();

        private TypData data;

        private final Map<Integer, Integer> strToType = new TreeMap<Integer, Integer>();
        private final Map<Integer, Integer> typeToStr = new TreeMap<Integer, Integer>();

        public TYPFile(ImgChannel chan) {
                setHeader(header);
                setWriter(new BufferedImgFileWriter(chan));
                position(TYPHeader.HEADER_LEN);
        }

        public void write() {

                ImgFileWriter writer = getWriter();
                writer.position(TYPHeader.HEADER_LEN);

                writeSection(writer, header.getPolygonData(), header.getPolygonIndex(), data.getPolygons());
                writeSection(writer, header.getLineData(), header.getLineIndex(), data.getLines());
                writeSection(writer, header.getPointData(), header.getPointIndex(), data.getPoints());

                SectionWriter subWriter = header.getShapeStacking().makeSectionWriter(writer);
                data.getStacking().write(subWriter);
                Utils.closeFile(subWriter);

                writeSection(writer, header.getIconData(), header.getIconIndex(), data.getIcons());

                writeLabels(writer);
                writeStrIndex(writer);
                writerTypeIndex(writer);

                zapZero(header.getShapeStacking(), header.getLabels(), header.getStringIndex(), header.getTypeIndex());

                log.debug("syncing TYP file");
                position(0);
                getHeader().writeHeader(getWriter());
        }

        private void writeLabels(ImgFileWriter in) {
                if (data.getIcons().isEmpty())
                        return;
               
                SectionWriter writer = header.getLabels().makeSectionWriter(in);

                List<SortKey<TypIconSet>> keys = new ArrayList<SortKey<TypIconSet>>();
                Sort sort = data.getSort();
                for (TypIconSet icon : data.getIcons()) {
                        String label = icon.getLabel();
                        if (label != null) {
                                SortKey<TypIconSet> key = sort.createSortKey(icon, label);
                                keys.add(key);
                        }
                }
                Collections.sort(keys);

                // Offset 0 is reserved to mean no label.
                writer.put((byte) 0);

                for (SortKey<TypIconSet> key : keys) {
                        int off = writer.position();
                        TypIconSet icon = key.getObject();
                        int type = icon.getTypeForFile();

                        String label = icon.getLabel();
                        if (label != null) {
                                CharBuffer cb = CharBuffer.wrap(label);
                                CharsetEncoder encoder = data.getEncoder();
                                try {
                                        ByteBuffer buffer = encoder.encode(cb);
                                        writer.put(buffer);

                                        // If we succeeded then note offsets for indexes
                                        strToType.put(off, type);
                                        typeToStr.put(type, off);

                                } catch (CharacterCodingException ignore) {
                                        String name = encoder.charset().name();
                                        throw new TypLabelException(name);
                                }
                                writer.put((byte) 0);
                        }
                }
                Utils.closeFile(writer);
        }

        private void writeStrIndex(ImgFileWriter in) {
                SectionWriter writer = header.getStringIndex().makeSectionWriter(in);
                int psize = ptrSize(header.getLabels().getSize());
                header.getStringIndex().setItemSize((char) (3 + psize));

                for (Map.Entry<Integer, Integer> ent : strToType.entrySet()) {
                        putN(writer, psize, ent.getKey());
                        putN(writer, 3, ent.getValue());
                }
                Utils.closeFile(writer);
        }

        private void writerTypeIndex(ImgFileWriter in) {
                SectionWriter writer = header.getTypeIndex().makeSectionWriter(in);
                int psize = ptrSize(header.getLabels().getSize());
                header.getTypeIndex().setItemSize((char) (3 + psize));

                for (Map.Entry<Integer, Integer> ent : typeToStr.entrySet()) {
                        putN(writer, 3, ent.getKey());
                        putN(writer, psize, ent.getValue());
                }
                Utils.closeFile(writer);
        }

        private void writeSection(ImgFileWriter writer, Section dataSection, Section indexSection,
                        List<? extends TypElement> elementData)
        {
                Collections.sort(elementData);

                SectionWriter subWriter = dataSection.makeSectionWriter(writer);
                CharsetEncoder encoder = data.getEncoder();
                for (TypElement elem : elementData)
                        elem.write(subWriter, encoder);
                Utils.closeFile(subWriter);

                int size = dataSection.getSize();
                int typeSize = indexSection.getItemSize();
                int psize = ptrSize(size);

                indexSection.setItemSize((char) (typeSize + psize));

                subWriter = indexSection.makeSectionWriter(writer);
                for (TypElement elem : elementData) {
                        int offset = elem.getOffset();
                        int type = elem.getTypeForFile();
                        putN(subWriter, typeSize, type);
                        putN(subWriter, psize, offset);
                }
                Utils.closeFile(subWriter);

                zapZero(dataSection, indexSection);
        }

        private void zapZero(Section... sect) {
                for (Section s : sect) {
                        if (s.getSize() == 0) {
                                s.setPosition(0);
                                s.setItemSize((char) 0);
                        }
                }
        }

        private int ptrSize(int size) {
                int psize = 1;
                if (size > 0xffffff)
                        psize = 4;
                else if (size > 0xffff)
                        psize = 3;
                else if (size > 0xff)
                        psize = 2;
                return psize;
        }

        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;
                }
        }
       
        public void setData(TypData data) {
                this.data = data;
                TypParam param = data.getParam();
                header.setCodePage((char) param.getCodePage());
                header.setFamilyId((char) param.getFamilyId());
                header.setProductId((char) param.getProductId());
        }
}