mikunyan 3.9.0 → 3.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 33a04c05a0c06fecf65f90ce2ff228d64b9e4fba
4
- data.tar.gz: a93d678c8e9b2adb72d0fca3b347472b037d50b7
3
+ metadata.gz: 7c39b71c73cdf957d6227efcd70aca3813759e15
4
+ data.tar.gz: d755c5b99e3dd798f52b8149872e359717470533
5
5
  SHA512:
6
- metadata.gz: be9b4a4f524f679546097ad5340f537788e8107b6adcf2c29f0215858c11c5da6eeadc1f84e7f4fe14d71e013501aeefbf6476ebe059605f7c3ac2897d3641cb
7
- data.tar.gz: d60f47062c0d4f5760803ccd1a3e63e225285f52bb26ddcb669b47a9778a9fb6cc114cd97e30f4059532c2f2643ab18bcd9ca27eea39a38d51dfb314546f1461
6
+ metadata.gz: 788e10112ff00680cc19adbd2327c4a7bf80b8a097accdf58c976e2bbcb9f7c4f917abf7bebab54e0454e540b1f65f86dfd277cd13ee4b994cb3279e0d3c8f1a
7
+ data.tar.gz: 03ad199057859b6924aeafd8b52c31fe959a6aaeb989b475013972dfd88e79900284b2b4191abe92f1f4f1a0f02ad060d681116012a255fe6753ef6d0ec03f3a
data/README.md CHANGED
@@ -18,6 +18,14 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install mikunyan
20
20
 
21
+ If you want to install development build:
22
+
23
+ $ git clone https://github.com/Ishotihadus/mikunyan
24
+ $ cd mikunyan
25
+ $ bundle install
26
+ $ rake build
27
+ $ gem install -l pkg/mikunyan-3.9.x.gem
28
+
21
29
  ## Usage
22
30
 
23
31
  ### Basic Usage
@@ -96,7 +104,7 @@ obj.key
96
104
 
97
105
  You can get png file directly from Texture2D asset. Output object's class is `ChunkyPNG::Image`.
98
106
 
99
- Acceptable format is basic texture formats (1, 2, 3, 4, 5, 7 and 13) and ETC_RGB4 (34).
107
+ Only some basic texture formats (1--5, 7, 9, 13--20, 22, 62, and 63) and ETC_RGB4 (34) are available.
100
108
 
101
109
  ```ruby
102
110
  require 'mikunyan/decoders'
@@ -111,16 +119,19 @@ img = Mikunyan::ImageDecoder.decode_object(obj)
111
119
  img.save('mikunyan.png')
112
120
  ```
113
121
 
114
- ### Json Outputer
122
+ Mikunyan cannot decode ASTC files. Use `Mikunyan::ImageDecoder.create_astc_file` instead.
123
+
124
+ ### Json / YAML Outputer
115
125
 
116
- `mikunyan-json` is the executable command for converting unity3d to json.
126
+ `mikunyan-json` is an executable command for converting unity3d to json.
117
127
 
118
128
  $ mikunyan-json bundle.unity3d > bundle.json
119
129
 
120
130
  Available options:
121
131
 
122
132
  - `--as-asset` (`-a`): interpret input file as not AssetBudnle but Asset
123
- - `--pretty` (`-p`): prettify output json
133
+ - `--pretty` (`-p`): prettify output json (`mikunyan-json` only)
134
+ - `--yaml` (`-y`): YAML mode
124
135
 
125
136
  ## Dependencies
126
137
 
