BlendParser.hx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // .blend file parser
  2. // https://github.com/armory3d/blend
  3. // Reference:
  4. // https://github.com/fschutt/mystery-of-the-blend-backup
  5. // https://web.archive.org/web/20170630054951/http://www.atmind.nl/blender/mystery_ot_blend.html
  6. // Usage:
  7. // var bl = new BlendParser(blob:kha.Blob);
  8. // trace(bl.dir("Scene"));
  9. // var scenes = bl.get("Scene");
  10. // trace(scenes[0].get("id").get("name"));
  11. package arm.format;
  12. // https://github.com/Kode/Kha
  13. import kha.Blob;
  14. class BlendParser {
  15. public var pos: Int;
  16. var blob: Blob;
  17. // Header
  18. public var version: String;
  19. public var pointerSize: Int;
  20. public var littleEndian: Bool;
  21. // Data
  22. public var blocks: Array<Block> = [];
  23. public var dna: Dna = null;
  24. public var map = new Map<Int, Map<Int, Block>>(); // Map blocks by memory address
  25. public function new(blob: Blob) {
  26. this.blob = blob;
  27. this.pos = 0;
  28. if (readChars(7) != "BLENDER") {
  29. this.blob = Blob.fromBytes(haxe.io.Bytes.ofData(Krom.inflate(blob.toBytes().getData(), false)));
  30. this.pos = 0;
  31. if (readChars(7) != "BLENDER") return;
  32. }
  33. parse();
  34. }
  35. public function dir(type: String): Array<String> {
  36. // Return structure fields
  37. var typeIndex = getTypeIndex(dna, type);
  38. if (typeIndex == -1) return null;
  39. var ds = getStruct(dna, typeIndex);
  40. var fields: Array<String> = [];
  41. for (i in 0...ds.fieldNames.length) {
  42. var nameIndex = ds.fieldNames[i];
  43. var typeIndex = ds.fieldTypes[i];
  44. fields.push(dna.types[typeIndex] + " " + dna.names[nameIndex]);
  45. }
  46. return fields;
  47. }
  48. public function get(type: String): Array<Handle> {
  49. if (dna == null) return null;
  50. // Return all structures of type
  51. var typeIndex = getTypeIndex(dna, type);
  52. if (typeIndex == -1) return null;
  53. var ds = getStruct(dna, typeIndex);
  54. var handles: Array<Handle> = [];
  55. for (b in blocks) {
  56. if (dna.structs[b.sdnaIndex].type == typeIndex) {
  57. var h = new Handle();
  58. handles.push(h);
  59. h.block = b;
  60. h.ds = ds;
  61. }
  62. }
  63. return handles;
  64. }
  65. public static function getStruct(dna: Dna, typeIndex: Int): DnaStruct {
  66. for (ds in dna.structs) if (ds.type == typeIndex) return ds;
  67. return null;
  68. }
  69. public static function getTypeIndex(dna: Dna, type: String): Int {
  70. for (i in 0...dna.types.length) if (type == dna.types[i]) return i;
  71. return -1;
  72. }
  73. function parse() {
  74. // Pointer size: _ 32bit, - 64bit
  75. pointerSize = readChar() == "_" ? 4 : 8;
  76. // v - little endian, V - big endian
  77. littleEndian = readChar() == "v";
  78. if (littleEndian) {
  79. read16 = read16LE;
  80. read32 = read32LE;
  81. read64 = read64LE;
  82. readf32 = readf32LE;
  83. }
  84. else {
  85. read16 = read16BE;
  86. read32 = read32BE;
  87. read64 = read64BE;
  88. readf32 = readf32BE;
  89. }
  90. version = readChars(3);
  91. // Reading file blocks
  92. // Header - data
  93. while (pos < blob.length) {
  94. align();
  95. var b = new Block();
  96. // Block type
  97. b.code = readChars(4);
  98. if (b.code == "ENDB") break;
  99. blocks.push(b);
  100. b.blend = this;
  101. // Total block length
  102. b.size = read32();
  103. // Memory address
  104. var addr = readPointer();
  105. if (!map.exists(addr.high)) map.set(addr.high, new Map<Int, Block>());
  106. map.get(addr.high).set(addr.low, b);
  107. // Index of dna struct contained in this block
  108. b.sdnaIndex = read32();
  109. // Number of dna structs in this block
  110. b.count = read32();
  111. b.pos = pos;
  112. // This block stores dna structures
  113. if (b.code == "DNA1") {
  114. dna = new Dna();
  115. var id = readChars(4); // SDNA
  116. var nameId = readChars(4); // NAME
  117. var namesCount = read32();
  118. for (i in 0...namesCount) {
  119. dna.names.push(readString());
  120. }
  121. align();
  122. var typeId = readChars(4); // TYPE
  123. var typesCount = read32();
  124. for (i in 0...typesCount) {
  125. dna.types.push(readString());
  126. }
  127. align();
  128. var lenId = readChars(4); // TLEN
  129. for (i in 0...typesCount) {
  130. dna.typesLength.push(read16());
  131. }
  132. align();
  133. var structId = readChars(4); // STRC
  134. var structCount = read32();
  135. for (i in 0...structCount) {
  136. var ds = new DnaStruct();
  137. dna.structs.push(ds);
  138. ds.dna = dna;
  139. ds.type = read16();
  140. var fieldCount = read16();
  141. if (fieldCount > 0) {
  142. ds.fieldTypes = [];
  143. ds.fieldNames = [];
  144. for (j in 0...fieldCount) {
  145. ds.fieldTypes.push(read16());
  146. ds.fieldNames.push(read16());
  147. }
  148. }
  149. }
  150. }
  151. else {
  152. pos += b.size;
  153. }
  154. }
  155. }
  156. function align() {
  157. // 4 bytes aligned
  158. var mod = pos % 4;
  159. if (mod > 0) pos += 4 - mod;
  160. }
  161. public function read8(): Int {
  162. var i = blob.readU8(pos);
  163. pos += 1;
  164. return i;
  165. }
  166. public var read16: Void->Int;
  167. public var read32: Void->Int;
  168. public var read64: Void->haxe.Int64;
  169. public var readf32: Void->Float;
  170. function read16LE(): Int {
  171. var i = blob.readS16LE(pos);
  172. pos += 2;
  173. return i;
  174. }
  175. function read32LE(): Int {
  176. var i = blob.readS32LE(pos);
  177. pos += 4;
  178. return i;
  179. }
  180. function read64LE(): haxe.Int64 {
  181. return haxe.Int64.make(read32(), read32());
  182. }
  183. function readf32LE(): Float {
  184. var f = blob.readF32LE(pos);
  185. pos += 4;
  186. return f;
  187. }
  188. function read16BE(): Int {
  189. var i = blob.readS16BE(pos);
  190. pos += 2;
  191. return i;
  192. }
  193. function read32BE(): Int {
  194. var i = blob.readS32BE(pos);
  195. pos += 4;
  196. return i;
  197. }
  198. function read64BE(): haxe.Int64 {
  199. return haxe.Int64.make(read32(), read32());
  200. }
  201. function readf32BE(): Float {
  202. var f = blob.readF32BE(pos);
  203. pos += 4;
  204. return f;
  205. }
  206. public function read8array(len: Int): haxe.io.Int32Array {
  207. var ar = new haxe.io.Int32Array(len);
  208. for (i in 0...len) ar[i] = read8();
  209. return ar;
  210. }
  211. public function read16array(len: Int): haxe.io.Int32Array {
  212. var ar = new haxe.io.Int32Array(len);
  213. for (i in 0...len) ar[i] = read16();
  214. return ar;
  215. }
  216. public function read32array(len: Int): haxe.io.Int32Array {
  217. var ar = new haxe.io.Int32Array(len);
  218. for (i in 0...len) ar[i] = read32();
  219. return ar;
  220. }
  221. public function readf32array(len: Int): kha.arrays.Float32Array {
  222. var ar = new kha.arrays.Float32Array(len);
  223. for (i in 0...len) ar[i] = readf32();
  224. return ar;
  225. }
  226. public function readString(): String {
  227. var s = "";
  228. while (true) {
  229. var ch = read8();
  230. if (ch == 0) break;
  231. s += String.fromCharCode(ch);
  232. }
  233. return s;
  234. }
  235. public function readChars(len: Int): String {
  236. var s = "";
  237. for (i in 0...len) s += readChar();
  238. return s;
  239. }
  240. public function readChar(): String {
  241. return String.fromCharCode(read8());
  242. }
  243. public function readPointer(): haxe.Int64 {
  244. return pointerSize == 4 ? haxe.Int64.ofInt(read32()) : read64();
  245. }
  246. }
  247. class Block {
  248. public var blend: BlendParser;
  249. public var code: String;
  250. public var size: Int;
  251. public var sdnaIndex: Int;
  252. public var count: Int;
  253. public var pos: Int; // Byte pos of data start in blob
  254. public function new() {}
  255. }
  256. class Dna {
  257. public var names: Array<String> = [];
  258. public var types: Array<String> = [];
  259. public var typesLength: Array<Int> = [];
  260. public var structs: Array<DnaStruct> = [];
  261. public function new() {}
  262. }
  263. class DnaStruct {
  264. public var dna: Dna;
  265. public var type: Int; // Index in dna.types
  266. public var fieldTypes: Array<Int>; // Index in dna.types
  267. public var fieldNames: Array<Int>; // Index in dna.names
  268. public function new() {}
  269. }
  270. class Handle {
  271. public var block: Block;
  272. public var offset: Int = 0; // Block data bytes offset
  273. public var ds: DnaStruct;
  274. public function new() {}
  275. function getSize(index: Int): Int {
  276. var nameIndex = ds.fieldNames[index];
  277. var typeIndex = ds.fieldTypes[index];
  278. var dna = ds.dna;
  279. var n = dna.names[nameIndex];
  280. var size = 0;
  281. if (n.indexOf("*") >= 0) size = block.blend.pointerSize;
  282. else size = dna.typesLength[typeIndex];
  283. if (n.indexOf("[") > 0) size *= getArrayLen(n);
  284. return size;
  285. }
  286. function baseName(s: String): String {
  287. while (s.charAt(0) == "*") s = s.substring(1, s.length);
  288. if (s.charAt(s.length - 1) == "]") s = s.substring(0, s.indexOf("["));
  289. return s;
  290. }
  291. function getArrayLen(s: String): Int {
  292. return Std.parseInt(s.substring(s.indexOf("[") + 1, s.indexOf("]")));
  293. }
  294. public function get(name: String, index = 0, asType: String = null, arrayLen = 0): Dynamic {
  295. // Return raw type or structure
  296. var dna = ds.dna;
  297. for (i in 0...ds.fieldNames.length) {
  298. var nameIndex = ds.fieldNames[i];
  299. var dnaName = dna.names[nameIndex];
  300. if (name == baseName(dnaName)) {
  301. var typeIndex = ds.fieldTypes[i];
  302. var type = dna.types[typeIndex];
  303. var newOffset = offset;
  304. for (j in 0...i) newOffset += getSize(j);
  305. // Cast void* to type
  306. if (asType != null) {
  307. for (i in 0...dna.types.length) {
  308. if (dna.types[i] == asType) {
  309. typeIndex = i;
  310. break;
  311. }
  312. }
  313. }
  314. // Raw type
  315. if (typeIndex < 12) {
  316. var blend = block.blend;
  317. blend.pos = block.pos + newOffset;
  318. var isArray = dnaName.charAt(dnaName.length - 1) == "]";
  319. var len = isArray ? (arrayLen > 0 ? arrayLen : getArrayLen(dnaName)) : 1;
  320. switch (type) {
  321. case "int": return isArray ? blend.read32array(len) : blend.read32();
  322. case "char": return isArray ? blend.readString() : blend.read8();
  323. case "uchar": return isArray ? blend.read8array(len) : blend.read8();
  324. case "short": return isArray ? blend.read16array(len) : blend.read16();
  325. case "ushort": return isArray ? blend.read16array(len) : blend.read16();
  326. case "float": return isArray ? blend.readf32array(len) : blend.readf32();
  327. case "double": return 0; //blend.readf64();
  328. case "long": return isArray ? blend.read32array(len) : blend.read32();
  329. case "ulong": return isArray ? blend.read32array(len) : blend.read32();
  330. case "int64_t": return blend.read64();
  331. case "uint64_t": return blend.read64();
  332. case "void": return 0;
  333. }
  334. }
  335. // Structure
  336. var h = new Handle();
  337. h.ds = BlendParser.getStruct(dna, typeIndex);
  338. var isPointer = dnaName.charAt(0) == "*";
  339. if (isPointer) {
  340. block.blend.pos = block.pos + newOffset;
  341. var addr = block.blend.readPointer();
  342. if (block.blend.map.exists(addr.high)) {
  343. h.block = block.blend.map.get(addr.high).get(addr.low);
  344. }
  345. else h.block = block;
  346. h.offset = 0;
  347. }
  348. else {
  349. h.block = block;
  350. h.offset = newOffset;
  351. }
  352. h.offset += dna.typesLength[typeIndex] * index;
  353. return h;
  354. }
  355. }
  356. return null;
  357. }
  358. }