lib/u3d/utils.rb in u3d-1.2.3 vs lib/u3d/utils.rb in u3d-1.3.0
- old
+ new
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
## --- BEGIN LICENSE BLOCK ---
# Copyright (c) 2016-present WeWantToKnow AS
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +27,15 @@
require 'filesize'
require 'u3d_core/helper'
module U3d
# Several different utility methods
- # rubocop:disable ModuleLength
+ # rubocop:disable Metrics/ModuleLength
module Utils
# Regex to capture each part of a version string (0.0.0x0)
CSIDL_LOCAL_APPDATA = 0x001c
- UNITY_VERSION_REGEX = /(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:(\w)(?:(\d+))?)?/
+ UNITY_VERSION_REGEX = /(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:(\w)(?:(\d+))?)?/.freeze
class << self
def final_url(url, redirect_limit: 10)
follow_redirects(url, redirect_limit: redirect_limit, http_method: :head) do |request, _response|
request.uri.to_s
@@ -51,10 +53,11 @@
end
end
def follow_redirects(url, redirect_limit: 10, http_method: :get, request_headers: {}, &block)
raise 'Too many redirections' if redirect_limit.zero?
+
response = nil
request = nil
uri = URI(url)
begin
use_ssl = /^https/.match(url)
@@ -63,28 +66,29 @@
request_headers.each do |k, v|
request[k] = v
end
response = http.request request
end
- rescue OpenSSL::OpenSSLError => ssl_error
+ rescue OpenSSL::OpenSSLError => e
UI.error 'SSL has faced an error, you may want to check our README to fix it'
- raise ssl_error
+ raise e
end
case response
- when Net::HTTPSuccess then
+ when Net::HTTPSuccess
yield(request, response)
- when Net::HTTPRedirection then
+ 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
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
# size a hint of the expected size
def download_file(path, url, size: nil)
@@ -109,14 +113,16 @@
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
@@ -138,10 +144,11 @@
end
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
@@ -153,13 +160,14 @@
def get_write_access(dir)
if U3dCore::Helper.operating_system == :win
yield
else
- stat_command = if U3dCore::Helper.operating_system == :linux
+ stat_command = case U3dCore::Helper.operating_system
+ when :linux
"stat -c \"%U,%a\" #{dir}"
- elsif U3dCore::Helper.operating_system == :mac
+ 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)
@@ -197,33 +205,91 @@
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'
+ 'version format 0.0.0x0'
end
[ver[1], ver[2], ver[3], ver[4], ver[5]]
end
def windows_local_appdata
- require 'win32api'
+ 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
-
- getdir = Win32API.new('shell32', 'SHGetFolderPath', 'LLLLP', 'L')
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.expand_path(windir.rstrip)
return windir if Dir.exist? windir
+
raise "Local Appdata retrieved (#{windir}) is not correct"
end
+ 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
+
def pretty_filesize(filesize)
- Filesize.from(filesize.round.to_s + ' B').pretty
+ Filesize.from("#{filesize.round} B").pretty
end
def windows_path(path)
UI.deprecated("Use U3dCore::Helper.windows_path")
U3dCore::Helper.windows_path(path)
@@ -250,15 +316,16 @@
end
private
def http_max_retries
- ENV['U3D_HTTP_MAX_RETRIES'].to_i if ENV['U3D_HTTP_MAX_RETRIES']
+ ENV.fetch('U3D_HTTP_MAX_RETRIES', nil)&.to_i
end
def http_read_timeout
return ENV['U3D_HTTP_READ_TIMEOUT'].to_i if ENV['U3D_HTTP_READ_TIMEOUT']
+
300
end
def http_opts(opt = {})
# the keys are #ca_file, #ca_path, cert, #cert_store, ciphers, #close_on_empty_response, key, #open_timeout,
@@ -268,7 +335,7 @@
UI.verbose "Using http opts: #{opt}"
opt
end
end
end
- # rubocop:enable ModuleLength
+ # rubocop:enable Metrics/ModuleLength
end