@@ -129,7 +140,7 @@ Available options:
129
140
  - [bin_utils](https://rubygems.org/gems/bin_utils)
130
141
  - [chunky_png](https://rubygems.org/gems/chunky_png)
131
142
 
132
- Mikunyan use [oily_png](https://rubygems.org/gems/oily_png) instead of chunky_png if available.
143
+ Mikunyan uses [oily_png](https://rubygems.org/gems/oily_png) instead of chunky_png if available.
133
144
 
134
145
  ## FAQ
135
146
 
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'mikunyan'
3
- require 'json'
4
3
  require 'base64'
5
4
 
6
5
  def obj64(obj)
@@ -19,7 +18,7 @@ def obj64(obj)
19
18
  end
20
19
  end
21
20
 
22
- opts = {:as_asset => false, :pretty => false}
21
+ opts = {:as_asset => false, :pretty => false, :yaml => false}
23
22
  arg = nil
24
23
  i = 0
25
24
  while i < ARGV.count
@@ -29,9 +28,10 @@ while i < ARGV.count
29
28
  opts[:as_asset] = true
30
29
  when '--pretty', '-p'
31
30
  opts[:pretty] = true
31
+ when '--yaml', '-y'
32
+ opts[:yaml] = true
32
33
  else
33
34
  warn("Unknown option: #{ARGV[i]}")
34
- exit(1)
35
35
  end
36
36
  else
37
37
  arg = ARGV[i] unless arg
@@ -39,6 +39,10 @@ while i < ARGV.count
39
39
  i += 1
40
40
  end
41
41
 
42
+ if option[:pretty] && option[:yaml]
43
+ warn("Option --pretty is ignored if --yaml is specified.")
44
+ end
45
+
42
46
  unless File.file?(arg)
43
47
  warn("File not found: #{arg}")
44
48
  exit(1)
@@ -51,7 +55,7 @@ if opts[:as_asset]
51
55
  objs = []
52
56
  asset.path_ids.each do |e|
53
57
  obj = asset.parse_object_simple(e)
54
- objs << obj64(obj) if obj
58
+ objs << obj
55
59
  end
56
60
  assets[asset.name] = objs
57
61
  else
@@ -60,14 +64,21 @@ else
60
64
  objs = []
61
65
  asset.path_ids.each do |e|
62
66
  obj = asset.parse_object_simple(e)
63
- objs << obj64(obj) if obj
67
+ objs << obj
64
68
  end
65
69
  assets[asset.name] = objs
66
70
  end
67
71
  end
68
72
 
69
- if opts[:pretty]
70
- puts JSON.pretty_generate(assets)
73
+ if opts[:yaml]
74
+ require 'yaml'
75
+ puts YAML.dump(assets)
71
76
  else
72
- puts JSON.generate(assets)
77
+ require 'json'
78
+ assets = assets.map{|k, v| [k, obj64(v)]}.to_h
79
+ if opts[:pretty]
80
+ puts JSON.pretty_generate(assets)
81
+ else
82
+ puts JSON.generate(assets)
83
+ end
73
84
  end
@@ -6,3 +6,7 @@ require "mikunyan/binary_reader"
6
6
  require "mikunyan/object_value"
7
7
  require "mikunyan/type_tree"
8
8
  require "mikunyan/constants"
9
+
10
+ # Module for deserializing Unity Assets and AssetBundles
11
+ module Mikunyan
12
+ end
@@ -1,21 +1,133 @@
1
1
  module Mikunyan
2
+ # Class for representing Unity Asset
3
+ # @attr_reader [String] name Asset name
4
+ # @attr_reader [Integer] format file format number
5
+ # @attr_reader [String] generator_version version string of generator
6
+ # @attr_reader [Integer] target_platform target platform number
7
+ # @attr_reader [Symbol] endian data endianness (:little or :big)
8
+ # @attr_reader [Array<Mikunyan::Asset::Klass>] klasses defined classes
9
+ # @attr_reader [Array<Mikunyan::Asset::ObjectData>] objects included objects
10
+ # @attr_reader [Array<Integer>] add_ids ?
11
+ # @attr_reader [Array<Mikunyan::Asset::Reference>] references reference data
2
12
  class Asset
3
- attr_accessor :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references
13
+ attr_reader :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references
14
+
15
+ # Struct for representing Asset class definition
16
+ # @attr [Integer] class_id class ID
17
+ # @attr [Integer,nil] script_id script ID
18
+ # @attr [String] hash hash value (16 or 32 bytes)
19
+ # @attr [Mikunyan::TypeTree, nil] type_tree given TypeTree
4
20
  Klass = Struct.new(:class_id, :script_id, :hash, :type_tree)
21
+
22
+ # Struct for representing Asset object information
23
+ # @attr [Integer] path_id path ID
24
+ # @attr [Integer] offset data offset
25
+ # @attr [Integer] size data size
26
+ # @attr [Integer,nil] type_id type ID
27
+ # @attr [Integer,nil] class_id class ID
28
+ # @attr [Integer,nil] class_idx class definition index
29
+ # @attr [Boolean] destroyed? destroyed or not
30
+ # @attr [String] data binary data of object
5
31
  ObjectData = Struct.new(:path_id, :offset, :size, :type_id, :class_id, :class_idx, :destroyed?, :data)
32
+
33
+ # Struct for representing Asset reference information
34
+ # @attr [String] path path
35
+ # @attr [String] guid GUID (16 bytes)
36
+ # @attr [Integer] type ?
37
+ # @attr [String] file_path Asset name
6
38
  Reference = Struct.new(:path, :guid, :type, :file_path)
7
39
 
40
+ # Load Asset from binary string
41
+ # @param [String] bin binary data
42
+ # @param [String] name Asset name
43
+ # @return [Mikunyan::Asset] deserialized Asset object
44
+ def self.load(bin, name)
45
+ r = Asset.new(name)
46
+ r.send(:load, bin)
47
+ r
48
+ end
49
+
50
+ # Load Asset from file
51
+ # @param [String] file file name
52
+ # @param [String] name Asset name (automatically generated if not specified)
53
+ # @return [Mikunyan::Asset] deserialized Asset object
54
+ def self.file(file, name=nil)
55
+ name = File.basename(name, '.*') unless name
56
+ Asset.load(File.binread(file), name)
57
+ end
58
+
59
+ # Returns list of all path IDs
60
+ # @return [Array<Integer>] list of all path IDs
61
+ def path_ids
62
+ @objects.map{|e| e.path_id}
63
+ end
64
+
65
+ # Returns list of containers
66
+ # @return [Array<Hash>,nil] list of all containers
67
+ def containers
68
+ obj = parse_object(1)
69
+ return nil unless obj && obj.m_Container && obj.m_Container.array?
70
+ obj.m_Container.value.map do |e|
71
+ {:name => e.first.value, :preload_index => e.second.preloadIndex.value, :path_id => e.second.asset.m_PathID.value}
72
+ end
73
+ end
74
+
75
+ # Parse object of given path ID
76
+ # @param [Integer,ObjectData] path_id path ID or object
77
+ # @return [Mikunyan::ObjectValue,nil] parsed object
78
+ def parse_object(path_id)
79
+ if path_id.class == Integer
80
+ obj = @objects.find{|e| e.path_id == path_id}
81
+ return nil unless obj
82
+ elsif path_id.class == ObjectData
83
+ obj = path_id
84
+ else
85
+ return nil
86
+ end
87
+
88
+ 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})
89
+ type_tree = Asset.parse_type_tree(klass)
90
+ return nil unless type_tree
91
+
92
+ parse_object_private(BinaryReader.new(obj.data, @endian), type_tree)
93
+ end
94
+
95
+ # Parse object of given path ID and simplify it
96
+ # @param [Integer,ObjectData] path_id path ID or object
97
+ # @return [Hash,nil] parsed object
98
+ def parse_object_simple(path_id)
99
+ Asset.object_simplify(parse_object(path_id))
100
+ end
101
+
102
+ # Returns object type name string
103
+ # @param [Integer,ObjectData] path_id path ID or object
104
+ # @return [String,nil] type name
105
+ def object_type(path_id)
106
+ if path_id.class == Integer
107
+ obj = @objects.find{|e| e.path_id == path_id}
108
+ return nil unless obj
109
+ elsif path_id.class == ObjectData
110
+ obj = path_id
111
+ else
112
+ return nil
113
+ end
114
+ 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})
115
+ if klass && klass.type_tree && klass.type_tree.nodes[0]
116
+ klass.type_tree.nodes[0].type
117
+ elsif klass
118
+ Mikunyan::CLASS_ID[klass.class_id]
119
+ else
120
+ nil
121
+ end
122
+ end
123
+
124
+ private
125
+
8
126
  def initialize(name)
9
127
  @name = name
10
128
  @endian = :big
11
129
  end
12
130
 
13
- def self.file(file, name)
14
- r = Asset.new(name)
15
- r.load(File.binread(file))
16
- r
17
- end
18
-
19
131
  def load(bin)
20
132
  br = BinaryReader.new(bin)
21
133
  metadata_size = br.i32u
@@ -108,97 +220,6 @@ module Mikunyan
108
220
  end
109
221
  end
110
222
 
