diff --git a/lib/application_manager.rb b/lib/application_manager.rb
index 80287b5..29a4422 100644
--- a/lib/application_manager.rb
+++ b/lib/application_manager.rb
@@ -1,5 +1,7 @@
class W3DHub
class ApplicationManager
+ include CyberarmEngine::Common
+
def initialize
@tasks = [] # :installer, :importer, :repairer, :uninstaller
end
@@ -75,8 +77,33 @@ class W3DHub
end
end
+ def run(app_id, channel, *args)
+ if (app_data = installed?(app_id, channel))
+ Process.spawn(app_data[:install_path], *args)
+ end
+ end
+
+ def installed!(task)
+ # install_dir
+ # installed_version
+ # installPath # game executable
+ # wine_prefix # optional
+
+ install_directory = Cache.install_path(task.application, task.channel)
+ application_data = {
+ install_directory: install_directory,
+ installed_version: task.channel.current_version,
+ install_path: "#{install_directory}/game.exe",
+ wine_prefix: task.wine_prefix
+ }
+
+ window.settings[:games] ||= {}
+ window.settings[:games,][:"#{task.app_id}_#{task.release_channel}"] = application_data
+ window.settings.save_settings
+ end
+
def installed?(app_id, channel)
- false
+ window.settings[:games, :"#{app_id}_#{channel}"]
end
def installing?(app_id, channel)
diff --git a/lib/application_manager/task.rb b/lib/application_manager/task.rb
index 62144bd..f3bfd00 100644
--- a/lib/application_manager/task.rb
+++ b/lib/application_manager/task.rb
@@ -5,7 +5,7 @@ class W3DHub
attr_reader :app_id, :release_channel, :application, :channel,
:total_bytes_to_download, :bytes_downloaded, :packages_to_download,
- :manifests
+ :manifests, :packages, :files, :wine_prefix
def initialize(app_id, release_channel)
@app_id = app_id
@@ -14,13 +14,17 @@ class W3DHub
@task_state = :not_started # :not_started, :running, :paused, :halted, :complete, :failed
@application = window.applications.games.find { |g| g.id == app_id }
- @channel = @application.channels.find { |c| c.name == release_channel }
+ @channel = @application.channels.find { |c| c.id == release_channel }
@packages_to_download = []
@total_bytes_to_download = -1
@bytes_downloaded = -1
@manifests = []
+ @files = {}
+ @packages = []
+
+ @wine_prefix = nil
setup
end
@@ -88,7 +92,19 @@ class W3DHub
def fail!(reason = "")
@task_state = :failed
- @task_failure_reason = "Failed: #{reason}"
+ @task_failure_reason = reason.to_s
+ end
+
+ # Quick checks before network and computational work starts
+ def fail_fast
+ # tar present?
+ tar_present = system("tar --help")
+ fail!("FAIL FAST: `tar --help` command failed, tar is not installed. Will be unable to unpack packages.") unless tar_present
+
+ if W3DHub.unix?
+ wine_present = system("which #{window.settings[:wine_command]}")
+ fail!("FAIL FAST: `which wine` command failed, wine is not installed. Will be unable to create prefixes or launch games.") unless wine_prefix
+ end
end
def run_on_main_thread(block)
@@ -154,6 +170,8 @@ class W3DHub
puts "#{manifest.game}-#{manifest.type}: #{manifest.version} (#{manifest.base_version})"
manifest.files.each do |file|
+ @files["#{file.name}:#{manifest.version}"] = file
+
next if file.removed? # No package data
if file.patch?
@@ -193,14 +211,13 @@ class W3DHub
package_details = Api.package_details(hashes)
if package_details
+ @packages = [package_details].flatten
@packages_to_download = []
update_application_taskbar("Downloading #{@application.name}...", "Verifying local packages...", 0.0)
package_details.each do |pkg|
- unless verify_package(pkg)
- @packages_to_download << pkg
- end
+ @packages_to_download << pkg unless verify_package(pkg)
end
@total_bytes_to_download = @packages_to_download.sum { |pkg| pkg.size - pkg.custom_partially_valid_at_bytes }
@@ -238,18 +255,62 @@ class W3DHub
end
def unpack_packages(packages)
+ path = Cache.install_path(@application, @channel)
+ puts "Unpacking packages in '#{path}'..."
+ Cache.create_directories(path)
+
+ packages.each do |package|
+ puts " #{package.name}:#{package.version}"
+ package_path = Cache.package_path(package.category, package.subcategory, "#{package.name}.zip", package.version)
+
+ puts " Running tar command: #{"tar -xf #{package_path} -C #{path}"}"
+ status = system("tar -xf #{package_path} -C #{path}")
+ if status
+ else
+ puts "COMMAND FAILED!"
+ end
+ end
end
def create_wine_prefix
+ if W3DHub.unix?
+ # TODO: create a wine prefix if configured
+ end
end
def install_dependencies(packages)
end
def mark_application_installed
+ window.application_manager.installed!(self)
+
puts "#{@app_id} has been installed."
end
+ def verify_files(_)
+ # TODO: Check extracted game files or just re-extract everything?
+
+ # Zip.on_exists_proc = true # Overwrite existing files
+ # zip_file = Zip::InputStream.new(File.open(package_path))
+ # while (entry = zip_file.get_next_entry)
+
+ # extract_path = "#{path}/#{entry.name}"
+ # manifest_file = @files["#{entry.name}:#{package.version}"]
+
+ # if manifest_file.removed?
+ # puts " !skipped: #{extract_path}"
+ # else
+ # target_file_exits = File.exist?(extract_path)
+ # next if target_file_exits # && Digest::SHA256.new.hexdigest(File.read(extract_path)) == manifest_file.checksum
+
+ # puts " #{path}/#{extract_path}"
+ # Cache.create_directories(extract_path) unless target_file_exits
+
+ # entry.extract(extract_path)
+ # end
+ # end
+ end
+
#############
# Functions #
#############
@@ -287,7 +348,7 @@ class W3DHub
digest = Digest::SHA256.new
path = Cache.package_path(package.category, package.subcategory, package.name, package.version)
- return false unless File.exists?(path)
+ return false unless File.exist?(path)
file_size = File.size(path)
puts " File size: #{file_size}"
@@ -300,7 +361,7 @@ class W3DHub
read_length = chunk_size
read_length = file_size - chunk_start if chunk_start + chunk_size > file_size
- break if file_size - chunk_start < 0
+ break if (file_size - chunk_start).negative?
f.seek(chunk_start)
@@ -327,4 +388,4 @@ class W3DHub
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/application_manager/tasks/installer.rb b/lib/application_manager/tasks/installer.rb
index 8af379e..ffbd05f 100644
--- a/lib/application_manager/tasks/installer.rb
+++ b/lib/application_manager/tasks/installer.rb
@@ -6,6 +6,9 @@ class W3DHub
end
def execute_task
+ fail_fast
+ return false if failed?
+
update_application_taskbar("Downloading #{@application.name}...", "Fetching manifests...", 0.0)
manifests = fetch_manifests
return false if failed?
@@ -33,7 +36,6 @@ class W3DHub
sleep 1
update_application_taskbar("Installing #{@application.name}...", "Installing dependencies...", 0.0)
-
install_dependencies(packages)
return false if failed?
sleep 1
diff --git a/lib/cache.rb b/lib/cache.rb
index 5877b23..7bb6966 100644
--- a/lib/cache.rb
+++ b/lib/cache.rb
@@ -27,8 +27,8 @@ class W3DHub
end
end
- def self.create_directories(path)
- target_directory = File.dirname(path)
+ def self.create_directories(path, is_directory = false)
+ target_directory = is_directory ? path : File.dirname(path)
FileUtils.mkdir_p(target_directory) unless Dir.exist?(target_directory)
end
@@ -39,6 +39,12 @@ class W3DHub
"#{package_cache_dir}/#{category}/#{subcategory}/#{version}/#{name}.package"
end
+ def self.install_path(application, channel)
+ app_install_dir = $window.settings[:app_install_dir]
+
+ "#{app_install_dir}/#{application.category}/#{application.id}/#{channel.id}"
+ end
+
# Download a W3D Hub package
def self.fetch_package(package, block)
path = package_path(package.category, package.subcategory, package.name, package.version)
diff --git a/lib/common.rb b/lib/common.rb
index 7e3bb93..d3d8ee8 100644
--- a/lib/common.rb
+++ b/lib/common.rb
@@ -15,4 +15,24 @@ class W3DHub
def self.format_size_number(i)
format("%0.2f", i)
end
+
+ def self.windows?
+ RbConfig::CONFIG["host_os"] =~ /(mingw|mswin|windows)/i
+ end
+
+ def self.mac?
+ RbConfig::CONFIG["host_os"] =~ /(darwin|mac os)/i
+ end
+
+ def self.linux?
+ RbConfig::CONFIG["host_os"] =~ /(linux|bsd|aix|solaris)/i
+ end
+
+ def self.unix?
+ linux? || mac?
+ end
+
+ def self.home_directory
+ File.expand_path("~")
+ end
end
diff --git a/lib/pages/games.rb b/lib/pages/games.rb
index a08f2b7..35c1442 100644
--- a/lib/pages/games.rb
+++ b/lib/pages/games.rb
@@ -43,7 +43,7 @@ class W3DHub
self if hit?(x, y)
end
- game_button.subscribe(:clicked_left_mouse_button) do |e|
+ game_button.subscribe(:clicked_left_mouse_button) do
populate_game_page(game, game.channels.first)
populate_games_list
end
@@ -85,16 +85,16 @@ class W3DHub
# end
# end
- if window.application_manager.installed?(game.id, channel.name)
+ if window.application_manager.installed?(game.id, channel.id)
Hash.new.tap { |hash|
- hash["Game Settings"] = { icon: "gear", block: proc { window.application_manager.settings(game.id, channel.name) } }
+ hash["Game Settings"] = { icon: "gear", block: proc { window.application_manager.settings(game.id, channel.id) } }
if game.id != "ren"
- hash["Repair Installation"] = { icon: "wrench", block: proc { window.application_manager.repair(game.id, channel.name) } }
- hash["Uninstall"] = { icon: "trashCan", block: proc { window.application_manager.uninstall(game.id, channel.name) } }
+ hash["Repair Installation"] = { icon: "wrench", block: proc { window.application_manager.repair(game.id, channel.id) } }
+ hash["Uninstall"] = { icon: "trashCan", block: proc { window.application_manager.uninstall(game.id, channel.id) } }
end
- hash["Install Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.name, :installation) } }
- hash["User Data Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.name, :user_data) } }
- hash["View Screenshots"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.name, :screenshots) } }
+ hash["Install Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :installation) } }
+ hash["User Data Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :user_data) } }
+ hash["View Screenshots"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :screenshots) } }
}.each do |key, hash|
flow(width: 1.0, height: 22, margin_bottom: 8) do
image "#{GAME_ROOT_PATH}/media/ui_icons/#{hash[:icon]}.png", width: 0.11 if hash[:icon]
@@ -126,25 +126,24 @@ class W3DHub
flow(width: 1.0, height: 0.08) do
# background 0xff_551100
- # TODO: Determine if game is installed or not and show apporpiante options ["Play Now" and "Single Player", "Install" and "Import"]
- # game.play_items.each do |item|
- # button "#{item.label}", margin_left: 24 do
- # item.block&.call(game)
- # end
- # end
if window.application_manager.installed?(game.id, channel.id)
- button "Play Now", margin_left: 24
- button "Single Player", margin_left: 24
+ button "Play Now", margin_left: 24 do
+ window.application_manager.run(game.id, channel.id)
+ end
+
+ button "Single Player", margin_left: 24 do
+ window.application_manager.run(game.id, channel.id)
+ end
else
unless game.id == "ren"
- button "Install", margin_left: 24, enabled: !window.application_manager.task?(:installer, game.id, channel.name) do |button|
+ button "Install", margin_left: 24, enabled: !window.application_manager.task?(:installer, game.id, channel.id) do |button|
button.enabled = false
- window.application_manager.install(game.id, channel.name)
+ window.application_manager.install(game.id, channel.id)
end
end
button "Import", margin_left: 24, enabled: false do
- window.application_manager.import(game.id, channel.name, "?")
+ window.application_manager.import(game.id, channel.id, "?")
end
end
end
diff --git a/lib/settings.rb b/lib/settings.rb
index f616111..783ce2a 100644
--- a/lib/settings.rb
+++ b/lib/settings.rb
@@ -5,6 +5,8 @@ class W3DHub
language: Gosu.user_languages.first.split("_").first,
app_install_dir: default_app_install_dir,
package_cache_dir: default_package_cache_dir,
+ wine_command: "wine",
+ create_wine_prefixes: true,
allow_diagnostic_reports: false,
server_list_username: nil,
account: {},
@@ -14,45 +16,29 @@ class W3DHub
end
def self.default_app_install_dir
- if windows?
- "#{home_directory}/#{W3DHub::DIR_NAME}"
- elsif linux?
- "#{home_directory}/.local/share/#{W3DHub::DIR_NAME}"
- elsif mac?
- "#{home_directory}/.local/share/#{W3DHub::DIR_NAME}"
+ if W3DHub.windows?
+ "#{W3DHub.home_directory}/#{W3DHub::DIR_NAME}"
+ elsif W3DHub.linux?
+ "#{W3DHub.home_directory}/.local/share/#{W3DHub::DIR_NAME}"
+ elsif W3DHub.mac?
+ "#{W3DHub.home_directory}/.local/share/#{W3DHub::DIR_NAME}"
else
raise "Unknown platform: #{RbConfig::CONFIG["host_os"]}"
end
end
def self.default_package_cache_dir
- if windows?
- "#{home_directory}/#{W3DHub::DIR_NAME}/Launcher/package-cache"
- elsif linux?
- "#{home_directory}/.local/share/#{W3DHub::DIR_NAME}/package-cache"
- elsif mac?
- "#{home_directory}/.local/share/#{W3DHub::DIR_NAME}/package-cache"
+ if W3DHub.windows?
+ "#{W3DHub.home_directory}/#{W3DHub::DIR_NAME}/Launcher/package-cache"
+ elsif W3DHub.linux?
+ "#{W3DHub.home_directory}/.local/share/#{W3DHub::DIR_NAME}/package-cache"
+ elsif W3DHub.mac?
+ "#{W3DHub.home_directory}/.local/share/#{W3DHub::DIR_NAME}/package-cache"
else
raise "Unknown platform: #{RbConfig::CONFIG["host_os"]}"
end
end
- def self.windows?
- RbConfig::CONFIG["host_os"] =~ /(mingw|mswin|windows)/i
- end
-
- def self.mac?
- RbConfig::CONFIG["host_os"] =~ /(darwin|mac os)/i
- end
-
- def self.linux?
- RbConfig::CONFIG["host_os"] =~ /(linux|bsd|aix|solaris)/i
- end
-
- def self.home_directory
- File.expand_path("~")
- end
-
def initialize
unless File.exist?(SETTINGS_FILE_PATH)
@settings = Settings.defaults
diff --git a/lib/states/message_dialog.rb b/lib/states/message_dialog.rb
index 67078e8..d4ae1df 100644
--- a/lib/states/message_dialog.rb
+++ b/lib/states/message_dialog.rb
@@ -8,17 +8,23 @@ class W3DHub
background 0xee_444444
- stack(width: 1.0, height: 1.0, margin: 128, padding: 8, background: 0xee_222222) do
- flow(width: 1.0, height: 0.06) do
+ stack(width: 1.0, height: 1.0, margin: 128, background: 0xee_222222) do
+ flow(width: 1.0, height: 0.1, padding: 8) do
+ background 0x88_000000
+
image "#{GAME_ROOT_PATH}/media/ui_icons/warning.png", width: 0.04, align: :center, color: 0xff_ff8800
tagline "#{@options[:title]}", width: 0.9, text_align: :center
end
- para @options[:message], width: 1.0, height: 0.7, padding: 8
+ stack(width: 1.0, height: 0.78, padding: 16) do
+ para @options[:message], width: 1.0
+ end
- button "Okay", width: 1.0, margin_top: 64 do
- pop_state
+ stack(width: 1.0, height: 0.1, padding: 8) do
+ button "Okay", width: 1.0 do
+ pop_state
+ end
end
end
end
diff --git a/w3dhub.rb b/w3dhub.rb
index 5b9ed79..3b95630 100644
--- a/w3dhub.rb
+++ b/w3dhub.rb
@@ -9,9 +9,9 @@ end
require "fileutils"
require "digest"
require "rexml"
-require "zlib"
require "launchy"
+require "zip"
class W3DHub
GAME_ROOT_PATH = File.expand_path(".", __dir__)