electron_hook/lib.rs
1#![warn(missing_docs)]
2//! A library for modding Electron apps in-memory, without modifying any program files.
3//!
4//! This library was made for improving the modding experience for Discord, but it can be used for any Electron app.
5//!
6//! # Features
7//!
8//! - `asar`: Enables the ASAR archive builder. (enabled by default)
9//! - `uuid`: Enables the use of random UUIDs for ASAR archive names. (enabled by default)
10//!
11//! # Examples
12//!
13//! electron-hook maps the original `app.asar` to `_app.asar`,
14//! so keep this in mind if you need to call the original file anywhere,
15//! as shown in this example.
16//!
17//! ```rust,no_run
18//! use electron_hook::asar::Asar;
19//! use electron_hook::paths::mod_artifact_dir;
20//!
21//! let mod_dir = mod_artifact_dir("moonlight");
22//!
23//! let _download_url = "https://github.com/moonlight-mod/moonlight/releases/latest/download/dist.tar.gz";
24//! // extract and save `_download_url` into `mod_dir`
25//!
26//! let mod_entrypoint = mod_dir.join("injector.js");
27//!
28//! let template = r#"
29//! console.log("Mod injected!!!");
30//! let asar = require("path").resolve(__dirname, "../_app.asar");
31//! require(process.env.MODLOADER_MOD_ENTRYPOINT).inject(asar);
32//! "#;
33//!
34//! // Create the asar file
35//! let asar = Asar::new()
36//! .with_id("moonlight")
37//! .with_template(template)
38//! .with_mod_entrypoint(mod_entrypoint.to_str().unwrap())
39//! .create()
40//! .unwrap();
41//!
42//! electron_hook::launch(
43//! "/path/to/executable/Discord",
44//! "/path/to/electron-hook.so",
45//! asar.to_str().unwrap(),
46//! vec!["--pass-arguments-here".to_string()],
47//! true, // Detach the process
48//! );
49//! ```
50
51#[cfg(any(doc, feature = "asar"))]
52pub mod asar;
53pub mod paths;
54
55// For Linux
56#[cfg(target_os = "linux")]
57mod linux;
58
59// For Windows
60// TODO: Re-implement Windows support.
61#[cfg(target_os = "windows")]
62mod windows;
63
64// TODO: For MacOS
65
66/// Launches an Electron executable with the provided information.
67///
68/// `id` on Linux: the path to the executable.
69///
70/// `id` on Windows: the path to the directory containing `Update.exe`.
71///
72/// `library_path`: The path to the electron-hook `.so` or `.dll`
73///
74/// `asar_path`: The path to the ASAR file to inject
75///
76/// `args`: Arguments to pass to the executable
77///
78/// `detach`: It is recommended to set `detach` to true to prevent the process from dying when the parent process is closed.
79#[allow(unused_variables)]
80pub fn launch(
81 executable: &str,
82 library_path: &str,
83 asar_path: &str,
84 args: Vec<String>,
85 detach: bool,
86) -> Result<Option<u32>, String> {
87 #[cfg(target_os = "linux")]
88 {
89 linux::launch(executable, library_path, asar_path, args, detach)
90 }
91
92 #[cfg(target_os = "windows")]
93 {
94 // No need for detach on Windows, as the process already detaches itself.
95 windows::launch(executable, library_path, asar_path, args)
96 }
97}
98
99/// Launches an Electron executable through Flatpak with the provided information.
100///
101/// This is only available on Linux.
102///
103/// TODO: This only supports global packages. Are --user flatpak packages handled differently?
104///
105/// `id`: The ID of the flatpak package.
106///
107/// `library_path`: The path to the electron-hook `.so` or `.dll`
108///
109/// `asar_path`: The path to the ASAR file to inject
110///
111/// `args`: Arguments to pass to the executable
112///
113/// `detach`: It is recommended to set `detach` to true to prevent the process from dying when the parent process is closed.
114#[cfg(any(doc, target_os = "linux"))]
115pub fn launch_flatpak(
116 id: &FlatpakID,
117 library_path: &str,
118 asar_path: &str,
119 args: Vec<String>,
120 detach: bool,
121) -> Result<Option<u32>, String> {
122 linux::launch_flatpak(id, library_path, asar_path, args, detach)
123}
124
125/// The ID of a Flatpak package.
126pub enum FlatpakID {
127 /// A User install of a flatpak package. Will be run with `--user`
128 User(String),
129 /// A System install of a flatpak package. Will be run with `--system`
130 System(String),
131}
132
133impl std::fmt::Display for FlatpakID {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 match self {
136 FlatpakID::User(id) => write!(f, "{id}"),
137 FlatpakID::System(id) => write!(f, "{id}"),
138 }
139 }
140}