Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion plugins/modformats/ZipMod.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import os
import zipfile

Expand All @@ -9,7 +10,7 @@ def __init__(self, path):
super().__init__(path)
with zipfile.ZipFile(path, 'r') as F:
with F.open("METADATA.json", 'r') as fJ:
self.init_metadata(fJ)
self.init_metadata(json.load(fJ))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good change, but please also

  1. Implement this change on plugins/modformats/NestedZipMod.py
  2. Refer to my comment on the JSONHandler for how to also use in this context

try:
with F.open("DESCRIPTION.html", 'r', encoding="utf8") as fJ:
self.init_description(fJ)
Expand Down
9 changes: 4 additions & 5 deletions src/CoreOperations/ConfigManager.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
import os

from src.Utils.JSONHandler import JSONHandler


class ConfigManager:
__slots__ = ("init",
Expand All @@ -13,7 +15,6 @@ class ConfigManager:
"paths",
"ui")


def __init__(self, ui):
self.init = False
self.ui = ui
Expand All @@ -24,8 +25,7 @@ def __init__(self, ui):
self.__block_pref = 0
self.__first_time_launch = False
self.paths = None



def get_style_pref(self):
return self.__style_pref

Expand Down Expand Up @@ -89,8 +89,7 @@ def load_config(self):
self.__first_time_launch = False

def read_config(self):
with open(os.path.join(self.paths.config_loc, "config.json"), 'r') as F:
config_data = json.load(F)
with JSONHandler(os.path.join(self.paths.config_loc, "config.json"), "Error reading 'config.json'") as config_data:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps extend the message to "SDMM config file 'config.json' is not a valid JSON file" to make it clear to the user that the problematic file isn't inside a mod.

self.__game_loc = config_data.get("game_loc")
self.__lang_pref = config_data.get("language")
self.__style_pref = config_data.get("style")
Expand Down
28 changes: 22 additions & 6 deletions src/CoreOperations/Cymis/CymisParser.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import json
import os
import shutil

from PyQt5 import QtCore

from src.Utils.Path import splitpath
from src.Utils.JSONHandler import JSONHandler

translate = QtCore.QCoreApplication.translate


#####################
# DEFINE FLAG TYPES #
#####################
Expand All @@ -19,6 +21,7 @@ def __init__(self, options):
def get_flag_status(self):
return {self.name: self.value}


class Flag:
def __init__(self, options):
self.name = options['Name']
Expand All @@ -29,6 +32,7 @@ def __init__(self, options):
def get_flag_status(self):
return {self.name: self.value}


class ChooseOne:
def __init__(self, options):
self.type = options['Type']
Expand All @@ -47,6 +51,7 @@ def get_flag_status(self):

wizard_flags = {class_.__name__: class_ for class_ in [HiddenFlag, Flag, ChooseOne]}


############################
# DEFINE BOOLEAN OPERATORS #
############################
Expand All @@ -61,6 +66,7 @@ def and_operator(arguments, operator_list, flag_list):
parsed_flags.append(flag_list[item])
return all(parsed_flags)


def or_operator(arguments, operator_list, flag_list):
parsed_flags = []
for item in arguments:
Expand All @@ -72,6 +78,7 @@ def or_operator(arguments, operator_list, flag_list):
parsed_flags.append(flag_list[item])
return any(parsed_flags)


def not_operator(arguments, operator_list, flag_list):
to_flip = None

Expand All @@ -84,10 +91,12 @@ def not_operator(arguments, operator_list, flag_list):

return not(to_flip)

