2 Commits

7 changed files with 177 additions and 12 deletions

View File

@@ -32,10 +32,12 @@ module W3DHubLauncher
end
# Downloads requested url and on success check if host element is still in the layout and call the block if so.
def remote_image(path, url:, element:, &block)
def remote_image(path, url:, element:, required_path: nil, &block)
memcache_key = :"lock_net_download_#{element.style.tag}"
if !File.exist?(path) && MemCache[memcache_key].nil?
path_exists = File.exist?(path)
if !path_exists && MemCache[memcache_key].nil?
MemCache[memcache_key] = true
Worker::Api.download_url(url, path) do |result|
@@ -53,6 +55,14 @@ module W3DHubLauncher
end
end
end
if required_path && path_exists && !File.exist?(required_path)
e = find_element_by_tag(element.root, element.style.tag)
return unless e
block.call(element, path)
end
end
def on_main_thread(block)

View File

@@ -47,7 +47,7 @@ module W3DHubLauncher
def blur
@parent.hide_battleview_panel
CyberarmEngine::EventBus.unsubscribe("application_changed")
CyberarmEngine::EventBus.unsubscribe("application_changed", self)
end
def populate_game_content_container
@@ -154,7 +154,7 @@ module W3DHubLauncher
btn = button(safe_get_image("#{CACHE_PATH}/icon_#{game.id}.png"), tag: :"image_icon_#{game.id}", style_class: [:app_icon_button], enabled: @current_app.id != game.id, tip: game.name) do |btn|
populate_game(game, game&.channels&.first)
end
remote_image("#{CACHE_PATH}/icon_#{game.id}.ico", url: "https://s3.w3d.cyberarm.dev/games/#{game.id}/#{game.id}.ico", element: btn) do |e, path|
remote_image("#{CACHE_PATH}/icon_#{game.id}.ico", required_path: "#{CACHE_PATH}/icon_#{game.id}.png", url: "https://s3.w3d.cyberarm.dev/games/#{game.id}/#{game.id}.ico", element: btn) do |e, path|
Worker::Api.ico_to_png(ico_path: path, png_path: path.sub(".ico", ".png")) do |result|
e&.value = safe_get_image(result.data["path"]) if result.okay?
end

View File

@@ -19,6 +19,12 @@ module W3DHubLauncher
@sha256_checksum = hash["checksum"]
@checksum_chunks = hash["checksum-chunks"]
@download_url = hash["download_url"]
# Fix crashing on spaces
if @download_url
uri_path = @download_url.split("/").last
@download_url = @download_url.sub(uri_path, URI.encode_uri_component(uri_path))
end
end
def error?

View File

@@ -0,0 +1,107 @@
module W3DHubLauncher
class Worker
class Api
# Cached verifcation data for application and pending package files
class VerificationData
SCHEMA = 0
def initialize(path)
@path = path
data = {}
if File.exist?(@path)
begin
data = JSON.parse(File.read(@path))
rescue JSON::ParserError
data = {}
end
end
data = {} unless data.is_a?(Hash)
@schema = data["schema"] || SCHEMA
@data = {}
data["data"]&.each do |key, value|
@data[key] = Data.new(value)
end
end
def key(file_name, file_version: "", package: false)
file_name = file_name.gsub("\\", "/").downcase
if package
"package:{file_name}:#{file_version}"
else
file_name
end
end
def data(key)
@data[key]
end
def unchanged?(key, checksum:, file_path:)
data = data(key)
return nil unless data
return data if data.checksum.upcase.strip == checksum.upcase.strip &&
data.modification_time == File.mtime(file_path).to_i &&
data.file_size == File.size(file_path)
nil
end
def set(key, checksum:, file_path:)
data = Data.new(
{
"checksum" => checksum.upcase.strip,
"modification_time" => File.mtime(file_path).to_i,
"file_size" => File.size(file_path)
}
)
@data[key] = data
save
data
end
def delete(key)
data = @data.delete(key)
save
data
end
def save
File.write(@path, JSON.pretty_generate(self))
end
def to_json(options = {})
{
schema: SCHEMA,
data: @data
}.to_json(options)
end
class Data
attr_reader :checksum, :modification_time, :file_size
def initialize(data)
@checksum = data["checksum"]
@modification_time = data["modification_time"]
@file_size = data["file_size"]
end
def to_json(options = {})
{
checksum: @checksum,
modification_time: @modification_time,
file_size: @file_size
}.to_json(options)
end
end
end
end
end
end

View File

