fix: checkboxes changes saved

This commit is contained in:
RedDeadDepresso
2024-07-24 03:46:19 +01:00
parent 7a6d956c48
commit 977f701eef
14 changed files with 388 additions and 309 deletions

View File

@@ -60,13 +60,13 @@ class Config(QConfig):
# fckks
fckksEnable = ConfigItem(
"FCKKS", "Enable", False, BoolValidator()
"FilterConvertKKS", "Enable", False, BoolValidator()
)
fccksPath = ConfigItem(
"FCKKS", "InputPath", "", FolderValidator()
"FilterConvertKKS", "InputPath", "", FolderValidator()
)
convert = ConfigItem(
"FCKKS", "Convert", False, BoolValidator()
"FilterConvertKKS", "Convert", False, BoolValidator()
)
# installChara
@@ -102,44 +102,6 @@ class Config(QConfig):
# software update
checkUpdateAtStartUp = ConfigItem("Update", "CheckUpdateAtStartUp", True, BoolValidator())
# def validate(self):
# Logger.log_info("SCRIPT", "Validating config")
# self.ok = True
# self.tasks = self.config_data["Core"]["Tasks"]
# self.create_gamepath()
# for task in self.tasks:
# if self.config_data[task]["Enable"]:
# if "InputPath" in self.config_data[task]:
# path = self.config_data[task]["InputPath"]
# elif "OutputPath" in self.config_data[task]:
# path = self.config_data[task]["OutputPath"]
# if not os.path.exists(path):
# Logger.log_error("SCRIPT", f"Path invalid for task {task}")
# raise Exception()
# self.install_chara = self.config_data.get("InstallChara", {})
# self.create_backup = self.config_data.get("CreateBackup", {})
# self.remove_chara = self.config_data.get("RemoveChara", {})
# self.fc_kks = self.config_data.get("FCKKS", {})
# def create_gamepath(self):
# base = self.config_data["Core"]["GamePath"]
# self.game_path = {
# "base": base,
# "UserData": os.path.join(base, "UserData"),
# "BepInEx": os.path.join(base, "BepInEx"),
# "mods": os.path.join(base, "mods"),
# "chara": os.path.join(base, "UserData\\chara\\female"),
# "coordinate": os.path.join(base, "UserData\coordinate"),
# "Overlays": os.path.join(base, "UserData\Overlays")
# }
# for path in self.game_path.values():
# if not os.path.exists(path):
# Logger.log_error("SCRIPT", "Game path not valid")
# raise Exception()
YEAR = 2023
AUTHOR = "zhiyiYo"

View File

