4 * License : The MIT License
5 * Copyright(c) 2010 MikuToga Partners
8 package jp.sourceforge.mikutoga.pmd.parser;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.nio.charset.Charset;
13 import java.util.Arrays;
14 import jp.sfjp.mikutoga.bin.parser.CommonParser;
15 import jp.sfjp.mikutoga.bin.parser.MmdEofException;
16 import jp.sfjp.mikutoga.bin.parser.MmdFormatException;
17 import jp.sfjp.mikutoga.bin.parser.TextDecoder;
22 public class PmdParserBase extends CommonParser {
25 * PMDで用いられる文字エンコーディング(windows-31j)。
26 * ほぼShift_JISのスーパーセットと思ってよい。
27 * デコード結果はUCS-2集合に収まるはず。
29 public static final Charset CS_WIN31J = Charset.forName("windows-31j");
32 protected static final String CR = "\r"; // 0x0d
34 protected static final String LF = "\n"; // 0x0a
36 protected static final String CRLF = CR + LF; // 0x0d, 0x0a
39 private static final int TRIVTX = 3;
41 private static final int HEADER_LENGTH = 7;
42 private static final byte[] MAGIC_BYTES = {
43 (byte)0x50, (byte)0x6d, (byte)0x64, // "Pmd"
44 (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x3f, // 1.0f
49 assert MAGIC_BYTES.length == HEADER_LENGTH;
53 private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J);
55 private PmdBasicHandler basicHandler = NullHandler.HANDLER;
56 private PmdShapeHandler shapeHandler = NullHandler.HANDLER;
57 private PmdMaterialHandler materialHandler = NullHandler.HANDLER;
58 private PmdBoneHandler boneHandler = NullHandler.HANDLER;
59 private PmdMorphHandler morphHandler = NullHandler.HANDLER;
61 private int boneCount = -1;
62 private int morphCount = -1;
63 private int boneGroupCount = -1;
70 public PmdParserBase(InputStream source){
72 this.decoderWin31j.setZeroChopMode(true);
78 * 文字列の最後がLF(0x0a)の場合削除する。
81 * @return 末尾LFが削除された文字列
83 public static String chopLastLF(String name){
86 if(name.endsWith(LF)){
87 result = name.substring(0, name.length() - 1);
97 * テクスチャファイル名とスフィアマップファイル名を分離する。
98 * @param shadingFile シェーディング用ファイル情報
99 * @return [0]:テクスチャファイル名 [1]:スフィアマップファイル名。
102 public static String[] splitShadingFileInfo(String shadingFile){
105 result = shadingFile.split('\\'+"*", 2);
106 assert result.length == 1 || result.length == 2;
108 if(result.length == 1){
109 String onlyFile = result[0];
110 result = new String[2];
113 if(onlyFile.endsWith(".sph") || onlyFile.endsWith(".spa")){
114 result[1] = onlyFile;
116 result[0] = onlyFile;
120 assert result.length == 2;
127 * @param handler ハンドラ
129 public void setBasicHandler(PmdBasicHandler handler){
131 this.basicHandler = NullHandler.HANDLER;
133 this.basicHandler = handler;
140 * @param handler ハンドラ
142 public void setShapeHandler(PmdShapeHandler handler){
144 this.shapeHandler = NullHandler.HANDLER;
146 this.shapeHandler = handler;
153 * @param handler ハンドラ
155 public void setMaterialHandler(PmdMaterialHandler handler){
157 this.materialHandler = NullHandler.HANDLER;
159 this.materialHandler = handler;
166 * @param handler ハンドラ
168 public void setBoneHandler(PmdBoneHandler handler){
170 this.boneHandler = NullHandler.HANDLER;
172 this.boneHandler = handler;
179 * @param handler ハンドラ
181 public void setMorphHandler(PmdMorphHandler handler){
183 this.morphHandler = NullHandler.HANDLER;
185 this.morphHandler = handler;
191 * パースによって得られたボーン数を返す。
194 protected int getBoneCount(){
195 return this.boneCount;
199 * パースによって得られたモーフ数を返す。
202 protected int getMorphCount(){
203 return this.morphCount;
207 * パースによって得られたボーングループ数を返す。
210 protected int getBoneGroupCount(){
211 return this.boneGroupCount;
215 * 指定されたバイト長に収まるゼロ終端(0x00)文字列を読み込む。
216 * <p>入力バイト列はwindows-31jエンコーディングとして解釈される。
217 * <p>ゼロ終端以降のデータは無視されるが、
218 * IO入力は指定バイト数だけ読み進められる。
219 * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、
220 * そこまでのデータから文字列を構成する。
221 * @param byteLen 読み込みバイト数
223 * @throws IOException IOエラー
224 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
225 * @throws MmdFormatException 不正な文字エンコーディングが検出された。
227 protected String parsePmdText(int byteLen)
231 String result = parseString(this.decoderWin31j, byteLen);
237 * @throws IOException IOエラー
238 * @throws MmdFormatException フォーマットエラー
240 public void parsePmd()
241 throws IOException, MmdFormatException {
242 this.basicHandler.pmdParseStart();
246 boolean hasMoreData = hasMore();
247 this.basicHandler.pmdParseEnd(hasMoreData);
253 * PMDファイル本体のパースを開始する。
254 * パーサを拡張する場合はこのメソッドをオーバーライドする。
255 * @throws IOException IOエラー
256 * @throws MmdFormatException フォーマットエラー
258 protected void parseBody() throws IOException, MmdFormatException{
267 parseMorphOrderList();
268 parseBoneGroupList();
269 parseGroupedBoneList();
275 * PMDファイルヘッダ部のパースと通知。
276 * @throws IOException IOエラー
277 * @throws MmdFormatException フォーマットエラー
279 private void parsePmdHeader() throws IOException, MmdFormatException{
280 byte[] header = new byte[HEADER_LENGTH];
281 parseByteArray(header);
283 if( ! Arrays.equals(header, MAGIC_BYTES) ){
284 throw new MmdFormatException("unknown PMD-header type");
288 parsePmdText(PmdLimits.MAXBYTES_MODELNAME);
290 parsePmdText(PmdLimits.MAXBYTES_MODELDESC);
291 description = description.replace(CRLF, LF);
293 this.basicHandler.pmdHeaderInfo(header);
294 this.basicHandler.pmdModelInfo(modelName, description);
301 * @throws IOException IOエラー
302 * @throws MmdFormatException フォーマットエラー
304 private void parseVertexList() throws IOException, MmdFormatException{
305 int vertexNum = parseLeInt();
307 this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);
309 for(int ct = 0; ct < vertexNum; ct++){
310 float xPos = parseLeFloat();
311 float yPos = parseLeFloat();
312 float zPos = parseLeFloat();
313 this.shapeHandler.pmdVertexPosition(xPos, yPos, zPos);
315 float xVec = parseLeFloat();
316 float yVec = parseLeFloat();
317 float zVec = parseLeFloat();
318 this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);
320 float uVal = parseLeFloat();
321 float vVal = parseLeFloat();
322 this.shapeHandler.pmdVertexUV(uVal, vVal);
324 int boneId1 = parseLeUShortAsInt();
325 int boneId2 = parseLeUShortAsInt();
326 int weightForB1 = parseUByteAsInt();
327 this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);
329 boolean hideEdge = parseBoolean();
330 this.shapeHandler.pmdVertexEdge(hideEdge);
332 this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);
335 this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);
342 * @throws IOException IOエラー
343 * @throws MmdFormatException フォーマットエラー
345 private void parseSurfaceList() throws IOException, MmdFormatException{
346 int vertexNum = parseLeInt();
347 if(vertexNum % TRIVTX != 0) throw new MmdFormatException();
348 int surfaceNum = vertexNum / TRIVTX;
350 this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);
352 for(int ct = 0; ct < surfaceNum; ct++){
353 int vertexId1 = parseLeUShortAsInt();
354 int vertexId2 = parseLeUShortAsInt();
355 int vertexId3 = parseLeUShortAsInt();
356 this.shapeHandler.pmdSurfaceTriangle(vertexId1,
359 this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);
362 this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);
369 * @throws IOException IOエラー
370 * @throws MmdFormatException フォーマットエラー
372 private void parseMaterialList() throws IOException, MmdFormatException{
373 int materialNum = parseLeInt();
375 this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,
378 for(int ct = 0; ct < materialNum; ct++){
381 int toonidx = parseUByteAsInt();
382 boolean hasEdge = parseBoolean();
383 int surfaceCount = parseLeInt();
386 parsePmdText(PmdLimits.MAXBYTES_TEXTUREFILENAME);
387 String[] splitted = splitShadingFileInfo(shadingFile);
388 String textureFile = splitted[0];
389 String sphereFile = splitted[1];
391 this.materialHandler.pmdMaterialShading(toonidx,
392 textureFile, sphereFile );
393 this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);
395 this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);
398 this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);
405 * @throws IOException IOエラー
406 * @throws MmdFormatException フォーマットエラー
408 private void parseColor() throws IOException, MmdFormatException{
413 red = parseLeFloat();
414 green = parseLeFloat();
415 blue = parseLeFloat();
416 float alpha = parseLeFloat();
418 this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);
420 float shininess = parseLeFloat();
421 red = parseLeFloat();
422 green = parseLeFloat();
423 blue = parseLeFloat();
425 this.materialHandler.pmdMaterialSpecular(red, green, blue,
428 red = parseLeFloat();
429 green = parseLeFloat();
430 blue = parseLeFloat();
432 this.materialHandler.pmdMaterialAmbient(red, green, blue);
439 * @throws IOException IOエラー
440 * @throws MmdFormatException フォーマットエラー
442 private void parseBoneList() throws IOException, MmdFormatException{
443 this.boneCount = parseLeUShortAsInt();
445 this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);
447 for(int ct = 0; ct < this.boneCount; ct++){
449 parsePmdText(PmdLimits.MAXBYTES_BONENAME);
450 int parentId = parseLeUShortAsInt();
451 int tailId = parseLeUShortAsInt();
452 byte boneKind = parseByte();
453 int srcId = parseLeUShortAsInt();
455 this.boneHandler.pmdBoneInfo(boneName, boneKind);
456 this.boneHandler.pmdBoneLink(parentId, tailId, srcId);
458 float xPos = parseLeFloat();
459 float yPos = parseLeFloat();
460 float zPos = parseLeFloat();
462 this.boneHandler.pmdBonePosition(xPos, yPos, zPos);
464 this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);
467 this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);
474 * @throws IOException IOエラー
475 * @throws MmdFormatException フォーマットエラー
477 private void parseIKList() throws IOException, MmdFormatException{
478 int ikCount = parseLeUShortAsInt();
480 this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);
482 for(int ct = 0; ct < ikCount; ct++){
483 int boneId = parseLeUShortAsInt();
484 int targetId = parseLeUShortAsInt();
485 int chainLength = parseUByteAsInt();
486 int depth = parseLeUShortAsInt();
487 float weight = parseLeFloat();
489 parseIKChainList(chainLength);
491 this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);
493 this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);
496 this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);
503 * @param chainLength チェーン長
504 * @throws IOException IOエラー
505 * @throws MmdFormatException フォーマットエラー
507 private void parseIKChainList(int chainLength)
508 throws IOException, MmdFormatException{
509 this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,
512 for(int ct = 0; ct < chainLength; ct++){
513 int childId = parseLeUShortAsInt();
514 this.boneHandler.pmdIKChainInfo(childId);
516 this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);
519 this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);
526 * @throws IOException IOエラー
527 * @throws MmdFormatException フォーマットエラー
529 private void parseMorphList() throws IOException, MmdFormatException{
530 this.morphCount = parseLeUShortAsInt();
532 this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,
535 for(int ct = 0; ct < this.morphCount; ct++){
537 parsePmdText(PmdLimits.MAXBYTES_MORPHNAME);
538 int vertexCount = parseLeInt();
539 byte morphType = parseByte();
541 this.morphHandler.pmdMorphInfo(morphName, morphType);
543 parseMorphVertexList(vertexCount);
545 this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);
548 this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);
555 * @param vertexCount 頂点数
556 * @throws IOException IOエラー
557 * @throws MmdFormatException フォーマットエラー
559 private void parseMorphVertexList(int vertexCount)
560 throws IOException, MmdFormatException{
561 this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,
564 for(int ct = 0; ct < vertexCount; ct++){
565 int vertexId = parseLeInt();
566 float xPos = parseLeFloat();
567 float yPos = parseLeFloat();
568 float zPos = parseLeFloat();
569 this.morphHandler.pmdMorphVertexInfo(vertexId, xPos, yPos, zPos);
571 this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);
574 this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);
581 * @throws IOException IOエラー
582 * @throws MmdFormatException フォーマットエラー
584 private void parseMorphOrderList()
585 throws IOException, MmdFormatException{
586 int morphOrderCount = parseUByteAsInt();
588 this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,
591 for(int ct = 0; ct < morphOrderCount; ct++){
592 int morphId = parseLeUShortAsInt();
593 this.morphHandler.pmdMorphOrderInfo(morphId);
595 this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);
598 this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);
605 * @throws IOException IOエラー
606 * @throws MmdFormatException フォーマットエラー
608 private void parseBoneGroupList()
609 throws IOException, MmdFormatException{
610 this.boneGroupCount = parseUByteAsInt();
612 this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,
613 this.boneGroupCount);
615 for(int ct = 0; ct < this.boneGroupCount; ct++){
617 parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME);
618 groupName = chopLastLF(groupName);
619 this.boneHandler.pmdBoneGroupInfo(groupName);
621 this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);
624 this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);
631 * @throws IOException IOエラー
632 * @throws MmdFormatException フォーマットエラー
634 private void parseGroupedBoneList()
635 throws IOException, MmdFormatException{
636 int groupedBoneCount = parseLeInt();
638 this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,
641 for(int ct = 0; ct < groupedBoneCount; ct++){
642 int boneId = parseLeUShortAsInt();
643 int groupId = parseUByteAsInt();
644 this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);
646 this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);
649 this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);