111
- def path_ids
112
- @objects.map{|e| e.path_id}
113
- end
114
-
115
- def containers
116
- obj = parse_object(1)
117
- return nil unless obj && obj.m_Container && obj.m_Container.array?
118
- obj.m_Container.value.map do |e|
119
- {:name => e.first.value, :preload_index => e.second.preloadIndex.value, :path_id => e.second.asset.m_PathID.value}
120
- end
121
- end
122
-
123
- def parse_object(path_id)
124
- if path_id.class == Integer
125
- obj = @objects.find{|e| e.path_id == path_id}
126
- return nil unless obj
127
- elsif path_id.class == ObjectData
128
- obj = path_id
129
- else
130
- return nil
131
- end
132
-
133
- 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})
134
- type_tree = Asset.parse_type_tree(klass)
135
- return nil unless type_tree
136
-
137
- parse_object_private(BinaryReader.new(obj.data, @endian), type_tree)
138
- end
139
-
140
- def parse_object_simple(path_id)
141
- Asset.object_simplify(parse_object(path_id))
142
- end
143
-
144
- def object_type(path_id)
145
- if path_id.class == Integer
146
- obj = @objects.find{|e| e.path_id == path_id}
147
- return nil unless obj
148
- elsif path_id.class == ObjectData
149
- obj = path_id
150
- else
151
- return nil
152
- end
153
- 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})
154
- if klass && klass.type_tree && klass.type_tree.nodes[0]
155
- klass.type_tree.nodes[0].type
156
- elsif klass
157
- Mikunyan::CLASS_ID[klass.class_id]
158
- else
159
- nil
160
- end
161
- end
162
-
163
- def self.parse_type_tree(klass)
164
- return nil unless klass.type_tree
165
- nodes = klass.type_tree.nodes
166
- tree = {}
167
- stack = []
168
- nodes.each do |node|
169
- this = {:name => node.name, :node => node, :children => []}
170
- if node.depth == 0
171
- tree = this
172
- else
173
- stack[node.depth - 1][:children] << this
174
- end
175
- stack[node.depth] = this
176
- end
177
- tree
178
- end
179
-
180
- def self.object_simplify(obj)
181
- if obj.class != ObjectValue
182
- obj
183
- elsif obj.type == 'pair'
184
- [object_simplify(obj['first']), object_simplify(obj['second'])]
185
- elsif obj.type == 'map' && obj.array?
186
- obj.value.map{|e| [object_simplify(e['first']), object_simplify(e['second'])] }.to_h
187
- elsif obj.value?
188
- object_simplify(obj.value)
189
- elsif obj.array?
190
- obj.value.map{|e| object_simplify(e)}
191
- else
192
- hash = {}
193
- obj.keys.each do |key|
194
- hash[key] = object_simplify(obj[key])
195
- end
196
- hash
197
- end
198
- end
199
-
200
- private
201
-
202
223
  def parse_object_private(br, type_tree)
203
224
  r = nil
204
225
  node = type_tree[:node]
@@ -230,7 +251,6 @@ module Mikunyan
230
251
  children.each do |child|
231
252
  r[child[:name]] = parse_object_private(br, child)
232
253
  end
233
- br.jmp(pos + node.size)
234
254
  else
235
255
  pos = br.pos
236
256
  value = nil
@@ -268,5 +288,42 @@ module Mikunyan
268
288
  br.align(4) if node.flags & 0x4000 != 0
269
289
  r
270
290
  end
291
+
292
+ def self.object_simplify(obj)
293
+ if obj.class != ObjectValue
294
+ obj
295
+ elsif obj.type == 'pair'
296
+ [object_simplify(obj['first']), object_simplify(obj['second'])]
297
+ elsif obj.type == 'map' && obj.array?
298
+ obj.value.map{|e| [object_simplify(e['first']), object_simplify(e['second'])] }.to_h
299
+ elsif obj.value?
300
+ object_simplify(obj.value)
301
+ elsif obj.array?
302
+ obj.value.map{|e| object_simplify(e)}
303
+ else
304
+ hash = {}
305
+ obj.keys.each do |key|
306
+ hash[key] = object_simplify(obj[key])
307
+ end
308
+ hash
309
+ end
310
+ end
311
+
312
+ def self.parse_type_tree(klass)
313
+ return nil unless klass.type_tree
314
+ nodes = klass.type_tree.nodes
315
+ tree = {}
316
+ stack = []
317
+ nodes.each do |node|
318
+ this = {:name => node.name, :node => node, :children => []}
319
+ if node.depth == 0
320
+ tree = this
321
+ else
322
+ stack[node.depth - 1][:children] << this
323
+ end
324
+ stack[node.depth] = this
325
+ end
326
+ tree
327
+ end
271
328
  end
272
329
  end
@@ -1,19 +1,33 @@
1
1
  require 'extlz4'
2
2
 
3
3
  module Mikunyan
4
+ # Class for representing Unity AssetBundle
5
+ # @attr_reader [String] signature file signature (UnityRaw or UnityFS)
6
+ # @attr_reader [Integer] format file format number
7
+ # @attr_reader [String] unity_version version string of Unity to use this AssetBundle
8
+ # @attr_reader [String] generator_version version string of generator
9
+ # @attr_reader [Array<Mikunyan::Asset>] assets included Assets
4
10
  class AssetBundle
5
- attr_accessor :signature, :format, :unity_version, :generator_version, :assets
11
+ attr_reader :signature, :format, :unity_version, :generator_version, :assets
6
12
 
13
+ # Load AssetBundle from binary string
14
+ # @param [String] bin binary data
15
+ # @return [Mikunyan::AssetBundle] deserialized AssetBundle object
7
16
  def self.load(bin)
8
17
  r = AssetBundle.new
9
- r.load(bin)
18
+ r.send(:load, bin)
10
19
  r
11
20
  end
12
21
 
22
+ # Load AssetBundle from file
23
+ # @param [String] file file name
24
+ # @return [Mikunyan::AssetBundle] deserialized AssetBundle object
13
25
  def self.file(file)
14
26
  AssetBundle.load(File.binread(file))
15
27
  end
16
28
 
29
+ private
30
+
17
31
  def load(bin)
18
32
  br = BinaryReader.new(bin)
19
33
  @signature = br.cstr
@@ -31,8 +45,6 @@ module Mikunyan
31
45
  end
32
46
  end
33
47
 
34
- private
35
-
36
48
  def load_unity_raw(br)
37
49
  @assets = []
38
50
 
@@ -48,8 +60,7 @@ module Mikunyan
48
60
  asset_size = br.i32u
49
61
  br.jmp(asset_pos + asset_header_size - 4)
50
62
  asset_data = br.read(asset_size)
51
- asset = Asset.new(asset_name)
52
- asset.load(asset_data)
63
+ asset = Asset.load(asset_data, asset_name)
53
64
  @assets << asset
54
65
  end
55
66
  end
@@ -77,8 +88,7 @@ module Mikunyan
77
88
  blocks.each{|b| raw_data << uncompress(br.read(b[:c]), b[:u], b[:f])}
78
89
 
79
90
  asset_blocks.each do |b|
