From ed0a10e4afe4e6ba88e003ae8ff202b3f6c74977 Mon Sep 17 00:00:00 2001 From: "Dr. Hayes" <760637482@qq.com> Date: Thu, 18 Jun 2026 09:22:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=BA=90=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BrialQRify.luau | 656 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 src/BrialQRify.luau diff --git a/src/BrialQRify.luau b/src/BrialQRify.luau new file mode 100644 index 0000000..b6a54f4 --- /dev/null +++ b/src/BrialQRify.luau @@ -0,0 +1,656 @@ +--[[ + + +oooooooooo. o8o oooo .oooooo. ooooooooo. o8o .o88o. +`888' `Y8b `"' `888 d8P' `Y8b `888 `Y88. `"' 888 `" + 888 888 oooo d8b oooo .oooo. 888 888 888 888 .d88' oooo o888oo oooo ooo + 888oooo888' `888""8P `888 `P )88b 888 888 888 888ooo88P' `888 888 `88. .8' + 888 `88b 888 888 .oP"888 888 888 888 888`88b. 888 888 `88..8' + 888 .88P 888 888 d8( 888 888 `88b d88b 888 `88b. 888 888 `888' +o888bood8P' d888b o888o `Y888""8o o888o `Y8bood8P'Ybd' o888o o888o o888o o888o .8' + .o..P' + `Y8P' + +By Hayes@Brial + + +--]] +-- Supports QR Version 1-4, Error Correction L, Byte mode + +local toolbar = plugin:CreateToolbar("BrialQRify") +local button = toolbar:CreateButton("BrialQRify", "Generates a scannable QR code in StarterGui", "") + +local widgetInfo = DockWidgetPluginGuiInfo.new( + Enum.InitialDockState.Float, + true, true, + 420, 500, + 420, 500 +) + +local widget = plugin:CreateDockWidgetPluginGui("BrialQRify", widgetInfo) +widget.Title = "BrialQRify" +widget.Enabled = false + +local mainFrame = Instance.new("Frame") +mainFrame.Size = UDim2.new(1, 0, 1, 0) +mainFrame.BackgroundColor3 = Color3.fromRGB(40, 40, 40) +mainFrame.BorderSizePixel = 0 +mainFrame.Parent = widget + +local title = Instance.new("TextLabel") +title.Size = UDim2.new(1, 0, 0, 40) +title.Position = UDim2.new(0, 0, 0, 0) +title.Text = "BrialQRify" +title.TextColor3 = Color3.fromRGB(255, 255, 255) +title.TextSize = 22 +title.Font = Enum.Font.GothamBold +title.BackgroundTransparency = 1 +title.Parent = mainFrame + +local separator = Instance.new("Frame") +separator.Size = UDim2.new(1, -20, 0, 1) +separator.Position = UDim2.new(0, 10, 0, 40) +separator.BackgroundColor3 = Color3.fromRGB(60, 60, 60) +separator.BorderSizePixel = 0 +separator.Parent = mainFrame + +local function createRow(y, labelText, boxWidth) + local row = Instance.new("Frame") + row.Size = UDim2.new(1, -20, 0, 32) + row.Position = UDim2.new(0, 10, 0, y) + row.BackgroundTransparency = 1 + row.Parent = mainFrame + + local lbl = Instance.new("TextLabel") + lbl.Size = UDim2.new(0, 120, 1, 0) + lbl.Position = UDim2.new(0, 0, 0, 0) + lbl.Text = labelText + lbl.TextColor3 = Color3.fromRGB(200, 200, 200) + lbl.TextSize = 14 + lbl.TextXAlignment = Enum.TextXAlignment.Right + lbl.BackgroundTransparency = 1 + lbl.Parent = row + + local box = Instance.new("TextBox") + box.Size = UDim2.new(0, boxWidth or 240, 1, 0) + box.Position = UDim2.new(0, 130, 0, 0) + box.Text = "" + box.TextColor3 = Color3.fromRGB(255, 255, 255) + box.TextSize = 14 + box.Font = Enum.Font.Gotham + box.BackgroundColor3 = Color3.fromRGB(55, 55, 55) + box.BorderSizePixel = 0 + box.ClearTextOnFocus = false + box.Parent = row + + return box, row +end + +local contentBox, contentRow = createRow(52, "Content:", 240) +contentBox.Text = "https://brial.cn/" + +local fgBox, fgRow = createRow(94, "Foreground:", 120) +fgBox.Text = "0,0,0" + +local bgBox, bgRow = createRow(136, "Background:", 120) +bgBox.Text = "255,255,255" + +local pixelBox, pixelRow = createRow(178, "Pixel Size:", 80) +pixelBox.Text = "10" + +local genBtn = Instance.new("TextButton") +genBtn.Size = UDim2.new(0, 200, 0, 38) +genBtn.Position = UDim2.new(0, 130, 0, 226) +genBtn.Text = "Generate QR Code" +genBtn.TextColor3 = Color3.fromRGB(255, 255, 255) +genBtn.TextSize = 16 +genBtn.Font = Enum.Font.GothamBold +genBtn.BackgroundColor3 = Color3.fromRGB(0, 120, 215) +genBtn.BorderSizePixel = 0 +genBtn.Parent = mainFrame + +local previewLabel = Instance.new("TextLabel") +previewLabel.Size = UDim2.new(1, -20, 0, 20) +previewLabel.Position = UDim2.new(0, 10, 0, 280) +previewLabel.Text = "Preview" +previewLabel.TextColor3 = Color3.fromRGB(150, 150, 150) +previewLabel.TextSize = 12 +previewLabel.TextXAlignment = Enum.TextXAlignment.Left +previewLabel.BackgroundTransparency = 1 +previewLabel.Parent = mainFrame + +local previewFrame = Instance.new("Frame") +previewFrame.Size = UDim2.new(1, -20, 0, 160) +previewFrame.Position = UDim2.new(0, 10, 0, 304) +previewFrame.BackgroundColor3 = Color3.fromRGB(220, 220, 220) +previewFrame.BorderSizePixel = 1 +previewFrame.BorderColor3 = Color3.fromRGB(80, 80, 80) +previewFrame.ClipsDescendants = true +previewFrame.Parent = mainFrame + +local previewPlaceholder = Instance.new("TextLabel") +previewPlaceholder.Size = UDim2.new(1, 0, 1, 0) +previewPlaceholder.Text = "QR code preview" +previewPlaceholder.TextColor3 = Color3.fromRGB(120, 120, 120) +previewPlaceholder.TextSize = 14 +previewPlaceholder.BackgroundTransparency = 1 +previewPlaceholder.Parent = previewFrame + +local statusLabel = Instance.new("TextLabel") +statusLabel.Size = UDim2.new(1, -20, 0, 28) +statusLabel.Position = UDim2.new(0, 10, 1, -32) +statusLabel.Text = "Ready" +statusLabel.TextColor3 = Color3.fromRGB(150, 255, 150) +statusLabel.TextSize = 13 +statusLabel.TextXAlignment = Enum.TextXAlignment.Left +statusLabel.BackgroundTransparency = 1 +statusLabel.Parent = mainFrame + +local QR = {} + +local VERSION_INFO = { + [1] = {size = 21, data = 19, ec = 7, capacity = 17, align = nil}, + [2] = {size = 25, data = 34, ec = 10, capacity = 32, align = {7, 19}}, + [3] = {size = 29, data = 55, ec = 15, capacity = 53, align = {7, 23}}, + [4] = {size = 33, data = 80, ec = 20, capacity = 78, align = {7, 27}}, +} + +local gfExp = {} +local gfLog = {} + +do + local x = 1 + for i = 0, 254 do + gfExp[i] = x + gfLog[x] = i + x = bit32.lshift(x, 1) + if x >= 256 then + x = bit32.bxor(x, 0x11D) + end + end + for i = 255, 511 do + gfExp[i] = gfExp[i - 255] + end +end + +local function gfMul(a, b) + if a == 0 or b == 0 then + return 0 + end + return gfExp[gfLog[a] + gfLog[b]] +end + +local function getBit(num, i) + return bit32.band(bit32.rshift(num, i), 1) ~= 0 +end + +local function chooseVersion(byteCount) + for v = 1, 4 do + if byteCount <= VERSION_INFO[v].capacity then + return v + end + end + return nil +end + +local function makeBytes(str) + local t = {} + for i = 1, #str do + t[#t + 1] = string.byte(str, i) + end + return t +end + +local function appendBits(bits, value, count) + for i = count - 1, 0, -1 do + bits[#bits + 1] = getBit(value, i) and 1 or 0 + end +end + +local function makeDataCodewords(bytes, version) + local info = VERSION_INFO[version] + local bits = {} + + appendBits(bits, 0x4, 4) + appendBits(bits, #bytes, 8) + + for _, b in ipairs(bytes) do + appendBits(bits, b, 8) + end + + local maxBits = info.data * 8 + local terminator = math.min(4, maxBits - #bits) + appendBits(bits, 0, terminator) + + while #bits % 8 ~= 0 do + bits[#bits + 1] = 0 + end + + local data = {} + for i = 1, #bits, 8 do + local v = 0 + for j = 0, 7 do + v = bit32.bor(bit32.lshift(v, 1), bits[i + j]) + end + data[#data + 1] = v + end + + local pads = {0xEC, 0x11} + local p = 1 + while #data < info.data do + data[#data + 1] = pads[p] + p = 3 - p + end + + return data +end + +local function reedSolomon(data, degree) + local gen = {1} + + for i = 0, degree - 1 do + local nextGen = {} + for j = 1, #gen + 1 do + nextGen[j] = 0 + end + + for j = 1, #gen do + nextGen[j] = bit32.bxor(nextGen[j], gen[j]) + nextGen[j + 1] = bit32.bxor(nextGen[j + 1], gfMul(gen[j], gfExp[i])) + end + + gen = nextGen + end + + local rem = {} + for i = 1, degree do + rem[i] = 0 + end + + for _, b in ipairs(data) do + local factor = bit32.bxor(b, rem[1]) + + for i = 1, degree - 1 do + rem[i] = rem[i + 1] + end + rem[degree] = 0 + + for i = 1, degree do + rem[i] = bit32.bxor(rem[i], gfMul(gen[i + 1], factor)) + end + end + + return rem +end + +local function makeMatrix(size) + local matrix = {} + local reserved = {} + + for y = 1, size do + matrix[y] = {} + reserved[y] = {} + for x = 1, size do + matrix[y][x] = false + reserved[y][x] = false + end + end + + local function setModule(x, y, dark, isReserved) + matrix[y][x] = dark + if isReserved then + reserved[y][x] = true + end + end + + return matrix, reserved, setModule +end + +local function addFinder(setModule, size, x, y) + for dy = -1, 7 do + for dx = -1, 7 do + local xx = x + dx + local yy = y + dy + + if xx >= 1 and xx <= size and yy >= 1 and yy <= size then + local dark = + (dx >= 0 and dx <= 6 and dy >= 0 and dy <= 6) and + (dx == 0 or dx == 6 or dy == 0 or dy == 6 or + (dx >= 2 and dx <= 4 and dy >= 2 and dy <= 4)) + + setModule(xx, yy, dark, true) + end + end + end +end + +local function addAlignment(setModule, cx, cy) + for dy = -2, 2 do + for dx = -2, 2 do + local dist = math.max(math.abs(dx), math.abs(dy)) + setModule(cx + dx, cy + dy, dist ~= 1, true) + end + end +end + +local function addFunctionPatterns(matrix, reserved, setModule, version) + local size = VERSION_INFO[version].size + + addFinder(setModule, size, 1, 1) + addFinder(setModule, size, size - 6, 1) + addFinder(setModule, size, 1, size - 6) + + for i = 9, size - 8 do + local dark = i % 2 == 1 + setModule(i, 7, dark, true) + setModule(7, i, dark, true) + end + + local align = VERSION_INFO[version].align + if align then + for _, cy in ipairs(align) do + for _, cx in ipairs(align) do + local nearTL = cx <= 9 and cy <= 9 + local nearTR = cx >= size - 8 and cy <= 9 + local nearBL = cx <= 9 and cy >= size - 8 + + if not nearTL and not nearTR and not nearBL then + addAlignment(setModule, cx, cy) + end + end + end + end + + setModule(9, size - 7, true, true) + + for i = 1, 9 do + if i ~= 7 then + reserved[9][i] = true + reserved[i][9] = true + end + end + + for i = size - 7, size do + reserved[9][i] = true + reserved[i][9] = true + end +end + +local function makeFormatBits(mask) + local data = bit32.bor(bit32.lshift(1, 3), mask) + local rem = bit32.lshift(data, 10) + local gen = 0x537 + + for i = 14, 10, -1 do + if getBit(rem, i) then + rem = bit32.bxor(rem, bit32.lshift(gen, i - 10)) + end + end + + return bit32.bxor(bit32.bor(bit32.lshift(data, 10), rem), 0x5412) +end + +local function drawFormatBits(matrix, mask) + local size = #matrix + local bits = makeFormatBits(mask) + + local function set(x, y, bitIndex) + matrix[y][x] = getBit(bits, bitIndex) + end + + for i = 0, 5 do + set(i + 1, 9, i) + end + + set(8, 9, 6) + set(9, 9, 7) + set(9, 8, 8) + + for i = 9, 14 do + set(15 - i, 9, i) + end + + for i = 0, 7 do + set(9, size - i, i) + end + + for i = 8, 14 do + set(size - 14 + i, 9, i) + end + + matrix[size - 7][9] = true +end + +local function maskCondition(mask, x, y) + local r = y - 1 + local c = x - 1 + + if mask == 0 then + return (r + c) % 2 == 0 + end + + return false +end + +function QR.generate(text) + local bytes = makeBytes(text) + local version = chooseVersion(#bytes) + + if not version then + error("Content too long: maximum 78 bytes supported. Chinese characters typically use 3 bytes each.") + end + + local info = VERSION_INFO[version] + local size = info.size + + local matrix, reserved, setModule = makeMatrix(size) + addFunctionPatterns(matrix, reserved, setModule, version) + + local data = makeDataCodewords(bytes, version) + local ec = reedSolomon(data, info.ec) + + local all = {} + for _, v in ipairs(data) do + all[#all + 1] = v + end + for _, v in ipairs(ec) do + all[#all + 1] = v + end + + local bits = {} + for _, b in ipairs(all) do + for i = 7, 0, -1 do + bits[#bits + 1] = getBit(b, i) and 1 or 0 + end + end + + local bitIndex = 1 + local upward = true + local x = size + + while x >= 1 do + if x == 7 then + x = x - 1 + end + + for i = 1, size do + local y = upward and (size - i + 1) or i + + for dx = 0, 1 do + local xx = x - dx + if xx >= 1 and not reserved[y][xx] then + local bit = false + if bitIndex <= #bits then + bit = bits[bitIndex] == 1 + bitIndex = bitIndex + 1 + end + + if maskCondition(0, xx, y) then + bit = not bit + end + + matrix[y][xx] = bit + end + end + end + + upward = not upward + x = x - 2 + end + + drawFormatBits(matrix, 0) + + return matrix, version +end + +local function parseColor(str) + local nums = {} + for n in string.gmatch(str, "%d+") do + nums[#nums + 1] = tonumber(n) + end + + if #nums < 3 then + return nil + end + + return Color3.fromRGB( + math.clamp(nums[1], 0, 255), + math.clamp(nums[2], 0, 255), + math.clamp(nums[3], 0, 255) + ) +end + +local function clearPreview() + for _, child in ipairs(previewFrame:GetChildren()) do + if child:IsA("Frame") and child ~= previewPlaceholder then + child:Destroy() + end + end + previewPlaceholder.Visible = true +end + +local function drawQRCode(parent, matrix, fg, bg, pixel, quietZone) + local size = #matrix + local total = size + quietZone * 2 + + parent.BackgroundColor3 = bg + parent.Size = UDim2.new(0, total * pixel, 0, total * pixel) + + for y = 1, size do + for x = 1, size do + if matrix[y][x] then + local p = Instance.new("Frame") + p.Size = UDim2.new(0, pixel, 0, pixel) + p.Position = UDim2.new(0, (x - 1 + quietZone) * pixel, 0, (y - 1 + quietZone) * pixel) + p.BackgroundColor3 = fg + p.BorderSizePixel = 0 + p.Parent = parent + end + end + end +end + +local function createQRInStarterGui(content, fg, bg, pixel) + local matrix, version = QR.generate(content) + local quietZone = 4 + local size = #matrix + local total = size + quietZone * 2 + + local starterGui = game:GetService("StarterGui") + + for _, child in ipairs(starterGui:GetChildren()) do + if child:IsA("ScreenGui") and string.sub(child.Name, 1, 8) == "QRCode_" then + child:Destroy() + end + end + + local gui = Instance.new("ScreenGui") + gui.Name = "QRCode_" .. os.time() + gui.ResetOnSpawn = false + gui.IgnoreGuiInset = true + + local container = Instance.new("Frame") + container.Name = "QRCode" + container.AnchorPoint = Vector2.new(0.5, 0.5) + container.Position = UDim2.new(0.5, 0, 0.5, 0) + container.BorderSizePixel = 0 + container.Parent = gui + + drawQRCode(container, matrix, fg, bg, pixel, quietZone) + + gui.Parent = starterGui + + return matrix, version, total * pixel +end + +genBtn.MouseButton1Click:Connect(function() + local content = contentBox.Text + + if content == "" then + statusLabel.Text = "Error: Please enter content" + statusLabel.TextColor3 = Color3.fromRGB(255, 100, 100) + return + end + + local fg = parseColor(fgBox.Text) + local bg = parseColor(bgBox.Text) + + if not fg then + statusLabel.Text = "Error: Invalid foreground color format (e.g. 0,0,0)" + statusLabel.TextColor3 = Color3.fromRGB(255, 100, 100) + return + end + + if not bg then + statusLabel.Text = "Error: Invalid background color format (e.g. 255,255,255)" + statusLabel.TextColor3 = Color3.fromRGB(255, 100, 100) + return + end + + if fg == bg then + statusLabel.Text = "Error: Foreground and background colors cannot be the same" + statusLabel.TextColor3 = Color3.fromRGB(255, 100, 100) + return + end + + local pixel = tonumber(pixelBox.Text) or 10 + pixel = math.clamp(math.floor(pixel), 4, 40) + + local ok, resultOrErr, version, finalSize = pcall(function() + return createQRInStarterGui(content, fg, bg, pixel) + end) + + if not ok then + statusLabel.Text = "Generation failed: " .. tostring(resultOrErr) + statusLabel.TextColor3 = Color3.fromRGB(255, 100, 100) + return + end + + local matrix = resultOrErr + + clearPreview() + previewPlaceholder.Visible = false + + local previewSize = previewFrame.AbsoluteSize.Y + local previewPixel = math.floor(previewSize / (#matrix + 8)) + if previewPixel < 1 then + previewPixel = 1 + end + + local previewContainer = Instance.new("Frame") + previewContainer.Position = UDim2.new(0.5, 0, 0.5, 0) + previewContainer.AnchorPoint = Vector2.new(0.5, 0.5) + previewContainer.BorderSizePixel = 0 + previewContainer.Parent = previewFrame + + drawQRCode(previewContainer, matrix, fg, bg, previewPixel, 4) + + statusLabel.Text = "Generated: Version " .. tostring(version) .. ", Size " .. tostring(finalSize) .. "x" .. tostring(finalSize) + statusLabel.TextColor3 = Color3.fromRGB(150, 255, 150) +end) + +button.Click:Connect(function() + widget.Enabled = not widget.Enabled +end) + +print("BrialQRify loaded successfully") \ No newline at end of file