Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
2025-06-22 03:54:44 +01:00
16 changed files with 539 additions and 440 deletions
+1
View File
@@ -1 +1,2 @@
/target
**/*.env
+6 -2
View File
@@ -1,5 +1,9 @@
{
"rust-analyzer.check.command": "clippy",
"editor.formatOnSave": true,
"rust-analyzer.cargo.features": "all"
}
"rust-analyzer.cargo.features": "all",
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
Generated
+60 -285
View File
@@ -35,7 +35,7 @@ dependencies = [
"atspi-common",
"serde",
"thiserror 1.0.69",
"zvariant 4.2.0",
"zvariant",
]
[[package]]
@@ -78,7 +78,7 @@ dependencies = [
"futures-lite",
"futures-util",
"serde",
"zbus 4.4.0",
"zbus",
]
[[package]]
@@ -212,25 +212,6 @@ dependencies = [
"libloading",
]
[[package]]
name = "ashpd"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df"
dependencies = [
"async-fs",
"async-net",
"enumflags2",
"futures-channel",
"futures-util",
"rand 0.9.1",
"raw-window-handle",
"serde",
"serde_repr",
"url",
"zbus 5.7.1",
]
[[package]]
name = "assembler"
version = "0.2.0"
@@ -319,17 +300,6 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "async-net"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
dependencies = [
"async-io",
"blocking",
"futures-lite",
]
[[package]]
name = "async-process"
version = "2.3.1"
@@ -421,11 +391,11 @@ dependencies = [
"enumflags2",
"serde",
"static_assertions",
"zbus 4.4.0",
"zbus",
"zbus-lockstep",
"zbus-lockstep-macros",
"zbus_names 3.0.0",
"zvariant 4.2.0",
"zbus_names",
"zvariant",
]
[[package]]
@@ -437,7 +407,7 @@ dependencies = [
"atspi-common",
"atspi-proxies",
"futures-lite",
"zbus 4.4.0",
"zbus",
]
[[package]]
@@ -448,8 +418,8 @@ checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496"
dependencies = [
"atspi-common",
"serde",
"zbus 4.4.0",
"zvariant 4.2.0",
"zbus",
"zvariant",
]
[[package]]
@@ -512,15 +482,6 @@ dependencies = [
"objc2 0.5.2",
]
[[package]]
name = "block2"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2"
dependencies = [
"objc2 0.6.1",
]
[[package]]
name = "blocking"
version = "1.6.1"
@@ -848,18 +809,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dispatch2"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0"
dependencies = [
"bitflags 2.9.1",
"block2 0.6.1",
"libc",
"objc2 0.6.1",
]
[[package]]
name = "dispatch2"
version = "0.3.0"
@@ -921,6 +870,12 @@ dependencies = [
"serde",
]
[[package]]
name = "dyn-clone"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
[[package]]
name = "ecolor"
version = "0.31.1"
@@ -1023,6 +978,16 @@ dependencies = [
"winit",
]
[[package]]
name = "egui_file"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e7289fecaa1af3f4944a7ac6e1c187d0700e32716c2a4c76d6bad7ffd255d72"
dependencies = [
"dyn-clone",
"egui",
]
[[package]]
name = "egui_glow"
version = "0.31.1"
@@ -1061,9 +1026,10 @@ dependencies = [
"dsa_editor",
"eframe",
"egui",
"rfd",
"egui_file",
"serde",
"toml",
"winit",
]
[[package]]
@@ -1227,15 +1193,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.31"
@@ -1376,7 +1333,7 @@ dependencies = [
"bitflags 2.9.1",
"cfg_aliases",
"cgl",
"dispatch2 0.3.0",
"dispatch2",
"glutin_egl_sys",
"glutin_glx_sys",
"glutin_wgl_sys",
@@ -1914,19 +1871,6 @@ dependencies = [
"memoffset",
]
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.9.1",
"cfg-if",
"cfg_aliases",
"libc",
"memoffset",
]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
@@ -2025,7 +1969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"libc",
"objc2 0.5.2",
"objc2-core-data",
@@ -2041,7 +1985,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
dependencies = [
"bitflags 2.9.1",
"block2 0.6.1",
"objc2 0.6.1",
"objc2-core-foundation",
"objc2-core-graphics",
@@ -2055,7 +1998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-core-location",
"objc2-foundation 0.2.2",
@@ -2067,7 +2010,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
dependencies = [
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
]
@@ -2079,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
]
@@ -2091,7 +2034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [
"bitflags 2.9.1",
"dispatch2 0.3.0",
"dispatch2",
"objc2 0.6.1",
]
@@ -2102,7 +2045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4"
dependencies = [
"bitflags 2.9.1",
"dispatch2 0.3.0",
"dispatch2",
"objc2 0.6.1",
"objc2-core-foundation",
"objc2-io-surface",
@@ -2114,7 +2057,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
dependencies = [
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
"objc2-metal",
@@ -2126,7 +2069,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
dependencies = [
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-contacts",
"objc2-foundation 0.2.2",
@@ -2145,7 +2088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"dispatch",
"libc",
"objc2 0.5.2",
@@ -2179,7 +2122,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
dependencies = [
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-app-kit 0.2.2",
"objc2-foundation 0.2.2",
@@ -2192,7 +2135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
]
@@ -2204,7 +2147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
"objc2-metal",
@@ -2227,7 +2170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-cloud-kit",
"objc2-core-data",
@@ -2247,7 +2190,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
dependencies = [
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
]
@@ -2259,7 +2202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
dependencies = [
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"objc2 0.5.2",
"objc2-core-location",
"objc2-foundation 0.2.2",
@@ -2432,12 +2375,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "pollster"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
[[package]]
name = "potential_utf"
version = "0.1.2"
@@ -2528,7 +2465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bd9640e0addc098a3481fd53bdc23970e5dd0edf6b349403aa680fb576c8f83"
dependencies = [
"cfg-if",
"nix 0.29.0",
"nix",
"quork-proc",
"thiserror 2.0.12",
"windows-sys 0.59.0",
@@ -2569,18 +2506,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
"rand_chacha",
"rand_core",
]
[[package]]
@@ -2590,17 +2517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
"rand_core",
]
[[package]]
@@ -2612,15 +2529,6 @@ dependencies = [
"getrandom 0.2.16",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.3",
]
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@@ -2662,30 +2570,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "rfd"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d"
dependencies = [
"ashpd",
"block2 0.6.1",
"dispatch2 0.2.0",
"js-sys",
"log",
"objc2 0.6.1",
"objc2-app-kit 0.3.1",
"objc2-core-foundation",
"objc2-foundation 0.3.1",
"pollster",
"raw-window-handle",
"urlencoding",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@@ -3245,15 +3129,8 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -4010,7 +3887,7 @@ dependencies = [
"android-activity",
"atomic-waker",
"bitflags 2.9.1",
"block2 0.5.1",
"block2",
"bytemuck",
"calloop",
"cfg_aliases",
@@ -4195,9 +4072,9 @@ dependencies = [
"futures-sink",
"futures-util",
"hex",
"nix 0.29.0",
"nix",
"ordered-stream",
"rand 0.8.5",
"rand",
"serde",
"serde_repr",
"sha1",
@@ -4206,42 +4083,9 @@ dependencies = [
"uds_windows",
"windows-sys 0.52.0",
"xdg-home",
"zbus_macros 4.4.0",
"zbus_names 3.0.0",
"zvariant 4.2.0",
]
[[package]]
name = "zbus"
version = "5.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68"
dependencies = [
"async-broadcast",
"async-executor",
"async-io",
"async-lock",
"async-process",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"enumflags2",
"event-listener",
"futures-core",
"futures-lite",
"hex",
"nix 0.30.1",
"ordered-stream",
"serde",
"serde_repr",
"tracing",
"uds_windows",
"windows-sys 0.59.0",
"winnow",
"zbus_macros 5.7.1",
"zbus_names 4.2.0",
"zvariant 5.5.3",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
@@ -4251,7 +4095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d"
dependencies = [
"zbus_xml",
"zvariant 4.2.0",
"zvariant",
]
[[package]]
@@ -4265,7 +4109,7 @@ dependencies = [
"syn",
"zbus-lockstep",
"zbus_xml",
"zvariant 4.2.0",
"zvariant",
]
[[package]]
@@ -4278,22 +4122,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn",
"zvariant_utils 2.1.0",
]
[[package]]
name = "zbus_macros"
version = "5.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zbus_names 4.2.0",
"zvariant 5.5.3",
"zvariant_utils 3.2.0",
"zvariant_utils",
]
[[package]]
@@ -4304,19 +4133,7 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
dependencies = [
"serde",
"static_assertions",
"zvariant 4.2.0",
]
[[package]]
name = "zbus_names"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
dependencies = [
"serde",
"static_assertions",
"winnow",
"zvariant 5.5.3",
"zvariant",
]
[[package]]
@@ -4328,8 +4145,8 @@ dependencies = [
"quick-xml 0.30.0",
"serde",
"static_assertions",
"zbus_names 3.0.0",
"zvariant 4.2.0",
"zbus_names",
"zvariant",
]
[[package]]
@@ -4416,22 +4233,7 @@ dependencies = [
"enumflags2",
"serde",
"static_assertions",
"zvariant_derive 4.2.0",
]
[[package]]
name = "zvariant"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1"
dependencies = [
"endi",
"enumflags2",
"serde",
"url",
"winnow",
"zvariant_derive 5.5.3",
"zvariant_utils 3.2.0",
"zvariant_derive",
]
[[package]]
@@ -4444,20 +4246,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn",
"zvariant_utils 2.1.0",
]
[[package]]
name = "zvariant_derive"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zvariant_utils 3.2.0",
"zvariant_utils",
]
[[package]]
@@ -4470,17 +4259,3 @@ dependencies = [
"quote",
"syn",
]
[[package]]
name = "zvariant_utils"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
dependencies = [
"proc-macro2",
"quote",
"serde",
"static_assertions",
"syn",
"winnow",
]
+1 -1
View File
@@ -20,7 +20,7 @@ pub fn tool_libcreate() {
_ => panic!("Invalid project type"),
};
let path = format!("{}/{}.dsa", project_path, project_name);
let path = format!("{project_path}/{project_name}.dsa");
std::fs::write(path, template).expect("Unable to write file");
}
+1 -1
View File
@@ -3,7 +3,7 @@ pub mod logging;
use std::io::Write;
pub fn input(prompt: &str) -> String {
print!("{}\n > ", prompt);
print!("{prompt}\n > ");
std::io::stdout().flush().unwrap();
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
+12 -2
View File
@@ -7,6 +7,7 @@ default-run = "emulator"
[lib]
name = "dsa_rs"
path = "src/lib.rs"
crate-type = ["cdylib", "rlib"]
[[bin]]
name = "emulator"
@@ -16,15 +17,24 @@ required-features = ["config"]
common = { path = "../common" }
assembler = { path = "../assembler" }
dsa_editor = { path = "../dsa_editor" }
eframe = "0.31.1"
eframe = { version = "0.31.1" }
egui = "0.31.1"
rfd = "0.15.3"
dirs = "6.0.0"
discord-presence = { version = "1.6.0", optional = true }
toml = { version = "0.8.23", optional = true }
serde = { version = "1.0.219", features = ["derive"], optional = true }
egui_file = "0.22.1"
[features]
default = ["config"]
discord-rpc = ["dep:discord-presence"]
config = ["dep:toml", "dep:serde"]
# Add support for Android for the fun of it.
[target.'cfg(target_os = "android")'.dependencies]
winit = { version = "0.30.11", features = ["android-native-activity"] }
# jni = "0.21.1"
[target.'cfg(target_os = "android")'.dependencies.eframe]
version = "0.31.1"
features = ["android-native-activity"]
-1
View File
@@ -1,2 +1 @@
#[cfg(feature = "discord-rpc")]
pub mod rpc;
+33 -9
View File
@@ -7,7 +7,7 @@
//!
//! # Configuration
//!
//! This may be disabled like so in your `.dsarc.toml` file:
//! This may be disabled like so in your `.dsa.emulator.toml` file:
//!
//! ```toml
//! [misc]
@@ -16,24 +16,23 @@
//!
//! Alternatively, you can hide this in your Discord settings.
use std::{
path::PathBuf,
sync::{
Arc,
mpsc::{Receiver, Sender},
},
time::Duration,
};
#[cfg(feature = "discord-rpc")]
use std::{path::PathBuf, sync::Arc, time::Duration};
use std::sync::mpsc::{Receiver, Sender};
#[cfg(feature = "discord-rpc")]
use discord_presence::{Client, DiscordError, models::ActivityTimestamps};
use crate::emulator::config::Config;
#[derive(Debug)]
#[cfg(feature = "discord-rpc")]
pub enum RpcClientError {
Client(DiscordError),
}
#[cfg(feature = "discord-rpc")]
impl std::fmt::Display for RpcClientError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
@@ -42,8 +41,10 @@ impl std::fmt::Display for RpcClientError {
}
}
#[cfg(feature = "discord-rpc")]
impl std::error::Error for RpcClientError {}
#[cfg(feature = "discord-rpc")]
impl From<DiscordError> for RpcClientError {
fn from(err: DiscordError) -> Self {
Self::Client(err)
@@ -52,6 +53,7 @@ impl From<DiscordError> for RpcClientError {
/// The type of activity the user is currently doing.
#[derive(Debug, Clone)]
#[cfg(feature = "discord-rpc")]
pub enum Activity {
Idle,
EditingFile(PathBuf),
@@ -59,6 +61,7 @@ pub enum Activity {
/// Messages to send over the wire.
#[derive(Debug)]
#[cfg(feature = "discord-rpc")]
pub enum Message {
/// Sent when we want to update the [`Context`].
Update(Activity),
@@ -66,9 +69,11 @@ pub enum Message {
Stop,
}
#[cfg(feature = "discord-rpc")]
unsafe impl Send for Message {}
#[derive(Debug, Clone)]
#[cfg(feature = "discord-rpc")]
pub struct RpcClient {
/// Sends updates to [`Context`] (our state).
sender: Sender<Message>,
@@ -76,6 +81,7 @@ pub struct RpcClient {
thread_handle: Option<Arc<std::thread::JoinHandle<()>>>,
}
#[cfg(feature = "discord-rpc")]
impl RpcClient {
#[expect(clippy::unreadable_literal)]
/// Sets up the [`RpcClient`].
@@ -169,6 +175,7 @@ impl RpcClient {
}
// Possibly unneeded but good practice.
#[cfg(feature = "discord-rpc")]
impl Drop for RpcClient {
fn drop(&mut self) {
self.stop();
@@ -181,14 +188,31 @@ impl Drop for RpcClient {
}
}
/// Stub for when the feature is disabled.
#[cfg(not(feature = "discord-rpc"))]
pub struct RpcClient {}
/// Stub for when the feature is disabled.
#[cfg(not(feature = "discord-rpc"))]
pub enum Message {}
/// Stub for when the feature is disabled.
#[cfg(not(feature = "discord-rpc"))]
pub enum Activity {}
/// Gets the discord [`RpcClient`] or returns None if this has been disabled in the config
/// options.
#[cfg(feature = "config")]
#[allow(clippy::needless_pass_by_value, unused_variables)]
pub fn get_rpc_client_or_none(
config: &Config,
rpc_sender: Sender<Message>,
rpc_reciever: Receiver<Message>,
) -> Result<Option<RpcClient>, Box<dyn std::error::Error + 'static>> {
#[cfg(not(feature = "discord-rpc"))]
return Ok(None);
#[cfg(feature = "discord-rpc")]
if config.misc.use_discord_rpc {
Ok(Some(RpcClient::new(rpc_sender, rpc_reciever)?))
} else {
+3 -3
View File
@@ -1,4 +1,3 @@
#[cfg(feature = "discord-rpc")]
use std::sync::Arc;
use std::{
sync::mpsc::{self, Receiver, Sender},
@@ -6,7 +5,7 @@ use std::{
time::Duration,
};
#[cfg(feature = "discord-rpc")]
#[allow(unused_imports)]
use crate::emulator::misc::rpc::{Activity, RpcClient};
use crate::emulator::system::{
@@ -17,11 +16,12 @@ use crate::emulator::system::{
use common::prelude::*;
#[expect(clippy::too_many_lines)]
#[allow(unused_variables)]
pub fn run_emulator(
cmd_rx: &Receiver<Command>,
state_tx: &Sender<State>,
mut processor: Processor,
#[cfg(feature = "discord-rpc")] rpc_client: Option<&Arc<RpcClient>>,
rpc_client: Option<&Arc<RpcClient>>,
) {
println!("INFO: Starting emulator.");
+148 -62
View File
@@ -1,10 +1,14 @@
use std::{ffi::OsStr, path::PathBuf, sync::mpsc::Sender};
use std::{
ffi::OsStr,
path::{Path, PathBuf},
sync::mpsc::Sender,
};
use common::prelude::Instruction;
use egui::{Align, Context, Key, Layout, Ui};
use rfd::FileDialog;
use dsa_editor::{CodeEditor, ColorTheme, Syntax};
use egui_file::FileDialog;
use crate::emulator::{
system::model::{Command, State},
@@ -29,6 +33,10 @@ pub struct Editor {
cursor_col: usize,
cursor_line: usize,
// file dialogs
open_file_dialog: Option<FileDialog>,
save_file_dialog: Option<FileDialog>,
// other
visible: bool,
sender: Sender<Command>,
@@ -98,6 +106,8 @@ impl Editor {
load_offset: 0,
offset_str: String::new(),
error: None,
open_file_dialog: None,
save_file_dialog: None,
}
}
@@ -130,36 +140,31 @@ impl Editor {
}
fn save(&mut self) {
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
dirs::home_dir().expect(
"Couldn't get your current working directory or your home directory.",
)
});
if let Some(path) = &self.path {
if let Err(why) = std::fs::write(path, &self.text) {
self.error = Some(format!("Failed to save file: {why}"));
return;
}
self.buffer = self.text.clone();
self.unsaved = false;
return;
if self.open_file_dialog.is_some() {
// TODO: Flash an error stating you can only have one menu open at once.
self.open_file_dialog = None;
}
if let Some(path) = FileDialog::new()
.add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
.add_filter("all", &["*"])
.set_directory(&work_dir)
.save_file()
{
if let Err(why) = std::fs::write(&path, &self.text) {
if let Some(path) = &self.path {
// Save to existing path
if let Err(why) = std::fs::write(path, &self.text) {
self.error = Some(format!("Failed to save file: {why}"));
} else {
self.path = Some(path);
self.buffer = self.text.clone();
self.unsaved = false;
}
} else {
// Open the save dialog.
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
dirs::home_dir().expect(
"Couldn't get your current working directory or your home directory.",
)
});
if self.save_file_dialog.is_none() {
let mut dialog = FileDialog::save_file(Some(work_dir));
dialog.open();
self.save_file_dialog = Some(dialog);
}
}
}
@@ -170,46 +175,125 @@ impl Editor {
)
});
if let Some(path) = FileDialog::new()
.add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
.add_filter("all", &["*"])
.set_directory(&work_dir)
.pick_file()
{
match path.extension().and_then(|ext| ext.to_str()) {
Some("dsb") => {
let contents = match std::fs::read(&path) {
Ok(contents) => contents,
Err(why) => {
self.error = Some(format!("Failed to read file: {why}"));
return;
}
};
// if let Some(path) = FileDialog::new()
// .add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
// .add_filter("all", &["*"])
// .set_directory(&work_dir)
// .pick_file()
// {
// match path.extension().and_then(|ext| ext.to_str()) {
// Some("dsb") => {
// let contents = match std::fs::read(&path) {
// Ok(contents) => contents,
// Err(why) => {
// self.error = Some(format!("Failed to read file: {why}"));
// return;
// }
// };
self.path = Some(path.clone());
self.output = contents;
self.unsaved = false;
self.text = String::from("Loaded Binary File!");
self.buffer = self.text.clone();
self.unsaved = false;
}
_ => {
if let Ok(contents) = std::fs::read_to_string(&path) {
self.path = Some(path.clone());
self.text.clone_from(&contents);
self.buffer = contents;
self.unsaved = false;
}
}
// self.path = Some(path.clone());
// self.output = contents;
// self.unsaved = false;
// self.text = String::from("Loaded Binary File!");
// self.buffer = self.text.clone();
// self.unsaved = false;
// }
// _ => {
// if let Ok(contents) = std::fs::read_to_string(&path) {
// self.path = Some(path.clone());
// self.text.clone_from(&contents);
// self.buffer = contents;
// self.unsaved = false;
// }
// }
// }
if self.save_file_dialog.is_some() {
// TODO: Flash an error stating you can only have one menu open at once.
self.save_file_dialog = None;
}
if self.open_file_dialog.is_none() {
if let Some(p) = &self.path {
let path = p.parent().map(Path::to_path_buf);
let mut dialog = FileDialog::open_file(path);
dialog.open();
self.open_file_dialog = Some(dialog);
} else {
let mut dialog = FileDialog::open_file(Some(work_dir));
dialog.open();
self.open_file_dialog = Some(dialog);
}
std::env::set_current_dir(
path.parent().expect("A file should be in a directory!"),
)
.expect("ERROR: Failed to set current working directory.");
}
}
fn handle_file_dialogs(&mut self, ctx: &egui::Context) {
// Handle open dialog
if let Some(dialog) = &mut self.open_file_dialog {
if dialog.show(ctx).selected() {
if let Some(file) = dialog.path() {
match std::fs::read_to_string(file) {
Ok(content) => {
self.text = content;
self.path = Some(file.to_path_buf());
self.unsaved = false;
self.error = None;
}
Err(e) => {
self.error = Some(format!("Failed to read file: {e}"));
}
}
}
self.open_file_dialog = None;
}
}
// Handle save dialog
if let Some(dialog) = &mut self.save_file_dialog {
if dialog.show(ctx).selected() {
if let Some(file) = dialog.path() {
match std::fs::write(file, &self.text) {
Ok(()) => {
self.path = Some(file.to_path_buf());
self.unsaved = false;
self.error = None;
}
Err(e) => {
self.error = Some(format!("Failed to save file: {e}"));
}
}
}
self.save_file_dialog = None;
}
}
}
// fn open(&mut self) {
// let work_dir = std::env::current_dir().unwrap_or_else(|_| {
// dirs::home_dir().expect(
// "Couldn't get your current working directory or your home directory.",
// )
// });
// if let Some(path) = FileDialog::new()
// .add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
// .add_filter("all", &["*"])
// .set_directory(&work_dir)
// .pick_file()
// {
// if let Ok(contents) = std::fs::read_to_string(&path) {
// self.path = Some(path.clone());
// self.text.clone_from(&contents);
// self.buffer = contents;
// self.unsaved = false;
// }
// std::env::set_current_dir(
// path.parent().expect("A file should be in a directory!"),
// )
// .expect("ERROR: Failed to set current working directory.");
// }
// }
fn render_output(&self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
// Output area with synchronized scrolling
egui::ScrollArea::vertical()
@@ -336,7 +420,9 @@ impl Editor {
});
}
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, ctx: &Context) {
self.handle_file_dialogs(ctx);
ui.horizontal(|ui| {
ui.label(format!("File type: {}", self.extension()));
ui.label(format!("Filename: {}", self.filename()));
+114
View File
@@ -13,3 +13,117 @@
)]
pub mod emulator;
use std::{
sync::{
Arc,
mpsc::{Receiver, Sender},
},
thread,
};
#[cfg(target_os = "android")]
use winit::platform::android::{EventLoopBuilderExtAndroid, activity::AndroidApp};
use crate::emulator::{
misc::rpc::RpcClient,
system::{
emulator::run_emulator,
memory::MainStore,
model::{Command, State},
processor::Processor,
},
ui::{
control_unit::ControlPanel, display::Display, editor::Editor,
interface::EmulatorUI, memory_inspector::MemoryInspector,
stack_inspector::StackInspector,
},
};
#[cfg(target_os = "android")]
#[unsafe(no_mangle)]
pub fn android_main(app: AndroidApp) -> Result<(), Box<dyn std::error::Error>> {
use crate::emulator::{config::Config, misc::rpc::get_rpc_client_or_none};
use std::path::Path;
// Initialize channels and read in configuration.
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
let (state_sender, state_reciever) = std::sync::mpsc::channel();
let config = Config::load(Path::new(".dsa.emulator.toml"))?;
// Setup RPC if enabled.
let (rpc_sender, rpc_reciever) = std::sync::mpsc::channel();
let rpc_client =
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
setup_emulator(cmd_receiver, state_sender, rpc_client);
let ui = setup_ui(cmd_sender, state_reciever);
// Run UI.
#[allow(unused_variables)]
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
event_loop_builder: Some(Box::new(move |builder| {
#[cfg(target_os = "android")]
builder.with_android_app(app);
})),
..Default::default()
};
eframe::run_native(
"DSA Simulator (Damn Simple Architecture 🔥)",
options,
Box::new(move |cc| {
cc.egui_ctx.set_visuals(egui::Visuals::default());
Ok(Box::new(ui))
}),
)?;
Ok(())
}
pub fn setup_emulator(
cmd_receiver: Receiver<Command>,
state_sender: Sender<State>,
rpc_client: Option<Arc<RpcClient>>,
) {
let main_store = MainStore::new();
let processor = Processor::new(Box::new(main_store), vec![]);
thread::spawn(move || {
run_emulator(&cmd_receiver, &state_sender, processor, rpc_client.as_ref());
});
}
/// Creates the [`EmulatorUI`].
#[must_use]
pub fn setup_ui(
cmd_sender: Sender<Command>,
state_reciever: Receiver<State>,
) -> EmulatorUI {
let mut ui = EmulatorUI::new(cmd_sender.clone(), state_reciever);
// Create UI modules.
let control_unit = ControlPanel::new(cmd_sender.clone());
ui.add_component(Box::new(control_unit));
let mem_inspector = MemoryInspector::new(cmd_sender.clone());
ui.add_component(Box::new(mem_inspector));
let stack_inspector = StackInspector::new();
ui.add_component(Box::new(stack_inspector));
let editor = Editor::new(cmd_sender);
ui.add_component(Box::new(editor));
let display = Display::new();
ui.add_component(Box::new(display));
let history = emulator::ui::history::History::new();
ui.add_component(Box::new(history));
ui
}
+5 -74
View File
@@ -1,28 +1,7 @@
#[cfg(feature = "discord-rpc")]
use std::path::Path;
use std::sync::Arc;
use std::{
path::Path,
sync::mpsc::{Receiver, Sender},
thread,
};
#[cfg(feature = "discord-rpc")]
use dsa_rs::emulator::misc::rpc::{RpcClient, get_rpc_client_or_none};
use dsa_rs::emulator::{
config::Config,
system::{
emulator::run_emulator,
memory::MainStore,
model::{Command, State},
processor::Processor,
},
ui::{
control_unit::ControlPanel, display::Display, editor::Editor,
interface::EmulatorUI, memory_inspector::MemoryInspector,
stack_inspector::StackInspector,
},
};
use dsa_rs::emulator::{config::Config, misc::rpc::get_rpc_client_or_none};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize channels and read in configuration.
@@ -31,21 +10,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::load(Path::new(".dsa.emulator.toml"))?;
// Setup RPC if enabled.
#[cfg(feature = "discord-rpc")]
let (rpc_sender, rpc_reciever) = std::sync::mpsc::channel();
#[cfg(feature = "discord-rpc")]
let rpc_client =
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
#[cfg(feature = "discord-rpc")]
setup_emulator(cmd_receiver, state_sender, rpc_client);
#[cfg(not(feature = "discord-rpc"))]
setup_emulator(cmd_receiver, state_sender);
dsa_rs::setup_emulator(cmd_receiver, state_sender, rpc_client);
let ui = setup_ui(cmd_sender, state_reciever);
let ui = dsa_rs::setup_ui(cmd_sender, state_reciever);
// Run UI.
#[allow(unused_variables)]
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
..Default::default()
@@ -62,47 +37,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
fn setup_emulator(
cmd_receiver: Receiver<Command>,
state_sender: Sender<State>,
#[cfg(feature = "discord-rpc")] rpc_client: Option<Arc<RpcClient>>,
) {
let main_store = MainStore::new();
let processor = Processor::new(Box::new(main_store), vec![]);
thread::spawn(move || {
#[cfg(feature = "discord-rpc")]
run_emulator(&cmd_receiver, &state_sender, processor, rpc_client.as_ref());
#[cfg(not(feature = "discord-rpc"))]
run_emulator(&cmd_receiver, &state_sender, processor);
});
}
/// Creates the [`EmulatorUI`].
fn setup_ui(cmd_sender: Sender<Command>, state_reciever: Receiver<State>) -> EmulatorUI {
let mut ui = EmulatorUI::new(cmd_sender.clone(), state_reciever);
// Create UI modules.
let control_unit = ControlPanel::new(cmd_sender.clone());
ui.add_component(Box::new(control_unit));
let mem_inspector = MemoryInspector::new(cmd_sender.clone());
ui.add_component(Box::new(mem_inspector));
let stack_inspector = StackInspector::new();
ui.add_component(Box::new(stack_inspector));
let editor = Editor::new(cmd_sender.clone());
ui.add_component(Box::new(editor));
let display = Display::new();
ui.add_component(Box::new(display));
let history = dsa_rs::emulator::ui::history::History::new();
ui.add_component(Box::new(history));
ui
}
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml-model href="http://schemas.android.com/apk/res/android"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dsa.emulator"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="35" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<!-- <uses-permission android:name="android.permission.INTERNET" /> -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:label="DSA Emulator"
android:icon="@mipmap/ic_launcher"
android:hasCode="false"
android:hardwareAccelerated="true">
<activity
android:name="android.app.NativeActivity"
android:label="DSA Emulator"
android:exported="true"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="unspecified"
android:launchMode="singleInstance">
<meta-data
android:name="android.app.lib_name"
android:value="dsa_rs" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env pwsh
$env:ANDROID_HOME = "C:\Users\jacob\AppData\Local\Android\Sdk"
$TOOL_PREFIX = "$env:ANDROID_HOME\build-tools\35.0.1"
# Only really works on Windows, for aarch64.
# Create directories
New-Item -ItemType Directory -Force -Path "..\target\apk_build\lib\arm64-v8a"
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\values"
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-hdpi"
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-mdpi"
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-xhdpi"
New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-xxhdpi"
# Copy the shared library
Copy-Item "..\target\aarch64-linux-android\release\libdsa_rs.so" "..\target\apk_build\lib\arm64-v8a\"
# Copy the manifest
Copy-Item "..\resources\emulator\AndroidManifest.xml" "..\target\apk_build\AndroidManifest.xml"
# Copy the icons
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-hdpi\ic_launcher.png"
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-mdpi\ic_launcher.png"
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-xhdpi\ic_launcher.png"
Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-xxhdpi\ic_launcher.png"
# Create strings.xml
@"
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">DSA Emulator</string>
</resources>
"@ | Out-File -FilePath "..\target\apk_build\res\values\strings.xml" -Encoding utf8
# Change to build directory
Push-Location "..\target\apk_build"
try {
# Compile resources
& "$TOOL_PREFIX\aapt2.exe" compile --dir res -o compiled_resources.zip
# Link resources
& "$TOOL_PREFIX\aapt2.exe" link -o unaligned.apk `
-I "$env:ANDROID_HOME\platforms\android-35\android.jar" `
--manifest AndroidManifest.xml `
compiled_resources.zip
# Add native libraries to APK
& "C:\Program Files\7-Zip\7z.exe" a -tzip unaligned.apk lib\*
# Align APK
& "$TOOL_PREFIX\zipalign.exe" -v 4 unaligned.apk aligned.apk
# Generate debug keystore if it doesn't exist
if (-not (Test-Path "debug.keystore")) {
& keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US"
}
# Sign APK
& "$TOOL_PREFIX\apksigner.bat" sign --ks debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android --key-pass pass:android --out dsa_emulator.apk aligned.apk
# Copy final APK
Copy-Item "dsa_emulator.apk" "..\dsa_emulator.apk"
Write-Host "APK created successfully at: ..\target\dsa_emulator.apk" -ForegroundColor Green
}
catch {
Write-Error "Build failed: $_"
}
finally {
# Return to original directory
Pop-Location
}
+46
View File
@@ -0,0 +1,46 @@
#!/bin/sh
export ANDROID_HOME="/mnt/c/Users/jacob/AppData/Local/Android/Sdk"
export TOOL_PREFIX="$ANDROID_HOME/build-tools/35.0.1"
# Only really works on Linux, for aarch64.
mkdir -p ../target/apk_build/lib/arm64-v8a
mkdir -p ../target/apk_build/res/values
mkdir -p ../target/apk_build/res/mipmap-hdpi
mkdir -p ../target/apk_build/res/mipmap-mdpi
mkdir -p ../target/apk_build/res/mipmap-xhdpi
mkdir -p ../target/apk_build/res/mipmap-xxhdpi
# Copy the shared library.
cp ../target/aarch64-linux-android/release/libdsa_rs.so ../target/apk_build/lib/arm64-v8a/
# Copy the manifest.
cp AndroidManifest.xml ../target/apk_build/AndroidManifest.xml
cat << EOF > ../target/apk_build/res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">DSA Emulator</string>
</resources>
EOF
pushd ../target/apk_build
$TOOL_PREFIX/aapt2 compile --dir res -o compiled_resources.zip
$TOOL_PREFIX/aapt2 link -o unaligned.apk \
-I "$ANDROID_HOME/platforms/android-35/android.jar" \
--manifest AndroidManifest.xml \
compiled_resources.zip
zip -r unaligned.apk lib/
$TOOL_PREFIX/zipalign -v 4 unaligned.apk aligned.apk
keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US"
$TOOL_PREFIX/apksigner sign --ks debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android --key-pass pass:android --out dsa_emulator.apk aligned.apk
cp dsa_emulator.apk ../dsa_emulator.apk
popd