add AO2XP auto-updater
This commit is contained in:
parent
4c5095ec9b
commit
6bff2b2b21
18
AO2XP.py
18
AO2XP.py
@ -1,9 +1,11 @@
|
|||||||
import sys, thread, time, os, platform, __builtin__
|
import subprocess, sys, thread, time, os, platform, __builtin__
|
||||||
from os.path import exists, abspath
|
from os.path import exists, abspath
|
||||||
|
|
||||||
from PyQt4 import QtGui, QtCore
|
from PyQt4 import QtGui, QtCore
|
||||||
|
|
||||||
import audio as AUDIO
|
import audio as AUDIO
|
||||||
|
import ini
|
||||||
|
|
||||||
__builtin__.audio = AUDIO
|
__builtin__.audio = AUDIO
|
||||||
del AUDIO
|
del AUDIO
|
||||||
|
|
||||||
@ -62,6 +64,7 @@ class gamewindow(QtGui.QMainWindow):
|
|||||||
self.settingsgui.showSettings()
|
self.settingsgui.showSettings()
|
||||||
|
|
||||||
if not debugmode:
|
if not debugmode:
|
||||||
|
# Vanilla downloader
|
||||||
force_downloader = len(sys.argv) > 1 and sys.argv[1] == "download"
|
force_downloader = len(sys.argv) > 1 and sys.argv[1] == "download"
|
||||||
if force_downloader or (not exists("base/background") and not exists("base/characters") and not exists("base/sounds") and not exists("base/evidence")):
|
if force_downloader or (not exists("base/background") and not exists("base/characters") and not exists("base/sounds") and not exists("base/evidence")):
|
||||||
jm = QtGui.QMessageBox.information(None, "Warning", "You seem to be missing the included Attorney Online content.\nWould you like to download them automatically?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
|
jm = QtGui.QMessageBox.information(None, "Warning", "You seem to be missing the included Attorney Online content.\nWould you like to download them automatically?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
|
||||||
@ -72,7 +75,18 @@ if not debugmode:
|
|||||||
else:
|
else:
|
||||||
os._exit(-3)
|
os._exit(-3)
|
||||||
|
|
||||||
import gameview, mainmenu, options, ini
|
# AO2XP update checker
|
||||||
|
can_update = ini.read_ini_bool("AO2XP.ini", "General", "install updates", True)
|
||||||
|
force_update = "forceupdate" in sys.argv[1:]
|
||||||
|
if can_update or force_update:
|
||||||
|
import updater
|
||||||
|
code = updater.checkForUpdates(force_update)
|
||||||
|
if code == 0:
|
||||||
|
subprocess.Popen(["./AO2XPupdat"])
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
import gameview, mainmenu, options
|
||||||
|
|
||||||
audio.init()
|
audio.init()
|
||||||
shit = gamewindow()
|
shit = gamewindow()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pyinstaller AO2XP.spec
|
pyinstaller AO2XP.spec
|
||||||
pyinstaller AO2XP_console.spec
|
pyinstaller AO2XP_console.spec
|
||||||
|
pyinstaller install_update.spec
|
||||||
move .\dist\* .
|
move .\dist\* .
|
||||||
|
30
install_update.py
Normal file
30
install_update.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import zipfile, tarfile, platform, os, time, shutil
|
||||||
|
|
||||||
|
ext = {
|
||||||
|
"Windows": "zip",
|
||||||
|
"Darwin": "zip",
|
||||||
|
"Linux": "gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def extractzip(): # Mac
|
||||||
|
archive = zipfile.ZipFile("update.zip")
|
||||||
|
if platform.system() == "Darwin": shutil.rmtree("AO2XP.app", ignore_errors=True) # delete the old app package.
|
||||||
|
archive.extractall() # extract the new version
|
||||||
|
|
||||||
|
def extractgz(): # Linux
|
||||||
|
archive = tarfile.open("update.tar.gz")
|
||||||
|
archive.extractall()
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists("update." + ext[platform.system()]):
|
||||||
|
print "Waiting 3 seconds for AO2XP to close..."
|
||||||
|
time.sleep(3)
|
||||||
|
print "Extracting update." + ext[platform.system()] + "..."
|
||||||
|
globals()["extract" + ext[platform.system()]]() # call the extract function according to OS
|
||||||
|
print "Done!"
|
||||||
|
os.remove("update." + ext[platform.system()])
|
||||||
|
|
||||||
|
else:
|
||||||
|
print "This program will be automatically run by AO2XP to apply updates.\nYou do not need to run this yourself."
|
||||||
|
raw_input("Press enter to exit.\n")
|
34
install_update.spec
Normal file
34
install_update.spec
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(['install_update.py'],
|
||||||
|
pathex=['C:\\Users\\Public\\1.7.5 Cut Content Patch\\Client'],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='AO2XPupdat',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
manifest=None,
|
||||||
|
console=True )
|
31
options.py
31
options.py
@ -1,6 +1,7 @@
|
|||||||
from ConfigParser import ConfigParser
|
from ConfigParser import ConfigParser
|
||||||
from os.path import exists
|
from os.path import exists
|
||||||
from os import listdir
|
from os import listdir, _exit
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from PyQt4 import QtGui, QtCore
|
from PyQt4 import QtGui, QtCore
|
||||||
|
|
||||||
@ -46,9 +47,9 @@ class Settings(QtGui.QDialog):
|
|||||||
cancelbtn.clicked.connect(self.onCancelClicked)
|
cancelbtn.clicked.connect(self.onCancelClicked)
|
||||||
|
|
||||||
separators = []
|
separators = []
|
||||||
for i in range(4):
|
for i in range(5):
|
||||||
separator = QtGui.QFrame()
|
separator = QtGui.QFrame()
|
||||||
separator.setFixedSize(separator.size().width(), 16)
|
separator.setFixedSize(separator.size().width(), 12)
|
||||||
separators.append(separator)
|
separators.append(separator)
|
||||||
|
|
||||||
###### General tab ######
|
###### General tab ######
|
||||||
@ -92,7 +93,15 @@ class Settings(QtGui.QDialog):
|
|||||||
self.currtheme.addItem(theme)
|
self.currtheme.addItem(theme)
|
||||||
currtheme_layout.addWidget(currtheme_label)
|
currtheme_layout.addWidget(currtheme_label)
|
||||||
currtheme_layout.addWidget(self.currtheme)
|
currtheme_layout.addWidget(self.currtheme)
|
||||||
|
|
||||||
|
update_layout = QtGui.QHBoxLayout()
|
||||||
|
self.check_updates = QtGui.QCheckBox("Check for AO2XP updates on startup")
|
||||||
|
self.check_updates_btn = QtGui.QPushButton()
|
||||||
|
self.check_updates_btn.setText("Check now...")
|
||||||
|
self.check_updates_btn.clicked.connect(self.onUpdateClicked)
|
||||||
|
update_layout.addWidget(self.check_updates)
|
||||||
|
update_layout.addWidget(self.check_updates_btn)
|
||||||
|
|
||||||
savechangeswarn = QtGui.QLabel()
|
savechangeswarn = QtGui.QLabel()
|
||||||
savechangeswarn.setText("* Change takes effect upon restarting the client")
|
savechangeswarn.setText("* Change takes effect upon restarting the client")
|
||||||
|
|
||||||
@ -106,6 +115,8 @@ class Settings(QtGui.QDialog):
|
|||||||
general_layout.addLayout(allowdownload_layout)
|
general_layout.addLayout(allowdownload_layout)
|
||||||
general_layout.addWidget(separators[2])
|
general_layout.addWidget(separators[2])
|
||||||
general_layout.addLayout(currtheme_layout)
|
general_layout.addLayout(currtheme_layout)
|
||||||
|
general_layout.addWidget(separators[3])
|
||||||
|
general_layout.addLayout(update_layout)
|
||||||
general_layout.addWidget(savechangeswarn, 50, QtCore.Qt.AlignBottom)
|
general_layout.addWidget(savechangeswarn, 50, QtCore.Qt.AlignBottom)
|
||||||
|
|
||||||
###### Audio tab ######
|
###### Audio tab ######
|
||||||
@ -113,7 +124,7 @@ class Settings(QtGui.QDialog):
|
|||||||
self.device_list = QtGui.QComboBox()
|
self.device_list = QtGui.QComboBox()
|
||||||
audio_layout.setWidget(0, QtGui.QFormLayout.LabelRole, device_label)
|
audio_layout.setWidget(0, QtGui.QFormLayout.LabelRole, device_label)
|
||||||
audio_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.device_list)
|
audio_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.device_list)
|
||||||
audio_layout.setWidget(1, QtGui.QFormLayout.FieldRole, separators[3])
|
audio_layout.setWidget(1, QtGui.QFormLayout.FieldRole, separators[4])
|
||||||
|
|
||||||
volumelabel = QtGui.QLabel("Sound volume")
|
volumelabel = QtGui.QLabel("Sound volume")
|
||||||
musiclabel = QtGui.QLabel("Music")
|
musiclabel = QtGui.QLabel("Music")
|
||||||
@ -176,6 +187,7 @@ class Settings(QtGui.QDialog):
|
|||||||
self.allowdownload_music.setChecked(ini.read_ini_bool(self.inifile, "General", "download music"))
|
self.allowdownload_music.setChecked(ini.read_ini_bool(self.inifile, "General", "download music"))
|
||||||
self.allowdownload_evidence.setChecked(ini.read_ini_bool(self.inifile, "General", "download evidence"))
|
self.allowdownload_evidence.setChecked(ini.read_ini_bool(self.inifile, "General", "download evidence"))
|
||||||
self.currtheme.setCurrentIndex(self.themes.index(ini.read_ini(self.inifile, "General", "theme", "default")))
|
self.currtheme.setCurrentIndex(self.themes.index(ini.read_ini(self.inifile, "General", "theme", "default")))
|
||||||
|
self.check_updates.setChecked(ini.read_ini_bool(self.inifile, "General", "install updates", True))
|
||||||
|
|
||||||
self.device_list.setCurrentIndex(ini.read_ini_int(self.inifile, "Audio", "device", audio.getcurrdevice()))
|
self.device_list.setCurrentIndex(ini.read_ini_int(self.inifile, "Audio", "device", audio.getcurrdevice()))
|
||||||
self.musicslider.setValue(ini.read_ini_int(self.inifile, "Audio", "Music volume", 100))
|
self.musicslider.setValue(ini.read_ini_int(self.inifile, "Audio", "Music volume", 100))
|
||||||
@ -190,6 +202,7 @@ class Settings(QtGui.QDialog):
|
|||||||
self.allowdownload_music.setChecked(True)
|
self.allowdownload_music.setChecked(True)
|
||||||
self.allowdownload_evidence.setChecked(True)
|
self.allowdownload_evidence.setChecked(True)
|
||||||
self.currtheme.setCurrentIndex(self.themes.index("default"))
|
self.currtheme.setCurrentIndex(self.themes.index("default"))
|
||||||
|
self.check_updates.setChecked(True)
|
||||||
|
|
||||||
self.device_list.setCurrentIndex(audio.getcurrdevice())
|
self.device_list.setCurrentIndex(audio.getcurrdevice())
|
||||||
self.musicslider.setValue(100)
|
self.musicslider.setValue(100)
|
||||||
@ -223,6 +236,7 @@ class Settings(QtGui.QDialog):
|
|||||||
self.inifile.set("General", "download music", self.allowdownload_music.isChecked())
|
self.inifile.set("General", "download music", self.allowdownload_music.isChecked())
|
||||||
self.inifile.set("General", "download evidence", self.allowdownload_evidence.isChecked())
|
self.inifile.set("General", "download evidence", self.allowdownload_evidence.isChecked())
|
||||||
self.inifile.set("General", "theme", self.currtheme.currentText())
|
self.inifile.set("General", "theme", self.currtheme.currentText())
|
||||||
|
self.inifile.set("General", "install updates", self.check_updates.isChecked())
|
||||||
|
|
||||||
self.inifile.set("Audio", "device", self.device_list.currentIndex())
|
self.inifile.set("Audio", "device", self.device_list.currentIndex())
|
||||||
self.inifile.set("Audio", "Music volume", self.musicslider.value())
|
self.inifile.set("Audio", "Music volume", self.musicslider.value())
|
||||||
@ -238,3 +252,10 @@ class Settings(QtGui.QDialog):
|
|||||||
|
|
||||||
def onCancelClicked(self):
|
def onCancelClicked(self):
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
|
def onUpdateClicked(self):
|
||||||
|
import updater
|
||||||
|
code = updater.checkForUpdates()
|
||||||
|
if code == 0:
|
||||||
|
subprocess.Popen(["./AO2XPupdat"])
|
||||||
|
_exit(0)
|
184
updater.py
Normal file
184
updater.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import json, urllib, sys, requests, time, os, platform
|
||||||
|
from PyQt4 import QtGui, QtCore
|
||||||
|
from game_version import *
|
||||||
|
|
||||||
|
returncode = -1
|
||||||
|
assetfile = {
|
||||||
|
"Windows": "AO2XP-Windows.zip",
|
||||||
|
"Darwin": "AO2XP-macOS.zip",
|
||||||
|
"Linux": "AO2XP-Linux.tar.gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkForUpdates(force=False):
|
||||||
|
update_dialog = QtGui.QProgressDialog()
|
||||||
|
update_dialog.setWindowTitle("AO2XP updater")
|
||||||
|
update_dialog.setAutoClose(False)
|
||||||
|
update_dialog.setAutoReset(False)
|
||||||
|
update_dialog.setLabelText("Checking for client updates...")
|
||||||
|
update_dialog.resize(512, 96)
|
||||||
|
update_dialog.setMinimum(0)
|
||||||
|
update_dialog.setMaximum(0)
|
||||||
|
update_dialog.setValue(0)
|
||||||
|
|
||||||
|
thr = updateThread(update_dialog, force)
|
||||||
|
|
||||||
|
def setProgressValue(value):
|
||||||
|
update_dialog.setValue(value)
|
||||||
|
def setLabelText(msg):
|
||||||
|
update_dialog.setLabelText(msg)
|
||||||
|
def showMessageBox(icon, title, msg):
|
||||||
|
getattr(QtGui.QMessageBox, str(icon))(None, title, msg)
|
||||||
|
def updateAvailableCall(name, version, body):
|
||||||
|
updateWindow = updateAvailableWidget(name, version, body)
|
||||||
|
result = updateWindow.show()
|
||||||
|
thr.waitConfirm = 1 if result else -1
|
||||||
|
if not result:
|
||||||
|
update_dialog.close()
|
||||||
|
returncode = -2
|
||||||
|
else:
|
||||||
|
update_dialog.setMinimum(0)
|
||||||
|
update_dialog.setMaximum(100)
|
||||||
|
|
||||||
|
thr.progressValue.connect(setProgressValue)
|
||||||
|
thr.labelText.connect(setLabelText)
|
||||||
|
thr.showMessageBox.connect(showMessageBox)
|
||||||
|
thr.updateAvailable.connect(updateAvailableCall)
|
||||||
|
thr.finished.connect(update_dialog.close)
|
||||||
|
thr.start()
|
||||||
|
|
||||||
|
update_dialog.exec_()
|
||||||
|
if not update_dialog.wasCanceled() and thr.isRunning(): thr.wait()
|
||||||
|
|
||||||
|
print "update return code:", returncode
|
||||||
|
return returncode
|
||||||
|
|
||||||
|
class updateAvailableWidget(QtGui.QDialog):
|
||||||
|
def __init__(self, title, version, body):
|
||||||
|
super(self.__class__, self).__init__()
|
||||||
|
self.layout = QtGui.QVBoxLayout(self)
|
||||||
|
self.layout.setAlignment(QtCore.Qt.AlignTop)
|
||||||
|
|
||||||
|
self.available = QtGui.QLabel(text="Changelog for update \"%s\":" % title)
|
||||||
|
self.changelog = QtGui.QTextEdit()
|
||||||
|
self.confirmtext = QtGui.QLabel(text="Do you want to download and install the update?")
|
||||||
|
self.yesnolayout = QtGui.QHBoxLayout()
|
||||||
|
self.yes = QtGui.QPushButton(text="Yes")
|
||||||
|
self.no = QtGui.QPushButton(text="No")
|
||||||
|
|
||||||
|
self.setModal(True)
|
||||||
|
self.setWindowTitle("Update %s available" % version)
|
||||||
|
self.changelog.setText(body)
|
||||||
|
self.changelog.setReadOnly(True)
|
||||||
|
self.yes.clicked.connect(self.confirm)
|
||||||
|
self.no.clicked.connect(self.cancel)
|
||||||
|
|
||||||
|
self.yesnolayout.addWidget(self.yes)
|
||||||
|
self.yesnolayout.addWidget(self.no)
|
||||||
|
self.layout.addWidget(self.available)
|
||||||
|
self.layout.addWidget(self.changelog)
|
||||||
|
self.layout.addWidget(self.confirmtext)
|
||||||
|
self.layout.addLayout(self.yesnolayout, QtCore.Qt.AlignBottom)
|
||||||
|
self.result = False
|
||||||
|
|
||||||
|
self.resize(640, self.sizeHint().height())
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
self.close()
|
||||||
|
self.result = True
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
self.close()
|
||||||
|
self.result = False
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
super(self.__class__, self).show()
|
||||||
|
self.exec_()
|
||||||
|
return self.result
|
||||||
|
|
||||||
|
class updateThread(QtCore.QThread):
|
||||||
|
progressValue = QtCore.pyqtSignal(int)
|
||||||
|
labelText = QtCore.pyqtSignal(str)
|
||||||
|
showMessageBox = QtCore.pyqtSignal(str, str, str)
|
||||||
|
updateAvailable = QtCore.pyqtSignal(str, str, str)
|
||||||
|
finished = QtCore.pyqtSignal()
|
||||||
|
waitConfirm = 0
|
||||||
|
|
||||||
|
def __init__(self, nouis, force):
|
||||||
|
super(self.__class__, self).__init__()
|
||||||
|
self.jm = nouis
|
||||||
|
self.force = force
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
global returncode
|
||||||
|
|
||||||
|
try:
|
||||||
|
manifest = json.load(urllib.urlopen("http://api.github.com/repos/headshot2017/AO2XP/releases"))
|
||||||
|
except:
|
||||||
|
self.showMessageBox.emit("critical", "Error", "Failed to check for updates.\nPlease check your internet connection.")
|
||||||
|
self.finished.emit()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.jm.wasCanceled():
|
||||||
|
return
|
||||||
|
|
||||||
|
if GAME_VERSION != manifest[0]["tag_name"] or self.force: # update available
|
||||||
|
self.updateAvailable.emit(manifest[0]["name"], manifest[0]["tag_name"], manifest[0]["body"])
|
||||||
|
self.waitConfirm = 0
|
||||||
|
|
||||||
|
while self.waitConfirm == 0: pass
|
||||||
|
|
||||||
|
if self.waitConfirm > 0:
|
||||||
|
resume_bytes = 0
|
||||||
|
filename = assetfile[platform.system()]
|
||||||
|
link = ""
|
||||||
|
for asset in manifest[0]["assets"]:
|
||||||
|
if asset["name"] == filename:
|
||||||
|
link = asset["browser_download_url"]
|
||||||
|
|
||||||
|
if not link:
|
||||||
|
self.showMessageBox.emit("critical", "Error", "Release %s is missing the '%s' file.\nCannot continue updating." % (manifest[0]["tag_name"], filename))
|
||||||
|
self.finished.emit()
|
||||||
|
return
|
||||||
|
|
||||||
|
updatezip = "update" + os.path.splitext(filename)[1]
|
||||||
|
if not os.path.exists(updatezip):
|
||||||
|
downloadfile = open(updatezip, "wb")
|
||||||
|
else:
|
||||||
|
existing_data = open(updatezip, "rb").read()
|
||||||
|
downloadfile = open(updatezip, "ab")
|
||||||
|
resume_bytes = len(existing_data)
|
||||||
|
print resume_bytes
|
||||||
|
del existing_data
|
||||||
|
|
||||||
|
self.labelText.emit("Downloading '%s' for update '%s'..." % (filename, manifest[0]["name"]))
|
||||||
|
dl = resume_bytes
|
||||||
|
speed = 0.0
|
||||||
|
start = time.clock()
|
||||||
|
calcspeed_time = time.time()
|
||||||
|
zip = requests.get(link, stream=True, headers={"Range": "bytes=%d-" % resume_bytes})
|
||||||
|
length = resume_bytes + int(zip.headers.get("content-length"))
|
||||||
|
|
||||||
|
for noby in zip.iter_content(chunk_size=4096):
|
||||||
|
if not self.jm.isVisible():
|
||||||
|
downloadfile.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
downloadfile.write(noby)
|
||||||
|
dl += len(noby)
|
||||||
|
percent = 100 * dl / length
|
||||||
|
if percent != self.jm.value():
|
||||||
|
self.progressValue.emit(percent)
|
||||||
|
self.labelText.emit("Downloading '%s' for update '%s'... %.1f KB/s" % (filename, manifest[0]["name"], speed))
|
||||||
|
|
||||||
|
if (time.time() - calcspeed_time) >= 0.5:
|
||||||
|
calcspeed_time = time.time()
|
||||||
|
speed = ((dl-resume_bytes)/(time.clock() - start)) / 1024.
|
||||||
|
self.labelText.emit("Downloading '%s' for update '%s'... %.1f KB/s" % (filename, manifest[0]["name"], speed))
|
||||||
|
|
||||||
|
print "downloaded update"
|
||||||
|
downloadfile.close()
|
||||||
|
returncode = 0
|
||||||
|
else:
|
||||||
|
returncode = -2
|
||||||
|
|
||||||
|
self.finished.emit()
|
Loading…
Reference in New Issue
Block a user