80
- asset = Asset.new(b[:name])
81
- asset.load(raw_data.byteslice(b[:offset], b[:size]))
91
+ asset = Asset.load(raw_data.byteslice(b[:offset], b[:size]), b[:name])
82
92
  @assets << asset
83
93
  end
84
94
  end
@@ -1,9 +1,16 @@
1
1
  require 'bin_utils'
2
2
 
3
3
  module Mikunyan
4
+ # Class for manipulating binary string
5
+ # @attr [Symbol] endian endianness
6
+ # @attr [Integer] pos position
7
+ # @attr [Integer] length data size
4
8
  class BinaryReader
5
9
  attr_accessor :endian, :pos, :length
6
10
 
11
+ # Constructor
12
+ # @param [String] data binary string
13
+ # @param [Symbol] endian endianness
7
14
  def initialize(data, endian = :big)
8
15
  @data = data
9
16
  @pos = 0
@@ -11,104 +18,131 @@ module Mikunyan
11
18
  @endian = endian
12
19
  end
13
20
 
21
+ # Returns whether little endian or not
22
+ # @return [Boolean]
14
23
  def little?
15
24
  @endian == :little
16
25
  end
17
26
 
18
- def jmp(pos = 0)
27
+ # Jump to given position
28
+ # @param [Integer] pos position
29
+ def jmp(pos=0)
19
30
  @pos = pos
20
31
  end
21
32
 
22
- def adv(size = 0)
33
+ # Advance position given size
34
+ # @param [Integer] size size
35
+ def adv(size=0)
23
36
  @pos += size
24
37
  end
25
38
 
39
+ # Round up position to multiple of given size
40
+ # @param [Integer] size size
26
41
  def align(size)
27
42
  @pos = (@pos + size - 1) / size * size
28
43
  end
29
44
 
45
+ # Read given size of binary string and seek
46
+ # @param [Integer] size size
47
+ # @return [String] data
30
48
  def read(size)
31
49
  data = @data.byteslice(@pos, size)
32
50
  @pos += size
33
51
  data
34
52
  end
35
53
 
54
+ # Read string until null character
55
+ # @return [String] string
36
56
  def cstr
37
57
  r = @data.unpack("@#{pos}Z*")[0]
38
58
  @pos += r.bytesize + 1
39
59
  r
40
60
  end
41
61
 
62
+ # Read 8bit signed integer
42
63
  def i8
43
64
  i8s
44
65
  end
45
66
 
67
+ # Read 8bit signed integer
46
68
  def i8s
47
69
  r = BinUtils.get_sint8(@data, @pos)
48
70
  @pos += 1
49
71
  r
50
72
  end
51
73
 
74
+ # Read 8bit unsigned integer
52
75
  def i8u
53
76
  r = BinUtils.get_int8(@data, @pos)
54
77
  @pos += 1
55
78
  r
56
79
  end
57
80
 
81
+ # Read 16bit signed integer
58
82
  def i16
59
83
  i16s
60
84
  end
61
85
 
86
+ # Read 16bit signed integer
62
87
  def i16s
63
88
  r = little? ? BinUtils.get_sint16_le(@data, @pos) : BinUtils.get_sint16_be(@data, @pos)
64
89
  @pos += 2
65
90
  r
66
91
  end
67
92
 
93
+ # Read 16bit unsigned integer
68
94
  def i16u
69
95
  r = little? ? BinUtils.get_int16_le(@data, @pos) : BinUtils.get_int16_be(@data, @pos)
70
96
  @pos += 2
71
97
  r
72
98
  end
73
99
 
100
+ # Read 32bit signed integer
74
101
  def i32
75
102
  i32s
76
103
  end
77
104
 
105
+ # Read 32bit signed integer
78
106
  def i32s
79
107
  r = little? ? BinUtils.get_sint32_le(@data, @pos) : BinUtils.get_sint32_be(@data, @pos)
80
108
  @pos += 4
81
109
  r
82
110
  end
83
111
 
112
+ # Read 32bit unsigned integer
84
113
  def i32u
85
114
  r = little? ? BinUtils.get_int32_le(@data, @pos) : BinUtils.get_int32_be(@data, @pos)
86
115
  @pos += 4
87
116
  r
88
117
  end
89
118
 
119
+ # Read 64bit signed integer
90
120
  def i64
91
121
  i64s
92
122
  end
93
123
 
124
+ # Read 64bit signed integer
94
125
  def i64s
95
126
  r = little? ? BinUtils.get_sint64_le(@data, @pos) : BinUtils.get_sint64_be(@data, @pos)
96
127
  @pos += 8
97
128
  r
98
129
  end
99
130
 
131
+ # Read 64bit unsigned integer
100
132
  def i64u
101
133
  r = little? ? BinUtils.get_int64_le(@data, @pos) : BinUtils.get_int64_be(@data, @pos)
102
134
  @pos += 8
103
135
  r
104
136
  end
105
137
 
138
+ # Read 32bit floating point value
106
139
  def float
107
140
  r = little? ? @data.byteslice(@pos, 4).unpack('e')[0] : @data.byteslice(@pos, 4).unpack('g')[0]
108
141
  @pos += 4
109
142
  r
110
143
  end
111
144
 
145
+ # Read 64bit floating point value
112
146
  def double
113
147
  r = little? ? @data.byteslice(@pos, 8).unpack('E')[0] : @data.byteslice(@pos, 8).unpack('G')[0]
114
148
  @pos += 8
@@ -1,4 +1,5 @@
1
1
  module Mikunyan
2
+ private
2
3
  STRING_TABLE = {
3
4
  0=>'AABB',
4
5
  5=>'AnimationClip',
@@ -1,11 +1,13 @@
1
1
  begin; require 'oily_png'; rescue LoadError; require 'chunky_png'; end
2
2
  require 'bin_utils'
3
+ require 'fiddle'
3
4
 
4
5
  module Mikunyan
6
+ # Class for image decoding tools
5
7
  class ImageDecoder
6
- Etc1ModifierTable = [[2, 8], [5, 17], [9, 29], [13, 42], [18, 60], [24, 80], [33, 106], [47, 183]]
7
- 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]]
8
-
8
+ # Decode image from Mikunyan::ObjectValue
9
+ # @param [Mikunyan::ObjectValue] object object to decode
10
+ # @return [ChunkyPNG::Image,nil] decoded image
9
11
  def self.decode_object(object)
10
12
  return nil unless object.class == ObjectValue
11
13
 
@@ -27,100 +29,364 @@ module Mikunyan
27
29
  when 2
28
30
  decode_argb4444(width, height, bin, endian)
