Subversion Repositories display

Rev

Rev 317 | 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.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.*;

/**
 * Standalone program to display the SRT file.  This is used for
 * the sorting order for different charsets apparently.
 *
 * @author Steve Ratcliffe
 */

public class SrtDisplay extends CommonDisplay {
        private Section description;
        private Section tableHeader;

        private int srt1start;

        private Section characterTable;
        private Section srt5;
        private CharsetDecoder decoder;
        private final List<CharPosition> expansions = new ArrayList<CharPosition>();

        protected void print() {
                readCommonHeader();
                readFileHeader();
                printBody();
        }

        /**
         * This header is unusual as doesn't follow the normal header conventions for
         * defining the sections.  It points to a single section that contains a header
         * which in turn points to other sections.
         */

        private void readFileHeader() {
                Displayer d = new Displayer(reader);
                d.setTitle("SRT Header");

                d.charValue("??? %d"); //NUmber of sections?? Seems to be true, I've only one section, value here 0x01
                srt1start = d.intValue("SRT 1 start");
                d.charValue("len %d");

                if (getHeaderLen() == 37) {
                        d.charValue("??? %d");
                        d.intValue("SRT 2 start %x");
                        d.charValue("len %d");
                }
                d.print(outStream);
        }

        private void printBody() {
                printSrt1();
                //printSrt2();
                printDescription();
                tableHeader();
        }

        /**
         * This section has regular section headers like the other app files.
         */

        private void printSrt1() {
                Displayer d = new Displayer(reader);
                reader.position(srt1start);
               
                d.setTitle("SRT 1 (pointers)");
                description = readSection(d, "Description", 2, false, false);
                tableHeader = readSection(d, "Table header", 3, false, false);

                d.print(outStream);
        }

        private void printDescription() {
                Displayer d = new Displayer(reader);
                reader.position(description.getStart());

                d.setTitle("Description");

                String s = d.zstringValue("Description: %s");

                long remain = description.getLen() - s.length() - 1;
                d.rawValue((int) remain);

                d.print(outStream);
        }

        /**
         * This is a strange section, because it also has a header with multiple sections.
         */

        private void tableHeader() {
                Displayer d = new Displayer(reader);
                d.setTitle("Character table header");

                reader.position(tableHeader.getStart());
                int remain = d.charValue("sub header len %d");
                d.charValue("id1 %d");
                d.charValue("id2 %d");

                int codepage = d.charValue("codepage %d");
                String cpname;
                if (codepage == 65001)
                        cpname = "utf-8";
                else
                        cpname = "cp" + codepage;
                Charset charset = Charset.forName(cpname);
                decoder = charset.newDecoder();

                d.intValue("??? %d");
                remain -= 12;
               
                if (remain >= 16) {
                        characterTable = readSection(d, "character table", 4, true, false); // TODO this is a su-bheader
                        d.rawValue(6, "padding?");
                        remain -= 16;
                }

                if (remain >= 16) {
                        srt5 = readSection(d, "SRT 5", 5, true, false); // TODO this is a sub-header
                        d.rawValue(6, "padding?");
                        remain -= 16;
                }

                d.rawValue(remain, "offset?");
                d.print(outStream);

                printSrt5();
                printCharacterTable();
        }

