private bool MergeObjIntoOther(EnumObject src, EnumObject dest) { // If the dest is from C# code, make src point to dest. In effect, // it will give its uses to the C# version if (dest.FromCSharp) { src.name = null; src.members.Clear(); src.addUsesTo = dest.name; return(true); } // Don't mess with an object that was declared to be finished in the json if (src.finished) { return(false); } foreach (var m in src.members) { if (StringComparer.OrdinalIgnoreCase.Equals(m.name, "None")) { continue; } dest.AddIfNotSet(m.name, m.value); if (this.enumMemberNameToEnumObj.TryGetValue(m.name, out var memberOwner)) { if (memberOwner != dest && memberOwner != src) { this.enumMemberNameToEnumObj[m.name] = dest; this.MergeObjIntoOther(memberOwner, dest); } } this.enumMemberNameToEnumObj[m.name] = dest; } foreach (var u in src.uses) { dest.AddUse(u); } return(true); }
private void ScrapeConstantsFromTraversedFiles(Dictionary <string, string> traversedFileMap) { Dictionary <string, string> autoReplacements = GetAutoValueReplacements(); HashSet <string> manualEnumMemberNames = this.GetManualEnumMemberNames(); // For each traversed header, scrape the constants foreach (KeyValuePair <string, string> item in traversedFileMap) { var header = item.Key; var currentNamespace = item.Value; if (!File.Exists(header)) { continue; } string currentHeaderName = Path.GetFileName(header).ToLowerInvariant(); List <EnumObject> autoEnumObjsForCurrentHeader = new List <EnumObject>(this.enumObjectsFromJsons.Where(e => e.autoPopulate != null && e.autoPopulate.header.ToLowerInvariant() == currentHeaderName)); Regex autoPopulateReg = null; if (autoEnumObjsForCurrentHeader.Count != 0) { StringBuilder autoPopulateRegexPattern = new StringBuilder(); foreach (EnumObject autoEnumObj in autoEnumObjsForCurrentHeader) { if (autoPopulateRegexPattern.Length != 0) { autoPopulateRegexPattern.Append('|'); } autoPopulateRegexPattern.Append($"(^{autoEnumObj.autoPopulate.filter})"); } autoPopulateReg = new Regex(autoPopulateRegexPattern.ToString()); } string continuation = null; bool processingGuidMultiLine = false; string defineGuidKeyword = null; foreach (string currentLine in File.ReadAllLines(header)) { string fixedCurrentLine = currentLine; if (continuation != null && continuation.EndsWith('"') && currentLine.StartsWith('"')) { continuation = continuation.Substring(0, continuation.Length - 1); fixedCurrentLine = currentLine.Substring(1); } string line = continuation == null ? fixedCurrentLine : continuation + fixedCurrentLine; if (line.EndsWith("\\")) { continuation = line.Substring(0, line.Length - 1); continue; } if (processingGuidMultiLine) { continuation = StripComments(line).Trim(); if (continuation.EndsWith(';')) { processingGuidMultiLine = false; this.AddConstantGuid(defineGuidKeyword, currentNamespace, continuation); continuation = null; } continue; } continuation = null; Match defineGuidMatch = DefineGuidConstRegex.Match(line); if (defineGuidMatch.Success) { defineGuidKeyword = defineGuidMatch.Groups[1].Value; line = defineGuidMatch.Groups[2].Value; line = StripComments(line).Trim(); if (line.EndsWith(';')) { this.AddConstantGuid(defineGuidKeyword, currentNamespace, line); } else { continuation = line; processingGuidMultiLine = true; } continue; } Match defineAviGuidMatch = DefineAviGuidConstRegex.Match(line); if (defineAviGuidMatch.Success) { defineGuidKeyword = defineAviGuidMatch.Groups[1].Value; var guidName = defineAviGuidMatch.Groups[2].Value; var l = defineAviGuidMatch.Groups[3].Value; var w1 = defineAviGuidMatch.Groups[4].Value; var w2 = defineAviGuidMatch.Groups[5].Value; var defineGuidLine = $"{guidName}, {l}, {w1}, {w2}, 0xC0,0,0,0,0,0,0,0x46)"; this.AddConstantGuid(defineGuidKeyword, currentNamespace, defineGuidLine); continue; } Match defineMediaTypeGuidMatch = DefineMediaTypeGuidConstRegex.Match(line); if (defineMediaTypeGuidMatch.Success) { defineGuidKeyword = defineMediaTypeGuidMatch.Groups[1].Value; var guidName = defineMediaTypeGuidMatch.Groups[2].Value; var value = defineMediaTypeGuidMatch.Groups[3].Value; var fccMatch = FccRegex.Match(value); if (fccMatch.Success) { var fccValue = fccMatch.Groups[1].Value.ToArray(); uint convertedValue = (uint)(fccValue[0]) | (uint)(fccValue[1] << 8) | (uint)(fccValue[2] << 16) | (uint)(fccValue[3] << 24); value = $"0x{convertedValue:x}"; } var defineGuidLine = $"{guidName}, {value}, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71)"; this.AddConstantGuid(defineGuidKeyword, currentNamespace, defineGuidLine); continue; } Match defineMatch = DefineRegex.Match(line); // Skip if not #define ... if (!defineMatch.Success) { this.TryScrapingEnumFlags(line); continue; } string name = defineMatch.Groups[1].Value; if (this.ShouldExclude(name)) { continue; } #if DEBUG if (name == "SOME_CONST_NAME") { } #endif // Get rid of trailing comments string rawValue = StripComments(defineMatch.Groups[2].Value.Trim()); if (autoReplacements.TryGetValue(rawValue, out var updatedRawValue)) { rawValue = updatedRawValue; } string fixedRawValue = rawValue; // Get rid of enclosing parens. Makes it easier to parse with regex if (fixedRawValue.StartsWith('(') && fixedRawValue.EndsWith(')')) { fixedRawValue = fixedRawValue.Substring(1, rawValue.Length - 2).Trim(); } Match ctlCodeMatch = CtlCodeRegex.Match(fixedRawValue); if (ctlCodeMatch.Success) { var parts = ctlCodeMatch.Groups[1].Value.Split(','); if (parts.Length == 4) { this.AddCtlCodeConstant(currentNamespace, name, parts[0].Trim(), parts[1].Trim(), parts[2].Trim(), parts[3].Trim()); continue; } } Match usageMatch = HidUsageRegex.Match(fixedRawValue); if (usageMatch.Success) { this.AddConstantValue(currentNamespace, "ushort", name, usageMatch.Groups[1].Value); continue; } if (fixedRawValue.StartsWith("AUDCLNT_ERR(")) { fixedRawValue = fixedRawValue.Replace("AUDCLNT_ERR(", "MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, "); } else if (fixedRawValue.StartsWith("AUDCLNT_SUCCESS(")) { fixedRawValue = fixedRawValue.Replace("AUDCLNT_SUCCESS(", "MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_AUDCLNT, "); } Match makeHresultMatch = MakeHresultRegex.Match(fixedRawValue); if (makeHresultMatch.Success) { var parts = makeHresultMatch.Groups[1].Value.Split(','); if (parts.Length == 3) { this.AddMakeHresultConstant(currentNamespace, name, parts[0].Trim(), parts[1].Trim(), parts[2].Trim()); continue; } } Match intCastToLpcstrMatch = IntCastToLpcstrRegex.Match(fixedRawValue); if (intCastToLpcstrMatch.Success) { var nativeStrType = intCastToLpcstrMatch.Groups[1].Value; var value = intCastToLpcstrMatch.Groups[2].Value; this.AddConstantInteger(currentNamespace, nativeStrType, name, value); continue; } if (this.regexConstHelper.TryProcessingLine(currentNamespace, name, fixedRawValue)) { continue; } // See if matches one of our well known constants formats Match match = DefineConstantRegex.Match(fixedRawValue); string valueText; string nativeTypeName = null; string matchedConstantType = null; bool matchedToOtherName = false; if (match.Success) { // #define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFF) if (!string.IsNullOrEmpty(match.Groups[2].Value)) { if (match.Groups[2].Value == "_HRESULT_TYPEDEF_") { nativeTypeName = "HRESULT"; } valueText = match.Groups[3].Value; } // #define E_UNEXPECTED ((HRESULT)0x8000FFFF) else if (!string.IsNullOrEmpty(match.Groups[5].Value)) { nativeTypeName = "HRESULT"; valueText = match.Groups[5].Value; } // #define DXGI_RESOURCE_PRIORITY_MINIMUM ( 0x28000000 ) else if (!string.IsNullOrEmpty(match.Groups[7].Value)) { valueText = match.Groups[7].Value; } // 1.0, -2.0f else if (!string.IsNullOrEmpty(match.Groups[6].Value)) { valueText = match.Groups[6].Value; string type = valueText.EndsWith('f') ? "float" : "double"; this.AddConstantValue(currentNamespace, type, name, valueText); continue; } // 1 << 5 else if (!string.IsNullOrEmpty(match.Groups[8].Value)) { string part1 = match.Groups[9].Value + "u"; string part2 = match.Groups[10].Value; valueText = part1 + part2; } // MAKEINTRESOURCE(-4), MAKEINTRESOURCEA(-1), MAKEINTRESOURCEW(42) else if (!string.IsNullOrEmpty(match.Groups[11].Value)) { if (match.Groups[11].Value.StartsWith("MAKEINTRESOURCEA")) { nativeTypeName = "LPCSTR"; } else { nativeTypeName = "LPCWSTR"; } valueText = match.Groups[12].Value; this.AddConstantInteger(currentNamespace, nativeTypeName, name, valueText); continue; } // (HWND)-4 else if (!string.IsNullOrEmpty(match.Groups[13].Value)) { nativeTypeName = "HWND"; valueText = match.Groups[14].Value; this.AddConstantInteger(currentNamespace, nativeTypeName, name, valueText); continue; } // (IDENT_FOO + 4) else if (match.Groups[15].Success) { valueText = match.Groups[15].Value; } // (NTSTATUS)0x00000000L else if (match.Groups[17].Success) { nativeTypeName = "NTSTATUS"; valueText = match.Groups[18].Value; } // (DWORD)-1 else if (match.Groups[20].Success) { valueText = "0xFFFFFFFF"; } // (BCRYPT_ALG_HANDLE) 0x000001a1 else if (match.Groups[21].Success) { nativeTypeName = "BCRYPT_ALG_HANDLE"; valueText = match.Groups[22].Value; } // SOME_OTHER_CONSTANT else if (match.Groups[23].Success) { string otherName = match.Groups[23].Value; matchedToOtherName = true; // Only use a constant as the value if we have seen the constant before // and we know its type if (this.writtenConstants.TryGetValue(otherName, out var otherType)) { // Skip guids for now if (otherType != "Guid") { matchedConstantType = otherType; } } // If we didn't match it to another constant, keep going as we may be setting an enum valueText = otherName; } else { continue; } } else { valueText = rawValue; if (valueText.StartsWith("__TEXT(")) { valueText = valueText.Substring(2); } if (valueText.StartsWith("TEXT(")) { valueText = valueText.Substring("TEXT(".Length); if (valueText.EndsWith(')')) { valueText = valueText.Substring(0, valueText.Length - 1); } } else if (valueText.StartsWith("L\"")) { valueText = valueText.Substring(1); } // Strings can't be part of enums so go ahead and add the constant directly if (valueText.StartsWith('"')) { this.AddConstantValue(currentNamespace, "string", name, valueText); continue; } valueText = valueText.Replace("(DWORD)", "(uint)"); valueText = valueText.Replace("(ULONG)", "(uint)"); } bool updatedEnum = false; // If we see the member is part of an enum, update the member value if (this.enumMemberNameToEnumObj.TryGetValue(name, out var enumObjList)) { foreach (EnumObject enumObj in enumObjList) { enumObj.AddIfNotSet(name, valueText); updatedEnum = true; } } if (autoPopulateReg != null && nativeTypeName == null) { Match autoPopulate = autoPopulateReg.Match(name); if (autoPopulate.Success) { for (int i = 1; i < autoPopulate.Groups.Count; i++) { if (!string.IsNullOrEmpty(autoPopulate.Groups[i].Value)) { EnumObject foundObjEnum = autoEnumObjsForCurrentHeader[i - 1]; foundObjEnum.AddIfNotSet(name, valueText); updatedEnum = true; if (!this.enumMemberNameToEnumObj.TryGetValue(name, out var list)) { list = new List <EnumObject>(); this.enumMemberNameToEnumObj.Add(name, list); } list.Add(foundObjEnum); break; } } } } // If we haven't used the member to update an enum, skip it... // ...unless it's an HRESULT. Always emit them as constants too if (match.Success && (!updatedEnum || nativeTypeName != null)) { // Only add the constant if it's not part of a manual enum if (!manualEnumMemberNames.Contains(name)) { if (matchedConstantType != null) { this.AddConstantValue(currentNamespace, matchedConstantType, name, valueText); } else { // Only add as an int if it didn't match some other constant/enum name if (!matchedToOtherName) { this.AddConstantInteger(currentNamespace, nativeTypeName, name, valueText); } } } } } } }