Mike Greene 5 роки тому
коміт
a01fb28a4a

+ 10 - 0
build

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+PornsterPath="/Users/mike/GitHub/KeyMasterOfGozer/pornster"
+ModFolder="/Users/mike/Downloads/Games/Girl Life/mod"
+
+${PornsterPath}/txtmerge.py ${PornsterPath}/locations ${PornsterPath}/pornster.txt
+
+${PornsterPath}/txt2gam.mac ${PornsterPath}/pornster.txt ${PornsterPath}/pornster.qsp
+
+cp ${PornsterPath}/pornster.qsp "${ModFolder}"

+ 153 - 0
keywords_en.xml

@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<QGen-keywords>
+  <Statements>
+    <Keyword name="pl" desc="PL [expression] / *PL [expression] - output text to an additional / main description window, then a new line"/>
+    <Keyword name="clear" desc="CLEAR / *CLEAR - cleaning additional / the main description window"/>
+    <Keyword name="clr" desc="CLR / *CLR - cleaning additional / the main description window"/>
+    <Keyword name="p" desc="P [expression] / *P [expression] - output text to an additional / main description window"/>
+    <Keyword name="nl" desc="NL [expression] / *NL [expression] - transition to a new line, then output the text to an additional/ main description window"/>
+    <Keyword name="msg" desc="MSG [expression] - output of the message in the information window"/>
+    <Keyword name="wait" desc="WAIT [#expression] - stop execution of the program for a specified number of milliseconds"/>
+    <Keyword name="act" desc="ACT [$variable],[$path to the image]:[operator] &amp; [operator] &amp; ... - adding an action"/>
+    <Keyword name="delact" desc="DELACT [$variable] / DEL ACT [$variable] - deleting an action"/>
+    <Keyword name="cla" desc="CLA - clearing the action list"/>
+    <Keyword name="cmdclear" desc="CMDCLEAR - clearing input line"/>
+    <Keyword name="cmdclr" desc="CMDCLEAR - clearing input line"/>
+    <Keyword name="cls" desc="CLS - clears the screen, leaving only a list of items"/>
+    <Keyword name="menu" desc="MENU [$expression] - displaying a menu with a given name"/>
+    <Keyword name="settimer" desc="SETTIMER [#expression] - specifies the timer interval for the counter location"/>
+    <Keyword name="dynamic" desc="DYNAMIC [$line of code],[parameter 1],[parameter 2], ... - dynamic code execution"/>
+    <Keyword name="set" desc="SET [variable name]=[expression] - setting the value of a variable"/>
+    <Keyword name="let" desc="LET [variable name]=[expression] - setting the value of a variable"/>
+    <Keyword name="killvar" desc="KILLVAR [$name],[#index] - delete all variables / specified variable"/>
+    <Keyword name="copyarr" desc="COPYARR [$array-receiver],[$source array] - copying the contents of an array to another array"/>
+    <Keyword name="addobj" desc="ADDOBJ [$variable],[$path to the image],[#position] / ADD OBJ [$variable],[$path to the image],[#position] - adding an item"/>
+    <Keyword name="delobj" desc="DELOBJ [$variable] / DEL OBJ [$variable] - object removal"/>
+    <Keyword name="killobj" desc="KILLOBJ [#position] - removal of all items / of the item in the given position"/>
+    <Keyword name="unselect" desc="UNSELECT - cancellation of object selection"/>
+    <Keyword name="unsel" desc="UNSEL - cancellation of object selection"/>
+    <Keyword name="killall" desc="KILLALL - delete all variables and items"/>
+    <Keyword name="opengame" desc="OPENGAME [$expression] - Loading the game state"/>
+    <Keyword name="openqst" desc="OPENQST [$expression] - opening and launching a game file"/>
+    <Keyword name="addqst" desc="ADDQST [$expression] - adding locations from the specified file"/>
+    <Keyword name="killqst" desc="KILLQST - delete all added via ADDQST locations"/>
+    <Keyword name="savegame" desc="SAVEGAME [$expression] - preservation of the game state"/>
+    <Keyword name="refint" desc="REFINT - interface update"/>
+    <Keyword name="showacts" desc="SHOWACTS [#expression] - display / hiding action list"/>
+    <Keyword name="showinput" desc="SHOWINPUT [#expression] - display / hiding the input line"/>
+    <Keyword name="showobjs" desc="SHOWOBJS [#expression] - display / hiding a list of items"/>
+    <Keyword name="showstat" desc="SHOWSTAT [#expression] - display / hiding an additional description window"/>
+    <Keyword name="if" desc="IF [#expression]:[operator1] &amp; [operator2] &amp; ... ELSE [operator3] &amp; [operator4] &amp; ... - condition"/>
+    <Keyword name="else" desc="IF [#expression]:[operator1] &amp; [operator2] &amp; ... ELSE [operator3] &amp; [operator4] &amp; ... - condition"/>
+    <Keyword name="elseif" desc="ELSEIF [#expression]:[operator1] &amp; [operator2] &amp; ... - alternate state"/>
+    <Keyword name="exit" desc="EXIT - completion of the current code"/>
+    <Keyword name="end" desc="END - completion of a multi-line form IF / ACT"/>
+    <Keyword name="jump" desc="JUMP [$expression] - go to the specified label"/>
+    <Keyword name="gosub" desc="GOSUB [$expression],[parameter 1],[parameter 2], ... - processing of the specified location"/>
+    <Keyword name="gs" desc="GS [$expression],[parameter 1],[parameter 2], ... - processing of the specified location"/>
+    <Keyword name="goto" desc="GOTO [$expression],[parameter 1],[parameter 2], ... - go to the specified location"/>
+    <Keyword name="gt" desc="GT [$expression],[parameter 1],[parameter 2], ... - go to the specified location"/>
+    <Keyword name="xgoto" desc="XGOTO [$expression],[parameter 1],[parameter 2], ... - go to the specified location without clearing the main description window"/>
+    <Keyword name="xgt" desc="XGT [$expression],[parameter 1],[parameter 2], ... - go to the specified location without clearing the main description window"/>
+    <Keyword name="play" desc="PLAY [$path to an audio file],[#volume] - playing an audio file with a specified volume"/>
+    <Keyword name="close" desc="CLOSE [$path to an audio file] / CLOSE ALL - stop playing a sound file / all sound files"/>
+    <Keyword name="view" desc="VIEW [$path to the graphic file] - image view"/>
+  </Statements>
+  <Expressions>
+    <Keyword name="and" desc="[#expression 1] AND [#expression 2] - logical &quot;and=&quot;"/>
+    <Keyword name="or" desc="[#expression 1] OR [#expression 2] - logical &quot;or&quot;"/>
+    <Keyword name="obj" desc="OBJ [$subject] - checking the presence of an object in a backpack"/>
+    <Keyword name="loc" desc="LOC [$location] - checking the existence of a location"/>
+    <Keyword name="no" desc="NO [#expression] - negation"/>
+    <Keyword name="mod" desc="[#expression 1] MOD [#expression 2] - calculation of the remainder from division"/>
+    <Keyword name="desc" desc="DESC([$expression]) - returns the text of the basic description of the location"/>
+    <Keyword name="$desc" desc="$DESC([$expression]) - returns the text of the basic description of the location"/>
+    <Keyword name="iif" desc="IIF([#expression],[Expression_Yes],[expression_no]) - возвращает одно из выражений по условию"/>
+    <Keyword name="$iif" desc="$IIF([#expression],[Expression_Yes],[expression_no]) - возвращает одно из выражений по условию"/>
+    <Keyword name="input" desc="INPUT([expression]) - shows the text input window and returns the entered value"/>
+    <Keyword name="$input" desc="$INPUT([expression]) - shows the text input window and returns the entered value"/>
+    <Keyword name="isplay" desc="ISPLAY([$expression]) - checks if the specified file is currently playing"/>
+    <Keyword name="max" desc="MAX([expression 1],[expression 2], ...) - returns the maximum of the argument values"/>
+    <Keyword name="$max" desc="$MAX([expression 1],[expression 2], ...) - returns the maximum of the argument values"/>
+    <Keyword name="min" desc="MIN([expression 1],[expression 2], ...) - returns the minimum of the argument values"/>
+    <Keyword name="$min" desc="$MIN([expression 1],[expression 2], ...) - returns the minimum of the argument values"/>
+    <Keyword name="rand" desc="RAND([#expression 1],[#expression 2]) - returns a random number between the given"/>
+    <Keyword name="rgb" desc="RGB([#red],[#green],[#blue]) - returns color code based on 3 components"/>
+    <Keyword name="getobj" desc="GETOBJ([#position]) - returns the name of the item located in the given position"/>
+    <Keyword name="$getobj" desc="$GETOBJ([#position]) - returns the name of the item located in the given position"/>
+    <Keyword name="dyneval" desc="DYNEVAL([$expression],[parameter 1],[parameter 2], ...) - returns the value of the dynamically evaluated expression"/>
+    <Keyword name="$dyneval" desc="$DYNEVAL([$expression],[parameter 1],[parameter 2], ...) - returns the value of the dynamically evaluated expression"/>
+    <Keyword name="func" desc="FUNC([$expression],[parameter 1],[parameter 2], ...) - processing specified location as a function"/>
+    <Keyword name="$func" desc="$FUNC([$expression],[parameter 1],[parameter 2], ...) - processing specified location as a function"/>
+    <Keyword name="arrsize" desc="ARRSIZE([$expression]) - returns the number of elements in the specified array"/>
+    <Keyword name="arrpos" desc="ARRPOS([#starting index],[$array name],[value]) - search in an array of an element with a given value"/>
+    <Keyword name="instr" desc="INSTR([#initial position],[$text],[$search text]) - search for text occurrences"/>
+    <Keyword name="isnum" desc="ISNUM([$expression]) - checks if the specified string is a number"/>
+    <Keyword name="trim" desc="TRIM([$expression]) - removes adjacent spaces and tabs from text"/>
+    <Keyword name="$trim" desc="$TRIM([$expression]) - removes adjacent spaces and tabs from text"/>
+    <Keyword name="ucase" desc="UCASE([$expression]) - converts small letters of text to large"/>
+    <Keyword name="$ucase" desc="$UCASE([$expression]) - converts small letters of text to large"/>
+    <Keyword name="lcase" desc="LCASE([$expression]) - converts large letters of text into small"/>
+    <Keyword name="$lcase" desc="$LCASE([$expression]) - converts large letters of text into small"/>
+    <Keyword name="len" desc="LEN([$expression]) - returns the length of the specified string"/>
+    <Keyword name="mid" desc="MID([$text],[#start],[#length]) - cuts out a string of the specified length starting from the specified location"/>
+    <Keyword name="$mid" desc="$MID([$text],[#start],[#length]) - cuts out a string of the specified length starting from the specified location"/>
+    <Keyword name="replace" desc="REPLACE([$text],[$search text],[$text to replace]) - replaces the specified string in the text"/>
+    <Keyword name="$replace" desc="$REPLACE([$text],[$search text],[$text to replace]) - replaces the specifieed string in the text"/>
+    <Keyword name="str" desc="STR([#expression]) - converts a number to a string"/>
+    <Keyword name="$str" desc="$STR([#expression]) - converts a number to a string"/>
+    <Keyword name="val" desc="VAL([$expression]) - converts a string to a number"/>
+    <Keyword name="arrcomp" desc="ARRCOMP([#starting index],[$array name],[$template]) - search in an array of an element corresponding to a regular expression"/>
+    <Keyword name="strcomp" desc="STRCOMP([$expression],[$template]) - checks the specified text for consistency with a regular expression"/>
+    <Keyword name="strfind" desc="STRFIND([$expression],[$template],[#number]) - returns a substring, corresponding to the group numbered [#number] regular expression"/>
+    <Keyword name="$strfind" desc="$STRFIND([$expression],[$template],[#number]) - returns a substring, corresponding to the group numbered [#number] regular expression"/>
+    <Keyword name="strpos" desc="STRPOS([$expression],[$template],[#number]) - returns the position of the substring, corresponding to the group with the number [#number] regular expression"/>
+    <Keyword name="countobj" desc="COUNTOBJ -returns the current number of items"/>
+    <Keyword name="msecscount" desc="MSECSCOUNT - returns the number of milliseconds, elapsed since the start of the game"/>
+    <Keyword name="rnd" desc="RND - returns a random value from 1 to 1000"/>
+    <Keyword name="curloc" desc="CURLOC - returns the name of the current location"/>
+    <Keyword name="$curloc" desc="$CURLOC - returns the name of the current location"/>
+    <Keyword name="qspver" desc="QSPVER - returns the version of the interpreter"/>
+    <Keyword name="$qspver" desc="$QSPVER - returns the version of the interpreter"/>
+    <Keyword name="selobj" desc="SELOBJ - returns the name of the selected item"/>
+    <Keyword name="$selobj" desc="$SELOBJ - returns the name of the selected item"/>
+    <Keyword name="selact" desc="SELACT - returns the name of the selected action"/>
+    <Keyword name="$selact" desc="$SELACT - returns the name of the selected action"/>
+    <Keyword name="curacts" desc="CURACTS - returns all current actions as code"/>
+    <Keyword name="$curacts" desc="$CURACTS - returns all current actions as code"/>
+    <Keyword name="user_text" desc="USER_TEXT - returns text in the input line"/>
+    <Keyword name="$user_text" desc="$USER_TEXT - returns text in the input line"/>
+    <Keyword name="usrtxt" desc="USRTXT - returns text in the input line"/>
+    <Keyword name="$usrtxt" desc="$USRTXT - returns text in the input line"/>
+    <Keyword name="maintxt" desc="MAINTXT - returns text in the main description window"/>
+    <Keyword name="$maintxt" desc="$MAINTXT - returns text in the main description window"/>
+    <Keyword name="stattxt" desc="STATTXT - returns text in an additional description window"/>
+    <Keyword name="$stattxt" desc="$STATTXT - returns text in an additional description window"/>
+  </Expressions>
+  <Variables>
+    <Keyword name="args" desc="ARGS - array with procedure parametersarray with procedure parameters / function"/>
+    <Keyword name="$args" desc="$ARGS - array with procedure parameters / function"/>
+    <Keyword name="result" desc="RESULT - the variable contains the result, returned by the current function"/>
+    <Keyword name="$result" desc="$RESULT - the variable contains the result, returned by the current function"/>
+    <Keyword name="disablescroll" desc="DISABLESCROLL - if the variable is not equal to 0, it prohibits auto-scrolling of the text in the output"/>
+    <Keyword name="disablesubex" desc="DISABLESUBEX - if the variable is not equal to 0, it prohibits the use of &quot;subexpressions&quot; in the text"/>
+    <Keyword name="nosave" desc="NOSAVE - if the variable is not equal to 0, the user can not save the game state"/>
+    <Keyword name="debug" desc="DEBUG - if the variable is not equal to 0, then disabling the game ID check when the state is loaded"/>
+    <Keyword name="$counter" desc="$COUNTER - variable contains the name of the location-counter"/>
+    <Keyword name="$ongload" desc="$ONGLOAD - variable contains the name of the location-the state load handler"/>
+    <Keyword name="$ongsave" desc="$ONGSAVE - variable contains the name of the location-a stateful handler"/>
+    <Keyword name="$onnewloc" desc="$ONNEWLOC - variable contains the name of the location-new location handler"/>
+    <Keyword name="$onactsel" desc="$ONACTSEL - variable contains the name of the location-the action selector"/>
+    <Keyword name="$onobjsel" desc="$ONOBJSEL - variable contains the name of the location-object selector handler"/>
+    <Keyword name="$onobjadd" desc="$ONOBJADD - variable contains the name of the location-addon handler"/>
+    <Keyword name="$onobjdel" desc="$ONOBJDEL - variable contains the name of the location-object removal handler"/>
+    <Keyword name="$usercom" desc="$USERCOM - variable contains the name of the location-input line handler"/>
+    <Keyword name="usehtml" desc="USEHTML - if the variable is not equal to 0, then includes the possibility of using HTML"/>
+    <Keyword name="bcolor" desc="BCOLOR - the variable contains the background color"/>
+    <Keyword name="fcolor" desc="FCOLOR - variable contains the main font color"/>
+    <Keyword name="lcolor" desc="LCOLOR - variable contains the main color of links"/>
+    <Keyword name="fsize" desc="FSIZE - variable contains the main font size"/>
+    <Keyword name="$fname" desc="$FNAME - variable contains the name of the main font"/>
+    <Keyword name="$backimage" desc="$BACKIMAGE - the variable contains the path to the background image"/>
+  </Variables>
+</QGen-keywords>