        private void printCharacterTable() {
                Displayer d = new Displayer(reader);
                d.setTitle("SRT 4 Character table");

                int rs = characterTable.getRecordSize();
                reader.position(tableHeader.getStart() + characterTable.getStart());
                @SuppressWarnings({"unchecked"})
                Set<CharPosition>[] charmap = new TreeSet[256];
                for (int i = 1; i <= characterTable.getNumberOfRecords(); i++) {
                        DisplayItem item = d.item();

                        int rec = reader.get3();
                        item.setBytes3(rec);

                        int type = rec & 0xff;
                        int pos = (rec >> 8) & 0xff;
                        int subpos = (rec >> 16) & 0xf;
                        int third = (rec >> 20) & 0xf;

                        StringBuilder sb = new StringBuilder();
                        Formatter fmt = new Formatter(sb);
                        fmt.format("0x%02x ", i);
                        fmt.format("(%c) ", toUnicode(i));
                        if ((type & 0x1) != 0)
                                sb.append("Letter ");
                        if ((type & 0x2) != 0)
                                sb.append("Number ");

                        if ((type & 0x30) == 0) {
                                sb.append("prim=");
                                sb.append(pos);
                                if (subpos != 1 || third != 1) {
                                        sb.append(" sec=");
                                        sb.append(subpos);
                                        sb.append(" tert=");
                                        sb.append(third);
                                }
                        } else {
                                // This is an expansion, it sorts as two or more characters (eg ß sorts near ss).
                                // The pos is an index into srt5.
                                expansion(sb, pos, (type >> 4) & 0x3);
                        }

                        item.addText(sb.toString());

                        if (pos == 0 && subpos == 0)
                                continue;

            CharPosition c = new CharPosition();
            c.val = i;
            c.pos = pos;
            c.subpos = subpos;
                        c.third = third;
            Set<CharPosition> set = charmap[pos];
            if (set == null) {
                set = new TreeSet<CharPosition>();
                charmap[pos] = set;
            }
            set.add(c);
                }

        // Show the actual sort order
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i <= characterTable.getNumberOfRecords(); i++) {
            Set<CharPosition> charPositionses = charmap[i];
            if (charPositionses == null)
                continue;
            sb.append('[');
                        sb.append(String.format("0x%02x:", i));
            for (CharPosition c : charPositionses) {
                sb.append(toUnicode(c.val));
            }
            sb.append("],");
        }
        d.item().addText(sb.toString());
                d.print(outStream);
        }

        /**
         * Some characters sort as if they were two separate characters (eg ß sorts like 'ss').
         * @param sb
         * @param pos
         * @param n
         */

        private void expansion(StringBuilder sb, int pos, int n) {
                for (int i = 0; i <= n; i++) {
                        CharPosition ch = expansions.get(pos + i - 1);
                        sb.append(ch);
                        if (i != n)
                                sb.append(" & ");
                }
        }

        private char toUnicode(int i) {
                ByteBuffer b = ByteBuffer.allocate(1);
                b.put((byte) i);
                b.flip();
                try {
                        CharBuffer chars = decoder.decode(b);
                        return chars.charAt(0);
                } catch (CharacterCodingException e) {
                        return '?';
                }
        }

        private class CharPosition implements Comparable {
        private int val;
        private int pos;
        private int subpos;
                private int third;

        public int compareTo(Object o) {
            CharPosition c2 = (CharPosition) o;
            if (c2.subpos == subpos)
                return compareThird(c2);
            else if (subpos < c2.subpos)
                return -1;
            else
                return 1;
        }

                private int compareThird(CharPosition c2) {
                        if (third == c2.third)
                                return 0;
                        else if (third < c2.third)
                                return -1;
                        else
                                return 1;
                }

                public String toString() {
                        return "prim=" + pos + ",sec=" + subpos + ",tert=" + third;
                }
        }

        private void printSrt5() {
                Displayer d = new Displayer(reader);
                d.setTitle("SRT 5 (expansions)");

                reader.position(tableHeader.getStart() + srt5.getStart());
                for (int i = 0; i < srt5.getNumberOfRecords(); i++) {
                        char c = reader.getChar();
                        DisplayItem item = d.item();
                        item.setBytes(c);
                        item.addText("%x %c %c", (int)c, (char)((c >> 8) &0xff), (char)(c & 0xff));

                        CharPosition ch = new CharPosition();
                        ch.pos = c & 0xff;
                        ch.subpos = (c >> 8) & 0xf;
                        ch.third = (c >> 12) & 0xf;
                        expansions.add(ch);
                }
                d.print(outStream);
        }

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

                CommonDisplay td = new SrtDisplay();
                td.display(args[0],"SRT");
        }
}