Skip to content

Instantly share code, notes, and snippets.

@Egor-Skriptunoff
Last active December 17, 2023 11:31
Show Gist options
  • Save Egor-Skriptunoff/be9a7e4546b47b74a9aae604f5a8c272 to your computer and use it in GitHub Desktop.
Save Egor-Skriptunoff/be9a7e4546b47b74a9aae604f5a8c272 to your computer and use it in GitHub Desktop.
Template script file for Logitech Gaming Software
---------------------------------------------------------------------------------------------
-- 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 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)
--
-- ------------------------------------------------------------------------------------------
-- 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(...)
print_orig(...)
local t = {...}
for j = 1, select("#", ...) do
t[j] = tostring(t[j])
end
OutputLogMessage("%s\n", 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
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]
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 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
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_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]
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 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 GetEntropyCounter
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 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
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 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 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
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)
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
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 entropy 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
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
-- 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
----------------------------------------------------------------------
-- insert all your functions and variables here
--
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 entropy 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, etc.)
-- please note that you have only 1 second 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
-- (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 "..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
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
----------------------------------------------------------------------
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
----------------------------------------------------------------------
-- 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
@magnapeccatrix
Copy link

@Egor-Skriptunoff thank you for your reply, I have two game profiles that I pasted your script on both of profiles because I don't know if it's possible to have it load once and activated for all different game profiles. So everytime I open one of two games or alt tab between them, script is loaded again if I enable LGS to auto detect game. Sometimes, it crashes on switching only once. I have one of the early G502 which shows anomaly such as if I lower its polling rate, cursor is accelerated and vice versa. Could be my mouse is faulty and can not handle script well.

I wanted to include some extra info if it helps something, thank you again Egor for your script.

@Egor-Skriptunoff
Copy link
Author

@magnapeccatrix -

I don't know if it's possible to have it load once and activated for all different game profiles.

No.
It looks like LGS creates new Lua VM on every profile switch.

The behavior of the script does not depend on the polling rate of your mouse.

If you don't need functions Get/SetMousePositionInPixels you may remove lines 172-275 from the script or replace them with single statement Sleep(100). This might help switching profiles faster, but I did not test it.

@DanEdens
Copy link

DanEdens commented Nov 4, 2022

Thanks! This is a fantastic head start for what I'm after. cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment