package jp.sourceforge.mikutoga.pmd.parser;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Arrays;
import jp.sourceforge.mikutoga.parser.CommonParser;
+import jp.sourceforge.mikutoga.parser.MmdEofException;
import jp.sourceforge.mikutoga.parser.MmdFormatException;
-import jp.sourceforge.mikutoga.parser.MmdInputStream;
+import jp.sourceforge.mikutoga.parser.TextDecoder;
/**
* PMDモデルファイルのパーサ基本部。
*/
public class PmdParserBase extends CommonParser {
+ /**
+ * PMDで用いられる文字エンコーディング(windows-31j)。
+ * ほぼShift_JISのスーパーセットと思ってよい。
+ * デコード結果はUCS-2集合に収まるはず。
+ */
+ public static final Charset CS_WIN31J = Charset.forName("windows-31j");
+
/** 改行文字列 CR。 */
protected static final String CR = "\r"; // 0x0d
/** 改行文字列 LF。 */
}
+ private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J);
+
private PmdBasicHandler basicHandler = null;
private PmdShapeHandler shapeHandler = null;
private PmdMaterialHandler materialHandler = null;
* コンストラクタ。
* @param source 入力ソース
*/
- public PmdParserBase(MmdInputStream source){
+ public PmdParserBase(InputStream source){
super(source);
+ this.decoderWin31j.setZeroChopMode(true);
return;
}
}
/**
+ * 指定されたバイト長に収まるゼロ終端(0x00)文字列を読み込む。
+ * 入力バイト列はwindows-31jエンコーディングとして解釈される。
+ * ゼロ終端以降のデータは無視されるが、
+ * IO入力は指定バイト数だけ読み進められる。
+ * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、
+ * そこまでのデータから文字列を構成する。
+ * @param byteLen 読み込みバイト数
+ * @return デコードされた文字列
+ * @throws IOException IOエラー
+ * @throws MmdEofException 読み込む途中でストリーム終端に達した。
+ * @throws MmdFormatException 不正な文字エンコーディングが検出された。
+ */
+ protected String parsePmdText(int byteLen)
+ throws IOException,
+ MmdEofException,
+ MmdFormatException {
+ String result = parseString(this.decoderWin31j, byteLen);
+ return result;
+ }
+
+ /**
* PMDファイルのパースを開始する。
* @throws IOException IOエラー
* @throws MmdFormatException フォーマットエラー
byte[] header = new byte[HEADER_LENGTH];
parseByteArray(header);
- for(int idx = 0; idx < MAGIC_BYTES.length; idx++){
- if(header[idx] != MAGIC_BYTES[idx]){
- throw new MmdFormatException("unknown PMD-header type");
- }
+ if( ! Arrays.equals(header, MAGIC_BYTES) ){
+ throw new MmdFormatException("unknown PMD-header type");
}
String modelName =
- parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELNAME);
+ parsePmdText(PmdLimits.MAXBYTES_MODELNAME);
String description =
- parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELDESC);
+ parsePmdText(PmdLimits.MAXBYTES_MODELDESC);
description = description.replace(CRLF, LF);
if(this.basicHandler != null){
boolean hasEdge = parseBoolean();
int surfaceCount = parseLeInt();
String shadingFile =
- parseZeroTermWin31J(PmdLimits.MAXBYTES_TEXTUREFILENAME);
+ parsePmdText(PmdLimits.MAXBYTES_TEXTUREFILENAME);
String[] splitted = splitShadingFileInfo(shadingFile);
String textureFile = splitted[0];
String sphereFile = splitted[1];
for(int ct = 0; ct < this.boneCount; ct++){
String boneName =
- parseZeroTermWin31J(PmdLimits.MAXBYTES_BONENAME);
+ parsePmdText(PmdLimits.MAXBYTES_BONENAME);
int parentId = parseLeUShortAsInt();
int tailId = parseLeUShortAsInt();
byte boneKind = parseByte();
- int ikId = parseLeUShortAsInt();
+ int srcId = parseLeUShortAsInt();
this.boneHandler.pmdBoneInfo(boneName, boneKind);
- this.boneHandler.pmdBoneLink(parentId, tailId, ikId);
+ this.boneHandler.pmdBoneLink(parentId, tailId, srcId);
float xPos = parseLeFloat();
float yPos = parseLeFloat();
for(int ct = 0; ct < this.morphCount; ct++){
String morphName =
- parseZeroTermWin31J(PmdLimits.MAXBYTES_MORPHNAME);
+ parsePmdText(PmdLimits.MAXBYTES_MORPHNAME);
int vertexCount = parseLeInt();
byte morphType = parseByte();
for(int ct = 0; ct < this.boneGroupCount; ct++){
String groupName =
- parseZeroTermWin31J(PmdLimits.MAXBYTES_BONEGROUPNAME);
+ parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME);
groupName = chopLastLF(groupName);
this.boneHandler.pmdBoneGroupInfo(groupName);