Skip to content

Commit c2fcf27

Browse files
committed
Export function to decomp.me scratch (beta)
1 parent e88a58b commit c2fcf27

File tree

12 files changed

+277
-30
lines changed

12 files changed

+277
-30
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ twox-hash = "1.6.3"
6464

6565
# For Linux static binaries, use rustls
6666
[target.'cfg(target_os = "linux")'.dependencies]
67-
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "json", "rustls"] }
67+
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "json", "multipart", "rustls"] }
6868
self_update = { version = "0.39.0", default-features = false, features = ["rustls"] }
6969

7070
# For all other platforms, use native TLS
7171
[target.'cfg(not(target_os = "linux"))'.dependencies]
72-
reqwest = "0.11.23"
72+
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "json", "multipart", "default-tls"] }
7373
self_update = "0.39.0"
7474

7575
[target.'cfg(windows)'.dependencies]

src/app.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use time::UtcOffset;
1616

1717
use crate::{
1818
app_config::{deserialize_config, AppConfigVersion},
19-
config::{build_globset, load_project_config, ProjectObject, ProjectObjectNode},
19+
config::{build_globset, load_project_config, ProjectObject, ProjectObjectNode, ScratchConfig},
2020
diff::DiffAlg,
2121
jobs::{
2222
objdiff::{start_build, ObjDiffConfig},
@@ -60,6 +60,7 @@ pub struct ObjectConfig {
6060
pub base_path: Option<PathBuf>,
6161
pub reverse_fn_order: Option<bool>,
6262
pub complete: Option<bool>,
63+
pub scratch: Option<ScratchConfig>,
6364
}
6465

6566
#[derive(Clone, Eq, PartialEq)]
@@ -128,6 +129,8 @@ pub struct AppConfig {
128129
#[serde(skip)]
129130
pub queue_reload: bool,
130131
#[serde(skip)]
132+
pub queue_scratch: bool,
133+
#[serde(skip)]
131134
pub project_config_info: Option<ProjectConfigInfo>,
132135
}
133136

@@ -157,6 +160,7 @@ impl Default for AppConfig {
157160
obj_change: false,
158161
queue_build: false,
159162
queue_reload: false,
163+
queue_scratch: false,
160164
project_config_info: None,
161165
}
162166
}
@@ -314,7 +318,7 @@ impl App {
314318

315319
let ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state;
316320
config_state.post_update(ctx, jobs, &self.config);
317-
diff_state.post_update(&self.config);
321+
diff_state.post_update(ctx, jobs, &self.config);
318322

319323
let Ok(mut config) = self.config.write() else {
320324
return;

src/app_config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ impl ObjectConfigV0 {
6060
base_path: Some(self.base_path),
6161
reverse_fn_order: self.reverse_fn_order,
6262
complete: None,
63+
scratch: None,
6364
}
6465
}
6566
}

src/config.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ pub struct ProjectObject {
5050
pub reverse_fn_order: Option<bool>,
5151
#[serde(default)]
5252
pub complete: Option<bool>,
53+
#[serde(default)]
54+
pub scratch: Option<ScratchConfig>,
55+
}
56+
57+
#[derive(Default, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
58+
pub struct ScratchConfig {
59+
#[serde(default)]
60+
pub platform: Option<String>,
61+
#[serde(default)]
62+
pub compiler: Option<String>,
63+
#[serde(default)]
64+
pub c_flags: Option<String>,
65+
#[serde(default)]
66+
pub ctx_path: Option<PathBuf>,
67+
#[serde(default)]
68+
pub build_ctx: bool,
5369
}
5470

5571
impl ProjectObject {
@@ -66,7 +82,7 @@ impl ProjectObject {
6682

6783
#[derive(Clone)]
6884
pub enum ProjectObjectNode {
69-
File(String, ProjectObject),
85+
File(String, Box<ProjectObject>),
7086
Dir(String, Vec<ProjectObjectNode>),
7187
}
7288

@@ -114,7 +130,7 @@ fn build_nodes(
114130
}
115131
}
116132
}
117-
let mut object = object.clone();
133+
let mut object = Box::new(object.clone());
118134
if let (Some(target_obj_dir), Some(path), None) =
119135
(target_obj_dir, &object.path, &object.target_path)
120136
{

src/jobs/create_scratch.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use std::{fs, path::PathBuf, sync::mpsc::Receiver};
2+
3+
use anyhow::{anyhow, bail, Context, Result};
4+
use const_format::formatcp;
5+
6+
use crate::{
7+
app::AppConfig,
8+
jobs::{
9+
objdiff::{run_make, BuildConfig, BuildStatus},
10+
start_job, update_status, Job, JobContext, JobResult, JobState,
11+
},
12+
};
13+
14+
#[derive(Debug, Clone)]
15+
pub struct CreateScratchConfig {
16+
pub build_config: BuildConfig,
17+
pub context_path: Option<PathBuf>,
18+
pub build_context: bool,
19+
20+
// Scratch fields
21+
pub compiler: String,
22+
pub platform: String,
23+
pub compiler_flags: String,
24+
pub function_name: String,
25+
pub target_obj: PathBuf,
26+
}
27+
28+
impl CreateScratchConfig {
29+
pub(crate) fn from_config(config: &AppConfig, function_name: String) -> Result<Self> {
30+
let Some(selected_obj) = &config.selected_obj else {
31+
bail!("No object selected");
32+
};
33+
let Some(target_path) = &selected_obj.target_path else {
34+
bail!("No target path for {}", selected_obj.name);
35+
};
36+
let Some(scratch_config) = &selected_obj.scratch else {
37+
bail!("No scratch configuration for {}", selected_obj.name);
38+
};
39+
Ok(Self {
40+
build_config: BuildConfig::from_config(config),
41+
context_path: scratch_config.ctx_path.clone(),
42+
build_context: scratch_config.build_ctx,
43+
compiler: scratch_config.compiler.clone().unwrap_or_default(),
44+
platform: scratch_config.platform.clone().unwrap_or_default(),
45+
compiler_flags: scratch_config.c_flags.clone().unwrap_or_default(),
46+
function_name,
47+
target_obj: target_path.to_path_buf(),
48+
})
49+
}
50+
51+
pub fn is_available(config: &AppConfig) -> bool {
52+
let Some(selected_obj) = &config.selected_obj else {
53+
return false;
54+
};
55+
selected_obj.target_path.is_some() && selected_obj.scratch.is_some()
56+
}
57+
}
58+
59+
#[derive(Default, Debug, Clone)]
60+
pub struct CreateScratchResult {
61+
pub scratch_url: String,
62+
}
63+
64+
#[derive(Debug, Default, Clone, serde::Deserialize)]
65+
struct CreateScratchResponse {
66+
pub slug: String,
67+
pub claim_token: String,
68+
}
69+
70+
const API_HOST: &str = "http://127.0.0.1:8000";
71+
const WEB_HOST: &str = "http://localhost:8080";
72+
73+
fn run_create_scratch(
74+
status: &JobContext,
75+
cancel: Receiver<()>,
76+
config: CreateScratchConfig,
77+
) -> Result<Box<CreateScratchResult>> {
78+
let project_dir =
79+
config.build_config.project_dir.as_ref().ok_or_else(|| anyhow!("Missing project dir"))?;
80+
81+
let mut context = None;
82+
if let Some(context_path) = &config.context_path {
83+
if config.build_context {
84+
update_status(status, "Building context".to_string(), 0, 2, &cancel)?;
85+
match run_make(&config.build_config, context_path) {
86+
BuildStatus { success: true, .. } => {}
87+
BuildStatus { success: false, stdout, stderr, .. } => {
88+
bail!("Failed to build context:\n{stdout}\n{stderr}")
89+
}
90+
}
91+
}
92+
let context_path = project_dir.join(context_path);
93+
context = Some(
94+
fs::read_to_string(&context_path)
95+
.map_err(|e| anyhow!("Failed to read {}: {}", context_path.display(), e))?,
96+
);
97+
}
98+
99+
update_status(status, "Creating scratch".to_string(), 1, 2, &cancel)?;
100+
let diff_flags = [format!("--disassemble={}", config.function_name)];
101+
let diff_flags = serde_json::to_string(&diff_flags).unwrap();
102+
let obj_path = project_dir.join(&config.target_obj);
103+
let file = reqwest::blocking::multipart::Part::file(&obj_path)
104+
.with_context(|| format!("Failed to open {}", obj_path.display()))?;
105+
let form = reqwest::blocking::multipart::Form::new()
106+
.text("compiler", config.compiler.clone())
107+
.text("platform", config.platform.clone())
108+
.text("compiler_flags", config.compiler_flags.clone())
109+
.text("diff_label", config.function_name.clone())
110+
.text("diff_flags", diff_flags)
111+
.text("context", context.unwrap_or_default())
112+
.text("source_code", "// Move related code from Context tab to here")
113+
.part("target_obj", file);
114+
let client = reqwest::blocking::Client::new();
115+
let response = client
116+
.post(formatcp!("{API_HOST}/api/scratch"))
117+
.multipart(form)
118+
.send()
119+
.map_err(|e| anyhow!("Failed to send request: {}", e))?;
120+
if !response.status().is_success() {
121+
return Err(anyhow!("Failed to create scratch: {}", response.text()?));
122+
}
123+
let body: CreateScratchResponse = response.json().context("Failed to parse response")?;
124+
let scratch_url = format!("{WEB_HOST}/scratch/{}/claim?token={}", body.slug, body.claim_token);
125+
126+
update_status(status, "Complete".to_string(), 2, 2, &cancel)?;
127+
Ok(Box::from(CreateScratchResult { scratch_url }))
128+
}
129+
130+
pub fn start_create_scratch(ctx: &egui::Context, config: CreateScratchConfig) -> JobState {
131+
start_job(ctx, "Create scratch", Job::CreateScratch, move |context, cancel| {
132+
run_create_scratch(&context, cancel, config)
133+
.map(|result| JobResult::CreateScratch(Some(result)))
134+
})
135+
}

src/jobs/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ use std::{
99

1010
use anyhow::Result;
1111

12-
use crate::jobs::{check_update::CheckUpdateResult, objdiff::ObjDiffResult, update::UpdateResult};
12+
use crate::jobs::{
13+
check_update::CheckUpdateResult, create_scratch::CreateScratchResult, objdiff::ObjDiffResult,
14+
update::UpdateResult,
15+
};
1316

1417
pub mod check_update;
18+
pub mod create_scratch;
1519
pub mod objdiff;
1620
pub mod update;
1721

@@ -20,6 +24,7 @@ pub enum Job {
2024
ObjDiff,
2125
CheckUpdate,
2226
Update,
27+
CreateScratch,
2328
}
2429
pub static JOB_ID: AtomicUsize = AtomicUsize::new(0);
2530

@@ -119,6 +124,7 @@ pub enum JobResult {
119124
ObjDiff(Option<Box<ObjDiffResult>>),
120125
CheckUpdate(Option<Box<CheckUpdateResult>>),
121126
Update(Box<UpdateResult>),
127+
CreateScratch(Option<Box<CreateScratchResult>>),
122128
}
123129

124130
fn should_cancel(rx: &Receiver<()>) -> bool {

src/jobs/objdiff.rs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,40 @@ impl Default for BuildStatus {
3333
}
3434
}
3535

36+
#[derive(Debug, Clone)]
37+
pub struct BuildConfig {
38+
pub project_dir: Option<PathBuf>,
39+
pub custom_make: Option<String>,
40+
pub selected_wsl_distro: Option<String>,
41+
}
42+
43+
impl BuildConfig {
44+
pub(crate) fn from_config(config: &AppConfig) -> Self {
45+
Self {
46+
project_dir: config.project_dir.clone(),
47+
custom_make: config.custom_make.clone(),
48+
selected_wsl_distro: config.selected_wsl_distro.clone(),
49+
}
50+
}
51+
}
52+
3653
pub struct ObjDiffConfig {
54+
pub build_config: BuildConfig,
3755
pub build_base: bool,
3856
pub build_target: bool,
39-
pub custom_make: Option<String>,
40-
pub project_dir: Option<PathBuf>,
4157
pub selected_obj: Option<ObjectConfig>,
42-
pub selected_wsl_distro: Option<String>,
4358
pub code_alg: DiffAlg,
4459
pub data_alg: DiffAlg,
4560
pub relax_reloc_diffs: bool,
4661
}
4762

4863
impl ObjDiffConfig {
4964
pub(crate) fn from_config(config: &AppConfig) -> Self {
50-
ObjDiffConfig {
65+
Self {
66+
build_config: BuildConfig::from_config(config),
5167
build_base: config.build_base,
5268
build_target: config.build_target,
53-
custom_make: config.custom_make.clone(),
54-
project_dir: config.project_dir.clone(),
5569
selected_obj: config.selected_obj.clone(),
56-
selected_wsl_distro: config.selected_wsl_distro.clone(),
5770
code_alg: config.code_alg,
5871
data_alg: config.data_alg,
5972
relax_reloc_diffs: config.relax_reloc_diffs,
@@ -69,7 +82,14 @@ pub struct ObjDiffResult {
6982
pub time: OffsetDateTime,
7083
}
7184

72-
fn run_make(cwd: &Path, arg: &Path, config: &ObjDiffConfig) -> BuildStatus {
85+
pub(crate) fn run_make(config: &BuildConfig, arg: &Path) -> BuildStatus {
86+
let Some(cwd) = &config.project_dir else {
87+
return BuildStatus {
88+
success: false,
89+
stderr: "Missing project dir".to_string(),
90+
..Default::default()
91+
};
92+
};
7393
match (|| -> Result<BuildStatus> {
7494
let make = config.custom_make.as_deref().unwrap_or("make");
7595
#[cfg(not(windows))]
@@ -130,8 +150,11 @@ fn run_build(
130150
config: ObjDiffConfig,
131151
) -> Result<Box<ObjDiffResult>> {
132152
let obj_config = config.selected_obj.as_ref().ok_or_else(|| Error::msg("Missing obj path"))?;
133-
let project_dir =
134-
config.project_dir.as_ref().ok_or_else(|| Error::msg("Missing project dir"))?;
153+
let project_dir = config
154+
.build_config
155+
.project_dir
156+
.as_ref()
157+
.ok_or_else(|| Error::msg("Missing project dir"))?;
135158
let target_path_rel = if let Some(target_path) = &obj_config.target_path {
136159
Some(target_path.strip_prefix(project_dir).map_err(|_| {
137160
anyhow!(
@@ -171,7 +194,7 @@ fn run_build(
171194
total,
172195
&cancel,
173196
)?;
174-
run_make(project_dir, target_path_rel, &config)
197+
run_make(&config.build_config, target_path_rel)
175198
}
176199
_ => BuildStatus::default(),
177200
};
@@ -185,7 +208,7 @@ fn run_build(
185208
total,
186209
&cancel,
187210
)?;
188-
run_make(project_dir, base_path_rel, &config)
211+
run_make(&config.build_config, base_path_rel)
189212
}
190213
_ => BuildStatus::default(),
191214
};

0 commit comments

Comments
 (0)