From 7257030e7429d2536cb96a92cd604edb6d05ff8e Mon Sep 17 00:00:00 2001 From: cyberarm Date: Fri, 26 Nov 2021 22:36:29 -0600 Subject: [PATCH] Added support for applying patches! added DemoInputDelay state to make recording Boot state easier, misc fixes for tweaks. --- lib/application_manager/task.rb | 107 ++++++++++++++++++--- lib/application_manager/tasks/installer.rb | 8 -- lib/mixer.rb | 12 ++- lib/states/demo_input_delay.rb | 12 +++ lib/window.rb | 1 + w3dhub.rb | 1 + 6 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 lib/states/demo_input_delay.rb diff --git a/lib/application_manager/task.rb b/lib/application_manager/task.rb index 33caa25..f3fc2cf 100644 --- a/lib/application_manager/task.rb +++ b/lib/application_manager/task.rb @@ -51,6 +51,10 @@ class W3DHub Thread.new do status = execute_task + # Force free some bytes + GC.compact if GC.respond_to?(:compact) + GC.start + @task_state = :failed unless status @task_state = :complete unless @task_state == :failed @@ -221,6 +225,21 @@ class W3DHub if package_details @packages = [package_details].flatten + @packages.each do |rich| + package = packages.find do |pkg| + pkg.category == rich.category && + pkg.subcategory == rich.subcategory && + "#{pkg.name}.zip" == rich.name && + pkg.version == rich.version + end + + package.instance_variable_set(:"@name", rich.name) + package.instance_variable_set(:"@size", rich.size) + package.instance_variable_set(:"@checksum", rich.checksum) + package.instance_variable_set(:"@checksum_chunk_size", rich.checksum_chunk_size) + package.instance_variable_set(:"@checksum_chunks", rich.checksum_chunks) + end + @packages_to_download = [] @status.label = "Downloading #{@application.name}..." @@ -230,7 +249,7 @@ class W3DHub package_details.each do |pkg| @status.operations[:"#{pkg.checksum}"] = Status::Operation.new( label: pkg.name, - value: "Verifying...", + value: "Pending...", progress: 0.0 ) end @@ -283,7 +302,6 @@ class W3DHub puts "FAILED!" pp package_details end - end def verify_packages(packages) @@ -309,19 +327,26 @@ class W3DHub @status.step = :unpacking + i = -1 packages.each do |package| + i += 1 + status = if package.custom_is_patch @status.operations[:"#{package.checksum}"].value = "Patching..." + @status.progress = i.to_f / packages.count update_interface_task_status - apply_patch(package) + apply_patch(package, path) else @status.operations[:"#{package.checksum}"].value = "Unpacking..." + @status.progress = i.to_f / packages.count update_interface_task_status - unpack_package(package) + unpack_package(package, path) end + repair_windows_case_insensitive(package, path) + if status @status.operations[:"#{package.checksum}"].value = "Unpacked" @status.operations[:"#{package.checksum}"].progress = 1.0 @@ -435,7 +460,7 @@ class W3DHub return false unless File.exist?(path) operation = @status.operations[:"#{package.checksum}"] - operation&.value = "Verifying..." + operation&.value = "Verifying..." file_size = File.size(path) puts " File size: #{file_size}" @@ -480,22 +505,74 @@ class W3DHub Manifest.new(category, subcategory, name, version) end - def unpack_package(package) + def unpack_package(package, path) puts " #{package.name}:#{package.version}" - package_path = Cache.package_path(package.category, package.subcategory, "#{package.name}.zip", package.version) + package_path = Cache.package_path(package.category, package.subcategory, package.name, package.version) puts " Running #{W3DHub.tar_command} command: #{"#{W3DHub.tar_command} -xf #{package_path} -C #{path}"}" return system("#{W3DHub.tar_command} -xf #{package_path} -C #{path}") end - def apply_patch(package) - # Unpack patch to a known directory - # Load MIX1 file - # Read and Parse .w3dhub.patch json file - # Load target MIX1 (.mix and .dat) - # Update and remove files - # Overwrite updated file - false + def apply_patch(package, path) + puts " #{package.name}:#{package.version}" + package_path = Cache.package_path(package.category, package.subcategory, package.name, package.version) + temp_path = "#{Store.settings[:package_cache_dir]}/temp" + manifest_file = package.custom_is_patch + + Cache.create_directories(temp_path, true) + + puts " Running #{W3DHub.tar_command} command: #{"#{W3DHub.tar_command} -xf #{package_path} -C #{temp_path}"}" + system("#{W3DHub.tar_command} -xf #{package_path} -C #{temp_path}") + + puts " Loading #{temp_path}/#{manifest_file.name}.patch..." + patch_mix = W3DHub::Mixer::Reader.new(file_path: "#{temp_path}/#{manifest_file.name}.patch", ignore_crc_mismatches: true, eager_load: true) + patch_info = JSON.parse(patch_mix.package.files.find { |f| f.name == ".w3dhub.patch" || f.name == ".bhppatch" }.data, symbolize_names: true) + + repaired_path = "#{path}/#{manifest_file.name}" + # Fix borked data -> Data 'cause Windows don't care about capitalization + repaired_path = "#{path}/#{manifest_file.name.sub('data', 'Data')}" unless File.exist?(repaired_path) && path + + puts " Loading #{repaired_path}..." + target_mix = W3DHub::Mixer::Reader.new(file_path: repaired_path, ignore_crc_mismatches: true, eager_load: true) + + puts " Removing files..." if patch_info[:removedFiles].size.positive? + patch_info[:removedFiles].each do |file| + target_mix.package.files.delete_if { |f| f.name == file } + end + + puts " Adding/Updating files..." if patch_info[:updatedFiles].size.positive? + patch_info[:updatedFiles].each do |file| + patch = patch_mix.package.files.find { |f| f.name == file } + target = target_mix.package.files.find { |f| f.name == file } + + if target + target_mix.package.files[target_mix.package.files.index(target)] = patch + else + target_mix.package.files << patch + end + end + + + puts " Writing updated #{repaired_path}..." if patch_info[:updatedFiles].size.positive? + W3DHub::Mixer::Writer.new(file_path: repaired_path, package: target_mix.package, memory_buffer: true) + + FileUtils.remove_dir(temp_path) + + true + end + + def repair_windows_case_insensitive(package, path) + return true if @app_id == "apb" + + # Force data/ to Data/ + return true unless File.exist?("#{path}/data") && File.directory?("#{path}/data") + + puts " Moving #{path}/data/ to #{path}/Data/" + + FileUtils.mv(Dir.glob("#{path}/data/**"), "#{path}/Data", force: true) + FileUtils.remove_dir("#{path}/data", force: true) + + true end end end diff --git a/lib/application_manager/tasks/installer.rb b/lib/application_manager/tasks/installer.rb index 7a8a2f8..35a9543 100644 --- a/lib/application_manager/tasks/installer.rb +++ b/lib/application_manager/tasks/installer.rb @@ -9,33 +9,26 @@ class W3DHub fail_fast return false if failed? - # update_application_taskbar("Downloading #{@application.name}...", "Fetching manifests...", 0.0) manifests = fetch_manifests return false if failed? - # update_application_taskbar("Downloading #{@application.name}...", "Building package list...", 0.0) packages = build_package_list(manifests) return false if failed? - # update_application_taskbar("Downloading #{@application.name}...", "Downloading packages...", 0.0) fetch_packages(packages) return false if failed? - # update_application_taskbar("Downloading #{@application.name}...", "Verifying packages...", 0.0) verify_packages(packages) return false if failed? - # update_application_taskbar("Installing #{@application.name}...", "Unpacking...", 0.0) unpack_packages(packages) return false if failed? sleep 1 - # update_application_taskbar("Installing #{@application.name}...", "Creating wine prefix...", 0.0) create_wine_prefix return false if failed? sleep 1 - # update_application_taskbar("Installing #{@application.name}...", "Installing dependencies...", 0.0) install_dependencies(packages) return false if failed? sleep 1 @@ -43,7 +36,6 @@ class W3DHub mark_application_installed return false if failed? - # update_application_taskbar("Installed #{@application.name}", "", 1.0) sleep 5 hide_application_taskbar diff --git a/lib/mixer.rb b/lib/mixer.rb index e8c14ef..e14de3f 100644 --- a/lib/mixer.rb +++ b/lib/mixer.rb @@ -1,4 +1,5 @@ require "digest" +require "stringio" class W3DHub @@ -10,9 +11,9 @@ class W3DHub class Reader attr_reader :package - def initialize(file_path:, ignore_crc_mismatches: false) + def initialize(file_path:, ignore_crc_mismatches: false, eager_load: false) @package = Package.new - @file = File.open(file_path) + @file = eager_load ? StringIO.new(File.read(file_path)) : File.open(file_path) @file.pos = 0 @@ -79,10 +80,10 @@ class W3DHub class Writer attr_reader :package - def initialize(file_path:, package:) + def initialize(file_path:, package:, memory_buffer: false) @package = package - @file = File.open(file_path, "wb") + @file = memory_buffer ? StringIO.new : File.open(file_path, "wb") @file.pos = 0 @file.write("MIX1") @@ -121,7 +122,8 @@ class W3DHub write_i32(file_name_offset) @file.pos = 0 - @file.flush + + File.write(file_path, @file.string) if memory_buffer ensure @file&.close end diff --git a/lib/states/demo_input_delay.rb b/lib/states/demo_input_delay.rb new file mode 100644 index 0000000..3596fd2 --- /dev/null +++ b/lib/states/demo_input_delay.rb @@ -0,0 +1,12 @@ +class W3DHub + class States + class DemoInputDelay < CyberarmEngine::GameState + def button_down(id) + return unless id == Gosu::KB_SPACE + + pop_state # Erase self + push_state(States::Boot) + end + end + end +end diff --git a/lib/window.rb b/lib/window.rb index 3f929b8..9e41d20 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -15,6 +15,7 @@ class W3DHub I18n.locale = :en end + # push_state(W3DHub::States::DemoInputDelay) push_state(W3DHub::States::Boot) end diff --git a/w3dhub.rb b/w3dhub.rb index 9e9310b..6af286a 100644 --- a/w3dhub.rb +++ b/w3dhub.rb @@ -41,6 +41,7 @@ require_relative "lib/application_manager/tasks/installer" require_relative "lib/application_manager/tasks/uninstaller" require_relative "lib/application_manager/tasks/repairer" require_relative "lib/application_manager/tasks/importer" +require_relative "lib/states/demo_input_delay" require_relative "lib/states/boot" require_relative "lib/states/interface" require_relative "lib/states/message_dialog"