emulator on android: crashes but APKs are building

This commit is contained in:
2025-06-22 03:46:42 +01:00
parent a878483923
commit c7322d8171
7 changed files with 259 additions and 118 deletions
+6 -7
View File
@@ -7,7 +7,7 @@ default-run = "emulator"
[lib]
name = "dsa_rs"
path = "src/lib.rs"
type = "cdylib"
crate-type = ["cdylib", "rlib"]
[[bin]]
name = "emulator"
@@ -25,17 +25,16 @@ 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"]
[features]
default = ["config"]
discord-rpc = ["dep:discord-presence"]
config = ["dep:toml", "dep:serde"]
+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
}
+4 -107
View File
@@ -1,26 +1,7 @@
use std::path::Path;
use std::sync::Arc;
use std::{
path::Path,
sync::mpsc::{Receiver, Sender},
thread,
};
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.
@@ -34,9 +15,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let rpc_client =
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
setup_emulator(cmd_receiver, state_sender, rpc_client);
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)]
@@ -56,87 +37,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
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`].
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
}
#[cfg(target_os = "android")]
use winit::platform::android::{EventLoopBuilderExtAndroid, activity::AndroidApp};
#[cfg(target_os = "android")]
#[unsafe(no_mangle)]
fn android_main(app: AndroidApp) -> Result<(), Box<dyn std::error::Error>> {
// 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(())
}
@@ -1,17 +1,28 @@
<?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">
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:hasCode="false">
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: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" />
@@ -21,4 +32,4 @@
</intent-filter>
</activity>
</application>
</manifest>
</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