mirror of
https://gitlab.com/cleanflash/installer.git
synced 2026-08-12 18:31:37 +08:00
Add Rust port
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "clean_flash_installer"
|
||||
version = "34.0.0"
|
||||
edition = "2021"
|
||||
authors = ["FlashPatch Team"]
|
||||
|
||||
[dependencies]
|
||||
clean_flash_common = { path = "../clean_flash_common" }
|
||||
clean_flash_ui = { path = "../clean_flash_ui" }
|
||||
minifb = { workspace = true }
|
||||
windows-sys = { workspace = true }
|
||||
sevenz-rust2 = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
winresource = "0.1"
|
||||
@@ -0,0 +1,37 @@
|
||||
fn main() {
|
||||
// Set application icon from .ico resource (if present).
|
||||
if cfg!(target_os = "windows") {
|
||||
let mut res = winresource::WindowsResource::new();
|
||||
// Attempt to set icon; ignore if file doesn't exist.
|
||||
if std::path::Path::new("../../resources/icon.ico").exists() {
|
||||
res.set_icon("../../resources/icon.ico");
|
||||
}
|
||||
res.set("ProductName", "Clean Flash Player Installer");
|
||||
res.set("FileDescription", "Clean Flash Player Installer");
|
||||
res.set("ProductVersion", "34.0.0.330");
|
||||
res.set("CompanyName", "FlashPatch Team");
|
||||
res.set("LegalCopyright", "FlashPatch Team");
|
||||
res.set_manifest(r#"<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="CleanFlashInstaller.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
"#);
|
||||
let _ = res.compile();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/// Bitflag-based install options, mirroring the C# InstallFlags class.
|
||||
|
||||
pub const NONE: u32 = 0;
|
||||
pub const PEPPER: u32 = 1 << 0;
|
||||
pub const NETSCAPE: u32 = 1 << 1;
|
||||
pub const ACTIVEX: u32 = 1 << 2;
|
||||
pub const PLAYER: u32 = 1 << 3;
|
||||
pub const PLAYER_START_MENU: u32 = 1 << 4;
|
||||
pub const PLAYER_DESKTOP: u32 = 1 << 5;
|
||||
pub const X64: u32 = 1 << 6;
|
||||
pub const DEBUG: u32 = 1 << 7;
|
||||
|
||||
const UNINSTALL_TICKS: u32 = 9;
|
||||
const INSTALL_GENERAL_TICKS: u32 = 5;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct InstallFlags {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl InstallFlags {
|
||||
pub fn new() -> Self {
|
||||
Self { value: 0 }
|
||||
}
|
||||
|
||||
pub fn from(value: u32) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
pub fn get_value(self) -> u32 {
|
||||
self.value
|
||||
}
|
||||
|
||||
pub fn is_set(self, flag: u32) -> bool {
|
||||
(self.value & flag) == flag
|
||||
}
|
||||
|
||||
pub fn is_none_set(self) -> bool {
|
||||
self.value == 0
|
||||
}
|
||||
|
||||
pub fn set_flag(&mut self, flag: u32) {
|
||||
self.value |= flag;
|
||||
}
|
||||
|
||||
pub fn set_conditionally(&mut self, condition: bool, flag: u32) {
|
||||
if condition {
|
||||
self.set_flag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ticks(self) -> u32 {
|
||||
let is_64bit = cfg!(target_pointer_width = "64")
|
||||
|| std::env::var("PROCESSOR_ARCHITEW6432").is_ok();
|
||||
|
||||
let mut ticks = (if self.is_set(PEPPER) { 1 } else { 0 })
|
||||
+ (if self.is_set(NETSCAPE) { 1 } else { 0 })
|
||||
+ (if self.is_set(ACTIVEX) { 2 } else { 0 });
|
||||
|
||||
if is_64bit {
|
||||
ticks *= 2;
|
||||
}
|
||||
|
||||
if self.is_set(PLAYER) {
|
||||
ticks += 1;
|
||||
}
|
||||
|
||||
ticks += UNINSTALL_TICKS;
|
||||
ticks += INSTALL_GENERAL_TICKS;
|
||||
ticks
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,691 @@
|
||||
use crate::install_flags::{self, InstallFlags};
|
||||
use crate::installer;
|
||||
use clean_flash_common::{uninstaller, redirection, update_checker, ProgressCallback};
|
||||
use clean_flash_ui::font::FontManager;
|
||||
use clean_flash_ui::renderer::{Renderer, RgbaImage};
|
||||
use clean_flash_ui::widgets::button::GradientButton;
|
||||
use clean_flash_ui::widgets::checkbox::ImageCheckBox;
|
||||
use clean_flash_ui::widgets::label::Label;
|
||||
use clean_flash_ui::widgets::progress_bar::ProgressBar;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// Window dimensions matching the C# form.
|
||||
pub const WIDTH: usize = 712;
|
||||
pub const HEIGHT: usize = 329;
|
||||
const BG_COLOR: u32 = Renderer::rgb(50, 51, 51);
|
||||
const FG_COLOR: u32 = Renderer::rgb(245, 245, 245);
|
||||
|
||||
const PANEL_X: i32 = 90;
|
||||
const PANEL_Y: i32 = 162;
|
||||
|
||||
const DISCLAIMER_TEXT: &str = "I am aware that Adobe Flash Player is no longer supported, nor provided by Adobe Inc.\n\
|
||||
Clean Flash Player is a third-party version of Flash Player built from the latest Flash Player\n\
|
||||
version with adware removed.\n\n\
|
||||
Adobe is not required by any means to provide support for this version of Flash Player.";
|
||||
|
||||
const COMPLETE_INSTALL_TEXT: &str = "Clean Flash Player has been successfully installed!\n\
|
||||
Don't forget, Flash Player is no longer compatible with new browsers.\n\n\
|
||||
For browser recommendations and Flash Player updates,\n\
|
||||
check out Clean Flash Player's website!";
|
||||
|
||||
const COMPLETE_UNINSTALL_TEXT: &str = "\nAll versions of Flash Player have been successfully uninstalled.\n\n\
|
||||
If you ever change your mind, check out Clean Flash Player's website!";
|
||||
|
||||
/// Which panel is currently shown in the wizard.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Panel {
|
||||
Disclaimer,
|
||||
Choice,
|
||||
PlayerChoice,
|
||||
DebugChoice,
|
||||
BeforeInstall,
|
||||
Install,
|
||||
Complete,
|
||||
Failure,
|
||||
}
|
||||
|
||||
/// Shared progress state set by the background install thread.
|
||||
pub struct ProgressState {
|
||||
pub label: String,
|
||||
pub value: i32,
|
||||
pub done: bool,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Full application state for the installer form.
|
||||
pub struct InstallForm {
|
||||
pub panel: Panel,
|
||||
// Header
|
||||
pub title_text: String,
|
||||
pub subtitle_text: String,
|
||||
pub flash_logo: RgbaImage,
|
||||
// Checkbox images
|
||||
pub checkbox_on: RgbaImage,
|
||||
pub checkbox_off: RgbaImage,
|
||||
// Navigation buttons
|
||||
pub prev_button: GradientButton,
|
||||
pub next_button: GradientButton,
|
||||
// Disclaimer panel
|
||||
pub disclaimer_label: Label,
|
||||
pub disclaimer_box: ImageCheckBox,
|
||||
// Choice panel (browser plugins)
|
||||
pub browser_ask_label: Label,
|
||||
pub pepper_box: ImageCheckBox,
|
||||
pub pepper_label: Label,
|
||||
pub netscape_box: ImageCheckBox,
|
||||
pub netscape_label: Label,
|
||||
pub activex_box: ImageCheckBox,
|
||||
pub activex_label: Label,
|
||||
// Player choice panel
|
||||
pub player_ask_label: Label,
|
||||
pub player_box: ImageCheckBox,
|
||||
pub player_label: Label,
|
||||
pub player_desktop_box: ImageCheckBox,
|
||||
pub player_desktop_label: Label,
|
||||
pub player_start_menu_box: ImageCheckBox,
|
||||
pub player_start_menu_label: Label,
|
||||
// Debug choice panel
|
||||
pub debug_ask_label: Label,
|
||||
pub debug_button: GradientButton,
|
||||
pub debug_chosen: bool,
|
||||
// Before install panel
|
||||
pub before_install_label: Label,
|
||||
// Install panel
|
||||
pub install_header_label: Label,
|
||||
pub progress_label: Label,
|
||||
pub progress_bar: ProgressBar,
|
||||
// Complete panel
|
||||
pub complete_label: Label,
|
||||
// Failure panel
|
||||
pub failure_text_label: Label,
|
||||
pub failure_detail: String,
|
||||
pub copy_error_button: GradientButton,
|
||||
// Shared progress state (for background thread communication).
|
||||
pub progress_state: Arc<Mutex<ProgressState>>,
|
||||
// Fonts loaded once.
|
||||
pub fonts: FontManager,
|
||||
// Mouse tracking
|
||||
prev_mouse_down: bool,
|
||||
}
|
||||
|
||||
impl InstallForm {
|
||||
pub fn new() -> Self {
|
||||
let version = update_checker::FLASH_VERSION;
|
||||
let title_text = "Clean Flash Player".to_string();
|
||||
let subtitle_text = format!("built from version {} (China)", version);
|
||||
|
||||
// Load images from the common resources folder.
|
||||
// These are loaded from the C# project's assets alongside the binary.
|
||||
let flash_logo = load_resource_image("flashLogo.png");
|
||||
let checkbox_on = load_resource_image("checkboxOn.png");
|
||||
let checkbox_off = load_resource_image("checkboxOff.png");
|
||||
|
||||
let fonts = FontManager::new();
|
||||
|
||||
Self {
|
||||
panel: Panel::Disclaimer,
|
||||
title_text,
|
||||
subtitle_text,
|
||||
flash_logo,
|
||||
checkbox_on,
|
||||
checkbox_off,
|
||||
prev_button: GradientButton::new(90, 286, 138, 31, "QUIT"),
|
||||
next_button: GradientButton::new(497, 286, 138, 31, "AGREE"),
|
||||
// Disclaimer panel
|
||||
disclaimer_label: Label::new(PANEL_X + 25, PANEL_Y, DISCLAIMER_TEXT, 13.0),
|
||||
disclaimer_box: ImageCheckBox::new(PANEL_X, PANEL_Y),
|
||||
// Choice panel
|
||||
browser_ask_label: Label::new(
|
||||
PANEL_X - 2,
|
||||
PANEL_Y + 2,
|
||||
"Which browser plugins would you like to install?",
|
||||
13.0,
|
||||
),
|
||||
pepper_box: ImageCheckBox::new(PANEL_X, PANEL_Y + 47),
|
||||
pepper_label: Label::new(
|
||||
PANEL_X + 24,
|
||||
PANEL_Y + 47,
|
||||
"Pepper API (PPAPI)\n(Chrome/Opera/Brave)",
|
||||
13.0,
|
||||
),
|
||||
netscape_box: ImageCheckBox::new(PANEL_X + 186, PANEL_Y + 47),
|
||||
netscape_label: Label::new(
|
||||
PANEL_X + 210,
|
||||
PANEL_Y + 47,
|
||||
"Netscape API (NPAPI)\n(Firefox/ESR/Waterfox)",
|
||||
13.0,
|
||||
),
|
||||
activex_box: ImageCheckBox::new(PANEL_X + 365, PANEL_Y + 47),
|
||||
activex_label: Label::new(
|
||||
PANEL_X + 389,
|
||||
PANEL_Y + 47,
|
||||
"ActiveX (OCX)\n(IE/Embedded/Desktop)",
|
||||
13.0,
|
||||
),
|
||||
// Player choice panel
|
||||
player_ask_label: Label::new(
|
||||
PANEL_X - 2,
|
||||
PANEL_Y + 2,
|
||||
"Would you like to install the standalone Flash Player?",
|
||||
13.0,
|
||||
),
|
||||
player_box: ImageCheckBox::new(PANEL_X, PANEL_Y + 47),
|
||||
player_label: Label::new(
|
||||
PANEL_X + 24,
|
||||
PANEL_Y + 47,
|
||||
"Install Standalone\nFlash Player",
|
||||
13.0,
|
||||
),
|
||||
player_desktop_box: ImageCheckBox::new(PANEL_X + 186, PANEL_Y + 47),
|
||||
player_desktop_label: Label::new(
|
||||
PANEL_X + 210,
|
||||
PANEL_Y + 47,
|
||||
"Create Shortcuts\non Desktop",
|
||||
13.0,
|
||||
),
|
||||
player_start_menu_box: ImageCheckBox::new(PANEL_X + 365, PANEL_Y + 47),
|
||||
player_start_menu_label: Label::new(
|
||||
PANEL_X + 389,
|
||||
PANEL_Y + 47,
|
||||
"Create Shortcuts\nin Start Menu",
|
||||
13.0,
|
||||
),
|
||||
// Debug choice panel
|
||||
debug_ask_label: Label::new(
|
||||
PANEL_X - 2,
|
||||
PANEL_Y + 2,
|
||||
"Would you like to install the debug version of Clean Flash Player?\n\
|
||||
You should only choose the debug version if you are planning to create Flash applications.\n\
|
||||
If you are not sure, simply press NEXT.",
|
||||
13.0,
|
||||
),
|
||||
debug_button: GradientButton::new(PANEL_X + 186, PANEL_Y + 65, 176, 31, "INSTALL DEBUG VERSION"),
|
||||
debug_chosen: false,
|
||||
// Before install panel
|
||||
before_install_label: Label::new(PANEL_X + 3, PANEL_Y + 2, "", 13.0),
|
||||
// Install panel
|
||||
install_header_label: Label::new(PANEL_X + 3, PANEL_Y, "Installation in progress...", 13.0),
|
||||
progress_label: Label::new(PANEL_X + 46, PANEL_Y + 30, "Preparing...", 13.0),
|
||||
progress_bar: ProgressBar::new(PANEL_X + 49, PANEL_Y + 58, 451, 23),
|
||||
// Complete panel
|
||||
complete_label: Label::new(PANEL_X, PANEL_Y, "", 13.0),
|
||||
// Failure panel
|
||||
failure_text_label: Label::new(
|
||||
PANEL_X + 3,
|
||||
PANEL_Y + 2,
|
||||
"Oops! The installation process has encountered an unexpected problem.\n\
|
||||
The following details could be useful. Press the Retry button to try again.",
|
||||
13.0,
|
||||
),
|
||||
failure_detail: String::new(),
|
||||
copy_error_button: GradientButton::new(PANEL_X + 441, PANEL_Y + 58, 104, 31, "COPY"),
|
||||
progress_state: Arc::new(Mutex::new(ProgressState {
|
||||
label: "Preparing...".into(),
|
||||
value: 0,
|
||||
done: false,
|
||||
error: None,
|
||||
})),
|
||||
fonts,
|
||||
prev_mouse_down: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Called each frame: handle input, update state, draw.
|
||||
pub fn update_and_draw(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
mx: i32,
|
||||
my: i32,
|
||||
mouse_down: bool,
|
||||
) {
|
||||
let mouse_released = self.prev_mouse_down && !mouse_down;
|
||||
self.prev_mouse_down = mouse_down;
|
||||
|
||||
// Update navigation button hover states.
|
||||
self.prev_button.update(mx, my, mouse_down);
|
||||
self.next_button.update(mx, my, mouse_down);
|
||||
|
||||
// Handle click events.
|
||||
self.handle_input(mx, my, mouse_released);
|
||||
|
||||
// Poll progress state from background thread if installing.
|
||||
if self.panel == Panel::Install {
|
||||
self.poll_progress();
|
||||
}
|
||||
|
||||
// ----- Draw -----
|
||||
renderer.clear(BG_COLOR);
|
||||
|
||||
// Header: flash logo.
|
||||
renderer.draw_image(90, 36, &self.flash_logo);
|
||||
|
||||
// Title.
|
||||
self.fonts.draw_text(
|
||||
renderer,
|
||||
233,
|
||||
54,
|
||||
&self.title_text,
|
||||
32.0, // ~24pt Segoe UI
|
||||
FG_COLOR,
|
||||
);
|
||||
|
||||
// Subtitle.
|
||||
self.fonts.draw_text(
|
||||
renderer,
|
||||
280,
|
||||
99,
|
||||
&self.subtitle_text,
|
||||
17.0, // ~13pt Segoe UI
|
||||
FG_COLOR,
|
||||
);
|
||||
|
||||
// Separator line at y=270.
|
||||
renderer.fill_rect(0, 270, WIDTH as i32, 1, Renderer::rgb(105, 105, 105));
|
||||
|
||||
// Draw current panel.
|
||||
match self.panel {
|
||||
Panel::Disclaimer => self.draw_disclaimer(renderer),
|
||||
Panel::Choice => self.draw_choice(renderer),
|
||||
Panel::PlayerChoice => self.draw_player_choice(renderer),
|
||||
Panel::DebugChoice => self.draw_debug_choice(renderer),
|
||||
Panel::BeforeInstall => self.draw_before_install(renderer),
|
||||
Panel::Install => self.draw_install(renderer),
|
||||
Panel::Complete => self.draw_complete(renderer),
|
||||
Panel::Failure => self.draw_failure(renderer),
|
||||
}
|
||||
|
||||
// Navigation buttons.
|
||||
self.prev_button.draw(renderer, &self.fonts);
|
||||
self.next_button.draw(renderer, &self.fonts);
|
||||
}
|
||||
|
||||
fn handle_input(&mut self, mx: i32, my: i32, mouse_released: bool) {
|
||||
// Navigation button clicks.
|
||||
if self.prev_button.clicked(mx, my, mouse_released) {
|
||||
self.on_prev_clicked();
|
||||
return;
|
||||
}
|
||||
if self.next_button.clicked(mx, my, mouse_released) {
|
||||
self.on_next_clicked();
|
||||
return;
|
||||
}
|
||||
|
||||
// Panel-specific input.
|
||||
match self.panel {
|
||||
Panel::Disclaimer => {
|
||||
let toggled = self.disclaimer_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
if toggled || self.disclaimer_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
if !toggled {
|
||||
self.disclaimer_box.checked = !self.disclaimer_box.checked;
|
||||
}
|
||||
self.next_button.enabled = self.disclaimer_box.checked;
|
||||
}
|
||||
}
|
||||
Panel::Choice => {
|
||||
self.pepper_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.netscape_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.activex_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
if self.pepper_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
self.pepper_box.checked = !self.pepper_box.checked;
|
||||
}
|
||||
if self.netscape_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
self.netscape_box.checked = !self.netscape_box.checked;
|
||||
}
|
||||
if self.activex_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
self.activex_box.checked = !self.activex_box.checked;
|
||||
}
|
||||
}
|
||||
Panel::PlayerChoice => {
|
||||
self.player_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.player_desktop_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.player_start_menu_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
if self.player_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
self.player_box.checked = !self.player_box.checked;
|
||||
}
|
||||
if self.player_desktop_label.clicked(mx, my, mouse_released, &self.fonts) && self.player_box.checked {
|
||||
self.player_desktop_box.checked = !self.player_desktop_box.checked;
|
||||
}
|
||||
if self.player_start_menu_label.clicked(mx, my, mouse_released, &self.fonts) && self.player_box.checked {
|
||||
self.player_start_menu_box.checked = !self.player_start_menu_box.checked;
|
||||
}
|
||||
// Disable sub-options when player unchecked.
|
||||
self.player_desktop_box.enabled = self.player_box.checked;
|
||||
self.player_start_menu_box.enabled = self.player_box.checked;
|
||||
if !self.player_box.checked {
|
||||
self.player_desktop_box.checked = false;
|
||||
self.player_start_menu_box.checked = false;
|
||||
}
|
||||
}
|
||||
Panel::DebugChoice => {
|
||||
if self.debug_button.clicked(mx, my, mouse_released) {
|
||||
// In the C# app this shows a MessageBox. For the Rust port we toggle directly.
|
||||
self.debug_chosen = true;
|
||||
self.open_before_install();
|
||||
}
|
||||
self.debug_button.update(mx, my, self.prev_mouse_down);
|
||||
}
|
||||
Panel::Failure => {
|
||||
self.copy_error_button.update(mx, my, self.prev_mouse_down);
|
||||
if self.copy_error_button.clicked(mx, my, mouse_released) {
|
||||
// Copy error to clipboard via clip.exe.
|
||||
let _ = std::process::Command::new("cmd")
|
||||
.args(["/C", &format!("echo {} | clip", self.failure_detail)])
|
||||
.output();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_prev_clicked(&mut self) {
|
||||
match self.panel {
|
||||
Panel::Disclaimer | Panel::Complete | Panel::Failure => {
|
||||
std::process::exit(0);
|
||||
}
|
||||
Panel::Choice => self.open_disclaimer(),
|
||||
Panel::PlayerChoice => self.open_choice(),
|
||||
Panel::DebugChoice => self.open_player_choice(),
|
||||
Panel::BeforeInstall => self.open_debug_choice(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_next_clicked(&mut self) {
|
||||
match self.panel {
|
||||
Panel::Disclaimer => self.open_choice(),
|
||||
Panel::Choice => self.open_player_choice(),
|
||||
Panel::PlayerChoice => self.open_debug_choice(),
|
||||
Panel::DebugChoice => self.open_before_install(),
|
||||
Panel::BeforeInstall | Panel::Failure => self.open_install(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_disclaimer(&mut self) {
|
||||
self.panel = Panel::Disclaimer;
|
||||
self.prev_button.text = "QUIT".into();
|
||||
self.next_button.text = "AGREE".into();
|
||||
self.next_button.visible = true;
|
||||
self.next_button.enabled = self.disclaimer_box.checked;
|
||||
self.prev_button.enabled = true;
|
||||
}
|
||||
|
||||
fn open_choice(&mut self) {
|
||||
self.panel = Panel::Choice;
|
||||
self.prev_button.text = "BACK".into();
|
||||
self.next_button.text = "NEXT".into();
|
||||
self.next_button.visible = true;
|
||||
self.next_button.enabled = true;
|
||||
self.prev_button.enabled = true;
|
||||
}
|
||||
|
||||
fn open_player_choice(&mut self) {
|
||||
self.panel = Panel::PlayerChoice;
|
||||
self.prev_button.text = "BACK".into();
|
||||
self.next_button.text = "NEXT".into();
|
||||
self.next_button.visible = true;
|
||||
self.next_button.enabled = true;
|
||||
self.prev_button.enabled = true;
|
||||
}
|
||||
|
||||
fn open_debug_choice(&mut self) {
|
||||
self.panel = Panel::DebugChoice;
|
||||
self.debug_chosen = false;
|
||||
self.prev_button.text = "BACK".into();
|
||||
self.next_button.text = "NEXT".into();
|
||||
self.next_button.visible = true;
|
||||
self.next_button.enabled = true;
|
||||
self.prev_button.enabled = true;
|
||||
}
|
||||
|
||||
fn open_before_install(&mut self) {
|
||||
self.panel = Panel::BeforeInstall;
|
||||
self.prev_button.text = "BACK".into();
|
||||
self.prev_button.enabled = true;
|
||||
|
||||
let has_plugins =
|
||||
self.pepper_box.checked || self.netscape_box.checked || self.activex_box.checked || self.player_box.checked;
|
||||
|
||||
if has_plugins {
|
||||
let mut browsers = Vec::new();
|
||||
if self.pepper_box.checked {
|
||||
browsers.push("Google Chrome");
|
||||
}
|
||||
if self.netscape_box.checked {
|
||||
browsers.push("Mozilla Firefox");
|
||||
}
|
||||
if self.activex_box.checked {
|
||||
browsers.push("Internet Explorer");
|
||||
}
|
||||
|
||||
let browser_str = join_with_and(&browsers);
|
||||
self.before_install_label.text = format!(
|
||||
"You are about to install Clean Flash Player.\n\
|
||||
Please close any browser windows running Flash content before you continue.\n\n\
|
||||
The installer will close all browser windows running Flash, uninstall previous versions of Flash Player and\n\
|
||||
Flash Center, and install Flash for {}.",
|
||||
browser_str
|
||||
);
|
||||
self.next_button.text = "INSTALL".into();
|
||||
} else {
|
||||
self.before_install_label.text =
|
||||
"You are about to uninstall Clean Flash Player.\n\
|
||||
Please close any browser windows running Flash content before you continue.\n\n\
|
||||
The installer will completely remove all versions of Flash Player from this computer,\n\
|
||||
including Clean Flash Player and older versions of Adobe Flash Player."
|
||||
.to_string();
|
||||
self.next_button.text = "UNINSTALL".into();
|
||||
}
|
||||
self.next_button.visible = true;
|
||||
self.next_button.enabled = true;
|
||||
}
|
||||
|
||||
fn open_install(&mut self) {
|
||||
self.panel = Panel::Install;
|
||||
self.prev_button.enabled = false;
|
||||
self.next_button.visible = false;
|
||||
|
||||
let mut flags = InstallFlags::new();
|
||||
flags.set_conditionally(self.pepper_box.checked, install_flags::PEPPER);
|
||||
flags.set_conditionally(self.netscape_box.checked, install_flags::NETSCAPE);
|
||||
flags.set_conditionally(self.activex_box.checked, install_flags::ACTIVEX);
|
||||
flags.set_conditionally(self.player_box.checked, install_flags::PLAYER);
|
||||
flags.set_conditionally(self.player_desktop_box.checked, install_flags::PLAYER_DESKTOP);
|
||||
flags.set_conditionally(
|
||||
self.player_start_menu_box.checked,
|
||||
install_flags::PLAYER_START_MENU,
|
||||
);
|
||||
flags.set_conditionally(self.debug_chosen, install_flags::DEBUG);
|
||||
|
||||
self.progress_bar.maximum = flags.get_ticks() as i32;
|
||||
self.progress_bar.value = 0;
|
||||
|
||||
// Reset shared state.
|
||||
{
|
||||
let mut state = self.progress_state.lock().unwrap();
|
||||
state.label = "Preparing...".into();
|
||||
state.value = 0;
|
||||
state.done = false;
|
||||
state.error = None;
|
||||
}
|
||||
|
||||
// Spawn background thread.
|
||||
let progress = Arc::clone(&self.progress_state);
|
||||
std::thread::spawn(move || {
|
||||
let callback = ThreadProgressCallback {
|
||||
state: Arc::clone(&progress),
|
||||
};
|
||||
|
||||
let redir = redirection::disable_redirection();
|
||||
|
||||
let result = (|| -> Result<(), clean_flash_common::InstallError> {
|
||||
uninstaller::uninstall(&callback)?;
|
||||
installer::install(&callback, &mut flags)?;
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
redirection::enable_redirection(redir);
|
||||
|
||||
let mut state = progress.lock().unwrap();
|
||||
state.done = true;
|
||||
if let Err(e) = result {
|
||||
state.error = Some(e.to_string());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn poll_progress(&mut self) {
|
||||
let state = self.progress_state.lock().unwrap();
|
||||
self.progress_label.text = state.label.clone();
|
||||
self.progress_bar.value = state.value;
|
||||
|
||||
if state.done {
|
||||
if let Some(ref err) = state.error {
|
||||
self.failure_detail = err.clone();
|
||||
drop(state);
|
||||
self.open_failure();
|
||||
} else {
|
||||
drop(state);
|
||||
self.open_complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_complete(&mut self) {
|
||||
self.panel = Panel::Complete;
|
||||
self.prev_button.text = "QUIT".into();
|
||||
self.prev_button.enabled = true;
|
||||
self.next_button.visible = false;
|
||||
|
||||
if self.pepper_box.checked || self.netscape_box.checked || self.activex_box.checked {
|
||||
self.complete_label.text = COMPLETE_INSTALL_TEXT.to_string();
|
||||
} else {
|
||||
self.complete_label.text = COMPLETE_UNINSTALL_TEXT.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
fn open_failure(&mut self) {
|
||||
self.panel = Panel::Failure;
|
||||
self.prev_button.text = "QUIT".into();
|
||||
self.prev_button.enabled = true;
|
||||
self.next_button.text = "RETRY".into();
|
||||
self.next_button.visible = true;
|
||||
}
|
||||
|
||||
// ---- Drawing helpers ----
|
||||
|
||||
fn draw_disclaimer(&self, r: &mut Renderer) {
|
||||
self.disclaimer_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.disclaimer_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_choice(&self, r: &mut Renderer) {
|
||||
self.browser_ask_label.draw(r, &self.fonts);
|
||||
self.pepper_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.pepper_label.draw(r, &self.fonts);
|
||||
self.netscape_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.netscape_label.draw(r, &self.fonts);
|
||||
self.activex_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.activex_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_player_choice(&self, r: &mut Renderer) {
|
||||
self.player_ask_label.draw(r, &self.fonts);
|
||||
self.player_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.player_label.draw(r, &self.fonts);
|
||||
self.player_desktop_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.player_desktop_label.draw(r, &self.fonts);
|
||||
self.player_start_menu_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.player_start_menu_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_debug_choice(&mut self, r: &mut Renderer) {
|
||||
self.debug_ask_label.draw(r, &self.fonts);
|
||||
self.debug_button.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_before_install(&self, r: &mut Renderer) {
|
||||
self.before_install_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_install(&self, r: &mut Renderer) {
|
||||
self.install_header_label.draw(r, &self.fonts);
|
||||
self.progress_label.draw(r, &self.fonts);
|
||||
self.progress_bar.draw(r);
|
||||
}
|
||||
|
||||
fn draw_complete(&self, r: &mut Renderer) {
|
||||
self.complete_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_failure(&self, r: &mut Renderer) {
|
||||
self.failure_text_label.draw(r, &self.fonts);
|
||||
// Draw error detail as clipped text.
|
||||
let detail_y = PANEL_Y + 44;
|
||||
let detail_text = if self.failure_detail.len() > 300 {
|
||||
&self.failure_detail[..300]
|
||||
} else {
|
||||
&self.failure_detail
|
||||
};
|
||||
self.fonts.draw_text_multiline(
|
||||
r,
|
||||
PANEL_X + 4,
|
||||
detail_y,
|
||||
detail_text,
|
||||
11.0,
|
||||
FG_COLOR,
|
||||
1.0,
|
||||
);
|
||||
self.copy_error_button.draw(r, &self.fonts);
|
||||
}
|
||||
}
|
||||
|
||||
/// Progress callback that writes to the shared state from the background thread.
|
||||
struct ThreadProgressCallback {
|
||||
state: Arc<Mutex<ProgressState>>,
|
||||
}
|
||||
|
||||
impl ProgressCallback for ThreadProgressCallback {
|
||||
fn update_progress_label(&self, text: &str, tick: bool) {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.label = text.to_string();
|
||||
if tick {
|
||||
state.value += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn tick_progress(&self) {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.value += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn join_with_and(items: &[&str]) -> String {
|
||||
match items.len() {
|
||||
0 => String::new(),
|
||||
1 => items[0].to_string(),
|
||||
2 => format!("{} and {}", items[0], items[1]),
|
||||
_ => {
|
||||
let (last, rest) = items.split_last().unwrap();
|
||||
format!("{} and {}", rest.join(", "), last)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to load a resource image from the original C# project's asset folder.
|
||||
fn load_resource_image(name: &str) -> RgbaImage {
|
||||
let bytes: &[u8] = match name {
|
||||
"flashLogo.png" => include_bytes!("../../../resources/flashLogo.png"),
|
||||
"checkboxOn.png" => include_bytes!("../../../resources/checkboxOn.png"),
|
||||
"checkboxOff.png" => include_bytes!("../../../resources/checkboxOff.png"),
|
||||
_ => return RgbaImage::empty(),
|
||||
};
|
||||
RgbaImage::from_png_bytes(bytes)
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
use crate::install_flags::{self, InstallFlags};
|
||||
use clean_flash_common::{
|
||||
process_utils, registry, resources, system_info, InstallError, ProgressCallback,
|
||||
};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{self, Cursor};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Metadata for a single installable component.
|
||||
struct InstallEntry {
|
||||
install_text: &'static str,
|
||||
required_flags: u32,
|
||||
target_directory: PathBuf,
|
||||
registry_instructions: Option<&'static str>,
|
||||
}
|
||||
|
||||
/// Register an ActiveX OCX via regsvr32 (unregister first, then register).
|
||||
pub fn register_activex(filename: &str) -> Result<(), InstallError> {
|
||||
let path = Path::new(filename);
|
||||
let dir = path.parent().unwrap_or(Path::new("."));
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let _ = env::set_current_dir(dir);
|
||||
|
||||
let process = process_utils::run_process("regsvr32.exe", &["/s", "/u", &file_name]);
|
||||
if !process.is_successful() {
|
||||
return Err(InstallError::new(format!(
|
||||
"Failed to unregister ActiveX plugin: error code {}\n\n{}",
|
||||
process.exit_code, process.output
|
||||
)));
|
||||
}
|
||||
|
||||
let process = process_utils::run_process("regsvr32.exe", &["/s", &file_name]);
|
||||
if !process.is_successful() {
|
||||
return Err(InstallError::new(format!(
|
||||
"Failed to register ActiveX plugin: error code {}\n\n{}",
|
||||
process.exit_code, process.output
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a Windows shortcut (.lnk) using a PowerShell one-liner.
|
||||
fn create_shortcut(
|
||||
folder: &Path,
|
||||
executable: &Path,
|
||||
name: &str,
|
||||
description: &str,
|
||||
) -> Result<(), InstallError> {
|
||||
let lnk_path = folder.join(format!("{}.lnk", name));
|
||||
let exe_str = executable.to_string_lossy();
|
||||
let lnk_str = lnk_path.to_string_lossy();
|
||||
|
||||
// Use PowerShell to create the shortcut via WScript.Shell COM.
|
||||
let script = format!(
|
||||
"$ws = New-Object -ComObject WScript.Shell; \
|
||||
$s = $ws.CreateShortcut('{}'); \
|
||||
$s.TargetPath = '{}'; \
|
||||
$s.Description = '{}'; \
|
||||
$s.IconLocation = '{}'; \
|
||||
$s.Save()",
|
||||
lnk_str, exe_str, description, exe_str
|
||||
);
|
||||
|
||||
let result = process_utils::run_process("powershell.exe", &["-NoProfile", "-Command", &script]);
|
||||
if !result.is_successful() {
|
||||
return Err(InstallError::new(format!(
|
||||
"Failed to create shortcut: {}",
|
||||
result.output
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Extract the embedded 7z archive and install files to the correct locations.
|
||||
fn install_from_archive(
|
||||
archive_bytes: &[u8],
|
||||
form: &dyn ProgressCallback,
|
||||
flags: &mut InstallFlags,
|
||||
) -> Result<(), InstallError> {
|
||||
let si = system_info::SystemInfo::new();
|
||||
let flash32_path = si.flash32_path.clone();
|
||||
let flash64_path = si.flash64_path.clone();
|
||||
let system32_path = si.system32_path.clone();
|
||||
let flash_program32_path = si.flash_program32_path.clone();
|
||||
|
||||
let mut registry_to_apply: Vec<&str> = vec![resources::INSTALL_GENERAL];
|
||||
|
||||
if si.is_64bit {
|
||||
flags.set_flag(install_flags::X64);
|
||||
registry_to_apply.push(resources::INSTALL_GENERAL_64);
|
||||
}
|
||||
|
||||
let entries: Vec<(&str, InstallEntry)> = vec![
|
||||
(
|
||||
"controlpanel",
|
||||
InstallEntry {
|
||||
install_text: "Installing Flash Player utilities...",
|
||||
required_flags: install_flags::NONE,
|
||||
target_directory: system32_path.clone(),
|
||||
registry_instructions: None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"uninstaller",
|
||||
InstallEntry {
|
||||
install_text: "Extracting uninstaller...",
|
||||
required_flags: install_flags::NONE,
|
||||
target_directory: flash_program32_path.clone(),
|
||||
registry_instructions: None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"standalone",
|
||||
InstallEntry {
|
||||
install_text: "Installing 32-bit Standalone Flash Player...",
|
||||
required_flags: install_flags::PLAYER,
|
||||
target_directory: flash_program32_path.clone(),
|
||||
registry_instructions: None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"ocx32",
|
||||
InstallEntry {
|
||||
install_text: "Installing 32-bit Flash Player for Internet Explorer...",
|
||||
required_flags: install_flags::ACTIVEX,
|
||||
target_directory: flash32_path.clone(),
|
||||
registry_instructions: None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"np32",
|
||||
InstallEntry {
|
||||
install_text: "Installing 32-bit Flash Player for Firefox...",
|
||||
required_flags: install_flags::NETSCAPE,
|
||||
target_directory: flash32_path.clone(),
|
||||
registry_instructions: Some(resources::INSTALL_NP),
|
||||
},
|
||||
),
|
||||
(
|
||||
"pp32",
|
||||
InstallEntry {
|
||||
install_text: "Installing 32-bit Flash Player for Chrome...",
|
||||
required_flags: install_flags::PEPPER,
|
||||
target_directory: flash32_path.clone(),
|
||||
registry_instructions: Some(resources::INSTALL_PP),
|
||||
},
|
||||
),
|
||||
(
|
||||
"ocx64",
|
||||
InstallEntry {
|
||||
install_text: "Installing 64-bit Flash Player for Internet Explorer...",
|
||||
required_flags: install_flags::ACTIVEX | install_flags::X64,
|
||||
target_directory: flash64_path.clone(),
|
||||
registry_instructions: None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"np64",
|
||||
InstallEntry {
|
||||
install_text: "Installing 64-bit Flash Player for Firefox...",
|
||||
required_flags: install_flags::NETSCAPE | install_flags::X64,
|
||||
target_directory: flash64_path.clone(),
|
||||
registry_instructions: Some(resources::INSTALL_NP_64),
|
||||
},
|
||||
),
|
||||
(
|
||||
"pp64",
|
||||
InstallEntry {
|
||||
install_text: "Installing 64-bit Flash Player for Chrome...",
|
||||
required_flags: install_flags::PEPPER | install_flags::X64,
|
||||
target_directory: flash64_path.clone(),
|
||||
registry_instructions: Some(resources::INSTALL_PP_64),
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
let legacy = si.is_legacy_windows();
|
||||
|
||||
// Extract archive using sevenz-rust2.
|
||||
sevenz_rust2::decompress_with_extract_fn(
|
||||
Cursor::new(archive_bytes),
|
||||
".",
|
||||
|entry, reader, _dest| {
|
||||
let entry_name = entry.name().to_string();
|
||||
let parts: Vec<&str> = entry_name.split('/').collect();
|
||||
if parts.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let filename = parts[0];
|
||||
let install_key = filename.split('-').next().unwrap_or(filename);
|
||||
|
||||
// Find the matching entry.
|
||||
let Some((_key, install_entry)) = entries.iter().find(|(k, _)| *k == install_key)
|
||||
else {
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
};
|
||||
|
||||
// Check required flags.
|
||||
if install_entry.required_flags != install_flags::NONE
|
||||
&& !flags.is_set(install_entry.required_flags)
|
||||
{
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Check debug flag match.
|
||||
if install_entry.required_flags != install_flags::NONE {
|
||||
let is_debug_file = filename.contains("-debug");
|
||||
if flags.is_set(install_flags::DEBUG) != is_debug_file {
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check legacy flag for ActiveX entries.
|
||||
if (install_entry.required_flags & install_flags::ACTIVEX) != 0 {
|
||||
let is_legacy_file = filename.contains("-legacy");
|
||||
if legacy != is_legacy_file {
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
form.update_progress_label(install_entry.install_text, true);
|
||||
|
||||
// Ensure target directory exists.
|
||||
let _ = fs::create_dir_all(&install_entry.target_directory);
|
||||
|
||||
// Extract file: use just the file name (strip any path prefix).
|
||||
let out_name = parts.last().unwrap_or(&filename);
|
||||
let out_path = install_entry.target_directory.join(out_name);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
reader.read_to_end(&mut buf).map_err(sevenz_rust2::Error::from)?;
|
||||
fs::write(&out_path, &buf).map_err(sevenz_rust2::Error::from)?;
|
||||
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.map_err(|e| InstallError::new(format!("Failed to extract archive: {}", e)))?;
|
||||
|
||||
// Create Player shortcuts.
|
||||
if flags.is_set(install_flags::PLAYER) {
|
||||
let is_debug = flags.is_set(install_flags::DEBUG);
|
||||
let name = if is_debug {
|
||||
"Flash Player (Debug)"
|
||||
} else {
|
||||
"Flash Player"
|
||||
};
|
||||
let description = format!(
|
||||
"Standalone Flash Player {}{}",
|
||||
clean_flash_common::update_checker::FLASH_VERSION,
|
||||
if is_debug { " (Debug)" } else { "" }
|
||||
);
|
||||
let exe_name = if is_debug {
|
||||
"flashplayer_sa_debug.exe"
|
||||
} else {
|
||||
"flashplayer_sa.exe"
|
||||
};
|
||||
let executable = flash_program32_path.join(exe_name);
|
||||
|
||||
if flags.is_set(install_flags::PLAYER_START_MENU) {
|
||||
if let Some(start_menu) = get_start_menu() {
|
||||
let _ = create_shortcut(&start_menu, &executable, name, &description);
|
||||
}
|
||||
}
|
||||
|
||||
if flags.is_set(install_flags::PLAYER_DESKTOP) {
|
||||
if let Some(desktop) = get_desktop() {
|
||||
let _ = create_shortcut(&desktop, &executable, name, &description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect registry entries for enabled components.
|
||||
for (_key, entry) in &entries {
|
||||
if flags.is_set(entry.required_flags) {
|
||||
if let Some(reg) = entry.registry_instructions {
|
||||
registry_to_apply.push(reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form.update_progress_label("Applying registry changes...", true);
|
||||
let refs: Vec<&str> = registry_to_apply.iter().copied().collect();
|
||||
registry::apply_registry(&refs)?;
|
||||
|
||||
// Register ActiveX OCX files.
|
||||
if flags.is_set(install_flags::ACTIVEX) {
|
||||
form.update_progress_label(
|
||||
"Activating 32-bit Flash Player for Internet Explorer...",
|
||||
true,
|
||||
);
|
||||
let ocx32 = flash32_path.join(format!("Flash32_{}.ocx", si.version_path));
|
||||
register_activex(&ocx32.to_string_lossy())?;
|
||||
|
||||
if si.is_64bit {
|
||||
form.update_progress_label(
|
||||
"Activating 64-bit Flash Player for Internet Explorer...",
|
||||
true,
|
||||
);
|
||||
let ocx64 = flash64_path.join(format!("Flash64_{}.ocx", si.version_path));
|
||||
register_activex(&ocx64.to_string_lossy())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Main install entry point.
|
||||
pub fn install(
|
||||
form: &dyn ProgressCallback,
|
||||
flags: &mut InstallFlags,
|
||||
) -> Result<(), InstallError> {
|
||||
if flags.is_none_set() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// The 7z archive is embedded in the binary via include_bytes!.
|
||||
// For the port, we expect it at a known resource path; if not present,
|
||||
// this is a no-op placeholder.
|
||||
let archive_bytes: &[u8] = include_bytes!("../cleanflash.7z");
|
||||
|
||||
if archive_bytes.is_empty() {
|
||||
// Nothing to extract; still apply the rest of the steps.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
install_from_archive(archive_bytes, form, flags)
|
||||
}
|
||||
|
||||
fn get_start_menu() -> Option<PathBuf> {
|
||||
env::var("APPDATA")
|
||||
.ok()
|
||||
.map(|p| {
|
||||
PathBuf::from(p)
|
||||
.join("Microsoft")
|
||||
.join("Windows")
|
||||
.join("Start Menu")
|
||||
})
|
||||
}
|
||||
|
||||
fn get_desktop() -> Option<PathBuf> {
|
||||
env::var("USERPROFILE")
|
||||
.ok()
|
||||
.map(|p| PathBuf::from(p).join("Desktop"))
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
mod install_flags;
|
||||
mod install_form;
|
||||
mod installer;
|
||||
|
||||
use install_form::{InstallForm, HEIGHT, WIDTH};
|
||||
use clean_flash_ui::renderer::Renderer;
|
||||
use minifb::{Key, MouseButton, MouseMode, Window, WindowOptions};
|
||||
|
||||
fn main() {
|
||||
let title = format!(
|
||||
"Clean Flash Player {} Installer",
|
||||
clean_flash_common::update_checker::FLASH_VERSION
|
||||
);
|
||||
|
||||
let mut window = Window::new(
|
||||
&title,
|
||||
WIDTH,
|
||||
HEIGHT,
|
||||
WindowOptions {
|
||||
resize: false,
|
||||
..WindowOptions::default()
|
||||
},
|
||||
)
|
||||
.expect("Failed to create window");
|
||||
|
||||
// Set window icon from the resource embedded by build.rs.
|
||||
clean_flash_ui::set_window_icon(&window);
|
||||
|
||||
// Cap at ~60 fps.
|
||||
window.set_target_fps(60);
|
||||
|
||||
let mut renderer = Renderer::new(WIDTH, HEIGHT);
|
||||
let mut form = InstallForm::new();
|
||||
|
||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||
let (mx, my) = window
|
||||
.get_mouse_pos(MouseMode::Clamp)
|
||||
.unwrap_or((0.0, 0.0));
|
||||
let mouse_down = window.get_mouse_down(MouseButton::Left);
|
||||
|
||||
form.update_and_draw(&mut renderer, mx as i32, my as i32, mouse_down);
|
||||
|
||||
window
|
||||
.update_with_buffer(&renderer.buffer, WIDTH, HEIGHT)
|
||||
.expect("Failed to update window buffer");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user