OSDN Git Service

pmd-plugin 3.0.1(PMD 5.0.2) 対応
[mikutoga/TogaGem.git] / src / main / java / jp / sfjp / mikutoga / pmd / parser / PmdParserBase.java
1 /*
2  * pmd file parser
3  *
4  * License : The MIT License
5  * Copyright(c) 2010 MikuToga Partners
6  */
7
8 package jp.sfjp.mikutoga.pmd.parser;
9
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;
18 import jp.sfjp.mikutoga.pmd.ShadingUtil;
19
20 /**
21  * PMDモデルファイルのパーサ基本部。
22  */
23 public class PmdParserBase extends CommonParser {
24
25     /**
26      * PMDで用いられる文字エンコーディング(windows-31j)。
27      * ほぼShift_JISのスーパーセットと思ってよい。
28      * デコード結果はUCS-2集合に収まるはず。
29      */
30     public static final Charset CS_WIN31J = Charset.forName("windows-31j");
31
32     /** 改行文字列 CR。 */
33     protected static final String CR = "\r";       // 0x0d
34     /** 改行文字列 LF。 */
35     protected static final String LF = "\n";       // 0x0a
36     /** 改行文字列 CRLF。 */
37     protected static final String CRLF = CR + LF;  // 0x0d, 0x0a
38
39     /** 3角ポリゴン頂点数。 */
40     private static final int TRIVTX = 3;
41
42     private static final int HEADER_LENGTH = 7;
43     private static final byte[] MAGIC_BYTES = {
44         (byte)0x50, (byte)0x6d, (byte)0x64,               // "Pmd"
45         (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x3f,   // 1.0f
46     };
47
48
49     static{
50         assert MAGIC_BYTES.length == HEADER_LENGTH;
51     }
52
53
54     private final TextDecoder decoderWin31j  = new TextDecoder(CS_WIN31J);
55
56     private PmdBasicHandler basicHandler =       NullHandler.HANDLER;
57     private PmdShapeHandler shapeHandler =       NullHandler.HANDLER;
58     private PmdMaterialHandler materialHandler = NullHandler.HANDLER;
59     private PmdBoneHandler boneHandler =         NullHandler.HANDLER;
60     private PmdMorphHandler morphHandler =       NullHandler.HANDLER;
61
62     private int boneCount      = -1;
63     private int morphCount     = -1;
64     private int boneGroupCount = -1;
65
66
67     /**
68      * コンストラクタ。
69      * @param source 入力ソース
70      */
71     public PmdParserBase(InputStream source){
72         super(source);
73         this.decoderWin31j.setZeroChopMode(true);
74         return;
75     }
76
77
78     /**
79      * 文字列の最後がLF(0x0a)の場合削除する。
80      * <p>ボーングループ名対策。
81      * @param name 文字列
82      * @return 末尾LFが削除された文字列
83      */
84     public static String chopLastLF(String name){
85         String result;
86
87         if(name.endsWith(LF)){
88             result = name.substring(0, name.length() - 1);
89         }else{
90             result = name;
91         }
92
93         return result;
94     }
95
96     /**
97      * 基本情報通知ハンドラを登録する。
98      * @param handler ハンドラ
99      */
100     public void setBasicHandler(PmdBasicHandler handler){
101         if(handler == null){
102             this.basicHandler = NullHandler.HANDLER;
103         }else{
104             this.basicHandler = handler;
105         }
106         return;
107     }
108
109     /**
110      * 形状情報通知ハンドラを登録する。
111      * @param handler ハンドラ
112      */
113     public void setShapeHandler(PmdShapeHandler handler){
114         if(handler == null){
115             this.shapeHandler = NullHandler.HANDLER;
116         }else{
117             this.shapeHandler = handler;
118         }
119         return;
120     }
121
122     /**
123      * 材質情報通知ハンドラを登録する。
124      * @param handler ハンドラ
125      */
126     public void setMaterialHandler(PmdMaterialHandler handler){
127         if(handler == null){
128             this.materialHandler = NullHandler.HANDLER;
129         }else{
130             this.materialHandler = handler;
131         }
132         return;
133     }
134
135     /**
136      * ボーン情報通知ハンドラを登録する。
137      * @param handler ハンドラ
138      */
139     public void setBoneHandler(PmdBoneHandler handler){
140         if(handler == null){
141             this.boneHandler = NullHandler.HANDLER;
142         }else{
143             this.boneHandler = handler;
144         }
145         return;
146     }
147
148     /**
149      * モーフ情報通知ハンドラを登録する。
150      * @param handler ハンドラ
151      */
152     public void setMorphHandler(PmdMorphHandler handler){
153         if(handler == null){
154             this.morphHandler = NullHandler.HANDLER;
155         }else{
156             this.morphHandler = handler;
157         }
158         return;
159     }
160
161     /**
162      * パースによって得られたボーン数を返す。
163      * @return ボーン数
164      */
165     protected int getBoneCount(){
166         return this.boneCount;
167     }
168
169     /**
170      * パースによって得られたモーフ数を返す。
171      * @return モーフ数
172      */
173     protected int getMorphCount(){
174         return this.morphCount;
175     }
176
177     /**
178      * パースによって得られたボーングループ数を返す。
179      * @return ボーングループ数
180      */
181     protected int getBoneGroupCount(){
182         return this.boneGroupCount;
183     }
184
185     /**
186      * 指定されたバイト長に収まるゼロ終端(0x00)文字列を読み込む。
187      * <p>入力バイト列はwindows-31jエンコーディングとして解釈される。
188      * <p>ゼロ終端以降のデータは無視されるが、
189      * IO入力は指定バイト数だけ読み進められる。
190      * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、
191      * そこまでのデータから文字列を構成する。
192      * @param byteLen 読み込みバイト数
193      * @return デコードされた文字列
194      * @throws IOException IOエラー
195      * @throws MmdEofException 読み込む途中でストリーム終端に達した。
196      * @throws MmdFormatException 不正な文字エンコーディングが検出された。
197      */
198     protected String parsePmdText(int byteLen)
199             throws IOException,
200                    MmdEofException,
201                    MmdFormatException {
202         String result = parseString(this.decoderWin31j, byteLen);
203         return result;
204     }
205
206     /**
207      * PMDファイルのパースを開始する。
208      * @throws IOException IOエラー
209      * @throws MmdFormatException フォーマットエラー
210      */
211     public void parsePmd()
212             throws IOException, MmdFormatException {
213         this.basicHandler.pmdParseStart();
214
215         parseBody();
216
217         boolean hasMoreData = hasMore();
218         this.basicHandler.pmdParseEnd(hasMoreData);
219
220         return;
221     }
222
223     /**
224      * PMDファイル本体のパースを開始する。
225      * パーサを拡張する場合はこのメソッドをオーバーライドする。
226      * @throws IOException IOエラー
227      * @throws MmdFormatException フォーマットエラー
228      */
229     protected void parseBody() throws IOException, MmdFormatException{
230         parsePmdHeader();
231
232         parseVertexList();
233         parseSurfaceList();
234         parseMaterialList();
235         parseBoneList();
236         parseIKList();
237         parseMorphList();
238         parseMorphOrderList();
239         parseBoneGroupList();
240         parseGroupedBoneList();
241
242         return;
243     }
244
245     /**
246      * PMDファイルヘッダ部のパースと通知。
247      * @throws IOException IOエラー
248      * @throws MmdFormatException フォーマットエラー
249      */
250     private void parsePmdHeader() throws IOException, MmdFormatException{
251         byte[] header = new byte[HEADER_LENGTH];
252         parseByteArray(header);
253
254         if( ! Arrays.equals(header, MAGIC_BYTES) ){
255             throw new MmdFormatException("unknown PMD-header type");
256         }
257
258         String modelName =
259                 parsePmdText(PmdLimits.MAXBYTES_MODELNAME);
260         String description =
261                 parsePmdText(PmdLimits.MAXBYTES_MODELDESC);
262         description = description.replace(CRLF, LF);
263
264         this.basicHandler.pmdHeaderInfo(header);
265         this.basicHandler.pmdModelInfo(modelName, description);
266
267         return;
268     }
269
270     /**
271      * 頂点情報のパースと通知。
272      * @throws IOException IOエラー
273      * @throws MmdFormatException フォーマットエラー
274      */
275     private void parseVertexList() throws IOException, MmdFormatException{
276         int vertexNum = parseLeInt();
277
278         this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);
279
280         for(int ct = 0; ct < vertexNum; ct++){
281             float xPos = parseLeFloat();
282             float yPos = parseLeFloat();
283             float zPos = parseLeFloat();
284             this.shapeHandler.pmdVertexPosition(xPos, yPos, zPos);
285
286             float xVec = parseLeFloat();
287             float yVec = parseLeFloat();
288             float zVec = parseLeFloat();
289             this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);
290
291             float uVal = parseLeFloat();
292             float vVal = parseLeFloat();
293             this.shapeHandler.pmdVertexUV(uVal, vVal);
294
295             int boneId1     = parseLeUShortAsInt();
296             int boneId2     = parseLeUShortAsInt();
297             int weightForB1 = parseUByteAsInt();
298             this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);
299
300             boolean hideEdge = parseBoolean();
301             this.shapeHandler.pmdVertexEdge(hideEdge);
302
303             this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);
304         }
305
306         this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);
307
308         return;
309     }
310
311     /**
312      * 面情報のパースと通知。
313      * @throws IOException IOエラー
314      * @throws MmdFormatException フォーマットエラー
315      */
316     private void parseSurfaceList() throws IOException, MmdFormatException{
317         int vertexNum = parseLeInt();
318         if(vertexNum % TRIVTX != 0) throw new MmdFormatException();
319         int surfaceNum = vertexNum / TRIVTX;
320
321         this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);
322
323         for(int ct = 0; ct < surfaceNum; ct++){
324             int vertexId1 = parseLeUShortAsInt();
325             int vertexId2 = parseLeUShortAsInt();
326             int vertexId3 = parseLeUShortAsInt();
327             this.shapeHandler.pmdSurfaceTriangle(vertexId1,
328                                                  vertexId2,
329                                                  vertexId3 );
330             this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);
331         }
332
333         this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);
334
335         return;
336     }
337
338     /**
339      * 材質情報のパースと通知。
340      * @throws IOException IOエラー
341      * @throws MmdFormatException フォーマットエラー
342      */
343     private void parseMaterialList() throws IOException, MmdFormatException{
344         int materialNum = parseLeInt();
345
346         this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,
347                                        materialNum );
348
349         for(int ct = 0; ct < materialNum; ct++){
350             parseColor();
351
352             int toonidx = parseUByteAsInt();
353             boolean hasEdge  = parseBoolean();
354             int surfaceCount = parseLeInt();
355
356             String shadingFile =
357                     parsePmdText(PmdLimits.MAXBYTES_TEXTUREFILENAME);
358             String[] splitted = ShadingUtil.splitShadingFileInfo(shadingFile);
359             String textureFile = splitted[0];
360             String sphereFile  = splitted[1];
361
362             this.materialHandler.pmdMaterialShading(toonidx,
363                                                     textureFile, sphereFile );
364             this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);
365
366             this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);
367         }
368
369         this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);
370
371         return;
372     }
373
374     /**
375      * 色情報のパースと通知。
376      * @throws IOException IOエラー
377      * @throws MmdFormatException フォーマットエラー
378      */
379     private void parseColor() throws IOException, MmdFormatException{
380         float red;
381         float green;
382         float blue;
383
384         red   = parseLeFloat();
385         green = parseLeFloat();
386         blue  = parseLeFloat();
387         float alpha = parseLeFloat();
388
389         this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);
390
391         float shininess = parseLeFloat();
392         red   = parseLeFloat();
393         green = parseLeFloat();
394         blue  = parseLeFloat();
395
396         this.materialHandler.pmdMaterialSpecular(red, green, blue,
397                                                  shininess);
398
399         red   = parseLeFloat();
400         green = parseLeFloat();
401         blue  = parseLeFloat();
402
403         this.materialHandler.pmdMaterialAmbient(red, green, blue);
404
405         return;
406     }
407
408     /**
409      * ボーン情報のパースと通知。
410      * @throws IOException IOエラー
411      * @throws MmdFormatException フォーマットエラー
412      */
413     private void parseBoneList() throws IOException, MmdFormatException{
414         this.boneCount = parseLeUShortAsInt();
415
416         this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);
417
418         for(int ct = 0; ct < this.boneCount; ct++){
419             String boneName =
420                     parsePmdText(PmdLimits.MAXBYTES_BONENAME);
421             int parentId  = parseLeUShortAsInt();
422             int tailId    = parseLeUShortAsInt();
423             byte boneKind = parseByte();
424             int srcId     = parseLeUShortAsInt();
425
426             this.boneHandler.pmdBoneInfo(boneName, boneKind);
427             this.boneHandler.pmdBoneLink(parentId, tailId, srcId);
428
429             float xPos = parseLeFloat();
430             float yPos = parseLeFloat();
431             float zPos = parseLeFloat();
432
433             this.boneHandler.pmdBonePosition(xPos, yPos, zPos);
434
435             this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);
436         }
437
438         this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);
439
440         return;
441     }
442
443     /**
444      * IKリスト情報のパースと通知。
445      * @throws IOException IOエラー
446      * @throws MmdFormatException フォーマットエラー
447      */
448     private void parseIKList() throws IOException, MmdFormatException{
449         int ikCount = parseLeUShortAsInt();
450
451         this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);
452
453         for(int ct = 0; ct < ikCount; ct++){
454             int boneId      = parseLeUShortAsInt();
455             int targetId    = parseLeUShortAsInt();
456             int chainLength = parseUByteAsInt();
457             int depth       = parseLeUShortAsInt();
458             float weight    = parseLeFloat();
459
460             this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);
461
462             parseIKChainList(chainLength);
463
464             this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);
465         }
466
467         this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);
468
469         return;
470     }
471
472     /**
473      * IKチェーン情報のパースと通知。
474      * @param chainLength チェーン長
475      * @throws IOException IOエラー
476      * @throws MmdFormatException フォーマットエラー
477      */
478     private void parseIKChainList(int chainLength)
479             throws IOException, MmdFormatException{
480         this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,
481                                    chainLength);
482
483         for(int ct = 0; ct < chainLength; ct++){
484             int childId = parseLeUShortAsInt();
485             this.boneHandler.pmdIKChainInfo(childId);
486
487             this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);
488         }
489
490         this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);
491
492         return;
493     }
494
495     /**
496      * モーフ情報のパースと通知。
497      * @throws IOException IOエラー
498      * @throws MmdFormatException フォーマットエラー
499      */
500     private void parseMorphList() throws IOException, MmdFormatException{
501         this.morphCount = parseLeUShortAsInt();
502
503         this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,
504                                     this.morphCount );
505
506         for(int ct = 0; ct < this.morphCount; ct++){
507             String morphName =
508                     parsePmdText(PmdLimits.MAXBYTES_MORPHNAME);
509             int vertexCount = parseLeInt();
510             byte morphType  = parseByte();
511
512             this.morphHandler.pmdMorphInfo(morphName, morphType);
513
514             parseMorphVertexList(vertexCount);
515
516             this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);
517         }
518
519         this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);
520
521         return;
522     }
523
524     /**
525      * モーフ形状のパースと通知。
526      * @param vertexCount 頂点数
527      * @throws IOException IOエラー
528      * @throws MmdFormatException フォーマットエラー
529      */
530     private void parseMorphVertexList(int vertexCount)
531             throws IOException, MmdFormatException{
532         this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,
533                                     vertexCount );
534
535         for(int ct = 0; ct < vertexCount; ct++){
536             int vertexId = parseLeInt();
537             float xPos = parseLeFloat();
538             float yPos = parseLeFloat();
539             float zPos = parseLeFloat();
540             this.morphHandler.pmdMorphVertexInfo(vertexId, xPos, yPos, zPos);
541
542             this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);
543         }
544
545         this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);
546
547         return;
548     }
549
550     /**
551      * モーフGUI表示順のパースと通知。
552      * @throws IOException IOエラー
553      * @throws MmdFormatException フォーマットエラー
554      */
555     private void parseMorphOrderList()
556             throws IOException, MmdFormatException{
557         int morphOrderCount = parseUByteAsInt();
558
559         this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,
560                                     morphOrderCount );
561
562         for(int ct = 0; ct < morphOrderCount; ct++){
563             int morphId = parseLeUShortAsInt();
564             this.morphHandler.pmdMorphOrderInfo(morphId);
565
566             this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);
567         }
568
569         this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);
570
571         return;
572     }
573
574     /**
575      * ボーングループ名のパースと通知。
576      * @throws IOException IOエラー
577      * @throws MmdFormatException フォーマットエラー
578      */
579     private void parseBoneGroupList()
580             throws IOException, MmdFormatException{
581         this.boneGroupCount = parseUByteAsInt();
582
583         this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,
584                                    this.boneGroupCount);
585
586         for(int ct = 0; ct < this.boneGroupCount; ct++){
587             String groupName =
588                     parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME);
589             groupName = chopLastLF(groupName);
590             this.boneHandler.pmdBoneGroupInfo(groupName);
591
592             this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);
593         }
594
595         this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);
596
597         return;
598     }
599
600     /**
601      * ボーングループ内訳のパースと通知。
602      * @throws IOException IOエラー
603      * @throws MmdFormatException フォーマットエラー
604      */
605     private void parseGroupedBoneList()
606             throws IOException, MmdFormatException{
607         int groupedBoneCount = parseLeInt();
608
609         this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,
610                                    groupedBoneCount);
611
612         for(int ct = 0; ct < groupedBoneCount; ct++){
613             int boneId  = parseLeUShortAsInt();
614             int groupId = parseUByteAsInt();
615             this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);
616
617             this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);
618         }
619
620         this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);
621
622         return;
623     }
624
625 }