@@ -14,98 +14,98 @@ class FileManager:
def __init__(self, config):
self.config = config
def find_all_files(self, directory):
file_list = []
compressed_file_list = []
compressed_extensions = [".rar", ".zip", ".7z"]
def findAllFiles(self, directory):
fileList = []
archiveList = []
archiveExtensions = [".rar", ".zip", ".7z"]
for root, dirs, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
file_size = os.path.getsize(file_path)
_, file_extension = os.path.splitext(file_path)
filePath = os.path.join(root, file)
fileSize = os.path.getsize(filePath)
_, fileExtension = os.path.splitext(filePath)
if file_extension in compressed_extensions:
compressed_file_list.append((file_path, file_size, file_extension))
if fileExtension in archiveExtensions:
archiveList.append((filePath, fileSize, fileExtension))
else:
file_list.append((file_path, file_size, file_extension))
fileList.append((filePath, fileSize, fileExtension))
file_list.sort(key=lambda x: x[1])
compressed_file_list.sort(key=lambda x: x[1])
return file_list, compressed_file_list
fileList.sort(key=lambda x: x[1])
archiveList.sort(key=lambda x: x[1])
return fileList, archiveList
def copy_and_paste(self, type, source_path, destination_folder):
source_path = source_path[0]
base_name = os.path.basename(source_path)
destination_path = os.path.join(destination_folder, base_name)
def copyAndPaste(self, type, sourcePath, destinationFolder):
sourcePath = sourcePath[0]
baseName = os.path.basename(sourcePath)
destinationPath = os.path.join(destinationFolder, baseName)
conflicts = self.config.install_chara["FileConflicts"]
already_exists = os.path.exists(destination_path)
alreadyExists = os.path.exists(destinationPath)
if already_exists and conflicts == "Skip":
logger.log_skipped(type, base_name)
if alreadyExists and conflicts == "Skip":
logger.skipped(type, baseName)
return
elif already_exists and conflicts == "Replace":
logger.log_replaced(type, base_name)
elif alreadyExists and conflicts == "Replace":
logger.replaced(type, baseName)
elif already_exists and conflicts == "Rename":
max_retries = 3
for attempt in range(max_retries):
elif alreadyExists and conflicts == "Rename":
maxRetries = 3
for attempt in range(maxRetries):
try:
filename, file_extension = os.path.splitext(source_path)
new_name = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')
new_source_path = f"{filename}_{new_name}{file_extension}"
os.rename(source_path, new_source_path)
source_path = new_source_path
logger.log_renamed(type, base_name)
filename, fileExtension = os.path.splitext(sourcePath)
newName = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')
newSourcePath = f"{filename}_{newName}{fileExtension}"
os.rename(sourcePath, newSourcePath)
sourcePath = newSourcePath
logger.renamed(type, baseName)
filename, file_extension = os.path.splitext(destination_path)
destination_path = f"{filename}_{new_name}{file_extension}"
filename, fileExtension = os.path.splitext(destinationPath)
destinationPath = f"{filename}_{newName}{fileExtension}"
break # Exit the loop if renaming is successful
except PermissionError:
if attempt < max_retries - 1:
if attempt < maxRetries - 1:
time.sleep(1) # Wait for 1 second before retrying
else:
logger.log_error(type, f"Failed to rename {base_name} after {max_retries} attempts.")
logger.error(type, f"Failed to rename {baseName} after {maxRetries} attempts.")
return
try:
shutil.copy(source_path, destination_path)
print(f"File copied successfully from {source_path} to {destination_path}")
if not already_exists:
logger.log_success(type, base_name)
shutil.copy(sourcePath, destinationPath)
print(f"File copied successfully from {sourcePath} to {destinationPath}")
if not alreadyExists:
logger.success(type, baseName)
except FileNotFoundError:
logger.log_error(type, f"{base_name} does not exist.")
logger.error(type, f"{baseName} does not exist.")
except PermissionError:
logger.log_error(type, f"Permission denied for {base_name}")
logger.error(type, f"Permission denied for {baseName}")
except Exception as e:
logger.log_error(type, f"An error occurred: {e}")
logger.error(type, f"An error occurred: {e}")
def find_and_remove(self, type, source_path, destination_folder):
source_path = source_path[0]
base_name = os.path.basename(source_path)
destination_path = os.path.join(destination_folder, base_name)
if os.path.exists(destination_path):
def findAndRemove(self, type, sourcePath, destinationFolder):
sourcePath = sourcePath[0]
baseName = os.path.basename(sourcePath)
destinationPath = os.path.join(destinationFolder, baseName)
if os.path.exists(destinationPath):
try:
os.remove(destination_path)
logger.log_removed(type, base_name)
os.remove(destinationPath)
logger.removed(type, baseName)
except OSError as e:
logger.log_error(type, base_name)
logger.error(type, baseName)
def create_archive(self, folders, archive_path):
def createArchive(self, folders, archivePath):
# Specify the full path to the 7zip executable
path_to_7zip = patoolib.util.find_program("7z")
if not path_to_7zip:
logger.log_error("SCRIPT", "7zip not found. Unable to create backup")
pathTo7zip = patoolib.util.find_program("7z")
if not pathTo7zip:
logger.error("SCRIPT", "7zip not found. Unable to create backup")
raise Exception()
if os.path.exists(archive_path+".7z"):
os.remove(archive_path+".7z")
if os.path.exists(archivePath+".7z"):
os.remove(archivePath+".7z")
path_to_7zip = f'"{path_to_7zip}"'
archive_path = f'"{archive_path}"'
exclude_folders = [
pathTo7zip = f'"{pathTo7zip}"'
archivePath = f'"{archivePath}"'
excludeFolders = [
'"Sideloader Modpack"',
'"Sideloader Modpack - Studio"',
'"Sideloader Modpack - KK_UncensorSelector"',
@@ -118,52 +118,52 @@ class FileManager:
]
# Create a string of folder names to exclude
exclude_string = ''
for folder in exclude_folders:
exclude_string += f'-xr!{folder} '
excludeString = ''
for folder in excludeFolders:
excludeString += f'-xr!{folder} '
# Create a string of folder names to include
include_string = ''
includeString = ''
for folder in folders:
include_string += f'"{folder}" '
includeString += f'"{folder}" '
# Construct the 7zip command
command = f'{path_to_7zip} a -t7z {archive_path} {include_string} {exclude_string}'
command = f'{pathTo7zip} a -t7z {archivePath} {includeString} {excludeString}'
# Call the command
process = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Print the output
for line in process.stdout.decode().split('\n'):
if line.strip() != "":
logger.log_info("7-Zip", line)
logger.info("7-Zip", line)
# Check the return code
if process.returncode not in [0, 1]:
raise Exception()
def extract_archive(self, archive_path):
def extractArchive(self, archivePath):
try:
archive_name = os.path.basename(archive_path)
logger.log_info("ARCHIVE", f"Extracting {archive_name}")
extract_path = os.path.join(f"{os.path.splitext(archive_path)[0]}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}")
patoolib.extract_archive(archive_path, outdir=extract_path)
return extract_path
archiveName = os.path.basename(archivePath)
logger.info("ARCHIVE", f"Extracting {archiveName}")
extractPath = os.path.join(f"{os.path.splitext(archivePath)[0]}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}")
patoolib.extract_archive(archivePath, outdir=extractPath)
return extractPath
except patoolib.util.PatoolError as e:
text = f"There is an error with the archive {archive_name} but it is impossible to detect the cause. Maybe it requires a password?"
text = f"There is an error with the archive {archiveName} but it is impossible to detect the cause. Maybe it requires a password?"
while self.config.install_chara["Password"] == "Request Password":
try:
dialog = customtkinter.CTkInputDialog(text=text, title="Enter Password")
password = dialog.get_input()
if password is not None or "":
patoolib.extract_archive(archive_path, outdir=extract_path, password=password)
return extract_path
patoolib.extract_archive(archivePath, outdir=extractPath, password=password)
return extractPath
else:
break
except:
text = f"Wrong password or {archive_name} is corrupted. Please enter password again or click Cancel"
text = f"Wrong password or {archiveName} is corrupted. Please enter password again or click Cancel"
logger.log_skipped("ARCHIVE", archive_name)
logger.skipped("ARCHIVE", archiveName)
fileManager = FileManager()

View File

@@ -26,7 +26,7 @@ class Logger:
self.logger.setLevel(logging.INFO)
self.logger.addHandler(handler1)
def __out__(self, message: str, level: int = 1, raw_print=False) -> None:
def __out__(self, category: str, message: str, level: int = 1, raw_print=False) -> None:
"""
Output log
:param message: log message
@@ -41,10 +41,13 @@ class Logger:
while len(logging.root.handlers) > 0:
logging.root.handlers.pop()
# Status Text: INFO, WARNING, ERROR, CRITICAL
status = ['&nbsp;&nbsp;&nbsp;&nbsp;INFO', '&nbsp;WARNING', '&nbsp;&nbsp;&nbsp;ERROR', 'CRITICAL']
# Status Color: Blue, Orange, Red, Purple
statusColor = ['#2d8cf0', '#f90', '#ed3f14', '#3e0480']
# Status Text: INFO, SUCCESS, ERROR, SKIPPED, REPLACED, RENAMED, REMOVED
status = ['&nbsp;&nbsp;&nbsp;&nbsp;INFO', '&nbsp;&nbsp;SUCCESS', '&nbsp;&nbsp;&nbsp;ERROR',
'&nbsp;SKIPPED', '&nbsp;REPLACED', '&nbsp;RENAMED', '&nbsp;REMOVED']
# Status Color: Blue, Red, Green, Orange,
statusColor = ['#2d8cf0', '#ed3f14', '#f90', '#f90', '#f90', '#f90', '#00c12b']
# Status HTML: <b style="color:$color">status</b>
statusHtml = [
f'<b style="color:{_color};">{status}</b>'
@@ -55,7 +58,7 @@ class Logger:
message = message.replace('\n', '<br>').replace(' ', '&nbsp;')
adding = (f'''
<div style="font-family: Consolas, monospace;color:{statusColor[level - 1]};">
{statusHtml[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}
{statusHtml[level - 1]} | {category} | {message}
</div>
''')
self.logs += adding
@@ -63,37 +66,61 @@ class Logger:
else:
print(f'{statusHtml[level - 1]} | {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | {message}')
def info(self, message: str) -> None:
def info(self, category: str, message: str) -> None:
"""
:param message: log message
Output info log
"""
self.__out__(message, 1)
self.__out__(category, message, 1)
def warning(self, message: str) -> None:
"""
:param message: log message
Output warn log
"""
self.__out__(message, 2)
def error(self, message: Union[str, Exception]) -> None:
def success(self, category: str, message: Union[str, Exception]) -> None:
"""
:param message: log message
Output error log
"""
self.__out__(message, 3)
self.__out__(category, message, 2)
def critical(self, message: str) -> None:
def error(self, category: str, message: Union[str, Exception]) -> None:
"""
:param message: log message
Output critical log
Output error log
"""
self.__out__(message, 4)
self.__out__(category, message, 3)
def skipped(self, category: str, message: str) -> None:
"""
:param message: log message
Output warn log
"""
self.__out__(category, message, 4)
def replaced(self, category: str, message: str) -> None:
"""
:param message: log message
Output warn log
"""
self.__out__(category, message, 5)
def renamed(self, category: str, message: str) -> None:
"""
:param message: log message
Output warn log
"""
self.__out__(category, message, 6)
def removed(self, category: str, message: str) -> None:
"""
:param message: log message
Output warn log
"""
self.__out__(category, message, 6)
def line(self) -> None:
"""

View File

@@ -6,13 +6,16 @@ from qfluentwidgets import ToolButton, CheckBox
from qfluentwidgets.common.icon import FluentIcon as FIF
from app.common.signal_bus import signalBus
from app.common.config import cfg
class NavigationCheckBox(QWidget):
def __init__(self, text, settingGroup) -> None:
def __init__(self, text, configItem, settingGroup) -> None:
super().__init__()
self.layout = QHBoxLayout()
self.checkBox = CheckBox(text)
self.configItem = configItem
self.loadCheckState()
self.toolButton = ToolButton(FIF.SETTING)
self.settingGroup = settingGroup
@@ -25,10 +28,22 @@ class NavigationCheckBox(QWidget):
self.setLayout(self.layout)
def __connectSignalToSlot(self):
self.checkBox.stateChanged.connect(self.saveCheckState)
self.toolButton.clicked.connect(lambda: signalBus.switchToSettingGroup.emit(self.settingGroup))
signalBus.selectAllClicked.connect(lambda: self.checkBox.setChecked(True))
signalBus.clearAllClicked.connect(lambda: self.checkBox.setChecked(False))
def loadCheckState(self):
value = cfg.get(self.configItem)
if value:
self.checkBox.setCheckState(Qt.CheckState.Checked)
else:
self.checkBox.setCheckState(Qt.CheckState.Unchecked)
def saveCheckState(self, state):
isChecked = state > 0
cfg.set(self.configItem, isChecked)

View File

@@ -1,17 +0,0 @@
from app.common.config import cfg
class Request:
def __init__(self) -> None:
pass
class Handler:
def __init__(self) -> None:
pass
def set_next(self):
pass
def handle(self):
pass

View File

@@ -1,24 +1,25 @@
import os
class CreateBackup:
def __init__(self, config, file_manager):
"""Initializes the Bounty module.
from app.common.file_manager import fileManager
from app.modules.handler import Handler
Args:
config (Config): BAAuto Config instance
"""
self.config = config
self.file_manager = file_manager
self.backup_folders = self.config.create_backup["GameFolders"]
self.filename = self.config.create_backup["Filename"]
self.output_path = self.config.create_backup["OutputPath"]
self.game_path = self.config.game_path
def logic_wrapper(self):
selected_folders = [self.game_path[folder] for folder in self.backup_folders if self.backup_folders[folder]]
output_path = os.path.join(self.output_path, self.filename)
self.file_manager.create_archive(selected_folders, output_path)
class CreateBackup(Handler):
def __str__(self) -> str:
return "Create Backup"
def loadConfig(self, config):
super().loadConfig(config)
folders = ["mods", "UserData", "BepInEx"]
self.folders = [self.gamePath[f] for f in folders if self.config[f]]
self.outputPath = self.config["OutputPath"]
self.filename = self.config["Filename"]
def handle(self, request):
outputPath = os.path.join(self.outputPath, self.filename)
fileManager.createArchive(self.folders, outputPath)
self.setNext(request)

View File

@@ -2,106 +2,107 @@ import os
import re as regex
import codecs
import shutil
from util.logger import Logger
class FilterConvertKKS:
def __init__(self, config, file_manager):
"""Initializes the Bounty module.
from app.common.logger import logger
from app.modules.handler import Handler
Args:
config (Config): BAAuto Config instance
"""
self.config = config
self.file_manager = file_manager
self.convert = self.config.fc_kks["Convert"]
def get_list(self, folder_path):
new_list = []
for root, dirs, files in os.walk(folder_path):
class FilterConvertKKS(Handler):
def __str__(self) -> str:
return "Filter Convert KKS"
def loadConfig(self, config):
super().loadConfig(config)
self.convert = self.config["Convert"]
def getList(self, folderPath):
newList = []
for root, dirs, files in os.walk(folderPath):
for filename in files:
if regex.match(r".*(\.png)$", filename):
new_list.append(os.path.join(root, filename))
return new_list
newList.append(os.path.join(root, filename))
return newList
def check_png(self, card_path):
with codecs.open(card_path, "rb") as card:
def checkPng(self, cardPath):
with codecs.open(cardPath, "rb") as card:
data = card.read()
card_type = 0
cardType = 0
if data.find(b"KoiKatuChara") != -1:
card_type = 1
cardType = 1
if data.find(b"KoiKatuCharaSP") != -1:
card_type = 2
cardType = 2
elif data.find(b"KoiKatuCharaSun") != -1:
card_type = 3
Logger.log_info(f"[{card_type}]", f"{card_path}")
return card_type
cardType = 3
logger.info(f"[{cardType}]", f"{cardPath}")
return cardType
def convert_kk(self, card_name, card_path, destination_path):
with codecs.open(card_path, mode="rb") as card:
def convertKk(self, cardName, cardPath, destinationPath):
with codecs.open(cardPath, mode="rb") as card:
data = card.read()
replace_list = [
replaceList = [
[b"\x15\xe3\x80\x90KoiKatuCharaSun", b"\x12\xe3\x80\x90KoiKatuChara"],
[b"Parameter\xa7version\xa50.0.6", b"Parameter\xa7version\xa50.0.5"],
[b"version\xa50.0.6\xa3sex", b"version\xa50.0.5\xa3sex"],
]
for text in replace_list:
for text in replaceList:
data = data.replace(text[0], text[1])
new_file_path = os.path.normpath(os.path.join(destination_path, f"KKS2KK_{card_name}"))
# print(f"new_file_path {new_file_path}")
newFilePath = os.path.normpath(os.path.join(destinationPath, f"KKS2KK_{cardName}"))
with codecs.open(new_file_path, "wb") as new_card:
new_card.write(data)
with codecs.open(newFilePath, "wb") as newCard:
newCard.write(data)
def logic_wrapper(self):
def handle(self, request):
path = self.config.fc_kks["InputPath"]
kks_card_list = []
kks_folder = "_KKS_card_"
kks_folder2 = "_KKS_to_KK_"
kksCardList = []
kksFolder = "_KKS_card_"
kksFolder2 = "_KKS_to_KK_"
png_list = self.get_list(path)
pngList = self.getList(path)
count = len(png_list)
count = len(pngList)
if count > 0:
Logger.log_info("SCRIPT", "0: unknown / 1: kk / 2: kksp / 3: kks")
for png in png_list:
if self.check_png(png) == 3:
kks_card_list.append(png)
logger.info("SCRIPT", "0: unknown / 1: kk / 2: kksp / 3: kks")
for png in pngList:
if self.checkPng(png) == 3:
kksCardList.append(png)
else:
Logger.log_success("SCRIPT", f"no PNG found")
logger.success("SCRIPT", f"no PNG found")
return
count = len(kks_card_list)
count = len(kksCardList)
if count > 0:
print(kks_card_list)
print(kksCardList)
target_folder = os.path.normpath(os.path.join(path, kks_folder))
target_folder2 = os.path.normpath(os.path.join(path, kks_folder2))
if not os.path.isdir(target_folder):
os.mkdir(target_folder)
targetFolder = os.path.normpath(os.path.join(path, kksFolder))
targetFolder2 = os.path.normpath(os.path.join(path, kksFolder2))
if not os.path.isdir(targetFolder):
os.mkdir(targetFolder)
if self.convert:
Logger.log_info("SCRIPT", f"Conversion to KK is [{self.convert}]")
if not os.path.isdir(target_folder2):
os.mkdir(target_folder2)
logger.info("SCRIPT", f"Conversion to KK is [{self.convert}]")
if not os.path.isdir(targetFolder2):
os.mkdir(targetFolder2)
for card_path in kks_card_list:
source = card_path
card = os.path.basename(card_path)
target = os.path.normpath(os.path.join(target_folder, card))
for cardPath in kksCardList:
source = cardPath
card = os.path.basename(cardPath)
target = os.path.normpath(os.path.join(targetFolder, card))
# copy & convert before move
if self.convert:
self.convert_kk(card, source, target_folder2)
self.convertKk(card, source, targetFolder2)
# move file
shutil.move(source, target)
if self.convert:
Logger.log_success("SCRIPT", f"[{count}] cards moved to [{kks_folder}] folder, converted and save to [{kks_folder2}] folder")
logger.success("SCRIPT", f"[{count}] cards moved to [{kksFolder}] folder, converted and save to [{kksFolder2}] folder")
else:
Logger.log_success("SCRIPT", f"[{count}] cards moved to [{kks_folder}] folder")
logger.success("SCRIPT", f"[{count}] cards moved to [{kksFolder}] folder")
else:
Logger.log_success("SCRIPT", f"no KKS card found")
logger.success("SCRIPT", f"no KKS card found")
self.setNext(request)

15
app/modules/handler.py Normal file
View File

@@ -0,0 +1,15 @@
class Handler:
def __str__(self) -> str:
return "Handler"
def loadConfig(self, config):
self.gamePath = config.get("Core", "GamePath")
self.config = config.get(str(self))
def handle(self, request):
pass
def setNext(self, request):
request.removeHandler()
request.process()

View File

@@ -1,56 +1,57 @@
import os
import codecs
from util.logger import Logger
class InstallChara:
def __init__(self, config, file_manager):
"""Initializes the Bounty module.
from app.common.file_manager import fileManager
from app.common.logger import logger
from app.modules.handler import Handler
Args:
config (Config): BAAuto Config instance
"""
self.config = config
self.file_manager = file_manager
self.game_path = self.config.game_path
self.input_path = self.config.install_chara["InputPath"]
def resolve_png(self, image_path):
with codecs.open(image_path[0], "rb") as card:
class InstallChara(Handler):
def __str__(self) -> str:
return "Install Chara"
def loadConfig(self, config):
super().loadConfig(config)
self.inputPath = self.config["InputPath"]
def resolvePng(self, imagePath):
with codecs.open(imagePath[0], "rb") as card:
data = card.read()
if data.find(b"KoiKatuChara") != -1:
if data.find(b"KoiKatuCharaSP") != -1 or data.find(b"KoiKatuCharaSun") != -1:
basename = os.path.basename(image_path[0])
Logger.log_error("CHARA", f"{basename} is a KKS card")
basename = os.path.basename(imagePath[0])
logger.error("CHARA", f"{basename} is a KKS card")
return
self.file_manager.copy_and_paste("CHARA", image_path, self.game_path["chara"])
fileManager.copyAndPaste("CHARA", imagePath, self.gamePath["chara"])
elif data.find(b"KoiKatuClothes") != -1:
self.file_manager.copy_and_paste("COORD",image_path, self.game_path["coordinate"])
fileManager.copyAndPaste("COORD",imagePath, self.gamePath["coordinate"])
else:
self.file_manager.copy_and_paste("OVERLAYS", image_path, self.game_path["Overlays"])
fileManager.copyAndPaste("OVERLAYS", imagePath, self.gamePath["Overlays"])
def logic_wrapper(self, folder_path=None):
if folder_path is None:
folder_path = self.input_path
foldername = os.path.basename(folder_path)
Logger.log_msg("FOLDER", foldername)
file_list, compressed_file_list = self.file_manager.find_all_files(folder_path)
def handle(self, request, folderPath=None):
if folderPath is None:
folderPath = self.inputPath
foldername = os.path.basename(folderPath)
logger.log_msg("FOLDER", foldername)
fileList, archiveList = fileManager.findAllFiles(folderPath)
for file in file_list:
file_extension = file[2]
match file_extension:
for file in fileList:
extension = file[2]
match extension:
case ".zipmod":
self.file_manager.copy_and_paste("MODS", file, self.game_path["mods"])
fileManager.copyAndPaste("MODS", file, self.gamePath["mods"])
case ".png":
self.resolve_png(file)
self.resolvePng(file)
case _:
basename = os.path.basename(file[0])
Logger.log_error("UKNOWN", f"Cannot classify {basename}")
print("[MSG]")
logger.error("UKNOWN", f"Cannot classify {basename}")
for compressed in compressed_file_list:
extract_path = self.file_manager.extract_archive(compressed[0])
if extract_path is not None:
self.logic_wrapper(extract_path)
for archive in archiveList:
extractPath = fileManager.extractArchive(archive[0])
if extractPath is not None:
self.handle(extractPath)
self.setNext(request)

View File

@@ -1,47 +1,48 @@
import os
import codecs
from util.logger import Logger
class RemoveChara:
def __init__(self, config, file_manager):
"""Initializes the Bounty module.
from app.common.file_manager import fileManager
from app.common.logger import logger
from app.modules.handler import Handler
Args:
config (Config): BAAuto Config instance
"""
self.config = config
self.file_manager = file_manager
self.game_path = self.config.game_path
self.input_path = self.config.remove_chara["InputPath"]
def resolve_png(self, image_path):
with codecs.open(image_path[0], "rb") as card:
class RemoveChara(Handler):
def __str__(self) -> str:
return "Remove Chara"
def loadConfig(self, config):
super().configLoad(config)
self.inputPath = self.config["InputPath"]
def resolvePng(self, imagePath):
with codecs.open(imagePath[0], "rb") as card:
data = card.read()
if data.find(b"KoiKatuChara") != -1:
if data.find(b"KoiKatuCharaSP") != -1 or data.find(b"KoiKatuCharaSun") != -1:
return
self.file_manager.find_and_remove("CHARA", image_path, self.game_path["chara"])
fileManager.findAndRemove("CHARA", imagePath, self.gamePath["chara"])
elif data.find(b"KoiKatuClothes") != -1:
self.file_manager.find_and_remove("COORD",image_path, self.game_path["coordinate"])
fileManager.findAndRemove("COORD",imagePath, self.gamePath["coordinate"])
else:
self.file_manager.find_and_remove("OVERLAYS", image_path, self.game_path["Overlays"])
fileManager.findAndRemove("OVERLAYS", imagePath, self.gamePath["Overlays"])
def logic_wrapper(self):
foldername = os.path.basename(self.input_path)
Logger.log_msg("FOLDER", foldername)
file_list, archive_list = self.file_manager.find_all_files(self.input_path)
def handle(self, request):
foldername = os.path.basename(self.inputPath)
logger.info("FOLDER", foldername)
fileList, archiveList = fileManager.findAllFiles(self.inputPath)
for file in file_list:
file_extension = file[2]
match file_extension:
for file in fileList:
extension = file[2]
match extension:
case ".zipmod":
self.file_manager.find_and_remove("MODS", file, self.game_path["mods"])
fileManager.findAndRemove("MODS", file, self.game_path["mods"])
case ".png":
self.resolve_png(file)
self.resolvePng(file)
case _:
pass
print("[MSG]")
logger.line()
self.setNext(request)

73
app/modules/request.py Normal file
View File

@@ -0,0 +1,73 @@
import os
from app.common.config import cfg
from app.common.logger import logger
from app.modules.handler import Handler
from app.modules.create_backup import CreateBackup
from app.modules.fc_kks import FilterConvertKKS
from app.modules.install_chara import InstallChara
from app.modules.remove_chara import RemoveChara
class Request:
def __init__(self):
self._handlers = [CreateBackup(), FilterConvertKKS(), InstallChara(), RemoveChara()]
self._config = cfg.toDict()
self._isValid = True
logger.info("SCRIPT", "Validating config")
self.validateGamepath()
self._handlers = [x for x in self.handlers if self.isTaskEnabled(x)]
if not self._isValid:
raise Exception()
def validatePath(self, path, errorMsg):
if not os.path.exists(path):
logger.error("SCRIPT", errorMsg)
self._isValid = False
return False
return True
def validateGamepath(self):
base = self.config['Core']['GamePath']
self.config['Core']['GamePath'] = {
"base": base,
"UserData": os.path.join(base, "UserData"),
"BepInEx": os.path.join(base, "BepInEx"),
"mods": os.path.join(base, "mods"),
"chara": os.path.join(base, "UserData\\chara\\female"),
"coordinate": os.path.join(base, "UserData\\coordinate"),
"Overlays": os.path.join(base, "UserData\\Overlays")
}
for directory, path in self.config['Core']['GamePath'].items():
self.validatePath(path, f"Game path not valid. Missing {directory} directory.")
def isTaskEnabled(self, handler: Handler):
task = str(handler).replace(" ", "")
taskConfig = self.config[task]
if not taskConfig["Enable"]:
return False
if (path := taskConfig.get("InputPath")):
self.validatePath(path, f"Invalid path for {str(handler)}: {path}")
if (path := taskConfig.get("OutputPath")):
self.validatePath(path, f"Invalid path for {str(handler)}: {path}")
if self._isValid:
handler.loadConfig(self.config)
return True
def removeHandler(self) -> Handler:
if len(self._handlers) > 1:
return self._handlers.pop(0)
def process(self):
if self._handlers:
self._handlers[0].handle()

View File

@@ -15,7 +15,7 @@ class LoggerInterface(QFrame):
# setting label
self.mainLayout = QVBoxLayout()
self.topLayout = QHBoxLayout()
self.settingLabel = QLabel(self.tr("Log"))
self.loggerLabel = QLabel(self.tr("Log"))
self.clearBUtton = PushButton('Clear')
self.autoscrollButton = PushButton('Autoscroll Off')
self.loggerBox = TextEdit()
@@ -28,7 +28,7 @@ class LoggerInterface(QFrame):
self.setObjectName('loggerInterface')
# initialize style sheet
self.settingLabel.setObjectName('settingLabel')
self.loggerLabel.setObjectName('settingLabel')
StyleSheet.SETTING_INTERFACE.apply(self)
# initialize layout
@@ -36,7 +36,7 @@ class LoggerInterface(QFrame):
self.__connectSignalToSlot()
def __initLayout(self):
self.topLayout.addWidget(self.settingLabel, alignment=Qt.AlignmentFlag.AlignLeft)
self.topLayout.addWidget(self.loggerLabel, alignment=Qt.AlignmentFlag.AlignLeft)
self.topLayout.addWidget(self.clearBUtton, alignment=Qt.AlignmentFlag.AlignRight)
self.topLayout.addWidget(self.autoscrollButton, alignment=Qt.AlignmentFlag.AlignRight)

View File

@@ -53,10 +53,10 @@ class MainWindow(FluentWindow):
)
self.navigationInterface.panel.topLayout.setAlignment(Qt.AlignCenter)
scrollLayout = self.navigationInterface.panel.scrollLayout
scrollLayout.addWidget(NavigationCheckBox('Create Backup', self.settingInterface.backupGroup))
scrollLayout.addWidget(NavigationCheckBox('Filter and Convert KKS', self.settingInterface.fckksGroup))
scrollLayout.addWidget(NavigationCheckBox('Install Chara', self.settingInterface.installGroup))
scrollLayout.addWidget(NavigationCheckBox('Remove Chara', self.settingInterface.removeGroup))
scrollLayout.addWidget(NavigationCheckBox('Create Backup', cfg.backupEnable, self.settingInterface.backupGroup))
scrollLayout.addWidget(NavigationCheckBox('Filter and Convert KKS', cfg.fckksEnable, self.settingInterface.fckksGroup))
scrollLayout.addWidget(NavigationCheckBox('Install Chara', cfg.installEnable, self.settingInterface.installGroup))
scrollLayout.addWidget(NavigationCheckBox('Remove Chara', cfg.removeEnable, self.settingInterface.removeGroup))
scrollLayout.addWidget(NavigationActionButtons())
# add custom widget to bottom

View File

@@ -4,7 +4,7 @@ try:
with open('traceback.log', 'w') as f:
pass
from app.common.logger import Logger
from app.common.logger import logger
from app.modules.install_chara import InstallChara
from app.modules.remove_chara import RemoveChara
from app.modules.fc_kks import FilterConvertKKS
@@ -20,7 +20,7 @@ try:
config (Config): BAAuto Config instance
"""
self.config = config
self.file_manager = file_manager
fileManager = file_manager
self.modules = {
'InstallChara': None,
'RemoveChara': None,
@@ -28,22 +28,22 @@ try:
'FCKKS': None,
}
if self.config.install_chara['Enable']:
self.modules['InstallChara'] = InstallChara(self.config, self.file_manager)
self.modules['InstallChara'] = InstallChara(self.config, fileManager)
if self.config.remove_chara['Enable']:
self.modules['RemoveChara'] = RemoveChara(self.config, self.file_manager)
self.modules['RemoveChara'] = RemoveChara(self.config, fileManager)
if self.config.create_backup['Enable']:
self.modules['CreateBackup'] = CreateBackup(self.config, self.file_manager)
self.modules['CreateBackup'] = CreateBackup(self.config, fileManager)
if self.config.fc_kks["Enable"]:
self.modules['FCKKS'] = FilterConvertKKS(self.config, self.file_manager)
self.modules['FCKKS'] = FilterConvertKKS(self.config, fileManager)
def run(self):
for task in self.config.tasks:
if self.modules[task]:
Logger.log_info("SCRIPT", f'Start Task: {task}')
logger.info("SCRIPT", f'Start Task: {task}')
try:
self.modules[task].logic_wrapper()
except:
Logger.log_error("SCRIPT", f'Task error: {task}. For more info, check the traceback.log file.')
logger.error("SCRIPT", f'Task error: {task}. For more info, check the traceback.log file.')
with open('traceback.log', 'a') as f:
f.write(f'[{task}]\n')
traceback.print_exc(None, f, True)