mirror of
https://github.com/RedDeadDepresso/KKAFIO.git
synced 2025-12-22 01:10:01 +00:00
fix: checkboxes changes saved
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
@@ -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 = [' INFO', ' WARNING', ' ERROR', 'CRITICAL']
|
||||
# Status Color: Blue, Orange, Red, Purple
|
||||
statusColor = ['#2d8cf0', '#f90', '#ed3f14', '#3e0480']
|
||||
# Status Text: INFO, SUCCESS, ERROR, SKIPPED, REPLACED, RENAMED, REMOVED
|
||||
status = [' INFO', ' SUCCESS', ' ERROR',
|
||||
' SKIPPED', ' REPLACED', ' RENAMED', ' 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(' ', ' ')
|
||||
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:
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
class CreateBackup(Handler):
|
||||
def __str__(self) -> str:
|
||||
return "Create Backup"
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
15
app/modules/handler.py
Normal 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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
73
app/modules/request.py
Normal 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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
16
script.py
16
script.py
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user