Files
2025-09-29 00:52:08 +02:00

463 lines
14 KiB
Ruby
Executable File

#
# File:: data_get_usage.rb
# Description:: Functions for getting the usage of items in a given file.
#
# Author:: Marissa Warner-Wu <marissa.warner-wu@rockstarnorth.com>
# Date:: 22 July 2009
#
#-----------------------------------------------------------------------------
# Uses
#-----------------------------------------------------------------------------
require 'pp'
require 'pipeline/os/file'
require 'pipeline/resourcing/path'
require 'pipeline/projectutil/data_search'
require 'pipeline/projectutil/data_extract'
require "rexml/document"
include REXML
#-----------------------------------------------------------------------------
# Implementation
#-----------------------------------------------------------------------------
module Pipeline
module ProjectUtil
#-------------------------------------------------------------------------
# Functions
#-------------------------------------------------------------------------
#
# == Description
# A function for projects with levels which iterates through the objects in a given IDE file and
# then searches for them in the IPLs using data_get_usage_IPL().
#
# Generates a hash which prints to the screen or to an optional output file, if given.
#
# === Example Usage
# Pretty much what it says on the box.
#
# ProjectUtil::data_get_usage_IDE( g_Project, g_TargetName, g_OutputFile, g_IDE )
#
def ProjectUtil::data_get_usage_IDE( project, target, outputFile, ide )
throw ArgumentError.new( "IDE file does not exist (#{ide})." ) unless File.exist?( ide )
# Set the level, if needed
level = ""
if true #project.has_levels
if( OS::Path::get_trailing_directory(ide) == "generic" )
level = "generic"
puts "This map has been determined as being generic.\n\n"
else
level = OS::Path::get_directories(ide)[-2]
throw ParseError.new( "Unable to get level name from IDE path." ) if level.empty?
puts "This map has been determined as being part of the #{level} level.\n\n"
end
end #project.has_levels
# Get the list of files to search
files = []
if level.empty?
throw RuntimeError.new( "Currently no support for non-leveled projects." )
else
files = ProjectUtil::data_find_maps_levels(project, level)
end
# Parse the IDE for objects
objects = []
File.open(ide, 'r') do
text = IO.readlines( ide )
start = text.index( "objs\n" ) + 1
finish = text.index( "end\n" ) - 1
objects = text[start..finish]
throw ParseError.new( "Unable to parse the IDE." ) if objects.empty?
end
# Print objects and set up the hash
objects_hash = {}
objects.each do |line|
# Parse each line in the list for the object name
item = line.split(',')[0].downcase
# Add item to the hash, values are currently empty arrays
objects_hash[item] = {}
end #objects.each
# Search the IPLs
ProjectUtil::data_get_usage_IPL(project, files, objects_hash) do |output_hash|
if outputFile.empty?
# If we have no output file, pretty print to the screen
ObjectUsageHash::pp( output_hash )
else
# Otherwise, write to the output file
puts "\nWriting output to #{outputFile}"
if OS::Path::get_extension( outputFile ) == "xml"
ObjectUsageHash::to_xml( outputFile, OS::Path::get_basename(ide), output_hash)
else
# Currently we are using ObjectUsageHash::to_txt_by_map(), but we could also use
# ObjectUsageHash::to_txt_by_ipl() for more detailed output.
ObjectUsageHash::to_txt_by_map( outputFile, OS::Path::get_basename(ide), output_hash)
end
end
end #data_get_usage_IPL
end #data_get_usage_IDE
#
# == Description
# A function which takes a hash with object names as keys and a list of files and searches
# the IPLs in those files for the given objects. The value for each key is given as another
# hash which gives the object count by map name. The hash then looks like this:
#
# { item name => { map name => [[ipl name1, count], [ipl name2, count]] } }
#
# Checks both static and streaming IPLs. Yields the updated hash to an optional given block or
# pretty prints to the screen.
#
# === Example Usage
# Suppose that the item "tools_test" appears 10 times in alp_lake.ipl. We
# should then get the following from this piece of code:
#
# require 'pp'
# file_list = ["X:\jimmy\build\dev\independent\levels\alpine\alp_lake.ipl"]
# items = { "tools_test" => [] }
# ProjectUtil::data_search_IPL(project, file_list, objects_hash) do |myHash|
# pp myHash
# end
# ==> {"tools_test" => {["alp_lake" => [["alp_lake", 10]] } }
#
# Getting info from the hash:
# all_map_info = myHash["tools_test"]
# map_info = all_map_info["alp_lake"]
# ipl_info = map_info.assoc("alp_lake")
# ipl_name = ipl_info[0]
# count = ipl_info[1]
#
def ProjectUtil::data_get_usage_IPL(project, files, objects, &block)
# Set variables for extracting img/rpf files
temp = "X:/temp"
::FileUtils::rm_r( temp ) if ( ::File::directory?( temp ) )
r = RageUtils.new( project, project.default_branch )
# Go through the list of IPLs
puts "Checking the following IPLs..."
files.each do |file|
# Switch by file extension
case OS::Path::get_extension( file )
when "ipl"
puts OS::Path::get_filename( file )
ipl_objects = ProjectUtil::data_search_object_in_IPL(file, objects)
objects = ObjectUsageHash::merge(objects, ipl_objects)
when "img"
ProjectUtil::data_extract_image( r, file, temp ) do |extracted_filename|
if (OS::Path::get_extension( extracted_filename ) == "ipl")
puts OS::Path::get_filename( extracted_filename )
ipl_objects = ProjectUtil::data_search_object_in_IPL(extracted_filename, objects)
objects = ObjectUsageHash::merge(objects, ipl_objects)
end
end #data_extract_image
::FileUtils::rm_r( temp ) if ( ::File::directory?( temp ) )
when "rpf"
ProjectUtil::data_extract_rpf( r, file, temp ) do |extracted_filename|
if (OS::Path::get_extension( extracted_filename ) == "ipl")
puts OS::Path::get_filename( extracted_filename )
ipl_objects = ProjectUtil::data_search_object_in_IPL(extracted_filename, objects)
objects = ObjectUsageHash::merge(objects, ipl_objects)
end
end #data_extract_rpf
::FileUtils::rm_r( temp ) if ::File::directory?( temp )
end #case OS::Path::get_extension( file )
end #files.each
# If a block is given then yield the object hash, otherwise pretty print it
if block_given?
yield( objects )
else
ObjectUsageHash::pp( objects )
end
end #data_get_usage_IPL
end # ProjectUtil module
#
# == Description
# Small class for helper functions which work with the hash object used in data_get_usage_IPL().
#
class ObjectUsageHash
#---------------------------------------------------------------------
# Class Methods
#---------------------------------------------------------------------
#
# == Description
# A specialised helper function which merges the hash objects used in data_get_usage_IPL().
# Returns the merged hash.
#
# === Example Usage
# Assumes that the parent hash is one like that received by data_get_usage_IPL(), which contains
# information for all the maps, and the child hash is one like that generated by data_search_object_in_IPL()
# which contains only information for a single IPL.
#
def ObjectUsageHash::merge(parent_hash, child_hash)
# Create the new hash for merging
temp_hash = Hash.new
new_hash = Hash.new
# Go through the child hash
child_hash.each do |key, value|
# Pull out the map name
map_name = ""
if value[0].include?( "_stream" )
map_name = value[0].split( "_stream" )[0]
else
map_name = value[0]
end
# Update map info
map_info = parent_hash[key]
if map_info.has_key?( map_name )
# If we already have info for this map, add to the existing info
map_info[map_name] << value
else
# Otherwise create new info
map_info[map_name] = [value]
end
# Construct the new value
temp_hash[key] = map_info
end #child_hash.each do
# Merge the new values with the parent
parent_hash.each_key do |key|
if temp_hash.has_key?( key )
new_hash[key] = temp_hash[key]
else
new_hash[key] = parent_hash[key]
end
end
#Return the merged hash
new_hash
end #merge
#
# == Description
# A specialised helper function which pretty prints the Get Usage hash information.
#
# === Example Usage
# Fairly self-explanatory, only really useful for debugging. Usually you would want
# to print to a file as in below.
#
#
def ObjectUsageHash::pp( usage_hash )
usage_hash.each do |item, mapInfo|
puts "---------------- #{item}:"
pp mapInfo
end
end #pp
#
# == Description
# A specialised helper function which prints out the Get Usage hash information
# to a text file.
#
# Only prints info by IPL and not by map name.
#
# === Example Usage
# Suppose tools_test is an object in the map alp_test.
#
# output_file = "x:/test.txt"
# map_file = "alp_test"
# myHash = {"tools_test" => {["alp_lake" => [["alp_lake", 10]] } }
# ObjectUsageHash::to_txt_by_ipl(output_file, map_file, myHash)
#
# This would give you a file x:/test.txt which contains the following:
# """
# --- Object usage for alp_test ---
#
# --- tools_test:
# alp_lake - 10
# """
#
#
def ObjectUsageHash::to_txt_by_ipl( outputFile, mapFile, usage_hash )
throw ArgumentError.new("No output file given.") if (outputFile.nil? or outputFile.empty?)
# Open the output file and start writing
::FileUtils::rm( outputFile ) if ( File.exist?( outputFile ) )
log = File.new( outputFile, 'w' )
log.write( "--- Object usage information for #{mapFile} ---\n\n" )
# Alphabetise the output
key_list = usage_hash.keys
key_list.sort!
# Print info for each item
key_list.each do |item|
# Write the item name
log.write("--- #{item}:\n")
# Write the map info
mapInfo = usage_hash[item]
if mapInfo.empty?
log.write("[[UNUSED]]\n")
else
mapInfo.each_value do |ipls|
ipls.each { |iplInfo| log.write("#{iplInfo[0]} - #{iplInfo[1]}\n") }
end
end #if mapInfo.empty?
log.write("\n")
end #usage_hash.each
log.close()
end #to_txt_by_ipl
#
# == Description
# A specialised helper function which prints out the Get Usage hash information
# to a text file.
#
# Only prints info by map name and not by IPL.
#
# === Example Usage
# Suppose tools_test is an object in the map alp_test.
#
# output_file = "x:/test.txt"
# map_file = "alp_test"
# myHash = {"tools_test" => {["alp_lake" => [["alp_lake", 10]] } }
# ObjectUsageHash::to_txt_by_map(output_file, map_file, myHash)
#
# This would give you a file x:/test.txt which contains the following:
# """
# --- Object usage for alp_test ---
#
# --- tools_test:
# alp_lake - 10
# """
#
#
def ObjectUsageHash::to_txt_by_map( outputFile, mapFile, usage_hash )
throw ArgumentError.new("No output file given.") if (outputFile.nil? or outputFile.empty?)
# Open the output file and start writing
::FileUtils::rm( outputFile ) if ( File.exist?( outputFile ) )
log = File.new( outputFile, 'w' )
log.write( "--- Object usage information for #{mapFile} ---\n\n" )
# Alphabetise the output
key_list = usage_hash.keys
key_list.sort!
# Print info for each item
key_list.each do |item|
# Write the item name
log.write("--- #{item}:\n")
# Write the map info
mapInfo = usage_hash[item]
if mapInfo.empty?
log.write("[[UNUSED]]\n")
else
mapInfo.each do |map, ipls|
count = 0
ipls.each { |iplInfo| count += iplInfo[1] }
log.write("#{map} - #{count}\n")
end
end #if mapInfo.empty?
log.write("\n")
end #usage_hash.each
log.close()
end #to_txt_by_map
#
# == Description
# A specialised helper function which prints out the Get Usage hash information
# to an XML file.
#
# === Example Usage
# Suppose tools_test is an object in the map alp_test.
#
# output_file = "x:/test.xml"
# map_file = "alp_test"
# myHash = {"tools_test" => {["alp_lake" => [["alp_lake", 10]] } }
# ObjectUsageHash::to_xml(output_file, map_file, myHash)
#
# This would give you a file x:/test.xml which contains the following content:
# """
# <props name="alp_test">
# <item name="tools_test">
# <map name="alp_lake">
# <ipl name="alp_lake" count="10"
# </map>
# </item>
# </props>
# """
#
#
def ObjectUsageHash::to_xml( outputFile, mapFile, usage_hash )
throw ArgumentError.new("No output file given.") if (outputFile.nil? or outputFile.empty?)
# Open the output file and start writing
::FileUtils::rm( outputFile ) if ( File.exist?( outputFile ) )
log = Document.new
log << XMLDecl.new
log << Comment.new("Object usage information for #{mapFile}")
# Print props file name
root = Element.new( 'props' )
root.add_attribute( 'name', mapFile )
# Alphabetise the output
key_list = usage_hash.keys
key_list.sort!
# Print info for each item
key_list.each do |item|
# Write the item name
item_elem = root.add_element( 'item' )
item_elem.add_attribute( 'name', item )
# Write the map info
mapInfo = usage_hash[item]
unless mapInfo.empty?
mapInfo.each do |map, ipls|
map_elem = item_elem.add_element( 'map' )
map_elem.add_attribute( 'name', map )
ipls.each do |iplInfo|
ipl_elem = map_elem.add_element( 'ipl' )
ipl_elem.add_attribute( 'name', iplInfo[0] )
ipl_elem.add_attribute( 'count', iplInfo[1].to_s )
end
end #mapInfo.each
end #if mapInfo.empty?
end #usage_hash.each
log << root
# Write the XML
File.open( outputFile, 'w' ) do |file|
fmt = REXML::Formatters::Pretty.new()
fmt.write( log, file )
end
end #to_xml
end #ObjectUsageHash class
end # Pipeline module
# data_get_usage.rb