+ 12 - 0
locations/mod_pornster.qsrc

@@ -0,0 +1,12 @@
+#mod_pornster
+
+!input mod entry point here. this will be checked on every location, please keep it clean and brief to avoid game lag
+
+!pornstudio
+if $curloc = 'pornstudio':
+
+	act "Hack the Schedule": gs 'mod_pornster_hack'
+
+end
+
+--- mod_pornster ---------------------------------

+ 32 - 0
locations/mod_pornster_edit.qsrc

@@ -0,0 +1,32 @@
+#mod_pornster_edit
+
+$loc = 'mod_pornster_edit'
+$location_type = 'public_indoors'
+$menu_loc = 'mod_pornster_edit'
+$menu_arg = 'start'
+cls
+
+! Expected Parameters:
+! 0) the numeric porn day number offset (zero is today)
+! 1) the session number on the day
+pdateoffset = $ARGS[0]
+psessionnum = $ARGS[1]
+
+act "Back to Schedule": gs 'mod_pornster_hack'
+
+$picker = ''
+counter = 1
+num_porntypes = arrsize('$porndesc')
+
+:pick
+
+$picker += '<a href="exec:pornplan[''<<pdateoffset>>,<<psessionnum>>''] = <<counter>> & pfilmday[pdateoffset] = <<counter>> & gs ''stat'' & gs ''mod_pornster_hack''"><<$porndesc[counter]>></a><br>'
+
+if counter <= num_porntypes:
+	counter += 1
+	jump 'pick'
+end
+
+*pl $picker
+
+--- mod_pornster_edit ---------------------------------

