xar unarchiver (.xar, .pkg, .xip)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

implement initial decompression logic

+50 -11
+50 -11
czar.cr
··· 1 1 #!/usr/bin/env crystal 2 - 3 2 require "binary_parser" 4 3 require "compress/zlib" 4 + require "compress/gzip" 5 5 require "xml" 6 6 7 + require "./sliceio" 8 + require "./bzip2" 9 + 7 10 def perror(msg : String) 8 - STDERR.write Slice.new (msg + "\n").to_unsafe, (msg + "\n").size 11 + STDERR.write Slice.new((msg + "\n").to_unsafe, (msg + "\n").size) 9 12 exit 1 10 13 end 11 14 ··· 117 120 file.size = (xml_value(entity, "size").first rescue 0).to_u64 118 121 119 122 data = xml_select(entity, "data") 120 - unless data.size < 1 123 + unless data.empty? 121 124 xar_decode_data data.first, file.data 122 125 end 123 - 124 126 ea = xml_select(entity, "ea") 125 - unless ea.size < 1 127 + unless ea.empty? 126 128 xar_decode_ea ea.first, file.ea 127 129 end 128 - 129 130 files = [file] 130 131 children = xml_select(entity, "file") 131 132 if children.size > 0 ··· 141 142 142 143 perror "no filename given" if ARGV.size == 0 143 144 144 - File.open ARGV.first, "r" do |file| 145 + File.open(ARGV.first, "r") do |file| 145 146 header = XARHeader.new 146 147 header.load file 147 148 ··· 156 157 157 158 toc_data = Bytes.new header.length_uncompressed 158 159 file.seek header.header_size 160 + 159 161 Compress::Zlib::Reader.open file do |zfile| 160 162 zfile.read toc_data 161 163 end ··· 166 168 167 169 tocs = xml_select(xar_obj.first, "toc") 168 170 perror "empty TOC" if tocs.empty? 169 - toc = tocs.first 170 171 172 + toc = tocs.first 171 173 puts "reading TOC" 172 - xar = XAR.new 173 174 175 + xar = XAR.new 174 176 elem = xml_select(toc, "checksum").first 175 177 xar.checksum.style = XARChecksumAlgo.parse elem["style"] 176 178 xar.checksum.size = xml_value(elem, "size").first.to_u64 177 179 xar.checksum.offset = xml_value(elem, "offset").first.to_u64 178 - 179 180 puts "TOC is checksummed as #{xar.checksum.style}, #{xar.checksum.size} bytes at offset #{xar.checksum.offset}" 180 181 181 182 xml_select(toc, "file").each do |entity| ··· 183 184 end 184 185 185 186 puts "contains #{xar.files.select { |e| e.type == XARFileType::FILE }.size} files across #{xar.files.select { |e| e.type == XARFileType::DIRECTORY }.size} directories" 187 + puts xar.files.map{ |e| "#{e.path}#{e.name}" }.join " " 186 188 187 - puts xar.files.map{ |e| "#{e.path}#{e.name}" }.join " " 189 + # Unarchive files 190 + xar.files.each do |xarfile| 191 + next if xarfile.type == XARFileType::DIRECTORY 192 + 193 + output_path = File.join("#{ARGV.first}.extracted", xarfile.path, xarfile.name) 194 + Dir.mkdir_p(File.dirname(output_path)) unless File.exists?(File.dirname(output_path)) 195 + 196 + # Log file metadata 197 + puts "Processing file: #{output_path}" 198 + puts " Offset: #{xarfile.data.offset}" 199 + puts " Size: #{xarfile.data.size}" 200 + puts " Encoding: #{xarfile.data.encoding}" 201 + 202 + # Seek to the correct offset in the archive file 203 + file.seek header.header_size.to_u64 + header.length_compressed + xarfile.data.offset 204 + 205 + # Read the compressed data 206 + compressed_data = Bytes.new(xarfile.data.size) 207 + file.read(compressed_data) 208 + 209 + # Decompress the data if necessary 210 + decompressed_data = case xarfile.data.encoding 211 + when XARFileEncoding::GZIP 212 + Compress::Gzip::Reader.new(SliceIO.new(compressed_data)).getb_to_end 213 + when XARFileEncoding::BZIP2 214 + Bzip2::Reader.new(SliceIO.new(compressed_data)).getb_to_end 215 + else 216 + compressed_data 217 + end 218 + 219 + # Write the decompressed data to the output file 220 + begin 221 + File.write(output_path, decompressed_data) 222 + puts "Extracted: #{output_path}" 223 + rescue e 224 + perror "Error writing file #{output_path}: #{e}" 225 + end 226 + end 188 227 end