1 /// A program to simplify the process of translating UEFI headers from C -> D
2 /// Usage: efihdrtool InFile OutFile
3 module efihdrtool;
4 
5 import std.stdio, std.string, std.array, std.algorithm, std.range, std.math,
6     std.format, std.conv;
7 import std.path, std.process;
8 
9 File fpIn;
10 File fpOut;
11 char[] cLine, nLine;
12 string moduleName = "0";
13 string inFile;
14 bool moduleDone = false;
15 
16 void main(string[] args)
17 {
18     if (args.length < 3)
19     {
20         stderr.writefln("Usage: %s InFile.h [+] OutFile.d", args[0]);
21         return;
22     }
23 	fpOut = File(args[$-1], "w");
24 	if (args[$-1].startsWith("source/"))
25 		moduleName = args[$-1]["source/uefi/".length .. $ - 2].replace("/", ".");
26 	foreach(iff; args[1..$-1])
27 	{
28 		stderr.writeln("Processing ",iff);
29 		inFile = iff[iff.indexOf("Include/") + 8 .. $];
30 		fpIn = File(iff, "r");
31 		nextLine();
32 		while (!fpIn.eof())
33 		{
34 			nextLine();
35 			process();
36 		}
37 		fpIn.close();
38 	}
39     fpOut.flush();
40     fpOut.close();
41     writeln(execute(["dfix", args[$-1]]));
42     writeln(execute(["dfmt", args[$-1], "-i"]));
43 }
44 
45 void nextLine()
46 {
47 	cLine = nLine;
48 	nLine = [];
49     fpIn.readln(nLine);
50     nLine = nLine.strip;
51 }
52 
53 bool startsWith(const(char[]) a, const(char[]) b)
54 {
55     size_t bl = b.length;
56     if (b[$ - 1] == '\0')
57         bl--;
58     if (a.length < bl)
59         return false;
60     for (int i = 0; i < bl; i++)
61     {
62         if (a[i] != b[i])
63             return false;
64     }
65     return true;
66 }
67 
68 bool endsWith(const(char[]) a, const(char[]) b)
69 {
70     size_t bl = b.length;
71     if (b[$ - 1] == '\0')
72         bl--;
73     if (a.length < bl)
74         return false;
75     for (int i = 0; i < bl; i++)
76     {
77         if (a[$ - bl + i] != b[i])
78             return false;
79     }
80     return true;
81 }
82 
83 __gshared int pack = 0;
84 
85 string chompx(string s, const(dchar)[] chs)
86 {
87 	foreach(ch; chs)
88 	{
89 		s = s.chomp([ch]).chompPrefix([ch]);
90 	}
91 	return s;
92 }
93 
94 void process()
95 {
96     if (cLine.length < 1)
97     {
98         //fpOut.writeln();
99         return;
100     }
101     if (cLine.startsWith("#pragma"))
102     {
103         if (cLine.indexOf("pack") > 0)
104         {
105             if (cLine.endsWith(`()`))
106             {
107                 pack = 0;
108             }
109             else
110             {
111                 pack = 1;
112             }
113         }
114     }
115     if (cLine == "///")
116         return;
117     if (cLine.startsWith("///"))
118     {
119         fpOut.writeln(cLine);
120     }
121     if (cLine == "/** @file" && !moduleDone)
122     {
123         fpOut.writeln("/**");
124         fpOut.writefln("\tBased on %s, original notice:\n", inFile);
125         while (true)
126         {
127             nextLine();
128             if (cLine != "**/")
129             {
130                 fpOut.writeln("\t", cLine.replace("<BR>", ""));
131             }
132             else
133             {
134                 break;
135             }
136         }
137         fpOut.writeln("**/");
138         fpOut.writefln("module uefi.%s;", moduleName);
139         fpOut.writeln("import uefi.base;");
140         fpOut.writeln("import uefi.base_type;");
141         //fpOut.writeln("import std.bitmanip;");
142         fpOut.writeln("public:");
143         fpOut.writeln("extern (C):");
144 		moduleDone = true;
145         return;
146     }
147     if (cLine == "/**")
148     {
149         fpOut.writeln("/**");
150         while (true)
151         {
152             nextLine();
153             if (!cLine.endsWith("**/"))
154             {
155                 fpOut.writeln("\t", cLine);
156             }
157             else
158             {
159                 break;
160             }
161         }
162         fpOut.writeln("**/");
163         return;
164     }
165     if (cLine.startsWith("#ifndef __"))
166     {
167         nextLine(); // skip header safeguard
168         return;
169     }
170     if (cLine.startsWith("#include "))
171     {
172         fpOut.writeln("// FIXME: INCLUDE ", cLine[9 .. $]);
173         stderr.writeln("// FIXME: INCLUDE ", cLine[9 .. $]);
174         return;
175     }
176     if (cLine.startsWith("#define"))
177     {
178         if (cLine.endsWith("_H_"))
179         {
180             return;
181         }
182         if (cLine.endsWith("\\") && nLine.startsWith("{"))
183         {
184             string Id = cLine[8 .. $ - 2].idup;
185             nextLine();
186 			if(!cLine.canFind(','))
187             	nextLine();
188             string Guid = cLine[0 .. $].idup;
189             nextLine();
190             fpOut.writeln("enum EFI_GUID ", Id, " = EFI_GUID(",
191                 Guid.chompx("\\;[]{}").replace("{", "[").replace("}", "]"), ");");
192         }
193         else
194         {
195             // #define abc def
196             auto Split = cLine.replace("ULL", "UL").split;
197             if (Split[1].indexOf("(") > 0)
198             {
199                 fpOut.writeln("// ", cLine);
200                 while (cLine[$ - 1] == '\\')
201                 {
202                     nextLine();
203                     fpOut.writeln("// ", cLine);
204                 }
205             }
206             else
207             {
208                 fpOut.writef("enum %s = ", Split[1]); //Split[2..$].join(" ")
209                 bool hadSemicolon = false;
210                 foreach (txt; Split[2 .. $])
211                 {
212                     if (txt.startsWith("//") || txt.startsWith("/*"))
213                     {
214                         hadSemicolon = true;
215                         fpOut.write("; ");
216                     }
217                     if (txt == "{")
218                     {
219                         fpOut.write(txt, "GUID( ");
220                     }
221                     else if (txt == "}")
222                     {
223                         fpOut.write(txt, " );");
224                     }
225                     else
226                     {
227                         fpOut.write(txt, ' ');
228                     }
229                 }
230                 fpOut.write((hadSemicolon) ? "\n" : ";\n");
231             }
232         }
233         return;
234     }
235     if (cLine.startsWith("typedef "))
236     {
237         cLine = cLine[8 .. $];
238         if (cLine.startsWith("PACKED"))
239         {
240             cLine = cLine[7 .. $];
241         }
242         if (cLine.startsWith("struct _") && (cLine[$ - 1] != '{'))
243             cLine = cLine[7 .. $];
244         if (cLine.startsWith("union _") && (cLine[$ - 1] != '{'))
245             cLine = cLine[6 .. $];
246         if (cLine.startsWith("struct"))
247         {
248             auto app = appender!string;
249             nextLine();
250             handleStructInterior(app);
251             string Id;
252             if (cLine.length < 3)
253             {
254                 nextLine();
255                 Id = cLine[0 .. $ - 1].idup;
256             }
257             else
258             {
259                 Id = cLine[2 .. $ - 1].idup;
260             }
261             fpOut.writeln("struct ", Id, pack ? " {align(1):\n" : " {\n", app.data,
262                 "}");
263         }
264         else if (cLine.startsWith("enum"))
265         {
266             auto app = appender!string;
267             nextLine();
268             while (!cLine.startsWith("}"))
269             {
270                 app ~= cLine.idup;
271                 app ~= '\n';
272                 nextLine();
273             }
274             string Id;
275             if (cLine.length < 3)
276             {
277                 nextLine();
278                 Id = cLine[0 .. $ - 1].idup;
279             }
280             else
281             {
282                 Id = cLine[2 .. $ - 1].idup;
283             }
284             fpOut.writeln("alias ", Id, " = UINT32;");
285             fpOut.writeln("enum :", Id, " {\n", app.data, "}");
286         }
287         else if (cLine.startsWith("union"))
288         {
289             auto app = appender!string;
290             nextLine();
291             handleStructInterior(app);
292             string Id;
293             if (cLine.length < 3)
294             {
295                 nextLine();
296                 Id = cLine[0 .. $ - 1].idup;
297             }
298             else
299             {
300                 Id = cLine[2 .. $ - 1].idup;
301             }
302             fpOut.writeln("union ", Id, " {\n", app.data, "}");
303         }
304         else
305         {
306             auto Split = cLine[0 .. $ - 1].split;
307             fpOut.writefln("alias %s = %s;", Split[$ - 1], Split[0 .. $ - 1].join(" "));
308         }
309         return;
310     }
311     if (cLine == "typedef") // function pointer
312     {
313         nextLine();
314         string RetType = cLine.idup;
315         nextLine();
316         // (EFIAPI *EFI_TEXT_CLEAR_SCREEN)(
317         string FunName = cLine[9 .. $ - 2].idup;
318         nextLine();
319         auto Args = appender!string;
320         while (cLine != ");")
321         {
322             auto Kw = cLine.split;
323             foreach (K; Kw)
324             {
325                 if ((K == "IN") || (K == "OUT") || (K == "EFIAPI") || (K == "OPTIONAL"))
326                     continue;
327                 if ((K == "IN,") || (K == "OUT,") || (K == "EFIAPI,") || (K == "OPTIONAL,"))
328                 {
329                     Args ~= ',';
330                     continue;
331                 }
332                 if (K == "CONST")
333                     K = "const".dup;
334                 if (K == "VOID")
335                     K = "void".dup;
336                 Args ~= K;
337                 Args ~= ' ';
338             }
339             nextLine();
340         }
341         fpOut.writefln("alias %s = %s function(%s) @nogc nothrow;", FunName, RetType,
342             Args.data);
343     }
344     if (cLine.startsWith("struct"))
345     {
346         auto app = appender!string;
347         handleStructInterior(app);
348         fpOut.writeln(app.data);
349         fpOut.writeln("}");
350         return;
351     }
352 }
353 
354 void handleStructInterior(T)(T app)
355 {
356     int numNests = 1;
357     bool inBitfields = false;
358     while (true)
359     {
360         if (cLine.length < 1)
361         {
362             nextLine();
363             continue;
364         }
365 		if(cLine.startsWith("//"))
366 		{
367 			app ~= cLine.idup;
368 			app ~= '\n';
369 			nextLine();
370 			continue;
371 		}
372         if (cLine.startsWith("PACKED "))
373         {
374             cLine = cLine[7 .. $];
375         }
376         if (cLine.startsWith("union {") || cLine.startsWith("struct {"))
377         {
378             app ~= `/* FIX:NESTED STRUCTURES */`;
379             numNests++;
380         }
381         if (cLine.startsWith("}"))
382         {
383             numNests--;
384         }
385         if (numNests <= 0)
386         {
387             break;
388         }
389 
390         if (cLine.indexOf(":") > 0)
391         {
392             import std.regex;
393 			cLine = cLine.strip;
394             auto spl = splitter(cLine, regex(`[ \t:;]+`)).array;
395             if (!inBitfields)
396             {
397                 inBitfields = true;
398                 app ~= "mixin(bitfields!(\n";
399             }
400             else
401             {
402                 app ~= ",\n";
403             }
404             app ~= spl[0].idup; // type
405             app ~= `,"`;
406             app ~= spl[1].idup; // fname
407             app ~= `",`;
408             app ~= spl[2].idup; // width
409         }
410         else
411         {
412             if (inBitfields)
413             {
414                 inBitfields = false;
415                 app ~= "));";
416             }
417             app ~= cLine.idup;
418             app ~= '\n';
419         }
420         nextLine();
421     }
422 	if (inBitfields)
423 	{
424 		inBitfields = false;
425 		app ~= "));";
426 	}
427 }