This repository has been archived by the owner on Mar 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
win32verstamp.py
200 lines (174 loc) · 6.75 KB
/
win32verstamp.py
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
""" Stamp a Win32 binary with version information.
"""
from win32api import BeginUpdateResource, UpdateResource, EndUpdateResource
import os
import struct
import glob
import optparse
VS_FFI_SIGNATURE = -17890115 # 0xFEEF04BD
VS_FFI_STRUCVERSION = 0x00010000
VS_FFI_FILEFLAGSMASK = 0x0000003f
VOS_NT_WINDOWS32 = 0x00040004
null_byte = "\0".encode("ascii") # str in py2k, bytes in py3k
#
# Set VS_FF_PRERELEASE and DEBUG if Debug
#
def file_flags(debug):
if debug:
return 3 # VS_FF_DEBUG | VS_FF_PRERELEASE
return 0
def file_type(is_dll):
if is_dll:
return 2 # VFT_DLL
return 1 # VFT_APP
def VS_FIXEDFILEINFO(maj, min, sub, build, debug=0, is_dll=1):
return struct.pack('lllllllllllll',
VS_FFI_SIGNATURE, # dwSignature
VS_FFI_STRUCVERSION, # dwStrucVersion
(maj << 16) | min, # dwFileVersionMS
(sub << 16) | build,# dwFileVersionLS
(maj << 16) | min, # dwProductVersionMS
(sub << 16) | build, # dwProductVersionLS
VS_FFI_FILEFLAGSMASK, # dwFileFlagsMask
file_flags(debug), # dwFileFlags
VOS_NT_WINDOWS32, # dwFileOS
file_type(is_dll), # dwFileType
0x00000000, # dwFileSubtype
0x00000000, # dwFileDateMS
0x00000000, # dwFileDateLS
)
def nullterm(s):
# get raw bytes for a NULL terminated unicode string.
return (str(s) + '\0').encode('unicode-internal')
def pad32(s, extra=2):
# extra is normally 2 to deal with wLength
l = 4 - ((len(s) + extra) & 3)
if l < 4:
return s + (null_byte * l)
return s
def addlen(s):
return struct.pack('h', len(s) + 2) + s
def String(key, value):
key = nullterm(key)
value = nullterm(value)
result = struct.pack('hh', len(value)//2, 1) # wValueLength, wType
result = result + key
result = pad32(result) + value
return addlen(result)
def StringTable(key, data):
key = nullterm(key)
result = struct.pack('hh', 0, 1) # wValueLength, wType
result = result + key
for k, v in data.items():
result = result + String(k, v)
result = pad32(result)
return addlen(result)
def StringFileInfo(data):
result = struct.pack('hh', 0, 1) # wValueLength, wType
result = result + nullterm('StringFileInfo')
# result = pad32(result) + StringTable('040904b0', data)
result = pad32(result) + StringTable('040904E4', data)
return addlen(result)
def Var(key, value):
result = struct.pack('hh', len(value), 0) # wValueLength, wType
result = result + nullterm(key)
result = pad32(result) + value
return addlen(result)
def VarFileInfo(data):
result = struct.pack('hh', 0, 1) # wValueLength, wType
result = result + nullterm('VarFileInfo')
result = pad32(result)
for k, v in data.items():
result = result + Var(k, v)
return addlen(result)
def VS_VERSION_INFO(maj, min, sub, build, sdata, vdata, debug=0, is_dll=1):
ffi = VS_FIXEDFILEINFO(maj, min, sub, build, debug, is_dll)
result = struct.pack('hh', len(ffi), 0) # wValueLength, wType
result = result + nullterm('VS_VERSION_INFO')
result = pad32(result) + ffi
result = pad32(result) + StringFileInfo(sdata) + VarFileInfo(vdata)
return addlen(result)
def stamp(pathname, options):
# For some reason, the API functions report success if the file is open
# but doesnt work! Try and open the file for writing, just to see if it is
# likely the stamp will work!
try:
f = open(pathname, "a+b")
f.close()
except IOError as why:
print("WARNING: File %s could not be opened - %s" % (pathname, why))
ver = options.version
try:
bits = [int(i) for i in ver.split(".")]
vmaj, vmin, vsub, vbuild = bits
except (IndexError, TypeError, ValueError):
raise ValueError("--version must be a.b.c.d (all integers) - got %r" % ver)
ifn = options.internal_name
if not ifn:
ifn = os.path.basename(pathname)
ofn = options.original_filename
if ofn is None:
ofn = os.path.basename(pathname)
sdata = {
'Comments' : options.comments,
'CompanyName' : options.company,
'FileDescription' : options.description,
'FileVersion' : ver,
'InternalName' : ifn,
'LegalCopyright' : options.copyright,
'LegalTrademarks' : options.trademarks,
'OriginalFilename' : ofn,
'ProductName' : options.product,
'ProductVersion' : ver,
}
vdata = {
'Translation' : struct.pack('hh', 0x409,1252),
}
is_dll = options.dll
if is_dll is None:
is_dll = os.path.splitext(pathname)[1].lower() in '.dll .pyd'.split()
is_debug = options.debug
if is_debug is None:
is_debug = os.path.splitext(pathname)[0].lower().endswith("_d")
# convert None to blank strings
for k, v in list(sdata.items()):
if v is None:
sdata[k] = ""
vs = VS_VERSION_INFO(vmaj, vmin, vsub, vbuild, sdata, vdata, is_debug, is_dll)
h = BeginUpdateResource(pathname, 0)
UpdateResource(h, 16, 1, vs)
EndUpdateResource(h, 0)
if options.verbose:
print("Stamped:", pathname)
if __name__ == '__main__':
parser = optparse.OptionParser("%prog [options] filespec ...",
description=__doc__)
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")
parser.add_option("", "--version", default="0.0.0.0",
help="The version number as m.n.s.b")
parser.add_option("", "--dll",
help="""Stamp the file as a DLL. Default is to look at the
file extension for .dll or .pyd.""")
parser.add_option("", "--debug", help="""Stamp the file as a debug binary.""")
parser.add_option("", "--product", help="""The product name to embed.""")
parser.add_option("", "--company", help="""The company name to embed.""")
parser.add_option("", "--trademarks", help="The trademark string to embed.")
parser.add_option("", "--comments", help="The comments string to embed.")
parser.add_option("", "--copyright",
help="""The copyright message string to embed.""")
parser.add_option("", "--description", metavar="DESC",
help="The description to embed.")
parser.add_option("", "--internal-name", metavar="NAME",
help="""The internal filename to embed. If not specified
the base filename is used.""")
parser.add_option("", "--original-filename",
help="""The original filename to embed. If not specified
the base filename is used.""")
options, args = parser.parse_args()
if not args:
parser.error("You must supply a file to stamp. Use --help for details.")
for g in args:
for f in glob.glob(g):
stamp(f, options)