Fix Print3D Toolbox: fix button alignment
[blender-addons.git] / game_engine_save_as_runtime.py
blobefc8690f75accaef5c1d6006f647a32cc6a9918f
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 'name': 'Save As Game Engine Runtime',
21 'author': 'Mitchell Stokes (Moguri)',
22 'version': (0, 3, 1),
23 "blender": (2, 61, 0),
24 'location': 'File > Export',
25 'description': 'Bundle a .blend file with the Blenderplayer',
26 'warning': '',
27 'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/'\
28 'Scripts/Game_Engine/Save_As_Runtime',
29 'tracker_url': 'https://projects.blender.org/tracker/index.php?'\
30 'func=detail&aid=23564',
31 'category': 'Game Engine'}
33 import bpy
34 import os
35 import sys
36 import shutil
37 import tempfile
40 def CopyPythonLibs(dst, overwrite_lib, report=print):
41 import platform
43 # use python module to find pytohn's libpath
44 src = os.path.dirname(platform.__file__)
46 # dst points to lib/, but src points to current python's library path, eg:
47 # '/usr/lib/python3.2' vs '/usr/lib'
48 # append python's library dir name to destination, so only python's
49 # libraries would be copied
50 if os.name == 'posix':
51 dst = os.path.join(dst, os.path.basename(src))
53 if os.path.exists(src):
54 write = False
55 if os.path.exists(dst):
56 if overwrite_lib:
57 shutil.rmtree(dst)
58 write = True
59 else:
60 write = True
61 if write:
62 shutil.copytree(src, dst, ignore=lambda dir, contents: [i for i in contents if i == '__pycache__'])
63 else:
64 report({'WARNING'}, "Python not found in %r, skipping pythn copy" % src)
67 def WriteAppleRuntime(player_path, output_path, copy_python, overwrite_lib):
68 # Enforce the extension
69 if not output_path.endswith('.app'):
70 output_path += '.app'
72 # Use the system's cp command to preserve some meta-data
73 os.system('cp -R "%s" "%s"' % (player_path, output_path))
75 bpy.ops.wm.save_as_mainfile(filepath=os.path.join(output_path, "Contents/Resources/game.blend"),
76 relative_remap=False,
77 compress=False,
78 copy=True,
81 # Python doesn't need to be copied for OS X since it's already inside blenderplayer.app
84 def WriteRuntime(player_path, output_path, copy_python, overwrite_lib, copy_dlls, report=print):
85 import struct
87 # Check the paths
88 if not os.path.isfile(player_path) and not(os.path.exists(player_path) and player_path.endswith('.app')):
89 report({'ERROR'}, "The player could not be found! Runtime not saved")
90 return
92 # Check if we're bundling a .app
93 if player_path.endswith('.app'):
94 WriteAppleRuntime(player_path, output_path, copy_python, overwrite_lib)
95 return
97 # Enforce "exe" extension on Windows
98 if player_path.endswith('.exe') and not output_path.endswith('.exe'):
99 output_path += '.exe'
101 # Get the player's binary and the offset for the blend
102 file = open(player_path, 'rb')
103 player_d = file.read()
104 offset = file.tell()
105 file.close()
107 # Create a tmp blend file (Blenderplayer doesn't like compressed blends)
108 tempdir = tempfile.mkdtemp()
109 blend_path = os.path.join(tempdir, bpy.path.clean_name(output_path))
110 bpy.ops.wm.save_as_mainfile(filepath=blend_path,
111 relative_remap=False,
112 compress=False,
113 copy=True,
116 # Get the blend data
117 blend_file = open(blend_path, 'rb')
118 blend_d = blend_file.read()
119 blend_file.close()
121 # Get rid of the tmp blend, we're done with it
122 os.remove(blend_path)
123 os.rmdir(tempdir)
125 # Create a new file for the bundled runtime
126 output = open(output_path, 'wb')
128 # Write the player and blend data to the new runtime
129 print("Writing runtime...", end=" ")
130 output.write(player_d)
131 output.write(blend_d)
133 # Store the offset (an int is 4 bytes, so we split it up into 4 bytes and save it)
134 output.write(struct.pack('B', (offset>>24)&0xFF))
135 output.write(struct.pack('B', (offset>>16)&0xFF))
136 output.write(struct.pack('B', (offset>>8)&0xFF))
137 output.write(struct.pack('B', (offset>>0)&0xFF))
139 # Stuff for the runtime
140 output.write(b'BRUNTIME')
141 output.close()
143 print("done")
145 # Make the runtime executable on Linux
146 if os.name == 'posix':
147 os.chmod(output_path, 0o755)
149 # Copy bundled Python
150 blender_dir = os.path.dirname(bpy.app.binary_path)
151 runtime_dir = os.path.dirname(output_path)
153 if copy_python:
154 print("Copying Python files...", end=" ")
155 py_folder = os.path.join(bpy.app.version_string.split()[0], "python", "lib")
156 dst = os.path.join(runtime_dir, py_folder)
157 CopyPythonLibs(dst, overwrite_lib, report)
158 print("done")
160 # And DLLs
161 if copy_dlls:
162 print("Copying DLLs...", end=" ")
163 for file in [i for i in os.listdir(blender_dir) if i.lower().endswith('.dll')]:
164 src = os.path.join(blender_dir, file)
165 dst = os.path.join(runtime_dir, file)
166 shutil.copy2(src, dst)
168 print("done")
170 from bpy.props import *
173 class SaveAsRuntime(bpy.types.Operator):
174 bl_idname = "wm.save_as_runtime"
175 bl_label = "Save As Game Engine Runtime"
176 bl_options = {'REGISTER'}
178 if sys.platform == 'darwin':
179 # XXX, this line looks suspicious, could be done better?
180 blender_bin_dir = '/' + os.path.join(*bpy.app.binary_path.split('/')[0:-4])
181 ext = '.app'
182 else:
183 blender_bin_path = bpy.app.binary_path
184 blender_bin_dir = os.path.dirname(blender_bin_path)
185 ext = os.path.splitext(blender_bin_path)[-1].lower()
187 default_player_path = os.path.join(blender_bin_dir, 'blenderplayer' + ext)
188 player_path = StringProperty(
189 name="Player Path",
190 description="The path to the player to use",
191 default=default_player_path,
192 subtype='FILE_PATH',
194 filepath = StringProperty(
195 subtype='FILE_PATH',
197 copy_python = BoolProperty(
198 name="Copy Python",
199 description="Copy bundle Python with the runtime",
200 default=True,
202 overwrite_lib = BoolProperty(
203 name="Overwrite 'lib' folder",
204 description="Overwrites the lib folder (if one exists) with the bundled Python lib folder",
205 default=False,
208 # Only Windows has dlls to copy
209 if ext == '.exe':
210 copy_dlls = BoolProperty(
211 name="Copy DLLs",
212 description="Copy all needed DLLs with the runtime",
213 default=True,
215 else:
216 copy_dlls = False
218 def execute(self, context):
219 import time
220 start_time = time.clock()
221 print("Saving runtime to %r" % self.filepath)
222 WriteRuntime(self.player_path,
223 self.filepath,
224 self.copy_python,
225 self.overwrite_lib,
226 self.copy_dlls,
227 self.report,
229 print("Finished in %.4fs" % (time.clock()-start_time))
230 return {'FINISHED'}
232 def invoke(self, context, event):
233 if not self.filepath:
234 ext = '.app' if sys.platform == 'darwin' else os.path.splitext(bpy.app.binary_path)[-1]
235 self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ext)
237 wm = context.window_manager
238 wm.fileselect_add(self)
239 return {'RUNNING_MODAL'}
242 def menu_func(self, context):
243 self.layout.operator(SaveAsRuntime.bl_idname)
246 def register():
247 bpy.utils.register_module(__name__)
249 bpy.types.INFO_MT_file_export.append(menu_func)
252 def unregister():
253 bpy.utils.unregister_module(__name__)
255 bpy.types.INFO_MT_file_export.remove(menu_func)
258 if __name__ == "__main__":
259 register()