| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- // .blend file parser
- // https://github.com/armory3d/blend
- // Reference:
- // https://github.com/fschutt/mystery-of-the-blend-backup
- // https://web.archive.org/web/20170630054951/http://www.atmind.nl/blender/mystery_ot_blend.html
- // Usage:
- // var bl = new BlendParser(blob:kha.Blob);
- // trace(bl.dir("Scene"));
- // var scenes = bl.get("Scene");
- // trace(scenes[0].get("id").get("name"));
- package arm.format;
- // https://github.com/Kode/Kha
- import kha.Blob;
- class BlendParser {
- public var pos: Int;
- var blob: Blob;
- // Header
- public var version: String;
- public var pointerSize: Int;
- public var littleEndian: Bool;
- // Data
- public var blocks: Array<Block> = [];
- public var dna: Dna = null;
- public var map = new Map<Int, Map<Int, Block>>(); // Map blocks by memory address
- public function new(blob: Blob) {
- this.blob = blob;
- this.pos = 0;
- if (readChars(7) != "BLENDER") {
- this.blob = Blob.fromBytes(haxe.io.Bytes.ofData(Krom.inflate(blob.toBytes().getData(), false)));
- this.pos = 0;
- if (readChars(7) != "BLENDER") return;
- }
- parse();
- }
- public function dir(type: String): Array<String> {
- // Return structure fields
- var typeIndex = getTypeIndex(dna, type);
- if (typeIndex == -1) return null;
- var ds = getStruct(dna, typeIndex);
- var fields: Array<String> = [];
- for (i in 0...ds.fieldNames.length) {
- var nameIndex = ds.fieldNames[i];
- var typeIndex = ds.fieldTypes[i];
- fields.push(dna.types[typeIndex] + " " + dna.names[nameIndex]);
- }
- return fields;
- }
- public function get(type: String): Array<Handle> {
- if (dna == null) return null;
- // Return all structures of type
- var typeIndex = getTypeIndex(dna, type);
- if (typeIndex == -1) return null;
- var ds = getStruct(dna, typeIndex);
- var handles: Array<Handle> = [];
- for (b in blocks) {
- if (dna.structs[b.sdnaIndex].type == typeIndex) {
- var h = new Handle();
- handles.push(h);
- h.block = b;
- h.ds = ds;
- }
- }
- return handles;
- }
- public static function getStruct(dna: Dna, typeIndex: Int): DnaStruct {
- for (ds in dna.structs) if (ds.type == typeIndex) return ds;
- return null;
- }
- public static function getTypeIndex(dna: Dna, type: String): Int {
- for (i in 0...dna.types.length) if (type == dna.types[i]) return i;
- return -1;
- }
- function parse() {
- // Pointer size: _ 32bit, - 64bit
- pointerSize = readChar() == "_" ? 4 : 8;
- // v - little endian, V - big endian
- littleEndian = readChar() == "v";
- if (littleEndian) {
- read16 = read16LE;
- read32 = read32LE;
- read64 = read64LE;
- readf32 = readf32LE;
- }
- else {
- read16 = read16BE;
- read32 = read32BE;
- read64 = read64BE;
- readf32 = readf32BE;
- }
- version = readChars(3);
- // Reading file blocks
- // Header - data
- while (pos < blob.length) {
- align();
- var b = new Block();
- // Block type
- b.code = readChars(4);
- if (b.code == "ENDB") break;
- blocks.push(b);
- b.blend = this;
- // Total block length
- b.size = read32();
- // Memory address
- var addr = readPointer();
- if (!map.exists(addr.high)) map.set(addr.high, new Map<Int, Block>());
- map.get(addr.high).set(addr.low, b);
- // Index of dna struct contained in this block
- b.sdnaIndex = read32();
- // Number of dna structs in this block
- b.count = read32();
- b.pos = pos;
- // This block stores dna structures
- if (b.code == "DNA1") {
- dna = new Dna();
- var id = readChars(4); // SDNA
- var nameId = readChars(4); // NAME
- var namesCount = read32();
- for (i in 0...namesCount) {
- dna.names.push(readString());
- }
- align();
- var typeId = readChars(4); // TYPE
- var typesCount = read32();
- for (i in 0...typesCount) {
- dna.types.push(readString());
- }
- align();
- var lenId = readChars(4); // TLEN
- for (i in 0...typesCount) {
- dna.typesLength.push(read16());
- }
- align();
- var structId = readChars(4); // STRC
- var structCount = read32();
- for (i in 0...structCount) {
- var ds = new DnaStruct();
- dna.structs.push(ds);
- ds.dna = dna;
- ds.type = read16();
- var fieldCount = read16();
- if (fieldCount > 0) {
- ds.fieldTypes = [];
- ds.fieldNames = [];
- for (j in 0...fieldCount) {
- ds.fieldTypes.push(read16());
- ds.fieldNames.push(read16());
- }
- }
- }
- }
- else {
- pos += b.size;
- }
- }
- }
- function align() {
- // 4 bytes aligned
- var mod = pos % 4;
- if (mod > 0) pos += 4 - mod;
- }
- public function read8(): Int {
- var i = blob.readU8(pos);
- pos += 1;
- return i;
- }
- public var read16: Void->Int;
- public var read32: Void->Int;
- public var read64: Void->haxe.Int64;
- public var readf32: Void->Float;
- function read16LE(): Int {
- var i = blob.readS16LE(pos);
- pos += 2;
- return i;
- }
- function read32LE(): Int {
- var i = blob.readS32LE(pos);
- pos += 4;
- return i;
- }
- function read64LE(): haxe.Int64 {
- return haxe.Int64.make(read32(), read32());
- }
- function readf32LE(): Float {
- var f = blob.readF32LE(pos);
- pos += 4;
- return f;
- }
- function read16BE(): Int {
- var i = blob.readS16BE(pos);
- pos += 2;
- return i;
- }
- function read32BE(): Int {
- var i = blob.readS32BE(pos);
- pos += 4;
- return i;
- }
- function read64BE(): haxe.Int64 {
- return haxe.Int64.make(read32(), read32());
- }
- function readf32BE(): Float {
- var f = blob.readF32BE(pos);
- pos += 4;
- return f;
- }
- public function read8array(len: Int): haxe.io.Int32Array {
- var ar = new haxe.io.Int32Array(len);
- for (i in 0...len) ar[i] = read8();
- return ar;
- }
- public function read16array(len: Int): haxe.io.Int32Array {
- var ar = new haxe.io.Int32Array(len);
- for (i in 0...len) ar[i] = read16();
- return ar;
- }
- public function read32array(len: Int): haxe.io.Int32Array {
- var ar = new haxe.io.Int32Array(len);
- for (i in 0...len) ar[i] = read32();
- return ar;
- }
- public function readf32array(len: Int): kha.arrays.Float32Array {
- var ar = new kha.arrays.Float32Array(len);
- for (i in 0...len) ar[i] = readf32();
- return ar;
- }
- public function readString(): String {
- var s = "";
- while (true) {
- var ch = read8();
- if (ch == 0) break;
- s += String.fromCharCode(ch);
- }
- return s;
- }
- public function readChars(len: Int): String {
- var s = "";
- for (i in 0...len) s += readChar();
- return s;
- }
- public function readChar(): String {
- return String.fromCharCode(read8());
- }
- public function readPointer(): haxe.Int64 {
- return pointerSize == 4 ? haxe.Int64.ofInt(read32()) : read64();
- }
- }
- class Block {
- public var blend: BlendParser;
- public var code: String;
- public var size: Int;
- public var sdnaIndex: Int;
- public var count: Int;
- public var pos: Int; // Byte pos of data start in blob
- public function new() {}
- }
- class Dna {
- public var names: Array<String> = [];
- public var types: Array<String> = [];
- public var typesLength: Array<Int> = [];
- public var structs: Array<DnaStruct> = [];
- public function new() {}
- }
- class DnaStruct {
- public var dna: Dna;
- public var type: Int; // Index in dna.types
- public var fieldTypes: Array<Int>; // Index in dna.types
- public var fieldNames: Array<Int>; // Index in dna.names
- public function new() {}
- }
- class Handle {
- public var block: Block;
- public var offset: Int = 0; // Block data bytes offset
- public var ds: DnaStruct;
- public function new() {}
- function getSize(index: Int): Int {
- var nameIndex = ds.fieldNames[index];
- var typeIndex = ds.fieldTypes[index];
- var dna = ds.dna;
- var n = dna.names[nameIndex];
- var size = 0;
- if (n.indexOf("*") >= 0) size = block.blend.pointerSize;
- else size = dna.typesLength[typeIndex];
- if (n.indexOf("[") > 0) size *= getArrayLen(n);
- return size;
- }
- function baseName(s: String): String {
- while (s.charAt(0) == "*") s = s.substring(1, s.length);
- if (s.charAt(s.length - 1) == "]") s = s.substring(0, s.indexOf("["));
- return s;
- }
- function getArrayLen(s: String): Int {
- return Std.parseInt(s.substring(s.indexOf("[") + 1, s.indexOf("]")));
- }
- public function get(name: String, index = 0, asType: String = null, arrayLen = 0): Dynamic {
- // Return raw type or structure
- var dna = ds.dna;
- for (i in 0...ds.fieldNames.length) {
- var nameIndex = ds.fieldNames[i];
- var dnaName = dna.names[nameIndex];
- if (name == baseName(dnaName)) {
- var typeIndex = ds.fieldTypes[i];
- var type = dna.types[typeIndex];
- var newOffset = offset;
- for (j in 0...i) newOffset += getSize(j);
- // Cast void* to type
- if (asType != null) {
- for (i in 0...dna.types.length) {
- if (dna.types[i] == asType) {
- typeIndex = i;
- break;
- }
- }
- }
- // Raw type
- if (typeIndex < 12) {
- var blend = block.blend;
- blend.pos = block.pos + newOffset;
- var isArray = dnaName.charAt(dnaName.length - 1) == "]";
- var len = isArray ? (arrayLen > 0 ? arrayLen : getArrayLen(dnaName)) : 1;
- switch (type) {
- case "int": return isArray ? blend.read32array(len) : blend.read32();
- case "char": return isArray ? blend.readString() : blend.read8();
- case "uchar": return isArray ? blend.read8array(len) : blend.read8();
- case "short": return isArray ? blend.read16array(len) : blend.read16();
- case "ushort": return isArray ? blend.read16array(len) : blend.read16();
- case "float": return isArray ? blend.readf32array(len) : blend.readf32();
- case "double": return 0; //blend.readf64();
- case "long": return isArray ? blend.read32array(len) : blend.read32();
- case "ulong": return isArray ? blend.read32array(len) : blend.read32();
- case "int64_t": return blend.read64();
- case "uint64_t": return blend.read64();
- case "void": return 0;
- }
- }
- // Structure
- var h = new Handle();
- h.ds = BlendParser.getStruct(dna, typeIndex);
- var isPointer = dnaName.charAt(0) == "*";
- if (isPointer) {
- block.blend.pos = block.pos + newOffset;
- var addr = block.blend.readPointer();
- if (block.blend.map.exists(addr.high)) {
- h.block = block.blend.map.get(addr.high).get(addr.low);
- }
- else h.block = block;
- h.offset = 0;
- }
- else {
- h.block = block;
- h.offset = newOffset;
- }
- h.offset += dna.typesLength[typeIndex] * index;
- return h;
- }
- }
- return null;
- }
- }
|