+ 90 - 0
locations/mod_pornster_hack.qsrc

@@ -0,0 +1,90 @@
+#mod_pornster_hack
+
+$loc = 'mod_pornster_hack'
+$location_type = 'public_indoors'
+$menu_loc = 'mod_pornster_hack'
+$menu_arg = 'start'
+cls
+
+act 'Back to Manager': gs 'pornstudio', 'manager'
+
+$contracting = {
+	*clr
+	'<center><img <<$set_imgh>> src="images/locations/city/citycenter/studio/studia_0.jpg"></center>'
+	''
+	d1 = day
+	w1 = week
+	m1 = month
+	counter = 1
+	$pornofday = ''
+	:pornopt
+		killvar 'nh1'
+		pcount[counter] = counter
+		w1 += 1
+		if w1 > 7:w1 = 1
+		d1 += 1
+		if monthsend[m1] < d1: m1 += 1 & d1 = 1
+		if m1 > 12: m1 = 1
+
+		if m1 = 1 and d1 >= 1 and d1 <= 8:nh1 = 1
+		if m1 = 2 and d1 = 23:nh1 = 1
+		if m1 = 3 and d1 = 8:nh1 = 1
+		if m1 = 5 and (d1 = 1 or d1 = 2):nh1 = 1
+		if m1 = 5 and d1 = 9:nh1 = 1
+		if m1 = 6 and (d1 = 12 or d1 = 13):nh1 = 1
+		if m1 = 11 and d1 = 4:nh1 = 1
+
+		if (weekdayfilter = 1 and w1 >= 6) or (weekendfilter = 1 and w1 < 6) or (weekdayfilter = 0 and weekendfilter = 0) or nh1 = 1:
+			if datefilter['<<selected[counter]>>'] = 0:
+				if nh1 = 1:
+					$pornofday = '<i><b>National holiday, no shootings!</b></i><br>'
+				else
+					porns = 0
+					:pornplanloop
+						dynamic $pornfilmdesc, pornplan['<<counter>>,<<porns>>']
+						if (pfactor <= ppnum) or (ppnum = 0):
+							$line_color = 'None'
+							$line_cmnt = ''
+							$line_button = 'Take'
+							line_button_val = 2
+							if porntaken['<<counter>>,<<porns>>'] = 2:
+								$line_color = 'green'
+								$line_cmnt =  '- <i>Already contracted by you</i>'
+								$line_button = 'Drop'
+								line_button_val = 0
+							elseif porntaken['<<counter>>,<<porns>>'] = 1:
+								$line_color = 'red'
+								$line_cmnt =  '- <i>Taken by someone else</i>'
+							end
+							if $line_color = 'None':
+								$line_deco_f = ''
+								$line_deco_b = ''
+							else
+								$line_deco_f = '<font color="<<$line_color>>">'
+								$line_deco_b = '</font>'
+							end
+							! Alter Button - Goes to picklist of Film Types
+							$pornofday += '<a href="exec:gs ''mod_pornster_edit'', ''<<counter>>'', ''<<porns>>''">[Alter]</a> '
+							! Ownership Button - Take or Drop
+							$pornofday += '<a href="exec:pornstack += 1 & porntaken[''<<counter>>,<<porns>>''] = <<line_button_val>> & pfilmday[<<pcount[counter]>>] = pornplan[''<<counter>>,<<porns>>''] & gs ''stat'' & dynamic $contracting">[<<$line_button>>] </a>'
+							! Description of this Film
+							$pornofday += '<<$line_deco_f>><<$porndesc[pornplan[''<<counter>>,<<porns>>'']]>><<$line_cmnt>><<$line_deco_b>>'
+							$pornofday += '<br>'
+						end
+					if porns < 3: porns += 1 & jump 'pornplanloop'
+				end
+				if $pornofday ! '':
+					'<<$monthName[m1]>> <<d1>>, <<$week[w1]>>:'
+					'<<$pornofday>>'
+					$pornofday = ''
+				end
+			end
+		end
+	if counter < 49:
+		counter += 1
+		jump 'pornopt'
+	end
+}
+dynamic $contracting
+
+--- mod_pornster_hack ---------------------------------

