1use crate::{ErrOR, ErrorInfo, JValue, Json, Jsonpiler};
3use core::fmt::{self, Display, Write as _};
4use std::{
5 env, fs,
6 path::Path,
7 process::{Command, exit},
8};
9#[expect(dead_code, reason = "todo")]
13pub(crate) fn de64(encoded: &str) -> ErrOR<Vec<u8>> {
14 const ERR: &str = "Unreachable (de64)";
15 let mut decoded = Vec::new();
16 let mut buffer = 0u32;
17 let mut buffer_len = 0u32;
18 for ch in encoded.chars() {
19 if !('0'..='o').contains(&ch) {
20 return Err("Invalid character in input string.".into());
21 }
22 let val = u32::from(ch).checked_sub(48).ok_or(ERR)?;
23 buffer = (buffer << 6u32) | val;
24 buffer_len = buffer_len.checked_add(6).ok_or(ERR)?;
25 while buffer_len >= 8 {
26 let shift = buffer_len.checked_sub(8).ok_or(ERR)?;
27 let byte = u8::try_from(buffer >> shift)?;
28 decoded.push(byte);
29 buffer_len = shift;
30 buffer &= (1u32 << buffer_len).checked_sub(1).ok_or(ERR)?;
31 }
32 }
33 Ok(decoded)
34}
35#[expect(dead_code, reason = "todo")]
39pub(crate) fn en64(input: &[u8]) -> Result<String, &str> {
40 const ERR: &str = "Unreachable (en64)";
41 let mut encoded = String::new();
42 let mut helper = |enc: u8| {
43 encoded.push(char::from_u32(u32::from(enc).checked_add(48).ok_or(ERR)?).ok_or(ERR)?);
44 Ok(())
45 };
46 let chunks = input.chunks(3);
47 for chunk in chunks {
48 let b0 = chunk.first().unwrap_or(&0u8);
49 let b1 = chunk.get(1).unwrap_or(&0u8);
50 helper((b0 >> 2u8) & 0x3F)?;
51 helper(((b0 << 4u8) | (b1 >> 4u8)) & 0x3F)?;
52 if chunk.len() >= 2 {
53 let b2 = chunk.get(2).unwrap_or(&0u8);
54 helper(((b1 << 2u8) | (b2 >> 6u8)) & 0x3F)?;
55 if chunk.len() == 3 {
56 helper(b2 & 0x3F)?;
57 }
58 }
59 }
60 Ok(encoded)
61}
62#[expect(clippy::print_stderr, reason = "")]
64pub(crate) fn error_exit(text: &str) -> ! {
65 eprintln!("{text}");
66 exit(-1)
67}
68pub(crate) fn escape_string(unescaped: &str) -> Result<String, fmt::Error> {
79 let mut escaped = String::new();
80 escaped.push('"');
81 for ch in unescaped.chars() {
82 match ch {
83 '"' => write!(escaped, r#"\""#)?,
84 '\\' => write!(escaped, r"\\")?,
85 '\n' => write!(escaped, r"\n")?,
86 '\t' => write!(escaped, r"\t")?,
87 '\r' => write!(escaped, r"\r")?,
88 '\u{08}' => write!(escaped, r"\b")?,
89 '\u{0C}' => write!(escaped, r"\f")?,
90 u_ch if u_ch < '\u{20}' => write!(escaped, r"\u{:04x}", u32::from(ch))?,
91 _ => escaped.push(ch),
92 }
93 }
94 escaped.push('"');
95 Ok(escaped)
96}
97#[must_use]
99pub(crate) const fn obj_json(val: JValue, e_info: ErrorInfo) -> Json {
100 Json { info: e_info, value: val }
101}
102#[inline]
128pub fn run() -> ! {
129 #[cfg(all(not(doc), not(target_os = "windows")))]
130 compile_error!("This program can only run on Windows.");
131 let args: Vec<String> = env::args().collect();
132 let Some(program_name) = args.first() else { error_exit("Failed to get name of the program") };
133 let input_file = unwrap_or_exit(
134 args.get(1).ok_or_else(|| format!("{program_name} <input json file> [arguments of .exe...]")),
135 "Usage",
136 );
137 let source =
138 unwrap_or_exit(fs::read_to_string(input_file), &format!("Failed to read file '{input_file}'"));
139 let mut jsonpiler = Jsonpiler::default();
140 let file = Path::new(input_file);
141 let asm = &file.with_extension("s").to_string_lossy().to_string();
142 let obj = &file.with_extension("obj").to_string_lossy().to_string();
143 let exe = &file.with_extension("exe").to_string_lossy().to_string();
144 unwrap_or_exit(jsonpiler.build(source, input_file, asm), "Error");
145 (!Command::new("as")
146 .args([asm, "-o", obj])
147 .status()
148 .unwrap_or_else(|err| error_exit(&format!("Failed to assemble: {err}")))
149 .success())
150 .then(|| error_exit("Assembling process returned Bad status."));
151 #[cfg(not(debug_assertions))]
152 {
153 unwrap_or_exit(fs::remove_file(asm), &format!("Failed to remove '{asm}'"))
154 }
155 (!Command::new("ld")
156 .args([
157 obj,
158 "-o",
159 exe,
160 "-LC:/Windows/System32",
161 "-luser32",
162 "-lkernel32",
163 "-lucrtbase",
164 "--gc-sections",
165 "-e_start",
166 ])
167 .status()
168 .unwrap_or_else(|err| error_exit(&format!("Failed to link: {err}")))
169 .success())
170 .then(|| error_exit("Linking process returned Bad status."));
171 unwrap_or_exit(fs::remove_file(obj), &format!("Failed to remove '{obj}'"));
172 let exit_code =
173 Command::new(unwrap_or_exit(env::current_dir(), "Failed to get current directory").join(exe))
174 .args(args.get(2..).unwrap_or(&[]))
175 .spawn()
176 .unwrap_or_else(|err| error_exit(&format!("Failed to spawn child process: {err}")))
177 .wait()
178 .unwrap_or_else(|err| error_exit(&format!("Failed to wait for child process: {err}")))
179 .code()
180 .unwrap_or_else(|| error_exit("Failed to retrieve the exit code."));
181 exit(exit_code)
182}
183pub(crate) fn unwrap_or_exit<T, U: Display>(result: Result<T, U>, text: &str) -> T {
185 result.unwrap_or_else(|err| error_exit(&format!("{text}: {err}")))
186}