Skip to content
This repository has been archived by the owner on Apr 21, 2023. It is now read-only.

Commit

Permalink
2.4.0 stuff
Browse files Browse the repository at this point in the history
Significant improvements to text merging for multiple language support,
fixed bug with uninstalling game data mods,
support for 1-click GameBanana downloads
  • Loading branch information
NiceneNerd committed Oct 1, 2019
1 parent b69276f commit 1b8633f
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 118 deletions.
19 changes: 17 additions & 2 deletions bcml/Ui_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Form implementation generated from reading ui file 'c:\Users\macad\Documents\Git\BCML-2\.vscode\settings.ui',
# licensing of 'c:\Users\macad\Documents\Git\BCML-2\.vscode\settings.ui' applies.
#
# Created: Fri Sep 13 23:11:59 2019
# Created: Tue Oct 1 11:32:50 2019
# by: pyside2-uic running on PySide2 5.13.0
#
# WARNING! All changes made in this file will be lost!
Expand All @@ -13,7 +13,7 @@
class Ui_SettingsDialog(object):
def setupUi(self, SettingsDialog):
SettingsDialog.setObjectName("SettingsDialog")
SettingsDialog.resize(400, 200)
SettingsDialog.resize(333, 248)
self.verticalLayout = QtWidgets.QVBoxLayout(SettingsDialog)
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(SettingsDialog)
Expand Down Expand Up @@ -55,6 +55,20 @@ def setupUi(self, SettingsDialog):
self.btnBrowseMlc.setObjectName("btnBrowseMlc")
self.horizontalLayout_3.addWidget(self.btnBrowseMlc)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.label_4 = QtWidgets.QLabel(SettingsDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
self.label_4.setSizePolicy(sizePolicy)
self.label_4.setObjectName("label_4")
self.horizontalLayout_4.addWidget(self.label_4)
self.drpLang = QtWidgets.QComboBox(SettingsDialog)
self.drpLang.setObjectName("drpLang")
self.horizontalLayout_4.addWidget(self.drpLang)
self.verticalLayout.addLayout(self.horizontalLayout_4)
self.chkDark = QtWidgets.QCheckBox(SettingsDialog)
self.chkDark.setObjectName("chkDark")
self.verticalLayout.addWidget(self.chkDark)
Expand All @@ -79,5 +93,6 @@ def retranslateUi(self, SettingsDialog):
self.btnBrowseGame.setText(QtWidgets.QApplication.translate("SettingsDialog", "Browse...", None, -1))
self.label_3.setText(QtWidgets.QApplication.translate("SettingsDialog", "MLC Directory", None, -1))
self.btnBrowseMlc.setText(QtWidgets.QApplication.translate("SettingsDialog", "Browse...", None, -1))
self.label_4.setText(QtWidgets.QApplication.translate("SettingsDialog", "Game Language", None, -1))
self.chkDark.setText(QtWidgets.QApplication.translate("SettingsDialog", "Use dark theme", None, -1))

103 changes: 73 additions & 30 deletions bcml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import urllib.error
import urllib.request
import zipfile
#import glob
from collections import namedtuple
from configparser import ConfigParser
from pathlib import Path
Expand Down Expand Up @@ -75,7 +74,7 @@ def __init__(self):

self.btnPackage = QtWidgets.QToolButton(self.statusBar())
self.btnPackage.setIcon(util.get_icon('pack.png'))
self.btnPackage.setToolTip('Create BCNL Nano Patch Mod')
self.btnPackage.setToolTip('Create BNP Mod')
self.btnSettings = QtWidgets.QToolButton(self.statusBar())
self.btnSettings.setIcon(util.get_icon('settings.png'))
self.btnSettings.setToolTip('Settings')
Expand Down Expand Up @@ -139,7 +138,8 @@ def SetupChecks(self):
except FileNotFoundError:
QtWidgets.QMessageBox.information(
self, 'First Time', 'It looks like this may be your first time running BCML.'
'Please select the directory where Cemu is installed.')
'Please select the directory where Cemu is installed. You can '
'tell it\'s right if it contains <code>Cemu.exe</code>.')
folder = QFileDialog.getExistingDirectory(
self, 'Select Cemu Directory')
if folder:
Expand All @@ -153,7 +153,9 @@ def SetupChecks(self):
except FileNotFoundError:
QtWidgets.QMessageBox.information(
self, 'Dump Location', 'BCML needs to know the location of your game dump. '
'Please select the "content" directory in your dumped copy of Breath of the Wild.')
'Please select the "content" directory in your dumped copy of Breath of the Wild. '
'Note this is <em>not</em> usually inside Cemu\'s MLC folder. This is where the '
'base game in installed, not update or DLC data.')
folder = '/'
while not (Path(folder).parent / 'code' / 'app.xml').exists():
folder = QFileDialog.getExistingDirectory(
Expand Down Expand Up @@ -189,25 +191,19 @@ def SetupChecks(self):
sys.exit(0)
else:
sys.exit(0)

ver = platform.python_version_tuple()
if int(ver[0]) < 3 or (int(ver[0]) >= 3 and int(ver[1]) < 7):
QtWidgets.QMessageBox.warning(
if 'lang' not in util.get_settings() or not util.get_settings()['lang']:
lang, okay = QtWidgets.QInputDialog.getItem(
self,
'Error',
f'BCML requires Python 3.7 or higher, but your Python version is {ver[0]}.{ver[1]}'
'Select Language',
'Select the regional language you\nuse to play Breath of the Wild',
texts.LANGUAGES,
0,
False,
flags=QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint
)
sys.exit(0)

is_64bits = sys.maxsize > 2**32
if not is_64bits:
QtWidgets.QMessageBox.warning(
self,
'Error',
'BCML requires 64 bit Python, but it looks like you\'re running 32 bit.'
)
sys.exit(0)

if okay and lang:
util.get_settings()['lang'] = lang
util.save_settings()
self.LoadMods()

def LoadMods(self):
Expand Down Expand Up @@ -440,11 +436,8 @@ def MergePacks_Clicked(self):

def MergeTexts_Clicked(self):
def all_texts():
text_mods = set()
for text_mod in util.get_modpack_dir().rglob('**/texts_*.yml'):
text_mods.add(util.get_file_language(text_mod))
for mod in text_mods:
texts.merge_texts(mod)
tm = texts.TextsMerger()
tm.perform_merge()

self.PerformOperation(all_texts, title='Remerging Texts')

Expand Down Expand Up @@ -886,6 +879,8 @@ def __init__(self, *args, **kwargs):
self.txtGameDump.setText(str(util.get_game_dir()))
self.txtMlc.setText(str(util.get_mlc_dir()))
self.chkDark.setChecked(util.get_settings_bool('dark_theme'))
self.drpLang.addItems(texts.LANGUAGES)
self.drpLang.setCurrentText(util.get_settings()['lang'])

self.btnBrowseCemu.clicked.connect(self.BrowseCemuClicked)
self.btnBrowseGame.clicked.connect(self.BrowseGameClicked)
Expand Down Expand Up @@ -938,6 +933,8 @@ def accept(self):
QtWidgets.QApplication.instance().setStyleSheet(DARK_THEME)
else:
QtWidgets.QApplication.instance().setStyleSheet('')
util.get_settings()['lang'] = self.drpLang.currentText()
util.save_settings()
return super().accept()

# About Dialog
Expand Down Expand Up @@ -1171,21 +1168,67 @@ def libyaz_load():

# Main

def uri_validator(x):
from urllib.parse import urlparse
try:
result = urlparse(x)
return all([result.scheme, result.netloc, result.path])
except Exception: # pylint: disable=broad-except
return False


def process_args() -> Path:
try:
if Path(sys.argv[1]).exists():
return Path(sys.argv[1])
except WindowsError:
try_url = sys.argv[1].replace('bcml:', '')
if uri_validator(try_url) and 'gamebanana.com' in try_url:
from tempfile import NamedTemporaryFile
try:
with NamedTemporaryFile('wb', prefix='GameBanana',
suffix='.bnp', delete=False) as tmp:
tmp.write(urllib.request.urlopen(try_url).read())
return Path(tmp.name)
except Exception: # pylint: disable=broad-except
pass
return None


def main():
util.clear_temp_dir()
util.create_schema_handler()
libyaz_load()
app = QtWidgets.QApplication([])
ver = platform.python_version_tuple()
if int(ver[0]) < 3 or (int(ver[0]) >= 3 and int(ver[1]) < 7):
QtWidgets.QMessageBox.warning(
None,
'Error',
f'BCML requires Python 3.7 or higher, but your Python version is {ver[0]}.{ver[1]}'
)
sys.exit(0)

is_64bits = sys.maxsize > 2**32
if not is_64bits:
QtWidgets.QMessageBox.warning(
None,
'Error',
'BCML requires 64 bit Python, but it looks like you\'re running 32 bit.'
)
sys.exit(0)
if util.get_settings_bool('dark_theme'):
app.setStyleSheet(DARK_THEME)
if 'Roboto Lt' in QtGui.QFontDatabase().families(QtGui.QFontDatabase.Latin):
app.setFont(QtGui.QFont('Roboto Lt', 10, weight=QtGui.QFont.DemiBold))
if 'Roboto Lt' in QtGui.QFontDatabase().families(QtGui.QFontDatabase.Latin):
app.setFont(QtGui.QFont('Roboto Lt', 10, weight=QtGui.QFont.DemiBold))
application = MainWindow()
try:
application.show()
application.SetupChecks()
if len(sys.argv) > 1 and Path(sys.argv[1]).exists():
application.InstallClicked(Path(sys.argv[1]))
if len(sys.argv) > 1:
parg = process_args()
if parg:
application.InstallClicked(parg)
app.exec_()
except Exception: # pylint: disable=broad-except
tb = traceback.format_exc(limit=-2)
Expand Down
24 changes: 16 additions & 8 deletions bcml/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,18 @@ def get_savedata_mods() -> List[BcmlMod]:
return sorted(sdata_mods, key=lambda mod: mod.priority)


def merge_gamedata(verbose: bool = False):
def merge_gamedata(verbose: bool = False, force: bool = False):
""" Merges installed gamedata mods and saves the new Bootup.pack, fixing the RSTB if needed """
mods = get_gamedata_mods()
glog_path = util.get_master_modpack_dir() / 'logs' / 'gamedata.log'
if not mods:
print('No gamedata merging necessary.')
if glog_path.exists():
glog_path.unlink()
if (util.get_master_modpack_dir() / 'logs' / 'gamedata.sarc').exists():
(util.get_master_modpack_dir() / 'logs' / 'gamedata.sarc').unlink()
return
if glog_path.exists():
if glog_path.exists() and not force:
with glog_path.open('r') as l_file:
if xxhash.xxh32(str(mods)).hexdigest() == l_file.read():
print('No gamedata merging necessary.')
Expand Down Expand Up @@ -339,17 +340,18 @@ def merge_gamedata(verbose: bool = False):
l_file.write(xxhash.xxh32(str(mods)).hexdigest())


def merge_savedata(verbose: bool = False):
def merge_savedata(verbose: bool = False, force: bool = False):
""" Merges install savedata mods and saves the new Bootup.pack, fixing the RSTB if needed"""
mods = get_savedata_mods()
slog_path = util.get_master_modpack_dir() / 'logs' / 'savedata.log'
if not mods:
print('No gamedata merging necessary.')
if slog_path.exists():
slog_path.unlink()
if (util.get_master_modpack_dir() / 'logs' / 'savedata.sarc').exists():
(util.get_master_modpack_dir() / 'logs' / 'savedata.sarc').unlink()
return
if slog_path.exists():
if slog_path.exists() and not force:
with slog_path.open('r') as l_file:
if xxhash.xxh32(str(mods)).hexdigest() == l_file.read():
print('No savedata merging necessary.')
Expand Down Expand Up @@ -582,10 +584,13 @@ def consolidate_diffs(self, diffs: list):
return all_diffs

def perform_merge(self):
merge_gamedata()
force = False
if 'force' in self._options:
force = self._options['force']
merge_gamedata(force=force)

def get_checkbox_options(self):
return []
return [('force', 'Remerge game data even if no changes detected')]

@staticmethod
def is_bootup_injector():
Expand Down Expand Up @@ -657,10 +662,13 @@ def consolidate_diffs(self, diffs: list):
return all_diffs

def perform_merge(self):
merge_savedata()
force = False
if 'force' in self._options:
force = self._options['force']
merge_savedata(force=force)

def get_checkbox_options(self):
return []
return [('force', 'Remerge save data even if no changes detected')]

@staticmethod
def is_bootup_injector():
Expand Down
1 change: 1 addition & 0 deletions bcml/data/msyt/lang_hashes.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bcml/data/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version='2.3.4'
version='2.4.0'
2 changes: 2 additions & 0 deletions bcml/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ def install_mod(mod: Path, verbose: bool = False, options: dict = {}, wait_merge
else:
try:
print('Performing merges...')
if 'disable' not in options:
options['disable'] = []
for merger in mergers.sort_mergers([cls() for cls in mergers.get_mergers() \
if cls.NAME not in options['disable']]):
if merger.NAME in options:
Expand Down
Loading

0 comments on commit 1b8633f

Please sign in to comment.