@@ -19,6 +19,8 @@ module W3DHubLauncher
@status = Worker::Request::STATUS_PENDING
@verification_data = Worker::Api::VerificationData.new("#{CACHE_PATH}/verification_data-#{@application.id}-#{@channel.id}.json")
# caching mechinism for file path normalization
@file_index = {}
# manifests!
@@ -32,6 +34,10 @@ module W3DHubLauncher
# packages to verify and/or download
@packages = []
# whether task should perform a per file checksum or just read it from the cache and
# check file modification time and size
@force_verification = false
setup
end
@@ -136,7 +142,7 @@ module W3DHubLauncher
# Process manifest game files in NEWEST to OLDEST order so that we don't erroneously flag
# valid files as invalid due to an OLDER version of the file being checked FIRST.
@manifest_files.reverse.each do |file|
@manifest_files.reverse.each_with_index do |file, i|
break unless File.directory?(@installation_directory)
# skip irrelevant older versions of files
@@ -147,8 +153,15 @@ module W3DHubLauncher
next unless File.exist?(file_path) # && !File.directory?(file_path)
puts "verifying #{file_path}..."
checksum = cached_checksums[file_path] || Digest::SHA256.file(file_path).hexdigest.upcase
cached_checksums[file_path] ||= checksum
key = @verification_data.key(file.name)
pp [key, @force_verification]
verification_data = @force_verification ? nil : @verification_data.unchanged?(key, checksum: file.checksum, file_path: file_path)
# Use cached checksum if the file appears unchanged, otherwise recalculate it
checksum = verification_data&.checksum || Digest::SHA256.file(file_path).hexdigest.upcase
# Set or update cached verification data if files don't match
@verification_data.set(key, checksum: checksum, file_path: file_path) unless verification_data
if checksum == file.checksum
@required_manifest_files.delete(file)
@@ -159,6 +172,8 @@ module W3DHubLauncher
else
puts " failed verification:#{file.version}"
end
update_status(label: "Verifying files...", fraction: i.to_f / @manifest_files.size)
end
end
@@ -249,7 +264,7 @@ module W3DHubLauncher
if manifest_file.patch?
apply_patch(manifest_file, package)
else
unzip(package_path)
unzip(manifest_file, package_path)
end
processed_packages[package_path] = manifest_file
@@ -265,6 +280,8 @@ module W3DHubLauncher
if File.exist?(file_path) && !File.directory?(file_path)
puts "Removing file: #{file_path}"
File.delete(file_path)
key = @verification_data.key(manifest_file.name)
@verification_data.delete(key)
remove_from_file_index(file_path)
end
@@ -285,7 +302,7 @@ module W3DHubLauncher
end
end
# updated, and moved applications will overwrite existing application data in settings
# updated, repaired, and moved applications will overwrite existing application data in settings
def mark_application_installed
app = Worker::Api::Settings::Application.create(
id: @application.id,
@@ -469,7 +486,7 @@ module W3DHubLauncher
package_path = package_cache_path(package)
puts "unpacking patch..."
unzip(package_path, f.path)
unzip(false, package_path, output_path: f.path)
puts "reading patch data.."
patch_mix = W3DHubLauncher::WWMix.new(path: f.path)
@@ -504,12 +521,16 @@ module W3DHubLauncher
raise temp_mix.error_reason unless temp_mix.save
puts "copying file..."
FileUtils.cp(temp.path, normalize_path(manifest_file.name))
file_path = normalize_path(manifest_file.name)
FileUtils.cp(temp.path, file_path)
key = @verification_data.key(manifest_file.name)
@verification_data.set(key, checksum: manifest_file.checksum, file_path: file_path)
end
end
end
def unzip(zip_path, output_path = nil)
def unzip(manifest_file, zip_path, output_path: nil)
stream = Zip::InputStream.new(File.open(zip_path))
while(entry = stream.get_next_entry)
@@ -529,6 +550,23 @@ module W3DHubLauncher
end
end
# the manifest file we receive MIGHT NOT be for the current zip file entry
# so look it up to be safe
if manifest_file
manifest = @manifests[manifest_file.version]
if manifest
entry_manifest_file = manifest.files.find do |f|
f.name.gsub("\\", "/").downcase == entry.name.gsub("\\", "/").downcase
end
if entry_manifest_file
key = @verification_data.key(entry.name)
@verification_data.set(key, checksum: entry_manifest_file.checksum, file_path: file_path)
end
end
end
add_to_file_index(file_path)
end
end

View File

@@ -4,6 +4,9 @@ module W3DHubLauncher
def setup
super
# User requested a repair, make sure everything is as it should be!
@force_verification = true
@task_verb = "Repairing".freeze
end
end

View File

@@ -60,6 +60,7 @@ require_relative "lib/worker/api/server_event"
require_relative "lib/worker/api/test_event"
require_relative "lib/worker/api/legacy_manifest"
require_relative "lib/worker/api/legacy_manifest_package"
require_relative "lib/worker/api/verification_data"
require_relative "lib/worker/request"
require_relative "lib/worker/w3dhub_api"
require_relative "lib/worker/task"