29
31
  when 3
30
- decode_rgb888(width, height, bin, endian)
32
+ decode_rgb888(width, height, bin)
31
33
  when 4
32
- decode_rgba8888(width, height, bin, endian)
34
+ decode_rgba8888(width, height, bin)
33
35
  when 5
34
- decode_argb8888(width, height, bin, endian)
36
+ decode_argb8888(width, height, bin)
35
37
  when 7
36
38
  decode_rgb565(width, height, bin, endian)
39
+ when 9
40
+ decode_r16(width, height, bin)
37
41
  when 13
38
42
  decode_rgba4444(width, height, bin, endian)
43
+ when 14
44
+ decode_bgra8888(width, height, bin)
45
+ when 15
46
+ decode_rhalf(width, height, bin, endian)
47
+ when 16
48
+ decode_rghalf(width, height, bin, endian)
49
+ when 17
50
+ decode_rgbahalf(width, height, bin, endian)
51
+ when 18
52
+ decode_rfloat(width, height, bin, endian)
53
+ when 19
54
+ decode_rgfloat(width, height, bin, endian)
55
+ when 20
56
+ decode_rgbafloat(width, height, bin, endian)
57
+ when 22
58
+ decode_rgb9e5float(width, height, bin, endian)
39
59
  when 34
40
60
  decode_etc1(width, height, bin)
61
+ when 62
62
+ decode_rg16(width, height, bin)
63
+ when 63
64
+ decode_r8(width, height, bin)
41
65
  else
42
66
  nil
43
67
  end
44
68
  end
45
69
 
46
- def self.decode_etc1(width, height, bin)
47
- bw = (width + 3) / 4
48
- bh = (height + 3) / 4
49
- pixels = "\0" * (64 * bw * bh)
50
- pixels.force_encoding('ascii-8bit')
51
- bh.times do |by|
52
- bw.times do |bx|
53
- block = decode_etc1_block(BinUtils.get_sint64_be(bin, (bx + by * bw) * 8)).pack('N16')
54
- pixels[( bx * 4 * bh + by) * 16, 16] = block.byteslice( 0, 16)
55
- pixels[((bx * 4 + 1) * bh + by) * 16, 16] = block.byteslice(16, 16)
56
- pixels[((bx * 4 + 2) * bh + by) * 16, 16] = block.byteslice(32, 16)
57
- pixels[((bx * 4 + 3) * bh + by) * 16, 16] = block.byteslice(48, 16)
58
- end
70
+ # Decode image from RGBA4444 binary
71
+ # @param [Integer] width image width
72
+ # @param [Integer] height image height
73
+ # @param [String] bin binary to decode
74
+ # @param [Symbol] endian endianness of binary
75
+ # @return [ChunkyPNG::Image] decoded image
76
+ def self.decode_rgba4444(width, height, bin, endian = :big)
77
+ mem = String.new(capacity: width * height * 4)
78
+ (width * height).times do |i|
79
+ c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
80
+ c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f)
81
+ BinUtils.append_int32_be!(mem, c << 4 | c)
59
82
  end
60
- ChunkyPNG::Image.from_rgba_stream(bh * 4, bw * 4, pixels).rotate_right!.crop!(0, 0, width, height)
83
+ ChunkyPNG::Image.from_rgba_stream(width, height, mem)
61
84
  end
62
85
 
63
- def self.decode_a8(width, height, bin)
64
- pixels = bin.unpack('C*').map do |c|
65
- c << 24 | c << 16 | c << 8 | 0xff
86
+ # Decode image from ARGB4444 binary
87
+ # @param [Integer] width image width
88
+ # @param [Integer] height image height
89
+ # @param [String] bin binary to decode
90
+ # @param [Symbol] endian endianness of binary
91
+ # @return [ChunkyPNG::Image] decoded image
92
+ def self.decode_argb4444(width, height, bin, endian = :big)
93
+ mem = String.new(capacity: width * height * 4)
94
+ (width * height).times do |i|
95
+ c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
96
+ c = ((c & 0x0f00) << 16) | ((c & 0x00f0) << 12) | ((c & 0x000f) << 8) | ((c & 0xf000) >> 12)
97
+ BinUtils.append_int32_be!(mem, c << 4 | c)
66
98
  end
67
- ChunkyPNG::Image.new(width, height, pixels)
99
+ ChunkyPNG::Image.from_rgba_stream(width, height, mem)
68
100
  end
69
101
 
102
+ # Decode image from RGB565 binary
103
+ # @param [Integer] width image width
104
+ # @param [Integer] height image height
105
+ # @param [String] bin binary to decode
106
+ # @param [Symbol] endian endianness of binary
107
+ # @return [ChunkyPNG::Image] decoded image
70
108
  def self.decode_rgb565(width, height, bin, endian = :big)
71
- pixels = bin.unpack(endian == :little ? 'v*' : 'n*').map do |c|
109
+ mem = String.new(capacity: width * height * 3)
110
+ (width * height).times do |i|
111
+ c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
72
112
  r = (c & 0xf800) >> 8
73
113
  g = (c & 0x07e0) >> 3
74
114
  b = (c & 0x001f) << 3
75
- r = r | r >> 5
76
- g = g | g >> 6
77
- b = b | b >> 5
78
- r << 24 | g << 16 | b << 8 | 0xff
115
+ BinUtils.append_int8!(mem, r | r >> 5, g | g >> 6, b | b >> 5)
79
116
  end
80
- ChunkyPNG::Image.new(width, height, pixels)
117
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
81
118
  end
82
119
 
83
- def self.decode_rgb888(width, height, bin, endian = :big)
84
- if endian == :little
85
- ChunkyPNG::Image.from_bgr_stream(width, height, bin)
86
- else
87
- ChunkyPNG::Image.from_rgb_stream(width, height, bin)
120
+ # Decode image from A8 binary
121
+ # @param [Integer] width image width
122
+ # @param [Integer] height image height
123
+ # @param [String] bin binary to decode
124
+ # @return [ChunkyPNG::Image] decoded image
125
+ def self.decode_a8(width, height, bin)
126
+ mem = String.new(capacity: width * height * 3)
127
+ (width * height).times do |i|
128
+ c = BinUtils.get_int8(bin, i)
129
+ BinUtils.append_int8!(mem, c, c, c)
88
130
  end
131
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
89
132
  end
90
133
 
