diff --git a/gui/rpgmaker_tab.py b/gui/rpgmaker_tab.py index 6db1ab2..0eb5d02 100644 --- a/gui/rpgmaker_tab.py +++ b/gui/rpgmaker_tab.py @@ -256,6 +256,13 @@ class RPGMakerTab(QWidget): self.join408_cb.setToolTip("Join 408 codes into a single string") left_column.addWidget(self.join408_cb) + self.speakers408_cb = QCheckBox("Process Speakers in 408") + self.speakers408_cb.setToolTip("Apply speaker detection and processing to code 408 (same as code 401)") + left_column.addWidget(self.speakers408_cb) + # Only show SPEAKERS408 for ACE engine (rpgmakerace.py) + if self.engine != "ACE": + self.speakers408_cb.hide() + left_column.addSpacing(15) # Main Codes @@ -379,6 +386,8 @@ class RPGMakerTab(QWidget): self.fixtextwrap_cb.stateChanged.disconnect() self.ignoretltext_cb.stateChanged.disconnect() self.join408_cb.stateChanged.disconnect() + if self.engine == "ACE": + self.speakers408_cb.stateChanged.disconnect() # Main Codes self.code401_cb.stateChanged.disconnect() @@ -415,6 +424,8 @@ class RPGMakerTab(QWidget): self.fixtextwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.ignoretltext_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.join408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + if self.engine == "ACE": + self.speakers408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) # Main Codes self.code401_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) @@ -473,6 +484,10 @@ class RPGMakerTab(QWidget): "CODE111": self.code111_cb.isChecked(), "CODE108": self.code108_cb.isChecked(), } + + # Only include SPEAKERS408 for ACE engine + if self.engine == "ACE": + config["SPEAKERS408"] = self.speakers408_cb.isChecked() return config @@ -485,6 +500,10 @@ class RPGMakerTab(QWidget): self.fixtextwrap_cb.setChecked(config.get("FIXTEXTWRAP", True)) self.ignoretltext_cb.setChecked(config.get("IGNORETLTEXT", False)) self.join408_cb.setChecked(config.get("JOIN408", False)) + + # Only set SPEAKERS408 for ACE engine + if self.engine == "ACE": + self.speakers408_cb.setChecked(config.get("SPEAKERS408", False)) # Main Codes self.code401_cb.setChecked(config.get("CODE401", True)) diff --git a/modules/rpgmakerace.py b/modules/rpgmakerace.py index c9eaeb2..a6e0281 100644 --- a/modules/rpgmakerace.py +++ b/modules/rpgmakerace.py @@ -74,7 +74,7 @@ LEAVE = False # Config (Default) # FIRSTLINESPEAKERS: Guess speaker from first line. -FIRSTLINESPEAKERS = False +FIRSTLINESPEAKERS = True # FACENAME101: Map face name -> speaker. FACENAME101 = False # BRFLAG: Newlines ->
. @@ -86,7 +86,9 @@ IGNORETLTEXT = False # TLSYSTEMVARIABLES: Translate System Variables. (Optional but sometimes necessary. Can break stuff.) TLSYSTEMVARIABLES = False # Join 408 codes into a single string like 401. -JOIN408 = False +JOIN408 = True +# SPEAKERS408: Process speakers in code 408 the same way as code 401. +SPEAKERS408 = False # Dialogue / Scroll / Choices (Main Codes) CODE101 = True @@ -95,7 +97,7 @@ CODE405 = True CODE102 = True # Optional -CODE408 = False +CODE408 = True # Variables CODE122 = False @@ -1360,16 +1362,18 @@ def searchCodes(page, pbar, jobList, filename): # Brackets (support multiple names like 【A】【B】) # This now properly detects speakers after control codes are removed if len(speakerList) == 0: + # Strip \sp, \l, \r control codes to check for bracketed speakers + testString = re.sub(r"^(?:[\\]+[splrSPLR]\[[^\]]*\]\s*)+", "", jaString) + # Check if the line contains bracketed names 【name】 - # After control codes are stripped, check for pattern - startsWithBracket = re.match(r"^\s*【", jaString) is not None + startsWithBracket = re.match(r"^\s*【", testString) is not None endsWithBracket = re.search( r"(】\s*$|】\s*(?:[\\]+[A-Za-z]+(?:\[(?:[^\[\]]|\[[^\]]*\])*\])+\s*)$)", - jaString, + testString, ) is not None if startsWithBracket and endsWithBracket: - candidates = re.findall(r"【(.*?)】", jaString) + candidates = re.findall(r"【(.*?)】", testString) if candidates: candidates = [c.strip() for c in candidates] if candidates: @@ -2122,10 +2126,144 @@ def searchCodes(page, pbar, jobList, filename): jaString = codeList[i]["p"][0] match = re.search(r"(.+)", jaString) if match: - # Remove Textwrap - jaString = codeList[i]["p"][0] - ojaString = jaString - jaString = jaString.replace("\n", " ") + # Save starting index + j = i + nametag = "" + + # Speaker Check (if SPEAKERS408 is enabled) + if SPEAKERS408: + speakerList = [] + + # Remove any RPGMaker Code at start + ffMatch = re.search( + r"^((?:[\\]+[^cCnNiIkKvVrRlL]+\[[\d\w]+\])+)", + jaString, + ) + if ffMatch != None: + jaString = jaString.replace(ffMatch.group(0), "") + nametag += ffMatch.group(0) + + # m and z Codes + matchSpeaker = re.search(r"(.*?)[\\]+m\[\d+?\][\\]+z\[\d+?\]", jaString) + if matchSpeaker: + speakerList.append(matchSpeaker.group(1)) + if "\\c" in speakerList[0]: + speakerList = re.findall( + r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + speakerList[0], + ) + + # Brackets (support multiple names like 【A】【B】) + if len(speakerList) == 0: + # Strip \sp, \l, \r control codes to check for bracketed speakers + testString = re.sub(r"^(?:[\\]+[splrSPLR]\[[^\]]*\]\s*)+", "", jaString) + + # Check if the line contains bracketed names 【name】 + startsWithBracket = re.match(r"^\s*【", testString) is not None + endsWithBracket = re.search( + r"(】\s*$|】\s*(?:[\\]+[A-Za-z]+(?:\[(?:[^\[\]]|\[[^\]]*\])*\])+\s*)$)", + testString, + ) is not None + + if startsWithBracket and endsWithBracket: + candidates = re.findall(r"【(.*?)】", testString) + if candidates: + candidates = [c.strip() for c in candidates] + if candidates: + speakerList = candidates + + # Colors + if len(speakerList) == 0: + speakerList = re.findall( + r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$", + jaString, + ) + + # Colons + if len(speakerList) == 0: + speakerList = re.findall( + r"(.+):$", + jaString, + ) + + # First Line Speakers + if len(speakerList) == 0 and FIRSTLINESPEAKERS is True: + # Test Speaker + if ( + len(jaString) < 40 + and len(codeList) > i + 1 + and "c" in codeList[i + 1] + and codeList[i + 1]["c"] in [408, -1] + and len(codeList[i + 1]["p"]) > 0 + and len(codeList[i + 1]["p"][0]) > 0 + ): + nextString = codeList[i + 1]["p"][0].strip() + + # Remove any RPGMaker Code at start + ffMatchNS = re.search( + r"^((?:[\\]+[^cCnNiIkKvVSsrRlL{}]+?\[[\d\w\W]+?\]?\])+)", + nextString, + ) + if ffMatchNS != None: + nextString = nextString.replace(ffMatchNS.group(1), "") + + # Remove other format codes + formatMatch = re.search(r"(^[\\]+[\W]+?)", nextString) + if formatMatch != None: + nextString = nextString.replace(formatMatch.group(1), "") + + # If next line starts with dialogue marker, current line is likely a speaker + if nextString and nextString[0] in [ + "「", + '"', + "(", + "(", + "*", + "[", + ]: + speakerList = re.findall(r".+", jaString) + + # Replace Speaker + if len(speakerList) != 0 and len(codeList) > i + 1 and codeList[i + 1]["c"] in [408, -1]: + # Single + if len(speakerList) == 1: + response = getSpeaker(speakerList[0]) + speaker = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Multiple (Brackets) + elif len(speakerList) > 1: + jaStringUpdated = jaString + for idx, sp in enumerate(speakerList): + response = getSpeaker(sp) + tled = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + if not setData: + pattern = r"【\s*" + re.escape(sp) + r"\s*】" + jaStringUpdated = re.sub(pattern, lambda m: f"【{tled}】", jaStringUpdated) + # Back-compat: set 'speaker' to the first translated name + if idx == 0: + speaker = tled + + # Set Data + if not setData and len(speakerList) > 1: + codeList[i]["p"][0] = nametag + jaStringUpdated + elif not setData and len(speakerList) == 1: + codeList[i]["p"][0] = nametag + jaString.replace(speakerList[0], speaker) + nametag = "" + + # Iterate to next string + i += 1 + j = i + while codeList[i]["c"] in [-1]: + i += 1 + j = i + jaString = codeList[i]["p"][0] + + # Using this to keep track of 408's in a row. + currentGroup.append(jaString) # Join Up 408's into single string if len(codeList) > i + 1 and JOIN408 is True: @@ -2144,22 +2282,78 @@ def searchCodes(page, pbar, jobList, filename): if len(codeList) <= i + 1: break + # Format String + if len(currentGroup) > 0: + finalJAString = "\n".join(currentGroup) + + # Set Back + if not setData: + codeList[j]["p"] = [finalJAString] + + # Process speaker name tag if SPEAKERS408 enabled + if SPEAKERS408: + ### \\n + regex = r"([\\]+[kKnN][wWcCrRrEe]?[\[<](?:[\\]*\w\[\d+\])?(.*?)(?:[\\]*\w\[\d+\])?[>])" + matchSpeaker = re.search(regex, finalJAString) + + # Set Name + if matchSpeaker: + nametag = matchSpeaker.group(1) + speakerName = matchSpeaker.group(2) + + # Translate Speaker + response = getSpeaker(speakerName) + tledSpeaker = response[0] + totalTokens[0] += response[1][0] + totalTokens[1] += response[1][1] + + # Set Nametag and Remove from Final String + finalJAString = finalJAString.replace(nametag, "") + nametag = nametag.replace(speakerName, tledSpeaker) + speaker = tledSpeaker + # Pass 1 if setData: # Remove Textwrap - jaString = jaString.replace("\n", " ") - list408.append(jaString) + finalJAString = finalJAString.replace("\n", " ") + + # Append with or without speaker + if SPEAKERS408 and speaker != "" and finalJAString != "": + list408.append(f"[{speaker}]: {finalJAString}") + else: + list408.append(finalJAString) + + speaker = "" + currentGroup = [] + syncIndex = i + 1 # Pass 2 else: - translatedText = list408[0] - list408.pop(0) + if len(list408) > 0: + translatedText = list408[0] + list408.pop(0) - # Textwrap - translatedText = dazedwrap.wrapText(translatedText, width=WIDTH) + # Remove speaker if SPEAKERS408 enabled + if SPEAKERS408: + matchSpeaker = re.search(r'(^\[.+?\]\s?[|:]\s?)', translatedText) + if matchSpeaker: + translatedText = translatedText.replace(matchSpeaker.group(1), "") - # Set Data - codeList[i]["p"][0] = codeList[i]["p"][0].replace(ojaString, translatedText) + # Textwrap + translatedText = dazedwrap.wrapText(translatedText, width=WIDTH) + + # Add Nametag Back In if SPEAKERS408 enabled + if SPEAKERS408 and nametag: + translatedText = nametag + translatedText + nametag = "" + + # Set Data + codeList[j]["p"][0] = translatedText + + # Reset + speaker = "" + currentGroup = [] + syncIndex = i + 1 ## Event Code: 108 (Script) if "c" in codeList[i] and (codeList[i]["c"] == 108) and CODE108 is True: