aboutsummaryrefslogtreecommitdiff
path: root/test/tools.rs
blob: d644b599ea531c33ff266c6674732a5e9aef4c4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use std::io::Write;
use std::process::{Command, Stdio};
use tempfile::NamedTempFile;

// this is where it is on my ubuntu machine, sorry :3
const OBJDUMP_PATH: &'static str = "/usr/powerpc64le-linux-gnu/bin/objdump";
const AS_PATH: &'static str = "/usr/powerpc64le-linux-gnu/bin/as";
const XENIA_DIS: &'static str = "./xenia_dis/target/release/xenia_dis";
const DOLPHIN_DIS: &'static str = "./gekko_dis/target/release/gekko_dis";
const RPCS3_DIS: &'static str = "./rpcs3_dis/target/release/cell_dis";

pub fn cell_ppu_dis_bulk(inputs: &[u8]) -> Result<Vec<String>, String> {
    assert!(inputs.len() % 4 == 0);

    let mut child = Command::new(RPCS3_DIS)
        .args(["-"])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .expect("can run");
    child.stdin.as_mut().expect("child has stdin")
        .write_all(&inputs).expect("can fill child stdin");
    let out = child.wait_with_output().expect("can wait");
    if !out.status.success() {
        let err_str = String::from_utf8_lossy(&out.stderr);
        eprintln!("failed to cell_dis: {:?}", err_str);
        panic!("failed to cell_dis: {:?}", err_str);
    }
    let text = std::str::from_utf8(&out.stdout).expect("is utf8");
    Ok(text.split("\n").map(|s| s.to_string()).collect())
}

pub fn cell_ppu_dis(bytes: &[u8]) -> Result<String, String> {
    let out = Command::new(RPCS3_DIS)
        .args([
              format!("{:02x}", bytes[3]),
              format!("{:02x}", bytes[2]),
              format!("{:02x}", bytes[1]),
              format!("{:02x}", bytes[0]),
        ])
        .output()
        .expect("can run");
    if !out.status.success() {
        eprintln!("failed to cell_dis {bytes:x?}");
        panic!("failed to cell_dis {bytes:x?}");
    }

    let cell_dis_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8");
    Ok(cell_dis_out.to_string())
}

pub fn gekko_dis_bulk(inputs: &[u8]) -> Result<Vec<String>, String> {
    assert!(inputs.len() % 4 == 0);

    let mut child = Command::new(DOLPHIN_DIS)
        .args(["-"])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .expect("can run");
    child.stdin.as_mut().expect("child has stdin")
        .write_all(&inputs).expect("can fill child stdin");
    let out = child.wait_with_output().expect("can wait");
    if !out.status.success() {
        let err_str = String::from_utf8_lossy(&out.stderr);
        eprintln!("failed to gekko_dis: {:?}", err_str);
        panic!("failed to gekko_dis: {:?}", err_str);
    }
    let text = std::str::from_utf8(&out.stdout).expect("is utf8");
    Ok(text.split("\n").map(|s| s.to_string()).collect())
}

pub fn gekko_dis(bytes: &[u8]) -> Result<String, String> {
    let out = Command::new(DOLPHIN_DIS)
        .args([
              format!("{:02x}", bytes[3]),
              format!("{:02x}", bytes[2]),
              format!("{:02x}", bytes[1]),
              format!("{:02x}", bytes[0]),
        ])
        .output()
        .expect("can run");
    if !out.status.success() {
        eprintln!("failed to gekko_dis {bytes:x?}");
        panic!("failed to gekko_dis {bytes:x?}");
    }

    let gekko_dis_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8");
    Ok(gekko_dis_out.to_string())
}

pub fn xenia_dis_bulk(inputs: &[u8]) -> Result<Vec<String>, String> {
    assert!(inputs.len() % 4 == 0);

    let mut child = Command::new(XENIA_DIS)
        .args(["-"])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .expect("can run");
    child.stdin.as_mut().expect("child has stdin")
        .write_all(&inputs).expect("can fill child stdin");
    let out = child.wait_with_output().expect("can wait");
    if !out.status.success() {
        let err_str = String::from_utf8_lossy(&out.stderr);
        eprintln!("failed to xenia_dis: {:?}", err_str);
        panic!("failed to xenia_dis: {:?}", err_str);
    }
    let text = std::str::from_utf8(&out.stdout).expect("is utf8");
    Ok(text.split("\n").map(|s| s.to_string()).collect())
}

pub fn xenia_dis(bytes: &[u8]) -> Result<String, String> {
    let out = Command::new(XENIA_DIS)
        .args([
              format!("{:02x}", bytes[3]),
              format!("{:02x}", bytes[2]),
              format!("{:02x}", bytes[1]),
              format!("{:02x}", bytes[0]),
        ])
        .output()
        .expect("can run");
    if !out.status.success() {
        eprintln!("failed to xenia_dis {bytes:x?}");
        panic!("failed to xenia_dis {bytes:x?}");
    }

    let xenia_dis_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8");
    Ok(xenia_dis_out.to_string())
}

pub fn objdump(bytes: &[u8]) -> Result<String, String> {
    let mut source = String::new();
    source.push_str(".byte ");

    let mut printed = false;
    for byte in bytes {
        if printed {
            source.push_str(", ");
        }
        source.push_str(&format!("0x{:02x}", byte));
        printed = true;
    }
    source.push_str("\n");

    let mut tempfile = NamedTempFile::new().unwrap();
    tempfile.write_all(source.as_bytes()).expect("can write source");
    let sourcepath = tempfile.into_temp_path();
    let mut objpath = sourcepath.to_path_buf();
    objpath.add_extension(".o");

    let out = Command::new(AS_PATH)
        .args(&[sourcepath.display().to_string(), "-o".to_string(), objpath.display().to_string()])
        .output()
        .expect("can run");
    if !out.status.success() {
        eprintln!("failed to assemble {bytes:x?}:");
        eprintln!("stdout: {}", std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8"));
        eprintln!("stderr: {}", std::str::from_utf8(out.stderr.as_slice()).expect("valid utf8"));
        panic!("failed to assemble");
    }

    let out = Command::new(OBJDUMP_PATH)
        .args(&["-d".to_string(), objpath.display().to_string()])
        .output()
        .expect("can run");
    if !out.status.success() {
        eprintln!("failed to objdump {bytes:x?}:");
        eprintln!("stdout: {}", std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8"));
        eprintln!("stderr: {}", std::str::from_utf8(out.stderr.as_slice()).expect("valid utf8"));
        panic!("failed to objdump");
    }


    let objdump_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8");

    let (_body, last_line) = objdump_out.trim().rsplit_once("\n").ok_or_else(|| {
        format!("did not have at least two lines of output in: \"{}\"", objdump_out)
    })?;

    Ok(last_line.to_owned())
}

#[allow(dead_code)]
pub fn gas(_text: &str) -> Result<Vec<u8>, String> {
    let _source = String::new();
    panic!("TODO")
}