91
- def self.decode_rgba4444(width, height, bin, endian = :big)
92
- pixels = bin.unpack(endian == :little ? 'v*' : 'n*').map do |c|
93
- c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f)
94
- c << 4 | c
134
+ # Decode image from R8 binary
135
+ # @param [Integer] width image width
136
+ # @param [Integer] height image height
137
+ # @param [String] bin binary to decode
138
+ # @return [ChunkyPNG::Image] decoded image
139
+ def self.decode_r8(width, height, bin)
140
+ decode_a8(width, height, bin)
141
+ end
142
+
143
+ # Decode image from RG16 binary
144
+ # @param [Integer] width image width
145
+ # @param [Integer] height image height
146
+ # @param [String] bin binary to decode
147
+ # @return [ChunkyPNG::Image] decoded image
148
+ def self.decode_rg16(width, height, bin)
149
+ mem = String.new(capacity: width * height * 3)
150
+ (width * height).times do |i|
151
+ BinUtils.append_int16_int8_be!(mem, BinUtils.get_int16_be(bin, i*2), 0)
95
152
  end
96
- ChunkyPNG::Image.new(width, height, pixels)
153
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
97
154
  end
98
155
 
99
- def self.decode_argb4444(width, height, bin, endian = :big)
100
- pixels = bin.unpack(endian == :little ? 'v*' : 'n*').map do |c|
101
- c = ((c & 0x0f00) << 16) | ((c & 0x00f0) << 12) | ((c & 0x000f) << 8) | ((c & 0xf000) >> 12)
102
- c << 4 | c
156
+ # Decode image from RGB24 binary
157
+ # @param [Integer] width image width
158
+ # @param [Integer] height image height
159
+ # @param [String] bin binary to decode
160
+ # @return [ChunkyPNG::Image] decoded image
161
+ def self.decode_rgb24(width, height, bin)
162
+ ChunkyPNG::Image.from_rgb_stream(width, height, bin)
163
+ end
164
+
165
+ # Decode image from RGBA32 binary
166
+ # @param [Integer] width image width
167
+ # @param [Integer] height image height
168
+ # @param [String] bin binary to decode
169
+ # @return [ChunkyPNG::Image] decoded image
170
+ def self.decode_rgba32(width, height, bin)
171
+ ChunkyPNG::Image.from_rgba_stream(width, height, bin)
172
+ end
173
+
174
+ # Decode image from ARGB32 binary
175
+ # @param [Integer] width image width
176
+ # @param [Integer] height image height
177
+ # @param [String] bin binary to decode
178
+ # @return [ChunkyPNG::Image] decoded image
179
+ def self.decode_argb32(width, height, bin)
180
+ mem = String.new(capacity: width * height * 4)
181
+ (width * height).times do |i|
182
+ c = BinUtils.get_int32_be(bin, i*4)
183
+ BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
103
184
  end
104
- ChunkyPNG::Image.new(width, height, pixels)
185
+ ChunkyPNG::Image.from_rgba_stream(width, height, mem)
105
186
  end
106
187
 
