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 }