pgtools 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +25 -0
  3. data/bin/bxm_decoder +2 -0
  4. data/bin/bxm_encoder +2 -0
  5. data/bin/clh_convert +2 -0
  6. data/bin/clp_convert +2 -0
  7. data/bin/clw_convert +2 -0
  8. data/bin/dat_creator +2 -0
  9. data/bin/dat_extractor +2 -0
  10. data/bin/dat_ls +2 -0
  11. data/bin/eff_idd_creator +2 -0
  12. data/bin/eff_idd_extractor +2 -0
  13. data/bin/exp_convert_wiiu_pc +2 -0
  14. data/bin/exp_tool +2 -0
  15. data/bin/mot_convert_wiiu_pc +2 -0
  16. data/bin/mot_tool +2 -0
  17. data/bin/pkz_extractor +2 -0
  18. data/bin/scr_creator +2 -0
  19. data/bin/scr_extractor +2 -0
  20. data/bin/wmb_cleanup +2 -0
  21. data/bin/wmb_common_bones +2 -0
  22. data/bin/wmb_convert_pc_switch +2 -0
  23. data/bin/wmb_convert_wiiu_pc +2 -0
  24. data/bin/wmb_export_assimp +2 -0
  25. data/bin/wmb_get_bone_map +2 -0
  26. data/bin/wmb_import_assimp +2 -0
  27. data/bin/wmb_import_nier +2 -0
  28. data/bin/wmb_import_wiiu +2 -0
  29. data/bin/wtb_convert_wiiu_pc +2 -0
  30. data/bin/wtx_creator +2 -0
  31. data/bin/wtx_extractor +2 -0
  32. data/lib/bayonetta/alignment.rb +14 -0
  33. data/lib/bayonetta/bone.rb +55 -0
  34. data/lib/bayonetta/bxm.rb +180 -0
  35. data/lib/bayonetta/clh.rb +159 -0
  36. data/lib/bayonetta/clp.rb +212 -0
  37. data/lib/bayonetta/clw.rb +166 -0
  38. data/lib/bayonetta/dat.rb +261 -0
  39. data/lib/bayonetta/eff.rb +314 -0
  40. data/lib/bayonetta/endianness.rb +53 -0
  41. data/lib/bayonetta/exp.rb +768 -0
  42. data/lib/bayonetta/linalg.rb +416 -0
  43. data/lib/bayonetta/material_database.yaml +2581 -0
  44. data/lib/bayonetta/mot.rb +763 -0
  45. data/lib/bayonetta/pkz.rb +63 -0
  46. data/lib/bayonetta/scr.rb +393 -0
  47. data/lib/bayonetta/tools/bxm_decoder.rb +23 -0
  48. data/lib/bayonetta/tools/bxm_encoder.rb +37 -0
  49. data/lib/bayonetta/tools/clh_convert.rb +60 -0
  50. data/lib/bayonetta/tools/clp_convert.rb +70 -0
  51. data/lib/bayonetta/tools/clw_convert.rb +60 -0
  52. data/lib/bayonetta/tools/dat_creator.rb +57 -0
  53. data/lib/bayonetta/tools/dat_extractor.rb +94 -0
  54. data/lib/bayonetta/tools/dat_ls.rb +106 -0
  55. data/lib/bayonetta/tools/eff_idd_creator.rb +66 -0
  56. data/lib/bayonetta/tools/eff_idd_extractor.rb +73 -0
  57. data/lib/bayonetta/tools/exp_convert_wiiu_pc.rb +33 -0
  58. data/lib/bayonetta/tools/exp_tool.rb +48 -0
  59. data/lib/bayonetta/tools/mot_convert_wiiu_pc.rb +33 -0
  60. data/lib/bayonetta/tools/mot_tool.rb +60 -0
  61. data/lib/bayonetta/tools/pkz_extractor.rb +75 -0
  62. data/lib/bayonetta/tools/scr_creator.rb +63 -0
  63. data/lib/bayonetta/tools/scr_extractor.rb +78 -0
  64. data/lib/bayonetta/tools/wmb_cleanup.rb +250 -0
  65. data/lib/bayonetta/tools/wmb_common_bones.rb +45 -0
  66. data/lib/bayonetta/tools/wmb_convert_pc_switch.rb +35 -0
  67. data/lib/bayonetta/tools/wmb_convert_wiiu_pc.rb +33 -0
  68. data/lib/bayonetta/tools/wmb_export_assimp.rb +479 -0
  69. data/lib/bayonetta/tools/wmb_get_bone_map.rb +50 -0
  70. data/lib/bayonetta/tools/wmb_import_assimp.rb +735 -0
  71. data/lib/bayonetta/tools/wmb_import_geometry_wiiu_pc.rb +472 -0
  72. data/lib/bayonetta/tools/wmb_import_nier.rb +309 -0
  73. data/lib/bayonetta/tools/wtb_convert_wiiu_pc.rb +95 -0
  74. data/lib/bayonetta/tools/wtb_import_textures.rb +103 -0
  75. data/lib/bayonetta/tools/wtx_creator.rb +69 -0
  76. data/lib/bayonetta/tools/wtx_extractor.rb +85 -0
  77. data/lib/bayonetta/vertex_types.yaml +213 -0
  78. data/lib/bayonetta/vertex_types2.yaml +164 -0
  79. data/lib/bayonetta/vertex_types_nier.yaml +145 -0
  80. data/lib/bayonetta/wmb.rb +2443 -0
  81. data/lib/bayonetta/wmb3.rb +759 -0
  82. data/lib/bayonetta/wtb.rb +481 -0
  83. data/lib/bayonetta.rb +60 -0
  84. metadata +254 -0
