428 lines
9.9 KiB
Python
428 lines
9.9 KiB
Python
#Preload Exception handler
|
|
import sys
|
|
def preloadExceptionHandler(exc_type, exc_value, tb):
|
|
import traceback
|
|
print("An error has occurred while initializing:")
|
|
traceback.print_exception(exc_type, exc_value, tb)
|
|
input("\nPress ENTER to exit.")
|
|
sys.exit(-1)
|
|
#sys.excepthook = preloadExceptionHandler
|
|
|
|
#Imports and variables
|
|
class uml:
|
|
version = 0
|
|
versionSub = 8
|
|
versionSub2 = 1
|
|
versionBranch = "beta (dev)"
|
|
versionString = str(version) + "." + str(versionSub) + "." +str(versionSub2)+ " " + versionBranch
|
|
|
|
import ctypes
|
|
import os
|
|
import shutil
|
|
import webbrowser
|
|
import time
|
|
import stat
|
|
|
|
scriptPath = os.path.dirname(os.path.realpath(__file__))
|
|
appPath = False
|
|
appName = False
|
|
originalAppPath = False
|
|
tmpAppPath = False
|
|
modPath = False
|
|
originalModPath = False
|
|
api = True
|
|
|
|
#Exception Handler
|
|
def openFileWithStandardApp(path):
|
|
# Windows
|
|
if os.name == "nt":
|
|
os.startfile(path)
|
|
# Macintosh
|
|
elif sys.platform == "darwin":
|
|
subprocess.call(['open', path])
|
|
# Generic Unix (X11)
|
|
else:
|
|
subprocess.call(['xdg-open', path])
|
|
|
|
def testAccess(path):
|
|
try:
|
|
os.rename(path,path)
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
def exceptionCleanup():
|
|
success = True
|
|
print("Attempting cleanup.")
|
|
print("\nMaking folders visible...")
|
|
try:
|
|
#ctypes.windll.kernel32.SetFileAttributesW(appPath,128)
|
|
#ctypes.windll.kernel32.SetFileAttributesW(originalAppPath,128)
|
|
pass
|
|
except Exception as e:
|
|
success = False
|
|
print(str(e))
|
|
|
|
print("\nUnloading mods...")
|
|
try:
|
|
unloadMods()
|
|
except Exception as e:
|
|
success = False
|
|
print(str(e))
|
|
|
|
print("\nCleaning up temporary files...")
|
|
try:
|
|
cleanUp()
|
|
except Exception as e:
|
|
success = False
|
|
print(str(e))
|
|
|
|
if success == False:
|
|
print("\nCleanup not fully successful. Please review the errors, and go to X for a guide on how to reset your game manually. Sorry for the inconvenience, I tried :(")
|
|
|
|
return success
|
|
|
|
def logError(logFilePath,textList):
|
|
logFile = False
|
|
|
|
try:
|
|
logFile = open(logFilePath,"w")
|
|
except:
|
|
return False
|
|
|
|
try:
|
|
for line in textList:
|
|
logFile.write(line + "\n")
|
|
except:
|
|
logFile.close()
|
|
return False
|
|
|
|
logFile.close()
|
|
return True
|
|
|
|
def exceptionHandler(exc_type, exc_value, tb):
|
|
clear()
|
|
|
|
print("An error occurred, trying to revert everything...")
|
|
exceptionCleanup()
|
|
input()
|
|
|
|
#sys.excepthook = exceptionHandler
|
|
|
|
#Modloader
|
|
def cloneMods(modDir):
|
|
for root,dirs,files in walklevel(modDir,0):
|
|
for dir in dirs:
|
|
if dir[0] == "-": continue
|
|
if dir[0] == "[" and dir[-1:] == "]":
|
|
cloneMods(os.path.join(root,dir))
|
|
else:
|
|
print("Applying Mod: " +dir)
|
|
if os.path.isfile(os.path.join(root,dir,"uml_installscript.py")) == True:
|
|
file = open(os.path.join(root,dir,"uml_installscript.py"))
|
|
exec(file.read(),globals(),locals())
|
|
file.close()
|
|
else:
|
|
cloneFolder(os.path.join(root,dir),tmpAppPath,True,False,True)
|
|
|
|
def loadMods(output = False, fast = False):
|
|
if fast == False:
|
|
if areModsLoaded():
|
|
if unloadMods() == False:
|
|
if output: print("Unloading mods failed!")
|
|
return False
|
|
|
|
if fast == True:
|
|
if areModsLoaded() == False:
|
|
if output: print("Can not fast-load mods when mods aren't loaded.")
|
|
return False
|
|
|
|
if fast == False:
|
|
if areModsLoaded():
|
|
if output: print("Mods are already loaded and could not be unloaded.")
|
|
return False
|
|
|
|
#print("Claiming app folder...")
|
|
#claimFolder(appPath)
|
|
#print("Claiming mod folder...")
|
|
#claimFolder(modPath)
|
|
print("Testing access...")
|
|
if testAccess(appPath) == False:
|
|
if output: print("Can't access folder! Is it in use?")
|
|
return
|
|
print("Cloning app folder...")
|
|
cloneFolder(appPath,tmpAppPath,False)
|
|
|
|
print("Cloning mods...")
|
|
cloneMods(modPath)
|
|
|
|
os.rename(appPath,originalAppPath)
|
|
os.rename(tmpAppPath,appPath)
|
|
else:
|
|
print("Testing access...")
|
|
if testAccess(appPath) == False:
|
|
if output: print("Can't access folder! Is it in use?")
|
|
return
|
|
os.rename(appPath,tmpAppPath)
|
|
cloneMods(modPath)
|
|
os.rename(tmpAppPath,appPath)
|
|
|
|
#ctypes.windll.kernel32.SetFileAttributesW(originalAppPath,2)
|
|
|
|
if output: print("\nMods have been loaded!")
|
|
return True
|
|
|
|
def unloadMods(output = False):
|
|
if areModsLoaded() == False:
|
|
if output: print("Mods are already unloaded.")
|
|
return True
|
|
|
|
print("Testing access...")
|
|
if testAccess(appPath) == False:
|
|
if output: print("Can't access folder! Is it in use?")
|
|
return
|
|
|
|
print("Removing cloned app folder...")
|
|
try:
|
|
shutil.rmtree(appPath)
|
|
except:
|
|
if output: print("Can't delete folder! Is it in use?")
|
|
return
|
|
|
|
|
|
tries = 0
|
|
while tries < 100:
|
|
try:
|
|
os.rename(originalAppPath,appPath)
|
|
tries + 1
|
|
except:
|
|
time.sleep(0.1)
|
|
tries + 1
|
|
else:
|
|
break
|
|
|
|
#ctypes.windll.kernel32.SetFileAttributesW(appPath,128)
|
|
|
|
if output: print("\nUnloading mods successful.")
|
|
return True
|
|
|
|
def openModsFolder():
|
|
if areModsLoaded():
|
|
webbrowser.open("file:///" +originalModPath)
|
|
else:
|
|
webbrowser.open("file:///" +modPath)
|
|
|
|
def areModsLoaded():
|
|
if os.path.isdir(originalAppPath):
|
|
return True
|
|
|
|
return False
|
|
|
|
def cloneFolder(src,dst,rpl,ignoreMods = True, isMod = False):
|
|
maxCount = 0
|
|
count = 0
|
|
|
|
for root,dirs,files in os.walk(src):
|
|
newRoot = root.replace(src,dst)
|
|
if ignoreMods == True:
|
|
if root.replace(modPath,"") != root: continue
|
|
|
|
maxCount = maxCount + len(files)
|
|
sys.stdout.write("\r" +str(count)+ "/" +str(maxCount))
|
|
sys.stdout.flush()
|
|
|
|
for root,dirs,files in os.walk(src):
|
|
newRoot = root.replace(src,dst)
|
|
if ignoreMods == True:
|
|
if root.replace(modPath,"") != root: continue
|
|
|
|
if os.path.isdir(newRoot) == False: os.makedirs(newRoot)
|
|
|
|
for file in files:
|
|
count = count + 1
|
|
sys.stdout.write("\r" +str(count)+ "/" +str(maxCount))
|
|
sys.stdout.flush()
|
|
|
|
if isMod == True:
|
|
if file[:4] == "uml_": continue
|
|
fullFile = os.path.join(root,file)
|
|
newFile = os.path.join(newRoot,file)
|
|
|
|
if os.path.isfile(newFile):
|
|
if rpl == True:
|
|
os.remove(newFile)
|
|
os.link(fullFile,newFile)
|
|
else:
|
|
os.link(fullFile,newFile)
|
|
|
|
print("")
|
|
|
|
def claimFolder(path):
|
|
os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO)
|
|
for root,dirs,files in os.walk(path):
|
|
for dir in dirs:
|
|
os.chmod(os.path.join(root,dir), stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO)
|
|
|
|
for file in files:
|
|
os.chmod(os.path.join(root,file), stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO)
|
|
|
|
def walklevel(some_dir, level=1):
|
|
some_dir = some_dir.rstrip(os.path.sep)
|
|
assert os.path.isdir(some_dir)
|
|
num_sep = some_dir.count(os.path.sep)
|
|
for root, dirs, files in os.walk(some_dir):
|
|
yield root, dirs, files
|
|
num_sep_this = root.count(os.path.sep)
|
|
if num_sep + level <= num_sep_this:
|
|
del dirs[:]
|
|
|
|
def title(string):
|
|
if api == True: return
|
|
if os.name == "nt":
|
|
os.system("title " +string)
|
|
else:
|
|
sys.stdout.write("\x1b]2;" +string+ "\x07")
|
|
|
|
def clear():
|
|
os.system('cls' if os.name=='nt' else 'clear')
|
|
|
|
def cleanUp():
|
|
if os.path.isdir(tmpAppPath):
|
|
shutil.rmtree(tmpAppPath)
|
|
|
|
def checkUp():
|
|
if os.path.isdir(modPath) == False:
|
|
while True:
|
|
clear()
|
|
print("You selected the following path: '" +appPath+ "'")
|
|
choice = input("Do you wish to set up that folder for mod-use? (y/n)\n")
|
|
if choice == "y":
|
|
os.makedirs(modPath)
|
|
#ctypes.windll.kernel32.SetFileAttributesW(modPath,2)
|
|
return
|
|
|
|
if choice == "n": sys.exit(-1)
|
|
|
|
def mainMenu():
|
|
clear()
|
|
if areModsLoaded() == False:
|
|
print("Welcome to Fier's Universal Modloader.")
|
|
print("Mods are not loaded.")
|
|
print("")
|
|
print("Please choose an action:")
|
|
print("1) Load Mods")
|
|
print("2) Open Mods-Folder")
|
|
choice = input("Choice: ")
|
|
|
|
clear()
|
|
if choice == "1": loadMods(True); input("Press ENTER to continue.")
|
|
if choice == "2": openModsFolder()
|
|
else:
|
|
print("Welcome to Fier's Universal Modloader.")
|
|
print("Mods are loaded.")
|
|
print("")
|
|
print("Please choose an action:")
|
|
print("1) Reload Mods")
|
|
print("2) Fast-Load Mods")
|
|
print("3) Unload Mods")
|
|
print("4) Open Mods-Folder")
|
|
choice = input("Choice: ")
|
|
|
|
clear()
|
|
if choice == "1": loadMods(True); input("Press ENTER to continue.")
|
|
if choice == "2": loadMods(True,True); input("Press ENTER to continue.")
|
|
if choice == "3": unloadMods(True); input("Press ENTER to continue.")
|
|
if choice == "4": openModsFolder()
|
|
|
|
def requestAppPath():
|
|
while True:
|
|
clear()
|
|
dir = input("Please insert a folder via Drag&Drop:\n")
|
|
if (dir[0] == '"' and dir[-1] == '"') or (dir[0] == "'" and dir[-1] == "'"): dir = dir[1:-1]
|
|
|
|
if dir == "console":
|
|
console()
|
|
else:
|
|
if os.path.isdir(dir) == True:
|
|
return dir
|
|
|
|
def console():
|
|
clear()
|
|
print("UniversalModloader Console")
|
|
print("Version: " +uml.versionString)
|
|
|
|
while True:
|
|
print("")
|
|
cmd = input(os.getcwd()+ ">")
|
|
|
|
if cmd == "exit":
|
|
return
|
|
|
|
if cmd.startswith("cd "):
|
|
try:
|
|
os.chdir(cmd[3:])
|
|
continue
|
|
except Exception as e:
|
|
print(e)
|
|
continue
|
|
continue
|
|
|
|
if cmd.startswith("pyc "):
|
|
try:
|
|
exec(cmd[4:])
|
|
continue
|
|
except Exception as e:
|
|
print(e)
|
|
continue
|
|
|
|
if cmd.startswith("mc "):
|
|
try:
|
|
cloneFolder(cmd[3:],cmd[3:] + " - clone",True,False)
|
|
continue
|
|
except Exception as e:
|
|
print(e)
|
|
continue
|
|
|
|
os.system(cmd)
|
|
|
|
def setupVariables():
|
|
global appPath
|
|
global appName
|
|
global originalAppPath
|
|
global tmpAppPath
|
|
global modPath
|
|
global originalModPath
|
|
|
|
if len(sys.argv) < 2:
|
|
appPath = requestAppPath()
|
|
else:
|
|
appPath = sys.argv[1]
|
|
|
|
if appPath == "console":
|
|
console()
|
|
|
|
if os.path.isdir(appPath) == False:
|
|
raise NameError("Folder not found: " +appPath)
|
|
|
|
if os.path.isdir(appPath.replace(" - umlOriginal","")): appPath = appPath.replace(" - umlOriginal","")
|
|
appName = appPath.replace(os.path.dirname(appPath)+ os.sep,"")
|
|
originalAppPath = appPath + " - umlOriginal"
|
|
tmpAppPath = appPath + " - umlTemp"
|
|
modPath = os.path.join(appPath + " - umlMods")
|
|
originalModPath = modPath
|
|
|
|
def init():
|
|
global api
|
|
api = False
|
|
title("Fier's Universal Modloader - " +uml.versionString)
|
|
setupVariables()
|
|
title("Fier's Universal Modloader - " +uml.versionString+ " : " +appName)
|
|
|
|
cleanUp()
|
|
checkUp()
|
|
|
|
while True:
|
|
mainMenu()
|
|
|
|
if __name__ == "__main__":
|
|
init() |