Documentation & change permission of some methods and attributes
This commit is contained in:
@@ -103,7 +103,7 @@ obj.key
|
||||
|
||||
You can get png file directly from Texture2D asset. Output object's class is `ChunkyPNG::Image`.
|
||||
|
||||
Acceptable format is basic texture formats (1, 2, 3, 4, 5, 7 and 13) and ETC_RGB4 (34).
|
||||
Only some basic texture formats (1--5, 7, 9, 13--20, 22, 62, and 63) and ETC_RGB4 (34) are available.
|
||||
|
||||
```ruby
|
||||
require 'mikunyan/decoders'
|
||||
@@ -118,6 +118,8 @@ img = Mikunyan::ImageDecoder.decode_object(obj)
|
||||
img.save('mikunyan.png')
|
||||
```
|
||||
|
||||
Mikunyan cannot decode ASTC files. Use `Mikunyan::ImageDecoder.create_astc_file` instead.
|
||||
|
||||
### Json / YAML Outputer
|
||||
|
||||
`mikunyan-json` is an executable command for converting unity3d to json.
|
||||
|
||||
@@ -6,3 +6,7 @@ require "mikunyan/binary_reader"
|
||||
require "mikunyan/object_value"
|
||||
require "mikunyan/type_tree"
|
||||
require "mikunyan/constants"
|
||||
|
||||
# Module for deserializing Unity Assets and AssetBundles
|
||||
module Mikunyan
|
||||
end
|
||||
|
||||
+156
-98
@@ -1,21 +1,133 @@
|
||||
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_accessor :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references
|
||||
attr_reader :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references
|
||||
|
||||
# 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 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
|
||||
# @return [Mikunyan::Asset] deserialized Asset object
|
||||
def self.load(bin, name)
|
||||
r = Asset.new(name)
|
||||
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)
|
||||
@name = name
|
||||
@endian = :big
|
||||
end
|
||||
|
||||
def self.file(file, name)
|
||||
r = Asset.new(name)
|
||||
r.load(File.binread(file))
|
||||
r
|
||||
end
|
||||
|
||||
def load(bin)
|
||||
br = BinaryReader.new(bin)
|
||||
metadata_size = br.i32u
|
||||
@@ -108,97 +220,6 @@ module Mikunyan
|
||||
end
|
||||
end
|
||||
|
||||
def path_ids
|
||||
@objects.map{|e| e.path_id}
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def parse_object_simple(path_id)
|
||||
Asset.object_simplify(parse_object(path_id))
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def parse_object_private(br, type_tree)
|
||||
r = nil
|
||||
node = type_tree[:node]
|
||||
@@ -267,5 +288,42 @@ module Mikunyan
|
||||
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
|
||||
|
||||
@@ -1,19 +1,33 @@
|
||||
require 'extlz4'
|
||||
|
||||
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_accessor :signature, :format, :unity_version, :generator_version, :assets
|
||||
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.load(bin)
|
||||
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
|
||||
@@ -31,8 +45,6 @@ module Mikunyan
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_unity_raw(br)
|
||||
@assets = []
|
||||
|
||||
@@ -48,8 +60,7 @@ module Mikunyan
|
||||
asset_size = br.i32u
|
||||
br.jmp(asset_pos + asset_header_size - 4)
|
||||
asset_data = br.read(asset_size)
|
||||
asset = Asset.new(asset_name)
|
||||
asset.load(asset_data)
|
||||
asset = Asset.load(asset_data, asset_name)
|
||||
@assets << asset
|
||||
end
|
||||
end
|
||||
@@ -77,8 +88,7 @@ module Mikunyan
|
||||
blocks.each{|b| raw_data << uncompress(br.read(b[:c]), b[:u], b[:f])}
|
||||
|
||||
asset_blocks.each do |b|
|
||||
asset = Asset.new(b[:name])
|
||||
asset.load(raw_data.byteslice(b[:offset], b[:size]))
|
||||
asset = Asset.load(raw_data.byteslice(b[:offset], b[:size]), b[:name])
|
||||
@assets << asset
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
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
|
||||
|
||||
# Constructor
|
||||
# @param [String] data binary string
|
||||
# @param [Symbol] endian endianness
|
||||
def initialize(data, endian = :big)
|
||||
@data = data
|
||||
@pos = 0
|
||||
@@ -11,104 +18,131 @@ module Mikunyan
|
||||
@endian = endian
|
||||
end
|
||||
|
||||
# Returns whether little endian or not
|
||||
# @return [Boolean]
|
||||
def little?
|
||||
@endian == :little
|
||||
end
|
||||
|
||||
def jmp(pos = 0)
|
||||
# Jump to given position
|
||||
# @param [Integer] pos position
|
||||
def jmp(pos=0)
|
||||
@pos = pos
|
||||
end
|
||||
|
||||
def adv(size = 0)
|
||||
# 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 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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module Mikunyan
|
||||
private
|
||||
STRING_TABLE = {
|
||||
0=>'AABB',
|
||||
5=>'AnimationClip',
|
||||
|
||||
@@ -3,10 +3,11 @@ require 'bin_utils'
|
||||
require 'fiddle'
|
||||
|
||||
module Mikunyan
|
||||
# Class for image decoding tools
|
||||
class ImageDecoder
|
||||
Etc1ModifierTable = [[2, 8], [5, 17], [9, 29], [13, 42], [18, 60], [24, 80], [33, 106], [47, 183]]
|
||||
Etc1SubblockTable = [[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]]
|
||||
|
||||
# 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
|
||||
|
||||
@@ -66,8 +67,12 @@ module Mikunyan
|
||||
end
|
||||
end
|
||||
|
||||
# 4 bit integer per color
|
||||
|
||||
# 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|
|
||||
@@ -78,6 +83,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
|
||||
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|
|
||||
@@ -88,8 +99,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
|
||||
end
|
||||
|
||||
# 5-6 bit integer per color
|
||||
|
||||
# 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)
|
||||
mem = String.new(capacity: width * height * 3)
|
||||
(width * height).times do |i|
|
||||
@@ -102,8 +117,11 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
end
|
||||
|
||||
# 8 bit integer per color
|
||||
|
||||
# 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|
|
||||
@@ -113,10 +131,20 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
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|
|
||||
@@ -125,14 +153,29 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
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)
|
||||
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)
|
||||
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|
|
||||
@@ -142,6 +185,11 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
|
||||
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|
|
||||
@@ -151,8 +199,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
|
||||
end
|
||||
|
||||
# 16 bit integer per color
|
||||
|
||||
# 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|
|
||||
@@ -163,8 +215,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
end
|
||||
|
||||
# 9 bit float per color
|
||||
|
||||
# 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|
|
||||
@@ -181,8 +237,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
end
|
||||
|
||||
# 16 bit (half) float per color
|
||||
|
||||
# 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|
|
||||
@@ -192,6 +252,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
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|
|
||||
@@ -202,6 +268,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
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|
|
||||
@@ -214,8 +286,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
|
||||
end
|
||||
|
||||
# 32 bit float per color
|
||||
|
||||
# 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'
|
||||
@@ -226,6 +302,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
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'
|
||||
@@ -236,6 +318,12 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
|
||||
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'
|
||||
@@ -246,8 +334,11 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
|
||||
end
|
||||
|
||||
# other formats
|
||||
|
||||
# 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)
|
||||
bw = (width + 3) / 4
|
||||
bh = (height + 3) / 4
|
||||
@@ -263,6 +354,9 @@ module Mikunyan
|
||||
ChunkyPNG::Image.from_rgb_stream(bw * 4, bh * 4, mem.to_str).crop!(0, 0, width, height)
|
||||
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,
|
||||
@@ -290,6 +384,9 @@ module Mikunyan
|
||||
|
||||
private
|
||||
|
||||
Etc1ModifierTable = [[2, 8], [5, 17], [9, 29], [13, 42], [18, 60], [24, 80], [33, 106], [47, 183]]
|
||||
Etc1SubblockTable = [[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]]
|
||||
|
||||
def self.decode_etc1_block(bin)
|
||||
colors = []
|
||||
codes = [bin >> 37 & 7, bin >> 34 & 7]
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
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
|
||||
|
||||
# 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
|
||||
@@ -11,46 +22,70 @@ module Mikunyan
|
||||
@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
|
||||
|
||||
def [](name)
|
||||
if array? && name.class == Integer
|
||||
@value[name]
|
||||
# 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[name]
|
||||
@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
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
module Mikunyan
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
@@ -28,6 +42,9 @@ module Mikunyan
|
||||
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]
|
||||
@@ -49,6 +66,9 @@ module Mikunyan
|
||||
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__)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module Mikunyan
|
||||
VERSION = "3.9.0"
|
||||
# version string
|
||||
VERSION = "3.9.0"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user