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 }