Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
.vroidファイルからへアプリセットを生成するRubyスクリプト
#!/usr/bin/ruby
# Copyright (C) 2020 Yakumo Sayo, Susanoo Lab. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# prerequired:
# gem install rubyzip
# usage: ruby DotVRoidToHairPreset.rb ~/vroid/avatars/HOGE.vroid
require 'JSON'
require 'fileutils'
require 'pathname'
require 'zip'
require 'rbconfig'
class DotVRoidToHairPreset
def initialize(path)
@dot_vroid_path = path
end
def delete_textures(canvas_ids)
canvas_ids.each {|id|
File.delete("#{@path}/materials/rendered_textures/#{id}.png")
}
end
public
def extract(dest_path, display_name)
FileUtils.mkpath(dest_path + '/materials/rendered_textures')
Zip::File.open(@dot_vroid_path) {|zip|
parse = ->(path) { JSON.parse(zip.read(path)) }
@meta = parse.('meta.json')
hair_defs = parse.('hair_editor/hair_defs.json')
@hairishes = hair_defs['Hairishes']
@hair_bone_store = hair_defs['HairBoneStore']
@material_set = parse.('materials/material_defs.json')
material_reduce!
FileUtils.mkpath(dest_path + '/materials/rendered_textures')
zip.each {|entry|
if @texture_ids.any? {|ids| entry.name.include?(ids) }
zip.extract(entry, dest_path + '/' + entry.name) {true}
puts entry.name
end
}
thumb = zip.read('thumbnails/thumbnail.jpg')
File.open(dest_path + '/thumbnail.jpg', mode = 'wb'){|f|
f.write(thumb)
}
}
File.open(dest_path+'/preset.json', mode = "w"){|f|
f.puts(JSON.generate({
'Hairishes' => @hairishes,
'_MaterialSet' => @material_set,
'_DisplayName' => display_name,
'_MetaData' => @meta,
'_HairBoneStore' => @hair_bone_store
}))
}
end
def hair_related_material_ids
result = []
get_f = -> (hairs) {
return nil unless hairs
hairs.each {|group|
result << group['Param']['_MaterialValueGUID']
result << group['Param']['_MaterialInheritedValueGUID']
get_f.(group['Children'])
}
}
get_f.(@hairishes)
result.compact.uniq
end
def material_reduce!
req_materials = hair_related_material_ids
@material_set['_Materials'] =
@material_set['_Materials'].reject {|mat|
!req_materials.include?(mat['_Id'])
}
@texture_ids = @material_set['_Materials'].map {|x|
x['_MainTextureId']
}
end
def self.presets_directory
@@presets_path ||= (
case RbConfig::CONFIG['host_os']
when /darwin|mac os/ #MacOS
app_name = "com.Company.ProductName"
path = "#{Dir.home}/Library/Application Support/#{app_name}/hair_presets/"
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
#Windows
path = "#{Dir.home}/AppData/LocalLow/pixiv/VRoidStudio/hair_presets/"
else
nil
end
)
end
def self.next_preset_num
Dir.chdir(presets_directory) {
Dir.glob('preset*/preset.json').map{|path|
path.delete('^0-9').to_i
}.max + 1
}
end
def run
preset_dir = self.class.presets_directory
num = self.class.next_preset_num
dest_path = preset_dir + '/preset' + num.to_s
filename = @dot_vroid_path.gsub('\\', '/')
.split('/').last
.split('.vroid').first
filename.encode(Encoding::UTF_8)
new_preset_name = num.to_s + ' : ' + filename
extract(dest_path, new_preset_name)
puts dest_path + ' created.'
end
end
inst = DotVRoidToHairPreset.new(ARGV[0])
inst.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment