4 * License : The MIT License
5 * Copyright(c) 2010 MikuToga Partners
8 package jp.sfjp.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;
18 import jp.sfjp.mikutoga.pmd.ShadingUtil;
23 public class PmdParserBase extends CommonParser {
26 * PMDで用いられる文字エンコーディング(windows-31j)。
27 * ほぼShift_JISのスーパーセットと思ってよい。
28 * デコード結果はUCS-2集合に収まるはず。
30 public static final Charset CS_WIN31J = Charset.forName("windows-31j");
33 protected static final String CR = "\r"; // 0x0d
35 protected static final String LF = "\n"; // 0x0a
37 protected static final String CRLF = CR + LF; // 0x0d, 0x0a
40 private static final int TRIVTX = 3;
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
50 assert MAGIC_BYTES.length == HEADER_LENGTH;
54 private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J);
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;
62 private int boneCount = -1;
63 private int morphCount = -1;
64 private int boneGroupCount = -1;
71 public PmdParserBase(InputStream source){
73 this.decoderWin31j.setZeroChopMode(true);
79 * 文字列の最後がLF(0x0a)の場合削除する。
82 * @return 末尾LFが削除された文字列
84 public static String chopLastLF(String name){
87 if(name.endsWith(LF)){
88 result = name.substring(0, name.length() - 1);
100 public void setBasicHandler(PmdBasicHandler handler){
102 this.basicHandler = NullHandler.HANDLER;
104 this.basicHandler = handler;
111 * @param handler ハンドラ
113 public void setShapeHandler(PmdShapeHandler handler){
115 this.shapeHandler = NullHandler.HANDLER;
117 this.shapeHandler = handler;
124 * @param handler ハンドラ
126 public void setMaterialHandler(PmdMaterialHandler handler){
128 this.materialHandler = NullHandler.HANDLER;
130 this.materialHandler = handler;
137 * @param handler ハンドラ
139 public void setBoneHandler(PmdBoneHandler handler){
141 this.boneHandler = NullHandler.HANDLER;
143 this.boneHandler = handler;
150 * @param handler ハンドラ
152 public void setMorphHandler(PmdMorphHandler handler){
154 this.morphHandler = NullHandler.HANDLER;
156 this.morphHandler = handler;
162 * パースによって得られたボーン数を返す。
165 protected int getBoneCount(){
166 return this.boneCount;
170 * パースによって得られたモーフ数を返す。
173 protected int getMorphCount(){
174 return this.morphCount;
178 * パースによって得られたボーングループ数を返す。
181 protected int getBoneGroupCount(){
182 return this.boneGroupCount;
186 * 指定されたバイト長に収まるゼロ終端(0x00)文字列を読み込む。
187 * <p>入力バイト列はwindows-31jエンコーディングとして解釈される。
188 * <p>ゼロ終端以降のデータは無視されるが、
189 * IO入力は指定バイト数だけ読み進められる。
190 * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、
191 * そこまでのデータから文字列を構成する。
192 * @param byteLen 読み込みバイト数
194 * @throws IOException IOエラー
195 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
196 * @throws MmdFormatException 不正な文字エンコーディングが検出された。
198 protected String parsePmdText(int byteLen)
202 String result = parseString(this.decoderWin31j, byteLen);
208 * @throws IOException IOエラー
209 * @throws MmdFormatException フォーマットエラー
211 public void parsePmd()
212 throws IOException, MmdFormatException {
213 this.basicHandler.pmdParseStart();
217 boolean hasMoreData = hasMore();
218 this.basicHandler.pmdParseEnd(hasMoreData);
224 * PMDファイル本体のパースを開始する。
225 * パーサを拡張する場合はこのメソッドをオーバーライドする。
226 * @throws IOException IOエラー
227 * @throws MmdFormatException フォーマットエラー
229 protected void parseBody() throws IOException, MmdFormatException{
238 parseMorphOrderList();
239 parseBoneGroupList();
240 parseGroupedBoneList();
246 * PMDファイルヘッダ部のパースと通知。
247 * @throws IOException IOエラー
248 * @throws MmdFormatException フォーマットエラー
250 private void parsePmdHeader() throws IOException, MmdFormatException{
251 byte[] header = new byte[HEADER_LENGTH];
252 parseByteArray(header);
254 if( ! Arrays.equals(header, MAGIC_BYTES) ){
255 throw new MmdFormatException("unknown PMD-header type");
259 parsePmdText(PmdLimits.MAXBYTES_MODELNAME);
261 parsePmdText(PmdLimits.MAXBYTES_MODELDESC);
262 description = description.replace(CRLF, LF);
264 this.basicHandler.pmdHeaderInfo(header);
265 this.basicHandler.pmdModelInfo(modelName, description);
272 * @throws IOException IOエラー
273 * @throws MmdFormatException フォーマットエラー
275 private void parseVertexList() throws IOException, MmdFormatException{
276 int vertexNum = parseLeInt();
278 this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);
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);
286 float xVec = parseLeFloat();
287 float yVec = parseLeFloat();
288 float zVec = parseLeFloat();
289 this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);
291 float uVal = parseLeFloat();
292 float vVal = parseLeFloat();
293 this.shapeHandler.pmdVertexUV(uVal, vVal);
295 int boneId1 = parseLeUShortAsInt();
296 int boneId2 = parseLeUShortAsInt();
297 int weightForB1 = parseUByteAsInt();
298 this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);
300 boolean hideEdge = parseBoolean();
301 this.shapeHandler.pmdVertexEdge(hideEdge);
303 this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);
306 this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);
313 * @throws IOException IOエラー
314 * @throws MmdFormatException フォーマットエラー
316 private void parseSurfaceList() throws IOException, MmdFormatException{
317 int vertexNum = parseLeInt();
318 if(vertexNum % TRIVTX != 0) throw new MmdFormatException();
319 int surfaceNum = vertexNum / TRIVTX;
321 this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);
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,
330 this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);
333 this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);
340 * @throws IOException IOエラー
341 * @throws MmdFormatException フォーマットエラー
343 private void parseMaterialList() throws IOException, MmdFormatException{
344 int materialNum = parseLeInt();
346 this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,
349 for(int ct = 0; ct < materialNum; ct++){
352 int toonidx = parseUByteAsInt();
353 boolean hasEdge = parseBoolean();
354 int surfaceCount = parseLeInt();
357 parsePmdText(PmdLimits.MAXBYTES_TEXTUREFILENAME);
358 String[] splitted = ShadingUtil.splitShadingFileInfo(shadingFile);
359 String textureFile = splitted[0];
360 String sphereFile = splitted[1];
362 this.materialHandler.pmdMaterialShading(toonidx,
363 textureFile, sphereFile );
364 this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);
366 this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);
369 this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);
376 * @throws IOException IOエラー
377 * @throws MmdFormatException フォーマットエラー
379 private void parseColor() throws IOException, MmdFormatException{
384 red = parseLeFloat();
385 green = parseLeFloat();
386 blue = parseLeFloat();
387 float alpha = parseLeFloat();
389 this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);
391 float shininess = parseLeFloat();
392 red = parseLeFloat();
393 green = parseLeFloat();
394 blue = parseLeFloat();
396 this.materialHandler.pmdMaterialSpecular(red, green, blue,
399 red = parseLeFloat();
400 green = parseLeFloat();
401 blue = parseLeFloat();
403 this.materialHandler.pmdMaterialAmbient(red, green, blue);
410 * @throws IOException IOエラー
411 * @throws MmdFormatException フォーマットエラー
413 private void parseBoneList() throws IOException, MmdFormatException{
414 this.boneCount = parseLeUShortAsInt();
416 this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);
418 for(int ct = 0; ct < this.boneCount; ct++){
420 parsePmdText(PmdLimits.MAXBYTES_BONENAME);
421 int parentId = parseLeUShortAsInt();
422 int tailId = parseLeUShortAsInt();
423 byte boneKind = parseByte();
424 int srcId = parseLeUShortAsInt();
426 this.boneHandler.pmdBoneInfo(boneName, boneKind);
427 this.boneHandler.pmdBoneLink(parentId, tailId, srcId);
429 float xPos = parseLeFloat();
430 float yPos = parseLeFloat();
431 float zPos = parseLeFloat();
433 this.boneHandler.pmdBonePosition(xPos, yPos, zPos);
435 this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);
438 this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);
445 * @throws IOException IOエラー
446 * @throws MmdFormatException フォーマットエラー
448 private void parseIKList() throws IOException, MmdFormatException{
449 int ikCount = parseLeUShortAsInt();
451 this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);
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();
460 this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);
462 parseIKChainList(chainLength);
464 this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);
467 this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);
474 * @param chainLength チェーン長
475 * @throws IOException IOエラー
476 * @throws MmdFormatException フォーマットエラー
478 private void parseIKChainList(int chainLength)
479 throws IOException, MmdFormatException{
480 this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,
483 for(int ct = 0; ct < chainLength; ct++){
484 int childId = parseLeUShortAsInt();
485 this.boneHandler.pmdIKChainInfo(childId);
487 this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);
490 this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);
497 * @throws IOException IOエラー
498 * @throws MmdFormatException フォーマットエラー
500 private void parseMorphList() throws IOException, MmdFormatException{
501 this.morphCount = parseLeUShortAsInt();
503 this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,
506 for(int ct = 0; ct < this.morphCount; ct++){
508 parsePmdText(PmdLimits.MAXBYTES_MORPHNAME);
509 int vertexCount = parseLeInt();
510 byte morphType = parseByte();
512 this.morphHandler.pmdMorphInfo(morphName, morphType);
514 parseMorphVertexList(vertexCount);
516 this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);
519 this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);
526 * @param vertexCount 頂点数
527 * @throws IOException IOエラー
528 * @throws MmdFormatException フォーマットエラー
530 private void parseMorphVertexList(int vertexCount)
531 throws IOException, MmdFormatException{
532 this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,
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);
542 this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);
545 this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);
552 * @throws IOException IOエラー
553 * @throws MmdFormatException フォーマットエラー
555 private void parseMorphOrderList()
556 throws IOException, MmdFormatException{
557 int morphOrderCount = parseUByteAsInt();
559 this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,
562 for(int ct = 0; ct < morphOrderCount; ct++){
563 int morphId = parseLeUShortAsInt();
564 this.morphHandler.pmdMorphOrderInfo(morphId);
566 this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);
569 this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);
576 * @throws IOException IOエラー
577 * @throws MmdFormatException フォーマットエラー
579 private void parseBoneGroupList()
580 throws IOException, MmdFormatException{
581 this.boneGroupCount = parseUByteAsInt();
583 this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,
584 this.boneGroupCount);
586 for(int ct = 0; ct < this.boneGroupCount; ct++){
588 parsePmdText(PmdLimits.MAXBYTES_BONEGROUPNAME);
589 groupName = chopLastLF(groupName);
590 this.boneHandler.pmdBoneGroupInfo(groupName);
592 this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);
595 this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);
602 * @throws IOException IOエラー
603 * @throws MmdFormatException フォーマットエラー
605 private void parseGroupedBoneList()
606 throws IOException, MmdFormatException{
607 int groupedBoneCount = parseLeInt();
609 this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,
612 for(int ct = 0; ct < groupedBoneCount; ct++){
613 int boneId = parseLeUShortAsInt();
614 int groupId = parseUByteAsInt();
615 this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);
617 this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);
620 this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);