+ 11 - 0
locations/mod_pornster_readme.qsrc

@@ -0,0 +1,11 @@
+#mod_pornster_readme
+
+!input your mod detail readme here. you can use msg or detailed structure, just make sure to create act 'return': gt 'mod_setting'
+*clr & cla
+act 'Return': gt 'mod_setting'
+
+'<center>Mod <b><font color=red> Pornster </font></b></center>'
+'Mod is to allow fine-grained tweaking of the porn schedule.  This is mostly just for me to get my feet wet making a mod.
+'
+
+--- mod_ipornster_readme ---------------------------------

+ 32 - 0
locations/mod_pornster_setup.qsrc

@@ -0,0 +1,32 @@
+#mod_pornster_setup
+
+!input basic mod information here.
+!this location must be the first one
+
+$mod_info[0] = 'pornster'
+!0=mod name, saved to $mod_name[i] array
+
+$mod_info[1] = '00101'
+!1=mod version
+!this will be displayed as version 1.2, fix3.
+!if you input 40500, will be showen as version 4.5
+
+$mod_info[2] = 'KeyMasterOfGozer'
+!2=mod author
+
+$mod_info[3] = 'Micro-manage porn schedule.'
+!3=mod description, input a short brief here.
+
+
+if curloc = 'mod_<<$mod_info[0]>>_setup':
+	usehtml = 1
+	!this will be only showed if you exec mod file. This location should be only called by gs, so this line will not trigger on normal mod playing
+	'<center><h1><font color=red>ACCESS DENIED</font></h1></center>'
+	'<<$mod_info[0]>> is a mod for <b>Girl Life - English Community Version</b>, and can not be played individually.'
+	''
+	'You can find the main game in <a href="https://www.tfgamessite.com/index.php?module=viewgame&id=597">Tfgames site</a> (external link)'
+	''
+	'Our forum: <a href="https://tfgamessite.com/phpbb3/viewforum.php?f=70">Tfgames site</a> (external link)'
+end
+
+--- mod_pornster_setup ---------------------------------

