Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Extracts files from Artemis Engine resource files (.psf).
#!/usr/bin/env ruby
# encoding: utf-8
# fronze_string_literal: true
##
# Extracts files from Artemis Engine resource files (.psf).
#
# Tested on:
# - Memory's Dogma CODE:01
require 'fileutils'
unless ARGV.length == 2
puts "Incorrect arguments."
puts "Usage: artemis_extract.rb <input_file_name> <output_directory_name>"
exit 2
end
input_file_name = ARGV[0]
output_directory_name = ARGV[1]
input_file = File.open(input_file_name, 'rb')
# Convert little endian binary string to long int
def long_int(bstr)
bstr.bytes.reverse.inject(0) {|m, c| m * 256 + c }
end
unless input_file.read(3) == "pf6"
puts "Incorrect file signature."
exit 1
end
info_size = long_int(input_file.read(4))
puts "Info size: #{info_size}"
file_count = long_int(input_file.read(4))
puts "File count: #{file_count}"
FileInfo = Struct.new :name, :offset, :size
file_list = []
file_count.times do |i|
name_size = long_int(input_file.read(4))
name = input_file.read(name_size).force_encoding('UTF-8')
if !name.valid_encoding?
name = name.force_encoding('SJIS')
if !name.valid_encoding?
raise 'Invalid encoding of file'
end
name = name.encode('UTF-8')
end
name = name.gsub('\\', File::SEPARATOR)
puts "#{i + 1}: #{name}"
input_file.seek(4, :CUR)
offset = long_int(input_file.read(4))
size = long_int(input_file.read(4))
file_list << FileInfo.new(name, offset, size)
end
puts '======== Extracting ========'
file_list.each_with_index do |file, i|
puts "#{i + 1}: #{file.name}"
FileUtils.mkdir_p(File.expand_path(File.dirname(file.name), output_directory_name))
input_file.seek(file.offset)
File.write(File.expand_path(file.name, output_directory_name), input_file.read(file.size))
end
input_file.close
puts 'Done!'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.