format codes
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
require "bundler/gem_tasks"
|
||||
require "rake/extensiontask"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/gem_tasks'
|
||||
require 'rake/extensiontask'
|
||||
|
||||
task :scream do
|
||||
puts "みくは自分を曲げないよ!"
|
||||
puts 'みくは自分を曲げないよ!'
|
||||
end
|
||||
|
||||
task :build => :compile
|
||||
task build: :compile
|
||||
|
||||
Rake::ExtensionTask.new('decoders/native') do |ext|
|
||||
ext.lib_dir = 'lib/mikunyan'
|
||||
ext.lib_dir = 'lib/mikunyan'
|
||||
end
|
||||
|
||||
task :default => [:clobber, :compile, :spec]
|
||||
task default: %i[clobber compile spec]
|
||||
|
||||
+9
-7
@@ -1,11 +1,13 @@
|
||||
require "mikunyan/version"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "mikunyan/asset_bundle"
|
||||
require "mikunyan/asset"
|
||||
require "mikunyan/binary_reader"
|
||||
require "mikunyan/object_value"
|
||||
require "mikunyan/type_tree"
|
||||
require "mikunyan/constants"
|
||||
require 'mikunyan/version'
|
||||
|
||||
require 'mikunyan/asset_bundle'
|
||||
require 'mikunyan/asset'
|
||||
require 'mikunyan/binary_reader'
|
||||
require 'mikunyan/object_value'
|
||||
require 'mikunyan/type_tree'
|
||||
require 'mikunyan/constants'
|
||||
|
||||
# Module for deserializing Unity Assets and AssetBundles
|
||||
module Mikunyan
|
||||
|
||||
+333
-333
@@ -1,340 +1,340 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Mikunyan
|
||||
# Class for representing Unity Asset
|
||||
# @attr_reader [String] name Asset name
|
||||
# @attr_reader [Integer] format file format number
|
||||
# @attr_reader [String] generator_version version string of generator
|
||||
# @attr_reader [Integer] target_platform target platform number
|
||||
# @attr_reader [Symbol] endian data endianness (:little or :big)
|
||||
# @attr_reader [Array<Mikunyan::Asset::Klass>] klasses defined classes
|
||||
# @attr_reader [Array<Mikunyan::Asset::ObjectData>] objects included objects
|
||||
# @attr_reader [Array<Integer>] add_ids ?
|
||||
# @attr_reader [Array<Mikunyan::Asset::Reference>] references reference data
|
||||
class Asset
|
||||
attr_reader :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references, :res_s
|
||||
# Class for representing Unity Asset
|
||||
# @attr_reader [String] name Asset name
|
||||
# @attr_reader [Integer] format file format number
|
||||
# @attr_reader [String] generator_version version string of generator
|
||||
# @attr_reader [Integer] target_platform target platform number
|
||||
# @attr_reader [Symbol] endian data endianness (:little or :big)
|
||||
# @attr_reader [Array<Mikunyan::Asset::Klass>] klasses defined classes
|
||||
# @attr_reader [Array<Mikunyan::Asset::ObjectData>] objects included objects
|
||||
# @attr_reader [Array<Integer>] add_ids ?
|
||||
# @attr_reader [Array<Mikunyan::Asset::Reference>] references reference data
|
||||
class Asset
|
||||
attr_reader :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references, :res_s
|
||||
|
||||
# Struct for representing Asset class definition
|
||||
# @attr [Integer] class_id class ID
|
||||
# @attr [Integer,nil] script_id script ID
|
||||
# @attr [String] hash hash value (16 or 32 bytes)
|
||||
# @attr [Mikunyan::TypeTree, nil] type_tree given TypeTree
|
||||
Klass = Struct.new(:class_id, :script_id, :hash, :type_tree)
|
||||
# Struct for representing Asset class definition
|
||||
# @attr [Integer] class_id class ID
|
||||
# @attr [Integer,nil] script_id script ID
|
||||
# @attr [String] hash hash value (16 or 32 bytes)
|
||||
# @attr [Mikunyan::TypeTree, nil] type_tree given TypeTree
|
||||
Klass = Struct.new(:class_id, :script_id, :hash, :type_tree)
|
||||
|
||||
# Struct for representing Asset object information
|
||||
# @attr [Integer] path_id path ID
|
||||
# @attr [Integer] offset data offset
|
||||
# @attr [Integer] size data size
|
||||
# @attr [Integer,nil] type_id type ID
|
||||
# @attr [Integer,nil] class_id class ID
|
||||
# @attr [Integer,nil] class_idx class definition index
|
||||
# @attr [Boolean] destroyed? destroyed or not
|
||||
# @attr [String] data binary data of object
|
||||
ObjectData = Struct.new(:path_id, :offset, :size, :type_id, :class_id, :class_idx, :destroyed?, :data)
|
||||
# Struct for representing Asset object information
|
||||
# @attr [Integer] path_id path ID
|
||||
# @attr [Integer] offset data offset
|
||||
# @attr [Integer] size data size
|
||||
# @attr [Integer,nil] type_id type ID
|
||||
# @attr [Integer,nil] class_id class ID
|
||||
# @attr [Integer,nil] class_idx class definition index
|
||||
# @attr [Boolean] destroyed? destroyed or not
|
||||
# @attr [String] data binary data of object
|
||||
ObjectData = Struct.new(:path_id, :offset, :size, :type_id, :class_id, :class_idx, :destroyed?, :data)
|
||||
|
||||
# Struct for representing Asset reference information
|
||||
# @attr [String] path path
|
||||
# @attr [String] guid GUID (16 bytes)
|
||||
# @attr [Integer] type ?
|
||||
# @attr [String] file_path Asset name
|
||||
Reference = Struct.new(:path, :guid, :type, :file_path)
|
||||
# Struct for representing Asset reference information
|
||||
# @attr [String] path path
|
||||
# @attr [String] guid GUID (16 bytes)
|
||||
# @attr [Integer] type ?
|
||||
# @attr [String] file_path Asset name
|
||||
Reference = Struct.new(:path, :guid, :type, :file_path)
|
||||
|
||||
# Load Asset from binary string
|
||||
# @param [String] bin binary data
|
||||
# @param [String] name Asset name
|
||||
# @param [String] res_s resS data
|
||||
# @return [Mikunyan::Asset] deserialized Asset object
|
||||
def self.load(bin, name, res_s = nil)
|
||||
r = Asset.new(name, res_s)
|
||||
r.send(:load, bin)
|
||||
r
|
||||
end
|
||||
|
||||
# Load Asset from file
|
||||
# @param [String] file file name
|
||||
# @param [String] name Asset name (automatically generated if not specified)
|
||||
# @return [Mikunyan::Asset] deserialized Asset object
|
||||
def self.file(file, name=nil)
|
||||
name = File.basename(name, '.*') unless name
|
||||
Asset.load(File.binread(file), name)
|
||||
end
|
||||
|
||||
# Returns list of all path IDs
|
||||
# @return [Array<Integer>] list of all path IDs
|
||||
def path_ids
|
||||
@objects.map{|e| e.path_id}
|
||||
end
|
||||
|
||||
# Returns list of containers
|
||||
# @return [Array<Hash>,nil] list of all containers
|
||||
def containers
|
||||
obj = parse_object(1)
|
||||
return nil unless obj && obj.m_Container && obj.m_Container.array?
|
||||
obj.m_Container.value.map do |e|
|
||||
{:name => e.first.value, :preload_index => e.second.preloadIndex.value, :path_id => e.second.asset.m_PathID.value}
|
||||
end
|
||||
end
|
||||
|
||||
# Parse object of given path ID
|
||||
# @param [Integer,ObjectData] path_id path ID or object
|
||||
# @return [Mikunyan::ObjectValue,nil] parsed object
|
||||
def parse_object(path_id)
|
||||
if path_id.class == Integer
|
||||
obj = @objects.find{|e| e.path_id == path_id}
|
||||
return nil unless obj
|
||||
elsif path_id.class == ObjectData
|
||||
obj = path_id
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
klass = (obj.class_idx ? @klasses[obj.class_idx] : @klasses.find{|e| e.class_id == obj.class_id} || @klasses.find{|e| e.class_id == obj.type_id})
|
||||
type_tree = Asset.parse_type_tree(klass)
|
||||
return nil unless type_tree
|
||||
|
||||
parse_object_private(BinaryReader.new(obj.data, @endian), type_tree)
|
||||
end
|
||||
|
||||
# Parse object of given path ID and simplify it
|
||||
# @param [Integer,ObjectData] path_id path ID or object
|
||||
# @return [Hash,nil] parsed object
|
||||
def parse_object_simple(path_id)
|
||||
Asset.object_simplify(parse_object(path_id))
|
||||
end
|
||||
|
||||
# Returns object type name string
|
||||
# @param [Integer,ObjectData] path_id path ID or object
|
||||
# @return [String,nil] type name
|
||||
def object_type(path_id)
|
||||
if path_id.class == Integer
|
||||
obj = @objects.find{|e| e.path_id == path_id}
|
||||
return nil unless obj
|
||||
elsif path_id.class == ObjectData
|
||||
obj = path_id
|
||||
else
|
||||
return nil
|
||||
end
|
||||
klass = (obj.class_idx ? @klasses[obj.class_idx] : @klasses.find{|e| e.class_id == obj.class_id} || @klasses.find{|e| e.class_id == obj.type_id})
|
||||
if klass && klass.type_tree && klass.type_tree.nodes[0]
|
||||
klass.type_tree.nodes[0].type
|
||||
elsif klass
|
||||
Mikunyan::CLASS_ID[klass.class_id]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(name, res_s = nil)
|
||||
@name = name
|
||||
@endian = :big
|
||||
@res_s = res_s
|
||||
end
|
||||
|
||||
def load(bin)
|
||||
br = BinaryReader.new(bin)
|
||||
metadata_size = br.i32u
|
||||
size = br.i32u
|
||||
@format = br.i32u
|
||||
data_offset = br.i32u
|
||||
|
||||
if @format >= 9
|
||||
@endian = :little if br.i32 == 0
|
||||
br.endian = @endian
|
||||
end
|
||||
|
||||
@generator_version = br.cstr
|
||||
@target_platform = br.i32
|
||||
@klasses = []
|
||||
|
||||
if @format >= 17
|
||||
has_type_trees = (br.i8 != 0)
|
||||
type_tree_count = br.i32u
|
||||
type_tree_count.times do
|
||||
class_id = br.i32
|
||||
br.adv(1)
|
||||
script_id = br.i16
|
||||
if class_id < 0 || class_id == 114
|
||||
hash = br.read(32)
|
||||
else
|
||||
hash = br.read(16)
|
||||
end
|
||||
@klasses << Klass.new(class_id, script_id, hash, has_type_trees ? TypeTree.load(br) : TypeTree.load_default(hash))
|
||||
end
|
||||
elsif @format >= 13
|
||||
has_type_trees = (br.i8 != 0)
|
||||
type_tree_count = br.i32u
|
||||
type_tree_count.times do
|
||||
class_id = br.i32
|
||||
if class_id < 0
|
||||
hash = br.read(32)
|
||||
else
|
||||
hash = br.read(16)
|
||||
end
|
||||
@klasses << Klass.new(class_id, nil, hash, has_type_trees ? TypeTree.load(br) : TypeTree.load_default(hash))
|
||||
end
|
||||
else
|
||||
@type_trees = {}
|
||||
type_tree_count = br.i32u
|
||||
type_tree_count.times do
|
||||
class_id = br.i32
|
||||
@klasses << Klass.new(class_id, nil, nil, @format == 10 || @format == 12 ? TypeTree.load(br) : TypeTree.load_legacy(br))
|
||||
end
|
||||
end
|
||||
|
||||
long_object_ids = (@format >= 14 || (7 <= @format && @format <= 13 && br.i32 != 0))
|
||||
|
||||
@objects = []
|
||||
object_count = br.i32u
|
||||
object_count.times do
|
||||
br.align(4) if @format >= 14
|
||||
path_id = long_object_ids ? br.i64 : br.i32
|
||||
offset = br.i32u
|
||||
size = br.i32u
|
||||
if @format >= 17
|
||||
@objects << ObjectData.new(path_id, offset, size, nil, nil, br.i32u, @format <= 10 && br.i16 != 0)
|
||||
else
|
||||
@objects << ObjectData.new(path_id, offset, size, br.i32, br.i16, nil, @format <= 10 && br.i16 != 0)
|
||||
end
|
||||
br.adv(2) if 11 <= @format && @format <= 16
|
||||
br.adv(1) if 15 <= @format && @format <= 16
|
||||
end
|
||||
|
||||
if @format >= 11
|
||||
@add_ids = []
|
||||
add_id_count = br.i32u
|
||||
add_id_count.times do
|
||||
br.align(4) if @format >= 14
|
||||
@add_ids << [(long_object_ids ? br.i64 : br.i32), br.i32]
|
||||
end
|
||||
end
|
||||
|
||||
if @format >= 6
|
||||
@references = []
|
||||
reference_count = br.i32u
|
||||
reference_count.times do
|
||||
@references << Reference.new(br.cstr, br.read(16), br.i32, br.cstr)
|
||||
end
|
||||
end
|
||||
|
||||
@objects.each do |e|
|
||||
br.jmp(data_offset + e.offset)
|
||||
e.data = br.read(e.size)
|
||||
end
|
||||
end
|
||||
|
||||
def parse_object_private(br, type_tree)
|
||||
r = nil
|
||||
node = type_tree[:node]
|
||||
children = type_tree[:children]
|
||||
|
||||
if node.array?
|
||||
data = nil
|
||||
size = parse_object_private(br, children.find{|e| e[:name] == 'size'}).value
|
||||
data_type_tree = children.find{|e| e[:name] == 'data'}
|
||||
if node.type == 'TypelessData'
|
||||
data = br.read(size * data_type_tree[:node].size)
|
||||
else
|
||||
data = size.times.map{ parse_object_private(br, data_type_tree) }
|
||||
end
|
||||
r = ObjectValue.new(node.name, node.type, br.endian, data)
|
||||
elsif node.size == -1
|
||||
r = ObjectValue.new(node.name, node.type, br.endian)
|
||||
if children.size == 1 && children[0][:name] == 'Array' && children[0][:node].type == 'Array' && children[0][:node].array?
|
||||
if node.type == 'string'
|
||||
size = parse_object_private(br, children[0][:children].find{|e| e[:name] == 'size'}).value
|
||||
r.value = br.read(size * children[0][:children].find{|e| e[:name] == 'data'}[:node].size).force_encoding("utf-8")
|
||||
br.align(4) if children[0][:node].flags & 0x4000 != 0
|
||||
else
|
||||
r.value = parse_object_private(br, children[0]).value
|
||||
end
|
||||
elsif node.type == 'StreamingInfo'
|
||||
children.each{|child| r[child[:name]] = parse_object_private(br, child)}
|
||||
r.value = @res_s.byteslice(r['offset'].value, r['size'].value) if r['path'].value == "archive:/#{name}/#{name}.resS"
|
||||
else
|
||||
children.each do |child|
|
||||
r[child[:name]] = parse_object_private(br, child)
|
||||
end
|
||||
end
|
||||
elsif children.size > 0
|
||||
pos = br.pos
|
||||
r = ObjectValue.new(node.name, node.type, br.endian)
|
||||
r.is_struct = true
|
||||
children.each do |child|
|
||||
r[child[:name]] = parse_object_private(br, child)
|
||||
end
|
||||
else
|
||||
pos = br.pos
|
||||
value = nil
|
||||
case node.type
|
||||
when 'bool'
|
||||
value = (br.i8 != 0)
|
||||
when 'SInt8'
|
||||
value = br.i8s
|
||||
when 'UInt8', 'char'
|
||||
value = br.i8u
|
||||
when 'SInt16', 'short'
|
||||
value = br.i16s
|
||||
when 'UInt16', 'unsigned short'
|
||||
value = br.i16u
|
||||
when 'SInt32', 'int'
|
||||
value = br.i32s
|
||||
when 'UInt32', 'unsigned int'
|
||||
value = br.i32u
|
||||
when 'SInt64', 'long long'
|
||||
value = br.i64s
|
||||
when 'UInt64', 'unsigned long long'
|
||||
value = br.i64u
|
||||
when 'float'
|
||||
value = br.float
|
||||
when 'double'
|
||||
value = br.double
|
||||
when 'ColorRGBA'
|
||||
value = [br.i8u, br.i8u, br.i8u, br.i8u]
|
||||
else
|
||||
value = br.read(node.size)
|
||||
end
|
||||
br.jmp(pos + node.size)
|
||||
r = ObjectValue.new(node.name, node.type, br.endian, value)
|
||||
end
|
||||
br.align(4) if node.flags & 0x4000 != 0
|
||||
r
|
||||
end
|
||||
|
||||
def self.object_simplify(obj)
|
||||
if obj.class != ObjectValue
|
||||
obj
|
||||
elsif obj.type == 'pair'
|
||||
[object_simplify(obj['first']), object_simplify(obj['second'])]
|
||||
elsif obj.type == 'map' && obj.array?
|
||||
obj.value.map{|e| [object_simplify(e['first']), object_simplify(e['second'])] }.to_h
|
||||
elsif obj.value?
|
||||
object_simplify(obj.value)
|
||||
elsif obj.array?
|
||||
obj.value.map{|e| object_simplify(e)}
|
||||
else
|
||||
hash = {}
|
||||
obj.keys.each do |key|
|
||||
hash[key] = object_simplify(obj[key])
|
||||
end
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_type_tree(klass)
|
||||
return nil unless klass.type_tree
|
||||
nodes = klass.type_tree.nodes
|
||||
tree = {}
|
||||
stack = []
|
||||
nodes.each do |node|
|
||||
this = {:name => node.name, :node => node, :children => []}
|
||||
if node.depth == 0
|
||||
tree = this
|
||||
else
|
||||
stack[node.depth - 1][:children] << this
|
||||
end
|
||||
stack[node.depth] = this
|
||||
end
|
||||
tree
|
||||
end
|
||||
# Load Asset from binary string
|
||||
# @param [String] bin binary data
|
||||
# @param [String] name Asset name
|
||||
# @param [String] res_s resS data
|
||||
# @return [Mikunyan::Asset] deserialized Asset object
|
||||
def self.load(bin, name, res_s = nil)
|
||||
r = Asset.new(name, res_s)
|
||||
r.send(:load, bin)
|
||||
r
|
||||
end
|
||||
|
||||
# Load Asset from file
|
||||
# @param [String] file file name
|
||||
# @param [String] name Asset name (automatically generated if not specified)
|
||||
# @return [Mikunyan::Asset] deserialized Asset object
|
||||
def self.file(file, name = nil)
|
||||
name ||= File.basename(name, '.*')
|
||||
Asset.load(File.binread(file), name)
|
||||
end
|
||||
|
||||
# Returns list of all path IDs
|
||||
# @return [Array<Integer>] list of all path IDs
|
||||
def path_ids
|
||||
@objects.map(&:path_id)
|
||||
end
|
||||
|
||||
# Returns list of containers
|
||||
# @return [Array<Hash>,nil] list of all containers
|
||||
def containers
|
||||
obj = parse_object(1)
|
||||
return nil unless obj&.m_Container && obj.m_Container.array?
|
||||
obj.m_Container.value.map do |e|
|
||||
{ name: e.first.value, preload_index: e.second.preloadIndex.value, path_id: e.second.asset.m_PathID.value }
|
||||
end
|
||||
end
|
||||
|
||||
# Parse object of given path ID
|
||||
# @param [Integer,ObjectData] path_id path ID or object
|
||||
# @return [Mikunyan::ObjectValue,nil] parsed object
|
||||
def parse_object(path_id)
|
||||
if path_id.class == Integer
|
||||
obj = @objects.find{|e| e.path_id == path_id}
|
||||
return nil unless obj
|
||||
elsif path_id.class == ObjectData
|
||||
obj = path_id
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
klass = (obj.class_idx ? @klasses[obj.class_idx] : @klasses.find{|e| e.class_id == obj.class_id} || @klasses.find{|e| e.class_id == obj.type_id})
|
||||
type_tree = Asset.parse_type_tree(klass)
|
||||
return nil unless type_tree
|
||||
|
||||
parse_object_private(BinaryReader.new(obj.data, @endian), type_tree)
|
||||
end
|
||||
|
||||
# Parse object of given path ID and simplify it
|
||||
# @param [Integer,ObjectData] path_id path ID or object
|
||||
# @return [Hash,nil] parsed object
|
||||
def parse_object_simple(path_id)
|
||||
Asset.object_simplify(parse_object(path_id))
|
||||
end
|
||||
|
||||
# Returns object type name string
|
||||
# @param [Integer,ObjectData] path_id path ID or object
|
||||
# @return [String,nil] type name
|
||||
def object_type(path_id)
|
||||
if path_id.class == Integer
|
||||
obj = @objects.find{|e| e.path_id == path_id}
|
||||
return nil unless obj
|
||||
elsif path_id.class == ObjectData
|
||||
obj = path_id
|
||||
else
|
||||
return nil
|
||||
end
|
||||
klass = (obj.class_idx ? @klasses[obj.class_idx] : @klasses.find{|e| e.class_id == obj.class_id} || @klasses.find{|e| e.class_id == obj.type_id})
|
||||
if klass&.type_tree && klass.type_tree.nodes[0]
|
||||
klass.type_tree.nodes[0].type
|
||||
elsif klass
|
||||
Mikunyan::CLASS_ID[klass.class_id]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(name, res_s = nil)
|
||||
@name = name
|
||||
@endian = :big
|
||||
@res_s = res_s
|
||||
end
|
||||
|
||||
def load(bin)
|
||||
br = BinaryReader.new(bin)
|
||||
metadata_size = br.i32u
|
||||
size = br.i32u
|
||||
@format = br.i32u
|
||||
data_offset = br.i32u
|
||||
|
||||
if @format >= 9
|
||||
@endian = :little if br.i32 == 0
|
||||
br.endian = @endian
|
||||
end
|
||||
|
||||
@generator_version = br.cstr
|
||||
@target_platform = br.i32
|
||||
@klasses = []
|
||||
|
||||
if @format >= 17
|
||||
has_type_trees = (br.i8 != 0)
|
||||
type_tree_count = br.i32u
|
||||
type_tree_count.times do
|
||||
class_id = br.i32
|
||||
br.adv(1)
|
||||
script_id = br.i16
|
||||
hash = if class_id < 0 || class_id == 114
|
||||
br.read(32)
|
||||
else
|
||||
br.read(16)
|
||||
end
|
||||
@klasses << Klass.new(class_id, script_id, hash, has_type_trees ? TypeTree.load(br) : TypeTree.load_default(hash))
|
||||
end
|
||||
elsif @format >= 13
|
||||
has_type_trees = (br.i8 != 0)
|
||||
type_tree_count = br.i32u
|
||||
type_tree_count.times do
|
||||
class_id = br.i32
|
||||
hash = if class_id < 0
|
||||
br.read(32)
|
||||
else
|
||||
br.read(16)
|
||||
end
|
||||
@klasses << Klass.new(class_id, nil, hash, has_type_trees ? TypeTree.load(br) : TypeTree.load_default(hash))
|
||||
end
|
||||
else
|
||||
@type_trees = {}
|
||||
type_tree_count = br.i32u
|
||||
type_tree_count.times do
|
||||
class_id = br.i32
|
||||
@klasses << Klass.new(class_id, nil, nil, @format == 10 || @format == 12 ? TypeTree.load(br) : TypeTree.load_legacy(br))
|
||||
end
|
||||
end
|
||||
|
||||
long_object_ids = (@format >= 14 || (7 <= @format && @format <= 13 && br.i32 != 0))
|
||||
|
||||
@objects = []
|
||||
object_count = br.i32u
|
||||
object_count.times do
|
||||
br.align(4) if @format >= 14
|
||||
path_id = long_object_ids ? br.i64 : br.i32
|
||||
offset = br.i32u
|
||||
size = br.i32u
|
||||
@objects << if @format >= 17
|
||||
ObjectData.new(path_id, offset, size, nil, nil, br.i32u, @format <= 10 && br.i16 != 0)
|
||||
else
|
||||
ObjectData.new(path_id, offset, size, br.i32, br.i16, nil, @format <= 10 && br.i16 != 0)
|
||||
end
|
||||
br.adv(2) if 11 <= @format && @format <= 16
|
||||
br.adv(1) if 15 <= @format && @format <= 16
|
||||
end
|
||||
|
||||
if @format >= 11
|
||||
@add_ids = []
|
||||
add_id_count = br.i32u
|
||||
add_id_count.times do
|
||||
br.align(4) if @format >= 14
|
||||
@add_ids << [(long_object_ids ? br.i64 : br.i32), br.i32]
|
||||
end
|
||||
end
|
||||
|
||||
if @format >= 6
|
||||
@references = []
|
||||
reference_count = br.i32u
|
||||
reference_count.times do
|
||||
@references << Reference.new(br.cstr, br.read(16), br.i32, br.cstr)
|
||||
end
|
||||
end
|
||||
|
||||
@objects.each do |e|
|
||||
br.jmp(data_offset + e.offset)
|
||||
e.data = br.read(e.size)
|
||||
end
|
||||
end
|
||||
|
||||
def parse_object_private(br, type_tree)
|
||||
r = nil
|
||||
node = type_tree[:node]
|
||||
children = type_tree[:children]
|
||||
|
||||
if node.array?
|
||||
data = nil
|
||||
size = parse_object_private(br, children.find{|e| e[:name] == 'size'}).value
|
||||
data_type_tree = children.find{|e| e[:name] == 'data'}
|
||||
data = if node.type == 'TypelessData'
|
||||
br.read(size * data_type_tree[:node].size)
|
||||
else
|
||||
size.times.map{parse_object_private(br, data_type_tree)}
|
||||
end
|
||||
r = ObjectValue.new(node.name, node.type, br.endian, data)
|
||||
elsif node.size == -1
|
||||
r = ObjectValue.new(node.name, node.type, br.endian)
|
||||
if children.size == 1 && children[0][:name] == 'Array' && children[0][:node].type == 'Array' && children[0][:node].array?
|
||||
if node.type == 'string'
|
||||
size = parse_object_private(br, children[0][:children].find{|e| e[:name] == 'size'}).value
|
||||
r.value = br.read(size * children[0][:children].find{|e| e[:name] == 'data'}[:node].size).force_encoding('utf-8')
|
||||
br.align(4) if children[0][:node].flags & 0x4000 != 0
|
||||
else
|
||||
r.value = parse_object_private(br, children[0]).value
|
||||
end
|
||||
elsif node.type == 'StreamingInfo'
|
||||
children.each{|child| r[child[:name]] = parse_object_private(br, child)}
|
||||
r.value = @res_s.byteslice(r['offset'].value, r['size'].value) if r['path'].value == "archive:/#{name}/#{name}.resS"
|
||||
else
|
||||
children.each do |child|
|
||||
r[child[:name]] = parse_object_private(br, child)
|
||||
end
|
||||
end
|
||||
elsif !children.empty?
|
||||
pos = br.pos
|
||||
r = ObjectValue.new(node.name, node.type, br.endian)
|
||||
r.is_struct = true
|
||||
children.each do |child|
|
||||
r[child[:name]] = parse_object_private(br, child)
|
||||
end
|
||||
else
|
||||
pos = br.pos
|
||||
value = nil
|
||||
case node.type
|
||||
when 'bool'
|
||||
value = (br.i8 != 0)
|
||||
when 'SInt8'
|
||||
value = br.i8s
|
||||
when 'UInt8', 'char'
|
||||
value = br.i8u
|
||||
when 'SInt16', 'short'
|
||||
value = br.i16s
|
||||
when 'UInt16', 'unsigned short'
|
||||
value = br.i16u
|
||||
when 'SInt32', 'int'
|
||||
value = br.i32s
|
||||
when 'UInt32', 'unsigned int'
|
||||
value = br.i32u
|
||||
when 'SInt64', 'long long'
|
||||
value = br.i64s
|
||||
when 'UInt64', 'unsigned long long'
|
||||
value = br.i64u
|
||||
when 'float'
|
||||
value = br.float
|
||||
when 'double'
|
||||
value = br.double
|
||||
when 'ColorRGBA'
|
||||
value = [br.i8u, br.i8u, br.i8u, br.i8u]
|
||||
else
|
||||
value = br.read(node.size)
|
||||
end
|
||||
br.jmp(pos + node.size)
|
||||
r = ObjectValue.new(node.name, node.type, br.endian, value)
|
||||
end
|
||||
br.align(4) if node.flags & 0x4000 != 0
|
||||
r
|
||||
end
|
||||
|
||||
def self.object_simplify(obj)
|
||||
if obj.class != ObjectValue
|
||||
obj
|
||||
elsif obj.type == 'pair'
|
||||
[object_simplify(obj['first']), object_simplify(obj['second'])]
|
||||
elsif obj.type == 'map' && obj.array?
|
||||
obj.value.map{|e| [object_simplify(e['first']), object_simplify(e['second'])]}.to_h
|
||||
elsif obj.value?
|
||||
object_simplify(obj.value)
|
||||
elsif obj.array?
|
||||
obj.value.map{|e| object_simplify(e)}
|
||||
else
|
||||
hash = {}
|
||||
obj.keys.each do |key|
|
||||
hash[key] = object_simplify(obj[key])
|
||||
end
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_type_tree(klass)
|
||||
return nil unless klass.type_tree
|
||||
nodes = klass.type_tree.nodes
|
||||
tree = {}
|
||||
stack = []
|
||||
nodes.each do |node|
|
||||
this = { name: node.name, node: node, children: [] }
|
||||
if node.depth == 0
|
||||
tree = this
|
||||
else
|
||||
stack[node.depth - 1][:children] << this
|
||||
end
|
||||
stack[node.depth] = this
|
||||
end
|
||||
tree
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+127
-125
@@ -1,132 +1,134 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'extlz4'
|
||||
require 'extlzma'
|
||||
|
||||
module Mikunyan
|
||||
# Class for representing Unity AssetBundle
|
||||
# @attr_reader [String] signature file signature (UnityRaw or UnityFS)
|
||||
# @attr_reader [Integer] format file format number
|
||||
# @attr_reader [String] unity_version version string of Unity to use this AssetBundle
|
||||
# @attr_reader [String] generator_version version string of generator
|
||||
# @attr_reader [Array<Mikunyan::Asset>] assets included Assets
|
||||
class AssetBundle
|
||||
attr_reader :signature, :format, :unity_version, :generator_version, :assets
|
||||
# Class for representing Unity AssetBundle
|
||||
# @attr_reader [String] signature file signature (UnityRaw or UnityFS)
|
||||
# @attr_reader [Integer] format file format number
|
||||
# @attr_reader [String] unity_version version string of Unity to use this AssetBundle
|
||||
# @attr_reader [String] generator_version version string of generator
|
||||
# @attr_reader [Array<Mikunyan::Asset>] assets included Assets
|
||||
class AssetBundle
|
||||
attr_reader :signature, :format, :unity_version, :generator_version, :assets
|
||||
|
||||
# Load AssetBundle from binary string
|
||||
# @param [String] bin binary data
|
||||
# @return [Mikunyan::AssetBundle] deserialized AssetBundle object
|
||||
def self.load(bin)
|
||||
r = AssetBundle.new
|
||||
r.send(:load, bin)
|
||||
r
|
||||
end
|
||||
|
||||
# Load AssetBundle from file
|
||||
# @param [String] file file name
|
||||
# @return [Mikunyan::AssetBundle] deserialized AssetBundle object
|
||||
def self.file(file)
|
||||
AssetBundle.load(File.binread(file))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load(bin)
|
||||
br = BinaryReader.new(bin)
|
||||
@signature = br.cstr
|
||||
@format = br.i32
|
||||
@unity_version = br.cstr
|
||||
@generator_version = br.cstr
|
||||
|
||||
case @signature
|
||||
when 'UnityRaw'
|
||||
load_unity_raw(br, false)
|
||||
when 'UnityFS'
|
||||
load_unity_fs(br)
|
||||
when 'UnityWeb'
|
||||
if @format <= 3
|
||||
load_unity_raw(br, true)
|
||||
else
|
||||
load_unity_fs(br)
|
||||
end
|
||||
else
|
||||
raise("Unknown signature: #{@signature}")
|
||||
end
|
||||
end
|
||||
|
||||
def load_unity_raw(br, compressed)
|
||||
@assets = []
|
||||
|
||||
file_size = br.i32u
|
||||
header_size = br.i32u
|
||||
|
||||
if compressed
|
||||
br.adv(4)
|
||||
block_count = br.i32u
|
||||
blocks = block_count.times.map{{ :c => br.i32u, :u => br.i32u }}
|
||||
br.jmp(header_size)
|
||||
data = String.new
|
||||
blocks.each{|b| data << LZMA.decode(br.read(b[:c]))}
|
||||
br = BinaryReader.new(data)
|
||||
else
|
||||
br.jmp(header_size)
|
||||
end
|
||||
|
||||
asset_count = br.i32u
|
||||
asset_count.times do
|
||||
asset_pos = br.pos
|
||||
asset_name = br.cstr
|
||||
asset_header_size = br.i32u
|
||||
asset_size = br.i32u
|
||||
br.jmp(asset_pos + asset_header_size - 4)
|
||||
asset_data = br.read(asset_size)
|
||||
asset = Asset.load(asset_data, asset_name)
|
||||
@assets << asset
|
||||
end
|
||||
end
|
||||
|
||||
def load_unity_fs(br)
|
||||
@assets = []
|
||||
|
||||
file_size = br.i64u
|
||||
ci_block_size = br.i32u
|
||||
ui_block_size = br.i32u
|
||||
flags = br.i32u
|
||||
|
||||
head = BinaryReader.new(uncompress(flags & 0x80 == 0 ? br.read(ci_block_size) : br.read_abs(ci_block_size, file_size - ci_block_size), ui_block_size, flags))
|
||||
guid = head.read(16)
|
||||
|
||||
blocks = []
|
||||
block_count = head.i32u
|
||||
block_count.times{ blocks << {:u => head.i32u, :c => head.i32u, :f => head.i16u} }
|
||||
|
||||
asset_blocks = []
|
||||
asset_count = head.i32u
|
||||
asset_count.times{ asset_blocks << {:offset => head.i64u, :size => head.i64u, :status => head.i32, :name => head.cstr} }
|
||||
|
||||
raw_data = String.new
|
||||
blocks.each{|b| raw_data << uncompress(br.read(b[:c]), b[:u], b[:f])}
|
||||
|
||||
asset_blocks.each do |b|
|
||||
next if b[:name].end_with?('.resS')
|
||||
res_s = asset_blocks.find{|e| e[:name] == "#{b[:name]}.resS"}
|
||||
asset = Asset.load(raw_data.byteslice(b[:offset], b[:size]), b[:name], res_s && raw_data.byteslice(res_s[:offset], res_s[:size]))
|
||||
@assets << asset
|
||||
end
|
||||
end
|
||||
|
||||
def uncompress(block, max_dest_size, flags)
|
||||
case flags & 0x3f
|
||||
when 0
|
||||
block
|
||||
# when 1
|
||||
# LZMA
|
||||
when 2, 3
|
||||
LZ4.raw_decode(block, max_dest_size)
|
||||
# when 4
|
||||
# LZHMA
|
||||
else
|
||||
warn("Unknown compression flag: #{@flags}")
|
||||
block
|
||||
end
|
||||
end
|
||||
# Load AssetBundle from binary string
|
||||
# @param [String] bin binary data
|
||||
# @return [Mikunyan::AssetBundle] deserialized AssetBundle object
|
||||
def self.load(bin)
|
||||
r = AssetBundle.new
|
||||
r.send(:load, bin)
|
||||
r
|
||||
end
|
||||
|
||||
# Load AssetBundle from file
|
||||
# @param [String] file file name
|
||||
# @return [Mikunyan::AssetBundle] deserialized AssetBundle object
|
||||
def self.file(file)
|
||||
AssetBundle.load(File.binread(file))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load(bin)
|
||||
br = BinaryReader.new(bin)
|
||||
@signature = br.cstr
|
||||
@format = br.i32
|
||||
@unity_version = br.cstr
|
||||
@generator_version = br.cstr
|
||||
|
||||
case @signature
|
||||
when 'UnityRaw'
|
||||
load_unity_raw(br, false)
|
||||
when 'UnityFS'
|
||||
load_unity_fs(br)
|
||||
when 'UnityWeb'
|
||||
if @format <= 3
|
||||
load_unity_raw(br, true)
|
||||
else
|
||||
load_unity_fs(br)
|
||||
end
|
||||
else
|
||||
raise("Unknown signature: #{@signature}")
|
||||
end
|
||||
end
|
||||
|
||||
def load_unity_raw(br, compressed)
|
||||
@assets = []
|
||||
|
||||
file_size = br.i32u
|
||||
header_size = br.i32u
|
||||
|
||||
if compressed
|
||||
br.adv(4)
|
||||
block_count = br.i32u
|
||||
blocks = block_count.times.map{{ c: br.i32u, u: br.i32u }}
|
||||
br.jmp(header_size)
|
||||
data = ''
|
||||
blocks.each{|b| data << LZMA.decode(br.read(b[:c]))}
|
||||
br = BinaryReader.new(data)
|
||||
else
|
||||
br.jmp(header_size)
|
||||
end
|
||||
|
||||
asset_count = br.i32u
|
||||
asset_count.times do
|
||||
asset_pos = br.pos
|
||||
asset_name = br.cstr
|
||||
asset_header_size = br.i32u
|
||||
asset_size = br.i32u
|
||||
br.jmp(asset_pos + asset_header_size - 4)
|
||||
asset_data = br.read(asset_size)
|
||||
asset = Asset.load(asset_data, asset_name)
|
||||
@assets << asset
|
||||
end
|
||||
end
|
||||
|
||||
def load_unity_fs(br)
|
||||
@assets = []
|
||||
|
||||
file_size = br.i64u
|
||||
ci_block_size = br.i32u
|
||||
ui_block_size = br.i32u
|
||||
flags = br.i32u
|
||||
|
||||
head = BinaryReader.new(uncompress(flags & 0x80 == 0 ? br.read(ci_block_size) : br.read_abs(ci_block_size, file_size - ci_block_size), ui_block_size, flags))
|
||||
guid = head.read(16)
|
||||
|
||||
blocks = []
|
||||
block_count = head.i32u
|
||||
block_count.times{blocks << { u: head.i32u, c: head.i32u, f: head.i16u }}
|
||||
|
||||
asset_blocks = []
|
||||
asset_count = head.i32u
|
||||
asset_count.times{asset_blocks << { offset: head.i64u, size: head.i64u, status: head.i32, name: head.cstr }}
|
||||
|
||||
raw_data = ''
|
||||
blocks.each{|b| raw_data << uncompress(br.read(b[:c]), b[:u], b[:f])}
|
||||
|
||||
asset_blocks.each do |b|
|
||||
next if b[:name].end_with?('.resS')
|
||||
res_s = asset_blocks.find{|e| e[:name] == "#{b[:name]}.resS"}
|
||||
asset = Asset.load(raw_data.byteslice(b[:offset], b[:size]), b[:name], res_s && raw_data.byteslice(res_s[:offset], res_s[:size]))
|
||||
@assets << asset
|
||||
end
|
||||
end
|
||||
|
||||
def uncompress(block, max_dest_size, flags)
|
||||
case flags & 0x3f
|
||||
when 0
|
||||
block
|
||||
# when 1
|
||||
# LZMA
|
||||
when 2, 3
|
||||
LZ4.raw_decode(block, max_dest_size)
|
||||
# when 4
|
||||
# LZHMA
|
||||
else
|
||||
warn("Unknown compression flag: #{@flags}")
|
||||
block
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+156
-154
@@ -1,160 +1,162 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bin_utils'
|
||||
|
||||
module Mikunyan
|
||||
# Class for manipulating binary string
|
||||
# @attr [Symbol] endian endianness
|
||||
# @attr [Integer] pos position
|
||||
# @attr [Integer] length data size
|
||||
class BinaryReader
|
||||
attr_accessor :endian, :pos, :length
|
||||
# Class for manipulating binary string
|
||||
# @attr [Symbol] endian endianness
|
||||
# @attr [Integer] pos position
|
||||
# @attr [Integer] length data size
|
||||
class BinaryReader
|
||||
attr_accessor :endian, :pos, :length
|
||||
|
||||
# Constructor
|
||||
# @param [String] data binary string
|
||||
# @param [Symbol] endian endianness
|
||||
def initialize(data, endian = :big)
|
||||
@data = data
|
||||
@pos = 0
|
||||
@length = data.bytesize
|
||||
@endian = endian
|
||||
end
|
||||
|
||||
# Returns whether little endian or not
|
||||
# @return [Boolean]
|
||||
def little?
|
||||
@endian == :little
|
||||
end
|
||||
|
||||
# Jump to given position
|
||||
# @param [Integer] pos position
|
||||
def jmp(pos=0)
|
||||
@pos = pos
|
||||
end
|
||||
|
||||
# Advance position given size
|
||||
# @param [Integer] size size
|
||||
def adv(size=0)
|
||||
@pos += size
|
||||
end
|
||||
|
||||
# Round up position to multiple of given size
|
||||
# @param [Integer] size size
|
||||
def align(size)
|
||||
@pos = (@pos + size - 1) / size * size
|
||||
end
|
||||
|
||||
# Read given size of binary string and seek
|
||||
# @param [Integer] size size
|
||||
# @return [String] data
|
||||
def read(size)
|
||||
data = @data.byteslice(@pos, size)
|
||||
@pos += size
|
||||
data
|
||||
end
|
||||
|
||||
# Read given size of binary string from specified position. This method does not seek.
|
||||
# @param [Integer] size size
|
||||
# @param [Integer] pos position
|
||||
# @return [String] data
|
||||
def read_abs(size, pos)
|
||||
@data.byteslice(pos, size)
|
||||
end
|
||||
|
||||
# Read string until null character
|
||||
# @return [String] string
|
||||
def cstr
|
||||
r = @data.unpack("@#{pos}Z*")[0]
|
||||
@pos += r.bytesize + 1
|
||||
r
|
||||
end
|
||||
|
||||
# Read 8bit signed integer
|
||||
def i8
|
||||
i8s
|
||||
end
|
||||
|
||||
# Read 8bit signed integer
|
||||
def i8s
|
||||
r = BinUtils.get_sint8(@data, @pos)
|
||||
@pos += 1
|
||||
r
|
||||
end
|
||||
|
||||
# Read 8bit unsigned integer
|
||||
def i8u
|
||||
r = BinUtils.get_int8(@data, @pos)
|
||||
@pos += 1
|
||||
r
|
||||
end
|
||||
|
||||
# Read 16bit signed integer
|
||||
def i16
|
||||
i16s
|
||||
end
|
||||
|
||||
# Read 16bit signed integer
|
||||
def i16s
|
||||
r = little? ? BinUtils.get_sint16_le(@data, @pos) : BinUtils.get_sint16_be(@data, @pos)
|
||||
@pos += 2
|
||||
r
|
||||
end
|
||||
|
||||
# Read 16bit unsigned integer
|
||||
def i16u
|
||||
r = little? ? BinUtils.get_int16_le(@data, @pos) : BinUtils.get_int16_be(@data, @pos)
|
||||
@pos += 2
|
||||
r
|
||||
end
|
||||
|
||||
# Read 32bit signed integer
|
||||
def i32
|
||||
i32s
|
||||
end
|
||||
|
||||
# Read 32bit signed integer
|
||||
def i32s
|
||||
r = little? ? BinUtils.get_sint32_le(@data, @pos) : BinUtils.get_sint32_be(@data, @pos)
|
||||
@pos += 4
|
||||
r
|
||||
end
|
||||
|
||||
# Read 32bit unsigned integer
|
||||
def i32u
|
||||
r = little? ? BinUtils.get_int32_le(@data, @pos) : BinUtils.get_int32_be(@data, @pos)
|
||||
@pos += 4
|
||||
r
|
||||
end
|
||||
|
||||
# Read 64bit signed integer
|
||||
def i64
|
||||
i64s
|
||||
end
|
||||
|
||||
# Read 64bit signed integer
|
||||
def i64s
|
||||
r = little? ? BinUtils.get_sint64_le(@data, @pos) : BinUtils.get_sint64_be(@data, @pos)
|
||||
@pos += 8
|
||||
r
|
||||
end
|
||||
|
||||
# Read 64bit unsigned integer
|
||||
def i64u
|
||||
r = little? ? BinUtils.get_int64_le(@data, @pos) : BinUtils.get_int64_be(@data, @pos)
|
||||
@pos += 8
|
||||
r
|
||||
end
|
||||
|
||||
# Read 32bit floating point value
|
||||
def float
|
||||
r = little? ? @data.byteslice(@pos, 4).unpack('e')[0] : @data.byteslice(@pos, 4).unpack('g')[0]
|
||||
@pos += 4
|
||||
r
|
||||
end
|
||||
|
||||
# Read 64bit floating point value
|
||||
def double
|
||||
r = little? ? @data.byteslice(@pos, 8).unpack('E')[0] : @data.byteslice(@pos, 8).unpack('G')[0]
|
||||
@pos += 8
|
||||
r
|
||||
end
|
||||
# Constructor
|
||||
# @param [String] data binary string
|
||||
# @param [Symbol] endian endianness
|
||||
def initialize(data, endian = :big)
|
||||
@data = data
|
||||
@pos = 0
|
||||
@length = data.bytesize
|
||||
@endian = endian
|
||||
end
|
||||
|
||||
# Returns whether little endian or not
|
||||
# @return [Boolean]
|
||||
def little?
|
||||
@endian == :little
|
||||
end
|
||||
|
||||
# Jump to given position
|
||||
# @param [Integer] pos position
|
||||
def jmp(pos = 0)
|
||||
@pos = pos
|
||||
end
|
||||
|
||||
# Advance position given size
|
||||
# @param [Integer] size size
|
||||
def adv(size = 0)
|
||||
@pos += size
|
||||
end
|
||||
|
||||
# Round up position to multiple of given size
|
||||
# @param [Integer] size size
|
||||
def align(size)
|
||||
@pos = (@pos + size - 1) / size * size
|
||||
end
|
||||
|
||||
# Read given size of binary string and seek
|
||||
# @param [Integer] size size
|
||||
# @return [String] data
|
||||
def read(size)
|
||||
data = @data.byteslice(@pos, size)
|
||||
@pos += size
|
||||
data
|
||||
end
|
||||
|
||||
# Read given size of binary string from specified position. This method does not seek.
|
||||
# @param [Integer] size size
|
||||
# @param [Integer] pos position
|
||||
# @return [String] data
|
||||
def read_abs(size, pos)
|
||||
@data.byteslice(pos, size)
|
||||
end
|
||||
|
||||
# Read string until null character
|
||||
# @return [String] string
|
||||
def cstr
|
||||
r = @data.unpack1("@#{pos}Z*")
|
||||
@pos += r.bytesize + 1
|
||||
r
|
||||
end
|
||||
|
||||
# Read 8bit signed integer
|
||||
def i8
|
||||
i8s
|
||||
end
|
||||
|
||||
# Read 8bit signed integer
|
||||
def i8s
|
||||
r = BinUtils.get_sint8(@data, @pos)
|
||||
@pos += 1
|
||||
r
|
||||
end
|
||||
|
||||
# Read 8bit unsigned integer
|
||||
def i8u
|
||||
r = BinUtils.get_int8(@data, @pos)
|
||||
@pos += 1
|
||||
r
|
||||
end
|
||||
|
||||
# Read 16bit signed integer
|
||||
def i16
|
||||
i16s
|
||||
end
|
||||
|
||||
# Read 16bit signed integer
|
||||
def i16s
|
||||
r = little? ? BinUtils.get_sint16_le(@data, @pos) : BinUtils.get_sint16_be(@data, @pos)
|
||||
@pos += 2
|
||||
r
|
||||
end
|
||||
|
||||
# Read 16bit unsigned integer
|
||||
def i16u
|
||||
r = little? ? BinUtils.get_int16_le(@data, @pos) : BinUtils.get_int16_be(@data, @pos)
|
||||
@pos += 2
|
||||
r
|
||||
end
|
||||
|
||||
# Read 32bit signed integer
|
||||
def i32
|
||||
i32s
|
||||
end
|
||||
|
||||
# Read 32bit signed integer
|
||||
def i32s
|
||||
r = little? ? BinUtils.get_sint32_le(@data, @pos) : BinUtils.get_sint32_be(@data, @pos)
|
||||
@pos += 4
|
||||
r
|
||||
end
|
||||
|
||||
# Read 32bit unsigned integer
|
||||
def i32u
|
||||
r = little? ? BinUtils.get_int32_le(@data, @pos) : BinUtils.get_int32_be(@data, @pos)
|
||||
@pos += 4
|
||||
r
|
||||
end
|
||||
|
||||
# Read 64bit signed integer
|
||||
def i64
|
||||
i64s
|
||||
end
|
||||
|
||||
# Read 64bit signed integer
|
||||
def i64s
|
||||
r = little? ? BinUtils.get_sint64_le(@data, @pos) : BinUtils.get_sint64_be(@data, @pos)
|
||||
@pos += 8
|
||||
r
|
||||
end
|
||||
|
||||
# Read 64bit unsigned integer
|
||||
def i64u
|
||||
r = little? ? BinUtils.get_int64_le(@data, @pos) : BinUtils.get_int64_be(@data, @pos)
|
||||
@pos += 8
|
||||
r
|
||||
end
|
||||
|
||||
# Read 32bit floating point value
|
||||
def float
|
||||
r = little? ? @data.byteslice(@pos, 4).unpack1('e') : @data.byteslice(@pos, 4).unpack1('g')
|
||||
@pos += 4
|
||||
r
|
||||
end
|
||||
|
||||
# Read 64bit floating point value
|
||||
def double
|
||||
r = little? ? @data.byteslice(@pos, 8).unpack1('E') : @data.byteslice(@pos, 8).unpack1('G')
|
||||
@pos += 8
|
||||
r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+345
-342
@@ -1,344 +1,347 @@
|
||||
module Mikunyan
|
||||
private
|
||||
STRING_TABLE = {
|
||||
0=>'AABB',
|
||||
5=>'AnimationClip',
|
||||
19=>'AnimationCurve',
|
||||
34=>'AnimationState',
|
||||
49=>'Array',
|
||||
55=>'Base',
|
||||
60=>'BitField',
|
||||
69=>'bitset',
|
||||
76=>'bool',
|
||||
81=>'char',
|
||||
86=>'ColorRGBA',
|
||||
96=>'Component',
|
||||
106=>'data',
|
||||
111=>'deque',
|
||||
117=>'double',
|
||||
124=>'dynamic_array',
|
||||
138=>'FastPropertyName',
|
||||
155=>'first',
|
||||
161=>'float',
|
||||
167=>'Font',
|
||||
172=>'GameObject',
|
||||
183=>'Generic Mono',
|
||||
196=>'GradientNEW',
|
||||
208=>'GUID',
|
||||
213=>'GUIStyle',
|
||||
222=>'int',
|
||||
226=>'list',
|
||||
231=>'long long',
|
||||
241=>'map',
|
||||
245=>'Matrix4x4f',
|
||||
256=>'MdFour',
|
||||
263=>'MonoBehaviour',
|
||||
277=>'MonoScript',
|
||||
288=>'m_ByteSize',
|
||||
299=>'m_Curve',
|
||||
307=>'m_EditorClassIdentifier',
|
||||
331=>'m_EditorHideFlags',
|
||||
349=>'m_Enabled',
|
||||
359=>'m_ExtensionPtr',
|
||||
374=>'m_GameObject',
|
||||
387=>'m_Index',
|
||||
395=>'m_IsArray',
|
||||
405=>'m_IsStatic',
|
||||
416=>'m_MetaFlag',
|
||||
427=>'m_Name',
|
||||
434=>'m_ObjectHideFlags',
|
||||
452=>'m_PrefabInternal',
|
||||
469=>'m_PrefabParentObject',
|
||||
490=>'m_Script',
|
||||
499=>'m_StaticEditorFlags',
|
||||
519=>'m_Type',
|
||||
526=>'m_Version',
|
||||
536=>'Object',
|
||||
543=>'pair',
|
||||
548=>'PPtr<Component>',
|
||||
564=>'PPtr<GameObject>',
|
||||
581=>'PPtr<Material>',
|
||||
596=>'PPtr<MonoBehaviour>',
|
||||
616=>'PPtr<MonoScript>',
|
||||
633=>'PPtr<Object>',
|
||||
646=>'PPtr<Prefab>',
|
||||
659=>'PPtr<Sprite>',
|
||||
672=>'PPtr<TextAsset>',
|
||||
688=>'PPtr<Texture>',
|
||||
702=>'PPtr<Texture2D>',
|
||||
718=>'PPtr<Transform>',
|
||||
734=>'Prefab',
|
||||
741=>'Quaternionf',
|
||||
753=>'Rectf',
|
||||
759=>'RectInt',
|
||||
767=>'RectOffset',
|
||||
778=>'second',
|
||||
785=>'set',
|
||||
789=>'short',
|
||||
795=>'size',
|
||||
800=>'SInt16',
|
||||
807=>'SInt32',
|
||||
814=>'SInt64',
|
||||
821=>'SInt8',
|
||||
827=>'staticvector',
|
||||
840=>'string',
|
||||
847=>'TextAsset',
|
||||
857=>'TextMesh',
|
||||
866=>'Texture',
|
||||
874=>'Texture2D',
|
||||
884=>'Transform',
|
||||
894=>'TypelessData',
|
||||
907=>'UInt16',
|
||||
914=>'UInt32',
|
||||
921=>'UInt64',
|
||||
928=>'UInt8',
|
||||
934=>'unsigned int',
|
||||
947=>'unsigned long long',
|
||||
966=>'unsigned short',
|
||||
981=>'vector',
|
||||
988=>'Vector2f',
|
||||
997=>'Vector3f',
|
||||
1006=>'Vector4f',
|
||||
1015=>'m_ScriptingClassIdentifier',
|
||||
1042=>'Gradient'
|
||||
}
|
||||
# frozen_string_literal: true
|
||||
|
||||
CLASS_ID = {
|
||||
1=>'GameObject',
|
||||
2=>'Component',
|
||||
3=>'LevelGameManager',
|
||||
4=>'Transform',
|
||||
5=>'TimeManager',
|
||||
6=>'GlobalGameManager',
|
||||
8=>'Behaviour',
|
||||
9=>'GameManager',
|
||||
11=>'AudioManager',
|
||||
12=>'ParticleAnimator',
|
||||
13=>'InputManager',
|
||||
15=>'EllipsoidParticleEmitter',
|
||||
17=>'Pipeline',
|
||||
18=>'EditorExtension',
|
||||
19=>'Physics2DSettings',
|
||||
20=>'Camera',
|
||||
21=>'Material',
|
||||
23=>'MeshRenderer',
|
||||
25=>'Renderer',
|
||||
26=>'ParticleRenderer',
|
||||
27=>'Texture',
|
||||
28=>'Texture2D',
|
||||
29=>'SceneSettings',
|
||||
30=>'GraphicsSettings',
|
||||
33=>'MeshFilter',
|
||||
41=>'OcclusionPortal',
|
||||
43=>'Mesh',
|
||||
45=>'Skybox',
|
||||
47=>'QualitySettings',
|
||||
48=>'Shader',
|
||||
49=>'TextAsset',
|
||||
50=>'Rigidbody2D',
|
||||
51=>'Physics2DManager',
|
||||
53=>'Collider2D',
|
||||
54=>'Rigidbody',
|
||||
55=>'PhysicsManager',
|
||||
56=>'Collider',
|
||||
57=>'Joint',
|
||||
58=>'CircleCollider2D',
|
||||
59=>'HingeJoint',
|
||||
60=>'PolygonCollider2D',
|
||||
61=>'BoxCollider2D',
|
||||
62=>'PhysicsMaterial2D',
|
||||
64=>'MeshCollider',
|
||||
65=>'BoxCollider',
|
||||
66=>'SpriteCollider2D',
|
||||
68=>'EdgeCollider2D',
|
||||
72=>'ComputeShader',
|
||||
74=>'AnimationClip',
|
||||
75=>'ConstantForce',
|
||||
76=>'WorldParticleCollider',
|
||||
78=>'TagManager',
|
||||
81=>'AudioListener',
|
||||
82=>'AudioSource',
|
||||
83=>'AudioClip',
|
||||
84=>'RenderTexture',
|
||||
87=>'MeshParticleEmitter',
|
||||
88=>'ParticleEmitter',
|
||||
89=>'Cubemap',
|
||||
90=>'Avatar',
|
||||
91=>'AnimatorController',
|
||||
92=>'GUILayer',
|
||||
93=>'RuntimeAnimatorController',
|
||||
94=>'ScriptMapper',
|
||||
95=>'Animator',
|
||||
96=>'TrailRenderer',
|
||||
98=>'DelayedCallManager',
|
||||
102=>'TextMesh',
|
||||
104=>'RenderSettings',
|
||||
108=>'Light',
|
||||
109=>'CGProgram',
|
||||
110=>'BaseAnimationTrack',
|
||||
111=>'Animation',
|
||||
114=>'MonoBehaviour',
|
||||
115=>'MonoScript',
|
||||
116=>'MonoManager',
|
||||
117=>'Texture3D',
|
||||
118=>'NewAnimationTrack',
|
||||
119=>'Projector',
|
||||
120=>'LineRenderer',
|
||||
121=>'Flare',
|
||||
122=>'Halo',
|
||||
123=>'LensFlare',
|
||||
124=>'FlareLayer',
|
||||
125=>'HaloLayer',
|
||||
126=>'NavMeshAreas',
|
||||
127=>'HaloManager',
|
||||
128=>'Font',
|
||||
129=>'PlayerSettings',
|
||||
130=>'NamedObject',
|
||||
131=>'GUITexture',
|
||||
132=>'GUIText',
|
||||
133=>'GUIElement',
|
||||
134=>'PhysicMaterial',
|
||||
135=>'SphereCollider',
|
||||
136=>'CapsuleCollider',
|
||||
137=>'SkinnedMeshRenderer',
|
||||
138=>'FixedJoint',
|
||||
140=>'RaycastCollider',
|
||||
141=>'BuildSettings',
|
||||
142=>'AssetBundle',
|
||||
143=>'CharacterController',
|
||||
144=>'CharacterJoint',
|
||||
145=>'SpringJoint',
|
||||
146=>'WheelCollider',
|
||||
147=>'ResourceManager',
|
||||
148=>'NetworkView',
|
||||
149=>'NetworkManager',
|
||||
150=>'PreloadData',
|
||||
152=>'MovieTexture',
|
||||
153=>'ConfigurableJoint',
|
||||
154=>'TerrainCollider',
|
||||
155=>'MasterServerInterface',
|
||||
156=>'TerrainData',
|
||||
157=>'LightmapSettings',
|
||||
158=>'WebCamTexture',
|
||||
159=>'EditorSettings',
|
||||
160=>'InteractiveCloth',
|
||||
161=>'ClothRenderer',
|
||||
162=>'EditorUserSettings',
|
||||
163=>'SkinnedCloth',
|
||||
164=>'AudioReverbFilter',
|
||||
165=>'AudioHighPassFilter',
|
||||
166=>'AudioChorusFilter',
|
||||
167=>'AudioReverbZone',
|
||||
168=>'AudioEchoFilter',
|
||||
169=>'AudioLowPassFilter',
|
||||
170=>'AudioDistortionFilter',
|
||||
171=>'SparseTexture',
|
||||
180=>'AudioBehaviour',
|
||||
181=>'AudioFilter',
|
||||
182=>'WindZone',
|
||||
183=>'Cloth',
|
||||
184=>'SubstanceArchive',
|
||||
185=>'ProceduralMaterial',
|
||||
186=>'ProceduralTexture',
|
||||
191=>'OffMeshLink',
|
||||
192=>'OcclusionArea',
|
||||
193=>'Tree',
|
||||
194=>'NavMeshObsolete',
|
||||
195=>'NavMeshAgent',
|
||||
196=>'NavMeshSettings',
|
||||
197=>'LightProbesLegacy',
|
||||
198=>'ParticleSystem',
|
||||
199=>'ParticleSystemRenderer',
|
||||
200=>'ShaderVariantCollection',
|
||||
205=>'LODGroup',
|
||||
206=>'BlendTree',
|
||||
207=>'Motion',
|
||||
208=>'NavMeshObstacle',
|
||||
210=>'TerrainInstance',
|
||||
212=>'SpriteRenderer',
|
||||
213=>'Sprite',
|
||||
214=>'CachedSpriteAtlas',
|
||||
215=>'ReflectionProbe',
|
||||
216=>'ReflectionProbes',
|
||||
218=>'Terrain',
|
||||
220=>'LightProbeGroup',
|
||||
221=>'AnimatorOverrideController',
|
||||
222=>'CanvasRenderer',
|
||||
223=>'Canvas',
|
||||
224=>'RectTransform',
|
||||
225=>'CanvasGroup',
|
||||
226=>'BillboardAsset',
|
||||
227=>'BillboardRenderer',
|
||||
228=>'SpeedTreeWindAsset',
|
||||
229=>'AnchoredJoint2D',
|
||||
230=>'Joint2D',
|
||||
231=>'SpringJoint2D',
|
||||
232=>'DistanceJoint2D',
|
||||
233=>'HingeJoint2D',
|
||||
234=>'SliderJoint2D',
|
||||
235=>'WheelJoint2D',
|
||||
238=>'NavMeshData',
|
||||
240=>'AudioMixer',
|
||||
241=>'AudioMixerController',
|
||||
243=>'AudioMixerGroupController',
|
||||
244=>'AudioMixerEffectController',
|
||||
245=>'AudioMixerSnapshotController',
|
||||
246=>'PhysicsUpdateBehaviour2D',
|
||||
247=>'ConstantForce2D',
|
||||
248=>'Effector2D',
|
||||
249=>'AreaEffector2D',
|
||||
250=>'PointEffector2D',
|
||||
251=>'PlatformEffector2D',
|
||||
252=>'SurfaceEffector2D',
|
||||
258=>'LightProbes',
|
||||
271=>'SampleClip',
|
||||
272=>'AudioMixerSnapshot',
|
||||
273=>'AudioMixerGroup',
|
||||
290=>'AssetBundleManifest',
|
||||
1001=>'Prefab',
|
||||
1002=>'EditorExtensionImpl',
|
||||
1003=>'AssetImporter',
|
||||
1004=>'AssetDatabase',
|
||||
1005=>'Mesh3DSImporter',
|
||||
1006=>'TextureImporter',
|
||||
1007=>'ShaderImporter',
|
||||
1008=>'ComputeShaderImporter',
|
||||
1011=>'AvatarMask',
|
||||
1020=>'AudioImporter',
|
||||
1026=>'HierarchyState',
|
||||
1027=>'GUIDSerializer',
|
||||
1028=>'AssetMetaData',
|
||||
1029=>'DefaultAsset',
|
||||
1030=>'DefaultImporter',
|
||||
1031=>'TextScriptImporter',
|
||||
1032=>'SceneAsset',
|
||||
1034=>'NativeFormatImporter',
|
||||
1035=>'MonoImporter',
|
||||
1037=>'AssetServerCache',
|
||||
1038=>'LibraryAssetImporter',
|
||||
1040=>'ModelImporter',
|
||||
1041=>'FBXImporter',
|
||||
1042=>'TrueTypeFontImporter',
|
||||
1044=>'MovieImporter',
|
||||
1045=>'EditorBuildSettings',
|
||||
1046=>'DDSImporter',
|
||||
1048=>'InspectorExpandedState',
|
||||
1049=>'AnnotationManager',
|
||||
1050=>'PluginImporter',
|
||||
1051=>'EditorUserBuildSettings',
|
||||
1052=>'PVRImporter',
|
||||
1053=>'ASTCImporter',
|
||||
1054=>'KTXImporter',
|
||||
1101=>'AnimatorStateTransition',
|
||||
1102=>'AnimatorState',
|
||||
1105=>'HumanTemplate',
|
||||
1107=>'AnimatorStateMachine',
|
||||
1108=>'PreviewAssetType',
|
||||
1109=>'AnimatorTransition',
|
||||
1110=>'SpeedTreeImporter',
|
||||
1111=>'AnimatorTransitionBase',
|
||||
1112=>'SubstanceImporter',
|
||||
1113=>'LightmapParameters',
|
||||
1120=>'LightmapSnapshot'
|
||||
}
|
||||
module Mikunyan
|
||||
private
|
||||
|
||||
STRING_TABLE = {
|
||||
0 => 'AABB',
|
||||
5 => 'AnimationClip',
|
||||
19 => 'AnimationCurve',
|
||||
34 => 'AnimationState',
|
||||
49 => 'Array',
|
||||
55 => 'Base',
|
||||
60 => 'BitField',
|
||||
69 => 'bitset',
|
||||
76 => 'bool',
|
||||
81 => 'char',
|
||||
86 => 'ColorRGBA',
|
||||
96 => 'Component',
|
||||
106 => 'data',
|
||||
111 => 'deque',
|
||||
117 => 'double',
|
||||
124 => 'dynamic_array',
|
||||
138 => 'FastPropertyName',
|
||||
155 => 'first',
|
||||
161 => 'float',
|
||||
167 => 'Font',
|
||||
172 => 'GameObject',
|
||||
183 => 'Generic Mono',
|
||||
196 => 'GradientNEW',
|
||||
208 => 'GUID',
|
||||
213 => 'GUIStyle',
|
||||
222 => 'int',
|
||||
226 => 'list',
|
||||
231 => 'long long',
|
||||
241 => 'map',
|
||||
245 => 'Matrix4x4f',
|
||||
256 => 'MdFour',
|
||||
263 => 'MonoBehaviour',
|
||||
277 => 'MonoScript',
|
||||
288 => 'm_ByteSize',
|
||||
299 => 'm_Curve',
|
||||
307 => 'm_EditorClassIdentifier',
|
||||
331 => 'm_EditorHideFlags',
|
||||
349 => 'm_Enabled',
|
||||
359 => 'm_ExtensionPtr',
|
||||
374 => 'm_GameObject',
|
||||
387 => 'm_Index',
|
||||
395 => 'm_IsArray',
|
||||
405 => 'm_IsStatic',
|
||||
416 => 'm_MetaFlag',
|
||||
427 => 'm_Name',
|
||||
434 => 'm_ObjectHideFlags',
|
||||
452 => 'm_PrefabInternal',
|
||||
469 => 'm_PrefabParentObject',
|
||||
490 => 'm_Script',
|
||||
499 => 'm_StaticEditorFlags',
|
||||
519 => 'm_Type',
|
||||
526 => 'm_Version',
|
||||
536 => 'Object',
|
||||
543 => 'pair',
|
||||
548 => 'PPtr<Component>',
|
||||
564 => 'PPtr<GameObject>',
|
||||
581 => 'PPtr<Material>',
|
||||
596 => 'PPtr<MonoBehaviour>',
|
||||
616 => 'PPtr<MonoScript>',
|
||||
633 => 'PPtr<Object>',
|
||||
646 => 'PPtr<Prefab>',
|
||||
659 => 'PPtr<Sprite>',
|
||||
672 => 'PPtr<TextAsset>',
|
||||
688 => 'PPtr<Texture>',
|
||||
702 => 'PPtr<Texture2D>',
|
||||
718 => 'PPtr<Transform>',
|
||||
734 => 'Prefab',
|
||||
741 => 'Quaternionf',
|
||||
753 => 'Rectf',
|
||||
759 => 'RectInt',
|
||||
767 => 'RectOffset',
|
||||
778 => 'second',
|
||||
785 => 'set',
|
||||
789 => 'short',
|
||||
795 => 'size',
|
||||
800 => 'SInt16',
|
||||
807 => 'SInt32',
|
||||
814 => 'SInt64',
|
||||
821 => 'SInt8',
|
||||
827 => 'staticvector',
|
||||
840 => 'string',
|
||||
847 => 'TextAsset',
|
||||
857 => 'TextMesh',
|
||||
866 => 'Texture',
|
||||
874 => 'Texture2D',
|
||||
884 => 'Transform',
|
||||
894 => 'TypelessData',
|
||||
907 => 'UInt16',
|
||||
914 => 'UInt32',
|
||||
921 => 'UInt64',
|
||||
928 => 'UInt8',
|
||||
934 => 'unsigned int',
|
||||
947 => 'unsigned long long',
|
||||
966 => 'unsigned short',
|
||||
981 => 'vector',
|
||||
988 => 'Vector2f',
|
||||
997 => 'Vector3f',
|
||||
1006 => 'Vector4f',
|
||||
1015 => 'm_ScriptingClassIdentifier',
|
||||
1042 => 'Gradient'
|
||||
}.freeze
|
||||
|
||||
CLASS_ID = {
|
||||
1 => 'GameObject',
|
||||
2 => 'Component',
|
||||
3 => 'LevelGameManager',
|
||||
4 => 'Transform',
|
||||
5 => 'TimeManager',
|
||||
6 => 'GlobalGameManager',
|
||||
8 => 'Behaviour',
|
||||
9 => 'GameManager',
|
||||
11 => 'AudioManager',
|
||||
12 => 'ParticleAnimator',
|
||||
13 => 'InputManager',
|
||||
15 => 'EllipsoidParticleEmitter',
|
||||
17 => 'Pipeline',
|
||||
18 => 'EditorExtension',
|
||||
19 => 'Physics2DSettings',
|
||||
20 => 'Camera',
|
||||
21 => 'Material',
|
||||
23 => 'MeshRenderer',
|
||||
25 => 'Renderer',
|
||||
26 => 'ParticleRenderer',
|
||||
27 => 'Texture',
|
||||
28 => 'Texture2D',
|
||||
29 => 'SceneSettings',
|
||||
30 => 'GraphicsSettings',
|
||||
33 => 'MeshFilter',
|
||||
41 => 'OcclusionPortal',
|
||||
43 => 'Mesh',
|
||||
45 => 'Skybox',
|
||||
47 => 'QualitySettings',
|
||||
48 => 'Shader',
|
||||
49 => 'TextAsset',
|
||||
50 => 'Rigidbody2D',
|
||||
51 => 'Physics2DManager',
|
||||
53 => 'Collider2D',
|
||||
54 => 'Rigidbody',
|
||||
55 => 'PhysicsManager',
|
||||
56 => 'Collider',
|
||||
57 => 'Joint',
|
||||
58 => 'CircleCollider2D',
|
||||
59 => 'HingeJoint',
|
||||
60 => 'PolygonCollider2D',
|
||||
61 => 'BoxCollider2D',
|
||||
62 => 'PhysicsMaterial2D',
|
||||
64 => 'MeshCollider',
|
||||
65 => 'BoxCollider',
|
||||
66 => 'SpriteCollider2D',
|
||||
68 => 'EdgeCollider2D',
|
||||
72 => 'ComputeShader',
|
||||
74 => 'AnimationClip',
|
||||
75 => 'ConstantForce',
|
||||
76 => 'WorldParticleCollider',
|
||||
78 => 'TagManager',
|
||||
81 => 'AudioListener',
|
||||
82 => 'AudioSource',
|
||||
83 => 'AudioClip',
|
||||
84 => 'RenderTexture',
|
||||
87 => 'MeshParticleEmitter',
|
||||
88 => 'ParticleEmitter',
|
||||
89 => 'Cubemap',
|
||||
90 => 'Avatar',
|
||||
91 => 'AnimatorController',
|
||||
92 => 'GUILayer',
|
||||
93 => 'RuntimeAnimatorController',
|
||||
94 => 'ScriptMapper',
|
||||
95 => 'Animator',
|
||||
96 => 'TrailRenderer',
|
||||
98 => 'DelayedCallManager',
|
||||
102 => 'TextMesh',
|
||||
104 => 'RenderSettings',
|
||||
108 => 'Light',
|
||||
109 => 'CGProgram',
|
||||
110 => 'BaseAnimationTrack',
|
||||
111 => 'Animation',
|
||||
114 => 'MonoBehaviour',
|
||||
115 => 'MonoScript',
|
||||
116 => 'MonoManager',
|
||||
117 => 'Texture3D',
|
||||
118 => 'NewAnimationTrack',
|
||||
119 => 'Projector',
|
||||
120 => 'LineRenderer',
|
||||
121 => 'Flare',
|
||||
122 => 'Halo',
|
||||
123 => 'LensFlare',
|
||||
124 => 'FlareLayer',
|
||||
125 => 'HaloLayer',
|
||||
126 => 'NavMeshAreas',
|
||||
127 => 'HaloManager',
|
||||
128 => 'Font',
|
||||
129 => 'PlayerSettings',
|
||||
130 => 'NamedObject',
|
||||
131 => 'GUITexture',
|
||||
132 => 'GUIText',
|
||||
133 => 'GUIElement',
|
||||
134 => 'PhysicMaterial',
|
||||
135 => 'SphereCollider',
|
||||
136 => 'CapsuleCollider',
|
||||
137 => 'SkinnedMeshRenderer',
|
||||
138 => 'FixedJoint',
|
||||
140 => 'RaycastCollider',
|
||||
141 => 'BuildSettings',
|
||||
142 => 'AssetBundle',
|
||||
143 => 'CharacterController',
|
||||
144 => 'CharacterJoint',
|
||||
145 => 'SpringJoint',
|
||||
146 => 'WheelCollider',
|
||||
147 => 'ResourceManager',
|
||||
148 => 'NetworkView',
|
||||
149 => 'NetworkManager',
|
||||
150 => 'PreloadData',
|
||||
152 => 'MovieTexture',
|
||||
153 => 'ConfigurableJoint',
|
||||
154 => 'TerrainCollider',
|
||||
155 => 'MasterServerInterface',
|
||||
156 => 'TerrainData',
|
||||
157 => 'LightmapSettings',
|
||||
158 => 'WebCamTexture',
|
||||
159 => 'EditorSettings',
|
||||
160 => 'InteractiveCloth',
|
||||
161 => 'ClothRenderer',
|
||||
162 => 'EditorUserSettings',
|
||||
163 => 'SkinnedCloth',
|
||||
164 => 'AudioReverbFilter',
|
||||
165 => 'AudioHighPassFilter',
|
||||
166 => 'AudioChorusFilter',
|
||||
167 => 'AudioReverbZone',
|
||||
168 => 'AudioEchoFilter',
|
||||
169 => 'AudioLowPassFilter',
|
||||
170 => 'AudioDistortionFilter',
|
||||
171 => 'SparseTexture',
|
||||
180 => 'AudioBehaviour',
|
||||
181 => 'AudioFilter',
|
||||
182 => 'WindZone',
|
||||
183 => 'Cloth',
|
||||
184 => 'SubstanceArchive',
|
||||
185 => 'ProceduralMaterial',
|
||||
186 => 'ProceduralTexture',
|
||||
191 => 'OffMeshLink',
|
||||
192 => 'OcclusionArea',
|
||||
193 => 'Tree',
|
||||
194 => 'NavMeshObsolete',
|
||||
195 => 'NavMeshAgent',
|
||||
196 => 'NavMeshSettings',
|
||||
197 => 'LightProbesLegacy',
|
||||
198 => 'ParticleSystem',
|
||||
199 => 'ParticleSystemRenderer',
|
||||
200 => 'ShaderVariantCollection',
|
||||
205 => 'LODGroup',
|
||||
206 => 'BlendTree',
|
||||
207 => 'Motion',
|
||||
208 => 'NavMeshObstacle',
|
||||
210 => 'TerrainInstance',
|
||||
212 => 'SpriteRenderer',
|
||||
213 => 'Sprite',
|
||||
214 => 'CachedSpriteAtlas',
|
||||
215 => 'ReflectionProbe',
|
||||
216 => 'ReflectionProbes',
|
||||
218 => 'Terrain',
|
||||
220 => 'LightProbeGroup',
|
||||
221 => 'AnimatorOverrideController',
|
||||
222 => 'CanvasRenderer',
|
||||
223 => 'Canvas',
|
||||
224 => 'RectTransform',
|
||||
225 => 'CanvasGroup',
|
||||
226 => 'BillboardAsset',
|
||||
227 => 'BillboardRenderer',
|
||||
228 => 'SpeedTreeWindAsset',
|
||||
229 => 'AnchoredJoint2D',
|
||||
230 => 'Joint2D',
|
||||
231 => 'SpringJoint2D',
|
||||
232 => 'DistanceJoint2D',
|
||||
233 => 'HingeJoint2D',
|
||||
234 => 'SliderJoint2D',
|
||||
235 => 'WheelJoint2D',
|
||||
238 => 'NavMeshData',
|
||||
240 => 'AudioMixer',
|
||||
241 => 'AudioMixerController',
|
||||
243 => 'AudioMixerGroupController',
|
||||
244 => 'AudioMixerEffectController',
|
||||
245 => 'AudioMixerSnapshotController',
|
||||
246 => 'PhysicsUpdateBehaviour2D',
|
||||
247 => 'ConstantForce2D',
|
||||
248 => 'Effector2D',
|
||||
249 => 'AreaEffector2D',
|
||||
250 => 'PointEffector2D',
|
||||
251 => 'PlatformEffector2D',
|
||||
252 => 'SurfaceEffector2D',
|
||||
258 => 'LightProbes',
|
||||
271 => 'SampleClip',
|
||||
272 => 'AudioMixerSnapshot',
|
||||
273 => 'AudioMixerGroup',
|
||||
290 => 'AssetBundleManifest',
|
||||
1001 => 'Prefab',
|
||||
1002 => 'EditorExtensionImpl',
|
||||
1003 => 'AssetImporter',
|
||||
1004 => 'AssetDatabase',
|
||||
1005 => 'Mesh3DSImporter',
|
||||
1006 => 'TextureImporter',
|
||||
1007 => 'ShaderImporter',
|
||||
1008 => 'ComputeShaderImporter',
|
||||
1011 => 'AvatarMask',
|
||||
1020 => 'AudioImporter',
|
||||
1026 => 'HierarchyState',
|
||||
1027 => 'GUIDSerializer',
|
||||
1028 => 'AssetMetaData',
|
||||
1029 => 'DefaultAsset',
|
||||
1030 => 'DefaultImporter',
|
||||
1031 => 'TextScriptImporter',
|
||||
1032 => 'SceneAsset',
|
||||
1034 => 'NativeFormatImporter',
|
||||
1035 => 'MonoImporter',
|
||||
1037 => 'AssetServerCache',
|
||||
1038 => 'LibraryAssetImporter',
|
||||
1040 => 'ModelImporter',
|
||||
1041 => 'FBXImporter',
|
||||
1042 => 'TrueTypeFontImporter',
|
||||
1044 => 'MovieImporter',
|
||||
1045 => 'EditorBuildSettings',
|
||||
1046 => 'DDSImporter',
|
||||
1048 => 'InspectorExpandedState',
|
||||
1049 => 'AnnotationManager',
|
||||
1050 => 'PluginImporter',
|
||||
1051 => 'EditorUserBuildSettings',
|
||||
1052 => 'PVRImporter',
|
||||
1053 => 'ASTCImporter',
|
||||
1054 => 'KTXImporter',
|
||||
1101 => 'AnimatorStateTransition',
|
||||
1102 => 'AnimatorState',
|
||||
1105 => 'HumanTemplate',
|
||||
1107 => 'AnimatorStateMachine',
|
||||
1108 => 'PreviewAssetType',
|
||||
1109 => 'AnimatorTransition',
|
||||
1110 => 'SpeedTreeImporter',
|
||||
1111 => 'AnimatorTransitionBase',
|
||||
1112 => 'SubstanceImporter',
|
||||
1113 => 'LightmapParameters',
|
||||
1120 => 'LightmapSnapshot'
|
||||
}.freeze
|
||||
end
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'mikunyan/decoders/image_decoder'
|
||||
|
||||
module Mikunyan
|
||||
# Module for helper classes for decoding object
|
||||
module DecodeHelper
|
||||
end
|
||||
# Module for helper classes for decoding object
|
||||
module DecodeHelper
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,481 +1,483 @@
|
||||
begin; require 'oily_png'; rescue LoadError; require 'chunky_png'; end
|
||||
# frozen_string_literal: true
|
||||
|
||||
begin
|
||||
require 'oily_png'
|
||||
rescue LoadError
|
||||
require 'chunky_png'
|
||||
end
|
||||
require 'bin_utils'
|
||||
require 'mikunyan/decoders/native'
|
||||
|
||||
module Mikunyan
|
||||
# Class for image decoding tools
|
||||
class ImageDecoder
|
||||
# Decode image from Mikunyan::ObjectValue
|
||||
# @param [Mikunyan::ObjectValue] object object to decode
|
||||
# @return [ChunkyPNG::Image,nil] decoded image
|
||||
def self.decode_object(object)
|
||||
return nil unless object.class == ObjectValue
|
||||
# Class for image decoding tools
|
||||
class ImageDecoder
|
||||
# Decode image from Mikunyan::ObjectValue
|
||||
# @param [Mikunyan::ObjectValue] object object to decode
|
||||
# @return [ChunkyPNG::Image,nil] decoded image
|
||||
def self.decode_object(object)
|
||||
return nil unless object.class == ObjectValue
|
||||
|
||||
endian = object.endian
|
||||
width = object['m_Width']
|
||||
height = object['m_Height']
|
||||
bin = object['image data']
|
||||
fmt = object['m_TextureFormat']
|
||||
return nil unless width && height && bin && fmt
|
||||
endian = object.endian
|
||||
width = object['m_Width']
|
||||
height = object['m_Height']
|
||||
bin = object['image data']
|
||||
fmt = object['m_TextureFormat']
|
||||
return nil unless width && height && bin && fmt
|
||||
|
||||
width = width.value
|
||||
height = height.value
|
||||
bin = bin.value
|
||||
fmt = fmt.value
|
||||
width = width.value
|
||||
height = height.value
|
||||
bin = bin.value
|
||||
fmt = fmt.value
|
||||
|
||||
if bin.size == 0 && object['m_StreamData']
|
||||
bin = object['m_StreamData'].value
|
||||
return nil unless bin
|
||||
end
|
||||
if bin.empty? && object['m_StreamData']
|
||||
bin = object['m_StreamData'].value
|
||||
return nil unless bin
|
||||
end
|
||||
|
||||
case fmt
|
||||
when 1
|
||||
decode_a8(width, height, bin)
|
||||
when 2
|
||||
decode_argb4444(width, height, bin, endian)
|
||||
when 3
|
||||
decode_rgb24(width, height, bin)
|
||||
when 4
|
||||
decode_rgba32(width, height, bin)
|
||||
when 5
|
||||
decode_argb32(width, height, bin)
|
||||
when 7
|
||||
decode_rgb565(width, height, bin, endian)
|
||||
when 9
|
||||
decode_r16(width, height, bin)
|
||||
when 10
|
||||
decode_dxt1(width, height, bin)
|
||||
when 12
|
||||
decode_dxt5(width, height, bin)
|
||||
when 13
|
||||
decode_rgba4444(width, height, bin, endian)
|
||||
when 14
|
||||
decode_bgra32(width, height, bin)
|
||||
when 15
|
||||
decode_rhalf(width, height, bin, endian)
|
||||
when 16
|
||||
decode_rghalf(width, height, bin, endian)
|
||||
when 17
|
||||
decode_rgbahalf(width, height, bin, endian)
|
||||
when 18
|
||||
decode_rfloat(width, height, bin, endian)
|
||||
when 19
|
||||
decode_rgfloat(width, height, bin, endian)
|
||||
when 20
|
||||
decode_rgbafloat(width, height, bin, endian)
|
||||
when 22
|
||||
decode_rgb9e5float(width, height, bin, endian)
|
||||
when 34
|
||||
decode_etc1(width, height, bin)
|
||||
when 45
|
||||
decode_etc2rgb(width, height, bin)
|
||||
when 46
|
||||
decode_etc2rgba1(width, height, bin)
|
||||
when 47
|
||||
decode_etc2rgba8(width, height, bin)
|
||||
when 48, 54
|
||||
decode_astc(width, height, 4, bin)
|
||||
when 49, 55
|
||||
decode_astc(width, height, 5, bin)
|
||||
when 50, 56
|
||||
decode_astc(width, height, 6, bin)
|
||||
when 51, 57
|
||||
decode_astc(width, height, 8, bin)
|
||||
when 52, 58
|
||||
decode_astc(width, height, 10, bin)
|
||||
when 53, 59
|
||||
decode_astc(width, height, 12, bin)
|
||||
when 62
|
||||
decode_rg16(width, height, bin)
|
||||
when 63
|
||||
decode_r8(width, height, bin)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Decode image from RGBA4444 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgba4444(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
|
||||
c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f)
|
||||
BinUtils.append_int32_be!(mem, c << 4 | c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from ARGB4444 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_argb4444(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
|
||||
c = ((c & 0x0f00) << 16) | ((c & 0x00f0) << 12) | ((c & 0x000f) << 8) | ((c & 0xf000) >> 12)
|
||||
BinUtils.append_int32_be!(mem, c << 4 | c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGB565 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgb565(width, height, bin, endian = :big)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_rgb565(bin, width * height, endian == :big)).flip
|
||||
end
|
||||
|
||||
# Decode image from A8 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_a8(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
c = BinUtils.get_int8(bin, i)
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R8 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_r8(width, height, bin)
|
||||
decode_a8(width, height, bin)
|
||||
end
|
||||
|
||||
# Decode image from RG16 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rg16(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
BinUtils.append_int16_int8_be!(mem, BinUtils.get_int16_be(bin, i*2), 0)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGB24 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgb24(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, bin).flip
|
||||
end
|
||||
|
||||
# Decode image from RGBA32 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgba32(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, bin).flip
|
||||
end
|
||||
|
||||
# Decode image from ARGB32 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_argb32(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = BinUtils.get_int32_be(bin, i*4)
|
||||
BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from BGRA32 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_bgra32(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = BinUtils.get_int32_le(bin, i*4)
|
||||
BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R16 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_r16(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
|
||||
c = f2i(r / 65535.0)
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGB9e5 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgb9e5float(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
n = endian == :little ? BinUtils.get_int32_le(bin, i*4) : BinUtils.get_int32_be(bin, i*4)
|
||||
e = (n & 0xf8000000) >> 27
|
||||
r = (n & 0x7fc0000) >> 9
|
||||
g = (n & 0x3fe00) >> 9
|
||||
b = n & 0x1ff
|
||||
r = (r / 512r + 1) * (2**(e-15))
|
||||
g = (g / 512r + 1) * (2**(e-15))
|
||||
b = (b / 512r + 1) * (2**(e-15))
|
||||
BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R Half-float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rhalf(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
c = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)))
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RG Half-float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rghalf(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
r = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*4) : BinUtils.get_int16_be(bin, i*4)))
|
||||
g = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*4+2) : BinUtils.get_int16_be(bin, i*4+2)))
|
||||
BinUtils.append_int8!(mem, r, g, 0)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGBA Half-float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgbahalf(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
r = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8) : BinUtils.get_int16_be(bin, i*8)))
|
||||
g = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8+2) : BinUtils.get_int16_be(bin, i*8+2)))
|
||||
b = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8+4) : BinUtils.get_int16_be(bin, i*8+4)))
|
||||
a = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8+6) : BinUtils.get_int16_be(bin, i*8+6)))
|
||||
BinUtils.append_int8!(mem, r, g, b, a)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rfloat(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
unpackstr = endian == :little ? 'e' : 'g'
|
||||
(width * height).times do |i|
|
||||
c = f2i(bin.byteslice(i*4, 4).unpack(unpackstr)[0])
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RG float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgfloat(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
unpackstr = endian == :little ? 'e2' : 'g2'
|
||||
(width * height).times do |i|
|
||||
r, g = bin.byteslice(i*8, 8).unpack(unpackstr)
|
||||
BinUtils.append_int8!(mem, f2i(r), f2i(g), 0)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGBA float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgbafloat(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
unpackstr = endian == :little ? 'e4' : 'g4'
|
||||
(width * height).times do |i|
|
||||
r, g, b, a = bin.byteslice(i*16, 16).unpack(unpackstr)
|
||||
BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b), f2i(a))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from DXT1 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_dxt1(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_dxt1(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from DXT5 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_dxt5(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_dxt5(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC1 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc1(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc1(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC2 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgb(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC2 Alpha1 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgba1(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2a1(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC2 Alpha8 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgba8(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2a8(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ASTC compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [Integer] blocksize block size
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_astc(width, height, blocksize, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_astc(bin, width, height, blocksize, blocksize))
|
||||
end
|
||||
|
||||
# Create ASTC file data from ObjectValue
|
||||
# @param [Mikunyan::ObjectValue,Hash] object target object
|
||||
# @return [String,nil] created file
|
||||
def self.create_astc_file(object)
|
||||
astc_list = {
|
||||
48 => 4, 49 => 5, 50 => 6, 51 => 8, 52 => 10, 53 => 12,
|
||||
54 => 4, 55 => 5, 56 => 6, 57 => 8, 58 => 10, 59 => 12
|
||||
}
|
||||
width = object['m_Width']
|
||||
height = object['m_Height']
|
||||
fmt = object['m_TextureFormat']
|
||||
bin = object['image data']
|
||||
width = width.value if width.class == ObjectValue
|
||||
height = height.value if height.class == ObjectValue
|
||||
fmt = fmt.value if fmt.class == ObjectValue
|
||||
bin = bin.value if bin.class == ObjectValue
|
||||
if width && height && fmt && astc_list[fmt]
|
||||
header = "\x13\xAB\xA1\x5C".force_encoding('ascii-8bit')
|
||||
header << [astc_list[fmt], astc_list[fmt], 1].pack("C*")
|
||||
header << [width].pack("V").byteslice(0, 3)
|
||||
header << [height].pack("V").byteslice(0, 3)
|
||||
header << "\x01\x00\x00"
|
||||
header + bin
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# convert 16bit float
|
||||
def self.n2f(n)
|
||||
case n
|
||||
when 0x0000
|
||||
0.0
|
||||
when 0x8000
|
||||
-0.0
|
||||
when 0x7c00
|
||||
Float::INFINITY
|
||||
when 0xfc00
|
||||
-Float::INFINITY
|
||||
else
|
||||
s = n & 0x8000 != 0
|
||||
e = n & 0x7c00
|
||||
f = n & 0x03ff
|
||||
case e
|
||||
when 0x7c00
|
||||
Float::NAN
|
||||
when 0
|
||||
(s ? -f : f) * 2.0**-24
|
||||
else
|
||||
(s ? -1 : 1) * (f / 1024.0 + 1) * (2.0 ** ((e >> 10)-15))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# [0.0,1.0] -> [0,255]
|
||||
def self.f2i(d)
|
||||
(d * 255).round.clamp(0, 255)
|
||||
end
|
||||
case fmt
|
||||
when 1
|
||||
decode_a8(width, height, bin)
|
||||
when 2
|
||||
decode_argb4444(width, height, bin, endian)
|
||||
when 3
|
||||
decode_rgb24(width, height, bin)
|
||||
when 4
|
||||
decode_rgba32(width, height, bin)
|
||||
when 5
|
||||
decode_argb32(width, height, bin)
|
||||
when 7
|
||||
decode_rgb565(width, height, bin, endian)
|
||||
when 9
|
||||
decode_r16(width, height, bin)
|
||||
when 10
|
||||
decode_dxt1(width, height, bin)
|
||||
when 12
|
||||
decode_dxt5(width, height, bin)
|
||||
when 13
|
||||
decode_rgba4444(width, height, bin, endian)
|
||||
when 14
|
||||
decode_bgra32(width, height, bin)
|
||||
when 15
|
||||
decode_rhalf(width, height, bin, endian)
|
||||
when 16
|
||||
decode_rghalf(width, height, bin, endian)
|
||||
when 17
|
||||
decode_rgbahalf(width, height, bin, endian)
|
||||
when 18
|
||||
decode_rfloat(width, height, bin, endian)
|
||||
when 19
|
||||
decode_rgfloat(width, height, bin, endian)
|
||||
when 20
|
||||
decode_rgbafloat(width, height, bin, endian)
|
||||
when 22
|
||||
decode_rgb9e5float(width, height, bin, endian)
|
||||
when 34
|
||||
decode_etc1(width, height, bin)
|
||||
when 45
|
||||
decode_etc2rgb(width, height, bin)
|
||||
when 46
|
||||
decode_etc2rgba1(width, height, bin)
|
||||
when 47
|
||||
decode_etc2rgba8(width, height, bin)
|
||||
when 48, 54
|
||||
decode_astc(width, height, 4, bin)
|
||||
when 49, 55
|
||||
decode_astc(width, height, 5, bin)
|
||||
when 50, 56
|
||||
decode_astc(width, height, 6, bin)
|
||||
when 51, 57
|
||||
decode_astc(width, height, 8, bin)
|
||||
when 52, 58
|
||||
decode_astc(width, height, 10, bin)
|
||||
when 53, 59
|
||||
decode_astc(width, height, 12, bin)
|
||||
when 62
|
||||
decode_rg16(width, height, bin)
|
||||
when 63
|
||||
decode_r8(width, height, bin)
|
||||
end
|
||||
end
|
||||
|
||||
# Decode image from RGBA4444 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgba4444(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = endian == :little ? BinUtils.get_int16_le(bin, i * 2) : BinUtils.get_int16_be(bin, i * 2)
|
||||
c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f)
|
||||
BinUtils.append_int32_be!(mem, c << 4 | c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from ARGB4444 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_argb4444(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = endian == :little ? BinUtils.get_int16_le(bin, i * 2) : BinUtils.get_int16_be(bin, i * 2)
|
||||
c = ((c & 0x0f00) << 16) | ((c & 0x00f0) << 12) | ((c & 0x000f) << 8) | ((c & 0xf000) >> 12)
|
||||
BinUtils.append_int32_be!(mem, c << 4 | c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGB565 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgb565(width, height, bin, endian = :big)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_rgb565(bin, width * height, endian == :big)).flip
|
||||
end
|
||||
|
||||
# Decode image from A8 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_a8(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
c = BinUtils.get_int8(bin, i)
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R8 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_r8(width, height, bin)
|
||||
decode_a8(width, height, bin)
|
||||
end
|
||||
|
||||
# Decode image from RG16 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rg16(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
BinUtils.append_int16_int8_be!(mem, BinUtils.get_int16_be(bin, i * 2), 0)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGB24 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgb24(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, bin).flip
|
||||
end
|
||||
|
||||
# Decode image from RGBA32 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgba32(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, bin).flip
|
||||
end
|
||||
|
||||
# Decode image from ARGB32 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_argb32(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = BinUtils.get_int32_be(bin, i * 4)
|
||||
BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from BGRA32 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_bgra32(width, height, bin)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
c = BinUtils.get_int32_le(bin, i * 4)
|
||||
BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R16 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_r16(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
c = endian == :little ? BinUtils.get_int16_le(bin, i * 2) : BinUtils.get_int16_be(bin, i * 2)
|
||||
c = f2i(r / 65535.0)
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGB9e5 binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgb9e5float(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
n = endian == :little ? BinUtils.get_int32_le(bin, i * 4) : BinUtils.get_int32_be(bin, i * 4)
|
||||
e = (n & 0xf8000000) >> 27
|
||||
r = (n & 0x7fc0000) >> 9
|
||||
g = (n & 0x3fe00) >> 9
|
||||
b = n & 0x1ff
|
||||
r = (r / 512r + 1) * (2**(e - 15))
|
||||
g = (g / 512r + 1) * (2**(e - 15))
|
||||
b = (b / 512r + 1) * (2**(e - 15))
|
||||
BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R Half-float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rhalf(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
c = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i * 2) : BinUtils.get_int16_be(bin, i * 2)))
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RG Half-float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rghalf(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
r = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i * 4) : BinUtils.get_int16_be(bin, i * 4)))
|
||||
g = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i * 4 + 2) : BinUtils.get_int16_be(bin, i * 4 + 2)))
|
||||
BinUtils.append_int8!(mem, r, g, 0)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGBA Half-float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgbahalf(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
(width * height).times do |i|
|
||||
r = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i * 8) : BinUtils.get_int16_be(bin, i * 8)))
|
||||
g = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i * 8 + 2) : BinUtils.get_int16_be(bin, i * 8 + 2)))
|
||||
b = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i * 8 + 4) : BinUtils.get_int16_be(bin, i * 8 + 4)))
|
||||
a = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i * 8 + 6) : BinUtils.get_int16_be(bin, i * 8 + 6)))
|
||||
BinUtils.append_int8!(mem, r, g, b, a)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from R float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rfloat(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
unpackstr = endian == :little ? 'e' : 'g'
|
||||
(width * height).times do |i|
|
||||
c = f2i(bin.byteslice(i * 4, 4).unpack1(unpackstr))
|
||||
BinUtils.append_int8!(mem, c, c, c)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RG float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgfloat(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
unpackstr = endian == :little ? 'e2' : 'g2'
|
||||
(width * height).times do |i|
|
||||
r, g = bin.byteslice(i * 8, 8).unpack(unpackstr)
|
||||
BinUtils.append_int8!(mem, f2i(r), f2i(g), 0)
|
||||
end
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from RGBA float binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @param [Symbol] endian endianness of binary
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_rgbafloat(width, height, bin, endian = :big)
|
||||
mem = String.new(capacity: width * height * 4)
|
||||
unpackstr = endian == :little ? 'e4' : 'g4'
|
||||
(width * height).times do |i|
|
||||
r, g, b, a = bin.byteslice(i * 16, 16).unpack(unpackstr)
|
||||
BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b), f2i(a))
|
||||
end
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
|
||||
end
|
||||
|
||||
# Decode image from DXT1 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_dxt1(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_dxt1(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from DXT5 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_dxt5(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_dxt5(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC1 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc1(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc1(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC2 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgb(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC2 Alpha1 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgba1(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2a1(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC2 Alpha8 compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgba8(width, height, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2a8(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ASTC compressed binary
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @param [Integer] blocksize block size
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_astc(width, height, blocksize, bin)
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_astc(bin, width, height, blocksize, blocksize))
|
||||
end
|
||||
|
||||
# Create ASTC file data from ObjectValue
|
||||
# @param [Mikunyan::ObjectValue,Hash] object target object
|
||||
# @return [String,nil] created file
|
||||
def self.create_astc_file(object)
|
||||
astc_list = {
|
||||
48 => 4, 49 => 5, 50 => 6, 51 => 8, 52 => 10, 53 => 12,
|
||||
54 => 4, 55 => 5, 56 => 6, 57 => 8, 58 => 10, 59 => 12
|
||||
}
|
||||
width = object['m_Width']
|
||||
height = object['m_Height']
|
||||
fmt = object['m_TextureFormat']
|
||||
bin = object['image data']
|
||||
width = width.value if width.class == ObjectValue
|
||||
height = height.value if height.class == ObjectValue
|
||||
fmt = fmt.value if fmt.class == ObjectValue
|
||||
bin = bin.value if bin.class == ObjectValue
|
||||
if width && height && fmt && astc_list[fmt]
|
||||
header = "\x13\xAB\xA1\x5C".force_encoding('ascii-8bit')
|
||||
header << [astc_list[fmt], astc_list[fmt], 1].pack('C*')
|
||||
header << [width].pack('V').byteslice(0, 3)
|
||||
header << [height].pack('V').byteslice(0, 3)
|
||||
header << "\x01\x00\x00"
|
||||
header + bin
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# convert 16bit float
|
||||
def self.n2f(n)
|
||||
case n
|
||||
when 0x0000
|
||||
0.0
|
||||
when 0x8000
|
||||
-0.0
|
||||
when 0x7c00
|
||||
Float::INFINITY
|
||||
when 0xfc00
|
||||
-Float::INFINITY
|
||||
else
|
||||
s = n & 0x8000 != 0
|
||||
e = n & 0x7c00
|
||||
f = n & 0x03ff
|
||||
case e
|
||||
when 0x7c00
|
||||
Float::NAN
|
||||
when 0
|
||||
(s ? -f : f) * 2.0**-24
|
||||
else
|
||||
(s ? -1 : 1) * (f / 1024.0 + 1) * (2.0**((e >> 10) - 15))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# [0.0,1.0] -> [0,255]
|
||||
def self.f2i(d)
|
||||
(d * 255).round.clamp(0, 255)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,93 +1,95 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Mikunyan
|
||||
# Class for representing decoded object
|
||||
# @attr [String] name object name
|
||||
# @attr [String] type object type name
|
||||
# @attr [Object] value object
|
||||
# @attr [Symbol] endian endianness
|
||||
# @attr [Boolean] is_struct
|
||||
class ObjectValue
|
||||
attr_accessor :name, :type, :value, :endian, :is_struct
|
||||
# Class for representing decoded object
|
||||
# @attr [String] name object name
|
||||
# @attr [String] type object type name
|
||||
# @attr [Object] value object
|
||||
# @attr [Symbol] endian endianness
|
||||
# @attr [Boolean] is_struct
|
||||
class ObjectValue
|
||||
attr_accessor :name, :type, :value, :endian, :is_struct
|
||||
|
||||
# Constructor
|
||||
# @param [String] name object name
|
||||
# @param [String] type object type name
|
||||
# @param [Symbol] endian endianness
|
||||
# @param [Object] value object
|
||||
def initialize(name, type, endian, value = nil)
|
||||
@name = name
|
||||
@type = type
|
||||
@endian = endian
|
||||
@value = value
|
||||
@is_struct = false
|
||||
@attr = {}
|
||||
end
|
||||
|
||||
# Return whether object is array or not
|
||||
# @return [Boolean]
|
||||
def array?
|
||||
value && value.class == Array
|
||||
end
|
||||
|
||||
# Return whether object is value or not
|
||||
# @return [Boolean]
|
||||
def value?
|
||||
value && value.class != Array
|
||||
end
|
||||
|
||||
# Return whether object is struct or not
|
||||
# @return [Boolean]
|
||||
def struct?
|
||||
is_struct
|
||||
end
|
||||
|
||||
# Return all keys
|
||||
# @return [Array] list of keys
|
||||
def keys
|
||||
@attr.keys
|
||||
end
|
||||
|
||||
# Return whether object contains key
|
||||
# @param [String] key
|
||||
# @return [Boolean]
|
||||
def key?(key)
|
||||
@attr.key?(key)
|
||||
end
|
||||
|
||||
# Return value
|
||||
# @return [Object] value
|
||||
def []
|
||||
@value
|
||||
end
|
||||
|
||||
# Return value of selected index or key
|
||||
# @param [Integer,String] i index or key
|
||||
# @return [Object] value
|
||||
def [](i)
|
||||
if array? && i.class == Integer
|
||||
@value[i]
|
||||
else
|
||||
@attr[i]
|
||||
end
|
||||
end
|
||||
|
||||
# Set value of selected key
|
||||
# @param [String] name key
|
||||
# @param [Object] value value
|
||||
# @return [Object] value
|
||||
def []=(name, value)
|
||||
@attr[name] = value
|
||||
end
|
||||
|
||||
# Return value of called key
|
||||
# @param [String] name key
|
||||
# @return [Object] value
|
||||
def method_missing(name, *args)
|
||||
@attr[name.to_s]
|
||||
end
|
||||
|
||||
# Implementation of respond_to_missing?
|
||||
def respond_to_missing?(symbol, include_private)
|
||||
@attr.key?(symbol.to_s)
|
||||
end
|
||||
# Constructor
|
||||
# @param [String] name object name
|
||||
# @param [String] type object type name
|
||||
# @param [Symbol] endian endianness
|
||||
# @param [Object] value object
|
||||
def initialize(name, type, endian, value = nil)
|
||||
@name = name
|
||||
@type = type
|
||||
@endian = endian
|
||||
@value = value
|
||||
@is_struct = false
|
||||
@attr = {}
|
||||
end
|
||||
|
||||
# Return whether object is array or not
|
||||
# @return [Boolean]
|
||||
def array?
|
||||
value && value.class == Array
|
||||
end
|
||||
|
||||
# Return whether object is value or not
|
||||
# @return [Boolean]
|
||||
def value?
|
||||
value && value.class != Array
|
||||
end
|
||||
|
||||
# Return whether object is struct or not
|
||||
# @return [Boolean]
|
||||
def struct?
|
||||
is_struct
|
||||
end
|
||||
|
||||
# Return all keys
|
||||
# @return [Array] list of keys
|
||||
def keys
|
||||
@attr.keys
|
||||
end
|
||||
|
||||
# Return whether object contains key
|
||||
# @param [String] key
|
||||
# @return [Boolean]
|
||||
def key?(key)
|
||||
@attr.key?(key)
|
||||
end
|
||||
|
||||
# Return value
|
||||
# @return [Object] value
|
||||
def []
|
||||
@value
|
||||
end
|
||||
|
||||
# Return value of selected index or key
|
||||
# @param [Integer,String] i index or key
|
||||
# @return [Object] value
|
||||
def [](i)
|
||||
if array? && i.class == Integer
|
||||
@value[i]
|
||||
else
|
||||
@attr[i]
|
||||
end
|
||||
end
|
||||
|
||||
# Set value of selected key
|
||||
# @param [String] name key
|
||||
# @param [Object] value value
|
||||
# @return [Object] value
|
||||
def []=(name, value)
|
||||
@attr[name] = value
|
||||
end
|
||||
|
||||
# Return value of called key
|
||||
# @param [String] name key
|
||||
# @return [Object] value
|
||||
def method_missing(name, *_args)
|
||||
@attr[name.to_s]
|
||||
end
|
||||
|
||||
# Implementation of respond_to_missing?
|
||||
def respond_to_missing?(symbol, _include_private)
|
||||
@attr.key?(symbol.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+79
-77
@@ -1,82 +1,84 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Mikunyan
|
||||
# Class for representing TypeTree
|
||||
# @attr [Array<Mikunyan::TypeTree::Node>] nodes list of all nodes
|
||||
class TypeTree
|
||||
attr_accessor :nodes
|
||||
# Class for representing TypeTree
|
||||
# @attr [Array<Mikunyan::TypeTree::Node>] nodes list of all nodes
|
||||
class TypeTree
|
||||
attr_accessor :nodes
|
||||
|
||||
# Struct for representing Node in TypeTree
|
||||
# @attr [String] version version string
|
||||
# @attr [Integer] depth depth of node (>= 0)
|
||||
# @attr [Boolean] array? array node or not
|
||||
# @attr [String] type type name
|
||||
# @attr [String] name node (attribute) name
|
||||
# @attr [Integer] index index in node list
|
||||
# @attr [Integer] flags flags of node
|
||||
Node = Struct.new(:version, :depth, :array?, :type, :name, :size, :index, :flags)
|
||||
# Struct for representing Node in TypeTree
|
||||
# @attr [String] version version string
|
||||
# @attr [Integer] depth depth of node (>= 0)
|
||||
# @attr [Boolean] array? array node or not
|
||||
# @attr [String] type type name
|
||||
# @attr [String] name node (attribute) name
|
||||
# @attr [Integer] index index in node list
|
||||
# @attr [Integer] flags flags of node
|
||||
Node = Struct.new(:version, :depth, :array?, :type, :name, :size, :index, :flags)
|
||||
|
||||
# Create TypeTree from binary string (new version)
|
||||
# @param [Mikunyan::BinaryReader] br
|
||||
# @return [Mikunyan::TypeTree] created TypeTree
|
||||
def self.load(br)
|
||||
nodes = []
|
||||
node_count = br.i32u
|
||||
buffer_size = br.i32u
|
||||
node_count.times do
|
||||
node = Node.new(br.i16u, br.i8u, br.i8u != 0, br.i32, br.i32, br.i32, br.i32u, br.i32u)
|
||||
nodes << node
|
||||
end
|
||||
buffer = br.read(buffer_size)
|
||||
nodes.each do |n|
|
||||
if n.type >= 0
|
||||
n.type = buffer.unpack("@#{n.type}Z*")[0]
|
||||
else
|
||||
n.type = Mikunyan::STRING_TABLE[n.type + 2**31]
|
||||
end
|
||||
if n.name >= 0
|
||||
n.name = buffer.unpack("@#{n.name}Z*")[0]
|
||||
else
|
||||
n.name = Mikunyan::STRING_TABLE[n.name + 2**31]
|
||||
end
|
||||
end
|
||||
r = TypeTree.new
|
||||
r.nodes = nodes
|
||||
r
|
||||
end
|
||||
|
||||
# Create TypeTree from binary string (legacy version)
|
||||
# @param [Mikunyan::BinaryReader] br
|
||||
# @return [Mikunyan::TypeTree] created TypeTree
|
||||
def self.load_legacy(br)
|
||||
nodes = []
|
||||
stack = [0]
|
||||
while stack.size > 0
|
||||
depth = stack.pop
|
||||
type = br.cstr
|
||||
name = br.cstr
|
||||
size = br.i32
|
||||
index = br.i32u
|
||||
is_array = (br.i32 != 0)
|
||||
version = br.i32u
|
||||
flags = br.i32u
|
||||
child_count = br.i32u
|
||||
child_count.times{ stack << depth + 1 }
|
||||
nodes << Node.new(version, depth, is_array, type, name, size, index, flags)
|
||||
end
|
||||
r = TypeTree.new
|
||||
r.nodes = nodes
|
||||
r
|
||||
end
|
||||
|
||||
# Create default TypeTree from hash string (if exists)
|
||||
# @param [String] hash
|
||||
# @return [Mikunyan::TypeTree,nil] created TypeTree
|
||||
def self.load_default(hash)
|
||||
hash_str = hash.unpack('H*')[0]
|
||||
file = File.expand_path("../typetrees/#{hash_str}.dat", __FILE__)
|
||||
return nil unless File.file?(file)
|
||||
r = TypeTree.new
|
||||
r.nodes = Marshal.load(File.binread(file))
|
||||
r
|
||||
end
|
||||
# Create TypeTree from binary string (new version)
|
||||
# @param [Mikunyan::BinaryReader] br
|
||||
# @return [Mikunyan::TypeTree] created TypeTree
|
||||
def self.load(br)
|
||||
nodes = []
|
||||
node_count = br.i32u
|
||||
buffer_size = br.i32u
|
||||
node_count.times do
|
||||
node = Node.new(br.i16u, br.i8u, br.i8u != 0, br.i32, br.i32, br.i32, br.i32u, br.i32u)
|
||||
nodes << node
|
||||
end
|
||||
buffer = br.read(buffer_size)
|
||||
nodes.each do |n|
|
||||
n.type = if n.type >= 0
|
||||
buffer.unpack1("@#{n.type}Z*")
|
||||
else
|
||||
Mikunyan::STRING_TABLE[n.type + 2**31]
|
||||
end
|
||||
n.name = if n.name >= 0
|
||||
buffer.unpack1("@#{n.name}Z*")
|
||||
else
|
||||
Mikunyan::STRING_TABLE[n.name + 2**31]
|
||||
end
|
||||
end
|
||||
r = TypeTree.new
|
||||
r.nodes = nodes
|
||||
r
|
||||
end
|
||||
|
||||
# Create TypeTree from binary string (legacy version)
|
||||
# @param [Mikunyan::BinaryReader] br
|
||||
# @return [Mikunyan::TypeTree] created TypeTree
|
||||
def self.load_legacy(br)
|
||||
nodes = []
|
||||
stack = [0]
|
||||
until stack.empty?
|
||||
depth = stack.pop
|
||||
type = br.cstr
|
||||
name = br.cstr
|
||||
size = br.i32
|
||||
index = br.i32u
|
||||
is_array = (br.i32 != 0)
|
||||
version = br.i32u
|
||||
flags = br.i32u
|
||||
child_count = br.i32u
|
||||
child_count.times{stack << depth + 1}
|
||||
nodes << Node.new(version, depth, is_array, type, name, size, index, flags)
|
||||
end
|
||||
r = TypeTree.new
|
||||
r.nodes = nodes
|
||||
r
|
||||
end
|
||||
|
||||
# Create default TypeTree from hash string (if exists)
|
||||
# @param [String] hash
|
||||
# @return [Mikunyan::TypeTree,nil] created TypeTree
|
||||
def self.load_default(hash)
|
||||
hash_str = hash.unpack1('H*')
|
||||
file = File.expand_path("../typetrees/#{hash_str}.dat", __FILE__)
|
||||
return nil unless File.file?(file)
|
||||
r = TypeTree.new
|
||||
r.nodes = Marshal.load(File.binread(file))
|
||||
r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Mikunyan
|
||||
# version string
|
||||
VERSION = "3.9.6"
|
||||
# version string
|
||||
VERSION = '3.9.6'
|
||||
end
|
||||
|
||||
+18
-17
@@ -1,31 +1,32 @@
|
||||
# coding: utf-8
|
||||
lib = File.expand_path("../lib", __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require "mikunyan/version"
|
||||
# frozen_string_literal: true
|
||||
|
||||
lib = File.expand_path('lib', __dir__)
|
||||
$:.unshift(lib) unless $:.include?(lib)
|
||||
require 'mikunyan/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "mikunyan"
|
||||
spec.name = 'mikunyan'
|
||||
spec.version = Mikunyan::VERSION
|
||||
spec.authors = ["Ishotihadus"]
|
||||
spec.email = ["hanachan.pao@gmail.com"]
|
||||
spec.authors = ['Ishotihadus']
|
||||
spec.email = ['hanachan.pao@gmail.com']
|
||||
|
||||
spec.summary = "Unity asset deserializer for Ruby"
|
||||
spec.description = "Library to deserialize Unity assetbundles and assets."
|
||||
spec.homepage = "https://github.com/Ishotihadus/mikunyan"
|
||||
spec.license = "MIT"
|
||||
spec.summary = 'Unity asset deserializer for Ruby'
|
||||
spec.description = 'Library to deserialize Unity assetbundles and assets.'
|
||||
spec.homepage = 'https://github.com/Ishotihadus/mikunyan'
|
||||
spec.license = 'MIT'
|
||||
|
||||
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
||||
f.match(%r{^(test|spec|features)/})
|
||||
end
|
||||
spec.bindir = "exe"
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
spec.extensions = ["ext/decoders/native/extconf.rb"]
|
||||
spec.bindir = 'exe'
|
||||
spec.executables = spec.files.grep(%r{^exe/}){|f| File.basename(f)}
|
||||
spec.require_paths = ['lib']
|
||||
spec.extensions = ['ext/decoders/native/extconf.rb']
|
||||
|
||||
spec.add_dependency 'extlz4', '~> 0'
|
||||
spec.add_dependency 'extlzma', '~> 0'
|
||||
spec.add_dependency 'bin_utils', '~> 0'
|
||||
spec.add_dependency 'chunky_png', '~> 1'
|
||||
spec.add_dependency 'extlz4', '~> 0'
|
||||
spec.add_dependency 'extlzma', '~> 0'
|
||||
spec.add_dependency 'json', '~> 2'
|
||||
|
||||
spec.add_development_dependency 'bundler', '~> 1'
|
||||
|
||||
Reference in New Issue
Block a user