+ 10 - 0
pornster.qproj

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<QGen-project version="4.0.0 beta 1">
+	<Structure>
+	<Location name="mod_pornster_setup"/>
+	<Location name="mod_pornster_readme"/>
+	<Location name="mod_pornster"/>
+	<Location name="mod_pornster_hack"/>
+	<Location name="mod_pornster_edit"/>
+	</Structure>
+</QGen-project>







+ 44 - 0
txtmerge.py

@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# usage: txtmerge.py <input_dir> <output_file_name>
+# does the exact opposite of txtsplit.py
+
+import os
+import sys
+import re
+import io
+import xml.etree.ElementTree as ET
+
+assert len(sys.argv) == 3, "usage:\ntxtmerge.py <input_dir> <output_file_name>"
+idir = str(sys.argv[1])
+oname = str(sys.argv[2])
+
+# read the project xml file first
+# let's do this later in order to implement directory structure
+tree = ET.parse('pornster.qproj')
+root = tree.getroot()
+
+
+ofile = io.open(oname, 'w', encoding='utf-16', newline='\r\n')
+
+for location in root.iter('Location'):
+    iname = location.attrib['name']
+    iname = iname.replace("$","_")
+
+    try:
+        ifile = io.open(os.path.join(idir,iname + '.qsrc'), 'rt', encoding='utf-8')
+        text = ifile.read()
+
+        # make sure there's a line at the end of file
+        # (why wouldn't there be one? WINDOWS!
+        if text[-1] != u'\n':
+            text += u'\n\n'
+
+        ofile.write(text)
+        ifile.close()
+    except IOError:
+        print("WARNING: missing location %s" % iname)
+        pass
+
+ofile.close()
+
+

