Stephan Fuchs 3 месяцев назад
Родитель
Сommit
0a290e0136
3 измененных файлов с 1645 добавлено и 0 удалено
  1. 2 0
      .gitignore
  2. 1360 0
      qrsc_to_tw.py
  3. 283 0
      variables.py

+ 2 - 0
.gitignore

@@ -45,3 +45,5 @@ glife.7z.tmp
 glife.7z
 
 *.test
+
+__pycache__/

+ 1360 - 0
qrsc_to_tw.py

@@ -0,0 +1,1360 @@
+#!/usr/bin/env python3
+
+import fileinput
+import io, os, re, datetime
+import hashlib
+import variables
+import time
+
+from concurrent.futures import ThreadPoolExecutor
+from concurrent.futures import as_completed
+
+qsp_sources_path = "locations"
+tw_sources_path = os.path.join("sugarcube","src","autogenerated")
+
+verbose = False
+#verbose = True
+skipMode = 0
+#skipMode = -1
+
+verbose_i = 20
+
+
+error_counter = 1
+regex_parts = {}
+
+regex_parts['operator_assign'] = r'=|\+=|-='
+
+regex_parts['parameter'] = r"\$ARGS\[\d+\]"
+regex_parts['var'] = r"\$?[a-z]+\w*"
+regex_parts['functioncall'] = r"\w+\s*\(\s*\w+\s*(?:,\s*\w*\s*)*\)"
+
+regex_parts['integer'] = r"\d+"
+regex_parts['string'] = r"""'\w'|"""+ r'''"\w"'''
+
+regex_parts['value_literal'] = "|".join([regex_parts['integer'],regex_parts['string']])
+
+regex_parts['value'] = "|".join([regex_parts['parameter'],regex_parts['var'],regex_parts['functioncall'],regex_parts['value_literal'] ])
+
+regex_parts['statement'] = '?'
+
+regex_parts['assignment'] = f"\s*({regex_parts['var']})\s*({regex_parts['operator_assign']})\s*({regex_parts['value']})\s*"
+
+def pv(desc,line):
+	if(verbose):
+			print(desc.ljust(verbose_i, ' ')+line)
+
+def convert_calculation(right):
+	pv("START CALCULATION:",right)
+	brackets_regex = r"""(?<![a-zA-Z0-9])\(\s*([^\)^\(]+(\s*\-|mod|\+|/|\*\s*)[^\)^\(]+)\s*\)"""
+	#brackets_regex = r"""(?<![a-zA-Z0-9])(?:min|rand|max|mid)\(\s*([^\)^\(]+(\s*\-|mod|\+|/|\*\s*)[^\)^\(]+)\s*\)"""
+	calculations = []
+	#print(right)
+	while b_match := re.search(brackets_regex,right):
+		complete_match = b_match.group(0)
+		calculation_raw = b_match.group(1)
+		operator = b_match.group(2)
+		if operator == 'mod':
+			calculation_raw = calculation_raw.replace('mod','%')
+		calculation = convert_literal(calculation_raw)
+		index = f"$CALC_HELPER_{len(calculations)}"
+		right = right.replace(complete_match,index)
+		calculations.append(calculation)
+	right = convert_literal(right)
+
+	#function_name_regex = r"""(?<![a-zA-Z0-9])(min|rand|mid|max)\s*\$LIT_HELPER_(0)"""
+
+
+	#while function_name_match := re.search(function_name_regex,right):
+	#	complete_match = function_name_match.group(0)
+	#	index = f"$LIT_HELPER_{len(calculations)}"
+	#	right = right.replace(complete_match,index)
+	#	calculations.append(complete_match)
+
+	while len(calculations) > 0:
+		calculation = calculations.pop()
+		index = f"$CALC_HELPER_{len(calculations)}"
+		right = right.replace(index,f"({calculation})")
+	pv("IS CALCULATION:",right)
+	return right
+
+def convert_command(command_raw):
+
+
+
+	command = command_raw.strip()
+
+	#if (command.startswith('<') or  and command.endswith('>'):
+	#	return command
+
+	split_by_and = command.split(' & ')
+	if len(split_by_and) > 1:
+		return ' '.join([convert_command(s) for s in split_by_and])
+
+	assign_operators = ['=','-=','+=']
+	for assign_operator in assign_operators:
+		split_by_assign = command.split(assign_operator,1)
+		if len(split_by_assign) > 1:
+			left = split_by_assign[0]
+			if left.startswith('set '):
+				left = left[4:]
+
+			right = split_by_assign[1]
+
+			right = convert_calculation(right)
+
+			pv("IS ASSIGNMENT:",right)
+			return f'<<set {convert_literal(left)} {assign_operator} {right}>>'
+
+	if match := re.match(r"^(?:set\s*)?(\$?\w+)\s*(\+=|-=|=)\s*([\$']?\w*'?|\w+\s*\(\s*\w+\s*(?:,\s*\w*\s*)*\))$",command):
+		return f'<<set {convert_literal(match.group(1))} {match.group(2)} {convert_literal(match.group(3))}>>'
+
+	if match := re.match(r"""^(x?gt|gs)([\s'"].+)$""",command):
+		arguments = match.group(2)
+		pv('GS OR GT:',command)
+		i = 0
+		replaces = []
+		while brackets_match := re.search(r"""\(([^\(^\)]*?,[^\(^\)]*?)\)""",arguments):
+			original = brackets_match.group(1)
+			pv("REPLACE:",original)
+			indentifier = f'$BRACKET_HELER_{i}'
+			arguments = arguments.replace(original,indentifier)
+			replaces.append(original)
+			i += 1
+
+		arguments = " ".join([convert_literal(l) for l in arguments.split(',')])
+
+		while len(replaces) > 0:
+			original = replaces.pop()
+			indentifier = f'$BRACKET_HELER_{len(replaces)}'
+			arguments = arguments.replace(indentifier,original)
+
+		return f'<<{match.group(1)} {arguments}>>'
+
+	if match := re.match(r"""^msg\s*(.+)$""",command,re.I):
+		return f"<<msg {match.group(1)}>>"
+
+	if match := re.match(r"""^dynamic \$(.*)$""",command,re.I):
+		arguments = match.group(1).replace(',',' ')
+		return f"<<{arguments}>>"
+
+
+
+	pv("NO COMMAND MATCH:",command)
+
+	return ''
+
+
+def convert_condition(condition_raw):
+	condition = condition_raw.strip()
+
+	if(re.match(r"dyneval\(",condition)):
+		return condition
+
+	#condition = convert_calculation(condition)
+	#print(condition)
+	subconditions = []
+
+	bracket_search_regex = r"""(?<![a-zA-Z0-9])\(\s*([^\)^\(]+)\s*\)"""
+	bracket_search_regex = r"""(?<![a-zA-Z0-9])\(\s*([^\)^\(]+(\s*and|or|xor\s*)[^\)^\(]+)\s*\)"""
+
+	while match := re.search(bracket_search_regex,condition):
+		fullmatch = match.group(0)
+		subcondition = match.group(1)
+		subcondition_converted = convert_condition(subcondition)
+		subcondition_ident = f"$CON_HELPER_{len(subconditions)} == 1"
+		condition = condition.replace(fullmatch,subcondition_ident)
+		subconditions.append(subcondition_converted)
+		#print(condition)
+
+	if len(subconditions) > 0:
+		condition = convert_condition(condition)
+		while len(subconditions) > 0:
+			subcondition = subconditions.pop()
+			subcondition_ident = f"$CON_HELPER_{len(subconditions)} == 1"
+			condition = condition.replace(subcondition_ident,f"({subcondition})")
+			#print(condition)
+		return condition
+
+
+	split_by_or = condition.split(' or ')
+	if len(split_by_or) > 1:
+		return ' or '.join([convert_condition(s) for s in split_by_or])
+
+	split_by_xor = condition.split(' xor ')
+	if len(split_by_xor) > 1:
+		return ' xor '.join([convert_condition(s) for s in split_by_xor])
+
+	split_by_and = condition.split(' and ')
+	if len(split_by_and) > 1:
+		return ' and '.join([convert_condition(s) for s in split_by_and])
+
+	#match = re.match(r"(\$ARGS\[\d+\]|\$?\w+)\s*([=><]+)\s*('?\w+'?)",condition)
+	#match = re.match(r"(\S+)\s*([=><]+)\s*(\S+)",condition)
+
+	if(len(condition) >= 2 and condition[0] == '(' and condition[-1] == ')'):
+		condition = condition[1:-1]
+	#print(condition)
+	match = re.match(r"([^<^>^=^!]+)\s*([=><!]+)\s*([^<^>^=^!]+)",condition)
+
+	if match:
+		left = convert_literal(convert_calculation(match.group(1)))
+		right = convert_literal(match.group(3))
+		operator = match.group(2)
+
+		if operator == '=':
+			operator = '=='
+		elif operator == '!':
+			operator = '!='
+		elif operator == '=>':
+			operator = '>='
+		elif operator == '=<':
+			operator = '<='
+
+
+		return ' '.join([left,operator,right])
+
+	return f'ERROR: FAILED TO CONVERT CONDITION: {condition}'
+
+def convert_literal(literal_raw):
+	literal = literal_raw.strip()
+	pv("START LITERAL:",literal)
+	if not literal:
+		return ''
+
+	subliterals = []
+	bracket_search_regex = r"""\[\s*([^\]^\[]+)\s*\]"""
+	while match := re.search(bracket_search_regex,literal):
+		fullmatch = match.group(0)
+		subcondition = match.group(1)
+		subcondition_converted = convert_literal(subcondition)
+		subcondition_ident = f"LIT_HELPER_{len(subliterals)}"
+		literal = literal.replace(fullmatch,subcondition_ident)
+		subliterals.append(subcondition_converted)
+		#print(condition)
+
+	if len(subliterals) > 0:
+		literal = convert_literal(literal)
+		while len(subliterals) > 0:
+			subcondition = subliterals.pop()
+			subcondition_ident = f"LIT_HELPER_{len(subliterals)}"
+			literal = literal.replace(subcondition_ident,f"[{subcondition}]")
+			#print(condition)
+		return literal
+
+	#brackets_regex = r"""\(\s*([^\)^\(]+)\s*\)"""
+	#function_name_regex = r"""(?<![a-zA-Z0-9])(min|rand|mid|max)\s*\$LIT_HELPER_(0)"""
+	#calculations = []
+	#while b_match := re.search(brackets_regex,literal):
+	#	complete_match = b_match.group(0)
+	#	calculation_raw = b_match.group(1)
+	#	calculation_parts = calculation_raw.split(',')
+	#	calculation = ','.join([convert_literal(s) for s in calculation_parts])
+	#	index = f"$LIT_HELPER_{len(calculations)}"
+	#	literal = literal.replace(complete_match,index)
+	#	calculations.append(calculation)
+
+	#while function_name_match := re.search(function_name_regex,literal):
+	#	complete_match = function_name_match.group(0)
+	#	index = f"$LIT_HELPER_{len(calculations)}"
+	#	literal = literal.replace(complete_match,index)
+	#	calculations.append(complete_match)
+
+	#if len(calculations) > 0:
+	#	literal = convert_literal(literal)
+	#	while len(calculations) > 0:
+	#		calculation = calculations.pop()
+	#		index = f"$LIT_HELPER_{len(calculations)}"
+	#		literal = literal.replace(index,f"({calculation})")
+	#	pv("LITERAL AFTER BRACKETS:",literal)
+
+	if(re.match(r"dyneval\(",literal)):
+		return literal
+
+	if literal == "''" or literal == '""':
+		return literal
+
+
+	if(literal.isnumeric() or (literal.startswith('-') and literal[1:].isnumeric())):
+		pv("IS NUMERIC",literal)
+		return literal
+
+	if(literal.startswith('(') and len(literal) > 1):
+		return '('+literal[1:]
+	if(literal.endswith(')') and len(literal) > 1):
+		return literal[:-1]+')'
+
+	#array_braces = False
+	#while match:=re.match(r"\$?([a-z][a-z0-9\-_{}'\+]*)(\['(.*?<<.*?>>'*?)'\])",literal,re.I):
+	#	value = match.group(3)
+	#	if not value.startswith("<"):
+	#		value = value.replace("<<","'+")
+	#	else:
+	#		value = value.replace("<<","")#
+	#
+	#	if not value.endswith(">"):
+	#		value = value.replace(">>","+'")
+	#	else:
+	#		value = value.replace(">>","")
+	#	literal = literal.replace(match.group(2),r"{{"+value+r"}}")
+	#	array_braces = True
+	#if array_braces:
+	#	literal = literal.replace(r'{{','[').replace(r'}}',']')
+
+
+
+	if(len(literal)>= 3 and ((literal[0] == '\'' and literal[-1] == '\'') or (literal[0] == '"' and literal[-1] == '"') )):
+
+		literal = literal.replace('<<','')
+		literal = literal.replace('>>','')
+		literal = literal.replace("''","'")
+
+		return literal
+
+
+
+	if(match := re.match(r"^arrsize\(\s*'(\$?)([a-z]+\w*)'\s*\)$",literal)):
+		return f"${match.group(2)}.length"
+
+	if(match := re.match(r"^killvar\s+'(\$?)([a-z]+\w*)'$",literal,re.I)):
+		return f"<<set ${match.group(2)} to null>>"
+
+	if(match := re.match(r'^\$ARGS\[(\d+)\]$',literal,re.I)):
+		#ARGS
+		return f'$location_var[$here][{match.group(1)}]'
+
+	if(match := re.match(r'^\$ARGS(LIT_HELPER_\d+)$',literal,re.I)):
+		#ARGS
+		return f'$location_var[$here]'+match.group(1)
+
+	if(match := re.match(r"^(\$?)[a-zA-z]+\w*(\[('\w*'|\d+)\])?$",literal)):
+		if match.group(1):
+			return literal
+		return '$'+literal
+
+	if(match := re.match(r"^(\$?)([a-zA-z]+\w*)\[(.+)\]$",literal)):
+		#if match.group(1):
+		#	return f"${match.group(2)}[{convert_literal(match.group(3))}]"
+		return f"${match.group(2)}[{convert_literal(match.group(3))}]"
+	#print(literal)
+
+
+	if(match := re.match(r'^(\w+)\s*\((\s*\w+\s*(?:,\s*\w*\s*)*)\)$',literal)):
+		function_name = match.group(1)
+		function_parameters = match.group(2)
+		return f'{function_name}({convert_literal(function_parameters)})'
+
+	while(match := re.search(r'<<(\$\w+)>>',literal)):
+		literal = literal.replace(match.group(0),match.group(1))
+
+	#Arithmetic Operations
+	arith_operations = ['*','/','-','+','%',',','mod']
+	for arith_operation in arith_operations:
+		split_by_arith = literal.split(arith_operation)
+		if len(split_by_arith) > 1:
+			if arith_operation == 'mod':
+				arith_operation = '%'
+			return f' {arith_operation} '.join([convert_literal(s) for s in split_by_arith])
+
+
+	if literal.startswith('wait '):
+		return f'<<{literal}>>'
+
+	if(match := re.match(r"""^killvar\s+['"]\$?(.*)['"]$""",literal)):
+		return f'<<set ${match.group(1)} = 0>>'
+
+	if literal.startswith('jump '):
+		jump_san = literal.replace('"','').replace("'","")
+		return f"<<warn 'JUMP COMMAND ENCOUNTERED: {jump_san}'>>"
+
+	if literal.startswith(':'):
+		return f"<<warn 'JUMP MARKER ENCOUNTERED: {literal}'>>"
+
+	if literal == "'" or literal == '"':
+		return ''
+
+	if re.match(r"""^['"\w\s\?\.!\(\)\$]+$""",literal):
+		pv("Plain String",literal)
+		return literal
+
+	#return literal
+	return f'ERROR: FAILED TO CONVERT LITERAL: """{literal}"""'
+
+
+function_name_conversions = []
+
+
+def convert_lineblock(lines,location_identifier=''):
+	outputs = []
+
+	nesting = []
+	commenting = False
+	isfunction = False
+	functionlines = []
+	functionlines_temp = []
+	function_name = ''
+
+	subpassages = {}
+	subpassage = ''
+
+	for line_raw in lines:
+		#line_raw = lines[line_num]
+		line = line_raw.strip()
+		original = line
+
+		pv("START:",line)
+
+		if len(line) == 0:
+			continue
+
+		whitespace_count = len(nesting)
+
+		# Subpassages
+		if len(subpassage) > 0:
+			if line.startswith('~$'):
+				endSubpassageName = line[2:]
+				gtOrGs = 'gt'
+				if endSubpassageName.startswith('gs:'):
+					gtOrGs = 'gs'
+					endSubpassageName = endSubpassageName[3:]
+				if endSubpassageName == subpassage:
+					outputs.append( whitespace_count * '\t' + f"<<{gtOrGs} '{subpassage}' $location_var[$here][0] $location_var[$here][1] $location_var[$here][2] $location_var[$here][3] $location_var[$here][4]>>\n")
+					subpassages[subpassage].append('<!-- END: '+subpassage+' -->')
+					subpassage = ''
+					continue
+			subpassages[subpassage].append(line)
+			continue
+
+		if len(subpassage) == 0:
+			if line.startswith('~^'):
+				startSubpassageName = line[2:]
+				subpassage = startSubpassageName
+				subpassages[subpassage] = []
+				continue
+
+
+
+		for preparation in preparations:
+			if len(preparation) > 2:
+				line = re.sub(preparation[0],preparation[1],line,0,preparation[2])
+			else:
+				line = re.sub(preparation[0],preparation[1],line)
+
+
+		line_output = ''
+
+
+
+		purges = [
+			'CLOSE ALL',
+			'close all',
+			'*clr & cla',
+			'killall',
+			'showstat 0','showstat 1',
+			'showobjs 0','showobjs 1',
+			'showinput 0','showinput 1',
+			"gs 'stat'","gs'stat'",
+		]
+
+		for purge in purges:
+			line = line.replace(purge,'')
+
+		pv("AFTER PURGES:",line)
+
+		#line = line.replace(" mod "," % ")
+
+		#while match := re.match(r"iif\s*\((.+),(.*),(.*)\)",line):
+		#	line = line.replace(match.group(0),f"({convert_condition(match.group(1))}) ? {convert_literal(match.group(2))} : {convert_literal(match.group(3))}")
+
+
+
+
+		# FUNCTIONS
+		if isfunction:
+			if line.endswith('}'):
+				isfunction = False
+				functionname_sanatized = function_name.replace('$','').replace('[','').replace(']','').replace("'",'')
+				functionlines.append(f':: {functionname_sanatized}_macro[widget]\n<<widget "{functionname_sanatized}">>\n')
+				functionlines_temp = convert_lineblock(functionlines_temp)
+				for fl in functionlines_temp:
+					functionlines.append(f'\t{fl}')
+				functionlines.append(f'<</widget>>\n')
+				function_name_conversions.append([function_name,functionname_sanatized])
+			else:
+				functionlines_temp.append(line)
+			continue
+
+		if match := re.match(r"""(\$[a-z][a-z0-9\-_'"\[\]]+)\s*=\s*{""",line,re.I):
+			isfunction = True
+			#functionlines.append(f'<<widget "{match.group(1)}">>')
+			function_name = match.group(1)
+			functionlines_temp = []
+			continue
+
+		pv("AFTER FUNCTIONS:",line)
+
+		while fuckyoumatch := re.search(r"""\[["']([^\]]*?)<<(.*?)>>(.*?)["']\]""",line):
+			index = f'${fuckyoumatch.group(2)}'
+			#left = ''
+			if fuckyoumatch.group(1):
+				index = f"'{fuckyoumatch.group(1)}'+" + index
+			#right = ''
+			if fuckyoumatch.group(3):
+				index = index + f"+'{fuckyoumatch.group(3)}'"
+			#	right = fuckyoumatch.group(3)
+
+			#index = f"'{left}'+${fuckyoumatch.group(2)}+'{right}'"
+			#print(index)
+			line = line.replace(fuckyoumatch.group(0),f"[{index}]")
+			#print("MATCH")
+
+		pv("AFTER ARRAY []:",line)
+
+		while dynevalmatch := re.search(r"""dyneval\s*\(\s*'\s*RESULT\s*=\s*(<<.*?)'\s*\)""",line):
+			fullmatch = dynevalmatch.group(0)
+			varname = dynevalmatch.group(1)
+			varname = varname.replace(r"[''",r"['").replace(r"'']",r"']")
+			varname = "State.getVar('$"+varname.replace('<<',"'+").replace('>>',"+'")+"')"
+			line = line.replace(fullmatch,varname)
+			#print(line)
+
+		pv("AFTER DYNEVAL:",line)
+
+		while dynevalmatch := re.search(r"""dyneval\s*\(\s*'\s*RESULT\s*=\s*\$?(.*?)'\s*\)""",line):
+			fullmatch = dynevalmatch.group(0)
+			varname = "$"+dynevalmatch.group(1)
+			varname = varname.replace('<<',"'+").replace('>>',"+'")
+			#varname = varname.replace(r"[''",r"['").replace(r"'']",r"']")
+			varname = varname.replace("['+","[").replace(r"+']",r"]")
+
+			varname = varname.replace('$$',"$")
+			line = line.replace(fullmatch,varname)
+
+		pv("AFTER DYNEVAL2:",line)
+
+		if line.startswith('!{') or line.startswith('!!{') or line == '!!{':
+			line_output = '<!-- ' + line
+			if line.endswith('}'):
+				line_output += ' -->'
+			else:
+				commenting = True
+			pv("IS COMMENT:",line_output)
+		elif line.endswith('}') and commenting:
+			line_output = line+' -->'
+			commenting = False
+			pv("IS COMMENT:",line_output)
+		elif commenting:
+			line_output = line.replace('--','-')
+			pv("IS COMMENT:",line_output)
+		elif line.lower() == 'end' or line.lower().startswith('end &!'):
+			whitespace_count -= 1
+			if len(nesting) > 0:
+				end_command = nesting.pop()
+				if end_command == 'if':
+					#file.write('<</if>>\n')
+					line_output = '<</if>>'
+				elif end_command == 'while':
+					#file.write('<</while>>\n')
+					line_output = '<</while>>'
+				elif end_command == 'act':
+					#file.write('<</act>>\n')
+					line_output = '<</act>>'
+				else:
+					print(f'ERROR: UNKNOWN NESTRING: {end_command}')
+			else:
+				line_output = '<</END>>'
+			pv("IS END:",line_output)
+		elif line.startswith('!'):
+			line_output = '<!-- ' + line + '-->'
+			pv("IS COMMENT:",line_output)
+		elif line.lower() == 'else':
+			#file.write('<<else>>\n')
+			line_output = '<<else>>'
+			whitespace_count -= 1
+			pv("IS ELSE:",line_output)
+		elif line.lower() == 'cls':
+			line_output = ''
+			pv("IS CLS:",line_output)
+		elif line.lower() == '*nl':
+			#file.write('\n')
+			line_output = ''
+			pv("IS NL:",line_output)
+		elif line[0:3] == '---':
+			line_output = ''
+			pv("IS EOF:",line_output)
+		elif match := re.match(r"^\s*msg\s*'(.*)'\s*$",line,re.I):
+			msg = match.group(1).replace("'",'"')
+			line_output = f"""<<msg '{msg}'>>"""
+			pv("IS MSG:",line_output)
+
+
+		elif match := re.match(r"""act\s*'(.*?)\s*\(<font color="red"><<will_cost>> Willpower</font>\)':\s*'<br><font color="red">You don''t have enough willpower to use this action.</font>'""",line,re.I):
+			label = match.group(1).replace("'",'"')
+			line_output = f"<<act `'{label} ('+$will_cost+')'`>><font color=red><br/>You don`t have enough willpower to use this action.</font><</act>>"
+		elif match := re.match(r"""^'(?:<center>)?<img\s+(?:<<\$?\w+>>\s+)src="images\/([\w\/\.]+)(?:'\s*\+\s*rand\((\d+,\d+)\)\s*\+\s*')?([\w\/\.]+)"\s*>(?:<\/center>)?'""",line,re.I):
+			#Images
+			if match.group(2):
+				line_output = f'''<<image "{match.group(1)}#{match.group(3)}" {match.group(2).replace(',',' ')}>>'''
+			else:
+				line_output = f'''<<image "{match.group(1)}{match.group(3)}">>'''
+			pv("IS IMAGE:",line_output)
+		elif match := re.match(r"""^'?(?:<center>)?<video\s+(?:<<\$?\w+>>\s+)*(?:\s*autoplay\s*|\s*loop\s*)*src="images\/([\w\/\.]+)(?:'\s*\+\s*rand\((\d+,\d+)\)\s*\+\s*')?([\w\/\.]+)"\s*>(?:<\/video>)?(?:<\/center>)?'?""",line,re.I):
+			#Images
+			if match.group(2):
+				line_output = f'''<<video "{match.group(1)}#{match.group(3)}" {match.group(2).replace(',',' ')}>>'''
+			else:
+				line_output = f'''<<video "{match.group(1)}{match.group(3)}">>'''
+			pv("IS VIDEO:",line_output)
+		elif line.startswith('<') and line.endswith('>'):
+			line_output = line
+			pv("IS HTML:",line_output)
+		elif line.startswith("'<") and line.endswith(">'"):
+			line_output = line[1:-1]
+			pv("IS COMMENTED HTML:",line_output)
+		elif match := re.match(r"""^'[\w<>\s=:'"\/\.\(\),\*]+'$""",line):
+			#Plain HTML
+			line_output = line[1:-1]
+			if link_match := re.findall(r"""(<a href="exec:([^"]+)">([^<]+)<\/a>)""",line_output):
+				#line_output = #line_output.replace(link_match[0],"moep")
+				#print("moep")
+				#print(link_match)
+				for lmatch in link_match:
+					line_output = line_output.replace(lmatch[0],f"""<<link "{lmatch[2]}">>{convert_command(lmatch[1])}<</link>>""")
+			pv("IS PLAIN HTML:",line_output)
+		elif match := re.match(r"^\s*(if|while)\s+([^:]+):(.*)",line,re.I):
+			command = match.group(1).lower()
+			line_w = f'<<{command} {convert_condition(match.group(2))}>>'
+			if match.group(3):
+				#if com := convert_command(match.group(3)):
+				#	line_w += '\n' + (whitespace_count+1) * '\t' + com
+				#else:
+				#	line_w += '\n' + (whitespace_count+1) * '\t' + convert_literal(match.group(3))
+				converted = convert_lineblock([match.group(3)])
+				if len(converted)>0 and converted[0]:
+					line_w += '\n' + (whitespace_count+1) * '\t' + converted[0]
+				line_w += '\n' + whitespace_count * '\t'+f'<</{command}>>'
+			else:
+				nesting.append(command)
+			#file.write(line_w)
+			line_output = line_w
+			pv("IS IF:",line_output)
+		elif match := re.match(r"\s*(act)\s*(.+):(.*)",line,re.I):
+			# Act-Command
+			command = match.group(1).lower()
+
+			line_w = f'<<{command} {convert_literal(match.group(2))}>>'
+			if match.group(3):
+				line_w += '\n' + (whitespace_count+1) * '\t'+convert_command(match.group(3))
+				line_w += '\n' + whitespace_count * '\t'+f'<</{command}>>'
+			else:
+				nesting.append(command)
+			#file.write(line_w)
+			line_output = line_w
+			pv("IS ACT:",line_output)
+		elif match := re.match(r"\s*(elseif)\s+([^:]+):(.*)",line,re.I):
+			# ElseIf
+			command = match.group(1).lower()
+			line_w = f'<<{command} {convert_condition(match.group(2))}>>'
+			whitespace_count -= 1
+			if match.group(3):
+				#line_w += '\n' + (whitespace_count+1) * '\t'+convert_command(match.group(3))
+				line_w += '\n' + whitespace_count * '\t'+f'<</if>>'
+				#nesting.pop()
+			#file.write(line_w)
+			line_output = line_w
+			pv("IS ELSEIF:",line_output)
+		#elif match := re.match(r"^dynamic\s+'(.*)'$",line,re.I):
+		#	out = match.group(1).replace('<<',"'+").replace('>>',"+'")
+		#	line_output = f"<<dynamic {out}>>"
+		elif match := re.match(r"^dynamic '\$?(.*?)\s*([\+\-]?=)\s*(.*?)\s*'$",line,re.I):
+			left = match.group(1)
+			right = match.group(3)
+			operator = match.group(2)
+
+			left = left.replace(r"[''",r"['").replace(r"'']",r"']")
+			left = left.replace(r"['+",r"[").replace(r"+']",r"]")
+			left = left.replace('<<',"'+").replace('>>',"+'")
+			left = left.replace('$$',"$")
+
+			right = right.replace(r"[''",r"['").replace(r"'']",r"']")
+			right = right.replace(r"['+",r"[").replace(r"+']",r"]")
+			right = right.replace('<<',"").replace('>>',"")
+			right = right.replace('$$',"$")
+			right = convert_literal(right)
+
+			line_output = f'<<set {left} {operator} {right}>>'
+			pv("IS DYNAMIC:",line_output)
+		#elif match := re.match(r"^dynamic '\$?(.*?)\s*([\+\-]?=)\s*(.*?)\s*'$",line,re.I):
+		elif match := re.match(r"^dynamic\s*\$([a-zA-Z]\w+)$",line,re.I):
+			line_output = f'<<{match.group(1)}>>'
+		elif match := re.match(r"""^([^\$])*<a\s+href\s*=\s*"exec:\s*(?:minut\s*\+=\s*(\d+)\s*&\s*)?gt(.*)?"\s*>(.*?)</a>.*$""",line,re.I):
+			while link_match := re.search(r"""<a\s+href\s*=\s*"exec:\s*(?:minut\s*\+=\s*(\d+)\s*&\s*)?gt(.*)?"\s*>(.*?)</a>""",line,re.I):
+				full_match = link_match.group(0)
+				time = ""
+				if link_match.group(1):
+					time = f"<<set $minut += {link_match.group(1)}>>"
+				goto = link_match.group(2)
+				goto = goto.replace(","," ").replace(r"''",r"'")
+				label = link_match.group(3)
+				link = f"<<link '{label}'>>{time}<<gt {goto}>><</link>>"
+				line = line.replace(full_match,link)
+			line_output = line
+		elif match := convert_command(line):
+			line_output = match
+			pv("IS COMMAND:",line_output)
+		else:
+			line_output = convert_literal(line)
+			if len(line_output) >= 2 and line_output[0] == '\'' and line_output[-1] == '\'':
+				line_output = f'<p>{line_output[1:-1]}</p>'
+				pv("IS LITERAL:",line_output)
+
+		whitespace = whitespace_count * '\t'
+		output = f'{whitespace}{line_output}\n'
+		if output.strip():
+			#file.write(cleanUpTheMess(output))
+			output = cleanUpTheMess(output)
+			if "ERROR:" in output:
+				global error_counter
+				output = f'{whitespace}<!-- FAILED TO CONVERT\n{whitespace}\t{original}\n{whitespace}-----\n\t{output}\n{whitespace}-->\n{whitespace}<<warn "CONVERSION ERROR {(hashlib.md5(original.encode())).hexdigest()}">>\n'
+				error_counter += 1
+			outputs.append(output)
+
+	for subpassId in subpassages:
+		subpass = subpassages[subpassId]
+		subpassageLines = convert_lineblock(subpass)
+		outputs.append(f'\n:: {subpassId}\n')
+		for subpassageLine in subpassageLines:
+			outputs.append(subpassageLine)
+
+	if len(functionlines) > 0:
+			#outputs.append(f'::{location_identifier}_widgets[widget]\n')
+			for functionline in functionlines:
+				functionline = functionline.replace('$ARGS','_args').replace('$location_var[$here][','_args[')
+				outputs.append(functionline)
+
+	return outputs
+
+
+
+def convert_file(filename,skipIfNotExplicit=0,defaultsubfolder=False):
+	skip = skipIfNotExplicit
+	qsp_filename = filename+".qsrc"
+	qsp_filepath = os.path.join(qsp_sources_path,qsp_filename)
+
+	tw_filename = filename+".tw"
+	tw_filepath = os.path.join(tw_sources_path,tw_filename)
+
+	if defaultsubfolder:
+		os.makedirs(os.path.join(tw_sources_path,defaultsubfolder),exist_ok =True)
+		tw_filepath = os.path.join(tw_sources_path,defaultsubfolder,tw_filename)
+
+	try:
+		with open(qsp_filepath) as file:
+			lines = [line.rstrip() for line in file]
+	except:
+		try:
+			with open(qsp_filepath, encoding="utf-8") as file:
+				lines = [line.rstrip() for line in file]
+		except:
+			return f"FAILED: {qsp_filename}"
+
+
+	location_identifier = ''
+
+	ignore_recusions = 0
+
+	if match := re.match(r"^\s*!!\s*(FOLDER\s*:\s*\w+)?\s*(SKIP\s*:\s*-?\d)?\s*(IGNORERECURSIONS\s*:\s*-?\d)?\s*$",lines[0],re.I):
+		if match.group(1):
+			parts = match.group(1).split(':')
+			new_path = os.path.join(tw_sources_path,parts[1].strip())
+			os.makedirs(new_path,exist_ok =True)
+			tw_filepath = os.path.join(new_path,tw_filename)
+		if match.group(2):
+			parts = match.group(2).split(':')
+			arg = int(parts[1])
+			if arg == 1:
+				skip = 1
+			elif arg == 0:
+				skip = 0
+			elif arg == -1:
+				skip = -1
+		if match.group(3):
+			parts = match.group(3).split(':')
+			arg = int(parts[1])
+			if arg == 1:
+				ignore_recusions = 1
+			elif arg == 0:
+				ignore_recusions = 0
+
+
+	if skip == 1:
+		return
+
+	if skip == -1 and os.path.exists(tw_filepath):
+		modification_time_delta = os.path.getmtime(qsp_filepath) - os.path.getmtime(tw_filepath)
+		if modification_time_delta <= 0:
+			return
+
+	identifier_line = 0
+	for line_num in range(0,len(lines)-1):
+		line_raw = lines[line_num]
+		line = line_raw.strip()
+		match = re.match(r"#\s*(\S+)", line)
+		if match:
+			location_identifier = match.group(1)
+			identifier_line = line_num
+			break
+
+	with open(tw_filepath, 'w') as file:
+		#file.write(f'<!-- GENERATED: {datetime.datetime.now()} -->\n')
+		file.write(f':: {location_identifier}\n')
+		file.write(f"<<set $here = '{location_identifier}'>>\n<<set $ARGS = $location_var[$here]>>\n")
+
+		if ignore_recusions == 1:
+			file.write(f'<<set _ts to Math.floor(Date.now() / 10000)>>\n')
+			file.write(f'<<setinit $gt_history[_ts][$here] = 0>>\n')
+
+
+
+
+
+		#for line_num in range(identifier_line+1,len(lines)-1):
+		outputs = convert_lineblock(lines[identifier_line+1:],location_identifier)
+
+		for output in outputs:
+			file.write(output)
+
+	with open(tw_filepath, 'r') as file:
+		data = file.read()
+
+	with open(tw_filepath, 'w') as file:
+		regex1 = r"""<<gs 'willpower' ([\s'\w]*)>>\s*<<if \$will_cost <= \$pc\.pcs_willpwr>>\s*<<act '([\w\s]*?)\s*\(will_cost Willpower\)'>>"""
+
+		while match := re.search(regex1,data,re.I):
+			blockStart = match.end()
+			regex_close = r"""<<else>>\s*<<act `?'"""+match.group(2)+r""".*?<</act>>\s*<</if>>"""
+			if close_match := re.search(regex_close,data,re.I):
+				blockEnd = close_match.start()
+				block = data[blockStart:blockEnd]
+				# Remove the re-calculation of the willpower-cost
+				newBlock = block
+				newBlock = newBlock.replace("<<gs 'willpower' "+match.group(1)+">>",'')
+				newBlock = re.sub(r"""<<gs 'willpower' 'pay' ([\s'\w]*)>>""","",newBlock)
+				data = data.replace(block,newBlock)
+
+				data = data.replace(close_match.group(0),"")
+
+				arguments = match.group(1).replace(' ',',')
+				data = data.replace(match.group(0),"<<act '"+match.group(2)+"' undefined `{willpower:["+arguments+"]}`>>")
+			else:
+				#print("FAIL: "+match.group(0))
+				break
+
+		data = data.replace('<<act ','<<actCLA ')
+		data = data.replace('<</act>>','<</actCLA>>')
+
+		file.write(data)
+
+	with open(tw_filepath, 'r') as file:
+		data = file.read()
+		regex_split = r"""::\s*SPLIT:(\w+)"""
+		while match := re.search(regex_split,data,re.I):
+			identifier = match.group(1)
+			regex_split_end = r"""<!-- END: SPLIT:"""+identifier+r"""\s*-->"""
+			if end_match := re.search(regex_split_end,data,re.I):
+				sub_filepath = os.path.join(new_path,identifier+'.tw')
+				subdata = data[match.start():end_match.end()]
+				subdata = subdata.replace("SPLIT:"+identifier,identifier,1)
+				with open(sub_filepath, 'w') as subfile:
+					subfile.write(subdata)
+				data = data[:match.start()]+data[end_match.end():]
+				data = data.replace("SPLIT:"+identifier,identifier,1)
+
+
+	with open(tw_filepath, 'w') as file:
+		file.write(data)
+
+	return tw_filepath
+
+FTCL = [r'ERROR: FAILED TO CONVERT LITERAL:\s*"""',r'"""']
+VAR = r"""\$?([a-zA-Z][a-zA-Z0-9-_\[\]'"]+]*)"""
+
+preparations = [
+	[
+		r"""\$week\[""",
+		r"$week_name["
+	],
+	[
+		r"""pcs_pubecol\[""",
+		r"pcs_pubecol_num["
+	],
+	[
+		r"""pcs_pubes\[""",
+		r"pcs_pubes_num["
+	],
+	[
+		r"""^PLAY\s*.*""",
+		r""
+	],
+	[
+		r"""^\s*pl\s+(.*)$""",
+		r"\1"
+	]
+	#[
+	#	r"""(</?(?:table|center|th|tr|td))([^>]*=[^>]*)(>)""",
+	#	r'\1\3',
+	#	re.I
+	#]
+]
+
+skill_names = [
+	[r"\$pcs_heels","highHeels"],
+	[r"\$pcs_stren","strength"],
+	[r"\$pcs_agil","agility"],
+	[r"\$pcs_intel","intelligence"],
+	[r"\$pcs_react","reaction"],
+	[r"\$pcs_sprt","spirit"],
+	[r"\$pcs_chrsm","charisma"],
+	[r"\$pcs_prcptn","perception"],
+	[r"\$pcs_humint","people"],
+	[r"\$pcs_persuas","persuasion"],
+	[r"\$pcs_observ","observation"],
+	[r"\$pcs_jab","jabs"],
+	[r"\$pcs_punch","punch"],
+	[r"\$pcs_kick","kick"],
+	[r"\$pcs_def","defense"],
+	[r"\$pcs_run","run"],
+	[r"\$pcs_vball","volleyball"],
+	[r"\$pcs_ftbll","football"],
+	[r"\$pcs_wrstlng","wrestling"],
+	[r"\$pcs_shoot","shoot"],
+	[r"\$pcs_bushcraft","bushcraft"],
+	[r"\$pcs_chess","chess"],
+	[r"\$pcs_icesktng","iceskating"],
+	[r"\$pcs_gaming","gaming"],
+	[r"\$pcs_makupskl","makeup"],
+	[r"\$pcs_danc","dance"],
+	[r"\$pcs_dancero","eroticdance"],
+	[r"\$pcs_dancpol","poledance"],
+	[r"\$pcs_cheer","cheerleading"],
+	[r"\$pcs_mdlng","modelling"],
+	[r"\$pcs_vokal","singing"],
+	[r"\$pcs_instrmusic","playInstrument"],
+	[r"\$pcs_photoskl","photo"],
+	[r"\$pcs_artskls","art"],
+	[r"\$pcs_compskl","computer"],
+	[r"\$pcs_comphckng","hacking"],
+	[r"\$pcs_hndiwrk","handyWork"],
+	[r"\$pcs_sewng","sewing"],
+	[r"\$pcs_servng","serving"],
+	[r"\$pcs_medcn","medicine"],
+
+]
+
+replaces = [
+	[
+		r"ERROR: FAILED TO CONVERT CONDITION: func\(",
+		r"func("
+	],
+	[
+		r"ERROR: FAILED TO CONVERT CONDITION: \$?([a-zA-Z])",
+		r"$\1"
+	],
+	[
+		FTCL[0]+r"killvar\s*'"+VAR+r"'"+FTCL[1],
+		r"<<set $\1 to undefined>>"
+	],
+	[
+		r"""<img\s+(<<\$set_imgh>>)?\s*src="([^<^\.]*)(<<\$?([a-zA-Z][a-zA-Z0-9-_\[\]'"\)\(),\s]+]*)>>)?(\.[^"]+)">""",
+		r"<<image `'\2\3\5'`>>"
+	],
+	[
+		r"""'([\w\/]+)<<\s*rand\s*\(\s*(\-?\d+)\s*,\s*(\-?\d+)\s*\)\s*>>([^']*)'""",
+		r"""'\1'+rand(\2,\3)+'\4'"""
+	],
+	[
+		r"""<<set \$?([a-zA-Z][a-zA-Z0-9-_'"]+]*\[.*)>>""",
+		r"""<<setinit $\1>>"""
+	],
+	[
+		# <<set $gopnikbandQW += -1>> -> <<setn $gopnikbandQW += -1>>
+		r"""<<set (\$[^>]*?)\s([+-]=)\s*(-?\d+)>>""",
+		r"""<<setn \1 \2 \3>>""",
+		1
+	],
+	[
+		# asd
+		r"""<<set (\$[^>]*?)\s(\+=)\s*(["'][^>]*?)>>""",
+		r"""<<sets \1 \2 \3>>""",
+		1
+	],
+	[
+		r"""(\+)\s+=""",
+		r"""\1="""
+	],
+	[
+		r"(I|you|You|he|He|she|She|it|It|we|We|they|They|can|Can|don|Don)''(m|re|s|ll|ve|t)",
+		r"\1'\2"
+	],
+	#[
+	#	r"^(.*"+FTCL[0]+r"(.*)"+FTCL[1]+r".*)$",
+	#	r"<!-- \1 -->\n<<warn 'FTCL: \1'>>"
+	#],
+	[
+		r"=\s*\$(mid|MID)\s*\(",
+		r"= mid("
+	],
+	[
+		r"(<<set\s*.*?\[)([a-zA-Z].*?)(\]\s*=.*>>)",
+		r"\1$\2\3"
+	],
+	[
+		r"(<<act\s*'.*)(\$.*?\])(.*'>>)",
+		r"\1'+\2+'\3"
+	],
+	[
+		r"\[([a-zA-Z]\w*)\]",
+		r"[$\1]"
+	],
+	[
+		r"([\+\-])\s+=",
+		r"\1="
+	],
+	[
+		r"<<setinit\s*(.*)\[\]\s*=\s*(.*)>>",
+		r"""<<setinitpush "\1" \2>>"""
+	],
+	[
+		r"\s+min\(",
+		r" Math.min("
+	],
+	[
+		r"\s+max\(",
+		r" Math.max("
+	],
+	[
+		r"""<<set (.*) to null>>\s*,\s*(.*)(\s*)""",
+		r"<<set \1[\2] to null>>\3"
+	]
+	,
+	[
+		r"""\$arrsize\(""",
+		r"arrsize("
+	],
+	[
+		r"""<<set(?:init)?\s+\$?(?P<name>\w+)\[arrsize\('\$?(?P=name)'\)\]\s*=\s*(.*)\s*>>""",
+		r"<<run $\1.push(\2)>>"
+	],
+	[
+		r"""<center><(?:h\d|b)><font color="maroon">(.*)</font></(?:h\d|b)></center>""",
+		r"<h2>\1</h2>"
+	],
+
+
+	[
+		r"""(<<act '[^']*)'([^']*'>>)""",
+		r"\1`\2"
+	],
+	# NPC-Stuff Start
+	[
+		r"""\$npc_(\w*?)\[([^\]]*?)\]""",
+		r"$npcs.get(\2,'\1')",
+		1
+	],
+	# Fix for Sub-Arrays
+	[
+		r"""\$npcs\.get\((.*?),('\w*')\)(\[[^\]]*\]+?)\]""",
+		r"$npcs.get(\1]\3,\2)",
+		1
+	],
+	[
+		r"""<<set(?:init)?\s+\$npcs\.get\((.*?)\)\s*=\s*(.*?)>>""",
+		r"<<run $npcs.set(\1,\2)>>",
+		1
+	],
+	[
+		r"""<<set(?:init)?\s+\$npcs\.get\((.*?)\)\s*\+=\s*(.*?)>>""",
+		r"<<run $npcs.inc(\1,\2)>>",
+		1
+	],
+	[
+		r"""<<set(?:init)?\s+\$npcs\.get\((.*?)\)\s*-=\s*(.*?)>>""",
+		r"<<run $npcs.dec(\1,\2)>>",
+		1
+	],
+	# NPC-Stuff END
+	[
+		r"""(\$\w+(?:\['\w+'\])?)\s*(==|>=?|<=?|!=)\s*(\-?\d+)""",
+		r"""getvar("\1") \2 \3""",
+		1
+	],
+	[
+		#Example: $property_construction_status[$i] == 0 -> getvar("$property_construction_status["+$i+"]") == 0
+		r"""(\$\w+)(?:\[(\$\w+)\])\s*(==|>=?|<=?|!=)\s*(\-?\d+)""",
+		r"""getvar("\1["+\2+"]") \3 \4""",
+		1
+	],
+	[
+		r"""\*?\s*\$\s*cl[ar]\s*$""",
+		r""
+	],
+	[
+		r"""<<gs 'clothing' 'wear'\s*(.*?)\s+(.*?)>>""",
+		r"<<run $wardrobe.wear_clothes_legacy('clothes',\1,\2)>>"
+	],
+	[
+		r"""<<gs 'shoes' 'wear'\s+(.*?)\s+(.*?)>>""",
+		r"<<run $wardrobe.wear('shoes',\1,\2)>>"
+	],
+	[
+		r"""<<gs\s+'shoes'\s+'wear'\s+'last_worn'\s*>>""",
+		r"<<run $wardrobe.wear_last('shoes')>>"
+	],
+	[
+		r"""<<gs\s+'bras'\s+'wear'\s+(.+?)\s+(.+?)\s*>>""",
+		r"<<run $wardrobe.wear('bra',\1,\2)>>"
+	],
+	[
+		r"""<<gs\s+'panties'\s+'wear'\s+(.+?)\s+(.+?)\s*>>""",
+		r"<<run $wardrobe.wear('panties',\1,\2)>>"
+	],
+	[
+		r"""<<gs\s+'coats'\s+'wear'\s+(.+?)\s+(.+?)\s*>>""",
+		r"<<run $wardrobe.wear('coat',\1,\2)>>"
+	],
+	[
+		r"""<<gs\s+'bras'\s+'wear'\s*>>""",
+		r"<<run $wardrobe.wear_last('bra')>>"
+	],
+	[
+		r"""<<gs\s+'panties'\s+'wear'\s*>>""",
+		r"<<run $wardrobe.wear_last('panties')>>"
+	],
+	[
+		r"""iif\(([^)]*?)\s*==?\s*'',""",
+		r"iif(!\1,",
+		1
+	],
+	# Rand in gs Fix
+	[
+		r"""<<g([st].*?)\srand\((.*?)\)(.*?)>>""",
+		r"<<g\1 `rand(\2)`\3>>",
+		1
+	],
+	# Pain
+	[
+		r"""(<<(?:else)?if\s+|and\s+|x?or\s+)(?:getvar\(")\$pain\[('\w+')\](?:"\))(.*?>>)""",
+		r"\1$pc.pain(\2)\3",
+		1
+	],
+	[
+		r"""<<set(?:init)?\s+\$pain\[('\w+')\]\s*\+=\s*(.*?)>>""",
+		r"<<run $pc.painInc(\1,\2)>>",
+		1
+	],
+	[
+		r"""<<set(?:init)?\s+\$pain\[('\w+')\]\s*-=\s*(.*?)>>""",
+		r"<<run $pc.painDec(\1,\2)>>",
+		1
+	],
+	[
+		r"""<<set(?:init)?\s+\$pain\[('\w+')\]\s*=\s*(.*?)>>""",
+		r"<<run $pc.painSet(\1,\2)>>",
+		1
+	],
+	#Cum
+	[
+		r"""\$cumloc\[(\d+)\]""",
+		r"$pc.cumAtLocation(\1)",
+		1
+	],
+	# Inner Thought
+	[
+		r"""(?:'?\s*\+\s*)?\$OpenInnerThought\s*\+\s*'(.*?)'\s*\+\s*\$CloseInnerThought(?:\s*\+\s*'?)?""",
+		r"""<span class="innerThought">\1</span>""",
+		1
+	],
+	# Group Membership
+	[r'(?:getvar\(")?\$grupTipe(?:"\))?\s*==\s*1',r"$q.school.func('isGroupMember','cool')",1],
+	[r'(?:getvar\(")?\$grupTipe(?:"\))?\s*==\s*2',r"$q.school.func('isGroupMember','jocks')",1],
+	[r'(?:getvar\(")?\$grupTipe(?:"\))?\s*==\s*3',r"$q.school.func('isGroupMember','nerds')",1],
+	[r'(?:getvar\(")?\$grupTipe(?:"\))?\s*==\s*4',r"$q.school.func('isGroupMember','gopniks')",1],
+	[r'(?:getvar\(")?\$grupTipe(?:"\))?\s*==\s*5',r"$q.school.func('isGroupMember','outcasts')",1],
+	[r'(?:getvar\(")?\$grupTipe(?:"\))?\s*==\s*6',r"$q.school.func('isGroupMember','teachers')",1],
+	[r'<<set(?:init)?\s+\$grupTipe(?:"\))?\s*=\s*1>>',r"<<run $q.school.func('setGroupMembership','cool')>>",1],
+	[r'<<set(?:init)?\s+\$grupTipe(?:"\))?\s*=\s*2>>',r"<<run $q.school.func('setGroupMembership','jocks')>>",1],
+	[r'<<set(?:init)?\s+\$grupTipe(?:"\))?\s*=\s*3>>',r"<<run $q.school.func('setGroupMembership','nerds')>>",1],
+	[r'<<set(?:init)?\s+\$grupTipe(?:"\))?\s*=\s*4>>',r"<<run $q.school.func('setGroupMembership','gopniks')>>",1],
+	[r'<<set(?:init)?\s+\$grupTipe(?:"\))?\s*=\s*5>>',r"<<run $q.school.func('setGroupMembership','outcasts')>>",1],
+	[r'<<set(?:init)?\s+\$grupTipe(?:"\))?\s*=\s*6>>',r"<<run $q.school.func('setGroupMembership','teachers')>>",1],
+]
+
+purge_messes=[
+	r"""<<set(init)? \$npc_selfie\[""",
+	r'''<<set(init)? \$npcGo\[''', # We need to replace this by another function,
+	r'''<<set(init)? \$npcGoSchool\['''
+]
+
+
+
+def cleanUpTheMess(output):
+	for purge_mess in purge_messes:
+		if match := re.search(purge_mess,output):
+			return ''
+
+	for skill_name in skill_names:
+		oldSN = skill_name[0]
+		newSN = skill_name[1]
+		oldSN_without_prefix = oldSN.split('_')[1]
+		output = re.sub(r"<<set(?:init)?\s+"+oldSN+r"\s*=\s*(.*?)\s*>>",r"<<run $pc.skillSetLevel('"+newSN+r"',\1)>>",output)
+		output = re.sub(r"<<gs\s+'exp_gain'\s+('"+oldSN_without_prefix+r"')\s+`?(.*?)`?>>",r"<<run $pc.skillExperienceGain('"+newSN+r"',\2)>>",output)
+		output = re.sub(oldSN,'$pc.skillLevel("'+newSN+'")',output)
+
+	for replace in replaces:
+		if len(replace) > 2:
+			if replace[2] == 1:
+				while(re.search(replace[0],output)):
+					output = re.sub(replace[0],replace[1],output)
+		else:
+			output = re.sub(replace[0],replace[1],output)
+
+	if warnmatch := re.search(r"""<<warn '(.*)'>>""",output):
+		return output.replace(warnmatch.group(1),warnmatch.group(1).replace("'",'"'))
+
+	if link_match := re.findall(r"""(<a href="exec:([^"]+)">([^<]+)<\/a>)""",output):
+		for lmatch in link_match:
+			output = output.replace(lmatch[0],f"""<<link "{lmatch[2]}">>{convert_command(lmatch[1])}<</link>>""")
+
+	output = re.sub(r"""\$result""","$result",output,0,re.I)
+
+	while image_match := re.match(r"""<img\s+(<<\$set_imgh>>)?\s*src="([^<^\.]*)(<<\$?([a-zA-Z][a-zA-Z0-9-_\[\]'"]+]*)>>)?\.[^"]+">""",output):
+		if len(image_match.group(3)) == 0:
+			break
+		output = output.replace(image_match.group(3),f'"+${image_match.group(4)}+"')
+
+	while match := re.search(r"""<<set(?:init)?[^=]+=\s*'([a-zA-Z0-9\.\?!<>"\s,`\-\(\)]+)('([a-zA-Z0-9\.\?!<>"\s,`\-\(\)]+))+'>>""",output):
+		output = output.replace(match.group(2),f"`{match.group(3)}")
+
+	# NPCs: add print to all get-calls which are not inside a macro
+	if(not output.strip().startswith('<<')):
+		output = re.sub(r"(?<!=)(\$npcs\.get\(.*?\))",r"<<=\1>>",output)
+
+	# Arousal
+	while match := re.search(r"<<gs 'arousal' '(\w+)' (.*?)(\s.*?)?>>",output):
+		if match.group(3):
+			arguments = "`["+match.group(3).replace("' '","','").strip()+"]`"
+		else:
+			arguments = ''
+		output = output.replace(match.group(0),"<<arouse '"+match.group(1)+"' "+match.group(2)+" "+arguments+">>")
+
+
+	for variable_replacement in variables.variable_replacements:
+		if len(variable_replacement) > 2:
+			if variable_replacement[2] == 1:
+				output = re.sub(variable_replacement[0],variable_replacement[1],output)
+			else:
+				output = output.replace(variable_replacement[0],variable_replacement[1])
+		else:
+			output = output.replace(variable_replacement[0],variable_replacement[1])
+			output = output.replace('getvar("'+variable_replacement[1]+'")',variable_replacement[1])
+
+	# Inventory-Vars
+	for inventory_variable in variables.inventory_variables:
+		output = re.sub(rf"<<set(?:init)?\s+\${inventory_variable[0]}\s*=\s*(.*?)\s*>>",rf"<<run $inventory.set('{inventory_variable[1]}',\1)>>",output)
+		output = re.sub(rf"<<set(?:init)?\s+\${inventory_variable[0]}\s*-=\s*(.*?)\s*>>",rf"<<run $inventory.dec('{inventory_variable[1]}',\1)>>",output)
+		output = re.sub(rf"<<set(?:init)?\s+\${inventory_variable[0]}\s*\+=\s*(.*?)\s*>>",rf"<<run $inventory.inc('{inventory_variable[1]}',\1)>>",output)
+		output = re.sub(rf"<<{inventory_variable[0]}>>",rf"""$inventory.get("{inventory_variable[1]}")""",output)
+		output = re.sub(rf"""getvar\("\${inventory_variable[0]}"\)""",rf"""$inventory.get("{inventory_variable[1]}")""",output)
+		output = re.sub(rf"""\${inventory_variable[0]}([^\w])""",rf"""$inventory.get("{inventory_variable[1]}")\1""",output)
+
+	# Get Set Variables
+	for get_set_variable in variables.get_set_variables:
+		output = re.sub(rf"<<set(?:init)?\s+{get_set_variable[0]}\s*=\s*(.*?)\s*>>",rf"<<run {get_set_variable[2]}>>",output)
+		if(len(get_set_variable) == 5):
+			output = re.sub(rf"<<set(?:init)?\s+{get_set_variable[0]}\s*\+=\s*(.*?)\s*>>",rf"<<run {get_set_variable[3]}>>",output)
+			output = re.sub(rf"<<set(?:init)?\s+{get_set_variable[0]}\s*\-=\s*(.*?)\s*>>",rf"<<run {get_set_variable[4]}>>",output)
+		#output = re.sub(rf"<<set(?:init)?\s+\${get_set_variable[0]}\s*-=\s*(.*?)\s*>>",rf"<<run $inventory.dec('{inventory_variable[1]}',\1)>>",output)
+		#output = re.sub(rf"<<set(?:init)?\s+\${get_set_variable[0]}\s*\+=\s*(.*?)\s*>>",rf"<<run $inventory.inc('{inventory_variable[1]}',\1)>>",output)
+		output = re.sub(rf"<<{get_set_variable[0]}>>",get_set_variable[1],output)
+		output = re.sub(rf"""getvar\("{get_set_variable[0]}"\)""",get_set_variable[1],output)
+		output = re.sub(rf"""{get_set_variable[0]}(?P<stuffToTheRight>[^\w])""",rf"""{get_set_variable[1]}\g<stuffToTheRight>""",output)
+
+	for pgsvr in  variables.post_get_set_variables_replacements:
+		output = output.replace(pgsvr[0],pgsvr[1])
+
+	output = output.replace("$location_var[$here][0] == ''","!$location_var[$here][0]")
+
+	return output
+
+def testConvertLine(line):
+	test_line = ''
+	#test_line = """gs 'npc_relationship', 'socialgroup_setting', 0, 0, -10, 10, -10, -10"""
+
+	if len(test_line) > 0:
+		verbose = True
+		print(test_line)
+		result = convert_lineblock([test_line])
+		if len(result) > 0:
+			print(result[0])
+		else:
+			print("EMPTY RESULT")
+		exit()
+
+def convertFiles(restrictfiles=[]):
+	#output_files = []
+
+	os.makedirs(tw_sources_path, exist_ok=True)
+
+	#for replace in replaces:
+	#	print(replace[0])
+
+	#restrictfiles = []
+	#restrictfiles = ['gschool_gopnik_chats']
+	restrictfilesRegex = [r'.*gschool_gopnik.*']
+
+	files  = os.listdir(qsp_sources_path)
+	file_counter = 0
+	last_displayed_percentage_time = time.time()
+
+	filesToDo = []
+
+	for file in files:
+		regexMatched = False
+		for fileRegex in restrictfilesRegex:
+			if re.match(fileRegex,file):
+				regexMatched = True
+		if (len(restrictfiles) == 0 and len(restrictfilesRegex) == 0) or (os.path.splitext(file)[0] in restrictfiles) or regexMatched:
+			if file.endswith(".qsrc"):
+				filesToDo.append(os.path.splitext(file)[0])
+				#output_files.append(convert_file(os.path.splitext(file)[0],skipMode,'unsorted'))
+
+
+	with ThreadPoolExecutor(32) as executor:
+		# submit all tasks
+		futures = [executor.submit(convert_file, p, skipMode, 'unsorted') for p in filesToDo]
+		# process all results
+		for future in as_completed(futures):
+			# open the file and load the data
+			completedPath = future.result()
+			# report progress
+			#print(f'.loaded {filepath}')
+
+
+			file_counter += 1
+			if(last_displayed_percentage_time + 5 < time.time()):
+				print(str(round(file_counter/len(files)*100, 2))+"%")
+				last_displayed_percentage_time = time.time()
+
+
+
+	#for output_file in output_files:
+	#	for line in fileinput.input(output_file, inplace=True):
+	#
+	#		for function_name_conversion in function_name_conversions:
+	#
+	#
+	#		if fileinput.filelineno() == 10:
+	#			print(('10'+line), end='')
+	#	break
+if __name__ == "__main__":
+	convertFiles()

