mikunyan 3.9.0 → 3.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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