+ 46 - 0
txtsplit.py

@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# usage: txtsplit.py <input_file_name> <output_dir>
+# splits a txt2gam file into individual location files
+# encoded in utf-8, for git to better handle
+
+import os
+import sys
+import re
+import io
+import xml.etree.ElementTree as ET
+
+assert len(sys.argv) == 3, "usage:\ntxtsplit.py <input_file_name> <output_dir>"
+iname = str(sys.argv[1])
+odir = str(sys.argv[2])
+
+# read the project xml file first
+# let's do this later in order to implement directory structure
+"""
+tree = ET.parse('glife.qproj')
+root = tree.getroot()
+"""
+
+ifile = io.open(iname, 'rt', encoding='utf-16')
+
+counter = 1
+
+oname = None
+firstline = ifile.readline().replace(u'\ufeff','')
+match = re.search(ur'^#\s(\$?[_.\w]+)$', firstline)
+if match:
+    oname = os.path.join(odir, match.group(1).replace("$","_") + '.qsrc' )
+    counter += 1
+assert oname, "file is in the wrong format, must start with a location name"
+
+ofile = io.open(oname, 'w', encoding='utf-8')
+ofile.write(firstline)
+
+for line in ifile:
+    match = re.search(ur'^#\s(\$?[_.\w]+)$', line)
+    if match:
+        ofile.close()
+        oname = os.path.join(odir, match.group(1).replace("$","_") + '.qsrc' )
+        counter += 1
+        ofile = io.open(oname, 'w', encoding='utf-8')
+    ofile.write(line)
+        

