# # File:: %RS_TOOLSLIB%/Util/anim/ingame/zip_animations.rb # Description:: # # Author:: Luke Openshaw # Date:: 09 October 2012 # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require 'RSG.Base.dll' require 'RSG.Base.Configuration.dll' require 'RSG.Base.Windows.dll' require 'RSG.SourceControl.Perforce' include RSG::Base::Configuration include RSG::Base::Logging include RSG::Base::Logging::Universal include RSG::Base::OS include RSG::Base::Windows include RSG::SourceControl::Perforce require 'mscorlib' require 'System.Core' require 'System.Xml' include System::Collections::Generic include System::IO include System::Diagnostics include System::Xml require 'pipeline/os/options' require 'pipeline/monkey/array' include Pipeline #----------------------------------------------------------------------------- # Constants #----------------------------------------------------------------------------- OPTIONS = [ LongOption::new( 'rebuild', LongOption::ArgType.None, 'f', 'Rebuild data; ignoring timestamp information.' ), LongOption::new( 'checkout', LongOption::ArgType.None, 'c', 'Checkout data from p4.' ), LongOption::new( 'branch', LongOption::ArgType.Required, 'b', 'Branch name.' ), ] def resolve_dictionary_name( dict_dir, sub_index ) dictionary_name = '' dict_dir = dict_dir.Replace("\\", "/") dict_dir_tokens = dict_dir.Split('/') for i in (sub_index + 1)..(dict_dir_tokens.length) if(i == sub_index + 1) dictionary_name = dict_dir_tokens[i-1] + "/" end dictionary_name = dictionary_name + dict_dir_tokens[i-1] end dictionary_name end def is_valid_tu_directory( p4, src_dir ) dir = src_dir.gsub( '@', '%40' ) args = [(Path.Combine( dir, '...' ) + '@2014/10/08,#head')] changeRecordSet = p4.Run( 'changes', System::Array[System::String].new(args) ) return changeRecordSet.count > 0 end def generate_master_icd_list( p4, master_list_filename, src_dir, dest_dir, log ) result = true System::IO::File.Delete(master_list_filename) if System::IO::File.Exists(master_list_filename) directory_list = Directory.GetDirectories(src_dir, "*.*", SearchOption.AllDirectories) valid_directory_list = [] directory_list.each do | dir | next if (Directory.GetFiles(dir, "*.clip")).length == 0 # Basic check that there is at least parity between clip/anim count. if (Directory.GetFiles(dir, "*.clip")).length == Directory.GetFiles(dir, "*.anim").length then if (is_valid_tu_directory( p4, dir ) == true) then valid_directory_list << dir end else log.Error("Mismatch of clip/anim count in #{dir}") result = false break end end dictionary_name_list = [] src_dir = src_dir.Replace("\\", "/") sub_index = src_dir.Split('/').length xml_writer_settings = XmlWriterSettings.new() xml_writer_settings.Indent = true xml_writer_settings.IndentChars = "\t" xml_writer = XmlWriter.Create(master_list_filename, xml_writer_settings) xml_writer.WriteStartDocument() xml_writer.WriteStartElement("Assets") valid_directory_list.each do | dir | dictionary_name = resolve_dictionary_name(dir, sub_index) #MPW - Produce the file for debugging but return false so we do not continue if(dictionary_name_list.include?(dictionary_name)) log.Error("Dictionary name already exists #{dictionary_name}, see log") result = false end dictionary_path = Path.Combine(dest_dir, dictionary_name + ".icd.zip") xml_writer.WriteStartElement("ZipArchive") xml_writer.WriteAttributeString("path", dictionary_path) xml_writer.WriteStartElement("Directory") xml_writer.WriteAttributeString("path", dir) xml_writer.WriteEndElement() xml_writer.WriteEndElement() dictionary_name_list << dictionary_name end xml_writer.WriteEndDocument() xml_writer.Close() return result end def checkout_files( p4, changelist, file_mapping, log) main_args = ['-c', changelist.Number.to_s] second_args = ['-t', '+l'] file_mapping.each do | zip_dictionary | if p4.Exists(zip_dictionary.DepotFilename) log.Message("Marking for edit #{zip_dictionary.DepotFilename}") args = main_args + [zip_dictionary.DepotFilename] args = System::Array[System::String].new(args) ret = p4.Run('edit', args) else log.Message("Marking for add #{zip_dictionary.DepotFilename}") args = main_args + ['-f', '-tbinary+Flw', zip_dictionary.LocalFilename] args = System::Array[System::String].new(args) ret = p4.Run('add', args) end # TODO: Handle errors better. Do we want to bail if there are any? ret.Messages.each do | msg | log.Message(msg) end log.Message("about to re-open #{zip_dictionary}") args = second_args + [zip_dictionary.DepotFilename] args = System::Array[System::String].new(args) ret = p4.Run('reopen', args) ret.Messages.each do | msg | log.Message(msg) end end end def run( config, branch, rebuild, checkout, log ) if config.Project.Branches.ContainsKey(branch) then src_dir = config.Project.Branches[branch].Art + "/anim/export_mb" dest_dir = config.Project.Branches[branch].Export + "/anim/ingame/" master_list_filename = config.Project.Branches[branch].Export + "/anim/ingame/master_icd_list.xml" else src_dir = config.DefaultBranch.Art + "/anim/export_mb" dest_dir = config.DefaultBranch.Export + "/anim/ingame/" master_list_filename = config.DefaultBranch.Export + "/anim/ingame/master_icd_list.xml" end p4 = config.Project.SCMConnect() begin log.Message("Syncing: #{dest_dir}") p4.Run( 'sync', true, Path.Combine( dest_dir, '...' ) ) log.Message("Sync complete") log.Message("Syncing: #{src_dir}") p4.Run( 'sync', Path.Combine( src_dir, '...' ) ) log.Message("Sync complete") if not Directory.Exists(dest_dir) then Directory.CreateDirectory(dest_dir) end zip_files = [] changelist = nil if checkout then log.Message("Checking out dictionaries...") zip_files = Directory.GetFiles( dest_dir, "*.icd.zip", SearchOption.AllDirectories ) zip_files_safe = [] zip_files.each do | zip_file | zip_files_safe << zip_file.gsub( '@', '%40' ) end if zip_files.count > 0 then file_mapping = FileMapping.Create(p4, System::Array[System::String].new(zip_files_safe)) if file_mapping.count > 0 then changelistname = 'In-game Animation Dictionaries [Asset Pipeline 3.0]' changelist = p4.CreatePendingChangelist(changelistname) checkout_files(p4, changelist, file_mapping, log) end end log.Message("Check out complete.") end if(generate_master_icd_list(p4, master_list_filename, src_dir, dest_dir, log)) then log.Message("Zipping clip dictionaries...") ir_exe = config.ToolsRoot + "\\ironlib\\lib\\RSG.Pipeline.Convert.exe" args = "" args = args + " --branch " + branch args = args + " --rebuild" if rebuild == true args = args + " #{master_list_filename}" ir_exe = config.Environment.Subst(ir_exe) args = config.Environment.Subst(args) log.Message("Asset pack command #{ir_exe} #{args}") startInfo = ProcessStartInfo::new() startInfo.Arguments = args startInfo.FileName = ir_exe startInfo.UseShellExecute = false proc = System::Diagnostics::Process.Start(startInfo) proc.WaitForExit() exit_code = proc.ExitCode log.Error("Errors zipping animations, see log") if (exit_code != 0) else log.Error("Errors generating master icd list") if (exit_code != 0) end if checkout then post_zip_files = Directory.GetFiles( dest_dir, "*.icd.zip", SearchOption.AllDirectories ) new_zip_files = Array::from_clr(post_zip_files).reject { |zip_file| zip_files.include?(zip_file) } zip_files_safe = [] new_zip_files.each do | zip_file | zip_files_safe << zip_file.gsub( '@', '%40' ) end if(zip_files_safe.count > 0) then file_mapping = FileMapping.Create(p4, System::Array[System::String].new(zip_files_safe)) if file_mapping.count > 0 then log.Message("Adding new zip files") log.Message("New files: #{new_zip_files.inspect}") if changelist == nil then changelistname = 'In-game Animation Dictionaries [Asset Pipeline 3.0]' changelist = p4.CreatePendingChangelist(changelistname) end checkout_files(p4, changelist, file_mapping, log) end end log.Message("Reverting unchanged dictionaries...") args = ['-a', Path.Combine( dest_dir, '...' )] p4.Run('revert', System::Array[System::String].new(args)) end ensure p4.Disconnect() end end #----------------------------------------------------------------------------- # Implementation #----------------------------------------------------------------------------- if ( __FILE__ == $0 ) then #------------------------------------------------------------------------- # Entry-Point #------------------------------------------------------------------------- LogFactory.Initialize() LogFactory.CreateApplicationConsoleLogTarget( ) g_Log = LogFactory.ApplicationLog begin g_Options = OS::Options::new( OPTIONS ) g_Config = RSG::Base::Configuration::ConfigFactory::CreateConfig( ) g_Rebuild = ( g_Options.is_enabled?( 'rebuild' ) ) g_Checkout = ( g_Options.is_enabled?( 'checkout' ) ) g_NoPopups = ( g_Options.is_enabled?( 'nopopups' ) ) g_BranchName = ( g_Options.get('branch') ) # Initialise log and console output. run( g_Config, g_BranchName, g_Rebuild, g_Checkout, g_Log ) LogFactory.ApplicationShutdown() LogFactory.ShowUniversalLogViewer(LogFactory.ApplicationLogFilename()) if ((not g_NoPopups) and g_Log.HasErrors()) rescue Exception => ex g_Log.Exception( ex, "Exception during #{__FILE__}: #{ex.message}") LogFactory.ApplicationShutdown() LogFactory.ShowUniversalLogViewer(LogFactory.ApplicationLogFilename()) if ((not g_NoPopups) and g_Log.HasErrors()) end end