class PAKFile

Class handles Quake style PAK files

Attributes

pak_file[R]

Public Class Methods

new(path = nil) click to toggle source

Opens an existing PAK file at path or if not specified creates a virtual PAK

# File lib/pakspy.rb, line 6
def initialize(path = nil)
  @file_hash = Hash.new
  @pak_file = nil

  unless path.nil?
    @pak_file = File.open path, "rb"

    header = Header.new @pak_file.read 12
    puts "Warning: Might not be a real PAK" unless header.magic == "PACK"
    file_count = header.size / 64

    @pak_file.seek header.offset
    file_count.times do
      entry = FileEntryPAK.new self, @pak_file.read(64)
      file_add entry
    end
  end
end

Public Instance Methods

extract(name, path) click to toggle source

Extracts a file name from this PAKFile to path on system

# File lib/pakspy.rb, line 31
def extract(name, path)
  file_entry = file_find(name)
  raise ArgumentError, "No such file #{name} in this PAK." if file_entry.nil?
  
  # Create the necessary directories
  path_current = ""
  File.dirname(path).split(/[\/\\]/).each do |dir|
    path_current += dir
    Dir.mkdir path_current unless Dir.exists?(path_current)
    path_current += "/"
  end

  # Transfer contents
  file = File.open path, "wb"
  file.write file_entry.read

  file.close
end
extract_all(dir) click to toggle source

Extracts all files in PAK to dir directory

# File lib/pakspy.rb, line 52
def extract_all(dir)
  files_list.each do |file_name|
    extract file_name, dir + "/" + file_name
  end
end
finalize() click to toggle source
# File lib/pakspy.rb, line 25
def finalize
  @pak_file.close unless @pak_file.nil?
end
insert(path, name) click to toggle source

Inserts a system file at path as name

# File lib/pakspy.rb, line 60
def insert(path, name)
  file_add FileEntrySystem.new self, path, name
end
insert_all(dir) click to toggle source

Inserts a directory dir recursively contents as their names

# File lib/pakspy.rb, line 66
def insert_all(dir)
  insert_all_helper(dir, "")
end
list() click to toggle source

Lists all files in PAK to an array

# File lib/pakspy.rb, line 116
def list
  files_list
end
save(path) click to toggle source

Saves all the changes to path

# File lib/pakspy.rb, line 72
def save(path)
  tmp_path = "#{path}.tmp_"
  file = File.open tmp_path, "wb"
  
  file_entries = Array.new

  # Will finish header later when we know where the file entries will be
  file.write "PACK"
  file.seek 12

  # Insert all of the files
  files_list.each do |name|
    entry = file_find name

    file_entry = Hash.new
    file_entry[:name] = entry.name
    file_entry[:offset] = file.pos

    file.write entry.read

    file_entry[:size] = file.pos - file_entry[:offset]
    file_entries.push file_entry
  end

  # Now we know the rest of the info needed for the header
  file_entry_pos = file.pos
  file.seek 4
  file.write [file_entry_pos, file_entries.length * 64].pack("VV")

  # And finally add the file entries
  file.seek file_entry_pos
  file_entries.each do |file_entry|
    file.write [file_entry[:name], file_entry[:offset], file_entry[:size]].pack("a56VV")
  end
  
  # close so we can open it again
  file.close

  # And finally move it to the correct place
  File.rename tmp_path, path
end

Private Instance Methods

file_add(file_entry) click to toggle source

Adds file_entry to pak

# File lib/pakspy.rb, line 126
def file_add(file_entry)
  @file_hash[file_entry.name] = file_entry
end
file_find(name) click to toggle source

Finds file called name in pak

# File lib/pakspy.rb, line 132
def file_find(name)
  @file_hash[name]
end
files_list() click to toggle source

Returns a list of all the files as an array of strings

# File lib/pakspy.rb, line 138
def files_list
  @file_hash.values.map { |entry| entry.name }
end
insert_all_helper(dir, prefix) click to toggle source
# File lib/pakspy.rb, line 142
def insert_all_helper(dir, prefix)
  Dir.entries(dir).each do |entry|
    unless entry == "." or entry == ".."
      if File.directory? "#{dir}/#{entry}"
        insert_all_helper("#{dir}/#{entry}", "#{prefix}#{entry}/")
      else
        insert("#{dir}/#{entry}", "#{prefix}#{entry}")
      end
    end
  end
end