const fs = require("fs"); const path = require("path"); const child_process = require("child_process"); var globalShaders = {}; var unverifiedShaders = []; var invalidShaders = []; var verifiedShaders = []; const attributeLayoutRegex = /ATTR_LAYOUT\(\d+\s*,\s*(\d+)\)\s*in\s*uvec4\s*([a-zA-Z0-9_]+)\s*;/; const bufferLayoutRegex = /UNIFORM_BUFFER_LAYOUT\((\d+)\s*,\s*\d+,\s*(\d+)\)(?:\s*uniform\s*([a-zA-Z]+\d)\s*)?/; const splitBufferLayoutRegex = /uniform\s*([a-zA-Z]+\d+)\s*/; const textureLayoutRegex = /TEXTURE_LAYOUT\((\d+), \d+, (\d+)\) uniform sampler2D ([a-zA-Z]+\d);/; const ufBlocklayoutRegex = /layout\(set\s?=\s?\d+,\s?binding\s?=\s?(\d+)\)\s*uniform\s+ufBlock/; const uniformUfRegex = /^\s*uniform\s+([biuvd]?vec[234]|bool|int|uint|float|double)\s+(uf_alphaTestRef|uf_verticesPerInstance|uf_streamoutBufferBase\[\d+\]|uf_tex\[\d+\]Scale|uf_pointSize|uf_fragCoordScale|uf_windowSpaceToClipSpaceTransform|uf_remappedPS\[\d+\]|uf_remappedVS\[\d+\]|uf_uniformRegisterVS\[\d+\]|uf_uniformRegisterPS\[\d+\])\s*;.*/m; function extractShaderInfo(shaderText) { let shaderInfo = {attributeLayouts: [], bufferLayouts: [], textureLayouts: [], ufBlock: {VKLocation: undefined, ufVariables: []}}; let shaderLines = shaderText.split("\n"); let ufBlockFlag = false; for (let line = 0; line\$[a-zA-Z_]+)(?:(?:int))?[ ]*=[ ]*(?\d+\.\d+|\d+|0x[0-9a-fA-F]+|(?[+\-*\/\(\)\d]+))/m); if (presetVariable != null) { if (presetVariable.groups.expression != undefined) currentPreset.variables.push({key: presetVariable.groups.key, value: (presetVariable.groups.isInt != undefined ? 666 : 666.666667), isInt: (presetVariable.groups.isInt != undefined)}); else currentPreset.variables.push({key: presetVariable.groups.key, value: (presetVariable.groups.isInt != undefined ? parseInt(presetVariable.groups.value) : parseFloat(presetVariable.groups.value)), isInt: (presetVariable.groups.isInt != undefined)}); } } } if (currentSection == "Preset") rulesPresets.push({name: currentPreset.name, variables: [...currentPreset.variables]}); for (let i=0; i { return (b.key.length - a.key.length); }); } if (rulesPresets.length == 0) rulesPresets.push({name: "Default", variables: []}); return [rulesPresets, packVersion]; } function verifyShader(rulesPresets, dumpShader, shaderText, vulkanSet, shaderPath) { // First, remove the Vulkan header let shaderLines = shaderText.split("\n"); let parsingVulkanHeader = false; for (line in shaderLines) { if (shaderLines[line].trim().startsWith("#ifdef VULKAN")) parsingVulkanHeader = true; else if (parsingVulkanHeader && (shaderLines[line].trim().startsWith("#define") || shaderLines[line].trim().startsWith("#else"))) shaderLines[line] = "\r"; else if (parsingVulkanHeader && shaderLines[line].trim().startsWith("#endif")) break; //else if (parsingVulkanHeader) console.error("what's this", shaderLines[line]); } // Replace presets for (preset in rulesPresets) { let currPreset = rulesPresets[preset]; let headerlessPresetLines = []; for (line in shaderLines) { let currLine = shaderLines[line]; for (presetVar in currPreset.variables) { let currPresetVar = currPreset.variables[presetVar]; let currPresetVarOccur = currLine.split(currPresetVar.key).length-1; for (let i=0; i= "0000000000000000_0000000000000000_xx.txt".length && /^[a-zA-Z0-9]{16}_[\w]{16}_[p|v]s/.test(packFiles[file].name)) { verifyFiles.shaders.push({name: packFiles[file].name, shaderText: fs.readFileSync(path.join(process.cwd(), ...folderArray, dirEntries[entry].name, packFiles[file].name), {encoding: "utf8"}), vulkanSet: ((packFiles[file].name.substr(34, 2) == "vs") ? 0 : 1)}); } else if (packFiles[file].isDirectory()) { verifyGraphicPacks([...folderArray, dirEntries[entry].name]); break; } } verifyPack(verifyFiles, [...folderArray, dirEntries[entry].name]); console.groupEnd(); } } } function extractDumpInfo(folderPath) { shaderEntries = fs.readdirSync(folderPath, {withFileTypes: true}); for (entry in shaderEntries) { let currShaderEntry = shaderEntries[entry]; if (!currShaderEntry.isFile() || path.extname(currShaderEntry.name) != ".txt" || path.basename(currShaderEntry.name, ".txt").slice(-2) == "gs") continue; globalShaders[path.basename(currShaderEntry.name, ".txt")] = extractShaderInfo(fs.readFileSync(folderPath+"/"+currShaderEntry.name, {encoding: "utf8"})); } } extractDumpInfo("./dump/shaders/"); console.info("Finished gathering information from shader dump."); verifyGraphicPacks(["graphicPacks", "Enhancements"]); verifyGraphicPacks(["graphicPacks", "Resolutions"]); verifyGraphicPacks(["graphicPacks", "Workarounds"]); verifyGraphicPacks(["graphicPacks", "Mods"]); console.info("Finished verifying the graphic packs!"); console.info(""); console.info("Verified shaders:"); console.info(verifiedShaders.join("\r\n")); console.info("Invalid shaders:"); console.info(invalidShaders.join("\r\n")); console.info("Unverified shaders:"); console.info(unverifiedShaders.join("\r\n")); if (fs.existsSync("./manual/")) fs.rmdirSync("./manual/", {recursive: true}); fs.mkdirSync("./manual/", {recursive: true}); for (file in invalidShaders) { fs.mkdirSync("./manual/"+path.basename(invalidShaders[file], ".txt")+"/", {recursive: true}); fs.copyFileSync("./dump/shaders/"+path.basename(invalidShaders[file]), "./manual/"+path.basename(invalidShaders[file], ".txt")+"/"+path.basename(invalidShaders[file], ".txt")+".dump"); fs.copyFileSync(invalidShaders[file], "./manual/"+path.basename(invalidShaders[file], ".txt")+"/"+path.basename(invalidShaders[file])); }