Skip to content

Instantly share code, notes, and snippets.

@Ishotihadus
Last active May 12, 2019 05:07
Show Gist options
  • Save Ishotihadus/4f698757559c9491d16e241a50d7108e to your computer and use it in GitHub Desktop.
Save Ishotihadus/4f698757559c9491d16e241a50d7108e to your computer and use it in GitHub Desktop.
exfavbin の Ruby 版 FAVORITE のアレをアレするやつ
require 'bin_utils'
require 'fileutils'
require 'oily_png'
require 'parallel'
require 'zlib'
class FavBin
attr_reader :entries
def initialize(bin)
entry_count = BinUtils.get_int32_le(bin)
filenames_offset = 8 + 12 * entry_count
@entries = entry_count.times.map do |i|
filename_offset = BinUtils.get_int32_le(bin, 8 + i * 12)
offset = BinUtils.get_int32_le(bin, 12 + i * 12)
length = BinUtils.get_int32_le(bin, 16 + i * 12)
filename = bin.unpack1("@#{filenames_offset + filename_offset}Z*")
data = bin.byteslice(offset, length)
{ name: filename.force_encoding('SJIS').encode('UTF-8'), data: data }
end
end
def nvsg2png(data)
data.force_encoding('ASCII-8BIT')
hzc1 = data.slice(0, 12)
raise 'invalid signature (expected hzc1)' unless hzc1[0, 4] == 'hzc1'
original_length = BinUtils.get_int32_le(hzc1, 4)
header_length = BinUtils.get_int32_le(hzc1, 8)
header = data.slice(12, header_length)
raise 'invalid signature (expected NVSG)' unless header[0, 4] == 'NVSG'
# version = BinUtils.get_int16_le(header, 4)
texture_type = BinUtils.get_int16_le(header, 6)
width = BinUtils.get_int16_le(header, 8)
height = BinUtils.get_int16_le(header, 10)
# offset_x = BinUtils.get_int16_le(header, 12)
# offset_y = BinUtils.get_int16_le(header, 14)
entry_count = BinUtils.get_int32_le(header, 20)
data = Zlib.inflate(data.slice((12 + header_length)..))
raise 'data length not match' unless original_length == data.length
if entry_count.zero?
generate_png(data, width, height, texture_type)
else
size = data.length / entry_count
entry_count.times.map do
d = data.slice!(0, size)
generate_png(d, width, height, type)
end
end
end
def generate_png(data, width, height, type)
case type
when 0
ChunkyPNG::Image.from_bgr_stream(width, height, data)
when 1, 2
d = data.unpack('V*')
d.map!{|e| (e >> 24) | (e << 8 & 0xffffff00)}
ChunkyPNG::Image.from_abgr_stream(width, height, d.pack('V*'))
when 3
d = data.unpack('C*')
d.map!{|e| (e << 24) | (e << 16) | (e << 8) | 0xff}
ChunkyPNG::Image.from_abgr_stream(width, height, d.pack('V*'))
when 4
d = data.unpack('C*')
d.map!{|e| e.zero? ? 0xff : 0xffffffff}
ChunkyPNG::Image.from_abgr_stream(width, height, d.pack('V*'))
end
end
end
filename = ARGV[0]
dirname = File.basename(ARGV[0], '.*')
favbin = FavBin.new(File.binread(filename))
FileUtils.mkdir_p(dirname)
Parallel.each(favbin.entries) do |e|
puts e[:name]
if e[:data][0, 4] == 'hzc1' && e[:data][12, 4] == 'NVSG'
pngs = favbin.nvsg2png(e[:data])
if pngs.is_a?(Array)
pngs.each_with_index do |png, i|
png.save("#{dirname}/#{e[:name]}_#{i}.png")
end
else
pngs.save("#{dirname}/#{e[:name]}.png")
end
else
ext =
case e[:data].unpack1('N')
when 0x52494646 # RIFF
'wav'
when 0x4f676753 # OggS
'ogg'
when 0x89504E57 # PNG
'png'
when 0xffd8ffe0 # JPEG
'jpg'
end
File.binwrite(ext ? "#{dirname}/#{e[:name]}.#{ext}" : "#{dirname}/#{e[:name]}", e[:data])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment