Browse Source

Add binary operations for lua

This is something I was always missing - posibbility to operate on
binary files or streams in pure lua. In most cases we do only need to
read simple variables from files such as integers with different amount
of bytes. This "class" will provide that ability.

It's a simple implementation of following C module for lua:
http://www.inf.puc-rio.br/~roberto/struct/

It has much less, though. Following elements have been implemented:

">" flag to set mode to big endian.
"<" flag to set mode to little endian.
"b" a signed char.
"B" an unsigned char.
"h" a signed short (2 bytes).
"H" an unsigned short (2 bytes).
"i" a signed int (4 bytes).
"I" an unsigned int (4 bytes).
"l" a signed long (8 bytes).
"L" an unsigned long (8 bytes).
"s" a zero-terminated string.

An example how to use it:

```lua
local packed = Struct.pack('<LIhBsb', 1234567891, 123456789,
-3200, 255, 'Test message', -1)
-- packed is now a lua string we can save to file as binary data
local L, I, h, B, s, b = Struct.unpack('<LIhBsb', packed)
print(L, I, h, B, s, b)
```

You can use g_resources.readFileContents as function to read binary
files and parse them via this class.
Konrad Kuśnierz 6 years ago
parent
commit
7ea6c46b2c
2 changed files with 87 additions and 0 deletions
  1. 1
    0
      modules/corelib/corelib.otmod
  2. 86
    0
      modules/corelib/struct.lua

+ 1
- 0
modules/corelib/corelib.otmod View File

@@ -10,6 +10,7 @@ Module
10 10
     dofile 'string'
11 11
     dofile 'table'
12 12
     dofile 'bitwise'
13
+    dofile 'struct'
13 14
 
14 15
     dofile 'const'
15 16
     dofile 'util'

+ 86
- 0
modules/corelib/struct.lua View File

@@ -0,0 +1,86 @@
1
+Struct = {}
2
+
3
+function Struct.pack(format, ...)
4
+  local stream = ''
5
+  local vars = {...}
6
+  local endianness = true
7
+
8
+  for i = 1, string.len(format) do
9
+    local opt = string.sub(format, i, i)
10
+
11
+    if opt == '<' or opt == '>' then
12
+      endianness = opt == '<' and true or false
13
+    elseif opt == 'b' or opt == 'B' or opt == 'h' or opt == 'H' or opt == 'i' or opt == 'I' or opt == 'l' or opt == 'L' then
14
+      local val = tonumber(table.remove(vars, 1))
15
+      local n = ((opt == 'h' or opt == 'H') and 2) or ((opt == 'i' or opt == 'I') and 4) or ((opt == 'l' or opt == 'L') and 8) or 1
16
+
17
+      if val < 0 then
18
+        val = val + 2 ^ (n * 8 - 1)
19
+      end
20
+
21
+      local binary = ''
22
+      for j = 1, n do
23
+        binary = binary .. string.char(val % (2 ^ 8))
24
+        val = math.floor(val / (2 ^ 8))
25
+      end
26
+
27
+      if not endianness then
28
+        binary = string.reverse(binary)
29
+      end
30
+
31
+      stream = stream .. binary
32
+    elseif opt == 's' then
33
+      stream = stream .. tostring(table.remove(vars, 1))
34
+      stream = stream .. string.char(0)
35
+    end
36
+  end
37
+
38
+  return stream
39
+end
40
+
41
+function Struct.unpack(format, stream)
42
+  local vars = {}
43
+  local endianness = true
44
+
45
+  for i = 1, string.len(format) do
46
+    local opt = string.sub(format, i, i)
47
+
48
+    if opt == '<' or opt == '>' then
49
+      endianness = opt == '<' and true or false
50
+    elseif opt == 'b' or opt == 'B' or opt == 'h' or opt == 'H' or opt == 'i' or opt == 'I' or opt == 'l' or opt == 'L' then
51
+      local n = ((opt == 'h' or opt == 'H') and 2) or ((opt == 'i' or opt == 'I') and 4) or ((opt == 'l' or opt == 'L') and 8) or 1
52
+      local signed = opt == 'b' or opt == 'h' or opt == 'i'
53
+
54
+      local val = 0
55
+      for j = 1, n do
56
+        local byte = string.byte(string.sub(stream, 1, 1))
57
+        if endianness then
58
+          val = val + byte * (2 ^ ((j - 1) * 8))
59
+        else
60
+          val = val + byte * (2 ^ ((n - j) * 8))
61
+        end
62
+        stream = string.sub(stream, 2)
63
+      end
64
+
65
+      if signed then
66
+        val = val - 2 ^ (n * 8 - 1)
67
+      end
68
+
69
+      table.insert(vars, val)
70
+    elseif opt == 's' then
71
+      local str = ''
72
+      for j = 1, string.len(stream) do
73
+        if string.sub(stream, j, j) == string.char(0) then
74
+          break
75
+        end
76
+
77
+        str = str .. string.sub(stream, j, j)
78
+      end
79
+
80
+      stream = string.sub(stream, string.len(str) + 2)
81
+      table.insert(vars, str)
82
+    end
83
+  end
84
+
85
+  return unpack(vars)
86
+end

Loading…
Cancel
Save