+ 46 - 0
txtsplit3.py

@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# usage: txtsplit.py <input_file_name> <output_dir>
+# splits a txt2gam file into individual location files
+# encoded in utf-8, for git to better handle
+
+import os
+import sys
+import re
+import io
+import xml.etree.ElementTree as ET
+
+assert len(sys.argv) == 3, "usage:\ntxtsplit.py <input_file_name> <output_dir>"
+iname = str(sys.argv[1])
+odir = str(sys.argv[2])
+
+# read the project xml file first
+# let's do this later in order to implement directory structure
+"""
+tree = ET.parse('glife.qproj')
+root = tree.getroot()
+"""
+
+ifile = io.open(iname, 'rt', encoding='utf-16')
+
+counter = 1
+
+oname = None
+firstline = ifile.readline().replace(u'\ufeff','')
+match = re.search('^#\s(\$?[_.\w]+)$', firstline)
+if match:
+    oname = os.path.join(odir, match.group(1).replace("$","_") + '.qsrc' )
+    counter += 1
+assert oname, "file is in the wrong format, must start with a location name"
+
+ofile = io.open(oname, 'w', encoding='utf-8')
+ofile.write(firstline)
+
+for line in ifile:
+    match = re.search('^#\s(\$?[_.\w]+)$', line)
+    if match:
+        ofile.close()
+        oname = os.path.join(odir, match.group(1).replace("$","_") + '.qsrc' )
+        counter += 1
+        ofile = io.open(oname, 'w', encoding='utf-8')
+    ofile.write(line)
+