version 3.9.0

This commit is contained in:
Ishotihadus
2017-07-05 01:37:28 +09:00
commit 1a1bed5302
910 changed files with 1434 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
require "mikunyan/version"
require "mikunyan/asset_bundle"
require "mikunyan/asset"
require "mikunyan/binary_reader"
require "mikunyan/object_value"
require "mikunyan/type_tree"
require "mikunyan/constants"
+272
View File
@@ -0,0 +1,272 @@
module Mikunyan
class Asset
attr_accessor :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references
Klass = Struct.new(:class_id, :script_id, :hash, :type_tree)
ObjectData = Struct.new(:path_id, :offset, :size, :type_id, :class_id, :class_idx, :destroyed?, :data)
Reference = Struct.new(:path, :guid, :type, :file_path)
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
size = br.i32u
@format = br.i32u
data_offset = br.i32u
if @format >= 9
@endian = :little if br.i32 == 0
br.endian = @endian
end
@generator_version = br.cstr
@target_platform = br.i32
@klasses = []
if @format >= 17
has_type_trees = (br.i8 != 0)
type_tree_count = br.i32u
type_tree_count.times do
class_id = br.i32
br.adv(1)
script_id = br.i16
if class_id < 0 || class_id == 114
hash = br.read(32)
else
hash = br.read(16)
end
@klasses << Klass.new(class_id, script_id, hash, has_type_trees ? TypeTree.load(br) : TypeTree.load_default(hash))
end
elsif @format >= 13
has_type_trees = (br.i8 != 0)
type_tree_count = br.i32u
type_tree_count.times do
class_id = br.i32
if class_id < 0
hash = br.read(32)
else
hash = br.read(16)
end
@klasses << Klass.new(class_id, nil, hash, has_type_trees ? TypeTree.load(br) : TypeTree.load_default(hash))
end
else
@type_trees = {}
type_tree_count = br.i32u
type_tree_count.times do
class_id = br.i32
@klasses << Klass.new(class_id, nil, nil, @format == 10 || @format == 12 ? TypeTree.load(br) : TypeTree.load_legacy(br))
end
end
long_object_ids = (@format >= 14 || (7 <= @format && @format <= 13 && br.i32 != 0))
@objects = []
object_count = br.i32u
object_count.times do
br.align(4) if @format >= 14
path_id = long_object_ids ? br.i64 : br.i32
offset = br.i32u
size = br.i32u
if @format >= 17
@objects << ObjectData.new(path_id, offset, size, nil, nil, br.i32u, @format <= 10 && br.i16 != 0)
else
@objects << ObjectData.new(path_id, offset, size, br.i32, br.i16, nil, @format <= 10 && br.i16 != 0)
end
br.adv(2) if 11 <= @format && @format <= 16
br.adv(1) if 15 <= @format && @format <= 16
end
if @format >= 11
@add_ids = []
add_id_count = br.i32u
add_id_count.times do
br.align(4) if @format >= 14
@add_ids << [(long_object_ids ? br.i64 : br.i32), br.i32]
end
end
if @format >= 6
@references = []
reference_count = br.i32u
reference_count.times do
@references << Reference.new(br.cstr, br.read(16), br.i32, br.cstr)
end
end
@objects.each do |e|
br.jmp(data_offset + e.offset)
e.data = br.read(e.size)
end
end
def 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]
children = type_tree[:children]
if node.array?
data = []
size = parse_object_private(br, children.find{|e| e[:name] == 'size'})
data_type_tree = children.find{|e| e[:name] == 'data'}
size.value.times do |i|
data << parse_object_private(br, data_type_tree)
end
data = data.map{|e| e.value}.pack('C*') if node.type == 'TypelessData'
r = ObjectValue.new(node.name, node.type, br.endian, data)
elsif node.size == -1
r = ObjectValue.new(node.name, node.type, br.endian)
if children.size == 1 && children[0][:name] == 'Array' && children[0][:node].type == 'Array' && children[0][:node].array?
r.value = parse_object_private(br, children[0]).value
r.value = r.value.map{|e| e.value}.pack('C*').force_encoding("utf-8") if node.type == 'string'
else
children.each do |child|
r[child[:name]] = parse_object_private(br, child)
end
end
elsif children.size > 0
pos = br.pos
r = ObjectValue.new(node.name, node.type, br.endian)
r.is_struct = true
children.each do |child|
r[child[:name]] = parse_object_private(br, child)
end
br.jmp(pos + node.size)
else
pos = br.pos
value = nil
case node.type
when 'bool'
value = (br.i8 != 0)
when 'SInt8'
value = br.i8s
when 'UInt8', 'char'
value = br.i8u
when 'SInt16', 'short'
value = br.i16s
when 'UInt16', 'unsigned short'
value = br.i16u
when 'SInt32', 'int'
value = br.i32s
when 'UInt32', 'unsigned int'
value = br.i32u
when 'SInt64', 'long long'
value = br.i64s
when 'UInt64', 'unsigned long long'
value = br.i64u
when 'float'
value = br.float
when 'double'
value = br.double
when 'ColorRGBA'
value = [br.i8u, br.i8u, br.i8u, br.i8u]
else
value = br.read(node.size)
end
br.jmp(pos + node.size)
r = ObjectValue.new(node.name, node.type, br.endian, value)
end
br.align(4) if node.flags & 0x4000 != 0
r
end
end
end
+98
View File
@@ -0,0 +1,98 @@
require 'extlz4'
module Mikunyan
class AssetBundle
attr_accessor :signature, :format, :unity_version, :generator_version, :assets
def self.load(bin)
r = AssetBundle.new
r.load(bin)
r
end
def self.file(file)
AssetBundle.load(File.binread(file))
end
def load(bin)
br = BinaryReader.new(bin)
@signature = br.cstr
@format = br.i32
@unity_version = br.cstr
@generator_version = br.cstr
case @signature
when 'UnityRaw'
load_unity_raw(br)
when 'UnityFS'
load_unity_fs(br)
else
warn("Unknown signature: #{@signature}")
end
end
private
def load_unity_raw(br)
@assets = []
file_size = br.i32u
header_size = br.i32u
br.jmp(header_size)
asset_count = br.i32u
asset_count.times do
asset_pos = br.pos
asset_name = br.cstr
asset_header_size = br.i32u
asset_size = br.i32u
br.jmp(asset_pos + asset_header_size - 4)
asset_data = br.read(asset_size)
asset = Asset.new(asset_name)
asset.load(asset_data)
@assets << asset
end
end
def load_unity_fs(br)
@assets = []
file_size = br.i64u
ci_block_size = br.i32u
ui_block_size = br.i32u
flags = br.i32u
head = BinaryReader.new(uncompress(br.read(ci_block_size), ui_block_size, flags))
guid = head.read(16)
blocks = []
block_count = head.i32u
block_count.times{ blocks << {:u => head.i32u, :c => head.i32u, :f => head.i16u} }
asset_blocks = []
asset_count = head.i32u
asset_count.times{ asset_blocks << {:offset => head.i64u, :size => head.i64u, :status => head.i32, :name => head.cstr} }
raw_data = ''
blocks.each{|b| raw_data << uncompress(br.read(b[:c]), b[:u], b[:f])}
asset_blocks.each do |b|
asset = Asset.new(b[:name])
asset.load(raw_data.byteslice(b[:offset], b[:size]))
@assets << asset
end
end
def uncompress(block, max_dest_size, flags)
case flags & 0x3f
when 0
block
when 2, 3
LZ4::raw_decode(block, max_dest_size)
else
warn("Unknown compression flag: #{@flags}")
block
end
end
end
end
+118
View File
@@ -0,0 +1,118 @@
require 'bin_utils'
module Mikunyan
class BinaryReader
attr_accessor :endian, :pos, :length
def initialize(data, endian = :big)
@data = data
@pos = 0
@length = data.bytesize
@endian = endian
end
def little?
@endian == :little
end
def jmp(pos = 0)
@pos = pos
end
def adv(size = 0)
@pos += size
end
def align(size)
@pos = (@pos + size - 1) / size * size
end
def read(size)
data = @data.byteslice(@pos, size)
@pos += size
data
end
def cstr
r = @data.unpack("@#{pos}Z*")[0]
@pos += r.bytesize + 1
r
end
def i8
i8s
end
def i8s
r = BinUtils.get_sint8(@data, @pos)
@pos += 1
r
end
def i8u
r = BinUtils.get_int8(@data, @pos)
@pos += 1
r
end
def i16
i16s
end
def i16s
r = little? ? BinUtils.get_sint16_le(@data, @pos) : BinUtils.get_sint16_be(@data, @pos)
@pos += 2
r
end
def i16u
r = little? ? BinUtils.get_int16_le(@data, @pos) : BinUtils.get_int16_be(@data, @pos)
@pos += 2
r
end
def i32
i32s
end
def i32s
r = little? ? BinUtils.get_sint32_le(@data, @pos) : BinUtils.get_sint32_be(@data, @pos)
@pos += 4
r
end
def i32u
r = little? ? BinUtils.get_int32_le(@data, @pos) : BinUtils.get_int32_be(@data, @pos)
@pos += 4
r
end
def i64
i64s
end
def i64s
r = little? ? BinUtils.get_sint64_le(@data, @pos) : BinUtils.get_sint64_be(@data, @pos)
@pos += 8
r
end
def i64u
r = little? ? BinUtils.get_int64_le(@data, @pos) : BinUtils.get_int64_be(@data, @pos)
@pos += 8
r
end
def float
r = little? ? @data.byteslice(@pos, 4).unpack('e')[0] : @data.byteslice(@pos, 4).unpack('g')[0]
@pos += 4
r
end
def double
r = little? ? @data.byteslice(@pos, 8).unpack('E')[0] : @data.byteslice(@pos, 8).unpack('G')[0]
@pos += 8
r
end
end
end
+343
View File
@@ -0,0 +1,343 @@
module Mikunyan
STRING_TABLE = {
0=>'AABB',
5=>'AnimationClip',
19=>'AnimationCurve',
34=>'AnimationState',
49=>'Array',
55=>'Base',
60=>'BitField',
69=>'bitset',
76=>'bool',
81=>'char',
86=>'ColorRGBA',
96=>'Component',
106=>'data',
111=>'deque',
117=>'double',
124=>'dynamic_array',
138=>'FastPropertyName',
155=>'first',
161=>'float',
167=>'Font',
172=>'GameObject',
183=>'Generic Mono',
196=>'GradientNEW',
208=>'GUID',
213=>'GUIStyle',
222=>'int',
226=>'list',
231=>'long long',
241=>'map',
245=>'Matrix4x4f',
256=>'MdFour',
263=>'MonoBehaviour',
277=>'MonoScript',
288=>'m_ByteSize',
299=>'m_Curve',
307=>'m_EditorClassIdentifier',
331=>'m_EditorHideFlags',
349=>'m_Enabled',
359=>'m_ExtensionPtr',
374=>'m_GameObject',
387=>'m_Index',
395=>'m_IsArray',
405=>'m_IsStatic',
416=>'m_MetaFlag',
427=>'m_Name',
434=>'m_ObjectHideFlags',
452=>'m_PrefabInternal',
469=>'m_PrefabParentObject',
490=>'m_Script',
499=>'m_StaticEditorFlags',
519=>'m_Type',
526=>'m_Version',
536=>'Object',
543=>'pair',
548=>'PPtr<Component>',
564=>'PPtr<GameObject>',
581=>'PPtr<Material>',
596=>'PPtr<MonoBehaviour>',
616=>'PPtr<MonoScript>',
633=>'PPtr<Object>',
646=>'PPtr<Prefab>',
659=>'PPtr<Sprite>',
672=>'PPtr<TextAsset>',
688=>'PPtr<Texture>',
702=>'PPtr<Texture2D>',
718=>'PPtr<Transform>',
734=>'Prefab',
741=>'Quaternionf',
753=>'Rectf',
759=>'RectInt',
767=>'RectOffset',
778=>'second',
785=>'set',
789=>'short',
795=>'size',
800=>'SInt16',
807=>'SInt32',
814=>'SInt64',
821=>'SInt8',
827=>'staticvector',
840=>'string',
847=>'TextAsset',
857=>'TextMesh',
866=>'Texture',
874=>'Texture2D',
884=>'Transform',
894=>'TypelessData',
907=>'UInt16',
914=>'UInt32',
921=>'UInt64',
928=>'UInt8',
934=>'unsigned int',
947=>'unsigned long long',
966=>'unsigned short',
981=>'vector',
988=>'Vector2f',
997=>'Vector3f',
1006=>'Vector4f',
1015=>'m_ScriptingClassIdentifier',
1042=>'Gradient'
}
CLASS_ID = {
1=>'GameObject',
2=>'Component',
3=>'LevelGameManager',
4=>'Transform',
5=>'TimeManager',
6=>'GlobalGameManager',
8=>'Behaviour',
9=>'GameManager',
11=>'AudioManager',
12=>'ParticleAnimator',
13=>'InputManager',
15=>'EllipsoidParticleEmitter',
17=>'Pipeline',
18=>'EditorExtension',
19=>'Physics2DSettings',
20=>'Camera',
21=>'Material',
23=>'MeshRenderer',
25=>'Renderer',
26=>'ParticleRenderer',
27=>'Texture',
28=>'Texture2D',
29=>'SceneSettings',
30=>'GraphicsSettings',
33=>'MeshFilter',
41=>'OcclusionPortal',
43=>'Mesh',
45=>'Skybox',
47=>'QualitySettings',
48=>'Shader',
49=>'TextAsset',
50=>'Rigidbody2D',
51=>'Physics2DManager',
53=>'Collider2D',
54=>'Rigidbody',
55=>'PhysicsManager',
56=>'Collider',
57=>'Joint',
58=>'CircleCollider2D',
59=>'HingeJoint',
60=>'PolygonCollider2D',
61=>'BoxCollider2D',
62=>'PhysicsMaterial2D',
64=>'MeshCollider',
65=>'BoxCollider',
66=>'SpriteCollider2D',
68=>'EdgeCollider2D',
72=>'ComputeShader',
74=>'AnimationClip',
75=>'ConstantForce',
76=>'WorldParticleCollider',
78=>'TagManager',
81=>'AudioListener',
82=>'AudioSource',
83=>'AudioClip',
84=>'RenderTexture',
87=>'MeshParticleEmitter',
88=>'ParticleEmitter',
89=>'Cubemap',
90=>'Avatar',
91=>'AnimatorController',
92=>'GUILayer',
93=>'RuntimeAnimatorController',
94=>'ScriptMapper',
95=>'Animator',
96=>'TrailRenderer',
98=>'DelayedCallManager',
102=>'TextMesh',
104=>'RenderSettings',
108=>'Light',
109=>'CGProgram',
110=>'BaseAnimationTrack',
111=>'Animation',
114=>'MonoBehaviour',
115=>'MonoScript',
116=>'MonoManager',
117=>'Texture3D',
118=>'NewAnimationTrack',
119=>'Projector',
120=>'LineRenderer',
121=>'Flare',
122=>'Halo',
123=>'LensFlare',
124=>'FlareLayer',
125=>'HaloLayer',
126=>'NavMeshAreas',
127=>'HaloManager',
128=>'Font',
129=>'PlayerSettings',
130=>'NamedObject',
131=>'GUITexture',
132=>'GUIText',
133=>'GUIElement',
134=>'PhysicMaterial',
135=>'SphereCollider',
136=>'CapsuleCollider',
137=>'SkinnedMeshRenderer',
138=>'FixedJoint',
140=>'RaycastCollider',
141=>'BuildSettings',
142=>'AssetBundle',
143=>'CharacterController',
144=>'CharacterJoint',
145=>'SpringJoint',
146=>'WheelCollider',
147=>'ResourceManager',
148=>'NetworkView',
149=>'NetworkManager',
150=>'PreloadData',
152=>'MovieTexture',
153=>'ConfigurableJoint',
154=>'TerrainCollider',
155=>'MasterServerInterface',
156=>'TerrainData',
157=>'LightmapSettings',
158=>'WebCamTexture',
159=>'EditorSettings',
160=>'InteractiveCloth',
161=>'ClothRenderer',
162=>'EditorUserSettings',
163=>'SkinnedCloth',
164=>'AudioReverbFilter',
165=>'AudioHighPassFilter',
166=>'AudioChorusFilter',
167=>'AudioReverbZone',
168=>'AudioEchoFilter',
169=>'AudioLowPassFilter',
170=>'AudioDistortionFilter',
171=>'SparseTexture',
180=>'AudioBehaviour',
181=>'AudioFilter',
182=>'WindZone',
183=>'Cloth',
184=>'SubstanceArchive',
185=>'ProceduralMaterial',
186=>'ProceduralTexture',
191=>'OffMeshLink',
192=>'OcclusionArea',
193=>'Tree',
194=>'NavMeshObsolete',
195=>'NavMeshAgent',
196=>'NavMeshSettings',
197=>'LightProbesLegacy',
198=>'ParticleSystem',
199=>'ParticleSystemRenderer',
200=>'ShaderVariantCollection',
205=>'LODGroup',
206=>'BlendTree',
207=>'Motion',
208=>'NavMeshObstacle',
210=>'TerrainInstance',
212=>'SpriteRenderer',
213=>'Sprite',
214=>'CachedSpriteAtlas',
215=>'ReflectionProbe',
216=>'ReflectionProbes',
218=>'Terrain',
220=>'LightProbeGroup',
221=>'AnimatorOverrideController',
222=>'CanvasRenderer',
223=>'Canvas',
224=>'RectTransform',
225=>'CanvasGroup',
226=>'BillboardAsset',
227=>'BillboardRenderer',
228=>'SpeedTreeWindAsset',
229=>'AnchoredJoint2D',
230=>'Joint2D',
231=>'SpringJoint2D',
232=>'DistanceJoint2D',
233=>'HingeJoint2D',
234=>'SliderJoint2D',
235=>'WheelJoint2D',
238=>'NavMeshData',
240=>'AudioMixer',
241=>'AudioMixerController',
243=>'AudioMixerGroupController',
244=>'AudioMixerEffectController',
245=>'AudioMixerSnapshotController',
246=>'PhysicsUpdateBehaviour2D',
247=>'ConstantForce2D',
248=>'Effector2D',
249=>'AreaEffector2D',
250=>'PointEffector2D',
251=>'PlatformEffector2D',
252=>'SurfaceEffector2D',
258=>'LightProbes',
271=>'SampleClip',
272=>'AudioMixerSnapshot',
273=>'AudioMixerGroup',
290=>'AssetBundleManifest',
1001=>'Prefab',
1002=>'EditorExtensionImpl',
1003=>'AssetImporter',
1004=>'AssetDatabase',
1005=>'Mesh3DSImporter',
1006=>'TextureImporter',
1007=>'ShaderImporter',
1008=>'ComputeShaderImporter',
1011=>'AvatarMask',
1020=>'AudioImporter',
1026=>'HierarchyState',
1027=>'GUIDSerializer',
1028=>'AssetMetaData',
1029=>'DefaultAsset',
1030=>'DefaultImporter',
1031=>'TextScriptImporter',
1032=>'SceneAsset',
1034=>'NativeFormatImporter',
1035=>'MonoImporter',
1037=>'AssetServerCache',
1038=>'LibraryAssetImporter',
1040=>'ModelImporter',
1041=>'FBXImporter',
1042=>'TrueTypeFontImporter',
1044=>'MovieImporter',
1045=>'EditorBuildSettings',
1046=>'DDSImporter',
1048=>'InspectorExpandedState',
1049=>'AnnotationManager',
1050=>'PluginImporter',
1051=>'EditorUserBuildSettings',
1052=>'PVRImporter',
1053=>'ASTCImporter',
1054=>'KTXImporter',
1101=>'AnimatorStateTransition',
1102=>'AnimatorState',
1105=>'HumanTemplate',
1107=>'AnimatorStateMachine',
1108=>'PreviewAssetType',
1109=>'AnimatorTransition',
1110=>'SpeedTreeImporter',
1111=>'AnimatorTransitionBase',
1112=>'SubstanceImporter',
1113=>'LightmapParameters',
1120=>'LightmapSnapshot'
}
end
+1
View File
@@ -0,0 +1 @@
require 'mikunyan/decoders/image_decoder'
+161
View File
@@ -0,0 +1,161 @@
begin; require 'oily_png'; rescue LoadError; require 'chunky_png'; end
require 'bin_utils'
module Mikunyan
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]]
def self.decode_object(object)
return nil unless object.class == ObjectValue
endian = object.endian
width = object['m_Width']
height = object['m_Height']
bin = object['image data']
fmt = object['m_TextureFormat']
return nil unless width && height && bin && fmt
width = width.value
height = height.value
bin = bin.value
fmt = fmt.value
case fmt
when 1
decode_a8(width, height, bin)
when 2
decode_argb4444(width, height, bin, endian)
when 3
decode_rgb888(width, height, bin, endian)
when 4
decode_rgba8888(width, height, bin, endian)
when 5
decode_argb8888(width, height, bin, endian)
when 7
decode_rgb565(width, height, bin, endian)
when 13
decode_rgba4444(width, height, bin, endian)
when 34
decode_etc1(width, height, bin)
else
nil
end
end
def self.decode_etc1(width, height, bin)
bw = (width + 3) / 4
bh = (height + 3) / 4
pixels = "\0" * (64 * bw * bh)
pixels.force_encoding('ascii-8bit')
bh.times do |by|
bw.times do |bx|
block = decode_etc1_block(BinUtils.get_sint64_be(bin, (bx + by * bw) * 8)).pack('N16')
pixels[( bx * 4 * bh + by) * 16, 16] = block.byteslice( 0, 16)
pixels[((bx * 4 + 1) * bh + by) * 16, 16] = block.byteslice(16, 16)
pixels[((bx * 4 + 2) * bh + by) * 16, 16] = block.byteslice(32, 16)
pixels[((bx * 4 + 3) * bh + by) * 16, 16] = block.byteslice(48, 16)
end
end
ChunkyPNG::Image.from_rgba_stream(bh * 4, bw * 4, pixels).rotate_right!.crop!(0, 0, width, height)
end
def self.decode_a8(width, height, bin)
pixels = bin.unpack('C*').map do |c|
c << 24 | c << 16 | c << 8 | 0xff
end
ChunkyPNG::Image.new(width, height, pixels)
end
def self.decode_rgb565(width, height, bin, endian = :big)
pixels = bin.unpack(endian == :little ? 'v*' : 'n*').map do |c|
r = (c & 0xf800) >> 8
g = (c & 0x07e0) >> 3
b = (c & 0x001f) << 3
r = r | r >> 5
g = g | g >> 6
b = b | b >> 5
r << 24 | g << 16 | b << 8 | 0xff
end
ChunkyPNG::Image.new(width, height, pixels)
end
def self.decode_rgb888(width, height, bin, endian = :big)
if endian == :little
ChunkyPNG::Image.from_bgr_stream(width, height, bin)
else
ChunkyPNG::Image.from_rgb_stream(width, height, bin)
end
end
def self.decode_rgba4444(width, height, bin, endian = :big)
pixels = bin.unpack(endian == :little ? 'v*' : 'n*').map do |c|
c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f)
c << 4 | c
end
ChunkyPNG::Image.new(width, height, pixels)
end
def self.decode_argb4444(width, height, bin, endian = :big)
pixels = bin.unpack(endian == :little ? 'v*' : 'n*').map do |c|
c = ((c & 0x0f00) << 16) | ((c & 0x00f0) << 12) | ((c & 0x000f) << 8) | ((c & 0xf000) >> 12)
c << 4 | c
end
ChunkyPNG::Image.new(width, height, pixels)
end
def self.decode_rgba8888(width, height, bin, endian = :big)
if endian == :little
ChunkyPNG::Image.from_abgr_stream(width, height, bin)
else
ChunkyPNG::Image.from_rgba_stream(width, height, bin)
end
end
def self.decode_argb8888(width, height, bin, endian = :big)
pixels = bin.unpack(endian == :little ? 'V*' : 'N*').map do |c|
c = ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24)
end
ChunkyPNG::Image.new(width, height, pixels)
end
private
def self.decode_etc1_block(bin)
colors = []
codes = [bin >> 37 & 7, bin >> 34 & 7]
subblocks = Etc1SubblockTable[bin[32]]
if bin[33] == 0
colors[0] = bin >> 40 & 0xf0f0f0
colors[0] = colors[0] | colors[0] >> 4
colors[1] = bin >> 36 & 0xf0f0f0
colors[1] = colors[1] | colors[1] >> 4
else
colors[0] = bin >> 40 & 0xf8f8f8
dr = (bin >> 56 & 3) - (bin >> 56 & 4)
dg = (bin >> 48 & 3) - (bin >> 48 & 4)
db = (bin >> 40 & 3) - (bin >> 40 & 4)
colors[1] = colors[0] + (dr << 19) + (dg << 11) + (db << 3)
colors[0] = colors[0] | (colors[0] >> 5 & 0x70707)
colors[1] = colors[1] | (colors[1] >> 5 & 0x70707)
end
ret = Array.new(16, 0)
16.times do |i|
modifier = Etc1ModifierTable[codes[subblocks[i]]][bin[i]]
ret[i] = etc1colormod(colors[subblocks[i]], bin[i + 16] == 0 ? modifier : -modifier)
end
ret
end
def self.etc1colormod(color, modifier)
r = (color >> 16 & 0xff) + modifier
g = (color >> 8 & 0xff) + modifier
b = (color & 0xff) + modifier
r = (r > 255 ? 255 : r < 0 ? 0 : r)
g = (g > 255 ? 255 : g < 0 ? 0 : g)
b = (b > 255 ? 255 : b < 0 ? 0 : b)
r << 24 | g << 16 | b << 8 | 0xff
end
end
end
+58
View File
@@ -0,0 +1,58 @@
module Mikunyan
class ObjectValue
attr_accessor :name, :type, :value, :endian, :is_struct
def initialize(name, type, endian, value = nil)
@name = name
@type = type
@endian = endian
@value = value
@is_struct = false
@attr = {}
end
def array?
value && value.class == Array
end
def value?
value && value.class != Array
end
def struct?
is_struct
end
def keys
@attr.keys
end
def key?(key)
@attr.key?(key)
end
def []
@value
end
def [](name)
if array? && name.class == Integer
@value[name]
else
@attr[name]
end
end
def []=(name, value)
@attr[name] = value
end
def method_missing(name, *args)
@attr[name.to_s]
end
def respond_to_missing?(symbol, include_private)
@attr.key?(symbol.to_s)
end
end
end
+61
View File
@@ -0,0 +1,61 @@
module Mikunyan
class TypeTree
attr_accessor :nodes
Node = Struct.new(:version, :depth, :array?, :type, :name, :size, :index, :flags)
def self.load(br)
nodes = []
node_count = br.i32u
buffer_size = br.i32u
node_count.times do
nodes << Node.new(br.i16u, br.i8u, br.i8u != 0, br.i32, br.i32, br.i32, br.i32u, br.i32u)
end
buffer = br.read(buffer_size)
nodes.each do |n|
if n.type >= 0
n.type = buffer.unpack("@#{n.type}Z*")[0]
else
n.type = Mikunyan::STRING_TABLE[n.type + 2**31]
end
if n.name >= 0
n.name = buffer.unpack("@#{n.name}Z*")[0]
else
n.name = Mikunyan::STRING_TABLE[n.name + 2**31]
end
end
r = TypeTree.new
r.nodes = nodes
r
end
def self.load_legacy(br)
nodes = []
stack = [0]
while stack.size > 0
depth = stack.pop
type = br.cstr
name = br.cstr
size = br.i32
index = br.i32u
is_array = (br.i32 != 0)
version = br.i32u
flags = br.i32u
child_count = br.i32u
child_count.times{ stack << depth + 1 }
nodes << Node.new(version, depth, is_array, type, name, size, index, flags)
end
r = TypeTree.new
r.nodes = nodes
r
end
def self.load_default(hash)
hash_str = hash.unpack('H*')[0]
file = File.expand_path("../typetrees/#{hash_str}.dat", __FILE__)
return nil unless File.file?(file)
r = TypeTree.new
r.nodes = Marshal.load(File.binread(file))
r
end
end
end

Some files were not shown because too many files have changed in this diff Show More