@@ -0,0 +1,212 @@
1
+ module Bayonetta
2
+
3
+ class CLPFile < LibBin::Structure
4
+
5
+ class FVector < LibBin::Structure
6
+ float :x
7
+ float :y
8
+ float :z
9
+
10
+ def from_bxm_vector(v)
11
+ @x, @y, @z = v.split(" ").collect(&:to_f)
12
+ self
13
+ end
14
+
15
+ def self.from_bxm_vector(v)
16
+ nv = self::new
17
+ nv.from_bxm_vector(v)
18
+ nv
19
+ end
20
+
21
+ end
22
+
23
+ class Header < LibBin::Structure
24
+ int32 :num
25
+ float :limit_spring_rate
26
+ float :spd_rate
27
+ float :stretchy
28
+ int16 :bundle_num
29
+ int16 :bundle_num_2
30
+ float :thick
31
+ register_field :gravity_vec, FVector
32
+ int32 :gravity_parts_no
33
+ float :first_bundle_rate
34
+ register_field :wind_vec, FVector
35
+ int32 :wind_parts_no
36
+ register_field :wind_offset, FVector
37
+ float :wind_sin
38
+ float :hit_adjust_rate
39
+
40
+ def from_bxm_header(h)
41
+ @num = h.at_css("m_Num").content.to_i
42
+ @limit_spring_rate = h.at_css("m_LimitSpringRate").content.to_f
43
+ @spd_rate = h.at_css("m_SpdRate").content.to_f
44
+ @stretchy = h.at_css("m_Stretchy").content.to_f
45
+ @bundle_num = h.at_css("m_BundleNum").content.to_i
46
+ @bundle_num_2 = h.at_css("m_BundleNum2").content.to_i
47
+ @thick = h.at_css("m_Thick").content.to_f
48
+ @gravity_vec = FVector.from_bxm_vector(h.at_css("m_GravityVec").content)
49
+ @gravity_parts_no = h.at_css("m_GravityPartsNo").content.to_i
50
+ @first_bundle_rate = h.at_css("m_FirstBundleRate").content.to_f
51
+ @wind_vec = FVector.from_bxm_vector(h.at_css("m_WindVec").content)
52
+ @wind_parts_no = h.at_css("m_WindPartsNo").content.to_i
53
+ @wind_offset = FVector.from_bxm_vector(h.at_css("m_WindOffset").content)
54
+ @wind_sin = h.at_css("m_WindSin").content.to_f
55
+ @hit_adjust_rate = h.at_css("m_HitAdjustRate").content.to_f
56
+ self
57
+ end
58
+
59
+ def self.from_bxm_header(h)
60
+ nh = self::new
61
+ nh.from_bxm_header(h)
62
+ nh
63
+ end
64
+
65
+ end
66
+
67
+ class Cloth < LibBin::Structure
68
+ int16 :no
69
+ int16 :no_up
70
+ int16 :no_down
71
+ int16 :no_side
72
+ int16 :no_poly
73
+ int16 :no_fix
74
+ float :rot_limit
75
+ register_field :offset, FVector
76
+
77
+ def from_bxm_cloth(h)
78
+ @no = h.at_css("no").content.to_i
79
+ @no_up = h.at_css("noUp").content.to_i
80
+ @no_down = h.at_css("noDown").content.to_i
81
+ @no_side = h.at_css("noSide").content.to_i
82
+ @no_poly = h.at_css("noPoly").content.to_i
83
+ @no_fix = h.at_css("noFix").content.to_i
84
+ @rot_limit = h.at_css("rotLimit").content.to_f
85
+ @offset = FVector.from_bxm_vector(h.at_css("offset").content)
86
+ self
87
+ end
88
+
89
+ def self.from_bxm_cloth(c)
90
+ cl = self::new
91
+ cl.from_bxm_cloth(c)
92
+ cl
93
+ end
94
+
95
+ def remap(map, poly = true, fix = true)
96
+ @no = check_and_remap_bone(map, @no)
97
+ @no_up = check_and_remap_bone(map, @no_up)
98
+ @no_down = check_and_remap_bone(map, @no_down)
99
+ @no_side = check_and_remap_bone(map, @no_side)
100
+ @no_poly = ( poly ? check_and_remap_bone(map, @no_poly) : 4095 )
101
+ @no_fix = ( fix ? check_and_remap_bone(map, @no_fix) : 4095 )
102
+ end
103
+
104
+ private
105
+ def check_and_remap_bone(map, no)
106
+ tmp = map[no]
107
+ raise "No bone found in map for #{no}!" unless tmp
108
+ tmp
109
+ end
110
+
111
+ end
112
+
113
+ register_field :header, Header
114
+ register_field :cloth, Cloth, count: 'header\num'
115
+
116
+ def was_big?
117
+ @__was_big
118
+ end
119
+
120
+ def remap(map, poly = true, fix = true)
121
+ @cloth.each { |c|
122
+ c.remap(map, poly, fix)
123
+ }
124
+ end
125
+
126
+ def self.load_bxm(input_name)
127
+ bxm = BXMFile::load(input_name)
128
+ clp = self::new
129
+
130
+ doc = bxm.to_xml
131
+ h = doc.at_css("//CLOTH//CLOTH_HEADER")
132
+ clp.header = Header.from_bxm_header(h)
133
+
134
+ clp.cloth = doc.css("//CLOTH//CLOTH_WK").collect { |c|
135
+ Cloth.from_bxm_cloth(c)
136
+ }
137
+ clp
138
+ end
139
+
140
+ def self.is_big?(f)
141
+ f.rewind
142
+ f.size
143
+ block = lambda { |int|
144
+ num = f.read(4).unpack(int).first
145
+ ( num >= 0 ) && ( num < 256 )
146
+ }
147
+ big = block.call("l>")
148
+ f.rewind
149
+ small = block.call("l<")
150
+ f.rewind
151
+ raise "Invalid data!" unless big ^ small
152
+ return big
153
+ end
154
+
155
+ def self.convert(input_name, output_name, output_big = false)
156
+ if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
157
+ input = input_name
158
+ else
159
+ input = File.open(input_name, "rb")
160
+ end
161
+ input_big = is_big?(input)
162
+
163
+ if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
164
+ output = output_name
165
+ else
166
+ output = File.open(output_name, "wb")
167
+ end
168
+ output.rewind
169
+
170
+ clp = self::new
171
+ clp.instance_variable_set(:@__was_big, input_big)
172
+ clp.__convert(input, output, input_big, output_big)
173
+
174
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
175
+ output.close unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
176
+ clp
177
+ end
178
+
179
+ def self.load(input_name)
180
+ if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
181
+ input = input_name
182
+ else
183
+ input = File.open(input_name, "rb")
184
+ end
185
+ input_big = is_big?(input)
186
+
187
+ clp = self::new
188
+ clp.instance_variable_set(:@__was_big, input_big)
189
+ clp.__load(input, input_big)
190
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
191
+ clp
192
+ end
193
+
194
+ def dump(output_name, output_big = false)
195
+ if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
196
+ output = output_name
197
+ else
198
+ output = File.open(output_name, "wb")
199
+ end
200
+ output.rewind
201
+
202
+ __set_dump_state(output, output_big, nil, nil)
203
+ __dump_fields
204
+ __unset_dump_state
205
+
206
+ output.close unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
207
+ self
208
+ end
209
+
210
+ end
211
+
212
+ end
@@ -0,0 +1,166 @@
1
+ module Bayonetta
2
+
3
+ class CLWFile < LibBin::Structure
4
+
5
+ class FVector < LibBin::Structure
6
+ float :x
7
+ float :y
8
+ float :z
9
+
10
+ def from_bxm_vector(v)
11
+ @x, @y, @z = v.split(" ").collect(&:to_f)
12
+ self
13
+ end
14
+
15
+ def self.from_bxm_vector(v)
16
+ nv = self::new
17
+ nv.from_bxm_vector(v)
18
+ nv
19
+ end
20
+
21
+ end
22
+
23
+ class ClothWind < LibBin::Structure
24
+ int32 :wind_type
25
+ int32 :parts_no
26
+ register_field :offset, FVector
27
+ register_field :offset2, FVector
28
+ float :radius
29
+ float :power
30
+ float :timer
31
+ float :swing_rate
32
+ float :swing_spd
33
+ float :delay_max
34
+
35
+ def from_bxm_cloth_wind(h)
36
+ @wind_type = h.at_css("WindType").content.to_i
37
+ @parts_no = h.at_css("PartsNo").content.to_i
38
+ @offset = FVector.from_bxm_vector(h.at_css("Offset").content)
39
+ @offset2 = FVector.from_bxm_vector(h.at_css("Offset2").content)
40
+ @radius = h.at_css("Radius").content.to_f
41
+ @power = h.at_css("Power").content.to_f
42
+ @timer = h.at_css("Timer").content.to_f
43
+ @swing_rate = h.at_css("SwingRate").content.to_f
44
+ @swing_spd = h.at_css("SwingSpd").content.to_f
45
+ @delay_max = h.at_css("DelayMax").content.to_f
46
+ self
47
+ end
48
+
49
+ def self.from_bxm_cloth_wind(c)
50
+ clw = self::new
51
+ clw.from_bxm_cloth_wind(c)
52
+ clw
53
+ end
54
+
55
+ def remap(map)
56
+ @parts_no = check_and_remap_bone(map, @parts_no)
57
+ end
58
+
59
+ private
60
+ def check_and_remap_bone(map, no)
61
+ tmp = map[no]
62
+ raise "No bone found in map for #{no}!" unless tmp
63
+ tmp
64
+ end
65
+
66
+ end
67
+
68
+ int32 :cloth_wind_num
69
+ register_field :cloth_wind, ClothWind, count: 'cloth_wind_num'
70
+
71
+ def was_big?
72
+ @__was_big
73
+ end
74
+
75
+ def remap(map)
76
+ @cloth_wind.each { |c|
77
+ c.remap(map)
78
+ }
79
+ end
80
+
81
+ def self.load_bxm(input_name)
82
+ bxm = BXMFile::load(input_name)
83
+ clw = self::new
84
+
85
+ doc = bxm.to_xml
86
+ clw.cloth_wind_num = doc.at_css("//CLOTH_WIND//CLOTH_WIND_NUM").content.to_i
87
+
88
+ clw.cloth_wind = doc.css("//CLOTH_WIND//CLOTH_WIND_WK").collect { |c|
89
+ ClothWind.from_bxm_cloth_wind(c)
90
+ }
91
+ clw
92
+ end
93
+
94
+ def self.is_big?(f)
95
+ f.rewind
96
+ f.size
97
+ block = lambda { |int|
98
+ num = f.read(4).unpack(int).first
99
+ ( num >= 0 ) && ( num < 256 )
100
+ }
101
+ big = block.call("l>")
102
+ f.rewind
103
+ small = block.call("l<")
104
+ f.rewind
105
+ raise "Invalid data!" unless big ^ small
106
+ return big
107
+ end
108
+
109
+ def self.convert(input_name, output_name, output_big = false)
110
+ if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
111
+ input = input_name
112
+ else
113
+ input = File.open(input_name, "rb")
114
+ end
115
+ input_big = is_big?(input)
116
+
117
+ if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
118
+ output = output_name
119
+ else
120
+ output = File.open(output_name, "wb")
121
+ end
122
+ output.rewind
123
+
124
+ clp = self::new
125
+ clp.instance_variable_set(:@__was_big, input_big)
126
+ clp.__convert(input, output, input_big, output_big)
127
+
128
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
129
+ output.close unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
130
+ clp
131
+ end
132
+
133
+ def self.load(input_name)
134
+ if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
135
+ input = input_name
136
+ else
137
+ input = File.open(input_name, "rb")
138
+ end
139
+ input_big = is_big?(input)
140
+
141
+ clp = self::new
142
+ clp.instance_variable_set(:@__was_big, input_big)
143
+ clp.__load(input, input_big)
144
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
145
+ clp
146
+ end
147
+
148
+ def dump(output_name, output_big = false)
149
+ if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
150
+ output = output_name
151
+ else
152
+ output = File.open(output_name, "wb")
153
+ end
154
+ output.rewind
155
+
156
+ __set_dump_state(output, output_big, nil, nil)
157
+ __dump_fields
158
+ __unset_dump_state
159
+
160
+ output.close unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
161
+ self
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -0,0 +1,261 @@
1
+ require 'stringio'
2
+ module Bayonetta
3
+ class DATFile < LibBin::Structure
4
+ include Alignment
5
+ attr_reader :big
6
+ ALIGNMENTS = {
7
+ 'dat' => 0x2000,
8
+ 'wmb' => 0x1000,
9
+ 'wtb' => 0x1000,
10
+ 'wtp' => 0x1000,
11
+ 'wta' => 0x40,
12
+ 'exp' => 0x1000,
13
+ 'sop' => 0x40,
14
+ 'eff' => 0x1000,
15
+ 'sdx' => 0x1000,
16
+ 'bxm' => 0x40
17
+ }
18
+ ALIGNMENTS.default = 0x10
19
+
20
+ class Header < LibBin::Structure
21
+ string :id, 4
22
+ uint32 :num_files
23
+ uint32 :offset_file_offsets
24
+ uint32 :offset_file_extensions
25
+ uint32 :offset_file_names
26
+ uint32 :offset_file_sizes
27
+ uint32 :offset_hash_map
28
+ end
29
+
30
+ class HashMap < LibBin::Structure
31
+ class Header < LibBin::Structure
32
+ uint32 :pre_hash_shift
33
+ uint32 :offset_bucket_ranks
34
+ uint32 :offset_hashes
35
+ uint32 :offset_file_indices
36
+ end
37
+ register_field :header, Header
38
+ int16 :bucket_ranks, count: '(1<<(31 - header\pre_hash_shift))', offset: 'header\offset_bucket_ranks', relative_offset: true
39
+ uint32 :hashes, count: '..\header\num_files', offset: 'header\offset_hashes', relative_offset: true
40
+ uint16 :file_indices, count: '..\header\num_files', offset: 'header\offset_file_indices', relative_offset: true
41
+
42
+ def get
43
+ {
44
+ pre_hash_shift: @header.pre_hash_shift,
45
+ hashes: file_indices.zip(hashes).sort { |(i1 ,h1), (i2, h2)|
46
+ i1 <=> i2
47
+ }.collect { |i, h|
48
+ h
49
+ }
50
+ }
51
+ end
52
+
53
+ def initialize
54
+ super
55
+ @header = Header::new
56
+ end
57
+
58
+ def set(hash_map)
59
+ bit_shift = hash_map[:pre_hash_shift]
60
+ hash_list = hash_map[:hashes]
61
+ num_files = hash_list.size
62
+ @header.pre_hash_shift = bit_shift
63
+ buckets = Hash::new { |h, k| h[k] = [] }
64
+ hash_list.each_with_index { |h, i|
65
+ bucket_index = h >> @header.pre_hash_shift
66
+ buckets[bucket_index].push [h, i]
67
+ }
68
+ @bucket_ranks = []
69
+ @hashes = []
70
+ @file_indices = []
71
+ bucket_rank = 0
72
+ num_buckets = (1 << (31 - header.pre_hash_shift))
73
+ num_buckets.times { |i|
74
+ if buckets.has_key?(i)
75
+ @bucket_ranks.push bucket_rank
76
+ bucket_rank += buckets[i].size
77
+ buckets[i].each { |h, ind|
78
+ @hashes.push h
79
+ @file_indices.push ind
80
+ }
81
+ else
82
+ @bucket_ranks.push -1
83
+ end
84
+ }
85
+ @header.offset_bucket_ranks = 0x10
86
+ @header.offset_hashes = header.offset_bucket_ranks + num_buckets * 2
87
+ @header.offset_file_indices = header.offset_hashes + num_files * 4
88
+ self
89
+ end
90
+ end
91
+
92
+ register_field :header, Header
93
+ uint32 :file_offsets, count: 'header\num_files', offset: 'header\offset_file_offsets'
94
+ string :file_extensions, 4, count: 'header\num_files', offset: 'header\offset_file_extensions'
95
+ uint32 :file_name_length, offset: 'header\offset_file_names'
96
+ string :file_names, count: 'header\num_files', offset: 'header\offset_file_names + 4 + __iterator * file_name_length', sequence: true
97
+ uint32 :file_sizes, count: 'header\num_files', offset: 'header\offset_file_sizes'
98
+ register_field :hash_map, HashMap, offset: 'header\offset_hash_map'
99
+ string :files, 'file_sizes[__iterator]', count: 'header\num_files', offset: 'file_offsets[__iterator]', sequence: true
100
+
101
+ def self.is_big?(f)
102
+ f.rewind
103
+ block = lambda { |big|
104
+ h = Header::load(f, big)
105
+ h.offset_file_offsets < f.size &&
106
+ h.offset_file_names < f.size &&
107
+ h.offset_file_sizes < f.size &&
108
+ h.offset_hash_map < f.size
109
+ }
110
+ big = block.call(true)
111
+ f.rewind
112
+ small = block.call(false)
113
+ f.rewind
114
+ raise "Invalid data!" unless big ^ small
115
+ return big
116
+ end
117
+
118
+ def initialize(big = false)
119
+ @big = big
120
+ super()
121
+ @header = Header::new
122
+ @header.id = "DAT\x00".b
123
+ @header.num_files = 0
124
+ @header.offset_file_offsets = 0
125
+ @header.offset_file_extensions = 0
126
+ @header.offset_file_names = 0
127
+ @header.offset_file_sizes = 0
128
+ @header.offset_hash_map = 0
129
+
130
+ @file_offsets = []
131
+ @file_extensions = []
132
+ @file_name_length = 0
133
+ @file_names = []
134
+ @file_sizes = []
135
+ @files = []
136
+
137
+ @hash_map = nil
138
+ end
139
+
140
+ def invalidate_layout
141
+ @header.offset_file_offsets = 0
142
+ @header.offset_file_extensions = 0
143
+ @header.offset_file_names = 0
144
+ @header.offset_file_sizes = 0
145
+ @header.offset_hash_map = 0
146
+ @file_offsets = []
147
+ @hash_map = nil
148
+ self
149
+ end
150
+
151
+ def layout
152
+ @file_names.collect { |name| name[0..-2] }
153
+ end
154
+
155
+ def each
156
+ if block_given? then
157
+ @header.num_files.times { |i|
158
+ yield @file_names[i][0..-2], StringIO::new(@files[i] ? @files[i] : "", "rb")
159
+ }
160
+ else
161
+ to_enum(:each)
162
+ end
163
+ end
164
+
165
+ def [](i)
166
+ return [@file_names[i][0..-2], StringIO::new(@files[i] ? @files[i] : "", "rb")]
167
+ end
168
+
169
+ def push(name, file)
170
+ invalidate_layout
171
+ @file_names.push name+"\x00"
172
+ if file.kind_of?(StringIO)
173
+ data = file.string
174
+ else
175
+ file.rewind
176
+ data = file.read
177
+ end
178
+ @files.push data
179
+ @file_sizes.push file.size
180
+ extname = File.extname(name)
181
+ raise "Invalid name, missing extension!" if extname == ""
182
+ @file_extensions.push extname[1..-1]+"\x00"
183
+ @header.num_files += 1
184
+ self
185
+ end
186
+
187
+ def compute_layout
188
+ @header.offset_file_offsets = 0x20
189
+ @header.offset_file_extensions = @header.offset_file_offsets + 4 * @header.num_files
190
+ @header.offset_file_names = @header.offset_file_extensions + 4 * @header.num_files
191
+ max_file_name_length = @file_names.collect(&:length).max
192
+ @file_name_length = max_file_name_length
193
+ @header.offset_file_sizes = @header.offset_file_names + 4 + @file_name_length * @header.num_files
194
+ @header.offset_file_sizes = align(@header.offset_file_sizes, 4)
195
+ if @hash_map
196
+ @header.offset_hash_map = @header.offset_file_sizes + 4 * @header.num_files
197
+ files_offset = @header.offset_hash_map + @hash_map.__size(@header.offset_hash_map, self)
198
+ else
199
+ @offset_hash_map = 0
200
+ files_offset = @header.offset_file_sizes + 4 * @header.num_files
201
+ end
202
+ @file_offsets = @header.num_files.times.collect { |i|
203
+ if @file_sizes[i] > 0
204
+ tmp = align(files_offset, ALIGNMENTS[@file_extensions[i][0..-2]])
205
+ files_offset = align(tmp + @file_sizes[i], ALIGNMENTS[@file_extensions[i][0..-2]])
206
+ tmp
207
+ else
208
+ 0
209
+ end
210
+ }
211
+ @total_size = align(files_offset, 0x1000)
212
+ self
213
+ end
214
+
215
+ def set_hash_map(hash)
216
+ @hash_map = HashMap::new
217
+ @hash_map.set hash
218
+ end
219
+
220
+ def self.load(input_name)
221
+ if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
222
+ input = input_name
223
+ else
224
+ File.open(input_name, "rb") { |f|
225
+ input = StringIO::new(f.read, "rb")
226
+ }
227
+ end
228
+ big = self::is_big?(input)
229
+ dat = self::new(big)
230
+ dat.__load(input, big)
231
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
232
+ dat
233
+ end
234
+
235
+ def dump(output_name)
236
+ compute_layout
237
+ if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
238
+ output = output_name
239
+ else
240
+ output = StringIO::new("", "wb")#File.open(output_name, "wb")
241
+ output.write("\x00"*@total_size)
242
+ output.rewind
243
+ end
244
+ output.rewind
245
+
246
+ __set_dump_state(output, @big, nil, nil)
247
+ __dump_fields
248
+ __unset_dump_state
249
+
250
+ unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
251
+ File.open(output_name, "wb") { |f|
252
+ f.write output.string
253
+ }
254
+ output.close
255
+ end
256
+ self
257
+ end
258
+
259
+ end
260
+
261
+ end