1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.scene.plugins.blender.file;
33 
34 import com.jme3.scene.plugins.blender.BlenderContext;
35 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
36 import java.util.HashMap;
37 import java.util.Map;
38 
39 /**
40  * The data block containing the description of the file.
41  * @author Marcin Roguski
42  */
43 public class DnaBlockData {
44 
45     private static final int SDNA_ID = 'S' << 24 | 'D' << 16 | 'N' << 8 | 'A';	//SDNA
46     private static final int NAME_ID = 'N' << 24 | 'A' << 16 | 'M' << 8 | 'E';	//NAME
47     private static final int TYPE_ID = 'T' << 24 | 'Y' << 16 | 'P' << 8 | 'E';	//TYPE
48     private static final int TLEN_ID = 'T' << 24 | 'L' << 16 | 'E' << 8 | 'N';	//TLEN
49     private static final int STRC_ID = 'S' << 24 | 'T' << 16 | 'R' << 8 | 'C';	//STRC
50     /** Structures available inside the file. */
51     private final Structure[] structures;
52     /** A map that helps finding a structure by type. */
53     private final Map<String, Structure> structuresMap;
54 
55     /**
56      * Constructor. Loads the block from the given stream during instance creation.
57      * @param inputStream
58      *        the stream we read the block from
59      * @param blenderContext
60      *        the blender context
61      * @throws BlenderFileException
62      *         this exception is throw if the blend file is invalid or somehow corrupted
63      */
DnaBlockData(BlenderInputStream inputStream, BlenderContext blenderContext)64     public DnaBlockData(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException {
65         int identifier;
66 
67         //reading 'SDNA' identifier
68         identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
69                 | inputStream.readByte() << 8 | inputStream.readByte();
70 
71         if (identifier != SDNA_ID) {
72             throw new BlenderFileException("Invalid identifier! '" + this.toString(SDNA_ID) + "' expected and found: " + this.toString(identifier));
73         }
74 
75         //reading names
76         identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
77                 | inputStream.readByte() << 8 | inputStream.readByte();
78         if (identifier != NAME_ID) {
79             throw new BlenderFileException("Invalid identifier! '" + this.toString(NAME_ID) + "' expected and found: " + this.toString(identifier));
80         }
81         int amount = inputStream.readInt();
82         if (amount <= 0) {
83             throw new BlenderFileException("The names amount number should be positive!");
84         }
85         String[] names = new String[amount];
86         for (int i = 0; i < amount; ++i) {
87             names[i] = inputStream.readString();
88         }
89 
90         //reding types
91         inputStream.alignPosition(4);
92         identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
93                 | inputStream.readByte() << 8 | inputStream.readByte();
94         if (identifier != TYPE_ID) {
95             throw new BlenderFileException("Invalid identifier! '" + this.toString(TYPE_ID) + "' expected and found: " + this.toString(identifier));
96         }
97         amount = inputStream.readInt();
98         if (amount <= 0) {
99             throw new BlenderFileException("The types amount number should be positive!");
100         }
101         String[] types = new String[amount];
102         for (int i = 0; i < amount; ++i) {
103             types[i] = inputStream.readString();
104         }
105 
106         //reading lengths
107         inputStream.alignPosition(4);
108         identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
109                 | inputStream.readByte() << 8 | inputStream.readByte();
110         if (identifier != TLEN_ID) {
111             throw new BlenderFileException("Invalid identifier! '" + this.toString(TLEN_ID) + "' expected and found: " + this.toString(identifier));
112         }
113         int[] lengths = new int[amount];//theamount is the same as int types
114         for (int i = 0; i < amount; ++i) {
115             lengths[i] = inputStream.readShort();
116         }
117 
118         //reading structures
119         inputStream.alignPosition(4);
120         identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16
121                 | inputStream.readByte() << 8 | inputStream.readByte();
122         if (identifier != STRC_ID) {
123             throw new BlenderFileException("Invalid identifier! '" + this.toString(STRC_ID) + "' expected and found: " + this.toString(identifier));
124         }
125         amount = inputStream.readInt();
126         if (amount <= 0) {
127             throw new BlenderFileException("The structures amount number should be positive!");
128         }
129         structures = new Structure[amount];
130         structuresMap = new HashMap<String, Structure>(amount);
131         for (int i = 0; i < amount; ++i) {
132             structures[i] = new Structure(inputStream, names, types, blenderContext);
133             if (structuresMap.containsKey(structures[i].getType())) {
134                 throw new BlenderFileException("Blend file seems to be corrupted! The type " + structures[i].getType() + " is defined twice!");
135             }
136             structuresMap.put(structures[i].getType(), structures[i]);
137         }
138     }
139 
140     /**
141      * This method returns the amount of the structures.
142      * @return the amount of the structures
143      */
getStructuresCount()144     public int getStructuresCount() {
145         return structures.length;
146     }
147 
148     /**
149      * This method returns the structure of the given index.
150      * @param index
151      *        the index of the structure
152      * @return the structure of the given index
153      */
getStructure(int index)154     public Structure getStructure(int index) {
155         try {
156             return (Structure) structures[index].clone();
157         } catch (CloneNotSupportedException e) {
158             throw new IllegalStateException("Structure should be clonable!!!", e);
159         }
160     }
161 
162     /**
163      * This method returns a structure of the given name. If the name does not exists then null is returned.
164      * @param name
165      *        the name of the structure
166      * @return the required structure or null if the given name is inapropriate
167      */
getStructure(String name)168     public Structure getStructure(String name) {
169         try {
170             return (Structure) structuresMap.get(name).clone();
171         } catch (CloneNotSupportedException e) {
172             throw new IllegalStateException(e.getMessage(), e);
173         }
174     }
175 
176     /**
177      * This method indicates if the structure of the given name exists.
178      * @param name
179      *        the name of the structure
180      * @return true if the structure exists and false otherwise
181      */
hasStructure(String name)182     public boolean hasStructure(String name) {
183         return structuresMap.containsKey(name);
184     }
185 
186     /**
187      * This method converts the given identifier code to string.
188      * @param code
189      *        the code taht is to be converted
190      * @return the string value of the identifier
191      */
toString(int code)192     private String toString(int code) {
193         char c1 = (char) ((code & 0xFF000000) >> 24);
194         char c2 = (char) ((code & 0xFF0000) >> 16);
195         char c3 = (char) ((code & 0xFF00) >> 8);
196         char c4 = (char) (code & 0xFF);
197         return String.valueOf(c1) + c2 + c3 + c4;
198     }
199 
200     @Override
toString()201     public String toString() {
202         StringBuilder stringBuilder = new StringBuilder("=============== ").append(SDNA_ID).append('\n');
203         for (Structure structure : structures) {
204             stringBuilder.append(structure.toString()).append('\n');
205         }
206         return stringBuilder.append("===============").toString();
207     }
208 }
209