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