Module: U3d::Utils
- Defined in:
- lib/u3d/utils.rb
Overview
Several different utility methods rubocop:disable Metrics/ModuleLength
Constant Summary collapse
- CSIDL_LOCAL_APPDATA =
Regex to capture each part of a version string (0.0.0x0)
0x001c- UNITY_VERSION_REGEX =
/(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:(\w)(?:(\d+))?)?/.freeze
- MOZILLA_AGENT_HEADER =
{ 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0' }.freeze
Class Method Summary collapse
-
.download_file(path, url, size: nil) ⇒ Object
size a hint of the expected size.
- .ensure_dir(dir) ⇒ Object
- .file_exists_not_empty?(path) ⇒ Boolean
- .final_url(url, redirect_limit: 10) ⇒ Object
- .follow_redirects(url, redirect_limit: 10, http_method: :get, request_headers: {}, &block) ⇒ Object
-
.get_ssl(url, redirect_limit: 10, request_headers: {}) ⇒ Object
FIXME: alias deprecated.
- .get_url_content_length(url) ⇒ Object
- .get_write_access(dir) ⇒ Object
- .hashfile(file_path, blocksize: 65_536) ⇒ Object
- .http_request_class(method, uri) ⇒ Object
- .page_content(url, redirect_limit: 10, request_headers: {}) ⇒ Object
- .parse_unity_version(version) ⇒ Object
- .pretty_filesize(filesize) ⇒ Object
-
.print_progress(current, total, started_at) ⇒ Object
if total is nil (unknown, falls back to print_progress_nosize).
- .print_progress_nosize(current, started_at) ⇒ Object
-
.strings(path) ⇒ Object
Ruby implementation of binutils strings.
- .windows_fileversion(info_key, path) ⇒ Object
- .windows_local_appdata ⇒ Object
- .windows_path(path) ⇒ Object
Class Method Details
.download_file(path, url, size: nil) ⇒ Object
size a hint of the expected size
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/u3d/utils.rb', line 101 def download_file(path, url, size: nil) File.open(path, 'wb') do |f| uri = URI(url) current = 0 last_print_update = 0 print_progress = UI.interactive? || U3dCore::Globals.verbose? Net::HTTP.start(uri.host, uri.port, http_opts(use_ssl: uri.scheme == 'https')) do |http| request = Net::HTTP::Get.new uri http.request request do |response| begin # override with actual results, this should help with # innacurrate declared sizes, especially on Windows platform size = Integer(response['Content-Length']) rescue ArgumentError UI.verbose 'Unable to get length of file in download' end started_at = Time.now.to_i - 1 response.read_body do |segment| f.write(segment) current += segment.length # wait for Net::HTTP buffer on slow networks # FIXME revisits, this slows down download on fast network # sleep 0.08 # adjust to reduce CPU next unless print_progress print_progress_now = Time.now.to_f - last_print_update > 0.5 # force printing when done downloading print_progress_now = true if !print_progress_now && size && current >= size next unless print_progress_now last_print_update = Time.now.to_f Utils.print_progress(current, size, started_at) print "\n" unless UI.interactive? end end end print "\n" if print_progress end end |
.ensure_dir(dir) ⇒ Object
164 165 166 |
# File 'lib/u3d/utils.rb', line 164 def ensure_dir(dir) FileUtils.mkpath(dir) unless File.directory?(dir) end |
.file_exists_not_empty?(path) ⇒ Boolean
325 326 327 |
# File 'lib/u3d/utils.rb', line 325 def file_exists_not_empty?(path) File.file?(path) && File.size(path).positive? end |
.final_url(url, redirect_limit: 10) ⇒ Object
43 44 45 46 47 |
# File 'lib/u3d/utils.rb', line 43 def final_url(url, redirect_limit: 10) follow_redirects(url, redirect_limit: redirect_limit, http_method: :head) do |request, _response| request.uri.to_s end end |
.follow_redirects(url, redirect_limit: 10, http_method: :get, request_headers: {}, &block) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/u3d/utils.rb', line 60 def follow_redirects(url, redirect_limit: 10, http_method: :get, request_headers: {}, &block) raise 'Too many redirections' if redirect_limit.zero? # Unity blocks the default Ruby agent, use another one request_headers = MOZILLA_AGENT_HEADER.merge(request_headers) response = nil request = nil uri = URI(url) begin use_ssl = /^https/.match(url) Net::HTTP.start(uri.host, uri.port, http_opts(use_ssl: use_ssl)) do |http| request = http_request_class http_method, uri request_headers.each do |k, v| request[k] = v end response = http.request request end rescue OpenSSL::OpenSSLError => e UI.error 'SSL has faced an error, you may want to check our README to fix it' raise e end case response when Net::HTTPSuccess yield(request, response) when Net::HTTPRedirection UI.verbose "Redirected to #{response['location']}" follow_redirects(response['location'], redirect_limit: redirect_limit - 1, http_method: http_method, request_headers: request_headers, &block) else raise "Request failed with status #{response.code}" end end |
.get_ssl(url, redirect_limit: 10, request_headers: {}) ⇒ Object
FIXME: alias deprecated
50 51 52 |
# File 'lib/u3d/utils.rb', line 50 def get_ssl(url, redirect_limit: 10, request_headers: {}) page_content(url, redirect_limit: redirect_limit, request_headers: request_headers) end |
.get_url_content_length(url) ⇒ Object
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/u3d/utils.rb', line 141 def get_url_content_length(url) UI.verbose "get_url_content_length #{url}" uri = URI(url) size = nil Net::HTTP.start(uri.host, uri.port, http_opts) do |http| response = http.request_head url size = Integer(response['Content-Length']) end UI.verbose "get_url_content_length #{url}: #{size}" size end |
.get_write_access(dir) ⇒ Object
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/u3d/utils.rb', line 168 def get_write_access(dir) if U3dCore::Helper. == :win yield else stat_command = case U3dCore::Helper. when :linux "stat -c \"%U,%a\" #{dir}" when :mac "stat -f \"%Su,%A\" #{dir}" end owner, access = U3dCore::CommandExecutor.execute(command: stat_command, admin: false).strip.split(',') current_user = U3dCore::CommandExecutor.execute(command: 'whoami', admin: false) U3dCore::CommandExecutor.execute(command: "chown #{current_user}: #{dir}", admin: true) U3dCore::CommandExecutor.execute(command: "chmod u+w #{dir}", admin: true) begin yield ensure U3dCore::CommandExecutor.execute(command: "chown #{owner}: #{dir}", admin: true) U3dCore::CommandExecutor.execute(command: "chmod #{access} #{dir}", admin: true) end end end |
.hashfile(file_path, blocksize: 65_536) ⇒ Object
153 154 155 156 157 158 159 160 161 162 |
# File 'lib/u3d/utils.rb', line 153 def hashfile(file_path, blocksize: 65_536) require 'digest' raise ArgumentError, 'Not a file' unless File.file?(file_path) md5 = Digest::MD5.new File.open(file_path, 'r') do |f| md5 << f.read(blocksize) until f.eof? end md5.hexdigest end |
.http_request_class(method, uri) ⇒ Object
93 94 95 96 97 98 |
# File 'lib/u3d/utils.rb', line 93 def http_request_class(method, uri) return Net::HTTP::Get.new uri if method == :get return Net::HTTP::Head.new uri if method == :head raise "Unknown method #{method}" end |
.page_content(url, redirect_limit: 10, request_headers: {}) ⇒ Object
54 55 56 57 58 |
# File 'lib/u3d/utils.rb', line 54 def page_content(url, redirect_limit: 10, request_headers: {}) follow_redirects(url, redirect_limit: redirect_limit, request_headers: request_headers) do |_request, response| response.body end end |
.parse_unity_version(version) ⇒ Object
213 214 215 216 217 218 219 220 |
# File 'lib/u3d/utils.rb', line 213 def parse_unity_version(version) ver = UNITY_VERSION_REGEX.match(version) if ver.nil? raise ArgumentError, "Version (#{version}) does not match the Unity " \ 'version format 0.0.0x0' end [ver[1], ver[2], ver[3], ver[4], ver[5]] end |
.pretty_filesize(filesize) ⇒ Object
296 297 298 |
# File 'lib/u3d/utils.rb', line 296 def pretty_filesize(filesize) Filesize.from("#{filesize.round} B").pretty end |
.print_progress(current, total, started_at) ⇒ Object
if total is nil (unknown, falls back to print_progress_nosize)
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/u3d/utils.rb', line 192 def print_progress(current, total, started_at) if total.nil? print_progress_nosize(current, started_at) return end ratio = [current.to_f / total, 1.0].min percent = (ratio * 100.0).round(1) arrow = (ratio * 20.0).floor time_spent = Time.now.to_i - started_at print("\r[") print('=' * [arrow - 1, 0].max) print('>') print('.' * (20 - arrow)) print("] #{pretty_filesize(current)}/#{pretty_filesize(total)} (#{percent}% at #{pretty_filesize(current.to_f / time_spent)}/s) ") end |
.print_progress_nosize(current, started_at) ⇒ Object
208 209 210 211 |
# File 'lib/u3d/utils.rb', line 208 def print_progress_nosize(current, started_at) time_spent = Time.now.to_i - started_at print("\r>#{pretty_filesize(current)} downloaded at #{pretty_filesize(current.to_f / time_spent)}/s) ") end |
.strings(path) ⇒ Object
Ruby implementation of binutils strings
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/u3d/utils.rb', line 306 def strings(path) min = 4 Enumerator.new do |y| File.open(path, "rb") do |f| s = "" f.each_char do |c| if c =~ /[[:print:]]/ # is there a cleaner way to do this check? s += c next else y.yield s if s.length >= min s = "" end end y.yield s if s.length >= min end end end |
.windows_fileversion(info_key, path) ⇒ Object
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/u3d/utils.rb', line 241 def windows_fileversion(info_key, path) require "fiddle" version_dll = Fiddle.dlopen('version.dll') kernel32 = Fiddle.dlopen('kernel32.dll') get_file_version_info_size = Fiddle::Function.new(version_dll['GetFileVersionInfoSize'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_LONG) get_file_version_info = Fiddle::Function.new(version_dll['GetFileVersionInfo'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) # FIXME: TYPE_INT => TYPE_LONG?? ver_query_value = Fiddle::Function.new(version_dll['VerQueryValue'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) rtl_move_memory = Fiddle::Function.new(kernel32['RtlMoveMemory'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_LONG, Fiddle::TYPE_LONG], Fiddle::TYPE_INT) file = "#{path.tr('/', '\\')}\u0000" s = [0].pack('L') version_size = get_file_version_info_size.call(file, s) raise StandardError if version_size.zero? version_info = ' ' * version_size version_ok = get_file_version_info.call(file, 0, version_size, version_info) raise StandardError if version_ok.zero? # TODO: use GetLastError # hack, giving up using VerQueryValue for now. rstring = version_info.unpack('v*').map { |c| c.chr if c < 256 } * '' r = /#{info_key}..(.*?)\000/.match(rstring) # puts "#{info_key} = #{r ? r[1] : '??'}" return r ? r[1] : nil # rubocop:disable Lint/UnreachableCode # hardcoding lang codepage struct_path = "\\StringFileInfo\\040904b0\\#{info_key}" addr = [0].pack('L') size = [0].pack('L') query_ok = ver_query_value.call(version_info, "#{struct_path}\u0000", addr, size) raise StandardError if query_ok.zero? raddr = addr.unpack1('L') rsize = size.unpack1('L') # this is not working right now, getting seg faults and other low level issues puts "Size: #{raddr} #{rsize}" fixed_info = Array.new(rsize, 0).pack('L*') query_ok = rtl_move_memory.call(fixed_info, raddr, fixed_info.length) raise StandardError if query_ok.zero? info = fixed_info.unpack('L*') file_version = [info[4], info[3], info[6], info[5]] product_version = [info[8], info[7], info[10], info[9]] [file_version, product_version] # rubocop:enable Lint/UnreachableCode rescue StandardError => e UI.error("Failure to find '#{info_key}' under '#{path}': #{e}") UI.error(e.backtrace) nil end |
.windows_local_appdata ⇒ Object
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/u3d/utils.rb', line 222 def windows_local_appdata require "fiddle" shell32 = Fiddle.dlopen('shell32') getdir = Fiddle::Function.new(shell32['SHGetFolderPath'], [Fiddle::TYPE_LONG, Fiddle::TYPE_LONG, Fiddle::TYPE_LONG, Fiddle::TYPE_LONG, Fiddle::TYPE_VOIDP], Fiddle::TYPE_LONG) windir = ' ' * 261 result = getdir.call(0, CSIDL_LOCAL_APPDATA, 0, 0, windir) raise "Unable to get Local Appdata directory, returned with value #{result}" unless result.zero? windir.rstrip! windir = windir.encode("UTF-8", Encoding.find('filesystem')) windir = File.(windir.rstrip) return windir if Dir.exist? windir raise "Local Appdata retrieved (#{windir}) is not correct" end |
.windows_path(path) ⇒ Object
300 301 302 303 |
# File 'lib/u3d/utils.rb', line 300 def windows_path(path) UI.deprecated("Use U3dCore::Helper.windows_path") U3dCore::Helper.windows_path(path) end |