Last active
December 17, 2023 11:31
-
-
Save Egor-Skriptunoff/be9a7e4546b47b74a9aae604f5a8c272 to your computer and use it in GitHub Desktop.
Revisions
-
Egor-Skriptunoff revised this gist
Apr 19, 2019 . 1 changed file with 15 additions and 15 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,15 +18,15 @@ -- ------------------------------------------------------------------------------------------ -- This new function is a drop-in replacement for standard Lua function "math.random()". -- It generates different sequences of random numbers on every profile load, you don't need to set seed (forget about "math.randomseed"). -- The random number generator adsorbs entropy from every event processed by OnEvent(). -- It takes into account everything: event type, button index, mouse position on the screen, current date and running time. -- This entropy is converted by SHAKE128 (SHA3 hash function) into stream of pseudo-random bits. -- That's why function "random()" returns random numbers having excellent statistical properties. -- Actually, after user clicked mouse buttons 100-200 times (no hurry please), -- these pseudo-random numbers might be considered cryptographically strong. -- -- ------------------------------------------------------------------------------------------ -- GetEntropyCounter() -- ------------------------------------------------------------------------------------------ -- This function returns estimation of lower bound of number of random bits consumed by random numbers mixer -- (wait until it reaches 256 prior to generating crypto keys) @@ -293,7 +293,7 @@ end local update_internal_state, random, perform_calculations local SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256 local GetEntropyCounter do local function create_array_of_lanes() @@ -653,7 +653,7 @@ do end local to_be_refined, to_be_refined_qty = {}, 0 -- buffer for entropy from user actions: 32-bit values, max 128 elements local refined, refined_qty = {}, 0 -- buffer for precalculated random numbers: 53-bit values, max 1024 elements local rnd_lanes = create_array_of_lanes() local RND = 0 @@ -718,17 +718,17 @@ do local log = math.log local log4 = log(4) local function entropy_from_delta(delta) -- "delta" is a difference between two sequencial measurements of some integer parameter controlled by user (pixel coord of mouse, timer tick count) -- all bits except 3 highest might be considered pure random delta = delta * delta return delta < 25 and 0 or log(delta) / log4 - 3 end local entropy_counter = 0 function GetEntropyCounter() return floor(entropy_counter) end local prev_x, prev_y, prev_t @@ -758,9 +758,9 @@ do mix16(x) refine32(c * 2^16 + d) mix16(y) entropy_counter = entropy_counter + entropy_from_delta((t - prev_t) / 16) -- timer's resolution is 16 ms + ((x < 16 or x >= size_x - 16) and 0 or min(4, entropy_from_delta(x - prev_x))) -- mouse x (mouse position modulo 16 pixels might be considered pure random except when near screen edge) + ((y < 16 or y >= size_y - 16) and 0 or min(4, entropy_from_delta(y - prev_y))) -- mouse y prev_x, prev_y, prev_t = x, y, t end end @@ -890,7 +890,7 @@ local function Sleep(delay_ms) if delay_ms > 0 then Sleep_orig(delay_ms) end update_internal_state() -- this invocation adds entropy to RNG (it's very fast) end @@ -961,7 +961,7 @@ function OnEvent(event, arg, family) elseif event == "MOUSE_BUTTON_PRESSED" or event == "MOUSE_BUTTON_RELEASED" then mouse_button = Logitech_order[arg] or arg -- convert 1,2,3 to "L","R","M" end update_internal_state(event, arg, family) -- this invocation adds entropy to RNG (it's very fast) ---------------------------------------------------------------------- -- LOG THIS EVENT ---------------------------------------------------------------------- @@ -1037,7 +1037,7 @@ function OnEvent(event, arg, family) -- print misc info local t = floor(GetRunningTime() / 1000) print("profile running time = "..floor(t / 3600)..":"..sub(100 + floor(t / 60) % 60, -2)..":"..sub(100 + t % 60, -2)) print("approximately "..GetEntropyCounter().." bits of entropy was received from button press events") local i = random(3) -- integer 1 <= i <= 3 print("random int:", i) local b = random(0, 255) -- integer 0 <= b <= 255 -
Egor-Skriptunoff revised this gist
Apr 19, 2019 . 1 changed file with 808 additions and 114 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,86 +1,151 @@ --------------------------------------------------------------------------------------------- -- LGS_script_template.lua --------------------------------------------------------------------------------------------- -- Version: 2019-04-19 -- Author: Egor Skriptunoff -- -- -- This is a template for "Logitech Gaming Software" script file. -- Four useful features are implemented here: -- -- -- ------------------------------------------------------------------------------------------ -- FEATURE #1 - random numbers of very high quality -- ------------------------------------------------------------------------------------------ -- random() -- float 0 <= x < 1 -- random(n) -- integer 1 <= x <= n -- random(m, n) -- integer m <= x <= n -- ------------------------------------------------------------------------------------------ -- This new function is a drop-in replacement for standard Lua function "math.random()". -- It generates different sequences of random numbers on every profile load, you don't need to set seed (forget about "math.randomseed"). -- The random number generator adsorbs enthropy from every event processed by OnEvent(). -- It takes into account everything: event type, button index, mouse position on the screen, current date and running time. -- This enthropy is converted by SHAKE128 (SHA3 hash function) into stream of pseudo-random bits. -- That's why function "random()" returns random numbers having excellent statistical properties. -- Actually, after user clicked mouse buttons 100-200 times (no hurry please), -- these pseudo-random numbers might be considered cryptographically strong. -- -- ------------------------------------------------------------------------------------------ -- GetEnthropyCounter() -- ------------------------------------------------------------------------------------------ -- This function returns estimation of lower bound of number of random bits consumed by random numbers mixer -- (wait until it reaches 256 prior to generating crypto keys) -- -- ------------------------------------------------------------------------------------------ -- SHA3_224(message) -- SHA3_256(message) -- SHA3_384(message) -- SHA3_512(message) -- SHAKE128(digest_size_in_bytes, message) -- SHAKE256(digest_size_in_bytes, message) -- ------------------------------------------------------------------------------------------ -- SHA3 hash functions are available. -- SHA3_224, SHA3_256, SHA3_384, SHA3_512 generate message digest of fixed length -- SHAKE128, SHAKE256 generate message digest of potentially infinite length -- Example #1: -- How to get SHA3-digest of your message: -- SHA3_224("The quick brown fox jumps over the lazy dog") == "d15dadceaa4d5d7bb3b48f446421d542e08ad8887305e28d58335795" -- SHAKE128(5, "The quick brown fox jumps over the lazy dog") == "f4202e3c58" -- Example #2: -- How to convert your password into infinite sequence of very high quality random bytes (the same password will give the same sequence): -- -- start the sequence, initialize it with your password -- local get_hex_byte = SHAKE128(-1, "your password") -- while .... do -- -- get next number from the inifinite sequence -- local next_random_byte = tonumber(get_hex_byte(), 16) -- integer 0 <= n <= 255 -- local next_random_dword = tonumber(get_hex_byte(4), 16) -- integer 0 <= n <= 4294967295 -- -- how to construct floating point number 0 <= x < 1 -- local next_random_float = (tonumber(get_hex_byte(3), 16) % 2^21 * 2^32 + tonumber(get_hex_byte(4), 16)) / 2^53 -- .... -- end -- -- -- -- ------------------------------------------------------------------------------------------ -- FEATURE #2 - you can see the output of print() in the LGS script editor -- ------------------------------------------------------------------------------------------ -- print(...) -- ------------------------------------------------------------------------------------------ -- Now this function displays messages in the bottom window of the script editor. -- You can use "print()" just like in standard Lua! -- When using "print()" instead of "OutputLogMessage()", don't append "\n" to a message. -- -- -- -- ------------------------------------------------------------------------------------------ -- FEATURE #3 - handy names for mouse buttons -- ------------------------------------------------------------------------------------------ -- "L", "R", "M" are now names for the first three mouse buttons -- ------------------------------------------------------------------------------------------ -- There is an unpleasant feature in LGS: Logitech and Microsoft enumerate mouse buttons differently. -- In OnEvent("MOUSE_BUTTON_PRESSED", arg, "mouse") parameter 'arg' uses Logitech order: -- 1=Left, 2=Right, 3=Middle, 4=Backward(X1), 5=Forward(X2), 6,7,8,... -- In PressMouseButton(button) parameter 'button' uses Microsoft order: -- 1=Left, 2=Middle, 3=Right, 4=X1(Backward), 5=X2(Forward) -- As you see, Right and Middle buttons are swapped; this is very confusing. -- To make your code more clear and less error-prone, try to avoid using numbers 1, 2 and 3. -- Instead, use strings "L", "R", "M" for the first three mouse buttons. -- Two modifications have been made: -- 1) The following functions now accept strings "L", "R", "M" as its argument: -- PressMouseButton(), -- ReleaseMouseButton(), -- PressAndReleaseMouseButton(), -- IsMouseButtonPressed() -- 2) 'mouse_button' variable was defined inside OnEvent() function body, it contains: -- either string "L", "R", "M" (for the first three mouse buttons) -- or number 4, 5, 6, 7, 8,... (for other mouse buttons). -- These modifications don't break compatibility with your old habits. -- You can still use numbers if you want: -- if event == "MOUSE_BUTTON_PRESSED" and arg == 2 then -- RMB event -- PressAndReleaseMouseButton(3) -- simulate pressing RMB -- But using strings improves readability: -- if event == "MOUSE_BUTTON_PRESSED" and mouse_button == "R" then -- PressAndReleaseMouseButton("R") -- -- -- -- ------------------------------------------------------------------------------------------ -- FEATURE #4 - Pixel-oriented functions for mouse -- ------------------------------------------------------------------------------------------ -- GetMousePositionInPixels() -- SetMousePositionInPixels(x,y) -- ------------------------------------------------------------------------------------------ -- You can now get and set mouse cursor position IN PIXELS. -- GetMousePositionInPixels() returns 6 values (probably you would need only the first two): -- x_in_pixels, -- integer from 0 to (screen_width-1) -- y_in_pixels, -- integer from 0 to (screen_height-1) -- screen_width_in_pixels, -- for example, 1920 -- screen_height_in_pixels, -- for example, 1080 -- x_64K, -- normalized x coordinate 0..65535, this is the first value returned by "GetMousePosition()" -- y_64K -- normalized y coordinate 0..65535, this is the second value returned by "GetMousePosition()" -- As you know, standard LGS function MoveMouseRelative() is limited to narrow distance range from -127 to +127 pixels. -- Now you can move mouse cursor more than 127 pixels away from its current position: -- local current_x, current_y = GetMousePositionInPixels() -- SetMousePositionInPixels(current_x + 300, current_y + 200) -- This method of relative moving works fine even when "Acceleration" flag is set in "Pointer settings" (the third icon from the left, at the bottom of the page). -- As you probably know, MoveMouseRelative() works incorrectly when this flag is set: the real distance not equals to the number of pixels requested. -- -- Don't forget that you must wait a bit (for example, Sleep(5)) after simulating mouse move, button press or button release. -- -- -- -- Important note: -- This script requires one second for initialization. -- In other words, when this LGS profile is started, you will have to wait for 1 second before playing. -- Explanation: -- Every time this profile is activated (and every time when your game changes the screen resolution) -- the process of automatic determination of screen resolution is started. -- This process takes about one second. -- During this process, mouse cursor will be programmatically moved some distance away from its current location. -- This cursor movement might be a hindrance to use your mouse, so just wait until the cursor stops moving. local print_orig, type, floor, min, max, sqrt, format, byte, char, rep, sub, gsub, concat, select, tostring = print, type, math.floor, math.min, math.max, math.sqrt, string.format, string.byte, string.char, string.rep, string.sub, string.gsub, table.concat, select, tostring local MoveMouseRelative, MoveMouseTo, GetMousePosition, Sleep_orig, GetRunningTime, OutputLogMessage = MoveMouseRelative, MoveMouseTo, GetMousePosition, Sleep, GetRunningTime, OutputLogMessage local function print(...) @@ -89,7 +154,7 @@ local function print(...) for j = 1, select("#", ...) do t[j] = tostring(t[j]) end OutputLogMessage("%s\n", concat(t, "\t")) end @@ -104,7 +169,9 @@ do -- both width and height of your screen must be between 150 and 10240 pixels xy_64K[1], xy_64K[2] = GetMousePosition() if enabled then local jump local attempts_qty = 3 -- number of failed attempts to determine screen resolution prior to disabling this functionality for attempt = 1, attempts_qty + 1 do for i = 1, 2 do local result local size = xy_data[i][4] @@ -122,7 +189,14 @@ do end if xy_pixels[1] and xy_pixels[2] then return xy_pixels[1], xy_pixels[2], xy_data[1][4], xy_data[2][4], xy_64K[1], xy_64K[2] elseif attempt <= attempts_qty then --print("Attempt #"..attempt) if jump then MoveMouseTo(3*2^14 - xy_64K[1]/2, 3*2^14 - xy_64K[2]/2) Sleep_orig(10) xy_64K[1], xy_64K[2] = GetMousePosition() end jump = true for _, data in ipairs(xy_data) do data[1] = {[0] = true} -- [1] = dict with used coord_64K values data[2] = 0 -- [2] = used coord_64K values qty @@ -136,8 +210,8 @@ do end local dx = xy_64K[1] < 2^15 and 1 or -1 local dy = xy_64K[2] < 2^15 and 1 or -1 local prev_coords_processed_1, prev_coords_processed_2, prev_variants_qty, trust for frame = 1, 90 * attempt do for i = 1, 2 do local data, coord_64K = xy_data[i], xy_64K[i] local data_1 = data[1] @@ -173,31 +247,415 @@ do data[4] = min_size end end local variants_qty = xy_data[1][3] + xy_data[2][3] local coords_processed_1 = xy_data[1][2] local coords_processed_2 = xy_data[2][2] if variants_qty ~= prev_variants_qty then prev_variants_qty = variants_qty prev_coords_processed_1 = coords_processed_1 prev_coords_processed_2 = coords_processed_2 end if min(coords_processed_1 - prev_coords_processed_1, coords_processed_2 - prev_coords_processed_2) >= 20 then --print("Determined at frame "..frame..", resolution: "..xy_data[1][4].." x "..xy_data[2][4]) trust = true break end local num = sqrt(frame + 0.1) % 1 < 0.5 and 2^13 or 0 MoveMouseRelative( dx * max(1, floor(num / ((xy_64K[1] - 2^15) * dx + (2^15 + 2^13/8)))), dy * max(1, floor(num / ((xy_64K[2] - 2^15) * dy + (2^15 + 2^13/8)))) ) Sleep_orig(10) xy_64K[1], xy_64K[2] = GetMousePosition() end if not trust then xy_data[1][4], xy_data[2][4] = nil end end end enabled = false print'Function "GetMousePositionInPixels()" failed to determine screen resolution and has been disabled' end return 0, 0, 0, 0, xy_64K[1], xy_64K[2] -- functionality is disabled, so no pixel-related information is returned end end local function SetMousePositionInPixels(x, y) local _, _, width, height = GetMousePositionInPixels() if width > 0 then MoveMouseTo( floor(max(0, min(width - 1, x)) * (2^16-1) / (width - 1) + 0.5), floor(max(0, min(height - 1, y)) * (2^16-1) / (height - 1) + 0.5) ) end end local update_internal_state, random, perform_calculations local SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256 local GetEnthropyCounter do local function create_array_of_lanes() local arr = {} for j = 1, 50 do arr[j] = 0 end return arr end local keccak_feed, XOR53 do local RC_lo, RC_hi, AND, XOR = {}, {} do local AND_of_two_bytes, m, sh_reg = {[0] = 0}, 0, 29 for y = 0, 127 * 256, 256 do for x = y, y + 127 do x = AND_of_two_bytes[x] * 2 AND_of_two_bytes[m] = x AND_of_two_bytes[m + 1] = x AND_of_two_bytes[m + 256] = x AND_of_two_bytes[m + 257] = x + 1 m = m + 2 end m = m + 256 end function AND(x, y, xor) local x0 = x % 2^32 local y0 = y % 2^32 local rx = x0 % 256 local ry = y0 % 256 local res = AND_of_two_bytes[rx + ry * 256] x = x0 - rx y = (y0 - ry) / 256 rx = x % 65536 ry = y % 256 res = res + AND_of_two_bytes[rx + ry] * 256 x = (x - rx) / 256 y = (y - ry) / 256 rx = x % 65536 + y % 256 res = res + AND_of_two_bytes[rx] * 65536 res = res + AND_of_two_bytes[(x + y - rx) / 256] * 16777216 if xor then return x0 + y0 - 2 * res else return res end end function XOR(x, y, z, t, u) if z then if t then if u then t = AND(t, u, true) end z = AND(z, t, true) end y = AND(y, z, true) end return AND(x, y, true) end local function split53(x) local lo = x % 2^32 return lo, (x - lo) / 2^32 end function XOR53(x, y) local x_lo, x_hi = split53(x) local y_lo, y_hi = split53(y) return XOR(x_hi, y_hi) * 2^32 + XOR(x_lo, y_lo) end local function next_bit() local r = sh_reg % 2 sh_reg = XOR((sh_reg - r) / 2, 142 * r) return r * m end for idx = 1, 24 do local lo = 0 for j = 0, 5 do m = 2^(2^j - 1) lo = lo + next_bit() end RC_lo[idx], RC_hi[idx] = lo, next_bit() end end function keccak_feed(lanes, str, offs, size, block_size_in_bytes) for pos = offs, offs + size - 1, block_size_in_bytes do for j = 1, block_size_in_bytes / 4 do pos = pos + 4 local a, b, c, d = byte(str, pos - 3, pos) lanes[j] = XOR(lanes[j], ((d * 256 + c) * 256 + b) * 256 + a) end local L01_lo, L01_hi, L02_lo, L02_hi, L03_lo, L03_hi, L04_lo, L04_hi, L05_lo, L05_hi, L06_lo, L06_hi, L07_lo, L07_hi, L08_lo, L08_hi, L09_lo, L09_hi, L10_lo, L10_hi, L11_lo, L11_hi, L12_lo, L12_hi, L13_lo, L13_hi, L14_lo, L14_hi, L15_lo, L15_hi, L16_lo, L16_hi, L17_lo, L17_hi, L18_lo, L18_hi, L19_lo, L19_hi, L20_lo, L20_hi, L21_lo, L21_hi, L22_lo, L22_hi, L23_lo, L23_hi, L24_lo, L24_hi, L25_lo, L25_hi = lanes[01], lanes[02], lanes[03], lanes[04], lanes[05], lanes[06], lanes[07], lanes[08], lanes[09], lanes[10], lanes[11], lanes[12], lanes[13], lanes[14], lanes[15], lanes[16], lanes[17], lanes[18], lanes[19], lanes[20], lanes[21], lanes[22], lanes[23], lanes[24], lanes[25], lanes[26], lanes[27], lanes[28], lanes[29], lanes[30], lanes[31], lanes[32], lanes[33], lanes[34], lanes[35], lanes[36], lanes[37], lanes[38], lanes[39], lanes[40], lanes[41], lanes[42], lanes[43], lanes[44], lanes[45], lanes[46], lanes[47], lanes[48], lanes[49], lanes[50] for round_idx = 1, 24 do local C1_lo = XOR(L01_lo, L06_lo, L11_lo, L16_lo, L21_lo) local C1_hi = XOR(L01_hi, L06_hi, L11_hi, L16_hi, L21_hi) local C2_lo = XOR(L02_lo, L07_lo, L12_lo, L17_lo, L22_lo) local C2_hi = XOR(L02_hi, L07_hi, L12_hi, L17_hi, L22_hi) local C3_lo = XOR(L03_lo, L08_lo, L13_lo, L18_lo, L23_lo) local C3_hi = XOR(L03_hi, L08_hi, L13_hi, L18_hi, L23_hi) local C4_lo = XOR(L04_lo, L09_lo, L14_lo, L19_lo, L24_lo) local C4_hi = XOR(L04_hi, L09_hi, L14_hi, L19_hi, L24_hi) local C5_lo = XOR(L05_lo, L10_lo, L15_lo, L20_lo, L25_lo) local C5_hi = XOR(L05_hi, L10_hi, L15_hi, L20_hi, L25_hi) local D_lo = XOR(C1_lo, C3_lo * 2 + (C3_hi - C3_hi % 2^31) / 2^31) local D_hi = XOR(C1_hi, C3_hi * 2 + (C3_lo - C3_lo % 2^31) / 2^31) local T0_lo = XOR(D_lo, L02_lo) local T0_hi = XOR(D_hi, L02_hi) local T1_lo = XOR(D_lo, L07_lo) local T1_hi = XOR(D_hi, L07_hi) local T2_lo = XOR(D_lo, L12_lo) local T2_hi = XOR(D_hi, L12_hi) local T3_lo = XOR(D_lo, L17_lo) local T3_hi = XOR(D_hi, L17_hi) local T4_lo = XOR(D_lo, L22_lo) local T4_hi = XOR(D_hi, L22_hi) L02_lo = (T1_lo - T1_lo % 2^20) / 2^20 + T1_hi * 2^12 L02_hi = (T1_hi - T1_hi % 2^20) / 2^20 + T1_lo * 2^12 L07_lo = (T3_lo - T3_lo % 2^19) / 2^19 + T3_hi * 2^13 L07_hi = (T3_hi - T3_hi % 2^19) / 2^19 + T3_lo * 2^13 L12_lo = T0_lo * 2 + (T0_hi - T0_hi % 2^31) / 2^31 L12_hi = T0_hi * 2 + (T0_lo - T0_lo % 2^31) / 2^31 L17_lo = T2_lo * 2^10 + (T2_hi - T2_hi % 2^22) / 2^22 L17_hi = T2_hi * 2^10 + (T2_lo - T2_lo % 2^22) / 2^22 L22_lo = T4_lo * 2^2 + (T4_hi - T4_hi % 2^30) / 2^30 L22_hi = T4_hi * 2^2 + (T4_lo - T4_lo % 2^30) / 2^30 D_lo = XOR(C2_lo, C4_lo * 2 + (C4_hi - C4_hi % 2^31) / 2^31) D_hi = XOR(C2_hi, C4_hi * 2 + (C4_lo - C4_lo % 2^31) / 2^31) T0_lo = XOR(D_lo, L03_lo) T0_hi = XOR(D_hi, L03_hi) T1_lo = XOR(D_lo, L08_lo) T1_hi = XOR(D_hi, L08_hi) T2_lo = XOR(D_lo, L13_lo) T2_hi = XOR(D_hi, L13_hi) T3_lo = XOR(D_lo, L18_lo) T3_hi = XOR(D_hi, L18_hi) T4_lo = XOR(D_lo, L23_lo) T4_hi = XOR(D_hi, L23_hi) L03_lo = (T2_lo - T2_lo % 2^21) / 2^21 + T2_hi * 2^11 L03_hi = (T2_hi - T2_hi % 2^21) / 2^21 + T2_lo * 2^11 L08_lo = (T4_lo - T4_lo % 2^3) / 2^3 + T4_hi * 2^29 % 2^32 L08_hi = (T4_hi - T4_hi % 2^3) / 2^3 + T4_lo * 2^29 % 2^32 L13_lo = T1_lo * 2^6 + (T1_hi - T1_hi % 2^26) / 2^26 L13_hi = T1_hi * 2^6 + (T1_lo - T1_lo % 2^26) / 2^26 L18_lo = T3_lo * 2^15 + (T3_hi - T3_hi % 2^17) / 2^17 L18_hi = T3_hi * 2^15 + (T3_lo - T3_lo % 2^17) / 2^17 L23_lo = (T0_lo - T0_lo % 2^2) / 2^2 + T0_hi * 2^30 % 2^32 L23_hi = (T0_hi - T0_hi % 2^2) / 2^2 + T0_lo * 2^30 % 2^32 D_lo = XOR(C3_lo, C5_lo * 2 + (C5_hi - C5_hi % 2^31) / 2^31) D_hi = XOR(C3_hi, C5_hi * 2 + (C5_lo - C5_lo % 2^31) / 2^31) T0_lo = XOR(D_lo, L04_lo) T0_hi = XOR(D_hi, L04_hi) T1_lo = XOR(D_lo, L09_lo) T1_hi = XOR(D_hi, L09_hi) T2_lo = XOR(D_lo, L14_lo) T2_hi = XOR(D_hi, L14_hi) T3_lo = XOR(D_lo, L19_lo) T3_hi = XOR(D_hi, L19_hi) T4_lo = XOR(D_lo, L24_lo) T4_hi = XOR(D_hi, L24_hi) L04_lo = T3_lo * 2^21 % 2^32 + (T3_hi - T3_hi % 2^11) / 2^11 L04_hi = T3_hi * 2^21 % 2^32 + (T3_lo - T3_lo % 2^11) / 2^11 L09_lo = T0_lo * 2^28 % 2^32 + (T0_hi - T0_hi % 2^4) / 2^4 L09_hi = T0_hi * 2^28 % 2^32 + (T0_lo - T0_lo % 2^4) / 2^4 L14_lo = T2_lo * 2^25 % 2^32 + (T2_hi - T2_hi % 2^7) / 2^7 L14_hi = T2_hi * 2^25 % 2^32 + (T2_lo - T2_lo % 2^7) / 2^7 L19_lo = (T4_lo - T4_lo % 2^8) / 2^8 + T4_hi * 2^24 % 2^32 L19_hi = (T4_hi - T4_hi % 2^8) / 2^8 + T4_lo * 2^24 % 2^32 L24_lo = (T1_lo - T1_lo % 2^9) / 2^9 + T1_hi * 2^23 % 2^32 L24_hi = (T1_hi - T1_hi % 2^9) / 2^9 + T1_lo * 2^23 % 2^32 D_lo = XOR(C4_lo, C1_lo * 2 + (C1_hi - C1_hi % 2^31) / 2^31) D_hi = XOR(C4_hi, C1_hi * 2 + (C1_lo - C1_lo % 2^31) / 2^31) T0_lo = XOR(D_lo, L05_lo) T0_hi = XOR(D_hi, L05_hi) T1_lo = XOR(D_lo, L10_lo) T1_hi = XOR(D_hi, L10_hi) T2_lo = XOR(D_lo, L15_lo) T2_hi = XOR(D_hi, L15_hi) T3_lo = XOR(D_lo, L20_lo) T3_hi = XOR(D_hi, L20_hi) T4_lo = XOR(D_lo, L25_lo) T4_hi = XOR(D_hi, L25_hi) L05_lo = T4_lo * 2^14 + (T4_hi - T4_hi % 2^18) / 2^18 L05_hi = T4_hi * 2^14 + (T4_lo - T4_lo % 2^18) / 2^18 L10_lo = T1_lo * 2^20 % 2^32 + (T1_hi - T1_hi % 2^12) / 2^12 L10_hi = T1_hi * 2^20 % 2^32 + (T1_lo - T1_lo % 2^12) / 2^12 L15_lo = T3_lo * 2^8 + (T3_hi - T3_hi % 2^24) / 2^24 L15_hi = T3_hi * 2^8 + (T3_lo - T3_lo % 2^24) / 2^24 L20_lo = T0_lo * 2^27 % 2^32 + (T0_hi - T0_hi % 2^5) / 2^5 L20_hi = T0_hi * 2^27 % 2^32 + (T0_lo - T0_lo % 2^5) / 2^5 L25_lo = (T2_lo - T2_lo % 2^25) / 2^25 + T2_hi * 2^7 L25_hi = (T2_hi - T2_hi % 2^25) / 2^25 + T2_lo * 2^7 D_lo = XOR(C5_lo, C2_lo * 2 + (C2_hi - C2_hi % 2^31) / 2^31) D_hi = XOR(C5_hi, C2_hi * 2 + (C2_lo - C2_lo % 2^31) / 2^31) T1_lo = XOR(D_lo, L06_lo) T1_hi = XOR(D_hi, L06_hi) T2_lo = XOR(D_lo, L11_lo) T2_hi = XOR(D_hi, L11_hi) T3_lo = XOR(D_lo, L16_lo) T3_hi = XOR(D_hi, L16_hi) T4_lo = XOR(D_lo, L21_lo) T4_hi = XOR(D_hi, L21_hi) L06_lo = T2_lo * 2^3 + (T2_hi - T2_hi % 2^29) / 2^29 L06_hi = T2_hi * 2^3 + (T2_lo - T2_lo % 2^29) / 2^29 L11_lo = T4_lo * 2^18 + (T4_hi - T4_hi % 2^14) / 2^14 L11_hi = T4_hi * 2^18 + (T4_lo - T4_lo % 2^14) / 2^14 L16_lo = (T1_lo - T1_lo % 2^28) / 2^28 + T1_hi * 2^4 L16_hi = (T1_hi - T1_hi % 2^28) / 2^28 + T1_lo * 2^4 L21_lo = (T3_lo - T3_lo % 2^23) / 2^23 + T3_hi * 2^9 L21_hi = (T3_hi - T3_hi % 2^23) / 2^23 + T3_lo * 2^9 L01_lo = XOR(D_lo, L01_lo) L01_hi = XOR(D_hi, L01_hi) L01_lo, L02_lo, L03_lo, L04_lo, L05_lo = XOR(L01_lo, AND(-1-L02_lo, L03_lo)), XOR(L02_lo, AND(-1-L03_lo, L04_lo)), XOR(L03_lo, AND(-1-L04_lo, L05_lo)), XOR(L04_lo, AND(-1-L05_lo, L01_lo)), XOR(L05_lo, AND(-1-L01_lo, L02_lo)) L01_hi, L02_hi, L03_hi, L04_hi, L05_hi = XOR(L01_hi, AND(-1-L02_hi, L03_hi)), XOR(L02_hi, AND(-1-L03_hi, L04_hi)), XOR(L03_hi, AND(-1-L04_hi, L05_hi)), XOR(L04_hi, AND(-1-L05_hi, L01_hi)), XOR(L05_hi, AND(-1-L01_hi, L02_hi)) L06_lo, L07_lo, L08_lo, L09_lo, L10_lo = XOR(L09_lo, AND(-1-L10_lo, L06_lo)), XOR(L10_lo, AND(-1-L06_lo, L07_lo)), XOR(L06_lo, AND(-1-L07_lo, L08_lo)), XOR(L07_lo, AND(-1-L08_lo, L09_lo)), XOR(L08_lo, AND(-1-L09_lo, L10_lo)) L06_hi, L07_hi, L08_hi, L09_hi, L10_hi = XOR(L09_hi, AND(-1-L10_hi, L06_hi)), XOR(L10_hi, AND(-1-L06_hi, L07_hi)), XOR(L06_hi, AND(-1-L07_hi, L08_hi)), XOR(L07_hi, AND(-1-L08_hi, L09_hi)), XOR(L08_hi, AND(-1-L09_hi, L10_hi)) L11_lo, L12_lo, L13_lo, L14_lo, L15_lo = XOR(L12_lo, AND(-1-L13_lo, L14_lo)), XOR(L13_lo, AND(-1-L14_lo, L15_lo)), XOR(L14_lo, AND(-1-L15_lo, L11_lo)), XOR(L15_lo, AND(-1-L11_lo, L12_lo)), XOR(L11_lo, AND(-1-L12_lo, L13_lo)) L11_hi, L12_hi, L13_hi, L14_hi, L15_hi = XOR(L12_hi, AND(-1-L13_hi, L14_hi)), XOR(L13_hi, AND(-1-L14_hi, L15_hi)), XOR(L14_hi, AND(-1-L15_hi, L11_hi)), XOR(L15_hi, AND(-1-L11_hi, L12_hi)), XOR(L11_hi, AND(-1-L12_hi, L13_hi)) L16_lo, L17_lo, L18_lo, L19_lo, L20_lo = XOR(L20_lo, AND(-1-L16_lo, L17_lo)), XOR(L16_lo, AND(-1-L17_lo, L18_lo)), XOR(L17_lo, AND(-1-L18_lo, L19_lo)), XOR(L18_lo, AND(-1-L19_lo, L20_lo)), XOR(L19_lo, AND(-1-L20_lo, L16_lo)) L16_hi, L17_hi, L18_hi, L19_hi, L20_hi = XOR(L20_hi, AND(-1-L16_hi, L17_hi)), XOR(L16_hi, AND(-1-L17_hi, L18_hi)), XOR(L17_hi, AND(-1-L18_hi, L19_hi)), XOR(L18_hi, AND(-1-L19_hi, L20_hi)), XOR(L19_hi, AND(-1-L20_hi, L16_hi)) L21_lo, L22_lo, L23_lo, L24_lo, L25_lo = XOR(L23_lo, AND(-1-L24_lo, L25_lo)), XOR(L24_lo, AND(-1-L25_lo, L21_lo)), XOR(L25_lo, AND(-1-L21_lo, L22_lo)), XOR(L21_lo, AND(-1-L22_lo, L23_lo)), XOR(L22_lo, AND(-1-L23_lo, L24_lo)) L21_hi, L22_hi, L23_hi, L24_hi, L25_hi = XOR(L23_hi, AND(-1-L24_hi, L25_hi)), XOR(L24_hi, AND(-1-L25_hi, L21_hi)), XOR(L25_hi, AND(-1-L21_hi, L22_hi)), XOR(L21_hi, AND(-1-L22_hi, L23_hi)), XOR(L22_hi, AND(-1-L23_hi, L24_hi)) L01_lo = XOR(L01_lo, RC_lo[round_idx]) L01_hi = L01_hi + RC_hi[round_idx] end lanes[01], lanes[02], lanes[03], lanes[04], lanes[05], lanes[06], lanes[07], lanes[08], lanes[09], lanes[10], lanes[11], lanes[12], lanes[13], lanes[14], lanes[15], lanes[16], lanes[17], lanes[18], lanes[19], lanes[20], lanes[21], lanes[22], lanes[23], lanes[24], lanes[25], lanes[26], lanes[27], lanes[28], lanes[29], lanes[30], lanes[31], lanes[32], lanes[33], lanes[34], lanes[35], lanes[36], lanes[37], lanes[38], lanes[39], lanes[40], lanes[41], lanes[42], lanes[43], lanes[44], lanes[45], lanes[46], lanes[47], lanes[48], lanes[49], lanes[50] = L01_lo, L01_hi % 2^32, L02_lo, L02_hi, L03_lo, L03_hi, L04_lo, L04_hi, L05_lo, L05_hi, L06_lo, L06_hi, L07_lo, L07_hi, L08_lo, L08_hi, L09_lo, L09_hi, L10_lo, L10_hi, L11_lo, L11_hi, L12_lo, L12_hi, L13_lo, L13_hi, L14_lo, L14_hi, L15_lo, L15_hi, L16_lo, L16_hi, L17_lo, L17_hi, L18_lo, L18_hi, L19_lo, L19_hi, L20_lo, L20_hi, L21_lo, L21_hi, L22_lo, L22_hi, L23_lo, L23_hi, L24_lo, L24_hi, L25_lo, L25_hi end end local function keccak(block_size_in_bytes, digest_size_in_bytes, is_SHAKE, message) local tail, lanes = "", create_array_of_lanes() local result local function partial(message_part) if message_part then if tail then local offs = 0 if tail ~= "" and #tail + #message_part >= block_size_in_bytes then offs = block_size_in_bytes - #tail keccak_feed(lanes, tail..sub(message_part, 1, offs), 0, block_size_in_bytes, block_size_in_bytes) tail = "" end local size = #message_part - offs local size_tail = size % block_size_in_bytes keccak_feed(lanes, message_part, offs, size - size_tail, block_size_in_bytes) tail = tail..sub(message_part, #message_part + 1 - size_tail) return partial else error("Adding more chunks is not allowed after receiving the result", 2) end else if tail then local gap_start = is_SHAKE and 31 or 6 tail = tail..(#tail + 1 == block_size_in_bytes and char(gap_start + 128) or char(gap_start)..rep("\0", (-2 - #tail) % block_size_in_bytes).."\128") keccak_feed(lanes, tail, 0, #tail, block_size_in_bytes) tail = nil local lanes_used = 0 local total_lanes = block_size_in_bytes / 4 local dwords = {} local function get_next_dwords_of_digest(dwords_qty) if lanes_used >= total_lanes then keccak_feed(lanes, nil, 0, 1, 1) lanes_used = 0 end dwords_qty = floor(min(dwords_qty, total_lanes - lanes_used)) for j = 1, dwords_qty do dwords[j] = format("%08x", lanes[lanes_used + j]) end lanes_used = lanes_used + dwords_qty return gsub(concat(dwords, "", 1, dwords_qty), "(..)(..)(..)(..)", "%4%3%2%1"), dwords_qty * 4 end local parts = {} local last_part, last_part_size = "", 0 local function get_next_part_of_digest(bytes_needed) bytes_needed = bytes_needed or 1 if bytes_needed <= last_part_size then last_part_size = last_part_size - bytes_needed local part_size_in_nibbles = bytes_needed * 2 local result = sub(last_part, 1, part_size_in_nibbles) last_part = sub(last_part, part_size_in_nibbles + 1) return result end local parts_qty = 0 if last_part_size > 0 then parts_qty = 1 parts[parts_qty] = last_part bytes_needed = bytes_needed - last_part_size end while bytes_needed >= 4 do local next_part, next_part_size = get_next_dwords_of_digest(bytes_needed / 4) parts_qty = parts_qty + 1 parts[parts_qty] = next_part bytes_needed = bytes_needed - next_part_size end if bytes_needed > 0 then last_part, last_part_size = get_next_dwords_of_digest(1) parts_qty = parts_qty + 1 parts[parts_qty] = get_next_part_of_digest(bytes_needed) else last_part, last_part_size = "", 0 end return concat(parts, "", 1, parts_qty) end if digest_size_in_bytes < 0 then result = get_next_part_of_digest else result = get_next_part_of_digest(digest_size_in_bytes) end end return result end end if message then -- Actually perform calculations and return the SHA3 digest of a message return partial(message)() else -- Return function for chunk-by-chunk loading -- User should feed every chunk of input data as single argument to this function and finally get SHA3 digest by invoking this function without an argument return partial end end function SHA3_224(message) return keccak(144, 28, false, message) end function SHA3_256(message) return keccak(136, 32, false, message) end function SHA3_384(message) return keccak(104, 48, false, message) end function SHA3_512(message) return keccak( 72, 64, false, message) end function SHAKE128(digest_size_in_bytes, message) return keccak(168, digest_size_in_bytes, true, message) end function SHAKE256(digest_size_in_bytes, message) return keccak(136, digest_size_in_bytes, true, message) end end local to_be_refined, to_be_refined_qty = {}, 0 -- buffer for enthropy from user actions: 32-bit values, max 128 elements local refined, refined_qty = {}, 0 -- buffer for precalculated random numbers: 53-bit values, max 1024 elements local rnd_lanes = create_array_of_lanes() local RND = 0 local function mix16(n) @@ -207,60 +665,243 @@ do RND = L36 * 126611 + (K53 - L36) * (505231 / 2^36) + n % 256 * 598352261448 + n * 2348539529 end function perform_calculations() -- returns true if job's done if to_be_refined_qty >= 42 or refined_qty <= 1024 - 25 then local used_qty = min(42, to_be_refined_qty) for j = 1, used_qty do rnd_lanes[j] = rnd_lanes[j] + to_be_refined[j] end for j = 42 + 1, to_be_refined_qty do to_be_refined[j - 42] = to_be_refined[j] end to_be_refined_qty = to_be_refined_qty - used_qty keccak_feed(rnd_lanes, nil, 0, 1, 1) local lane_idx, queued_bits_qty, queued_bits = 0, 0, 0 for j = 1, 25 do if queued_bits_qty < 21 then lane_idx = lane_idx + 1 queued_bits = queued_bits * 2^32 + rnd_lanes[lane_idx] queued_bits_qty = queued_bits_qty + 32 end local value53 = queued_bits % 2^21 queued_bits = (queued_bits - value53) / 2^21 queued_bits_qty = queued_bits_qty - 21 lane_idx = lane_idx + 1 value53 = rnd_lanes[lane_idx] * 2^21 + value53 if refined_qty < 1024 then refined_qty = refined_qty + 1 refined[refined_qty] = value53 else local refined_idx = RND % refined_qty + 1 local old_value53 = refined[refined_idx] refined[refined_idx] = XOR53(old_value53, value53) mix16(old_value53) end end else return true -- nothing to do end end local function refine32(value32) if to_be_refined_qty < 128 then to_be_refined_qty = to_be_refined_qty + 1 to_be_refined[to_be_refined_qty] = value32 % 2^32 else local idx = RND % to_be_refined_qty + 1 to_be_refined[idx] = (to_be_refined[idx] + value32) % 2^32 end end do local log = math.log local log4 = log(4) local function enthropy_from_delta(delta) -- "delta" is a difference between two sequencial measurements of some integer parameter controlled by user (pixel coord of mouse, timer tick count) -- all bits except 3 highest might be considered pure random delta = delta * delta return delta < 25 and 0 or log(delta) / log4 - 3 end local enthropy_counter = 0 function GetEnthropyCounter() return floor(enthropy_counter) end local prev_x, prev_y, prev_t local enumerated = {MOUSE_BUTTON_PRESSED = 600, G_PRESSED = 500, M_PRESSED = 400, MOUSE_BUTTON_RELEASED = 300, G_RELEASED = 200, M_RELEASED = 100, lhc = 50} function update_internal_state(event, arg, family) local x, y, size_x, size_y, c, d = GetMousePositionInPixels() mix16(c) mix16(d) local t = GetRunningTime() mix16(t) if event then if arg then event = (enumerated[event] or 0) + arg + (enumerated[family] or 0) mix16(event) else for j = 1, #event, 2 do local low, high = byte(event, j, j + 1) local value16 = low + (high or 0) * 256 mix16(value16) refine32(value16) end event, prev_x, prev_y, prev_t = 400, x, y, t end if event >= 400 then -- only "pressed" events refine32(t * 2^10 + event) mix16(x) refine32(c * 2^16 + d) mix16(y) enthropy_counter = enthropy_counter + enthropy_from_delta((t - prev_t) / 16) -- timer's resolution is 16 ms + ((x < 16 or x >= size_x - 16) and 0 or min(4, enthropy_from_delta(x - prev_x))) -- mouse x (mouse position modulo 16 pixels might be considered pure random except when near screen edge) + ((y < 16 or y >= size_y - 16) and 0 or min(4, enthropy_from_delta(y - prev_y))) -- mouse y prev_x, prev_y, prev_t = x, y, t end end end end local function get_53_random_bits() if refined_qty == 0 then perform_calculations() -- precalculate next 25 random numbers (53 bits each), it will take 30 ms end local refined_idx = RND % refined_qty + 1 local value53 = refined[refined_idx] refined[refined_idx] = refined[refined_qty] refined_qty = refined_qty - 1 mix16(value53) return value53 end local cached_bits, cached_bits_qty = 0, 0 local function get_random_bits(number_of_bits) local pwr_number_of_bits = 2^number_of_bits local result if number_of_bits <= cached_bits_qty then result = cached_bits % pwr_number_of_bits cached_bits = (cached_bits - result) / pwr_number_of_bits else local new_bits = get_53_random_bits() result = new_bits % pwr_number_of_bits cached_bits = (new_bits - result) / pwr_number_of_bits * 2^cached_bits_qty + cached_bits cached_bits_qty = 53 + cached_bits_qty end cached_bits_qty = cached_bits_qty - number_of_bits return result end local prev_width, prev_bits_in_factor, prev_k = 0 function random(m, n) -- drop-in replacement for math.random() if m then if not n then m, n = 1, m end local k = n - m + 1 if k < 1 or k > 2^53 then error("Invalid arguments for function random()", 2) end local width, bits_in_factor, modk if k == prev_k then width, bits_in_factor = prev_width, prev_bits_in_factor else local pwr_prev_width = 2^prev_width if k > pwr_prev_width / 2 and k <= pwr_prev_width then width = prev_width else width = 53 local width_low = -1 repeat local w = floor((width_low + width) / 2) if k <= 2^w then width = w else width_low = w end until width - width_low == 1 prev_width = width end bits_in_factor = 0 local bits_in_factor_high = width + 1 while bits_in_factor_high - bits_in_factor > 1 do local bits_in_new_factor = floor((bits_in_factor + bits_in_factor_high) / 2) if k % 2^bits_in_new_factor == 0 then bits_in_factor = bits_in_new_factor else bits_in_factor_high = bits_in_new_factor end end prev_k, prev_bits_in_factor = k, bits_in_factor end local factor, saved_bits, saved_bits_qty, pwr_saved_bits_qty = 2^bits_in_factor, 0, 0, 2^0 k = k / factor width = width - bits_in_factor local pwr_width = 2^width local gap = pwr_width - k repeat modk = get_random_bits(width - saved_bits_qty) * pwr_saved_bits_qty + saved_bits local modk_in_range = modk < k if not modk_in_range then local interval = gap saved_bits = modk - k saved_bits_qty = width - 1 pwr_saved_bits_qty = pwr_width / 2 repeat saved_bits_qty = saved_bits_qty - 1 pwr_saved_bits_qty = pwr_saved_bits_qty / 2 if pwr_saved_bits_qty <= interval then if saved_bits < pwr_saved_bits_qty then interval = nil else interval = interval - pwr_saved_bits_qty saved_bits = saved_bits - pwr_saved_bits_qty end end until not interval end until modk_in_range return m + modk * factor + get_random_bits(bits_in_factor) else return get_53_random_bits() / 2^53 end end end -- function Sleep() is redefined to automatically update internal state on every wake-up and to precalculate random numbers instead of long sleeping local function Sleep(delay_ms) delay_ms = delay_ms or 10 -- 10 ms by default local start_time = GetRunningTime() local time_now, jobs_done = start_time while not jobs_done and time_now < start_time + delay_ms - 100 do jobs_done = perform_calculations() -- 30 ms of useful job time_now = GetRunningTime() end delay_ms = delay_ms - (time_now - start_time) if delay_ms > 0 then Sleep_orig(delay_ms) end update_internal_state() -- this invocation adds enthropy to RNG (it's very fast) end local Logitech_order = {"L", "R", "M"} local Microsoft_order = {L=1, M=2, R=3, l=1, m=2, r=3} local PressMouseButton_orig, ReleaseMouseButton_orig, PressAndReleaseMouseButton_orig, IsMouseButtonPressed_orig = PressMouseButton, ReleaseMouseButton, PressAndReleaseMouseButton, IsMouseButtonPressed -- These functions now accept strings "L", "R", "M" instead of button number local function PressMouseButton(button) PressMouseButton_orig(Microsoft_order[button] or button) end @@ -278,6 +919,18 @@ local function IsMouseButtonPressed(button) end -- Test SHA3 functions do assert(SHA3_224("The quick brown fox jumps over the lazy dog") == "d15dadceaa4d5d7bb3b48f446421d542e08ad8887305e28d58335795") assert(SHA3_256("") == "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a") assert(SHA3_384("The quick brown fox jumps over the lazy dog") == "7063465e08a93bce31cd89d2e3ca8f602498696e253592ed26f07bf7e703cf328581e1471a7ba7ab119b1a9ebdf8be41") assert(SHAKE128(11, "The quick brown fox jumps over the lazy dog") == "f4202e3c5852f9182a0430") local generator = SHAKE128(-1, "The quick brown fox jumps over the lazy dog") -- negative digest size means "return generator of infinite stream" assert(generator(5) == "f4202e3c58") -- first 5 bytes assert(generator(4) == "52f9182a") -- next 4 bytes, and so on... end -- ============================================== NOTHING SHOULD BE MODIFIED ABOVE THIS LINE ============================================== ---------------------------------------------------------------------- -- FUNCTIONS AND VARIABLES @@ -286,23 +939,29 @@ end -- function OnEvent(event, arg, family) local mouse_button if event == "PROFILE_ACTIVATED" then ClearLog() EnablePrimaryMouseButtonEvents(true) update_internal_state(GetDate()) -- it takes about 1 second because of determining your screen resolution ---------------------------------------------------------------------- -- CODE FOR PROFILE ACTIVATION ---------------------------------------------------------------------- -- set your favourite mouse sensitivity SetMouseDPITableIndex(2) -- turn NumLock ON if it is currently OFF (to make numpad keys 0-9 usable in a game) if not IsKeyLockOn"NumLock" then PressAndReleaseKey"NumLock" end -- insert your code here (initialize variables, display "Hello" on LCD screen, etc.) -- elseif event == "MOUSE_BUTTON_PRESSED" or event == "MOUSE_BUTTON_RELEASED" then mouse_button = Logitech_order[arg] or arg -- convert 1,2,3 to "L","R","M" end update_internal_state(event, arg, family) -- this invocation adds enthropy to RNG (it's very fast) ---------------------------------------------------------------------- -- LOG THIS EVENT ---------------------------------------------------------------------- @@ -312,17 +971,15 @@ function OnEvent(event, arg, family) -- "family = '"..family.."'" -- ) -- if event == "PROFILE_DEACTIVATED" then EnablePrimaryMouseButtonEvents(false) ---------------------------------------------------------------------- -- CODE FOR PROFILE DEACTIVATION ---------------------------------------------------------------------- -- insert your code here (display "Bye!" on LCD screen, etc.) -- please note that you have only 1 second before your script will be aborted -- end ---------------------------------------------------------------------- -- MOUSE EVENTS PROCESSING ---------------------------------------------------------------------- @@ -352,20 +1009,50 @@ function OnEvent(event, arg, family) end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == 6 then -- (this is just a code example, remove it after reading) -- Move mouse along a circle local R = 50 local x, y = GetMousePositionInPixels() x = x + R -- (x,y) = center for j = 1, 90 do local angle = (2 * math.pi) * (j / 90) SetMousePositionInPixels(x - R * math.cos(angle), y - R * math.sin(angle)) Sleep() end -------------- end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == 6 then end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == 7 then end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == 7 then end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == 8 then -- (this is just a code example, remove it after reading) -- print misc info local t = floor(GetRunningTime() / 1000) print("profile running time = "..floor(t / 3600)..":"..sub(100 + floor(t / 60) % 60, -2)..":"..sub(100 + t % 60, -2)) print("approximately "..GetEnthropyCounter().." bits of enthropy was received from button press events") local i = random(3) -- integer 1 <= i <= 3 print("random int:", i) local b = random(0, 255) -- integer 0 <= b <= 255 print("random byte:", ("%02X"):format(b)) local x = random() -- float 0 <= x < 1 print("random float:", x) local mouse_x, mouse_y, screen_width, screen_height = GetMousePositionInPixels() print("your screen size is "..screen_width.."x"..screen_height) print("your mouse cursor is at pixel ("..mouse_x..","..mouse_y..")") -------------- end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == 8 then end ---------------------------------------------------------------------- -- KEYBOARD AND LEFT-HANDED-CONTROLLER EVENTS PROCESSING ---------------------------------------------------------------------- @@ -379,4 +1066,11 @@ function OnEvent(event, arg, family) if event == "M_RELEASED" and arg == 3 then -- M3 key end ---------------------------------------------------------------------- -- EXIT EVENT PROCESSING ---------------------------------------------------------------------- -- After current event is processed, we probably have at least 30 milliseconds before the next event -- It's a good time for "background calculations" (precalculate next 25 random numbers) perform_calculations() -- it takes 30 ms on a modern PC end -
Egor-Skriptunoff created this gist
Apr 7, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,382 @@ -- This is a template script file for "Logitech Gaming Software". -- It introduces four useful features: -- -- -- ------------------------------------------------------------------------------------------ -- Mouse button names for first three mouse buttons -- ------------------------------------------------------------------------------------------ -- The is an unpleasant feature in LGS: Logitech and Microsoft enumerate mouse buttons differently. -- In OnEvent("MOUSE_BUTTON_PRESSED", arg, "mouse") parameter 'arg' uses Logitech order: -- 1=Left, 2=Right, 3=Middle, 4=Backward(X1), 5=Forward(X2), 6,7,8,... -- In PressAndReleaseMouseButton(button) parameter 'button' uses Microsoft order: -- 1=Left, 2=Middle, 3=Right, 4=X1(Backward), 5=X2(Forward) -- As you see, Right and Middle buttons are swapped; this is very confusing. -- To make your code more clear and less error-prone, try to avoid using numbers 1,2,3 to describe mouse buttons. -- Instead, use strings "L", "R", "M" for the first three mouse buttons (and numbers 4,5,6,7,8,... for other mouse buttons). -- To make this possible: -- 1) Functions PressMouseButton(), ReleaseMouseButton(), PressAndReleaseMouseButton(), IsMouseButtonPressed() now accept "L", "R", "M" as its argument; -- 2) 'mouse_button' variable inside OnEvent() contains either string "L", "R", "M" (for the first three mouse buttons) or number 4,5,6,7,8,... (for other mouse buttons). -- This modification does not break compatibility with your old Lua code. You still can use numbers if you want: -- if event == "MOUSE_BUTTON_PRESSED" and arg == 2 then -- RMB event -- PressAndReleaseMouseButton(3) -- simulate pressing RMB -- But using strings improves readability: -- if event == "MOUSE_BUTTON_PRESSED" and mouse_button == "R" then -- PressAndReleaseMouseButton("R") -- -- -- -- ------------------------------------------------------------------------------------------ -- print(...) -- ------------------------------------------------------------------------------------------ -- Now this function displays messages in the bottom window of the script editor. -- You can now use "print()" just like in standard Lua! -- Forget about "OutputLogMessage()". -- When using "print()" instead of "OutputLogMessage()", don't append "\n" to a message. -- -- -- -- ------------------------------------------------------------------------------------------ -- random() -- random(n) -- random(m, n) -- ------------------------------------------------------------------------------------------ -- This function is a drop-in replacement for standard Lua function "math.random()". -- It generates different PRN sequences every time! -- Now you can forget about "math.randomseed()". -- -- -- -- ------------------------------------------------------------------------------------------ -- GetMousePositionInPixels() -- ------------------------------------------------------------------------------------------ -- This new function returns extended information about mouse cursor position. -- It returns mouse coordinates IN PIXELS! -- Six values are returned by the function: -- mouse_x_in_pixels, -- integer from 0 to (screen_width-1) -- mouse_y_in_pixels, -- integer from 0 to (screen_height-1) -- screen_width_in_pixels, -- for example, 1920 -- screen_height_in_pixels, -- for example, 1080 -- x_64K, -- normalized x coordinate 0..65535, this is the first value returned by "GetMousePosition()" -- y_64K -- normalized y coordinate 0..65535, this is the second value returned by "GetMousePosition()" -- -- -- -- ------------------------------------------------------------------------------------------ -- Important note -- ------------------------------------------------------------------------------------------ -- This script requires one second for initialization. -- In other words, when this LGS profile is started, you will have to wait for 1 second before playing. -- Explanation: -- Every time this profile is activated (and every time when your game changes the screen resolution) -- the process of automatic determination of screen resolution is started. -- This process is time-consuming: it takes about one second. -- During this process, mouse cursor will be programmatically moved 60 pixels away (diagonally) from its current location. -- This cursor movement might be a hindrance to use your mouse, so just wait until the cursor stops moving. local print_orig, type, floor, ceil, byte, sub, table_concat, select, tostring = print, type, math.floor, math.ceil, string.byte, string.sub, table.concat, select, tostring local MoveMouseRelative, GetMousePosition, Sleep_orig, GetRunningTime, OutputLogMessage = MoveMouseRelative, GetMousePosition, Sleep, GetRunningTime, OutputLogMessage local function print(...) print_orig(...) local t = {...} for j = 1, select("#", ...) do t[j] = tostring(t[j]) end OutputLogMessage("%s\n", table_concat(t, "\t")) end local GetMousePositionInPixels do local xy_data, xy_64K, xy_pixels, enabled = {{}, {}}, {}, {}, true function GetMousePositionInPixels() -- The function returns mouse_x_pixels, mouse_y_pixels, screen_width, screen_height, x_64K, y_64K -- 0 <= mouse_x_pixels < screen_width -- 0 <= mouse_y_pixels < screen_height -- both width and height of your screen must be between 150 and 10240 pixels xy_64K[1], xy_64K[2] = GetMousePosition() if enabled then for attempts = 5, 0, -1 do -- 5 failed attempts to determine screen resolution prior to disabling this functionality for i = 1, 2 do local result local size = xy_data[i][4] if size then local coord_64K = xy_64K[i] -- How to convert between pos_64K_x (0...65535) and pixel_x (0...(screen_width-1)) -- pos_64K_x = floor(pixel_x * (2^16-1) / (screen_width-1) + 0.5) -- pixel_x = floor((pos_64K_x + (0.5 + 2^-16)) * (screen_width-1) / (2^16-1)) local pixels = floor((coord_64K + (0.5 + 2^-16)) * (size - 1) / 65535) if 65535 * pixels >= (coord_64K - (0.5 + 2^-16)) * (size - 1) then result = pixels end end xy_pixels[i] = result end if xy_pixels[1] and xy_pixels[2] then return xy_pixels[1], xy_pixels[2], xy_data[1][4], xy_data[2][4], xy_64K[1], xy_64K[2] elseif attempts > 0 then for _, data in ipairs(xy_data) do data[1] = {[0] = true} -- [1] = dict with used coord_64K values data[2] = 0 -- [2] = used coord_64K values qty data[3] = 45 * 225 -- [3] = counter of possible sizes data[4] = nil -- [4] = minimal possible size data[5] = 6 -- [5] = only pointer to next number (in 8 lowest bits) for j = 6, 229 do -- [6]..[230] = 53-bit numbers data[j] = (2^45 - 1) * 256 + 1 + j -- 8 lowest bits = index of the next number (last number points to idx=0) end -- 45 highest bits = flags (1 = size is possible, 0 = size is impossible) data[230] = (2^45 - 1) * 256 end local dx = xy_64K[1] < 2^15 and 1 or -1 local dy = xy_64K[2] < 2^15 and 1 or -1 local prev_coords_processed, prev_variants_qty for j = 1, 60 do for i = 1, 2 do local data, coord_64K = xy_data[i], xy_64K[i] local data_1 = data[1] if not data_1[coord_64K] then data_1[coord_64K] = true data[2] = data[2] + 1 local min_size local prev_idx = 5 local idx = data[prev_idx] while idx > 0 do local N = data[idx] local mask = 2^53 local size_from = idx * 45 + (150 - 6 * 45) for size = size_from, size_from + 44 do mask = mask / 2 if N >= mask then N = N - mask if 65535 * floor((coord_64K + (0.5 + 2^-16)) * (size - 1) / 65535) < (coord_64K - (0.5 + 2^-16)) * (size - 1) then data[idx] = data[idx] - mask data[3] = data[3] - 1 else min_size = min_size or size end end end if data[idx] < mask then data[prev_idx] = data[prev_idx] + (N - idx) else prev_idx = idx end idx = N end data[4] = min_size end end local coords_processed = xy_data[1][2] + xy_data[2][2] local variants_qty = xy_data[1][3] + xy_data[2][3] if variants_qty ~= prev_variants_qty then prev_variants_qty = variants_qty prev_coords_processed = coords_processed elseif coords_processed > prev_coords_processed + 30 then break end MoveMouseRelative(dx, dy) Sleep_orig(10) xy_64K[1], xy_64K[2] = GetMousePosition() end end end enabled = false print'Function "GetMousePositionInPixels()" failed to determine screen resolution and has been disabled' end return 0, 0, 0, 0, xy_64K[1], xy_64K[2] -- function is disabled, so the output lacks pixel information end end local update_internal_state do local RND = 0 local function mix16(n) n = ((n + 0xDEAD) % 2^16 + 1) * 0xBEEF % (2^16 + 1) - 1 local K53 = RND local L36 = K53 % 2^36 RND = L36 * 126611 + (K53 - L36) * (505231 / 2^36) + n % 256 * 598352261448 + n * 2348539529 end function update_internal_state(n, s) local t = GetRunningTime() mix16(t) if s then for j = 1, #s, 2 do local low, high = byte(s, j, j + 1) mix16(low + (high or 0) * 256) end mix16(n) local a, b, _, _, c, d = GetMousePositionInPixels() mix16(c) mix16(d) mix16(a) mix16(b) end mix16(t % 65521) local L32 = RND % 2^32 RND = (RND - L32) / 2^32 + L32 * 2^21 return RND end end local function random(m, n) -- drop-in replacement for math.random() local r = update_internal_state() if m then if not n then m, n = 1, m end return m + r % (n - m + 1) else return r * 2^-53 end end -- function Sleep() is redefined to automatically update internal state on every wake-up local function Sleep(ms) ms = ms and ceil(ms) or 10 -- 10 ms by default Sleep_orig(ms) update_internal_state(ms, "") -- this invocation adds enthropy to RNG (it's very fast) end local Logitech_order = {"L", "R", "M"} local Microsoft_order = {L=1, M=2, R=3, l=1, m=2, r=3} -- The following functions now accept strings "L", "R", "M" instead of button number local PressMouseButton_orig, ReleaseMouseButton_orig, PressAndReleaseMouseButton_orig, IsMouseButtonPressed_orig = PressMouseButton, ReleaseMouseButton, PressAndReleaseMouseButton, IsMouseButtonPressed local function PressMouseButton(button) PressMouseButton_orig(Microsoft_order[button] or button) end local function ReleaseMouseButton(button) ReleaseMouseButton_orig(Microsoft_order[button] or button) end local function PressAndReleaseMouseButton(button) PressAndReleaseMouseButton_orig(Microsoft_order[button] or button) end local function IsMouseButtonPressed(button) return IsMouseButtonPressed_orig(Microsoft_order[button] or button) end ---------------------------------------------------------------------- -- FUNCTIONS AND VARIABLES ---------------------------------------------------------------------- -- insert all your functions and variables here -- function OnEvent(event, arg, family) local mouse_button if event == "PROFILE_ACTIVATED" then EnablePrimaryMouseButtonEvents(true) ClearLog() --SetMouseDPITableIndex(2) -- set default mouse sensitivity update_internal_state(0, GetDate()) -- it takes about 1 second because of determining your screen resolution ---------------------------------------------------------------------- -- CODE FOR PROFILE ACTIVATION ---------------------------------------------------------------------- -- insert your code here (initialize variables, display "Hello" on LCD screen, turn some keyboard lamp on, etc.) -- elseif event == "MOUSE_BUTTON_PRESSED" or event == "MOUSE_BUTTON_RELEASED" then mouse_button = Logitech_order[arg] or arg -- convert 1,2,3 to "L","R","M" end update_internal_state(arg, sub(event, 21, 21)..sub(event, 1, 3)..sub(family, 1, 1)) -- this invocation adds enthropy to RNG (it's very fast) ---------------------------------------------------------------------- -- LOG THIS EVENT ---------------------------------------------------------------------- -- print( -- "event = '"..event.."'", -- not mouse_button and "arg = "..arg or "mouse_button = "..(type(mouse_button) == "number" and mouse_button or "'"..mouse_button.."'"), -- "family = '"..family.."'" -- ) -- if event == "PROFILE_DEACTIVATED" then EnablePrimaryMouseButtonEvents(false) ---------------------------------------------------------------------- -- CODE FOR PROFILE DEACTIVATION ---------------------------------------------------------------------- -- insert your code here (display "Bye!" on LCD screen, turn some keyboard lamp off, etc.) -- you have one second to do something before your script will be aborted -- end ---------------------------------------------------------------------- -- MOUSE EVENTS PROCESSING ---------------------------------------------------------------------- if event == "MOUSE_BUTTON_PRESSED" and mouse_button == "L" then -- left mouse button end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == "L" then -- left mouse button end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == "R" then -- right mouse button end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == "R" then -- right mouse button end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == "M" then -- middle mouse button end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == "M" then -- middle mouse button end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == 4 then -- "backward" (X1) mouse button end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == 4 then -- "backward" (X1) mouse button end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == 5 then -- "forward" (X2) mouse button end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == 5 then -- "forward" (X2) mouse button end if event == "MOUSE_BUTTON_PRESSED" and mouse_button == 6 then -- -- local k = random(5, 10) -- integer 5 <= k <= 10 -- local i = random(3) -- integer 1 <= i <= 3 -- local x = random() -- float 0 <= x < 1 -- print("random numbers:", k, i, x) -- -- local mouse_x, mouse_y, screen_width, screen_height = GetMousePositionInPixels() -- print("your screen size is "..screen_width.."x"..screen_height) -- print("your mouse cursor is at pixel ("..mouse_x..","..mouse_y..")") -- end if event == "MOUSE_BUTTON_RELEASED" and mouse_button == 6 then end ---------------------------------------------------------------------- -- KEYBOARD AND LEFT-HANDED-CONTROLLER EVENTS PROCESSING ---------------------------------------------------------------------- if event == "G_PRESSED" and arg == 1 then -- G1 key end if event == "G_RELEASED" and arg == 1 then -- G1 key end if event == "M_PRESSED" and arg == 3 then -- M3 key end if event == "M_RELEASED" and arg == 3 then -- M3 key end end