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