boolean_operators = {'and': and_operator,

boolean_operators = {'and': and_operator,
'or': or_operator,
'not': not_operator}


#############################
# DEFINE INSTALLATION RULES #
#############################
Expand All @@ -105,18 +114,22 @@ def copy_rule(path_prefix, rule, source, destination):
else:
assert 0, translate("ModWizards::CYMIS::Logging", "{filepath} is neither a file nor a directory.").format(filepath=source)


installation_rules = {'copy': copy_rule}


################
# SAFETY UTILS #
################
def validate_path(path):
path_directories = splitpath(path)
assert not(any([check_if_only_periods(item) for item in path_directories])), translate("ModWizards::CYMIS::Logging", "Paths may not contain relative references.")


def check_if_only_periods(string):
return all(char == '.' for char in string) and len(string) > 1


##########################
# DEFINE CYMIS INSTALLER #
##########################
Expand All @@ -127,13 +140,13 @@ def __init__(self):
self.wizard_pages = []
self.installation_steps = []
self.version = None
self.enable_debug=None
self.enable_debug = None
self.log = None

@classmethod
def init_from_script(cls, filepath, log):
with open(filepath, 'r') as F:
cymis = json.load(F)
with JSONHandler(filepath, f"Error Reading '{filepath}'") as stream:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't call this a stream since it's a dict and not a filestream; maybe change the variable name to 'json_data'.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make the error message f"CYMIS script at {filepath}' is not a valid JSON file"

cymis = stream

instance = cls()
instance.version = cymis['Version']
Expand All @@ -160,6 +173,7 @@ def install_mod(self):
if installer_step.check_should_execute():
installer_step.execute_step()


class CymisInstallerPage:
def __init__(self, page, log=None):
self.title = page.get("Title", translate("ModWizards::CYMIS::FallbackUI", "No Title"))
Expand All @@ -179,6 +193,7 @@ def retrieve_flags(self):
retval.update(flag_status)
return retval


class CymisInstallationStep:
def __init__(self, path_prefix, step_info, flag_table, log):
execution_condition = step_info.get("if")
Expand All @@ -199,6 +214,7 @@ def check():
elif type(execution_condition) == dict:
assert len(execution_condition) == 1, f"More than one boolean operator in dictionary: {execution_condition}."
operator_name, operator_arguments = list(execution_condition.items())[0]

def check():
result = boolean_operators[operator_name](operator_arguments, boolean_operators, flag_table)
if log is not None:
Expand All @@ -217,4 +233,4 @@ def execute_step(self):
self.get_rule(instruction["rule"])(self.path_prefix, **instruction)

def get_rule(self, rule):
return installation_rules[rule]
return installation_rules[rule]
12 changes: 8 additions & 4 deletions src/CoreOperations/ModBuildGraph/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import array
import json
import os
import sys

Expand All @@ -11,10 +9,12 @@
from src.CoreOperations.PluginLoaders.ArchivesPluginLoader import get_archivetype_plugins_dict
from src.CoreOperations.PluginLoaders.FilePacksPluginLoader import get_filepack_plugins_dict, get_filetype_to_filepack_plugins_map
from src.Utils.Path import calc_has_dir_changed_info
from src.Utils.JSONHandler import JSONHandler


translate = QtCore.QCoreApplication.translate


class BuildStep:
__slots__ = ('mod', 'src', 'rule', 'rule_args', 'softcodes')

Expand All @@ -25,6 +25,7 @@ def __init__(self, mod, src, rule, softcodes, *rule_args):
self.rule_args = rule_args
self.softcodes = softcodes


def make_interned_buildstep(dct):
if 'mod' in dct:
softcodes = dct.get('softcodes', {})
Expand All @@ -39,9 +40,11 @@ def make_interned_buildstep(dct):
else:
return dct


def get_interned_mod_index(path):
with open(os.path.join(path, "INDEX.json"), 'r') as F:
return json.load(F, object_hook=make_interned_buildstep)
with JSONHandler(os.path.join(path, "INDEX.json"), "Error reading 'INDEX.json'", object_hook=make_interned_buildstep) as stream:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change 'stream' to 'json_data' or 'index_data' here

return stream


def trim_dead_nodes(build_pipeline, rules):
debug_n_thrown = 0
Expand Down Expand Up @@ -83,6 +86,7 @@ def trim_dead_nodes(build_pipeline, rules):

return list(steps)


def categorise_build_targets(build_graphs, ops, log, updateLog):
filetypes = get_targettable_filetypes()
filepacks = get_filepack_plugins_dict()
Expand Down
9 changes: 6 additions & 3 deletions src/CoreOperations/ModInstallation/PipelineRunners.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from src.CoreOperations.PluginLoaders.FilePacksPluginLoader import get_filepack_plugins_dict
from src.CoreOperations.PluginLoaders.PatchersPluginLoader import get_patcher_plugins_dict
from src.Utils.Signals import StandardRunnableSignals
from src.Utils.JSONHandler import JSONHandler

translate = QtCore.QCoreApplication.translate

patchers = get_patcher_plugins_dict()


class PipelineRunner(QtCore.QRunnable):
__slots__ = ("softcodes", "target", "filepack", "path_prefix", "paths", "cache_index", "archive_postaction", "signals")

Expand Down Expand Up @@ -111,6 +113,7 @@ def execute(self):
except Exception as e:
self.handleException(e)


class ArchivePipelineCollection(QtCore.QObject):
__slots__ = ("archive", "ops", "ui", "softcodes", "threadpool", "pre_message", "cache_index")

Expand Down Expand Up @@ -171,11 +174,11 @@ def make_link(actor):
self.raise_exception.emit(e)

def finalise_build(self):
with open(self.ops.paths.patch_cache_index_loc, 'r') as F:
total_cache = json.load(F)
with JSONHandler(self.ops.paths.patch_cache_index_loc, f"Error reading '{self.ops.paths.patch_cache_index_loc}'") as stream:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe f"SDMM cache index '{self.ops.paths.patch_cache_index_loc}' is not a valid JSON file" might be a clearer error message?

total_cache = stream
Comment on lines +177 to +178
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change 'stream' to 'cache_data'

for file, hashval in self.cache_index.items():
total_cache[os.path.join(self.archive.get_prefix(), file)] = hashval
with open(self.ops.paths.patch_cache_index_loc, 'w') as F:
json.dump(total_cache, F, indent=2)

self.finished.emit()
self.finished.emit()
24 changes: 14 additions & 10 deletions src/CoreOperations/ModInstallation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import json
import os
import sys
import shutil

from PyQt5 import QtCore
Expand All @@ -9,18 +9,21 @@
from src.CoreOperations.ModInstallation.PipelineRunners import ArchivePipelineCollection
from src.CoreOperations.ModInstallation.VariableParser import parse_mod_variables, scan_variables_for_softcodes
from src.CoreOperations.PluginLoaders.FilePacksPluginLoader import get_filepack_plugins_dict
from src.Utils.JSONHandler import JSONHandler
from src.Utils.MBE import mbetable_to_dict, dict_to_mbetable
from libs.dscstools import DSCSTools

translate = QtCore.QCoreApplication.translate


def generate_step_message(cur_items, cur_total):
return translate("ModInstall", "[Step {ratio}]").format(ratio=f"{cur_items}/{cur_total}")



def generate_prefixed_message(cur_items, cur_total, msg):
return f">> {generate_step_message(cur_items, cur_total)} {msg}"
import sys


def format_exception(exception):
return type(exception)(f"Error on line {sys.exc_info()[-1].tb_lineno} in file {__file__}:" + f" {exception}")

Expand Down Expand Up @@ -149,8 +152,8 @@ def process_graph(self, build_graphs, softcode_lookup):

# Create the cache folder if it doesn't exist
os.makedirs(self.ops.paths.patch_cache_loc, exist_ok=True)
with open(self.ops.paths.patch_cache_index_loc, 'r') as F:
cache_index = json.load(F)
with JSONHandler(self.ops.paths.patch_cache_index_loc, f"Error reading '{self.ops.paths.patch_cache_index_loc}") as stream:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "SDMM cache index '{self.ops.paths.patch_cache_index_loc}' is not a valid JSON file" might be a clearer error message?

cache_index = stream
Comment on lines +155 to +156
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change 'stream' to 'cache_data'


# Now prepare the process the build graph
# Init some variables to count the number of packs in the build graph,
Expand Down Expand Up @@ -214,6 +217,7 @@ def sendLog(self, msg):
def sendUpdateLog(self, msg):
self.updateLog.emit(generate_prefixed_message(self.substep, self.nsteps, msg))


class ResourceBootstrapper(QtCore.QObject):
log = QtCore.pyqtSignal(str)
updateLog = QtCore.pyqtSignal(str)
Expand Down Expand Up @@ -246,8 +250,8 @@ def execute(self):
try:
self.log.emit(translate("ModInstall", "{curr_step_msg} Checking required resources...").format(curr_step_msg=self.pre_message))

with open(os.path.join(self.ops.paths.config_loc, "filelist.json")) as F:
resource_archives = json.load(F)
with JSONHandler(os.path.join(self.ops.paths.config_loc, "filelist.json"), "Error reading 'filelist.json'") as stream:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "SDMM config file 'filelist.json' is not a valid JSON file" might be a clearer error message?

resource_archives = stream
Comment on lines +253 to +254
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change 'stream' to 'filelist_data'


required_resources = {}
for archive_type, archives in self.build_graphs.items():
Expand Down Expand Up @@ -368,6 +372,7 @@ def make_link(actor):
except Exception as e:
self.raise_exception.emit(e)


# 1) Skip sort if data isn't in the build graph
# 2) Enumerate the sorts
# 3) Make it work for all MDB1 archives
Expand Down Expand Up @@ -623,6 +628,7 @@ def sortmode_compress_keygen_digimarket(self, item_id, build_item_para, build_it
name = self.name_getter(item_id[0], build_item_name, 1)[1]
return (int(item_sort_id), name.encode('utf8'))


class ArchiveBuilder(QtCore.QObject):
finished = QtCore.pyqtSignal()
clean_up = QtCore.pyqtSignal()
Expand Down Expand Up @@ -777,5 +783,3 @@ def install(self):
self.thread.start()
except Exception as e:
self.raise_exception.emit(e)


Loading