struct RsFindScriptStruct ( private -- Hashtable which stores all menu item data. -- -- key: Menu item button text. -- value: Array - 1: maxscript filename, 2: the menu path scriptsHash = undefined, -- Hashtable which stores data parsed out of the macroScripts. -- -- key: maxscript filename, including the extension. -- value: The menu button text. macroScriptsHash = undefined, -- Parse the WW macroscript to find out what the button text is for each script file. In some cases, there is no script -- being imported. Dealing with that happens in a different function. fn processWWMacroScript mcr = ( local fstream = openFile mcr if fstream != undefined do ( while not ( eof fstream ) do ( currentLine = readLine fstream -- Find a macroscript block. if ( matchPattern currentLine pattern:"macroscript *" ) then ( local buttonText = undefined local scriptFilename = undefined -- Find the button text local foundButtonText = false while ( not foundButtonText ) and ( not eof fstream ) do ( nextButtonTextLine = readLine fstream if ( matchPattern ( toLower nextButtonTextLine ) pattern:"*buttontext:*" ) then ( -- First split by the colon local rawButtonText = ( filterString nextButtonTextLine ":" )[ 2 ] -- Pop quotations buttonText = trimLeft rawButtonText "\"" buttonText = trimRight buttonText "\"" -- Find the maxscript file local foundMaxscriptFile = false while ( not foundMaxscriptFile ) and ( not eof fstream ) do ( nextOpenBracketLine = readLine fstream if ( matchPattern ( toLower nextOpenBracketLine ) pattern:"*(*" ) then ( nextCodeLine = readLine fstream -- In almost all cases, the very next line of the macroscript is calling filein on a script file. if ( matchPattern ( toLower nextCodeLine ) pattern:"*filein (*" ) then ( local rawMSFilename = filterString nextCodeLine "/" rawMSFilename = rawMSFilename[ rawMSFilename.count ] local bracketIndex = findString rawMSFilename ")" -- Gotta get rid of the trailing quote and bracket. scriptFilename = toLower ( subString rawMSFilename 1 ( bracketIndex - 2 ) ) ) foundMaxscriptFile = true ) ) foundButtonText = true ) ) -- Add results of the current macroscript block to the hashtable. if not macroScriptsHash.Contains buttonText then ( macroScriptsHash.Add buttonText scriptFilename ) else ( --print ( "RsFindScript: Item (" + buttonText + ") has already been added to the hashtable!" ) ) ) ) close fstream ) ), -- Constructs a friendly path which reflects where the script lives in the WW menu system. fn getWWMenuPath fileName = ( local pathToRemove = RsConfigGetWildwestDir() + "\\script\\max\\" local rawMenuPath = substring fileName pathToRemove.count fileName.count -- Rebuild the menu path to reflect how it looks in 3dsmax local categories = filterString rawMenuPath "\\" local msFilename = categories[ categories.count ] -- Pop maxscript filename from the array, if the last item in the array is a maxscript. It could be a directory, in which case we want to keep. if ( matchPattern msFilename pattern:"*.ms" ignoreCase:true ) then ( deleteItem categories categories.count ) local fixedMenuPath = stringStream "" for category in categories do ( -- Lower name and replace any underscores with a space. local tempCategory = toLower ( substituteString category "_" " " ) -- Capitalize words. local splitWords = filterString tempCategory " " local properCategory = stringStream "" for word in splitWords do ( word[ 1 ] = toUpper word[ 1 ] format "% " word to:properCategory ) -- Get rid of trailing white space. properCategory = trimRight properCategory " " format "%/" properCategory to:fixedMenuPath ) fixedMenuPath = "RS Wildwest/" + ( trimRight fixedMenuPath "/" ) ( fixedMenuPath as string ) ), fn parseWWConfigXml configXml = ( -- I'm just going to do raw file parsing atm. Will switch to a proper XML parser later. fn getXmlAttributeValue xmlLine attribName = ( local attributeValue = undefined local startQuoteIndex = findString xmlLine ( attribName + "='" ) if startQuoteIndex != undefined do ( local rawAttributeText = subString xmlLine ( startQuoteIndex + ( attribName + "='" ).count ) xmlLine.count local endQuoteIndex = findString rawAttributeText "'" local attributeValue = subString rawAttributeText 1 ( endQuoteIndex - 1 ) if attributeValue == "" then attributeValue = undefined ) attributeValue ) local excludedDirectories = #() local excludedScripts = #() local macroscriptAttributes = #() local fstream = openFile configXml if fstream != undefined do ( while not ( eof fstream ) do ( local currentLine = readLine fstream -- Get excluded items. /* if ( matchPattern currentLine pattern:"**" ignoreCase:true ) do ( local foundEndExcludeItemsTag = false while not foundEndExcludeItemsTag do ( local currentItemLine = readLine fstream if ( matchPattern currentItemLine pattern:"**" ignoreCase:true ) do ( foundEndExcludeItemsTag = true ) if not foundEndExcludeItemsTag do ( local tmp = ( filterString currentItemLine ">" )[ 2 ] tmp = toLower ( filterString tmp "<" )[ 1 ] local extension = subString tmp ( tmp.count - 2 ) tmp.count -- We are excluding a directory. if extension == "*.*" then ( local excludedDirectory = subString tmp 1 ( tmp.count - 3 ) append excludedDirectories excludedDirectory -- We are excluding a maxscript. ) else if extension == ".ms" then ( print tmp ) ) ) */ if ( matchPattern currentLine pattern:"**" ignoreCase:true ) do ( local foundEndAttributesTag = false while not foundEndAttributesTag do ( local attributesLine = readLine fstream if ( matchPattern attributesLine pattern:"**" ignoreCase:true ) do ( foundEndAttributesTag = true ) -- Parse attribute data. if not foundEndAttributesTag do ( if ( matchPattern attributesLine pattern:"* 0 do ( local data = parseWWConfigXml wwConfigXml[ 1 ] excludedDirectories = data[ 1 ] excludedScripts = data[ 2 ] macroscriptAttributes = data[ 3 ] ) -- Iterate over each file in the directory and create a hashtable entry if possible. for file in files do ( local scriptFilename = toLower ( filenameFromPath file ) local menuPath = getWWMenuPath file local buttonText = undefined local maxScriptBody = undefined local isOverridden = false -- Get the button text. local enum = macroScriptsHash.GetEnumerator() while ( enum.MoveNext() ) do ( local macroButtonText = enum.Key local macroScriptFilename = enum.Value if macroScriptFilename != undefined do ( if ( matchPattern scriptFilename pattern:( "*" + macroScriptFilename + "*" ) ignoreCase:true ) do ( buttonText = macroButtonText ) ) ) -- See if the current file has any specific overrides. for macroscriptAttributeData in macroscriptAttributes while not isOverridden do ( local attributeScriptFilename = macroscriptAttributeData[ 1 ] local attributeButtonText = macroscriptAttributeData[ 2 ] local attributeMaxScriptBody = macroscriptAttributeData[ 3 ] if ( matchPattern file pattern:( "*" + attributeScriptFilename + "*" ) ignoreCase:true ) then ( buttonText = attributeButtonText maxScriptBody = attributeMaxScriptBody isOverridden = true ) ) if buttonText != undefined do ( if not ( scriptsHash.Contains buttonText ) then ( scriptsHash.Add buttonText #( toLower file, menuPath, maxScriptBody ) ) ) ) -- Attempt to include any macroscripts that did not have an associated script, but use maxscript code directly. Wooo boy. local enum = macroScriptsHash.GetEnumerator() while ( enum.MoveNext() ) do ( local buttonText = enum.Key local maxScriptFilename = enum.Value if maxScriptFilename == undefined do ( local found = false for macroscriptAttributeData in macroscriptAttributes while not found do ( local attributeScriptFilename = macroscriptAttributeData[ 1 ] local attributeButtonText = macroscriptAttributeData[ 2 ] local attributeMaxScriptBody = macroscriptAttributeData[ 3 ] if attributeMaxScriptBody != undefined do ( if attributeButtonText == buttonText do ( scriptsHash.Add buttonText #( ( toLower attributeScriptFilename ) , ( getWWMenuPath dir ), attributeMaxScriptBody ) found = true ) ) ) ) ) recurseBuildWWMenuData dir ) ), -- Entry point for constructing the necessary data for the search script system to work. fn build = ( print "Building data necessary for 'Find R* Script'..." -- Setup hashtables. scriptsHash = dotNetObject "System.Collections.Hashtable" macroScriptsHash = dotNetObject "System.Collections.Hashtable" -- Fill in hashtables with necessary data. processWWMacroScript ( RsConfigGetToolsDir() + "\\dcc\\current\\max2012\\ui\\macroscripts\\rswildwest.mcr" ) recurseBuildWWMenuData ( RsConfigGetWildwestDir() + "\\script\\max\\" ) ), public -- Deletes the hashtables, which will cause them to get rebuilt the next time a search is performed. fn flush = ( scriptsHash = undefined macroScriptsHash = undefined gc light:true true ), fn isBuilt = ( if scriptsHash != undefined and macroScriptsHash != undefined then ( return true ) else ( return false ) ), fn search searchStr exactPhrase:false rebuild:false = ( if ( scriptsHash == undefined ) or ( macroScriptsHash == undefined ) or ( rebuild == true ) do build() local results = #() local searches = #() -- Perform multiple searches from the single search string. if ( matchPattern searchStr pattern:"*;*" ) then ( searches = filterString searchStr ";" ) else ( append searches searchStr ) for search in searches do ( if not exactPhrase do ( -- Split up search string if there are spaces in it. if ( matchPattern search pattern:"* *" ) then ( local rawSearchStr = filterString search " " newSearchStr = stringStream "*" for i in rawSearchStr do ( format "%*" i to:newSearchStr ) search = "*" + newSearchStr as string ) else ( search = "*" + search + "*" ) ) local enum = scriptsHash.GetEnumerator() while ( enum.MoveNext() ) do ( if ( matchPattern ( enum.Key as string ) pattern:search ignoreCase:( not exactPhrase ) ) do ( -- What's in the array - 1: The menu item path, 2: Full path to the maxscript file on disk associated with the menu item, 3: MaxScript body append results #( ( scriptsHash.Item[ enum.Key ][ 2 ] + "/" + enum.Key) as string, scriptsHash.Item[ enum.Key ][ 1 ], scriptsHash.Item[ enum.Key ][ 3 ] ) ) ) ) results ) ) ::RsFindScript = RsFindScriptStruct()