# # 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 print_return( ret, log) ret.Messages.each do | msg | log.Message(msg) end ret.Errors.each do | msg | log.Error(msg) end end def generate_master_icd_list( 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 valid_directory_list << dir 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? print_return( ret, log) 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) print_return( ret, log) end end def run( config, rebuild, checkout, log, branch ) dlc_project_art_dirs = [] dlc_project_export_dirs = [] dlc_project_master_files = [] config.DLCProjects.each do | pair | # Avoid patch dlc if not pair.Value.Root.include? "patchPacks" then if pair.Value.Branches.ContainsKey(branch) then dlc_project_art_dirs << pair.Value.Branches[branch].Art + "/anim/export_mb" dlc_project_export_dirs << pair.Value.Branches[branch].Export + "/anim/ingame/" dlc_project_master_files << pair.Value.Branches[branch].Export + "/anim/ingame/master_icd_list.xml" else dlc_project_art_dirs << pair.Value.DefaultBranch.Art + "/anim/export_mb" dlc_project_export_dirs << pair.Value.DefaultBranch.Export + "/anim/ingame/" dlc_project_master_files << pair.Value.DefaultBranch.Export + "/anim/ingame/master_icd_list.xml" end end end p4 = config.Project.SCMConnect() changelistname = 'In-game DLC Animation Dictionaries [Asset Pipeline 3.0] - ' + Time.now.to_s changelist = p4.CreatePendingChangelist(changelistname) (0..dlc_project_art_dirs.length-1).each do |num| src_dir = dlc_project_art_dirs[num] dest_dir = dlc_project_export_dirs[num] master_list_filename = dlc_project_master_files[num] begin log.Message("Syncing: #{dest_dir}") ret = p4.Run( 'sync', true, Path.Combine( dest_dir, '...' ) ) print_return( ret, log) log.Message("Sync complete") log.Message("Syncing: #{src_dir}") ret = p4.Run( 'sync', Path.Combine( src_dir, '...' ) ) print_return( ret, log) log.Message("Sync complete") if (Directory.Exists( src_dir) == false) then next end if (Directory.Exists( dest_dir) == false) then Directory.CreateDirectory( dest_dir ) end zip_files = [] 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 log.Message(zip_files_safe.length.ToString()) if(zip_files_safe.length > 0) then file_mapping = FileMapping.Create(p4, System::Array[System::String].new(zip_files_safe)) if file_mapping.count > 0 then checkout_files(p4, changelist, file_mapping, log) end end log.Message("Check out complete.") end if(generate_master_icd_list(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}") 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 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_BranchName = ( g_Options.get('branch') ) g_NoPopups = ( g_Options.is_enabled?( 'nopopups' ) ) # Initialise log and console output. run( g_Config, g_Rebuild, g_Checkout, g_Log, g_BranchName ) 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