1 /** 2 INP 1 Format Reader 3 4 Copyright: 5 Copyright © 2020-2026, Inochi2D Project 6 7 License: 8 $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License) 9 10 Authors: 11 Luna Nielsen 12 */ 13 module inp.format.inp1.reader; 14 import inp.format.json.reader; 15 import inp.format.node; 16 import inp.format.inp1; 17 import nulib.io.stream.rw; 18 import nulib.io.stream; 19 import nulib.math; 20 import numem; 21 22 import inp.format : 23 INP_TAG_PAYLOAD, 24 INP_TAG_TEXTURES, 25 INP_TAG_VENDOR; 26 27 @nogc: 28 29 /** 30 Reads an INP1 File. 31 32 Params: 33 stream = The stream to read. 34 35 Returns: 36 A result type containing either a $(D DataNode) 37 or an error message. 38 */ 39 Result!DataNode readINP1(Stream stream) @nogc { 40 scope StreamReader reader = new StreamReader(stream); 41 DataNode result; 42 43 size_t start = stream.tell(); 44 reader.readINP1Impl(result); 45 stream.seek(start); 46 47 return ok(result.move()); 48 } 49 50 51 52 // 53 // IMPLEMENTATION DETAILS 54 // 55 private: 56 57 void readINP1Impl()(StreamReader reader, ref DataNode node) { 58 node = DataNode.createObject(); 59 node[INP_TAG_PAYLOAD] = DataNode.createObject(); 60 node[INP_TAG_TEXTURES] = DataNode.createArray(); 61 node[INP_TAG_VENDOR] = DataNode.createObject(); 62 63 ptrdiff_t streamLength = reader.stream.length; 64 readLoop: while(reader.stream.tell < streamLength) { 65 auto key = reader.readUTF8(8); 66 switch(key) { 67 default: 68 break readLoop; 69 70 case INP1_MAGIC: 71 ptrdiff_t rstart = reader.stream.tell; 72 uint payloadLength = reader.readU32BE(); 73 auto result = reader.readJson(node[INP_TAG_PAYLOAD], payloadLength); 74 break; 75 76 case INP_TAG_TEXTURES: 77 uint count = reader.readU32BE(); 78 foreach(i; 0..count) { 79 80 // Main data 81 uint dataLength = reader.readU32BE(); 82 ubyte encoding = reader.readU8(); 83 84 // NOTE: Inochi2D 0.8.x sometimes could end up writing textures 85 // with 0 length into the file, to allow the loader to handle 86 // this we replace it with an undefined DataNode if empty. 87 if (dataLength > 0) { 88 DataNode result = DataNode.createObject(); 89 ubyte[] data = nu_malloca!ubyte(dataLength); 90 reader.stream.read(data); 91 92 result["encoding"] = encoding; 93 result["data"] = data; 94 node[INP_TAG_TEXTURES] ~= result; 95 96 nu_freea(data); 97 } else { 98 node[INP_TAG_TEXTURES] ~= DataNode.init; 99 } 100 } 101 break; 102 103 case INP_TAG_VENDOR: 104 uint count = reader.readU32BE(); 105 foreach(i; 0..count) { 106 auto keyLength = reader.readU32BE(); 107 auto dataKey = reader.readUTF8(keyLength).take(); 108 auto dataLength = reader.readU32BE(); 109 auto dataValue = nu_malloca!ubyte(dataLength); 110 111 reader.stream.read(dataValue); 112 node[INP_TAG_VENDOR][dataKey] = dataValue; 113 114 nu_freea(dataValue); 115 nu_freea(dataKey); 116 } 117 break; 118 } 119 } 120 }