Repository navigation
Expand file tree
/
Copy pathcompile-web-assets.py
More file actions
executable file
·146 lines (115 loc) · 3.63 KB
/
Copy pathcompile-web-assets.py
File metadata and controls
executable file
·146 lines (115 loc) · 3.63 KB
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env python
import os
import sys
import itertools
MAX_LITERAL_SIZE = 65535
MAX_LINE_LENGTH = 82
def main():
try:
assets_root = sys.argv[1]
assert os.path.isdir(assets_root)
except:
print("Error: First argument must be a directory", file=sys.stderr)
sys.exit(1)
# List all the files in assets_root
assets = [
os.path.relpath(os.path.join(root, path), assets_root)
for root, __, paths in os.walk(assets_root)
for path in paths
]
# Write the encoded files and an index to stdout
print(prelude)
write_assets(assets_root, assets)
prelude = """
// Generated by scripts/compile-web-assets.py
#include <map>
#include <string>
"""
def write_assets(asset_root, assets):
print('std::map<std::string, const std::string> static_web_assets = {')
for i, asset in enumerate(assets):
print(' { ' + encode('/' + asset) + ', std::string(', end='')
data = open(os.path.join(asset_root, asset), "rb").read()
position = 0 # track the position to keep lines short
trigraph = 0 # track consecutive question marks to avoid writing trigraphs
prev_e = None # track the previous character to avoid tacking on hex digits
literal_size = 0 # number of characters in the current string literal
for c in data:
c = byte(c)
literal_size += 1
if position == 0:
print('\n "', end='')
position = 7
trigraph = 0
prev_e = None
if trigraph >= 2 and c in b"=/'()!<>-":
# split a trigraph
print('" "', end='')
prev_e = None
position += 3
trigraph = 0
elif c == b'?':
# count the amount of question marks
trigraph += 1
else:
trigraph = 0
e = encode_char(c, prev_e)
prev_e = e
print(e, end='')
position += len(e)
if position > MAX_LINE_LENGTH or c == b'\n':
# end a line if it gets too long and on newlines
print('"', end='')
position = 0
if literal_size > MAX_LITERAL_SIZE - MAX_LINE_LENGTH:
print(',')
print(' ' + str(literal_size) + ') + std::string(', end='')
literal_size = 0
if position != 0:
print('"', end='')
if not data:
print('""', end='')
if literal_size:
print(',')
print(' ' + str(literal_size) + ' ) },')
else:
print('"") },')
print('};')
def encode(string):
return ''.join(
['"'] +
[encode_char(byte(c)) for c in string.encode('utf-8')]
+ ['"']
)
c_escapes = {
b'\a': '\\a',
b'\b': '\\b',
b'\f': '\\f',
b'\n': '\\n',
b'\r': '\\r',
b'\t': '\\t',
b'\v': '\\v',
b'\\': '\\\\',
b'"': '\\"',
}
# The input character c should be a single character bytes
# The return value is an ascii-compatible unicode string
def encode_char(c, previous=None):
avoid_hex = previous and previous[0] == '\\' and previous[1] in 'x01234567'
n = ord(c)
if c in c_escapes:
return c_escapes[c]
if n < 8:
return '\\' + str(n)
elif 32 <= n and n < 127 and not (avoid_hex and c in b'01234567890abcdefABCDEF'):
return c.decode('ascii')
else:
return '\\x%x' % n
if sys.version < '3':
def byte(b):
return b
else:
def byte(b):
return bytes([b])
if __name__ == "__main__":
main()