-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnukey.rb
123 lines (121 loc) · 4.87 KB
/
nukey.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
MAGIC = 0xADFCCFDA
def which(cmd)
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
exts.each { |ext|
exe = File.join(path, "#{cmd}#{ext}")
return exe if File.executable?(exe) && !File.directory?(exe)
}
end
nil
end
if __FILE__ == $PROGRAM_NAME
# Try to find nasm, which is used for assembling
# the keymap sources into a format processable by nukey.
nasm = which 'nasm'
unless nasm
# Exit if nasm can't be found.
puts 'Unable to find `nasm` executable.'
exit 1
end
Dir.mkdir 'keymaps' unless Dir.exists? 'keymaps'
# Find all assembly sources in the ./sources directory.
# Those are the keymaps, in a format readable by nasm.
Dir.glob('sources/*.{s,asm}') do |file_name|
key_layout = File.basename file_name, '.*'
print "[#{key_layout}] Assembling..."
# Compile the current keymap with nasm.
# nasm flags:
# -Oo = Apply no optimizations
# -Fnull = Include no debug information
# -fobj = Produce a flat binary
status = system("\"#{nasm}\" -O0 -Fnull -fobj -o\"keymaps/#{key_layout}.bin\" \"#{file_name}\"")
unless status
puts "FAILED"
puts "[#{key_layout}] nasm invocation failed."
exit 1
end
puts "OK"
end
# Find all flat binaries produced in the previous step.
Dir.glob('keymaps/*.bin') do |file_name|
# Get the name of the current key layout.
key_layout = File.basename file_name, '.*'
offset = 0
found = false
contents = File.read(file_name).bytes.to_a
File.delete file_name
print "[#{key_layout}] Validating..."
while offset < contents.size
# Test whether the next four bytes are equal to the magic number.
# If they are, we've found our entry point.
found = true
found &= contents[offset + 0] == (MAGIC >> 0x18) & 0xFF
found &= contents[offset + 1] == (MAGIC >> 0x10) & 0xFF
found &= contents[offset + 2] == (MAGIC >> 0x08) & 0xFF
found &= contents[offset + 3] == (MAGIC >> 0x00) & 0xFF
break if found
offset += 1
end
# Add four to the offset to skip the magic number.
offset += 4
unless found
# Stop processing this keymap if the magic number couldn't be found.
puts "FAILED"
puts "[#{key_layout}] Failed to validate! Skipping."
break
end
puts "OK"
print "[#{key_layout}] Creating Crystal source..."
max_offset = [contents.size, offset + 256].min
# Construct an empty string for the keymap and append some
# boostrap code to it, to make it work with Crystal.
keymap = "KEYMAP_#{key_layout.upcase} = "
keymap << '"'
# Iterate over the key codes.
while offset < max_offset
keycode = contents[offset]
# There are special cases where the actual ascii character
# of the keycode can be embedded into the string directly.
if true &&
keycode >= 32 && # greater than or equal to ' '
keycode <= 126 && # less than or equal to '~'
keycode != 34 && # not '"'
keycode != 92 # not '\\'
# Embed the ascii character into the keymap
keymap << keycode.chr
elsif keycode == 8 # '\b'
keymap << "\\b"
elsif keycode == 9 # '\t'
keymap << "\\t"
elsif keycode == 27 # '\e'
keymap << "\\e"
else
# If the aforementioned special cases don't apply,
# an octal escape will be used to embed the keycode
# into the string. However, this can lead to issues
# if the next keycode is embedded as an ascii character
# and it starts with a number.
keycode_oct = keycode.to_s 8
if offset + 1 < max_offset
# Read the next keycode.
next_keycode = contents[offset + 1]
if next_keycode >= 32 && next_keycode <= 126
# The special cases probably apply for the next keycode,
# so we justify its octal representation with zeroes.
keymap << "\\#{keycode_oct.rjust 3, '0'}"
offset += 1
next
end
end
# Embed the keycode with an octal escape.
keymap << "\\#{keycode_oct}"
end
offset += 1
end
keymap << '"'
# Create a Crystal source file from the keymap.
File.write("keymaps/#{key_layout}.cr", keymap)
puts "OK"
end
end