107
- def self.decode_rgba8888(width, height, bin, endian = :big)
108
- if endian == :little
109
- ChunkyPNG::Image.from_abgr_stream(width, height, bin)
110
- else
111
- ChunkyPNG::Image.from_rgba_stream(width, height, bin)
188
+ # Decode image from BGRA32 binary
189
+ # @param [Integer] width image width
190
+ # @param [Integer] height image height
191
+ # @param [String] bin binary to decode
192
+ # @return [ChunkyPNG::Image] decoded image
193
+ def self.decode_bgra32(width, height, bin)
194
+ mem = String.new(capacity: width * height * 4)
195
+ (width * height).times do |i|
196
+ c = BinUtils.get_int32_le(bin, i*4)
197
+ BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
198
+ end
199
+ ChunkyPNG::Image.from_rgba_stream(width, height, mem)
200
+ end
201
+
202
+ # Decode image from R16 binary
203
+ # @param [Integer] width image width
204
+ # @param [Integer] height image height
205
+ # @param [String] bin binary to decode
206
+ # @param [Symbol] endian endianness of binary
207
+ # @return [ChunkyPNG::Image] decoded image
208
+ def self.decode_r16(width, height, bin, endian = :big)
209
+ mem = String.new(capacity: width * height * 3)
210
+ (width * height).times do |i|
211
+ c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
212
+ c = f2i(r / 65535.0)
213
+ BinUtils.append_int8!(mem, c, c, c)
214
+ end
215
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
216
+ end
217
+
218
+ # Decode image from RGB9e5 binary
219
+ # @param [Integer] width image width
220
+ # @param [Integer] height image height
221
+ # @param [String] bin binary to decode
222
+ # @param [Symbol] endian endianness of binary
223
+ # @return [ChunkyPNG::Image] decoded image
224
+ def self.decode_rgb9e5float(width, height, bin, endian = :big)
225
+ mem = String.new(capacity: width * height * 3)
226
+ (width * height).times do |i|
227
+ n = endian == :little ? BinUtils.get_int32_le(bin, i*4) : BinUtils.get_int32_be(bin, i*4)
228
+ e = (n & 0xf8000000) >> 27
229
+ r = (n & 0x7fc0000) >> 9
230
+ g = (n & 0x3fe00) >> 9
231
+ b = n & 0x1ff
232
+ r = (r / 512r + 1) * (2**(e-15))
233
+ g = (g / 512r + 1) * (2**(e-15))
234
+ b = (b / 512r + 1) * (2**(e-15))
235
+ BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b))
236
+ end
237
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
238
+ end
239
+
240
+ # Decode image from R Half-float binary
241
+ # @param [Integer] width image width
242
+ # @param [Integer] height image height
243
+ # @param [String] bin binary to decode
244
+ # @param [Symbol] endian endianness of binary
245
+ # @return [ChunkyPNG::Image] decoded image
246
+ def self.decode_rhalf(width, height, bin, endian = :big)
247
+ mem = String.new(capacity: width * height * 3)
248
+ (width * height).times do |i|
249
+ c = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)))
250
+ BinUtils.append_int8!(mem, c, c, c)
251
+ end
252
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
253
+ end
254
+
255
+ # Decode image from RG Half-float binary
256
+ # @param [Integer] width image width
257
+ # @param [Integer] height image height
258
+ # @param [String] bin binary to decode
259
+ # @param [Symbol] endian endianness of binary
260
+ # @return [ChunkyPNG::Image] decoded image
261
+ def self.decode_rghalf(width, height, bin, endian = :big)
262
+ mem = String.new(capacity: width * height * 3)
263
+ (width * height).times do |i|
264
+ r = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*4) : BinUtils.get_int16_be(bin, i*4)))
265
+ g = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*4+2) : BinUtils.get_int16_be(bin, i*4+2)))
266
+ BinUtils.append_int8!(mem, r, g, 0)
267
+ end
268
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
269
+ end
270
+
271
+ # Decode image from RGBA Half-float binary
272
+ # @param [Integer] width image width
273
+ # @param [Integer] height image height
274
+ # @param [String] bin binary to decode
275
+ # @param [Symbol] endian endianness of binary
276
+ # @return [ChunkyPNG::Image] decoded image
277
+ def self.decode_rgbahalf(width, height, bin, endian = :big)
278
+ mem = String.new(capacity: width * height * 4)
279
+ (width * height).times do |i|
280
+ r = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8) : BinUtils.get_int16_be(bin, i*8)))
281
+ g = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8+2) : BinUtils.get_int16_be(bin, i*8+2)))
282
+ b = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8+4) : BinUtils.get_int16_be(bin, i*8+4)))
283
+ a = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8+6) : BinUtils.get_int16_be(bin, i*8+6)))
284
+ BinUtils.append_int8!(mem, r, g, b, a)
285
+ end
286
+ ChunkyPNG::Image.from_rgba_stream(width, height, mem)
287
+ end
288
+
289
+ # Decode image from R float binary
290
+ # @param [Integer] width image width
291
+ # @param [Integer] height image height
292
+ # @param [String] bin binary to decode
293
+ # @param [Symbol] endian endianness of binary
294
+ # @return [ChunkyPNG::Image] decoded image
295
+ def self.decode_rfloat(width, height, bin, endian = :big)
296
+ mem = String.new(capacity: width * height * 3)
297
+ unpackstr = endian == :little ? 'e' : 'g'
298
+ (width * height).times do |i|
299
+ c = f2i(bin.byteslice(i*4, 4).unpack(unpackstr)[0])
300
+ BinUtils.append_int8!(mem, c, c, c)
301
+ end
302
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
303
+ end
304
+
305
+ # Decode image from RG float binary
306
+ # @param [Integer] width image width
307
+ # @param [Integer] height image height
308
+ # @param [String] bin binary to decode
309
+ # @param [Symbol] endian endianness of binary
310
+ # @return [ChunkyPNG::Image] decoded image
311
+ def self.decode_rgfloat(width, height, bin, endian = :big)
312
+ mem = String.new(capacity: width * height * 3)
313
+ unpackstr = endian == :little ? 'e2' : 'g2'
314
+ (width * height).times do |i|
315
+ r, g = bin.byteslice(i*8, 8).unpack(unpackstr)
316
+ BinUtils.append_int8!(mem, f2i(r), f2i(g), 0)
317
+ end
318
+ ChunkyPNG::Image.from_rgb_stream(width, height, mem)
319
+ end
320
+
321
+ # Decode image from RGBA float binary
322
+ # @param [Integer] width image width
323
+ # @param [Integer] height image height
324
+ # @param [String] bin binary to decode
325
+ # @param [Symbol] endian endianness of binary
326
+ # @return [ChunkyPNG::Image] decoded image
327
+ def self.decode_rgbafloat(width, height, bin, endian = :big)
328
+ mem = String.new(capacity: width * height * 4)
329
+ unpackstr = endian == :little ? 'e4' : 'g4'
330
+ (width * height).times do |i|
331
+ r, g, b, a = bin.byteslice(i*16, 16).unpack(unpackstr)
332
+ BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b), f2i(a))
333
+ end
334
+ ChunkyPNG::Image.from_rgba_stream(width, height, mem)
335
+ end
336
+
337
+ # Decode image from ETC1 compressed binary
338
+ # @param [Integer] width image width
339
+ # @param [Integer] height image height
340
+ # @param [String] bin binary to decode
341
+ # @return [ChunkyPNG::Image] decoded image
342
+ def self.decode_etc1(width, height, bin)
343
+ bw = (width + 3) / 4
344
+ bh = (height + 3) / 4
345
+ mem = Fiddle::Pointer.malloc(bw * bh * 48)
346
+ bh.times do |by|
347
+ bw.times do |bx|
348
+ block = decode_etc1_block(BinUtils.get_sint64_be(bin, (bx + by * bw) * 8))
349
+ 16.times do |i|
350
+ mem[((i / 4 + bx * 4) + (i % 4 + by * 4) * bw * 4) * 3, 3] = block[i]
351
+ end
352
+ end
112
353
  end
354
+ ChunkyPNG::Image.from_rgb_stream(bw * 4, bh * 4, mem.to_str).crop!(0, 0, width, height)
113
355
  end
114
356
 
115
- def self.decode_argb8888(width, height, bin, endian = :big)
116
- pixels = bin.unpack(endian == :little ? 'V*' : 'N*').map do |c|
117
- c = ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24)
357
+ # Create ASTC file data from ObjectValue
358
+ # @param [Mikunyan::ObjectValue,Hash] object target object
359
+ # @return [String,nil] created file
360
+ def self.create_astc_file(object)
361
+ astc_list = {
362
+ 48 => 4, 49 => 5, 50 => 6, 51 => 8, 52 => 10, 53 => 12,
363
+ 54 => 4, 55 => 5, 56 => 6, 57 => 8, 58 => 10, 59 => 12
364
+ }
365
+ width = object['m_Width']
366
+ height = object['m_Height']
367
+ fmt = object['m_TextureFormat']
368
+ bin = object['image data']
369
+ width = width.value if width.class == ObjectValue
370
+ height = height.value if height.class == ObjectValue
371
+ fmt = fmt.value if fmt.class == ObjectValue
372
+ bin = bin.value if bin.class == ObjectValue
373
+ if width && height && fmt && astc_list[fmt]
374
+ header = "\x13\xAB\xA1\x5C".force_encoding('ascii-8bit')
375
+ header << [astc_list[fmt], astc_list[fmt], 1].pack("C*")
376
+ header << [width].pack("V").byteslice(0, 3)
377
+ header << [height].pack("V").byteslice(0, 3)
378
+ header << "\x01\x00\x00"
379
+ header + bin
380
+ else
381
+ nil
118
382
  end
119
- ChunkyPNG::Image.new(width, height, pixels)
120
383
  end
121
384
 
122
385
  private
123
386
 
387
+ Etc1ModifierTable = [[2, 8], [5, 17], [9, 29], [13, 42], [18, 60], [24, 80], [33, 106], [47, 183]]
388
+ 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]]
389
+
124
390
  def self.decode_etc1_block(bin)
125
391
  colors = []
126
392
  codes = [bin >> 37 & 7, bin >> 34 & 7]
@@ -152,10 +418,38 @@ module Mikunyan
152
418
  r = (color >> 16 & 0xff) + modifier
153
419
  g = (color >> 8 & 0xff) + modifier
