diff --git a/gui/engine_config_tab.py b/gui/engine_config_tab.py new file mode 100644 index 0000000..457c1c1 --- /dev/null +++ b/gui/engine_config_tab.py @@ -0,0 +1,70 @@ +"""Unified Game Engine Configuration Tab. + +This tab dynamically shows the appropriate engine-specific configuration UI +based on signals from the Translation tab (engine selection). + +Currently supports: +- RPG Maker MV/MZ +- RPG Maker Ace +- Wolf RPG + +Easily extensible: add new engine widget + mapping entry. +""" +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QStackedLayout, QLabel +from PyQt5.QtCore import Qt + +from gui.rpgmaker_tab import RPGMakerTab +from gui.wolf_tab import WolfTab + + +class EngineConfigTab(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self._init_widgets() + + def _init_widgets(self): + layout = QVBoxLayout() + self.setLayout(layout) + + self.stack = QStackedLayout() + layout.addLayout(self.stack) + + # Engine widgets + self.mv_widget = RPGMakerTab("MVMZ") + self.ace_widget = RPGMakerTab("ACE") + self.wolf_widget = WolfTab() + + # Placeholder when no engine selected + self.placeholder = QLabel("Select a game engine in the Translation tab to configure its options.") + self.placeholder.setAlignment(Qt.AlignCenter) + self.placeholder.setStyleSheet("color:#888; padding:40px;") + + # Add to stack + self.widget_map = { + "mvmz": self.mv_widget, + "ace": self.ace_widget, + "wolf": self.wolf_widget, + } + self.stack.addWidget(self.placeholder) # index 0 + for w in self.widget_map.values(): + self.stack.addWidget(w) + + self.current_engine = None + self.show_placeholder() + + def show_placeholder(self): + self.stack.setCurrentIndex(0) + self.current_engine = None + + def show_engine(self, engine: str): + engine = (engine or "").lower() + widget = self.widget_map.get(engine) + if not widget: + self.show_placeholder() + return + # Find the widget's index in stack + for i in range(self.stack.count()): + if self.stack.widget(i) is widget: + self.stack.setCurrentIndex(i) + self.current_engine = engine + break diff --git a/gui/main.py b/gui/main.py index 98b0073..cba02ff 100644 --- a/gui/main.py +++ b/gui/main.py @@ -18,7 +18,7 @@ from PyQt5.QtGui import QIcon, QFont, QPixmap, QScreen # Import configuration widgets from gui.config_tab import ConfigTab -from gui.rpgmaker_tab import RPGMakerTab +from gui.engine_config_tab import EngineConfigTab from gui.log_viewer import LogViewer from gui.file_manager import FileManager from gui.translation_tab import TranslationTab @@ -170,11 +170,32 @@ class DazedMTLGUI(QMainWindow): # Translation Execution Tab (includes file management) self.translation_tab = TranslationTab(self) + try: + self.translation_tab.engine_changed.connect(self.show_engine_tab) + except Exception: + pass self.tab_widget.addTab(self.translation_tab, "Translation") - - # RPG Maker MV/MZ Tab - self.rpgmaker_tab = RPGMakerTab() - self.tab_widget.addTab(self.rpgmaker_tab, "RPG Maker MV/MZ") + + # Unified Engine Configuration Tab + self.engine_config_tab = EngineConfigTab() + self.tab_widget.addTab(self.engine_config_tab, "Engine Config") + # Default to MV/MZ config on first load + try: + self.show_engine_tab("mvmz") + except Exception: + pass + + def show_engine_tab(self, engine: str): + """Select engine config tab and display appropriate engine sub-widget.""" + try: + # Switch to Engine Config tab + for i in range(self.tab_widget.count()): + if self.tab_widget.tabText(i) == "Engine Config": + self.tab_widget.setCurrentIndex(i) + break + self.engine_config_tab.show_engine(engine) + except Exception: + pass def on_config_changed(self): """Handle configuration changes.""" diff --git a/gui/rpgmaker_tab.py b/gui/rpgmaker_tab.py index f2b80bf..d39b3ca 100644 --- a/gui/rpgmaker_tab.py +++ b/gui/rpgmaker_tab.py @@ -19,7 +19,11 @@ except ImportError: class RPGMakerTab(QWidget): - """RPG Maker MV/MZ configuration tab.""" + """RPG Maker MV/MZ & Ace configuration tab. + + This widget now supports both MV/MZ and Ace engines. Pass engine="ACE" to + target rpgmakerace.py; otherwise it will default to MV/MZ (rpgmakermvmz.py). + """ # Default configuration values for RPG Maker MV/MZ DEFAULT_CONFIG = { @@ -141,8 +145,9 @@ class RPGMakerTab(QWidget): config_changed = pyqtSignal() - def __init__(self): + def __init__(self, engine: str = "MVMZ"): super().__init__() + self.engine = engine.upper() self.config_integration = ConfigIntegration() if ConfigIntegration else None self.init_ui() self.connect_auto_apply() # Connect auto-apply before setting defaults @@ -158,10 +163,11 @@ class RPGMakerTab(QWidget): scroll_layout = QVBoxLayout() # Title and description - title_label = QLabel("RPG Maker MV/MZ Translation Settings") + title = "RPG Maker MV/MZ" if self.engine != "ACE" else "RPG Maker Ace" + title_label = QLabel(f"{title} Translation Settings") title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc;") scroll_layout.addWidget(title_label) - + description_label = QLabel( "Configure which RPG Maker event codes to translate and additional options. " "Each code represents a specific type of game content." @@ -618,12 +624,12 @@ class RPGMakerTab(QWidget): return is_valid def apply_to_module(self, show_messages=False): - """Apply current configuration to the rpgmakermvmz.py module.""" + """Apply current configuration to the correct RPG Maker module (MV/MZ or Ace).""" try: config = self.get_config() - - # Direct file modification approach (completely silent) - module_path = Path(__file__).parent.parent / "modules" / "rpgmakermvmz.py" + # Select module filename based on engine + module_filename = "rpgmakermvmz.py" if self.engine != "ACE" else "rpgmakerace.py" + module_path = Path(__file__).parent.parent / "modules" / module_filename if not module_path.exists(): if show_messages: @@ -662,7 +668,7 @@ class RPGMakerTab(QWidget): QMessageBox.information( self, "Success", - "Configuration has been applied to modules/rpgmakermvmz.py\n\n" + f"Configuration has been applied to modules/{module_filename}\n\n" "The module will now use these settings when running translations." ) @@ -673,16 +679,18 @@ class RPGMakerTab(QWidget): self.config_changed.emit() def load_from_module(self): - """Load configuration from the actual module file.""" + """Load configuration from the actual module file based on engine.""" if not self.config_integration: return - + try: - config = self.config_integration.read_current_config() + module_filename = "rpgmakermvmz.py" if self.engine != "ACE" else "rpgmakerace.py" + module_path = Path("modules") / module_filename + config = self.config_integration.read_current_config(module_path) if config: self.set_config(config) - QMessageBox.information(self, "Success", "Configuration loaded from module file") + QMessageBox.information(self, "Success", f"Configuration loaded from {module_filename}") else: - QMessageBox.warning(self, "Warning", "No configuration found in module file") + QMessageBox.warning(self, "Warning", f"No configuration found in {module_filename}") except Exception as e: QMessageBox.critical(self, "Error", f"Failed to load from module:\n{str(e)}") diff --git a/gui/translation_tab.py b/gui/translation_tab.py index e5e82a6..0d481e6 100644 --- a/gui/translation_tab.py +++ b/gui/translation_tab.py @@ -441,7 +441,12 @@ except Exception as e: class TranslationTab(QWidget): - """Simple translation tab with file management and console log.""" + """Simple translation tab with file management and console log. + + Emits engine_changed(str) when the selected module implies a different + engine configuration tab should be displayed. + """ + engine_changed = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) @@ -471,13 +476,13 @@ class TranslationTab(QWidget): def setup_ui(self): """Set up the user interface.""" layout = QVBoxLayout() - layout.setSpacing(15) # Add consistent spacing - + layout.setSpacing(15) + # Title title_label = QLabel("Translation") title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc; margin-bottom: 5px;") layout.addWidget(title_label) - + # Description desc_label = QLabel( "Manage your files and run translations. Add files to 'files' folder, select module, then click translate." @@ -485,54 +490,42 @@ class TranslationTab(QWidget): desc_label.setWordWrap(True) desc_label.setStyleSheet("color: #cccccc; margin-bottom: 15px;") layout.addWidget(desc_label) - - # Module selection section + + # Module selection module_group = QGroupBox("Translation Settings") module_group.setStyleSheet("QGroupBox { font-weight: bold; color: #007acc; margin-top: 10px; }") module_layout = QVBoxLayout() - - # Module selector - module_selector_layout = QHBoxLayout() - module_label = QLabel("Game Engine:") - module_selector_layout.addWidget(module_label) - + selector_layout = QHBoxLayout() + selector_layout.addWidget(QLabel("Game Engine:")) self.module_combo = QComboBox() - module_selector_layout.addWidget(self.module_combo) - - # Estimate checkbox + self.module_combo.currentTextChanged.connect(self._on_module_changed) + selector_layout.addWidget(self.module_combo) self.estimate_checkbox = QCheckBox("Estimate only (don't translate)") - module_selector_layout.addWidget(self.estimate_checkbox) - - module_layout.addLayout(module_selector_layout) + selector_layout.addWidget(self.estimate_checkbox) + module_layout.addLayout(selector_layout) module_group.setLayout(module_layout) layout.addWidget(module_group) - - # File management section at the top - file_management_splitter = QSplitter(Qt.Horizontal) - - # Input files + + # File management splitter + file_splitter = QSplitter(Qt.Horizontal) input_widget = self.create_input_files_widget() - file_management_splitter.addWidget(input_widget) - - # Output files + file_splitter.addWidget(input_widget) output_widget = self.create_output_files_widget() - file_management_splitter.addWidget(output_widget) - - # Set proportional sizes instead of fixed pixel sizes - file_management_splitter.setStretchFactor(0, 1) - file_management_splitter.setStretchFactor(1, 1) - layout.addWidget(file_management_splitter) - - # Console log at the bottom + file_splitter.addWidget(output_widget) + file_splitter.setStretchFactor(0, 1) + file_splitter.setStretchFactor(1, 1) + layout.addWidget(file_splitter) + + # Log log_widget = self.create_log_widget() layout.addWidget(log_widget) + # Modules list self.setup_module_list() - - # Translation button + + # Buttons button_layout = QHBoxLayout() button_layout.addStretch() - self.translate_button = QPushButton("Start Translation") self.translate_button.clicked.connect(self.start_translation) self.translate_button.setStyleSheet(""" @@ -549,7 +542,6 @@ class TranslationTab(QWidget): } """) button_layout.addWidget(self.translate_button) - self.stop_button = QPushButton("Stop Translation") self.stop_button.clicked.connect(self.stop_translation) self.stop_button.setEnabled(False) @@ -567,10 +559,8 @@ class TranslationTab(QWidget): } """) button_layout.addWidget(self.stop_button) - button_layout.addStretch() layout.addLayout(button_layout) - self.setLayout(layout) def setup_module_list(self): @@ -617,6 +607,8 @@ class TranslationTab(QWidget): for module in self.modules: extensions = ", ".join(module[1]) self.module_combo.addItem(f"{module[0]} ({extensions})") + if self.module_combo.count(): + self._on_module_changed(self.module_combo.currentText()) except Exception as e: # Store error for later logging since log_display might not exist yet @@ -624,6 +616,16 @@ class TranslationTab(QWidget): # Add a default option self.module_combo.addItem("RPG Maker MV/MZ (.json)") self.modules = [["RPG Maker MV/MZ", [".json"], None]] + self._on_module_changed(self.module_combo.currentText()) + + def _on_module_changed(self, text: str): + lowered = text.lower() + if "ace" in lowered: + self.engine_changed.emit("ace") + elif "wolf" in lowered and "wolf rpg 2" not in lowered: + self.engine_changed.emit("wolf") + elif "mv/mz" in lowered: + self.engine_changed.emit("mvmz") def create_input_files_widget(self): """Create the input files widget.""" diff --git a/gui/wolf_tab.py b/gui/wolf_tab.py new file mode 100644 index 0000000..f63f7b9 --- /dev/null +++ b/gui/wolf_tab.py @@ -0,0 +1,126 @@ +"""Wolf RPG configuration tab (basic initial implementation). + +Provides toggles for a subset of flags in wolf.py so users can enable/disable +what gets translated. This can be expanded later. +""" +from pathlib import Path +from PyQt5.QtWidgets import ( + QWidget, QVBoxLayout, QGroupBox, QCheckBox, QPushButton, QMessageBox, QLabel, QHBoxLayout, QScrollArea +) +from PyQt5.QtCore import pyqtSignal +import re + +class WolfTab(QWidget): + config_changed = pyqtSignal() + + DEFAULT_CONFIG = { + # Dialogue/choices + "CODE101": True, + "CODE102": True, + # Picture + "CODE150": True, + # Strings / variables + "CODE122": True, + # Other + "CODE210": True, + "CODE300": True, + "CODE250": True, + # Database flags + "SCENARIOFLAG": True, + "OPTIONSFLAG": True, + "NPCFLAG": True, + "DBNAMEFLAG": True, + "DBVALUEFLAG": True, + "ITEMFLAG": True, + "STATEFLAG": True, + "ENEMYFLAG": True, + "ARMORFLAG": True, + "WEAPONFLAG": True, + "SKILLFLAG": True, + } + + def __init__(self): + super().__init__() + self.init_ui() + self.reset_to_defaults() + + def init_ui(self): + main_layout = QVBoxLayout() + scroll = QScrollArea() + content = QWidget() + v = QVBoxLayout() + + title = QLabel("Wolf RPG Settings") + title.setStyleSheet("font-size:16px;font-weight:bold;color:#007acc") + v.addWidget(title) + desc = QLabel("Toggle which parts of Wolf RPG data are translated. This is a minimal first pass; more options can be added later.") + desc.setWordWrap(True) + desc.setStyleSheet("color:#cccccc") + v.addWidget(desc) + + # Groups + self.checkboxes = {} + self.add_group(v, "Dialogue / Choices", ["CODE101", "CODE102"]) + self.add_group(v, "Pictures / Variables", ["CODE150", "CODE122"]) + self.add_group(v, "Other Event Codes", ["CODE210", "CODE300", "CODE250"]) + self.add_group(v, "Database Sections", [ + "SCENARIOFLAG", "OPTIONSFLAG", "NPCFLAG", "DBNAMEFLAG", "DBVALUEFLAG", + "ITEMFLAG", "STATEFLAG", "ENEMYFLAG", "ARMORFLAG", "WEAPONFLAG", "SKILLFLAG" + ]) + + # Buttons + btn_row = QHBoxLayout() + self.reset_btn = QPushButton("Reset") + self.reset_btn.clicked.connect(self.reset_to_defaults) + self.apply_btn = QPushButton("Apply Now") + self.apply_btn.clicked.connect(self.apply_to_module) + btn_row.addWidget(self.reset_btn) + btn_row.addWidget(self.apply_btn) + btn_row.addStretch() + v.addLayout(btn_row) + + content.setLayout(v) + scroll.setWidget(content) + scroll.setWidgetResizable(True) + main_layout.addWidget(scroll) + self.setLayout(main_layout) + + def add_group(self, parent_layout, title, keys): + box = QGroupBox(title) + v = QVBoxLayout() + for k in keys: + cb = QCheckBox(k) + cb.stateChanged.connect(lambda _=None: self.apply_to_module(False)) + v.addWidget(cb) + self.checkboxes[k] = cb + box.setLayout(v) + parent_layout.addWidget(box) + + def get_config(self): + return {k: cb.isChecked() for k, cb in self.checkboxes.items()} + + def reset_to_defaults(self): + for k, val in self.DEFAULT_CONFIG.items(): + if k in self.checkboxes: + self.checkboxes[k].setChecked(val) + self.apply_to_module() + + def apply_to_module(self, show_message=False): + module_path = Path(__file__).parent.parent / 'modules' / 'wolf.py' + if not module_path.exists(): + if show_message: + QMessageBox.critical(self, 'Error', 'wolf.py not found in modules directory.') + return + try: + content = module_path.read_text(encoding='utf-8') + for key, value in self.get_config().items(): + pattern = rf'^{key}\s*=\s*.*$' + content = re.sub(pattern, f'{key} = {value}', content, flags=re.MULTILINE) + module_path.write_text(content, encoding='utf-8') + self.config_changed.emit() + if show_message: + QMessageBox.information(self, 'Applied', 'Wolf configuration applied to wolf.py') + except Exception as e: + if show_message: + QMessageBox.critical(self, 'Error', f'Failed to apply settings: {e}') + diff --git a/modules/rpgmakerace.py b/modules/rpgmakerace.py index 4616371..2684272 100644 --- a/modules/rpgmakerace.py +++ b/modules/rpgmakerace.py @@ -75,12 +75,12 @@ TRANSLATION_CONFIG = TranslationConfig( LEAVE = False # Config (Default) -FIRSTLINESPEAKERS = False # If 1st line of 401 is a speaker, set to True (False) -FACENAME101 = False # Find Speakers in 101 Codes based on Face Name (False) -NAMES = False # Output a list of all the character names found (False) -BRFLAG = False # If the game uses
instead (False) -FIXTEXTWRAP = True # Overwrites textwrap (True) -IGNORETLTEXT = False # Ignores all translated text. (False) +FIRSTLINESPEAKERS = False +FACENAME101 = False +NAMES = False +BRFLAG = False +FIXTEXTWRAP = True +IGNORETLTEXT = False # Dialogue / Scroll / Choices (Main Codes) CODE401 = True @@ -88,8 +88,8 @@ CODE405 = True CODE102 = True # Optional -CODE101 = False # Turn this one on when names exist in 101 -CODE408 = False # Warning, translates comments and can inflate costs. +CODE101 = False +CODE408 = False # Variables CODE122 = False diff --git a/start.py b/start.py new file mode 100644 index 0000000..496344e --- /dev/null +++ b/start.py @@ -0,0 +1,4 @@ +import modules.main + +if __name__ == "__main__": + modules.main.main() \ No newline at end of file