1 /// Internal parsing tools 2 module nwnlibd.parseutils; 3 4 import std.traits; 5 import std.conv; 6 7 /// Converts a static char array to string. 8 /// The static char array may or may not be null terminated 9 pure @safe 10 auto ref string charArrayToString(T)(in T str) if(isArray!T && isSomeChar!(ForeachType!T)){ 11 import std.string: indexOf; 12 auto i = str.indexOf('\0'); 13 if(i >= 0) 14 return str[0 .. i].idup(); 15 return str[0 .. $].idup(); 16 } 17 18 /// Converts a string to a static char array. 19 /// The static char array may or may not be null terminated 20 pure @safe 21 T stringToCharArray(T)(in string str) if(isStaticArray!T && isSomeChar!(ForeachType!T)){ 22 T ret; 23 24 import std.algorithm: min; 25 auto e = min(str.length, T.length); 26 ret[0 .. e] = str[0 .. e]; 27 28 if(e < T.length) 29 ret[e .. $] = 0; 30 31 return ret; 32 } 33 34 // Escapes non printable characters from a string 35 string toSafeString(T)(in T str){ 36 import std.ascii; 37 import std.format; 38 string ret; 39 foreach(c ; str){ 40 if(c.isPrintable) 41 ret ~= c; 42 else 43 ret ~= format!"\\x%02x"(c); 44 } 45 return ret; 46 } 47 48 49 50 /// Formats a binary array to something readable 51 string dumpByteArray(in ubyte[] byteArray){ 52 import std.string: rightJustify; 53 import std.conv: to; 54 string ret; 55 foreach(i ; 0..20){ 56 if(i==0)ret ~= " / "; 57 ret ~= i.to!string.rightJustify(4, '_'); 58 } 59 ret ~= "\n"; 60 foreach(i ; 0..byteArray.length){ 61 auto ptr = byteArray.ptr + i; 62 if(i%20==0)ret ~= (i/10).to!string.rightJustify(3)~" > "; 63 ret ~= (*cast(ubyte*)ptr).to!string.rightJustify(4); 64 if(i%20==19)ret ~= "\n"; 65 } 66 ret ~= "\n"; 67 return ret; 68 } 69 70 /// Read a $(D ubyte[]) by chunks 71 struct ChunkReader{ 72 const ubyte[] data; 73 size_t read_ptr = 0; 74 75 @property size_t bytesLeft() const { 76 return data.length - read_ptr; 77 } 78 79 ref T read(T)(){ 80 read_ptr += T.sizeof; 81 return *cast(T*)(data.ptr+read_ptr-T.sizeof); 82 } 83 84 const(T[]) readArray(T=ubyte)(size_t length){ 85 read_ptr += length*T.sizeof; 86 return cast(const(T[]))(data[read_ptr-length*T.sizeof .. read_ptr]); 87 } 88 89 T readPackedStruct(T)(){ 90 T ret; 91 foreach(MEMBER ; FieldNameTuple!T) 92 mixin("ret."~MEMBER~" = read!(typeof(ret."~MEMBER~"));"); 93 return ret; 94 } 95 96 const(T[]) peek(T=ubyte)(size_t length = 1){ 97 return cast(const(T[]))(data[read_ptr .. read_ptr + length * T.sizeof]); 98 } 99 } 100 101 102 struct ChunkWriter{ 103 ubyte[] data; 104 105 void put(T...)(in T chunks){ 106 size_t i = data.length; 107 108 size_t addLen = 0; 109 static foreach(chunk ; chunks){ 110 static if(isArray!(typeof(chunk))) 111 addLen += chunk.length * chunk[0].sizeof; 112 else 113 addLen += chunk.sizeof; 114 } 115 data.length += addLen; 116 117 foreach(chunk ; chunks){ 118 static if(isArray!(typeof(chunk))){ 119 const l = (cast(ubyte[])chunk).length; 120 data[i .. i + l] = cast(ubyte[])chunk; 121 } 122 else{ 123 const l = chunk.sizeof; 124 data[i .. i + l] = (cast(ubyte*)&chunk)[0..l]; 125 } 126 i += l; 127 } 128 129 assert(data.length == i); 130 } 131 } 132 133 134 135 template DebugPrintStruct(){ 136 string toString() const { 137 string ret = Unqual!(typeof(this)).stringof ~ "("; 138 import std.traits; 139 bool first = true; 140 foreach(M ; FieldNameTuple!(typeof(this))){ 141 ret ~= (first? null : ", ") ~ M ~ "="; 142 alias T = typeof(mixin("this." ~ M)); 143 static if(isArray!T && isSomeChar!(ForeachType!T)) 144 ret ~= '"' ~ mixin("this." ~ M).charArrayToString.toSafeString ~ '"'; 145 else 146 ret ~= mixin("this." ~ M).to!string; 147 first = false; 148 } 149 return ret ~ ")"; 150 } 151 } 152 153 /// Prints an integer as being a combination of bit flags 154 string flagsToString(ENUM, VAL)(in VAL value) if(is(ENUM == enum) && isIntegral!VAL) { 155 import std.string: format; 156 string ret; 157 VAL accu = 0; 158 foreach(FLAG ; EnumMembers!ENUM){ 159 if(value && FLAG == FLAG){ 160 ret ~= (ret is null ? null : "|") ~ FLAG.to!string; 161 accu |= FLAG; 162 } 163 } 164 if(accu != value){ 165 ret ~= (ret is null ? null : "|") ~ format!"0b%b"(accu ^ value); 166 } 167 if(ret is null) 168 ret = "None"; 169 return ret; 170 }