Repository navigation
Expand file tree
/
Copy pathBugPath.py
More file actions
711 lines (635 loc) · 20.9 KB
/
Copy pathBugPath.py
File metadata and controls
711 lines (635 loc) · 20.9 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
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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
## BugPath
##
## Locates all of the necessary directories to provide access to data and asset files.
##
## It exposes several useful variables. Those ending in "Dir" are full paths to directories
## while those ending in "Folder" or "Name" are the base name of the file/folder.
## All directory functions return valid directories or the Python None value if not found.
##
## isMac()
## Returns True if running on a Mac operating system.
##
## isMod()
## Returns True if running as a mod, False otherwise.
##
## isNoCustomAssets()
## Returns True if the NoCustomAssets setting is 1 in the mod's INI file,
## False if 0 or when not running as a mod.
##
## getModName()
## Returns CvModName.modName
##
## getModDir(), getModFolder()
## Directory of the loaded mod, or None if no mod is loaded.
##
## getUserDir()
## The "My Documents\My Games" directory on Windows or the "Documents"
## directory on MacOS X. Both are in the user's private documents area.
## If it cannot be found, it will be the directory containing <root-dir>.
##
## getRootDir()
## Directory containing the CivilizationIV.ini file.
## If it cannot be found, will look in <user-dir> for a folder named <app-folder>
## Use the CvAltRoot module to override the location found by this module.
##
## getAppDir(), getAppFolder()
## Directory containing the BTS application (CIV4BeyondSword.exe on Windows).
## This should be "Beyond the Sword" for most installations.
##
## getDataDir()
## Directory containing the mod's data files: user settings, help files, etc.
## This is the first folder containing a folder with a name matching the value of
## SETTINGS_FOLDER ("UserSettings" by default).
##
## Search order:
##
## <user-dir>\<mod-name> C:\Users\<user>\Documents\BUG Mod
## <root-dir>\<mod-name> C:\Users\<user>\Documents\Beyond the Sword\BUG Mod
## <app-dir>\<mod-name> C:\Programs\Sid Meier's Civilization IV\Beyond the Sword\BUG Mod
## <mod-dir>\<data-folder> C:\Programs\Sid Meier's Civilization IV\Beyond the Sword\Mods\BUG Mod x.x\Data
## <mod-dir> C:\Programs\Sid Meier's Civilization IV\Beyond the Sword\Mods\BUG Mod x.x
##
## getSettingsDir()
## Directory containing the mod's user settings.
##
## getInfoDir()
## Directory containing the mod's informational files such as the readme and help files.
##
## TODO
##
## Add override for dataDir.
## Add some other setting instead of modName for dataDir search.
##
## Based on CvPath by Dr. Elmer Jiggles.
##
## Copyright (c) 2008 The BUG Mod.
##
## Author: EmperorFool
# AI, UI, logging, or other modifications first developed in AdvCiv-SAS (Simple Advanced Strategy)
# (c) 2026 wonderingabout & AI/LLM helpers (see Authors in AdvCiv-SAS's root README.md)
#
from CvPythonExtensions import *
import os
import os.path
import sys
import BugConfigTracker
import BugUtil
import shutil
## Constants
DATA_FOLDER = "Data"
SETTINGS_FOLDER = "Settings"
# <!-- custom: renamed BUG Doc to BUG_Doc for consistent naming/easy linking; verify it matches the folder. Credit: ChatGPT 5.1. (GPT-5.2-Codex (summarized)) -->
INFO_FOLDER = "BUG_Doc" # advc.009: was "Info", but it's only about BUG
MODS_FOLDER = "Mods"
ASSETS_FOLDER = "Assets"
CUSTOM_ASSETS_FOLDER = "CustomAssets"
MY_DOCUMENTS_FOLDER_REG_KEYS = (("XP", r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", "Personal"), ("Vista", r"Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", "Personal"),)
MY_GAMES_FOLDER = "My Games"
def isMac():
# Returns True if running on a Mac operating system.
#
return sys.platform == 'darwin'
## Mod Info, Folder and Path Accessors
def getModName():
# Returns the name of the mod as specified in CvModName.
#
# See the file "Assets/Python/Contrib/CvModName.py" for more information.
#
initModName()
return _modName
def isMod():
# Returns True if running as a mod, False otherwise.
#
initModFolder()
return _isMod
def isNoCustomAssets():
# Returns True if the NoCustomAssets setting is 1 in the mod's INI file,
# False if 0 or when not running as a mod.
#
initNoCustomAssetsSetting()
return _noCustomAssets
def getModDir():
# Returns the full path of the mod directory if running as a mod.
#
# Can be overridden using a CvModFolder module, but this shouldn't be necessary
# as the folder's name is acquired from BTS itself.
#
initModFolder()
return _modDir
def getModFolder():
# Returns the name of the mod directory if running as a mod.
#
initModFolder()
return _modFolder
def getUserDir():
# Returns the full path of the user's Documents/My Games directory.
#
# #See the file "Info/CvAltRoot.py" for more information. # advc: That doesn't exist
#
initRootFolder()
return _userDir
def getRootDir():
# Returns the full path of the directory containing the file "CivilizationIV.ini".
#
# #See the file "Info/CvAltRoot.py" for more information. # advc
#
initRootFolder()
return _rootDir
def getDataDir():
# Returns the full path of the directory containing the mod's data files: user settings, help files, etc.
#
initDataFolder()
return _dataDir
def getSettingsDir():
# Returns the full path of the directory containing the mod's user settings.
#
initDataFolder()
return _settingsDir
def getInfoDir():
# Returns the full path of the directory containing the mod's informational files: readme, help files, etc.
#
initDataFolder()
return _infoDir
def getAppDir():
# Returns the full path of the directory containing the "Civ4BeyondSword.exe" application.
#
initAppFolder()
return _appDir
def getAppFolder():
# Returns the name of the directory containing the "Civ4BeyondSword.exe" application.
#
initAppFolder()
return _appFolder
## Finding and Creating Files and Directories
def findAssetFile(name, subdir=None):
# Returns the full path to the named asset file by searching the paths above.
#
initSearchPaths()
for dir in _assetFileSearchPaths:
path = getFilePath(dir, name, subdir)
if path:
return path
if subdir:
BugUtil.warn("BugPath - cannot find asset file %s in %s", name, subdir)
else:
BugUtil.warn("BugPath - cannot find asset file %s", name)
return None
def findDataFile(name, subdir=None):
# Returns the full path to the named data file.
#
return getFilePath(getDataDir(), name, subdir)
def findSettingsFile(name, subdir=None):
# Locates and returns the path to the named configuration file or None if not found.
#
return getFilePath(getSettingsDir(), name, subdir)
def findIniFile(name, subdir=None):
# Locates and returns the path to the named configuration file or None if not found.
#
# Deprecated: Use findSettingsFile() instead.
#
return findSettingsFile(name, subdir)
def findMainModIniFile():
# Locates and returns the path to the configuration file named for the mod or None if not found.
#
if getModName():
return findSettingsFile(getModName() + ".ini")
BugUtil.warn("BugPath - mod name not set")
return None
def findInfoFile(name, subdir=None):
# Locates and returns the path to the named informational file or None if not found.
#
return getFilePath(getInfoDir(), name, subdir)
def createDataFile(name, subdir=None):
# Returns the path to the named data file.
#
return createFile(getDataDir(), name, subdir)
def createSettingsFile(name, subdir=None):
# Returns the path to the named configuration file.
#
return createFile(getSettingsDir(), name, subdir)
def createIniFile(name, subdir=None):
# Returns the path to the named configuration file.
#
# Deprecated: Use createSettingsFile() instead.
#
return createSettingsFile(name, subdir)
def createInfoFile(name, subdir=None):
# Returns the path to the named informational file.
#
return createFile(getInfoDir(), name, subdir)
def findDir(name):
# Locates the named directory in dataDir.
#
path = join(getDataDir(), name)
if isdir(path):
return path
return None
def makeDir(name):
# Creates a new directory in the dataDir folder.
#
path = join(getDataDir(), name)
if path and not isdir(path):
try:
safeInfoPath("BugPath - creating '%s'", path)
os.makedirs(path)
return path
except OSError:
BugUtil.trace("Cannot create directory '%s' in '%s", name, getDataDir())
return path
def findOrMakeDir(name):
# Locates or creates the specified directory and returns the path to it.
#
return findDir(name) or makeDir(name)
## Initialization
def init():
# Initializes the entire module.
#
BugUtil.debug("BugPath - initializing...")
initAppFolder()
initModName()
initModFolder()
initNoCustomAssetsSetting()
initRootFolder()
initDataFolder()
initSearchPaths()
## Application Directory
_appDir = None
_appFolder = "Beyond the Sword"
_appFolderInitDone = False
def initAppFolder():
# Locates the directory that contains the BTS application.
#
global _appFolderInitDone
if _appFolderInitDone:
return
BugUtil.debug("BugPath - initializing application folder")
global _appDir, _appFolder
# Determine the app directory that holds the executable and Mods
# as well as the folder name inside MY_GAMES_FOLDER that holds CustomAssets and Mods.
if isMac():
_appDir = os.getcwd()
else:
if isfile(sys.executable):
#BugUtil.debug("BugPath - exe is '%s'", sys.executable)
_appDir = dirname(sys.executable)
if _appDir:
_appFolder = basename(_appDir)
safeInfoPath("BugPath - app dir is '%s'", _appDir)
safeDebugPath("BugPath - app folder is '%s'", _appFolder)
else:
BugUtil.warn("BugPath - no executable found")
_appFolderInitDone = True
## Mod Display Name
_modName = None
_modNameInitDone = False
def initModName():
# Pulls the modName attribute from the CvModName module.
#
global _modNameInitDone
if _modNameInitDone:
return
global _modName
try:
import CvModName
_modName = CvModName.modName
safeInfoPath("BugPath - mod name is '%s'", _modName)
except ImportError:
BugUtil.error("CvModName.py module not present")
except AttributeError:
BugUtil.error("CvModName.py module has no modName setting")
_modNameInitDone = True
## Mod Directory
_isMod = False
_modDir = None
_modFolder = None
_modFolderInitDone = False
def initModFolder():
# Checks if BUG is running as a mod and sets the folder and name if so.
#
global _modFolderInitDone
if _modFolderInitDone:
return
if not CyGame().isFinalInitialized():
BugUtil.debug("BugInit - game not fully initialized")
return
BugUtil.debug("BugPath - initializing mod folder")
global _modDir, _modFolder
try:
replay = CyReplayInfo()
replay.createInfo(0)
modDir = replay.getModName().replace('\\', r'/')
except:
BugUtil.trace("replay not ready")
else:
if modDir and len(modDir) > 2:
if not setModDir(abspath(modDir)):
BugUtil.error("Replay provided invalid mod directory")
if not _modDir:
BugUtil.debug("BugPath - checking CvModFolder")
try:
import CvModFolder
modFolder = CvModFolder.modFolder
except ImportError:
BugUtil.debug("BugPath - CvModFolder module not present")
except AttributeError:
BugUtil.error("CvModFolder.py module has no modFolder setting")
else:
if not setModDir(join(getRootDir(), MODS_FOLDER, modFolder)):
if not setModDir(join(getAppDir(), MODS_FOLDER, modFolder)):
BugUtil.error("Cannot find mod folder using '%s' from CvModFolder.py", modFolder)
if not _modFolder:
BugUtil.debug("BugPath - no mod directory found")
_modFolderInitDone = True
def setModDir(dir):
safeDebugPath("BugPath - checking mod dir '%s'", dir)
if isdir(dir):
global _isMod, _modDir, _modFolder
_isMod = True
_modDir = dir
_modFolder = basename(dir)
safeInfoPath("BugPath - mod dir is '%s'", dir)
safeDebugPath("BugPath - mod folder is '%s'", _modFolder)
BugConfigTracker.add("Mod_Directory", _modDir)
return True
return False
## NoCustomAssets Setting
_noCustomAssets = False
_noCustomAssetsSettingInitDone = False
def initNoCustomAssetsSetting():
# Checks if the NoCustomAssets setting is enabled when running as a mod.
#
global _noCustomAssetsSettingInitDone
if _noCustomAssetsSettingInitDone:
return
if isMod():
global _noCustomAssets
# EF: don't check game option because BUG is initialized only once at startup
# if CyGame().isOption(GameOptionTypes.GAMEOPTION_LOCK_MODS):
# _noCustomAssets = True
# BugUtil.debug("BugPath - Lock Modified Assets is set")
# else:
if getModDir() and getModFolder():
BugUtil.debug("BugPath - checking for NoCustomAssets")
try:
from configobj import ConfigObj
config = ConfigObj(join(getModDir(), getModFolder() + ".ini"), encoding='utf_8')
_noCustomAssets = config["CONFIG"].as_bool("NoCustomAssets")
except:
BugUtil.trace("BugPath - failed to parse mod INI file for NoCustomAssets")
BugUtil.info("BugPath - NoCustomAssets is %s", _noCustomAssets)
_noCustomAssetsSettingInitDone = True
## User and Root Directories
_userDir = None
_rootDir = None
_rootFolderInitDone = False
def initRootFolder():
# Finds the directory that contains the CivilizationIV.ini file and the user's documents directory.
#
global _rootFolderInitDone
if _rootFolderInitDone:
return
BugUtil.debug("BugPath - initializing system folders")
global _rootDir, _userDir
# override root dir from CvAltRoot
try:
import CvAltRoot
altRootDir = CvAltRoot.rootDir
if not setRootDir(altRootDir):
BugUtil.error("Directory from CvAltRoot.py is not valid or does not contain CivilizationIV.ini")
except ImportError:
BugUtil.debug("BugPath - CvAltRoot module not present")
except AttributeError:
BugUtil.error("CvAltRoot.py module has no rootDir setting")
# <!-- custom: Avoid old comma-exception binding while staying compatible with Civ4 Python 2.4; sys.exc_info()[1] was already used in BugUtil, so this pattern is deemed safe. (GPT-5.5) -->
except OSError:
e = sys.exc_info()[1]
errno, strerror = e.args[:2]
BugUtil.trace("Error accessing directory from CvAltRoot.py: [%d] %s", errno, strerror)
# K-Mod. I'd like to set the user directory like this:
# if not setUserDir(os.path.expanduser(join("~", MY_GAMES_FOLDER))):
# BugUtil.debug("BugPath - default user dir failed ('%s')" % (os.path.expanduser(join("~", MY_GAMES_FOLDER))))
# but unfortunately, this misses the "documents" part of the path name. Oh well.
# user dir
if isMac():
# Mac OS X
if not setUserDir(join(os.environ['HOME'], "Documents")):
BugUtil.warn("Cannot find user's Documents folder")
else:
# Windows
import _winreg
import re # used in a bugfix by K-Mod
def getRegValue(root, subkey, name):
key = _winreg.OpenKey(root, subkey)
try:
value = _winreg.QueryValueEx(key, name)
# K-Mod. We may need to expand environment variables.
# unfortuantely, expandvars in this particular version of python can't handle %variables%, so we need to first translate...
if value[1] == _winreg.REG_EXPAND_SZ:
# here we go...
return os.path.expandvars(re.sub(r'%([a-zA-Z_]+)%', r'${\1}', value[0]))
# K-Mod end
return value[0]
finally:
key.Close()
for version, key, subkey in MY_DOCUMENTS_FOLDER_REG_KEYS:
try:
myDocuments = getRegValue(_winreg.HKEY_CURRENT_USER, key, subkey)
except:
pass
else:
if setUserDir(join(myDocuments, MY_GAMES_FOLDER)):
safeInfoPath("BugPath - found valid Windows %s My Documents folder registry key", version)
break
else:
BugUtil.debug("BugPath - no valid My Documents registry key")
BugUtil.warn("Cannot find user's Documents folder")
# try to determine missing dir from other dir
if not _rootDir:
if _userDir:
if isMac():
setRootDir(join(_userDir, "Civilization IV " + getAppFolder()))
else:
setRootDir(join(_userDir, getAppFolder()))
else:
if not _userDir:
setUserDir(dirname(_rootDir))
# if neither is found, fail
if not _rootDir:
BugUtil.error("Cannot find CivilizationIV.ini; see %s/CvAltRoot.py for instructions", INFO_FOLDER)
_rootFolderInitDone = True
def setRootDir(dir):
safeDebugPath("BugPath - Checking root dir '%s'", dir)
if isdir(dir) and isfile(join(dir, "CivilizationIV.ini")):
global _rootDir
_rootDir = dir
safeInfoPath("BugPath - root dir is '%s'", dir)
BugConfigTracker.add("Root_Directory", _rootDir)
return True
return False
def setUserDir(dir):
safeDebugPath("BugPath - Checking user dir '%s'", dir)
if isdir(dir):
global _userDir
_userDir = dir
safeInfoPath("BugPath - user dir is '%s'", dir)
return True
return False
## Data Directory
_dataDir = None
_settingsDir = None
_infoDir = None
_dataFolderInitDone = False
def initDataFolder():
# Finds the first directory that contains a folder named SETTINGS_FOLDER.
#
global _dataFolderInitDone
if _dataFolderInitDone:
return
BugUtil.debug("BugPath - initializing data folder")
# K-Mod. If it doesn't already exist, create the folder in the user directory.
dir = join(getRootDir(), getModName(), SETTINGS_FOLDER)
if dir is not None:
if not isdir(dir):
# copy the default settings from the K-Mod folder.
default_dir = join(getModDir(), SETTINGS_FOLDER)
if isdir(default_dir):
try:
safeInfoPath("BugPath - copying settings to '%s'", dir)
# copytree is suppose to create the missing parent directores, but apparently it doesn't work. So I need to do this:
try:
os.makedirs(join(getRootDir(), getModName()))
except OSError:
pass
# sucks.
shutil.copytree(default_dir, dir)
except:
BugUtil.trace("Failed to copy settings")
if not isdir(dir):
# Second attempt: create the directory manually
try:
safeInfoPath("BugPath - creating '%s'", dir)
os.makedirs(dir)
except OSError:
BugUtil.trace("Failed to create directory '%s'", dir)
# K-Mod end
dataDirs = (
join(getRootDir(), getModName()), # My Games\BTS\BUG Mod
join(getUserDir(), getModName()), # My Games\BUG Mod
join(getAppDir(), getModName()), # Civ4\BTS\BUG Mod
join(getModDir(), DATA_FOLDER), # Civ4\BTS\Mods\BUG Mod 3.6\Data
join(getModDir()), # Civ4\BTS\Mods\BUG Mod 3.6
)
for dir in dataDirs:
if setDataDir(dir):
break
else:
BugUtil.error("No valid data directory containing %s found", SETTINGS_FOLDER)
_dataFolderInitDone = True
# <advc.009> setDataDir tries to locate INFO_FOLDER in the same place
# as SETTINGS_FOLDER, but settings need to be in \My Games\, whereas
# info is in the program folder. I don't see how this can work in standalone
# BUG (same code there) either - perhaps it doesn't; haven't tried it.
global _infoDir
for dir in dataDirs:
_infoDir = join(dir, INFO_FOLDER)
if isdir(_infoDir): # Show the troublesome path in the System tab too
BugConfigTracker.add("Info_Directory", _infoDir)
break
# </advc.009>
def setDataDir(dir):
if isdir(dir):
safeDebugPath("BugPath - Checking data dir '%s'", dir)
settingsDir = join(dir, SETTINGS_FOLDER)
if isdir(settingsDir):
safeInfoPath("BugPath - data dir is '%s'", dir)
global _dataDir, _settingsDir, _infoDir
_dataDir = dir
_settingsDir = settingsDir
# advc.009: Caller handles this now
#_infoDir = join(dir, INFO_FOLDER)
BugConfigTracker.add("Settings_Directory", _settingsDir)
return True
return False
## Asset Directories
_assetFileSearchPaths = []
_searchPathsInitDone = False
def initSearchPaths():
# Adds the CustomAssets, mod Assets and BTS Assets directories to a list of search paths.
#
global _searchPathsInitDone
if _searchPathsInitDone:
return
BugUtil.debug("BugPath - initializing asset search paths")
assetDirs = [join(getModDir(), ASSETS_FOLDER), join(getAppDir(), ASSETS_FOLDER)]
# EF: Mod's no longer access CustomAssets folder; too many issues
if not isNoCustomAssets() and not isMod():
assetDirs.insert(0, join(getRootDir(), CUSTOM_ASSETS_FOLDER))
for dir in assetDirs:
addAssetFileSearchPath(dir)
if _assetFileSearchPaths:
BugConfigTracker.add("Asset_Search_Paths", _assetFileSearchPaths)
else:
BugUtil.error("No asset directories found")
_searchPathsInitDone = True
def addAssetFileSearchPath(path):
# Adds the given path to the search list if it is a directory.
#
if isdir(path):
_assetFileSearchPaths.append(path)
## None-Safe Path/Directory/File Functions
def getFilePath(root, name, subdir=None):
# Returns the full path to the named file, or None if it doesn't exist.
#
if not root:
BugUtil.warn("Invalid root directory looking for '%s'", name)
if subdir:
path = join(root, subdir, name)
else:
path = join(root, name)
if isfile(path):
return path
safeDebugPath("BugPath - not a file: '%s'", path)
return None
def createFile(root, name, subdir=None):
# Returns the path to a new file to be created.
#
if subdir:
return join(root, subdir, name)
else:
return join(root, name)
def join(*paths):
for path in paths:
if path is None:
return None
return os.path.join(*paths)
def dirname(path):
if path is None:
return None
return os.path.dirname(path)
def basename(path):
if path is None:
return None
return os.path.basename(path)
def abspath(path):
if path is None:
return None
return os.path.abspath(path)
def isdir(path):
if path is None:
return False
return os.path.isdir(path)
def isfile(path):
if path is None:
return False
return os.path.isfile(path)
## Non-English-Safe Logging
def safeDebugPath(message, path):
try:
BugUtil.debug(message, path)
except:
pass
def safeInfoPath(message, path):
try:
BugUtil.info(message, path)
except:
pass