# # File:: %RS_TOOLSLIB%/pipeline/projectutil/data_convert_file.rb # Description:: Functions to convert assets. # # Author:: David Muir # Date:: 6 August 2008 # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require 'pipeline/config/project' require 'pipeline/content/content_core' require 'pipeline/content/treecore' require 'pipeline/os/path' require 'pipeline/projectutil/data_content' require 'pipeline/projectutil/data_convert_map_dependencies' require 'pipeline/projectutil/data_rpf' require 'pipeline/projectutil/misc' require 'pipeline/resourcing/convert' require 'pipeline/resourcing/path' require 'pipeline/util/string' #----------------------------------------------------------------------------- # Functions #----------------------------------------------------------------------------- module Pipeline module ProjectUtil # # == Description # Conversion exception class. # class ConvertException < RuntimeError attr_reader :filenames def initialize( message, filenames = [] ) super( message ) @filenames = filenames end end # # Return a platform file for an independent filename(s). The filenames # argument can either be a String filename or Array of String filenames. # # Returns either an Array of filenames (when multiple files or target is # nil) or a single filename when a single filename and target is specified. # # === Example # DHM TODO # def ProjectUtil::data_convert_platform_filenames( project, branch, filenames, target = nil ) throw ArgumentError.new( "Invalid project specified (#{project.class})." ) \ unless ( project.is_a?( Pipeline::Project ) ) throw ArgumentError.new( "Invalid branch specified (#{branch.class})." ) \ unless ( branch.is_a?( Pipeline::Branch ) ) throw ArgumentError.new( "Invalid target specified (#{target.class})." ) \ if ( ( not target.nil? ) and ( not target.is_a?( Pipeline::Target ) ) ) results = [] if ( filenames.is_a?( String ) ) then filename = OS::Path::normalise( filenames ) if ( branch.is_export_file?( filename ) or branch.is_processed_file?( filename ) ) then if ( target.nil? ) then results = [] branch.targets.each_pair do |platform, target| next unless ( target.enabled ) target.in_env() do |env| results << env.subst( Resourcing::convert_independent_filename_to_platform( filename, target ) ) end end return ( results ) else target.in_env() do |env| return env.subst( Resourcing::convert_independent_filename_to_platform( filename, target ) ) end end elsif ( project.is_cache_file?( filename ) ) target.in_env() do |env| platformFilename = Resourcing::convert_independent_filename_to_platform( OS::Path.get_filename( filename ), target ) return OS::Path.combine( branch.preview, platformFilename ) end else throw RuntimeError.new( "Invalid export or processed file: #{filename}." ) end elsif ( filenames.is_a?( Array ) ) then filenames.each do |filename| filename = OS::Path::normalise( filename ) inner = ProjectUtil::data_convert_platform_filenames( project, branch, filename, target ) if ( inner.is_a?( Array ) ) results += inner else results << inner end end else throw ArgumentError.new( "Invalid export or processed filenames object (#{filenames.class})." ) end return ( results ) end # # Convert a file, directory or Array of files and directories from their # absolute paths only. # # The project, branch and hence Ragebuilder version is determined and the # conversion done. # # The filenames passed in must be independent files and from the same project # and branch. # # Returns a Hash with :success and :files symbol keys. # # === Example Usage === # See %RS_TOOLSLIB%/util/data_convert_file.rb. # def ProjectUtil::data_convert_file( filenames, rebuild = false, preview = false, load_content = true, lookup_dependencies = true, &block ) filenames = [ filenames ] unless ( filenames.is_a?( Array ) ) config = Pipeline::Config::instance() content_group = [] conv_project = config.project conv_project.load_config( ) conv_branch = conv_project.branches[conv_project.default_branch] if ( conv_project.nil? ) then throw RuntimeError::new( 'No project found. Invalid file, configuration or no targets enabled?' + filenames.join(";") ) end if ( conv_branch.nil? ) then throw RuntimeError::new( 'No branch found. Invalid file, configuration or no targets enabled?' ) end # Verify we have one or more platforms enabled; if not display an # error telling the user nothing will convert. at_least_one_target = false conv_branch.targets.each_pair do |platform, target| next unless ( target.enabled ) at_least_one_target = true end if ( ( not at_least_one_target ) and ( not config.user.username.downcase.include?( User::ASSETBUILDER_USER ) ) ) error_msg = "There are no platforms enabled for the current branch.\n" error_msg += "Project: #{conv_project.uiname}\n" error_msg += "Branch: #{conv_branch.name}\n\n" error_msg += "Re-run the installer and enabled one or more platforms." GUI::MessageBox::error("Platform Conversion Error Notification", error_msg) return false end conv_project.load_content( ) if ( load_content ) content_files_group = Content::Group::new( 'content_files_group' ) elapsed_time = Util::time() do content_list = conv_project.content.find( ) do |content| ( content.is_a?( Content::File ) ) end content_files_group.children = content_list end # We need to cater for content dependencies if ( load_content and lookup_dependencies ) then filenames = ProjectUtil::get_content_with_dependencies( conv_project, filenames ) end # For each filename we are asking to be converted; find it if it exists # in the project's content tree, if it exists then pass it down to the # convert system, otherwise create a Content::File or Content::Directory # node for it so that it goes through the platform conversion # (converter_rage). unique_children = [] filenames.each do |filename| content_node = nil has_content = false if ( load_content ) then content_node = ProjectUtil::data_content_for_files( conv_project, filename ).shift if ( content_node.is_a?( Content::Directory ) and preview ) then content_node.inputs.clear node = Content::Zip::from_filename_and_target( filename, conv_project.ind_target ) content_node.add_input( node ) end has_content = ( not content_node.nil? ) end # DHM FIXME: much of this can be replaced by a call to the newly added: # data_convert_content_to_platform_content function. if ( has_content and ( content_node.outputs.size() > 0 ) ) then # Use content-tree based nodes as inputs to dynamically # generated target nodes. content_node.outputs.each do |o| # This is critical to pass the output nodes down; otherwise # you don't get the processed nodes being processed. unique_children << o conv_branch.targets.each_pair do |platform, target| next unless ( target.enabled ) content_filename = nil if( o.is_a?( Content::Directory ) ) then content_filename = OS::Path::normalise( "#{o.absolute_path}.rpf" ) else content_filename = o.filename end target_filename = ProjectUtil::data_convert_platform_filenames( conv_project, conv_branch, content_filename, target ) target_file = Content::from_filename( conv_project, conv_branch.name, target_filename ) target_file.inputs << o unique_children << target_file puts "TARGET [CONTENT]:" puts "\t#{content_filename}" puts "\t#{target_file}" end end else # Fallback as before; generate the platform content node(s) # and pass to the convert system for platform conversion. if ( content_node.is_a?( Content::File ) ) then export_file = Content::from_filename( conv_project, conv_branch.name, filename ) conv_branch.targets.each_pair do |platform, target| next unless ( target.enabled ) target_filename = ProjectUtil::data_convert_platform_filenames( conv_project, conv_branch, filename, target ) target_file = Content::from_filename( conv_project, conv_branch.name, target_filename ) target_file.inputs << export_file unique_children << target_file puts "TARGET [FALLBACK]:" puts "\t#{target.platform} #{target.target}" puts "\t#{filename}" puts "\t#{target_file}" end elsif ( content_node.is_a?( Content::Directory ) ) then conv_branch.targets.each_pair do |platform, target| next unless ( target.enabled ) fake_filename = OS::Path::normalise( "#{content_node.absolute_path}.rpf" ) target_filename = ProjectUtil::data_convert_platform_filenames( conv_project, conv_branch, fake_filename, target ) target_file = Content::from_filename( conv_project, conv_branch.name, target_filename ) target_file.inputs << content_node unique_children << target_file puts "TARGET DIRECTORY [FALLBACK]:" puts "\t#{target.platform} #{target.target}" puts "\t#{filename}" puts "\t#{target_file}" end else # Very old fallback for when there is no content node # constructed. export_file = Content::from_filename( conv_project, conv_branch.name, filename ) conv_branch.targets.each_pair do |platform, target| next unless ( target.enabled ) target_filename = ProjectUtil::data_convert_platform_filenames( conv_project, conv_branch, filename, target ) target_file = Content::from_filename( conv_project, conv_branch.name, target_filename ) target_file.inputs << export_file unique_children << target_file puts "TARGET [FALLBACK]:" puts "\t#{target.platform} #{target.target}" puts "\t#{filename}" puts "\t#{target_file}" end end end end # Now we simply pass the various content nodes for the passed in files # down to the ConvertSystem; which handles which converter to pass the # node to and do its business. unique_children.uniq! unique_children.each do |child| content_group << child end converted_files = [] result = true if ( content_group.size > 0 ) then converter = Resourcing::ConvertSystem.instance() converter.setup( conv_project, conv_branch.name, false, rebuild, false, preview, load_content ) result = converter.build( content_group ) do |node, success| yield( node, success ) if ( block_given? ) if ( node.is_a?( Content::File ) ) then converted_files << OS::Path::normalise( node.filename ) elsif ( node.is_a?( Content::Directory ) ) then converted_files << OS::Path::normalise( node.filename ) end end else throw ConvertException::new( "Nothing to convert. Did you specify export files?", filenames ) end { :success => result, :files => converted_files } end # Enumerates all input_filenames and checks the associated content to see if it has any dependencies # A merged, distinct array of normalised filenames is returned def ProjectUtil::get_content_with_dependencies( conv_project, input_filenames ) dependency_filenames = [] #get dependencies for the input_filenames input_filenames.each do |input_filename| content_node = ProjectUtil::data_content_for_files( conv_project, input_filename ).shift if( content_node != nil and (content_node.is_a?( Pipeline::Content::MapZip )) ) then ProjectUtil::get_lod_dependency_files( content_node, dependency_filenames ) end end #combine and remove duplicates output_filenames = [] input_filenames.each do |input_filename| output_filenames << OS::Path.normalise(input_filename) end dependency_filenames.each do |dependency_filename| output_filenames << OS::Path.normalise(dependency_filename) end output_filenames.uniq! output_filenames end # # Return Array of content nodes that represent tha platform content-nodes # for the passed-in content_node (for all enabled platforms). # def ProjectUtil::data_convert_content_to_platform_content( project, branch, content_node ) unique_children = [] if ( content_node.outputs.size() > 0 ) then # Use content-tree based nodes as inputs to dynamically # generated target nodes. content_node.outputs.each do |o| # This is critical to pass the output nodes down; otherwise # you don't get the processed nodes being processed. unique_children << o branch.targets.each_pair do |platform, target| next unless ( target.enabled ) target_filename = ProjectUtil::data_convert_platform_filenames( project, branch, o.filename, target ) target_file = Content::from_filename( project, branch.name, target_filename ) target_file.inputs << o unique_children << target_file end end else # Fallback as before; generate the platform content node(s) # and pass to the convert system for platform conversion. if ( content_node.is_a?( Content::Directory ) ) then branch.targets.each_pair do |platform, target| next unless ( target.enabled ) fake_filename = OS::Path::normalise( "#{content_node.absolute_path}.rpf" ) target_filename = ProjectUtil::data_convert_platform_filenames( project, branch, fake_filename, target ) target_file = Content::from_filename( project, branch.name, target_filename ) target_file.inputs << content_node unique_children << target_file end elsif ( content_node.is_a?( Content::File ) ) then filename = content_node.filename export_file = Content::from_filename( project, branch.name, filename ) branch.targets.each_pair do |platform, target| next unless ( target.enabled ) target_filename = ProjectUtil::data_convert_platform_filenames( project, branch, filename, target ) target_file = Content::from_filename( project, branch.name, target_filename ) target_file.inputs << export_file unique_children << target_file end else throw RuntimeError::new( "Invalid content node type (#{content_node.class})." ) end end unique_children end # # Convert a ZIP file or Array of ZIP files from their absolute filename only # to RPF files. # # The filenames passed in must be independent files and from the same project # and branch. # # === Example Usage === # See %RS_TOOLSLIB%/util/data_convert_zip_to_rpf.rb # def ProjectUtil::data_convert_zip_to_rpf( filenames, compressed = false, &block ) filenames = [ filename ] unless ( filenames.is_a?( Array ) ) config = Pipeline::Config::instance() log = Pipeline::LogSystem::instance().rootlog conv_project = nil conv_branch = nil conv_project, conv_branch = ProjectUtil::get_project_from_filenames( filenames ) outputroot = OS::Path::get_temp_directory() converted_files = [] filenames.each do |filename| if ( 0 != 'zip'.casecmp( OS::Path::get_extension( filename ) ) ) then log.warn( "Skipping non-ZIP file: #{filename}" ) next end outputpath = OS::Path::combine( outputroot, OS::Path::get_basename( filename ) ) log.info( "Extracting ZIP: #{filename} to #{outputpath}" ) files = ProjectUtil::data_zip_extract( filename, outputpath, true ) do |packfilename| log.info( " #{packfilename}" ) end rpf_filename = OS::Path::replace_ext( filename, 'rpf' ) #--------------------------------------------------------------------- # Package RPF File #--------------------------------------------------------------------- file_list = [] files.each do |packfilename| entry = {} entry[:src] = packfilename entry[:dst] = packfilename.sub( outputpath+'/', '' ) end ProjectUtil::data_rpf_create( rpf_filename, files, compressed ) end converted_files end end # ProjectUtil module end # Pipeline module # %RS_TOOLSLIB%/pipeline/projectutil/data_convert_file.rb