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 }