summaryrefslogtreecommitdiff
path: root/devtools/goldsrc_port_scripts
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/goldsrc_port_scripts')
-rw-r--r--devtools/goldsrc_port_scripts/PySourceSafe.py259
-rw-r--r--devtools/goldsrc_port_scripts/VisitFiles.py83
-rw-r--r--devtools/goldsrc_port_scripts/WildcardSearch.py61
-rw-r--r--devtools/goldsrc_port_scripts/find_game_sounds.py59
-rw-r--r--devtools/goldsrc_port_scripts/goldsrc_qc_9way_blends.py87
-rw-r--r--devtools/goldsrc_port_scripts/port_models.py226
-rw-r--r--devtools/goldsrc_port_scripts/qc_origin_fix.py35
7 files changed, 810 insertions, 0 deletions
diff --git a/devtools/goldsrc_port_scripts/PySourceSafe.py b/devtools/goldsrc_port_scripts/PySourceSafe.py
new file mode 100644
index 0000000..372e3b6
--- /dev/null
+++ b/devtools/goldsrc_port_scripts/PySourceSafe.py
@@ -0,0 +1,259 @@
+
+
+import os, sys
+
+
+class PySSException:
+ pass
+
+
+g_bVerbose = 0
+
+
+class PySourceSafe:
+ """
+ PySourceSafe represents a connection to a SS database.
+ Pass the directory to the database in the constructor, or \\hl2vss\hl2vss is assumed.
+ """
+
+ def __init__( self, dbPath=r"\\hl2vss\hl2vss" ):
+ # Store this off so we can put it in the environment when we run ss.
+ self.m_DBPath = dbPath
+ self.bUseExceptions = 0
+
+ self.m_VSSCommand = None
+
+ # Since we can't use spawnvpe to have it find ss.exe for us, we have to look in the path for it manually.
+ paths = os.environ['path'].split( ';' )
+ for path in paths:
+ if path[-1:] == '/' or path[-1:] == '\\':
+ testFilename = path + 'ss.exe'
+ else:
+ testFilename = path + '\\' + 'ss.exe'
+
+ testFilename = testFilename.replace( '\"', '' )
+ if os.access( testFilename, os.F_OK ):
+ self.m_VSSCommand = testFilename
+ break
+
+ # If we can't find the vss command, they we're screwed so throw an exception.
+ if not self.m_VSSCommand:
+ raise PySSException
+
+ for_cmd = 'for %I in ("' + self.m_VSSCommand + '") do echo %~sI'
+ p = os.popen(for_cmd)
+ self.m_VSSCommand = p.readlines()[-1] # last line from for command
+ p.close()
+
+ self.m_VSSCommand = self.m_VSSCommand.replace( '\n', '' )
+ self.lastExitStatus = 0
+ self.m_LastCommandOutput = ''
+
+
+ # Throw an exception on error? Default is no.
+ def EnableExceptions( self, bEnable ):
+ self.bUseExceptions = 1
+
+
+ # Get the output from the last command.
+ # This returns a list of the lines of text with the output.
+ def GetLastCommandOutput():
+ return self.m_LastCommandOutput
+
+
+ # Return a list of the filenames in a directory.
+ def ListFiles( self, rootDir ):
+ outlines = self.__RunCommand( ['dir',rootDir] )
+ if not outlines:
+ return None
+
+ returnList = []
+ for i in range( 1, len( outlines ) ):
+ if len( outlines[i] ) == 0:
+ break
+ elif outlines[i][0:1] != '$':
+ returnList.append( outlines[i] )
+
+ return returnList
+
+
+ def ListDirectories( self, rootDir ):
+ outlines = self.__RunCommand( ['dir',rootDir] )
+ if not outlines:
+ return None
+
+ returnList = []
+ for i in range( 1, len( outlines ) ):
+ if len( outlines[i] ) == 0:
+ break
+ elif outlines[i][0:1] == '$':
+ returnList.append( outlines[i] )
+
+ return returnList
+
+
+ # Example: p.AddFile( "$/hl2/release/dev/hl2/scripts", "c:\\test.txt", "some comment here" )
+ def AddFile( self, vssDir, localFilename, comment=None ):
+ if not self.__RunCommand( ['cp', vssDir] ):
+ return None
+
+ args = ['add', localFilename, '-I-']
+ if comment:
+ args.append( '-C%s' % comment )
+
+ return self.__RunCommand( args )
+
+
+ # Create a new directory (or 'subproject').
+ # Example: p.CreateDirectory( '$/tfc/models/test' )
+ # Note: this WILL create the subdirectories leading up to the final one if they don't exist.
+ def CreateDirectory( self, vssDir, comment=None ):
+ if not comment:
+ comment = ''
+
+ lastRet = []
+
+ # Create all directories leading up to this one/
+ dirs = vssDir.split( '/' )
+ curDir = ''
+ for dir in dirs:
+ if len( curDir ) == 0:
+ curDir = dir
+ else:
+ curDir = curDir + '/' + dir
+
+ # Does it exist already?
+ self.__RunCommand( ['properties', curDir], 0 )
+ if self.lastExitStatus != 0 and self.lastExitStatus != None:
+ lastRet = self.__RunCommand( ['create', curDir, '-C%s' % comment, '-I-'] )
+ if lastRet == None:
+ break
+
+ return lastRet
+
+
+ # Remove a file. Returns:
+ # 0 if the file doesn't exist
+ # 1 if it existed and was removed
+ # None if the file existed but there was an error.
+ def DeleteFile( self, vssFilename ):
+ self.__RunCommand( ['properties', vssFilename], 0 )
+ if self.lastExitStatus:
+ return 0
+ else:
+ # Now try to remove it.
+ self.__RunCommand( ['delete', vssFilename, '-I-'] )
+ if self.lastExitStatus:
+ return None
+ else:
+ return 1
+
+
+ # Get the checkout status of a file (and find out if the file even exists).
+ def GetFileStatus( self, ssFilename ):
+ pass
+
+
+ # Get the local working directory for the specified SS directory.
+ def GetWorkingDirectory( self, ssDir ):
+ pass
+
+
+ # Get the local filename for the specified file.
+ def GetLocalFilename( self, ssFilename ):
+ pass
+
+
+ # For all the CheckOut and Get commands, if localFilename is not None, then it'll
+ # treat that as the local file. Otherwise, it'll use the default working directory for that directory in SS.
+ # Returns 1 if successful, 0 if there was an error.
+
+ # Check out a file.
+ # Use GetLastOutput() to get the output string.
+ def CheckOutFile( self, ssFilename, localFilename=None ):
+ pass
+
+
+ # Check out the whole directory.
+ def CheckOutDir( self, ssDirName, localDirName=None, bRecursive=0 ):
+ pass
+
+
+ # Get a file.
+ def GetFile( self, ssFilename, localFilename=None ):
+ pass
+
+
+ # Get a whole directory.
+ def GetFile( self, ssFilename, localFilename=None ):
+ pass
+
+ # Check in a file. Optionally specify a comment.
+ # Returns 1 if successful, 0 otherwise.
+ def CheckInFile( self, ssFilename, localFilename=None, comment=None ):
+ pass
+
+
+ # The big master function to run a vss command and get the results back in a list.
+ def __RunCommand( self, args, bHandleErrors=1 ):
+ # First, set the environment up.
+ tempEnviron = os.environ
+ os.environ['ssdir'] = self.m_DBPath
+
+ # Now build the command.
+ cmd = self.m_VSSCommand
+ for i in args:
+ cmd = cmd + ' \"%s\"' % i
+
+ if g_bVerbose:
+ print "VSS: " + cmd
+
+ # Run the command and capture its output.
+ f = os.popen( cmd, 'r' )
+ lines = f.readlines()
+ self.lastExitStatus = f.close()
+
+ self.m_LastCommandOutput = lines
+ lines = [i.strip() for i in lines]
+
+ # Restore the environment.
+ os.environ = tempEnviron
+
+ if self.lastExitStatus != 0 and self.lastExitStatus != None:
+ if bHandleErrors:
+ print 'VSS Error (status: %d): ' % self.lastExitStatus
+ print 'cmd: %s' % cmd
+ print 'output: '
+ for i in lines:
+ print '\t%s' % i
+
+ if self.bUseExceptions:
+ raise PySSException
+ else:
+ return None
+
+ return lines
+
+
+
+p = PySourceSafe()
+
+
+# TEST CODE
+"""
+print "Files"
+files = p.ListFiles( '$/hl2/release/dev' )
+for i in files:
+ print i
+
+print "\n\nDirectories"
+files = p.ListDirectories( '$/hl2/release/dev' )
+for i in files:
+ print i
+
+
+vssRoot = '$/tfc/models'
+p.AddFile( "$/tfc", "c:\\test.txt", "test comment blah blah" )
+
+p.CreateDirectory( '$/tfc/aa/bb/cc/dd', 'here is hte comment' )
+"""
diff --git a/devtools/goldsrc_port_scripts/VisitFiles.py b/devtools/goldsrc_port_scripts/VisitFiles.py
new file mode 100644
index 0000000..50105c1
--- /dev/null
+++ b/devtools/goldsrc_port_scripts/VisitFiles.py
@@ -0,0 +1,83 @@
+
+import os
+import re
+
+
+STOP_RECURSING = 23452
+
+
+class VisitFiles:
+ """
+ This is a helper class to visit a bunch of files in a tree given
+ the regular expressions that will match the filenames you're interested in.
+
+ Create this class like a function, and pass in these parameters:
+ baseDir - a string for the root directory in the search. This must not end in a slash.
+
+ fileMasks - a list of strings that contain regular expressions for filenames you want to match.
+
+ fileCB - This function is called for each filename matched. It takes these parameters:
+ - short filename ("blah.txt")
+ - relative filename ("a/b/c/blah.txt")
+ - long filename ("c:/basedir/a/b/c/blah.txt")
+
+ dirCB - a function called for each directory name. This can be None. If it returns STOP_RECURSING,
+ then it won't do callbacks for the files and directories under this one.
+ - relative dirName ("./a/b")
+ - full dirname (from the baseDir you pass into __init__) ("d:/a/b")
+
+ bRecurse - a boolean telling whether or not to recurse into other directories.
+
+ Example: This would visit all files recursively (.+ matches any non-zero-length string).
+ VisitFiles( ".", [".+"], MyCallback )
+ """
+
+ def __init__( self, baseDir, fileMasks, fileCB, dirCB, bRecurse=1 ):
+ # Handle it appropriately whether they pass 1 filemask or a list of them.
+ if isinstance( fileMasks, list ):
+ self.fileMasks = [re.compile( x, re.IGNORECASE ) for x in fileMasks]
+ else:
+ self.fileMasks = [re.compile( fileMasks, re.IGNORECASE )]
+
+ self.fileCB = fileCB
+ self.dirCB = dirCB
+ self.bRecurse = bRecurse
+
+ self.__VisitFiles_R( ".", baseDir )
+
+
+ def __RemoveDotSlash( self, name ):
+ if name[0:2] == '.\\':
+ return name[2:]
+ else:
+ return name
+
+
+ def __VisitFiles_R( self, relativeDirName, baseDirName ):
+ if self.dirCB:
+ if self.dirCB( relativeDirName, baseDirName ) == 0:
+ return
+
+ files = os.listdir( baseDirName )
+
+ for filename in files:
+ upperFilename = filename.upper()
+ fullFilename = self.__RemoveDotSlash( baseDirName + "\\" + filename )
+ relativeName = self.__RemoveDotSlash( relativeDirName + "\\" + filename )
+
+ stats = os.stat( fullFilename )
+ if stats[0] & (1<<15):
+
+ matched = 0
+ for curRE in self.fileMasks:
+ if curRE.search( upperFilename ):
+ matched = 1
+ break
+
+ if matched:
+ self.fileCB( filename, relativeName, fullFilename )
+ else:
+
+ # It's a directory.
+ if self.bRecurse:
+ self.__VisitFiles_R( relativeName, fullFilename )
diff --git a/devtools/goldsrc_port_scripts/WildcardSearch.py b/devtools/goldsrc_port_scripts/WildcardSearch.py
new file mode 100644
index 0000000..954a092
--- /dev/null
+++ b/devtools/goldsrc_port_scripts/WildcardSearch.py
@@ -0,0 +1,61 @@
+
+from __future__ import generators
+import os
+import re
+import stat
+
+
+# This takes a DOS filename wildcard like *abc.t?t and returns a regex string that will match it.
+def GetRegExForDOSWildcard( wildcard ):
+ # First find the base directory name.
+ iLast = wildcard.rfind( "/" )
+ if iLast == -1:
+ iLast = wildcard.rfind( "\\" )
+
+ if iLast == -1:
+ dirName = "."
+ dosStyleWildcard = wildcard
+ else:
+ dirName = wildcard[0:iLast]
+ dosStyleWildcard = wildcard[iLast+1:]
+
+ # Now generate a regular expression for the search.
+ # DOS -> RE
+ # * -> .*
+ # . -> \.
+ # ? -> .
+ reString = dosStyleWildcard.replace( ".", r"\." ).replace( "*", ".*" ).replace( "?", "." ) + r'\Z'
+ return reString
+
+
+#
+# Useful function to return a list of files in a directory based on a dos-style wildcard like "*.txt"
+#
+# for name in WildcardSearch( "d:/hl2/src4/dt*.cpp", 1 ):
+# print name
+#
+def WildcardSearch( wildcard, bRecurse=0 ):
+ reString = GetRegExForDOSWildcard( wildcard )
+ matcher = re.compile( reString, re.IGNORECASE )
+
+ return __GetFiles_R( matcher, dirName, bRecurse )
+
+def __GetFiles_R( matcher, dirName, bRecurse ):
+ fileList = []
+ # For each file, see if we can find the regular expression.
+ files = os.listdir( dirName )
+ for baseName in files:
+ filename = dirName + "/" + baseName
+
+ mode = os.stat( filename )[stat.ST_MODE]
+ if stat.S_ISREG( mode ):
+ # Make sure the file matches the search string.
+ if matcher.match( baseName ):
+ fileList.append( filename )
+
+ elif bRecurse and stat.S_ISDIR( mode ):
+ fileList += __GetFiles_R( matcher, filename, bRecurse )
+
+ return fileList
+
+
diff --git a/devtools/goldsrc_port_scripts/find_game_sounds.py b/devtools/goldsrc_port_scripts/find_game_sounds.py
new file mode 100644
index 0000000..7b0a955
--- /dev/null
+++ b/devtools/goldsrc_port_scripts/find_game_sounds.py
@@ -0,0 +1,59 @@
+
+import os
+import sys
+import stat
+
+
+g_NumSoundsAdded = 0
+
+
+def ScanSounds_R( baseDir, relativeDir, outFile ):
+ files = os.listdir( baseDir )
+ for filename in files:
+ fullFilename = baseDir + "\\" + filename
+ if len( relativeDir ) > 0:
+ newRelativeDir = relativeDir + "/" + filename
+ else:
+ newRelativeDir = filename
+
+ mode = os.stat( fullFilename )[stat.ST_MODE]
+
+ if stat.S_ISREG( mode ):
+ if filename[-4:].upper() == ".WAV":
+ outFile.write( "\"%s\"\n" % ( newRelativeDir[0:-4] ) )
+ outFile.write( "{\n" )
+ outFile.write( "\t\"channel\"\t\t\"CHAN_ITEM\"\n" )
+ outFile.write( "\t\"volume\"\t\t\"VOL_NORM\"\n" )
+ outFile.write( "\t\"soundlevel\"\t\"SNDLVL_NONE\"\n" )
+ outFile.write( "\t\"pitch\"\t\t\t\"PITCH_NORM\"\n" )
+ outFile.write( "\t\"wave\"\t\t\t\"%s\"\n" % ( newRelativeDir ) )
+ outFile.write( "}\n\n" )
+
+ global g_NumSoundsAdded
+ g_NumSoundsAdded += 1
+
+ if stat.S_ISDIR( mode ):
+ ScanSounds_R( fullFilename, newRelativeDir, outFile )
+
+
+# Make sure we've got a valid base directory.
+if len( sys.argv ) < 2:
+ print "Error: Must specify the root sound directory."
+ sys.exit( 1 )
+
+baseDir = sys.argv[1]
+if os.access( baseDir, os.R_OK ) != 1:
+ print "Error: Can't access %s." % ( baseDir )
+ sys.exit( 1 )
+
+
+# Now scan all the .cpp files for sound function calls.
+outFile = open( "game_sounds.txt", "wt" )
+
+ScanSounds_R( baseDir, "", outFile )
+
+outFile.close()
+
+
+print "Added %d sounds to game_sounds.txt" % ( g_NumSoundsAdded )
+
diff --git a/devtools/goldsrc_port_scripts/goldsrc_qc_9way_blends.py b/devtools/goldsrc_port_scripts/goldsrc_qc_9way_blends.py
new file mode 100644
index 0000000..19869d9
--- /dev/null
+++ b/devtools/goldsrc_port_scripts/goldsrc_qc_9way_blends.py
@@ -0,0 +1,87 @@
+
+# This script converts old 9 way blends that look like this:
+#
+# $sequence crouch_tommy_shoot "crouch_tommy_shootupL" "crouch_tommy_shootup" "crouch_tommy_shootupR" "crouch_tommy_shootmidL" "crouch_tommy_shootmid" "crouch_tommy_shootmidR" "crouch_tommy_shootdownL" "crouch_tommy_shootdown" "crouch_tommy_shootdownL" blend XR 45 -45 fps 30 origin 0 0 17 { event 7002 1 "1" }
+#
+# to this:
+#
+# $sequence crouch_tommy_shoot {
+# "crouch_tommy_shootupL" "crouch_tommy_shootup" "crouch_tommy_shootupR"
+# "crouch_tommy_shootmidL" "crouch_tommy_shootmid" "crouch_tommy_shootmidR"
+# "crouch_tommy_shootdownL" "crouch_tommy_shootdown" "crouch_tommy_shootdownL"
+# blendwidth 3 blend body_yaw -45 45 blend body_pitch -65 65
+# fps 30 origin 0 0 17 { event 7002 1 "1" }
+# }
+
+import sys
+import re
+import string
+
+if len( sys.argv ) < 3:
+ print "Requires 2 parameters: <input file> <output file>"
+ sys.exit( 1 )
+
+
+# Read in the file.
+f = open( sys.argv[1], "rt" )
+lines = f.readlines()
+f.close()
+
+
+# Do the search/replace.
+def named( re, name ):
+ return r'(?P<' + name + r'>' + re + ')'
+
+def ident( animName ):
+ if animName:
+ return named( r'\"?\S+\"?', animName )
+ else:
+ return r'\"?\S+\"?'
+
+
+ws = r'\s*'
+
+# THIS IS THE FIRST PASS - ADDS THE 9-WAY BLENDS
+if 1:
+ nineNames = [ws+ident('a'+str(i)) for i in range(1,10)]
+ sequenceAndAnims = named( r'\s*', 'prefix' ) + r'\$sequence' + ws + ident('animName') + string.join( nineNames, '' )
+
+ # NOTE THERE IS A KNOWN BUG HERE - extra NEEDS TO INCLUDE THE 'blend xr -45 45' STUFF SO WE CAN STRIP IT OUT
+ extra = ws + ident('blend') + named( '.+', 'extra' )
+ myRE = re.compile( sequenceAndAnims + extra )
+
+ #lines = ['$sequence crouch_pistol_aim "crouch_pistol_aimupL" "crouch_pistol_aimup" "crouch_pistol_aimupR" "crouch_pistol_aimmidL" "crouch_pistol_aimmid" "crouch_pistol_aimmidR" "crouch_pistol_aimdownL" "crouch_pistol_aimdown" "crouch_pistol_aimdownR" loop blend XR 45 -45 fps 30 origin 0 0 17']
+
+ outLines = []
+ for curLine in lines:
+ m = myRE.match( curLine )
+ if m and (m.group('blend') == 'blend' or m.group('blend') == 'loop'):
+ prefix = m.group('prefix')
+ curLine = prefix + '$sequence %s {\n' % m.group('animName')
+ curLine += prefix + '\t%s %s %s\n' % ( m.group('a1'), m.group('a2'), m.group('a3') )
+ curLine += prefix + '\t%s %s %s\n' % ( m.group('a4'), m.group('a5'), m.group('a6') )
+ curLine += prefix + '\t%s %s %s\n' % ( m.group('a7'), m.group('a8'), m.group('a9') )
+ curLine += prefix + '\tblendwidth 3 blend body_yaw -45 45 blend body_pitch -65 65\n'
+ curLine += prefix + '\t' + m.group( 'extra' ) + '\n'
+ curLine += prefix + '}\n\n'
+
+ outLines.append( curLine )
+
+# THIS IS THE SECOND PASS - IT ADDS THE UPPER-BODY WEIGHTLISTS
+if 0:
+ lookFor = named( r'\s*', 'start' ) + named( r'blendwidth 3 blend body_yaw -45 45 blend body_pitch -65 65', 'mid' ) + named( r'\s*', 'end' )
+ myRE = re.compile( lookFor )
+
+ outLines = []
+ for curLine in lines:
+ m = myRE.match( curLine )
+ if m:
+ curLine = curLine + m.group('start') + 'weightlist upperbody\n'
+
+ outLines.append( curLine )
+
+
+# Save the output file.
+f = open( sys.argv[2], "wt" )
+f.writelines( outLines )
+f.close()
diff --git a/devtools/goldsrc_port_scripts/port_models.py b/devtools/goldsrc_port_scripts/port_models.py
new file mode 100644
index 0000000..bd5fcc1
--- /dev/null
+++ b/devtools/goldsrc_port_scripts/port_models.py
@@ -0,0 +1,226 @@
+
+import msvcrt, os, sys, re, WildcardSearch, VisitFiles
+
+
+def GetArg( argName, ifAtEnd='' ):
+ for i in range( 1, len( sys.argv ) ):
+ if argName.upper() == sys.argv[i].upper():
+ if i+1 >= len( sys.argv ):
+ return ifAtEnd
+ else:
+ return sys.argv[i+1]
+ return None
+
+
+def PrintUsage():
+ print r'port_models.py -srcdir <directory with the QCs> -vproject <game directory>'
+ print r'Note: -srcdir represents the ROOT of the models directory, but it points at the model sources'
+ print r' instead of the model content.'
+ print r'ex: port_models.py -srcdir c:\hl2\models\tfc -vproject c:\hl2\tfc'
+ sys.exit( 1 )
+
+
+def FixFilename( filename ):
+ filename = filename.replace( '/', '\\' )
+ if filename == '.\\':
+ return filename
+ else:
+ return re.compile( r'[^\.]?\.\\' ).sub( '', filename )
+
+
+def GetDirName( fullFilename ):
+ a = fullFilename.rfind( '/' )
+ b = fullFilename.rfind( '\\' )
+ if a == -1 and b == -1:
+ return FixFilename( fullFilename )
+ else:
+ return FixFilename( fullFilename[0 : max(a,b)] )
+
+
+g_bAlwaysContinue = 0
+def CheckContinue():
+ global g_bAlwaysContinue
+ if g_bAlwaysContinue:
+ return 1
+ else:
+ sys.stdout.write( 'Continue [(Y)es/(N)o/(A)lways]? ' )
+ r = str( msvcrt.getch() ).upper()
+ if r == 'Y':
+ return 1
+ elif r == 'A':
+ g_bAlwaysContinue = 1
+ return 1
+ else:
+ return 0
+
+
+#
+# Get command line args.
+#
+modelSourceDir = GetArg( '-srcdir' )
+vprojectDir = GetArg( '-vproject' )
+if not modelSourceDir or not vprojectDir:
+ PrintUsage()
+
+binDir = vprojectDir + '\\..\\bin'
+
+studiomdlFilename = binDir + '\\studiomdl.exe'
+vtexFilename = binDir + '\\vtex.exe'
+xwadFilename = binDir + '\\xwad.exe'
+if not os.access( studiomdlFilename, os.F_OK ) or not os.access( vtexFilename, os.F_OK ) or not os.access( xwadFilename, os.F_OK ):
+ print 'Can\'t find vtex.exe, xwad.exe, or studiomdl.exe in bin directory %s.' % (binDir)
+ sys.exit( 1 )
+
+
+
+#
+#
+# REGEX's and code to convert the QC file.
+#
+#
+reModelName = re.compile( r'(?P<start>\$modelname\s+)(?P<stripout>.*models(\\|\/))(?P<keep>.+)', re.IGNORECASE )
+reCDTexture = re.compile( r'(?P<start>\$(cdTexture|cdmaterials)\s+)(?P<stripout>.*models(\\|\/))(?P<keep>.+)', re.IGNORECASE )
+reCD = re.compile( r'\$cd\s', re.IGNORECASE )
+
+def ident( name=None ):
+ if name:
+ return r'(?P<' + name + r'>\s+((\".*?\")|([^\"]\S*)))'
+ else:
+ return r'\s+((\".*?\")|([^\"]\S*))'
+
+reAttachmentLine = re.compile( r'(?P<keep>.*\$attachment'+ident()+ident()+ident()+ident()+ident() + ')' + ident()+ident(), re.IGNORECASE )
+
+g_DirToMaterialsMap = {}
+
+def EditQCFile( fullFilename ):
+ f = open( fullFilename, 'rt' )
+ lines = f.readlines()
+ f.close()
+
+ bDidMaterials = 0
+
+ outLines = []
+ for line in lines:
+ if not reCD.search( line ):
+ # Strip out stuff before /models in the $modelname line.
+ m = reModelName.search( line )
+ if m:
+ line = m.group('start') + m.group('keep' ) + '\n'
+ else:
+ m = reCDTexture.search( line )
+ if m:
+ materialsDirName = 'models/' + m.group('keep')
+ bmpDir = m.group('keep')
+ g_DirToMaterialsMap[FixFilename(bmpDir.upper())] = materialsDirName
+ bDidMaterials = 1
+ line = '$cdmaterials %s\n' % materialsDirName
+
+ # Strip out the last 2 args on the $attachment line if they're using too many.
+ # Goldsrc studiomdl allowed 2 extra args but it ignored them.
+ m = reAttachmentLine.search( line )
+ if m:
+ line = m.group('keep')
+
+ outLines.append( line )
+
+ if not bDidMaterials:
+ print 'No $cdtexture line in %s so we don\'t know where to convert the BMPs into.' % (fullFilename)
+ sys.exit( 1 )
+
+ #
+ # Write the QC back out.
+ #
+ try:
+ f = open( fullFilename, 'wt' )
+ except IOError:
+ print 'Can\'t open %s for writing.\nMake sure it is not read-only.' % fullFilename
+ sys.exit( 1 )
+ f.writelines( outLines )
+ f.close()
+
+
+
+#
+# Convert all the QC files and backup the old ones.
+#
+print '--- Converting QC files and running studiomdl ---'
+
+def QCFileCallback( filename, relativeName, fullFilename ):
+ print '\t' + relativeName
+
+ # First, backup the QC.
+ os.system( 'copy \"%s\" \"%s\" > out' % (fullFilename, fullFilename + ".OLD") )
+
+ #
+ # Now, make some edits.
+ #
+ EditQCFile( fullFilename );
+
+ #
+ # Run studiomdl on it.
+ #
+ cmd = '%s -vproject \"%s\" \"%s\" > out' % (studiomdlFilename, vprojectDir, fullFilename)
+ if os.system( cmd ) != 0:
+ lines = open( 'out', 'rt' ).readlines()
+ sys.stdout.writelines( lines )
+ sys.exit( 1 )
+
+VisitFiles.VisitFiles( modelSourceDir, WildcardSearch.GetRegExForDOSWildcard( '*.qc' ), QCFileCallback, None )
+
+
+#
+# Now convert all the BMP files.
+#
+print '\n--- Converting BMP files and running vtex ---'
+def BMPFileCallback( filename, relativeName, fullFilename ):
+ print '\t' + relativeName
+
+ # Find out where we want to store this BMP's files under materials.
+ dirName = GetDirName( relativeName )
+
+ try:
+ cdMaterials = g_DirToMaterialsMap[dirName.upper()]
+ except:
+ #print '\tWarning: couldn\'t convert %s (don\'t know where to put it).' % (relativeName)
+ pass
+ else:
+ #
+ # Run XWAD.
+ #
+ cmd = '%s -quiet -bmpfile \"%s\" -basedir \"%s\"' % (xwadFilename, fullFilename, vprojectDir)
+ if os.system( cmd ) != 0:
+ print '\tError running xwad on %s' % fullFilename
+ if not CheckContinue():
+ print '\tcmd: %s' % cmd
+ sys.exit( 1 )
+
+ #
+ # Move the TGA it generated into the right directory.
+ #
+ materialsrcDir = '%s\\materialsrc\\%s' % (vprojectDir, cdMaterials)
+ if not os.access( materialsrcDir, os.F_OK ) and os.system( 'md \"%s\"' % materialsrcDir ) != 0:
+ print 'Can\'t create or access directory %s.' % materialsrcDir
+
+ materialsDir = '%s\\materials\\%s' % (vprojectDir, cdMaterials)
+ if not os.access( materialsDir, os.F_OK ) and os.system( 'md \"%s\"' % materialsDir ) != 0:
+ print 'Can\'t create or access directory %s.' % materialsDir
+
+ baseTGAFilename = filename[0:-4] + '.tga'
+ generatedFilename = vprojectDir + '\\materialsrc\\' + baseTGAFilename
+ cmd = 'move \"%s\" \"%s\"' % (generatedFilename, materialsrcDir)
+ if os.system( cmd ) != 0:
+ print 'system( %s ) failed.' % cmd
+
+ #
+ # Lastly, run vtex on it.
+ #
+ cmd = '%s -shader VertexLitGeneric -quiet \"%s\\%s\"' % (vtexFilename, materialsrcDir, baseTGAFilename)
+ if os.system( cmd ) != 0:
+ sys.exit( 1 )
+
+
+VisitFiles.VisitFiles( modelSourceDir, WildcardSearch.GetRegExForDOSWildcard( '*.bmp' ), BMPFileCallback, None )
+
+
+
+
diff --git a/devtools/goldsrc_port_scripts/qc_origin_fix.py b/devtools/goldsrc_port_scripts/qc_origin_fix.py
new file mode 100644
index 0000000..63541db
--- /dev/null
+++ b/devtools/goldsrc_port_scripts/qc_origin_fix.py
@@ -0,0 +1,35 @@
+
+#
+# This script is used to take a goldsrc-style character model with the origin at
+# its centerpoint and put its origin at its feet.
+#
+# It parses a .qc file, takes all $origin commands, and subtracts that amount
+# from all 'origin' blocks inside a sequence command.
+#
+# Redirect its output to a file (or the input file).
+#
+
+import sys
+import re
+
+f = open( sys.argv[1], "rt" )
+lines = f.readlines()
+f.close()
+
+re1 = re.compile( r'\$origin (?P<x>-?\d+)\s+(?P<y>-?\d+)\s+(?P<z>-?\d+)' )
+re2 = re.compile( r'(?P<ws>\s)origin (?P<x>-?\d+)\s+(?P<y>-?\d+)\s+(?P<z>-?\d+)' )
+
+curOffset = [0,0,0]
+
+def OriginOffset( m ):
+ testOffset = [ int(m.group('x')), int(m.group('y')), int(m.group('z')) ]
+ return '%sorigin %d %d %d' % (m.group('ws'), testOffset[0]-curOffset[0], testOffset[1]-curOffset[1], testOffset[2]-curOffset[2])
+
+for line in lines:
+ m = re1.search( line )
+ if m:
+ #print 'match: %s %s %s' % ( m.group('x'), m.group('y'), m.group('z') )
+ curOffset = [ int(m.group('x')), int(m.group('y')), int(m.group('z')) ]
+
+ sys.stdout.write( re2.sub( OriginOffset, line ) )
+