+ 283 - 0
variables.py

@@ -0,0 +1,283 @@
+variable_replacements = [
+    ['$pcs_firstname',  '$pc.name_first'],
+    ['$pcs_lastname',   '$pc.name_last'],
+    ['$pcs_nickname',   '$pc.name_nick'],
+    ['$genbsize',       '$pc.genbsize'],
+    ['$nbsize',         '$pc.nbsize'],
+    ['$wratio'      ,   '$pc.wratio'],
+    ['$bratio',         '$pc.bratio'],
+    ['$hratio',         '$pc.hratio'],
+    ['$fat',            '$pc.fat'],
+    ['$salo',           '$pc.salo'],
+    ['$salocatnow',           '$pc.salocatnow'],
+    ['$salolast',           '$pc.salolast'],
+    ['$salocatlast',           '$pc.salocatlast'],
+    ['$pcs_hips',           '$pc.pcs_hips'],
+    ['$pcs_waist',           '$pc.pcs_waist'],
+    ['$pcs_band',           '$pc.pcs_band'],
+    ['$pcs_bust',           '$pc.pcs_bust'],
+    ['$pcs_butt',           '$pc.pcs_butt'],
+    ['$pcs_cupsize',           '$pc.pcs_cupsize'],
+    ['$tits',           '$pc.tits'],
+    ['$titsize',           '$pc.titsize'],
+    ['$pcs_haircol',           '$pc.hairColor'],
+    ['$nathcol','$pc.hairColorNatural'],
+    ['$dyefade','$pc.hairDyeFade'],
+    ['$pcs_leghair',           '$pc.legHair'],
+    ['$pcs_weight',           '$pc.pcs_weight'],
+    ['$pcs_bmi',           '$pc.pcs_bmi'],
+    ['$pcs_hgt',           '$pc.pcs_hgt'],
+    ['$pcs_hairlng',           '$pc.pcs_hairlng'],
+    ['$pcs_tan',           '$pc.tan'],
+    ['$preg',           '$pc.preg'],
+    ['$knowpreg',           '$pc.knowpreg'],
+    ['$thinkpreg',           '$pc.thinkpreg'],
+    ['$PregChem',           '$pc.PregChem'],
+    ['$isPregnancyAware',           '$pc.isPregnancyAware'],
+    ['$bodset',           '$pc.bodset'],
+    ['$body',           '$pc.body'],
+    ['$pcs_teeth',           '$pc.pcs_teeth'],
+    ['$teeth',           '$pc.teeth'],
+    ['$moisturizerDailyCount',           '$pc.moisturizerDailyCount'],
+    ['$skinDailyGain',           '$pc.skinDailyGain'],
+    ['$skinDailyPenalty',           '$pc.skinDailyPenalty'],
+    ['$pcs_skin',           '$pc.pcs_skin'],
+    ['$pcs_vital',           '$pc.pcs_vital'],
+    ['$agilbuf',           '$pc.dexterity'],
+    ['$strenbuf',           '$pc.muscularity'],
+    ['$vitalbuf',           '$pc.healthiness'],
+    ['$vhips',           '$pc.vhips'],
+    ['$vhtmp',           '$pc.vhtmp'],
+    ['$pcs_pubes','$pc.pcs_pubes'],
+    ['$vidageday','$pc.visualAgeDaysInverse'],
+    ['$vidage','$pc.visualAge'],
+    ['$pcs_missing_teeth','$pc.pcs_missing_teeth'],
+    ['$pcs_apprncbase','$pc.pcs_apprncbase'],
+    ['$pubestyle','$pc.pubestyle'],
+    ['$pcs_mood[','$pc.mood_type['],
+    ['$pcs_mood','$pc.mood'],
+    ['$pcs_willpwr','$pc.pcs_willpwr'],
+    ['$willpowermax','$pc.willpowermax'],
+    ['$pcs_willpower_feeder','$pc.pcs_willpower_feeder'],
+    ['$pcs_energy','$pc.pcs_energy'],
+    ['$pcs_hydra','$pc.pcs_hydra'],
+    ['$pcs_sleep','$pc.pcs_sleep'],
+    ['$pcs_sweat','$pc.pcs_sweat'],
+    ['$analplugIN','$pc.analplugIN'],
+    ['$vibratorin','$pc.vibratorin'],
+
+    ['$pcs_intel','$pc.intelligence'],
+
+    ['$cosmetic_tattoo','$pc.cosmetic_tattoo'],
+    ['$pcs_makeup','$pc.pcs_makeup'],
+    ['$alko','$pc.alko'],
+    ['$frost','$pc.frost'],
+    ['$StrongNarkota','$pc.strongNarkota'],
+    ['$pcs_vag','$pc.pcs_vag'],
+    ['$pcs_throat','$pc.pcs_throat'],
+    ['$pcs_ass','$pc.pcs_ass'],
+    ['$SUB','$pc.pcs_sub'],
+    ['$pcs_dom','$pc.pcs_dom'],
+    ['$pcs_horny','$pc.horny'],
+    ['$pcs_inhib','$pc.pcs_inhib'],
+    ['$healthmax','$pc.healthmax'],
+    ['$stammax','$pc.stammax'],
+    ['$speed','$pc.speed'],
+    ['$min_arousal','$pc.min_arousal'],
+    ['$pcs_health','$pc.pcs_health'],
+    ['$pcs_stam','$pc.pcs_stam'],
+    ['$mood_trauma','$pc.mood_trauma'],
+    ['$pcs_apprnc','$pc.pcs_apprnc'],
+    ['$hotcat','$pc.hotcat'],
+    ['$pcs_breath','$pc.pcs_breath'],
+    ['$pcs_lipbalm','$pc.pcs_lipbalm'],
+    ['$pcs_hairbsh','$pc.pcs_hairbsh'],
+    ['$deodorant_on','$pc.deodorant_on'],
+    ['$glass','$pc.glass'],
+
+    ['$vgape','$pc.vgape'],
+    ['$agape','$pc.agape'],
+    ['$spanked','$pc.spanked'],
+
+    ['$nark','$pc.nark'],
+    ['$deodorant_time','$pc.deodorant_time'],
+
+    ['$insleep ','$pc.isSleeping'],
+    ['$will_counter ','$pc.will_counter'],
+
+    ['$over ','$pc.gameover'],
+
+    ['$birthday','$pc.birthday'],
+    ['$birthmonth','$pc.birthmonth'],
+    ['$birthyear','$pc.birthyear'],
+    [r'\$(age[^\w])',           r'$pc.\1',1],
+
+    ['$pcs_eyesize','$pc.eyesize'],
+    ['$pcs_eyecol','$pc.eyecolor'],
+    ['$pcs_lashes','$pc.eyelashes'],
+    ['$false_lashes','$pc.eyelashes_false'],
+    ['$pcs_nips','$pc.nipples'],
+    ['$clit_size','$pc.clit_size'],
+    ['$pcs_lip','$pc.lip_size'],
+    ['$pcs_haircol','$pc.pcs_haircol'],
+
+    ['$minut','$time.minutes'],
+    ['$hour','$time.hour'],
+    ['$day','$time.day'],
+    ['$month','$time.month'],
+    ['$year','$time.year'],
+    ['$dayStage','$time.dayStage'],
+    ['$DayInYear','$time.dayOfYear'],
+    ['$totminut ','$time.minutesTimestamp'],
+    ['$daystart','$time.daystart'],
+    ['$week','$time.weekday'],
+
+    ['$isSchoolHoliday','$time.isSchoolHoliday'],
+
+    ['$clothingworntype','$wardrobe.clothingworntype'],
+    ['$clothingwornnumber','$wardrobe.clothingwornnumber'],
+    ['$PCloQuality','$wardrobe.PCloQuality'],
+    ['$PCloThinness','$wardrobe.PCloThinness'],
+    ['$PCloTopCut','$wardrobe.PCloTopCut'],
+    ['$PCloBra','$wardrobe.PCloBra'],
+    ['$PCloOnePiece','$wardrobe.PCloOnePiece'],
+    ['$PCloPants','$wardrobe.PCloPants'],
+    ['$PCloSkirt','$wardrobe.PCloSkirt'],
+    ['$PCloPanties','$wardrobe.PCloPanties'],
+    ['$PCloDress','$wardrobe.PCloDress'],
+    ['$PCloStyle','$wardrobe.PCloStyle'],
+    ['$PCloStyle2','$wardrobe.PCloStyle2'],
+    ['$PCloStyle3','$wardrobe.PCloStyle3'],
+    ['$PCloInhibit','$wardrobe.PCloInhibit'],
+    ['$PCloBimbo','$wardrobe.PCloBimbo'],
+    ['$PCloCoverTop','$wardrobe.PCloCoverTop'],
+    ['$PCloCoverBack','$wardrobe.PCloCoverBack'],
+    ['$PCloCoverFront','$wardrobe.PCloCoverFront'],
+    ['$PCloThinness','$wardrobe.PCloThinness'],
+    ['$PCloTopCut','$wardrobe.PCloTopCut'],
+    ['$PCloCoverFront','$wardrobe.PCloCoverFront'],
+    ['$PXCloThinness','$wardrobe.PXCloThinness'],
+    ['$PXCloTopCut','$wardrobe.PXCloTopCut'],
+    ['$PXCloBottomShortness','$wardrobe.PXCloBottomShortness'],
+    ['$PCloswimwear','$wardrobe.PCloswimwear'],
+    ['$CoverTop','$wardrobe.CoverTop'],
+    ['$CoverBack','$wardrobe.CoverBack'],
+    ['$CoverFront','$wardrobe.CoverFront'],
+
+    ['$braworntype','$wardrobe.braworntype'],
+    ['$brawornnumber','$wardrobe.brawornnumber'],
+    ['$PBraMaterial','$wardrobe.PBraMaterial'],
+    ['$PBraType','$wardrobe.PBraType'],
+    ['$PBraFun','$wardrobe.PBraFun'],
+    ['$PBraQuality','$wardrobe.PBraQuality'],
+    ['$PBraThinness','$wardrobe.PBraThinness'],
+    ['$PBraCover','$wardrobe.PBraCover'],
+
+    ['$pantyworntype','$wardrobe.pantyworntype'],
+    ['$pantywornnumber','$wardrobe.pantywornnumber'],
+    ['$PPanMaterial','$wardrobe.PPanMaterial'],
+    ['$PPantyFun','$wardrobe.PPantyFun'],
+    ['$PPanQuality','$wardrobe.PPanQuality'],
+    ['$PPanThinness','$wardrobe.PPanThinness'],
+    ['$PPanCoverFront','$wardrobe.PPanCoverFront'],
+    ['$PPanCoverBack','$wardrobe.PPanCoverBack'],
+
+    ['$shoeworntype','$wardrobe.shoeworntype'],
+    ['$shoewornnumber','$wardrobe.shoewornnumber'],
+    ['$PShoQuaility','$wardrobe.PShoQuaility'],
+    ['$PShoCut','$wardrobe.PShoCut'],
+    ['$PShoHeels','$wardrobe.PShoHeels'],
+    ['$PShoStyle','$wardrobe.PShoStyle'],
+    ['$PXShoHeels','$wardrobe.PXShoHeels'],
+
+    ['$coatworntype','$wardrobe.coatworntype'],
+    ['$coatwornnumber','$wardrobe.coatwornnumber'],
+    ['$PCoatWarm','$wardrobe.PCoatWarm'],
+    ['$PCoatQuality','$wardrobe.PCoatQuality'],
+
+
+    ['$pcs_sleep','$pc.pcs_sleep'],
+
+
+    ["<<gs 'body' 'initial'>>","<<run $pc.bodyInit()>>"],
+    ["<<gs 'body' 'RegularUpdate'>>",""],
+    ["<<gs 'body' 'DailyUpdate'>>","<<run $pc.bodyDailyUpdate()>>"],
+    ["<<gs 'body'>>","<<run $pc.bodySaloCalc()>>"],
+    ["<<$pcs_apprnc>>","<<body_desc_appearance>>"],
+
+    ["<<gs 'arousal' 'end'>>","<<arousalEnd>>"],
+
+
+    ['$money','$finances.cash'],
+    ['$karta','$finances.bank'],
+    ['$bankAccount','$finances.hasBankAccount'],
+    ['$bankDebtLimit','$finances.bankOverdraftMax'],
+
+    ['$bag','$wardrobe.purseEquipped'],
+
+]
+
+#variable_replacements = []
+inventory_variables = [
+    ["stanok","razor"],
+    ["kosmetica","cosmetics"],
+    ["lipbalm","lipbalm"],
+    ["prezik","condom"],
+    ["painkiller","painkiller"],
+    ["vitamin","vitamin"],
+    ["tampon","tampon"],
+    ["shampoo","shampoo"],
+    ["deodorant","deo"],
+    ["eda","food"],
+    ["edaD","foodDiet"],
+    ["biscuits","biscuit"],
+    ["bottle","water"],
+    ["buterbrod","sandwich"],
+    ["wine","wine"],
+    ["item_moisturizer","moisturizer"],
+    ["sanpad","sanpad"],
+    ["hscrunch","hairScrunchie"],
+    ["hpingrip","hairPin"],
+    ["salfetka","wipe"],
+    ["mouthwash","mouthwash"],
+    ["greben","comb"],
+    ["compact_mirror","compactMirror"],
+    ["krem","sunblock"],
+    ["falselashesplain","falselashesplain"],
+    ["falselashesmink","falselashesmink"],
+    ["kosmetika","cosmetics"],
+    ["komp","computer"],
+    ["umbrella","umbrella"],
+    #["xxx","xxx"],
+]
+
+
+quest_variables = [
+    [r'\$missing_class','school','missedClasses'],
+    [r'\$BeInSchool','school','attendedDays'],
+
+    [r"\$christinaQW\['(?P<index>.*?)'\]",'christina',r'\g<index>'],
+    [r"\$cheerleadingQW\['(?P<index>.*?)'\]",'cheerleading',r'\g<index>'],
+]
+
+get_set_variables = [
+    [r'\$pirsA',"$pc.decoGet('piercing','tongue')",r"$pc.decoWear('piercing','tongue',\1)"],
+    [r'\$pirsB',"$pc.decoGet('piercing','lip')",r"$pc.decoWear('piercing','lip',\1)"],
+    [r'\$pirsC',"$pc.decoGet('piercing','ears')",r"$pc.decoWear('piercing','ears',\1)"],
+    [r'\$pirsD',"$pc.decoGet('piercing','nose')",r"$pc.decoWear('piercing','nose',\1)"],
+    [r'\$pirsE',"$pc.decoGet('piercing','eyebrow')",r"$pc.decoWear('piercing','eyebrow',\1)"],
+    [r'\$pirsF',"$pc.decoGet('piercing','nipple')",r"$pc.decoWear('piercing','nipple',\1)"],
+    [r'\$pirsG',"$pc.decoGet('piercing','pussy')",r"$pc.decoWear('piercing','pussy',\1)"],
+    [r'\$pirsN',"$pc.decoGet('piercing','navel')",r"$pc.decoWear('piercing','navel',\1)"],
+
+    [r'''\$stat\[(["'\w]+)\]''',r"$pc.sexStat(\1)",r"$pc.sexStatSet(\1,'aware',\2)",r"$pc.sexStatInc(\1,'aware',\2)",r"$pc.sexStatDec(\1,'aware',\2)"],
+
+
+]
+
+post_get_set_variables_replacements = [
+    ['''$pc.sexStat('think_virgin') == 1''','$pc.thinksIsVirgin'],
+    ['''$pc.sexStat('think_virgin') == 0''','!$pc.thinksIsVirgin'],
+    ['''$pc.sexStat('think_virgin') != 1''','!$pc.thinksIsVirgin'],
+    ['''$pc.sexStat('think_virgin') != 0''','$pc.thinksIsVirgin'],
+]