154
420
  b = (color & 0xff) + modifier
155
- r = (r > 255 ? 255 : r < 0 ? 0 : r)
156
- g = (g > 255 ? 255 : g < 0 ? 0 : g)
157
- b = (b > 255 ? 255 : b < 0 ? 0 : b)
158
- r << 24 | g << 16 | b << 8 | 0xff
421
+ r.clamp(0, 255).chr + g.clamp(0, 255).chr + b.clamp(0, 255).chr
422
+ end
423
+
424
+ # convert 16bit float
425
+ def self.n2f(n)
426
+ case n
427
+ when 0x0000
428
+ 0.0
429
+ when 0x8000
430
+ -0.0
431
+ when 0x7c00
432
+ Float::INFINITY
433
+ when 0xfc00
434
+ -Float::INFINITY
435
+ else
436
+ s = n & 0x8000 != 0
437
+ e = n & 0x7c00
438
+ f = n & 0x03ff
439
+ case e
440
+ when 0x7c00
441
+ Float::NAN
442
+ when 0
443
+ (s ? -f : f) * 2.0**-24
444
+ else
445
+ (s ? -1 : 1) * (f / 1024.0 + 1) * (2.0 ** ((e >> 10)-15))
446
+ end
447
+ end
448
+ end
449
+
450
+ # [0.0,1.0] -> [0,255]
451
+ def self.f2i(d)
452
+ (d * 255).round.clamp(0, 255)
159
453
  end
160
454
  end
161
455
  end
@@ -1,7 +1,18 @@
1
1
  module Mikunyan
2
+ # Class for representing decoded object
3
+ # @attr [String] name object name
4
+ # @attr [String] type object type name
5
+ # @attr [Object] value object
6
+ # @attr [Symbol] endian endianness
7
+ # @attr [Boolean] is_struct
2
8
  class ObjectValue
3
9
  attr_accessor :name, :type, :value, :endian, :is_struct
4
10
 
11
+ # Constructor
12
+ # @param [String] name object name
13
+ # @param [String] type object type name
14
+ # @param [Symbol] endian endianness
15
+ # @param [Object] value object
5
16
  def initialize(name, type, endian, value = nil)
6
17
  @name = name
7
18
  @type = type
@@ -11,46 +22,70 @@ module Mikunyan
11
22
  @attr = {}
12
23
  end
13
24
 
25
+ # Return whether object is array or not
26
+ # @return [Boolean]
14
27
  def array?
15
28
  value && value.class == Array
16
29
  end
17
30
 
31
+ # Return whether object is value or not
32
+ # @return [Boolean]
18
33
  def value?
19
34
  value && value.class != Array
20
35
  end
21
36
 
37
+ # Return whether object is struct or not
38
+ # @return [Boolean]
22
39
  def struct?
23
40
  is_struct
24
41
  end
25
42
 
43
+ # Return all keys
44
+ # @return [Array] list of keys
26
45
  def keys
27
46
  @attr.keys
28
47
  end
29
48
 
49
+ # Return whether object contains key
50
+ # @param [String] key
51
+ # @return [Boolean]
30
52
  def key?(key)
31
53
  @attr.key?(key)
32
54
  end
33
55
 
56
+ # Return value
57
+ # @return [Object] value
34
58
  def []
35
59
  @value
36
60
  end
37
61
 
38
- def [](name)
39
- if array? && name.class == Integer
40
- @value[name]
62
+ # Return value of selected index or key
63
+ # @param [Integer,String] i index or key
64
+ # @return [Object] value
65
+ def [](i)
66
+ if array? && i.class == Integer
67
+ @value[i]
41
68
  else
42
- @attr[name]
69
+ @attr[i]
43
70
  end
44
71
  end
45
72
 
73
+ # Set value of selected key
74
+ # @param [String] name key
75
+ # @param [Object] value value
76
+ # @return [Object] value
46
77
  def []=(name, value)
47
78
  @attr[name] = value
48
79
  end
49
80
 
81
+ # Return value of called key
82
+ # @param [String] name key
83
+ # @return [Object] value
50
84
  def method_missing(name, *args)
51
85
  @attr[name.to_s]
52
86
  end
53
87
 
88
+ # Implementation of respond_to_missing?
54
89
  def respond_to_missing?(symbol, include_private)
55
90
  @attr.key?(symbol.to_s)
56
91
  end
@@ -1,8 +1,22 @@
1
1
  module Mikunyan
2
+ # Class for representing TypeTree
3
+ # @attr [Array<Mikunyan::TypeTree::Node>] nodes list of all nodes
2
4
  class TypeTree
3
5
  attr_accessor :nodes
6
+
7
+ # Struct for representing Node in TypeTree
8
+ # @attr [String] version version string
9
+ # @attr [Integer] depth depth of node (>= 0)
10
+ # @attr [Boolean] array? array node or not
11
+ # @attr [String] type type name
12
+ # @attr [String] name node (attribute) name
13
+ # @attr [Integer] index index in node list
14
+ # @attr [Integer] flags flags of node
4
15
  Node = Struct.new(:version, :depth, :array?, :type, :name, :size, :index, :flags)
5
16
 
17
+ # Create TypeTree from binary string (new version)
18
+ # @param [Mikunyan::BinaryReader] br
19
+ # @return [Mikunyan::TypeTree] created TypeTree
6
20
  def self.load(br)
7
21
  nodes = []
8
22
  node_count = br.i32u
@@ -28,6 +42,9 @@ module Mikunyan
28
42
  r
29
43
  end
30
44
 
45
+ # Create TypeTree from binary string (legacy version)
46
+ # @param [Mikunyan::BinaryReader] br
47
+ # @return [Mikunyan::TypeTree] created TypeTree
31
48
  def self.load_legacy(br)
32
49
  nodes = []
33
50
  stack = [0]
@@ -49,6 +66,9 @@ module Mikunyan
49
66
  r
50
67
  end
51
68
 
69
+ # Create default TypeTree from hash string (if exists)
70
+ # @param [String] hash
71
+ # @return [Mikunyan::TypeTree,nil] created TypeTree
52
72
  def self.load_default(hash)
53
73
  hash_str = hash.unpack('H*')[0]
54
74
  file = File.expand_path("../typetrees/#{hash_str}.dat", __FILE__)
@@ -1,3 +1,4 @@
1
1
  module Mikunyan
2
- VERSION = "3.9.0"
2
+ # version string
3
+ VERSION = "3.9.1"
3
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mikunyan
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.0
4
+ version: 3.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ishotihadus
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-04 00:00:00.000000000 Z
11
+ date: 2017-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: extlz4