1 """Spells for dumping particular blocks from nifs."""
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 import BaseHTTPServer
43 import ntpath
44 import os
45 import tempfile
46 import types
47 import webbrowser
48 from xml.sax.saxutils import escape
49
50 from pyffi.formats.nif import NifFormat
51 from pyffi.spells.nif import NifSpell
52
53 -def tohex(value, nbytes=4):
54 """Improved version of hex."""
55 return ("0x%%0%dX" % (2*nbytes)) % (long(str(value)) & (2**(nbytes*8)-1))
56
58 """Format an array.
59
60 :param arr: An array.
61 :type arr: L{pyffi.object_models.xml.array.Array}
62 :return: String describing the array.
63 """
64 text = ""
65 if arr._count2 == None:
66 for i, element in enumerate(list.__iter__(arr)):
67 if i > 16:
68 text += "etc...\n"
69 break
70 text += "%i: %s\n" % (i, dumpAttr(element))
71 else:
72 k = 0
73 for i, elemlist in enumerate(list.__iter__(arr)):
74 for j, elem in enumerate(list.__iter__(elemlist)):
75 if k > 16:
76 text += "etc...\n"
77 break
78 text += "%i, %i: %s\n" % (i, j, dumpAttr(elem))
79 k += 1
80 if k > 16:
81 break
82 return text if text else "None"
83
85 """Return formatted string for block without following references.
86
87 :param block: The block to print.
88 :type block: L{NifFormat.NiObject}
89 :return: String string describing the block.
90 """
91 text = '%s instance at 0x%08X\n' % (block.__class__.__name__, id(block))
92 for attr in block._get_filtered_attribute_list():
93 attr_str_lines = \
94 dumpAttr(getattr(block, "_%s_value_" % attr.name)).splitlines()
95 if len(attr_str_lines) > 1:
96 text += '* %s :\n' % attr.name
97 for attr_str in attr_str_lines:
98 text += ' %s\n' % attr_str
99 elif attr_str_lines:
100 text += '* %s : %s\n' % (attr.name, attr_str_lines[0])
101 else:
102 text = '* %s : <None>\n' % attr.name
103 return text
104
106 """Format an attribute.
107
108 :param attr: The attribute to print.
109 :type attr: (anything goes)
110 :return: String for the attribute.
111 """
112 if isinstance(attr, (NifFormat.Ref, NifFormat.Ptr)):
113 ref = attr.get_value()
114 if ref:
115 if (hasattr(ref, "name")):
116 return "<%s:%s:0x%08X>" % (ref.__class__.__name__,
117 ref.name, id(attr))
118 else:
119 return "<%s:0x%08X>" % (ref.__class__.__name__,id(attr))
120 else:
121 return "<None>"
122 elif isinstance(attr, list):
123 return dumpArray(attr)
124 elif isinstance(attr, NifFormat.NiObject):
125 raise TypeError("cannot dump NiObject as attribute")
126 elif isinstance(attr, NifFormat.byte):
127 return tohex(attr.get_value(), 1)
128 elif isinstance(attr, (NifFormat.ushort, NifFormat.short)):
129 return tohex(attr.get_value(), 2)
130 elif isinstance(attr, (NifFormat.int, NifFormat.uint)):
131 return tohex(attr.get_value(), 4)
132 elif isinstance(attr, (types.IntType, types.LongType)):
133 return tohex(attr, 4)
134 else:
135 return str(attr)
136
147
149 """Dump the texture and material info of all geometries."""
150
151 SPELLNAME = "dump_tex"
152
158
159 - def branchentry(self, branch):
160 if isinstance(branch, NifFormat.NiTexturingProperty):
161 for textype in ('base', 'dark', 'detail', 'gloss', 'glow',
162 'bump_map', 'decal_0', 'decal_1', 'decal_2',
163 'decal_3'):
164 if getattr(branch, 'has_%s_texture' % textype):
165 texdesc = getattr(branch,
166 '%s_texture' % textype)
167 if texdesc.source:
168 if texdesc.source.use_external:
169 filename = texdesc.source.file_name
170 else:
171 filename = '(pixel data packed in file)'
172 else:
173 filename = '(no texture file)'
174 self.toaster.msg("[%s] %s" % (textype, filename))
175 self.toaster.msg("apply mode %i" % branch.apply_mode)
176
177 return False
178 elif isinstance(branch, NifFormat.NiMaterialProperty):
179 for coltype in ['ambient', 'diffuse', 'specular', 'emissive']:
180 col = getattr(branch, '%s_color' % coltype)
181 self.toaster.msg('%-10s %4.2f %4.2f %4.2f'
182 % (coltype, col.r, col.g, col.b))
183 self.toaster.msg('glossiness %f' % branch.glossiness)
184 self.toaster.msg('alpha %f' % branch.alpha)
185
186 return False
187 else:
188
189 return True
190
192 """Make a html report of selected blocks."""
193
194 SPELLNAME = "dump_htmlreport"
195 ENTITIES = { "\n": "<br/>" }
196
197 @classmethod
198 - def toastentry(cls, toaster):
199
200 toaster.reports_per_blocktype = {}
201
202 return True
203
208
209 - def branchentry(self, branch):
210
211 if not NifSpell._branchinspect(self, branch):
212 return True
213 blocktype = branch.__class__.__name__
214 reports = self.toaster.reports_per_blocktype.get(blocktype)
215 if not reports:
216
217 row = "<tr>"
218 row += "<th>%s</th>" % "file"
219 row += "<th>%s</th>" % "id"
220 for attr in branch._get_filtered_attribute_list(data=self.data):
221 row += ("<th>%s</th>"
222 % escape(attr.displayname, self.ENTITIES))
223 row += "</tr>"
224 reports = [row]
225 self.toaster.reports_per_blocktype[blocktype] = reports
226
227 row = "<tr>"
228 row += "<td>%s</td>" % escape(self.stream.name)
229 row += "<td>%s</td>" % escape("0x%08X" % id(branch), self.ENTITIES)
230 for attr in branch._get_filtered_attribute_list(data=self.data):
231 row += ("<td>%s</td>"
232 % escape(dumpAttr(getattr(branch, "_%s_value_"
233 % attr.name)),
234 self.ENTITIES))
235 row += "</tr>"
236 reports.append(row)
237
238 return True
239
240 @classmethod
242 if toaster.reports_per_blocktype:
243 rows = []
244 rows.append( "<head>" )
245 rows.append( "<title>Report</title>" )
246 rows.append( "</head>" )
247 rows.append( "<body>" )
248
249 for blocktype, reports in toaster.reports_per_blocktype.iteritems():
250 rows.append("<h1>%s</h1>" % blocktype)
251 rows.append('<table border="1" cellspacing="0">')
252 rows.append("\n".join(reports))
253 rows.append("</table>")
254
255 rows.append("</body>")
256
257 cls.browser("\n".join(rows))
258 else:
259 toaster.msg('No Report Generated')
260
261 @classmethod
263 """Display html in the default web browser without creating a
264 temp file.
265
266 Instantiates a trivial http server and calls webbrowser.open
267 with a URL to retrieve html from that server.
268 """
269 class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
270 def do_GET(self):
271 bufferSize = 1024*1024
272 for i in xrange(0, len(htmlstr), bufferSize):
273 self.wfile.write(htmlstr[i:i+bufferSize])
274
275 server = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), RequestHandler)
276 webbrowser.open('http://127.0.0.1:%s' % server.server_port)
277 server.handle_request()
278
280 """Export embedded images as DDS files. If the toaster's
281 ``--dryrun`` option is enabled, the image is written to a
282 temporary file, otherwise, if no further path information is
283 stored in the nif, it is written to
284 ``<nifname>-pixeldata-<n>.dds``. If a path is stored in the nif,
285 then the original file path is used.
286
287 The ``--arg`` option is used to strip the folder part of the path
288 and to replace it with something else (this is sometimes useful,
289 such as in Bully nft files).
290
291 The file extension is forced to ``.dds``.
292 """
293
294 SPELLNAME = "dump_pixeldata"
295
297 NifSpell.__init__(self, *args, **kwargs)
298 self.pixeldata_counter = 0
299 """Increments on each pixel data block."""
300
303
310
311 - def branchentry(self, branch):
312
313 if (isinstance(branch, NifFormat.NiSourceTexture)
314 and branch.pixel_data and branch.file_name):
315 self.save_as_dds(branch.pixel_data, branch.file_name)
316 return False
317 elif isinstance(branch, NifFormat.ATextureRenderData):
318 filename = "%s-pixeldata-%i" % (
319 os.path.basename(self.stream.name),
320 self.pixeldata_counter)
321 self.save_as_dds(branch, filename)
322 self.pixeldata_counter += 1
323 return False
324 else:
325
326 return True
327
328 @classmethod
330 """We do not toast the original file, so stream construction
331 is delegated to :meth:`get_toast_pixeldata_stream`.
332 """
333 if test_exists:
334 return False
335 else:
336 return None
337
338 @staticmethod
340 r"""Transform NiSourceTexture.file_name into something workable.
341
342 >>> SpellExportPixelData.get_pixeldata_head_root("test.tga")
343 ('', 'test')
344 >>> SpellExportPixelData.get_pixeldata_head_root(r"textures\test.tga")
345 ('textures', 'test')
346 >>> SpellExportPixelData.get_pixeldata_head_root(
347 ... r"Z:\Bully\Temp\Export\Textures\Clothing\P_Pants1\P_Pants1_d.tga")
348 ('z:/bully/temp/export/textures/clothing/p_pants1', 'p_pants1_d')
349 """
350
351
352 head, tail = ntpath.split(texture_filename)
353 root, ext = ntpath.splitext(tail)
354
355 head = head.lower()
356 root = root.lower()
357
358
359
360
361
362
363
364 head = head.replace("\\", "/")
365 return (head, root) if root else ("", "image")
366
391
403