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