1 /** 2 INP Format 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; 14 15 import nulib.io.stream; 16 import numem.optional; 17 18 public import inp.format.inp1; 19 public import inp.format.inp2; 20 public import inp.format.node; 21 22 /** 23 The different INP file formats. 24 */ 25 enum INPFileFormat : uint { 26 unknown = 0x00, 27 inp1 = 0x01, 28 inp2 = 0x02 29 } 30 31 /** 32 Tag for the INP payload section. 33 */ 34 enum char[8] INP_TAG_PAYLOAD = "INP_SECT"; 35 36 /** 37 Tag for the INP texture section. 38 */ 39 enum char[8] INP_TAG_TEXTURES = "TEX_SECT"; 40 41 /** 42 Tag for the INP extended vendor data section. 43 */ 44 enum char[8] INP_TAG_VENDOR = "EXT_SECT"; 45 46 /** 47 Texture format ID for PNG. 48 */ 49 enum ubyte INP_TEX_FMT_PNG = 0; 50 51 /** 52 Texture format ID for TGA. 53 */ 54 enum ubyte INP_TEX_FMT_TGA = 1; 55 56 /** 57 Texture format ID for BC7. 58 */ 59 enum ubyte INP_TEX_FMT_BC7 = 2; 60 61 /** 62 Determins the INP file format stored within the given stream. 63 64 Params: 65 stream = The stream to detect the INP file format for. 66 67 Returns: 68 The INP version of the file. 69 */ 70 INPFileFormat detectFormat(Stream stream) @nogc nothrow { 71 assert(stream.canRead(), "Stream is not readable!"); 72 assert(stream.canSeek(), "Stream is not seekable!"); 73 74 size_t start = stream.tell(); 75 ubyte[8] magic; 76 77 // Try reading magic bytes, length must be 8. 78 if (stream.read(magic) != 8) { 79 stream.seek(start); 80 return INPFileFormat.unknown; 81 } 82 83 stream.seek(start); 84 switch(cast(char[8])magic) { 85 default: return INPFileFormat.unknown; 86 case INP1_MAGIC: return INPFileFormat.inp1; 87 case INP2_MAGIC: return INPFileFormat.inp2; 88 } 89 } 90 91 /** 92 Reads an INP File from a stream. 93 94 Params: 95 stream = The stream to read from, must be readable and seekable. 96 97 Returns: 98 A result that contains a $(D DataNode) on success, 99 otherwise contains an error. 100 */ 101 Result!DataNode readINP(Stream stream) @nogc { 102 final switch(detectFormat(stream)) { 103 case INPFileFormat.unknown: 104 return error!DataNode("Unknown file format!"); 105 106 case INPFileFormat.inp1: 107 return readINP1(stream); 108 109 case INPFileFormat.inp2: 110 return readINP2(stream); 111 } 112 } 113 114 /** 115 Writes the given $(D DataNode) to the given $(D Stream). 116 117 Params: 118 stream = The stream to write to, must be writable. 119 node = The node containing INP data. 120 format = The file format to write, INP2 is recommended. 121 */ 122 void writeINP(Stream stream, ref DataNode node, INPFileFormat format = INPFileFormat.inp2) @nogc { 123 final switch(format) { 124 case INPFileFormat.unknown: 125 return; 126 127 case INPFileFormat.inp1: 128 stream.writeINP1(node); 129 return; 130 131 case INPFileFormat.inp2: 132 stream.writeINP2(node); 133 return; 134 } 135 }