forked from Marginal/OverlayEditor
/
importobjs.py
278 lines (256 loc) · 11.9 KB
/
importobjs.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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
import codecs
import wx
from glob import glob
from hashlib import md5
from os import listdir
from os.path import abspath, basename, dirname, exists, isdir, join, normpath, sep, splitext
from shutil import copy2
from sys import platform
from tempfile import gettempdir
if __debug__:
from traceback import print_exc
from clutterdef import ObjectDef, AutoGenPointDef, PolygonDef
from MessageBox import myMessageBox
from prefs import prefs, gcustom
# 2.5-only version of case-insensitive sort for str or unicode
def sortfolded(seq):
seq.sort(key=lambda x: x.lower())
def objpaths(pkgpath, srcpath):
# find base texture location
if srcpath.lower().endswith(sep+'custom objects'):
oldtexpath=srcpath[:srcpath.lower().index(sep+'custom objects')]
for t in listdir(oldtexpath):
if t.lower()=='custom object textures': break
else:
t='custom object textures'
oldtexpath=join(oldtexpath, t)
elif srcpath.lower().endswith(sep+'autogen objects'):
oldtexpath=srcpath[:srcpath.lower().index(sep+'autogen objects')]
for t in listdir(oldtexpath):
if t.lower()=='autogen textures': break
else:
t='AutoGen textures'
oldtexpath=join(oldtexpath, t)
else:
oldtexpath=srcpath
# find destination
for o in listdir(pkgpath):
if o.lower()=='custom objects':
newpath=join(pkgpath, o)
for t in listdir(pkgpath):
if t.lower()=='custom object textures': break
else:
t='custom object textures'
newtexpath=join(pkgpath, t)
newtexprefix=''
break
else:
for o in listdir(pkgpath):
if o.lower()=='objects':
newpath=join(pkgpath, o)
for t in listdir(pkgpath):
# only if "textures" folder exists
if t.lower()=='textures':
newtexpath=join(pkgpath, t)
newtexprefix='../'+t+'/'
break
else:
newtexpath=newpath
newtexprefix=''
break
else:
newpath=newtexpath=pkgpath
newtexprefix=''
return (srcpath, oldtexpath, newpath, newtexpath, newtexprefix)
# make a list of (src, dst). Exclude existing but unchanged textures.
def importpaths(pkgpath, paths):
retval=[]
(oldpath, oldtexpath, newpath, newtexpath, newtexprefix)=objpaths(pkgpath, dirname(paths[0]))
pathno = 0
while pathno < len(paths):
path = paths[pathno]
if isdir(path):
raise IOError(0, "Can't import an entire folder; select individual files instead.", path)
if path.lower().startswith(pkgpath.lower()):
raise IOError(0, "Can't import objects from the same package!", path)
basename(path).decode() # names must be ASCII only - may raise UnicodeError
(name,ext)=splitext(basename(path))
if ext.lower() in ['.dds', '.png']:
# Importing a texture as a draped polygon
f=join(gettempdir(), name+'.pol')
h=file(f, 'wt')
h.write((platform=='darwin' and 'A\n' or 'I\n') + '850\nDRAPED_POLYGON\n\nTEXTURE_NOWRAP\t%s\nSCALE\t25 25\n' % basename(path))
h.close()
retval.append((f, join(newpath, name+'.pol')))
for f in glob(join(oldpath, name+'.[DdPp][DdNn][SsGg]')):
n=join(newtexpath, basename(f))
if not samefile(f, n):
retval.append((f, n))
elif ext.lower() not in [ObjectDef.OBJECT, AutoGenPointDef.AGP, PolygonDef.FOREST, PolygonDef.FACADE, PolygonDef.LINE, PolygonDef.DRAPED]:
# we can only handle non-compound types
raise IOError, (0, "Can't import this type of file.", path)
else:
# Import some kind of object
retval.append((path, join(newpath, basename(path))))
badobj=(0, "This is not a valid X-Plane file.", path)
h=codecs.open(path, 'rU', 'latin1')
if not h.readline()[0] in ['I','A']: raise IOError, badobj
c=h.readline().split()
if not c: raise IOError, badobj
version=c[0]
if not version in ['2', '700', '800', '850', '1000']: raise IOError, badobj
if version!='2':
c=h.readline().split()
if not c or not (c[0]=='OBJ' or
(c[0]=='AG_POINT' and version=='1000') or
(c[0]=='FACADE' and version in ['800','1000']) or
(c[0]=='FOREST' and version=='800') or
(c[0]=='LINE_PAINT' and version=='850') or
(c[0]=='DRAPED_POLYGON' and version=='850')):
raise IOError, (0, "Can't import this type of file", path)
if version in ['2','700']:
while True:
line=h.readline()
if not line: raise IOError, badobj
line=line.strip()
if line:
if '//' in line:
tex=line[:line.index('//')].strip()
else:
tex=line.strip()
tex=splitext(tex.replace(':',sep).replace('\\',sep))[0]
for f in glob(join(oldtexpath, tex+'.[DdPp][DdNn][SsGg]')):
n=join(newtexpath, basename(f))
if (f, n) not in retval and not samefile(f, n):
retval.append((f, n))
for f in glob(join(oldtexpath, tex+'_LIT.[DdPp][DdNn][SsGg]')):
n=join(newtexpath, basename(f))
if (f, n) not in retval and not samefile(f, n):
retval.append((f, n))
break
else: # v8.x
while True:
line=h.readline()
if not line: break
c=line.split()
if len(c)<2:
pass
elif c[0] in ['TEXTURE', 'TEXTURE_NOWRAP', 'TEXTURE_LIT', 'TEXTURE_LIT_NOWRAP', 'TEXTURE_NORMAL', 'TEXTURE_NORMAL_NOWRAP', 'TEXTURE_DRAPED', 'TEXTURE_DRAPED_NORMAL']:
tex=splitext(line.strip()[len(c[0]):].strip().replace(':',sep).replace('\\',sep))[0]
for f in glob(join(oldtexpath, tex+'.[DdPp][DdNn][SsGg]')):
n=join(newtexpath, basename(f))
if (f, n) not in retval and not samefile(f, n):
retval.append((f, n))
elif c[0] in ['VEGETATION','OBJECT','FACADE', # .agp
'OBJ', 'ROOF_OBJ']: # v10 facade
# child object
if exists(join(dirname(path), c[1])):
paths.append(join(dirname(path), c[1]))
elif __debug__:
# skip missing children - might be a library object, but if not then a UI becoms cumbersome
print 'skipping %s in %s' % (c[1], path)
elif c[0]=='VT':
break # Stop at first vertex
h.close()
pathno += 1
return retval
# Actually copy the files. File contents will have been validated above, so we just need to update texture paths.
def importobjs(pkgpath, files):
(oldpath, oldtexpath, newpath, newtexpath, newtexprefix)=objpaths(pkgpath, dirname(files[0][0]))
if not isdir(newtexpath): mkdir(newtexpath)
for (src, dst) in files:
if splitext(src)[1].lower() in ['.dds', '.png']:
copy2(src, dst)
continue
# Preserve comments, copyrights etc
h=file(src, 'rU')
w=file(dst, 'wt')
line=h.readline().strip() # A or I
w.write((platform=='darwin' and 'A' or 'I')+line[1:]+'\n')
line=h.readline().strip() # version
w.write(line+'\n')
version=line.split()[0]
if version!='2':
w.write(h.readline().strip()+'\n') # file type
if version in ['2','700']:
while True:
line=h.readline().strip() # texture
if not line:
w.write('\n')
elif '//' in line:
w.write(newtexprefix+basename(line[:line.index('//')].strip().replace(':',sep).replace('\\',sep))+'\t'+line[line.index('//'):]+'\n')
break
else:
w.write(newtexprefix+basename(line.replace(':',sep).replace('\\',sep))+'\t//\n')
break
else: # v8.x
while True:
line=h.readline()
if not line: break
c=line.split()
if len(c)<2:
w.write(line)
elif c[0] in ['TEXTURE', 'TEXTURE_NOWRAP', 'TEXTURE_LIT', 'TEXTURE_LIT_NOWRAP', 'TEXTURE_NORMAL', 'TEXTURE_NORMAL_NOWRAP', 'TEXTURE_DRAPED', 'TEXTURE_DRAPED_NORMAL']:
w.write(c[0]+'\t'+newtexprefix+basename(line.strip()[len(c[0]):].strip().replace(':',sep).replace('\\',sep))+'\n')
elif c[0]=='VT':
w.write(line)
break # Stop at first vertex
else:
w.write(line)
for line in h:
w.write(line)
w.close()
h.close()
# Are the contents of two files equal? Returns True if src does not exist.
def samefile(src, dst):
# Compare by hashing. Choice of hash is unimportant so we use MD5 for speed with a low chance of collision.
if not exists(src): return True
try:
digest=[]
for f in [dst, src]:
h=file(f, 'rb')
d=h.read()
h.close()
digest.append(md5(d).hexdigest())
return digest[0]==digest[1]
except IOError:
return False
# import files
# returns list of files that need loading into the palette, or True if a full reload is required
def doimport(paths, palette):
if not paths or not prefs.package: return False
pkgpath = glob(join(prefs.xplane, gcustom, prefs.package))[0]
try:
files=importpaths(pkgpath, paths)
except EnvironmentError, e:
if __debug__: print_exc()
myMessageBox(str(e.strerror), "Can't import %s" % e.filename, wx.ICON_ERROR|wx.OK, palette.frame)
return False
except UnicodeError, e:
if __debug__: print_exc()
myMessageBox('Filename uses non-ASCII characters.', "Can't import %s." % e.object, wx.ICON_ERROR|wx.OK, palette.frame)
return False
existing=[]
for (src, dst) in files:
if exists(dst): existing.append(dst[len(pkgpath)+1:])
sortfolded(existing)
if existing:
if len(existing) > 1:
r = myMessageBox('This scenery package already contains the following files:\n ' + '\n '.join(existing) + '\n\nDo you want to replace them?', 'Replace files', wx.ICON_QUESTION|wx.YES_NO|wx.CANCEL, palette.frame)
else:
r = myMessageBox('This scenery package already contains the following file:\n ' + existing[0] + '\n\nDo you want to replace it?', 'Replace files', wx.ICON_QUESTION|wx.YES_NO, palette.frame)
if r==wx.NO:
# Strip out existing
for (src, dst) in list(files):
if exists(dst): files.remove((src,dst))
if not files: return False # None to do
existing=[] # No need to do reload
elif r!=wx.YES:
return False
try:
importobjs(pkgpath, files)
except EnvironmentError, e:
if __debug__: print_exc()
myMessageBox(str(e.strerror), "Can't import %s." % e.filename, wx.ICON_ERROR|wx.OK, palette.frame)
return False
return existing and True or files # Some existing files may be in use - do full reload