diff --git a/spwn-lang/Cargo.lock b/spwn-lang/Cargo.lock index 749642dd..375b5686 100644 --- a/spwn-lang/Cargo.lock +++ b/spwn-lang/Cargo.lock @@ -6,6 +6,37 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug", +] + [[package]] name = "base64" version = "0.2.1" @@ -18,12 +49,37 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43" +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding", + "cipher", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + [[package]] name = "crc32fast" version = "1.2.0" @@ -39,6 +95,16 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -87,6 +153,12 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "proc-macro2" version = "1.0.19" @@ -136,7 +208,9 @@ checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" name = "spwn-lang" version = "0.1.0" dependencies = [ + "aes", "base64", + "block-modes", "crc32fast", "lazy_static", "libflate", @@ -172,6 +246,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "unicode-xid" version = "0.2.1" @@ -184,6 +264,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + [[package]] name = "winapi" version = "0.3.9" diff --git a/spwn-lang/Cargo.toml b/spwn-lang/Cargo.toml index a4fa8105..a70a9576 100644 --- a/spwn-lang/Cargo.toml +++ b/spwn-lang/Cargo.toml @@ -16,4 +16,5 @@ logos = "0.11.4" termcolor = "1.1.2" smallvec = "1.4.2" - +aes = "0.6.0" +block-modes = "0.7.0" \ No newline at end of file diff --git a/spwn-lang/help.txt b/spwn-lang/help.txt index 49e084bc..9968e897 100644 --- a/spwn-lang/help.txt +++ b/spwn-lang/help.txt @@ -14,3 +14,6 @@ Flags: --no-optimize Removes post-optimization of triggers, making the output more readable, while also using a lot more objects and groups + + --level-name [name] + Targets a specific level diff --git a/spwn-lang/libraries/std/counter.spwn b/spwn-lang/libraries/std/counter.spwn index 706200ae..54bd9f23 100644 --- a/spwn-lang/libraries/std/counter.spwn +++ b/spwn-lang/libraries/std/counter.spwn @@ -410,6 +410,13 @@ impl @counter { return clone }, + _mod_: (self, num: @number | @counter) { + clone = self.clone() + out = @counter::new() + clone.divide(num, remainder = out) + return out + }, + _more_than_: (self, other: @number | @counter) { if other.type == @number { diff --git a/spwn-lang/src/ast.rs b/spwn-lang/src/ast.rs index 77c02147..e9be88be 100644 --- a/spwn-lang/src/ast.rs +++ b/spwn-lang/src/ast.rs @@ -63,7 +63,7 @@ pub enum ValueBody { Bool(bool), Expression(Expression), Str(String), - Import(ImportType), + Import(ImportType, bool), Array(Vec), Obj(ObjectLiteral), Macro(Macro), diff --git a/spwn-lang/src/compiler.rs b/spwn-lang/src/compiler.rs index 3f8f7303..ce3fcfa8 100644 --- a/spwn-lang/src/compiler.rs +++ b/spwn-lang/src/compiler.rs @@ -208,6 +208,7 @@ pub fn compile_spwn( &start_context, &mut globals, start_info.clone(), + false, )?; if standard_lib.len() != 1 { @@ -892,7 +893,18 @@ pub fn import_module( context: &Context, globals: &mut Globals, info: CompilerInfo, + forced: bool, ) -> Result { + if !forced { + if let Some(ret) = globals.prev_imports.get(path) { + merge_impl(&mut globals.implementations, &ret.1); + return Ok(smallvec![( + store_value(ret.0.clone(), 1, globals, context), + context.clone() + )]); + } + } + let mut module_path = match path { ImportType::Script(p) => globals .path @@ -963,6 +975,7 @@ pub fn import_module( &start_context, globals, info.clone(), + false, )?; if standard_lib.len() != 1 { @@ -1020,7 +1033,7 @@ pub fn import_module( merge_impl(&mut globals.implementations, &stored_impl); } - Ok(if returns.is_empty() { + let out = if returns.is_empty() { contexts .iter() .map(|x| { @@ -1035,7 +1048,16 @@ pub fn import_module( } returns - }) + }; + + if out.len() == 1 && &out[0].1 == context { + let cloned = clone_and_get_value(out[0].0, 9999, globals, context, true); + let s_impl = globals.implementations.clone(); + + globals.prev_imports.insert(path.clone(), (cloned, s_impl)); + } + + Ok(out) } // const ID_MAX: u16 = 999; diff --git a/spwn-lang/src/compiler_types.rs b/spwn-lang/src/compiler_types.rs index 8988e1f0..a81b1fa3 100644 --- a/spwn-lang/src/compiler_types.rs +++ b/spwn-lang/src/compiler_types.rs @@ -135,13 +135,13 @@ pub fn store_value( index } -pub fn clone_value( +pub fn clone_and_get_value( index: usize, lifetime: u16, globals: &mut Globals, context: &Context, constant: bool, -) -> StoredValue { +) -> Value { let mut old_val = globals.stored_values[index].clone(); match &mut old_val { @@ -184,6 +184,19 @@ pub fn clone_value( _ => (), }; + old_val +} + + +pub fn clone_value( + index: usize, + lifetime: u16, + globals: &mut Globals, + context: &Context, + constant: bool, +) -> StoredValue { + let old_val = clone_and_get_value(index, lifetime, globals, context, constant); + //clone all inner values //do the thing //bing bang @@ -216,7 +229,7 @@ pub type FnIDPtr = usize; pub type Returns = SmallVec<[(StoredValue, Context); CONTEXT_MAX]>; -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone, Hash)] pub enum ImportType { Script(PathBuf), Lib(String) @@ -284,6 +297,10 @@ impl Context { } } +// pub fn compare_contexts(context1: Context, context2: Context, globals: &mut Globals) -> bool { +// // returns true if the contexts are equal/mergable + +// } //will merge one set of context, returning false if no mergable contexts were found pub fn merge_contexts(contexts: &mut SmallVec<[Context; CONTEXT_MAX]>, globals: &mut Globals) -> bool { @@ -899,6 +916,8 @@ pub struct Globals { pub func_ids: Vec, pub objects: Vec, + pub prev_imports: HashMap, + pub trigger_order: usize, pub uid_counter: usize, @@ -958,6 +977,8 @@ impl Globals { lowest_y: HashMap::new(), type_ids: HashMap::new(), + + prev_imports: HashMap::new(), type_id_count: 0, trigger_order: 0, uid_counter: 0, @@ -1769,9 +1790,9 @@ impl ast::Variable { }) .collect(); } - ast::ValueBody::Import(i) => { + ast::ValueBody::Import(i, f) => { //let mut new_contexts = context.clone(); - start_val = import_module(i, &context, globals, info.clone())?; + start_val = import_module(i, &context, globals, info.clone(), *f)?; } ast::ValueBody::TypeIndicator(name) => { diff --git a/spwn-lang/src/documentation.rs b/spwn-lang/src/documentation.rs index 08da66ee..8786fe92 100644 --- a/spwn-lang/src/documentation.rs +++ b/spwn-lang/src/documentation.rs @@ -21,6 +21,7 @@ pub fn document_lib(path: &str) -> Result { &start_context, &mut globals, CompilerInfo::new(), + false, )?; if module.len() > 1 { diff --git a/spwn-lang/src/fmt.rs b/spwn-lang/src/fmt.rs index a1aa15ca..83a5c77f 100644 --- a/spwn-lang/src/fmt.rs +++ b/spwn-lang/src/fmt.rs @@ -254,7 +254,7 @@ impl SpwnFmt for ValueBody { Bool(x) => format!("{}", x), Expression(x) => format!("({})", x.fmt(ind)), Str(x) => format!("\"{}\"", x), - Import(x) => format!("import {:?}", x), + Import(x, f) => format!("import{} {:?}", if *f { "!" } else { "" }, x), Obj(x) => { (match x.mode { ObjectMode::Object => "obj".to_string(), diff --git a/spwn-lang/src/levelstring.rs b/spwn-lang/src/levelstring.rs index 9bde42a0..d647ec41 100644 --- a/spwn-lang/src/levelstring.rs +++ b/spwn-lang/src/levelstring.rs @@ -151,7 +151,7 @@ const START_HEIGHT: u16 = 10; const MAX_HEIGHT: u16 = 40; pub const SPWN_SIGNATURE_GROUP: Group = Group { - id: ID::Specific(1111), + id: ID::Specific(1001), }; //use crate::ast::ObjectMode; @@ -671,24 +671,46 @@ fn base_64_decrypt(encoded: Vec) -> Vec { use quick_xml::events::{BytesText, Event}; use quick_xml::Reader; -use std::io::BufReader; - -pub fn get_level_string(ls: String) -> String { +//use std::io::BufReader; +fn decrypt_savefile(mut sf: Vec) -> Vec { + if cfg!(target_os = "macos") { + use aes::Aes256; + + use block_modes::block_padding::Pkcs7; + use block_modes::{BlockMode, Ecb}; + + const IOS_KEY: &[u8] = &[ + 0x69, 0x70, 0x75, 0x39, 0x54, 0x55, 0x76, 0x35, 0x34, 0x79, 0x76, 0x5D, 0x69, 0x73, + 0x46, 0x4D, 0x68, 0x35, 0x40, 0x3B, 0x74, 0x2E, 0x35, 0x77, 0x33, 0x34, 0x45, 0x32, + 0x52, 0x79, 0x40, 0x7B, + ]; + + type AesEcb = Ecb; + + // re-create cipher mode instance + let cipher = AesEcb::new_var(&IOS_KEY, &[]).unwrap(); + + cipher.decrypt(&mut sf).unwrap().to_vec() + } else { + let xor = xor(sf.to_vec(), 11); + let replaced = String::from_utf8(xor) + .unwrap() + .replace("-", "+") + .replace("_", "/") + .replace("\0", ""); + let b64 = base64::decode(replaced.as_str()).unwrap(); + let mut decoder = gzip::Decoder::new(&b64[..]).unwrap(); + let mut data = Vec::new(); + decoder.read_to_end(&mut data).unwrap(); + data + } +} +pub fn get_level_string(ls: Vec, level_name: Option) -> Result { //decrypting the savefile - let xor = xor(ls.as_bytes().to_vec(), 11); - let replaced = String::from_utf8(xor) - .unwrap() - .replace("-", "+") - .replace("_", "/") - .replace("\0", ""); - let b64 = base64::decode(replaced.as_str()).unwrap(); - let decoder = gzip::Decoder::new(&b64[..]).unwrap(); - - //println!("{}", String::from_utf8(buf[..1000].to_vec()).unwrap()); + let content = decrypt_savefile(ls); + let string_content = String::from_utf8_lossy(&content); - //getting level string - - let mut reader = Reader::from_reader(BufReader::new(decoder)); + let mut reader = Reader::from_str(&string_content); reader.trim_text(true); let mut buf = Vec::new(); @@ -696,18 +718,41 @@ pub fn get_level_string(ls: String) -> String { // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s) let mut level_string = String::new(); let mut k4_detected = false; + let mut k2_detected = false; + let mut level_detected = false; + loop { match reader.read_event(&mut buf) { // unescape and decode the text event using the reader encoding Ok(Event::Text(e)) => { let text = e.unescape_and_decode(&reader).unwrap(); - if text == "k4" { + + if text == "k2" { + k2_detected = true; + if level_detected { + return Err( + "Level is not initialized! Please open the level, place some objects, then save and quit to initialize the level." + .to_string() + ); + } + } else if k2_detected { + if let Some(level_name) = level_name.clone() { + if text == level_name { + level_detected = true + } + } else { + level_detected = true + } + k2_detected = false + } + if level_detected && text == "k4" { k4_detected = true } else if k4_detected { level_string = text; break; } } + Ok(Event::Eof) => break, // exits the loop when reaching end of file Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), _ => (), // There are several other `Event`s we do not consider here @@ -716,6 +761,21 @@ pub fn get_level_string(ls: String) -> String { // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low buf.clear(); } + buf.clear(); + if level_detected && !k4_detected { + return Err( + "Level is not initialized! Please open the level, place some objects, then save and quit to initialize the level." + .to_string() + ); + } else if !k4_detected { + if let Some(level_name) = level_name { + return Err(format!("Level named \"{}\" was not found!", level_name)); + } else { + return Err( + "No level found! Please create a level for SPWN to operate on!".to_string(), + ); + } + } /*let mut k4_detected = false; for token in xmlparser::Tokenizer::from(String::from_utf8(buf).unwrap().as_str()) { @@ -745,7 +805,7 @@ pub fn get_level_string(ls: String) -> String { let mut ls_buf = Vec::new(); ls_decoder.read_to_end(&mut ls_buf).unwrap(); - String::from_utf8(ls_buf).unwrap() + Ok(String::from_utf8(ls_buf).unwrap()) } use quick_xml::Writer; @@ -753,31 +813,16 @@ use std::fs; use std::io::Cursor; use std::path::PathBuf; -pub fn encrypt_level_string(ls: String, old_ls: String, path: PathBuf) { - let file_content = fs::read_to_string(path.clone()).unwrap(); +pub fn encrypt_level_string(ls: String, old_ls: String, path: PathBuf, level_name: Option) { + let mut file = fs::File::open(path.clone()).unwrap(); + let mut file_content = Vec::new(); + file.read_to_end(&mut file_content).unwrap(); //decrypting the savefile - let xor_encrypted = xor(file_content.as_bytes().to_vec(), 11); - let replaced = String::from_utf8(xor_encrypted) - .unwrap() - .replace("-", "+") - .replace("_", "/") - .replace("\0", ""); - let b64 = base64::decode(replaced.as_str()).unwrap(); - let decoder = gzip::Decoder::new(&b64[..]).unwrap(); - - //encrypt the ls - //encrypting level string - /*def encrypt(dls): - fin = gzip.compress(dls) - fin = base64.b64encode(fin) - fin = fin.decode("utf-8").replace('+', '-').replace('/', '_') - fin = 'H4sIAAAAAAAAC' + fin[13:] - return(fin)*/ - - //setting level string - - let mut reader = Reader::from_reader(BufReader::new(decoder)); + let content = decrypt_savefile(file_content); + let string_content = String::from_utf8_lossy(&content); + + let mut reader = Reader::from_str(&string_content); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); @@ -787,6 +832,7 @@ pub fn encrypt_level_string(ls: String, old_ls: String, path: PathBuf) { let mut k4_detected = false; let mut done = false; let mut k2_detected = false; + let mut level_detected = false; //println!("{}", old_ls); @@ -797,7 +843,7 @@ pub fn encrypt_level_string(ls: String, old_ls: String, path: PathBuf) { // unescape and decode the text event using the reader encoding Ok(Event::Text(e)) => { let text = e.unescape_and_decode(&reader).unwrap(); - if k4_detected { + if k4_detected && level_detected { let encrypted_ls: String = { let mut ls_encoder = gzip::Encoder::new(Vec::new()).unwrap(); ls_encoder.write_all(&full_ls.as_bytes()).unwrap(); @@ -812,12 +858,23 @@ pub fn encrypt_level_string(ls: String, old_ls: String, path: PathBuf) { .is_ok()); done = true; k4_detected = false; + } else if k4_detected { + k4_detected = false; } else { assert!(writer.write_event(Event::Text(e)).is_ok()) } if k2_detected { - println!("Writing to level: {}", text); + if let Some(level_name) = &level_name { + if level_name == &text { + level_detected = true; + println!("Writing to level: {}", text); + } + } else { + level_detected = true; + println!("Writing to level: {}", text); + } + k2_detected = false; } @@ -837,32 +894,53 @@ pub fn encrypt_level_string(ls: String, old_ls: String, path: PathBuf) { // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low buf.clear(); } - let bytes = writer.into_inner().into_inner(); + let mut bytes = writer.into_inner().into_inner(); //encrypt level save use std::io::Write; - let mut encoder = zlib::Encoder::new(Vec::new()).unwrap(); - encoder.write_all(&bytes).unwrap(); - let compressed = encoder.finish().into_result().unwrap(); - use crc32fast::Hasher; + if cfg!(target_os = "macos") { + use aes::Aes256; - let mut hasher = Hasher::new(); - hasher.update(&bytes); - let checksum = hasher.finalize(); + use block_modes::block_padding::Pkcs7; + use block_modes::{BlockMode, Ecb}; - let data_size = bytes.len() as u32; + const IOS_KEY: &[u8] = &[ + 0x69, 0x70, 0x75, 0x39, 0x54, 0x55, 0x76, 0x35, 0x34, 0x79, 0x76, 0x5D, 0x69, 0x73, + 0x46, 0x4D, 0x68, 0x35, 0x40, 0x3B, 0x74, 0x2E, 0x35, 0x77, 0x33, 0x34, 0x45, 0x32, + 0x52, 0x79, 0x40, 0x7B, + ]; - let mut with_signature = b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x0b".to_vec(); - with_signature.extend(&compressed[2..compressed.len() - 4]); - with_signature.extend(checksum.to_le_bytes().to_vec()); - with_signature.extend(data_size.to_le_bytes().to_vec()); + type AesEcb = Ecb; - let encoded = base64::encode(&with_signature) - .replace("+", "-") - .replace("/", "_") - .as_bytes() - .to_vec(); + // re-create cipher mode instance + let cipher = AesEcb::new_var(&IOS_KEY, &[]).unwrap(); - let fin = xor(encoded, 11); - assert!(fs::write(path, fin).is_ok()); + let fin = cipher.encrypt_vec(&mut bytes); + assert!(fs::write(path, fin).is_ok()); + } else { + let mut encoder = zlib::Encoder::new(Vec::new()).unwrap(); + encoder.write_all(&bytes).unwrap(); + let compressed = encoder.finish().into_result().unwrap(); + use crc32fast::Hasher; + + let mut hasher = Hasher::new(); + hasher.update(&bytes); + let checksum = hasher.finalize(); + + let data_size = bytes.len() as u32; + + let mut with_signature = b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x0b".to_vec(); + with_signature.extend(&compressed[2..compressed.len() - 4]); + with_signature.extend(checksum.to_le_bytes().to_vec()); + with_signature.extend(data_size.to_le_bytes().to_vec()); + + let encoded = base64::encode(&with_signature) + .replace("+", "-") + .replace("/", "_") + .as_bytes() + .to_vec(); + + let fin = xor(encoded, 11); + assert!(fs::write(path, fin).is_ok()); + } } diff --git a/spwn-lang/src/main.rs b/spwn-lang/src/main.rs index 1ed43008..46e9d1bd 100644 --- a/spwn-lang/src/main.rs +++ b/spwn-lang/src/main.rs @@ -37,6 +37,20 @@ fn print_with_color(text: &str, color: Color) { .set_color(ColorSpec::new().set_fg(Some(color))) .unwrap(); writeln!(&mut stdout, "{}", text).unwrap(); + stdout + .set_color(ColorSpec::new().set_fg(Some(Color::White))) + .unwrap(); +} + +fn eprint_with_color(text: &str, color: Color) { + let mut stdout = StandardStream::stderr(ColorChoice::Always); + stdout + .set_color(ColorSpec::new().set_fg(Some(color))) + .unwrap(); + writeln!(&mut stdout, "{}", text).unwrap(); + stdout + .set_color(ColorSpec::new().set_fg(Some(Color::White))) + .unwrap(); } fn main() -> Result<(), Box> { @@ -59,13 +73,15 @@ fn main() -> Result<(), Box> { let mut gd_enabled = true; let mut opti_enambled = true; - - for arg in args_iter { - if arg == "--no-gd" { - gd_enabled = false; - } else if arg == "--no-optimize" { - opti_enambled = false; - } + let mut level_name = None; + + while let Some(arg) = args_iter.next() { + match arg.as_ref() { + "--no-gd" => gd_enabled = false, + "--no-optimize" => opti_enambled = false, + "--level-name" => level_name = args_iter.next().cloned(), + _ => (), + }; } print_with_color("Parsing ...", Color::Green); @@ -73,7 +89,7 @@ fn main() -> Result<(), Box> { let (statements, notes) = match parse_spwn(unparsed, script_path.clone()) { Err(err) => { - eprintln!("{}\n", err); + eprint_with_color(&format!("{}\n", err), Color::Red); std::process::exit(ERROR_EXIT_CODE); } Ok(p) => p, @@ -104,7 +120,7 @@ fn main() -> Result<(), Box> { notes, ) { Err(err) => { - eprintln!("{}\n", err); + eprint_with_color(&format!("{}\n", err), Color::White); std::process::exit(ERROR_EXIT_CODE); } Ok(p) => p, @@ -112,9 +128,24 @@ fn main() -> Result<(), Box> { let level_string = if let Some(gd_path) = &gd_path { print_with_color("Reading savefile...", Color::Cyan); - - let file_content = fs::read_to_string(gd_path)?; - let mut level_string = levelstring::get_level_string(file_content); + let mut file = fs::File::open(gd_path)?; + let mut file_content = Vec::new(); + use std::io::Read; + file.read_to_end(&mut file_content) + .expect("Problem reading savefile"); + let mut level_string = + match levelstring::get_level_string(file_content, level_name.clone()) { + Ok(s) => s, + Err(e) => { + eprint_with_color( + &format!("Error reading level:\n{}", e), + Color::Red, + ); + + std::process::exit(ERROR_EXIT_CODE); + } + }; + if level_string.is_empty() {} levelstring::remove_spwn_objects(&mut level_string); level_string } else { @@ -152,7 +183,12 @@ fn main() -> Result<(), Box> { match gd_path { Some(gd_path) => { print_with_color("\nWriting back to savefile...", Color::Cyan); - levelstring::encrypt_level_string(new_ls, level_string, gd_path); + levelstring::encrypt_level_string( + new_ls, + level_string, + gd_path, + level_name, + ); print_with_color( "Written to save. You can now open Geometry Dash again!", @@ -183,7 +219,7 @@ fn main() -> Result<(), Box> { let documentation = match documentation::document_lib(lib_path) { Ok(doc) => doc, Err(e) => { - eprintln!("{}\n", e); + eprint_with_color(&format!("{}\n", e), Color::Red); std::process::exit(ERROR_EXIT_CODE); } }; @@ -227,7 +263,8 @@ fn main() -> Result<(), Box> { Ok(()) }*/ a => { - eprintln!("Unknown subcommand: {}", a); + eprint_with_color(&format!("Unknown subcommand: {}", a), Color::Red); + std::process::exit(ERROR_EXIT_CODE); } } diff --git a/spwn-lang/src/optimize.rs b/spwn-lang/src/optimize.rs index 30c7e7ab..b6c6d8f1 100644 --- a/spwn-lang/src/optimize.rs +++ b/spwn-lang/src/optimize.rs @@ -257,7 +257,7 @@ fn get_targets<'a>( let trigger = network.get(&start.0).unwrap().triggers[start.1]; let start_obj = &objects[trigger.obj].0.params; - println!("{}", network[&start.0].connections_in); + //println!("{}", network[&start.0].connections_in); let list: Vec<(usize, Group)>; diff --git a/spwn-lang/src/parser.rs b/spwn-lang/src/parser.rs index f08f4926..b96ff683 100644 --- a/spwn-lang/src/parser.rs +++ b/spwn-lang/src/parser.rs @@ -1827,27 +1827,38 @@ fn parse_variable( ast::ValueBody::Array(arr) } - Some(Token::Import) => ast::ValueBody::Import(match tokens.next(false, false) { - Some(Token::StringLiteral) => { - ImportType::Script(PathBuf::from(str_content(tokens.slice(), tokens, notes)?)) + Some(Token::Import) => { + let mut first = tokens.next(false, false); + let mut forced = false; + if first == Some(Token::Exclamation) { + forced = true; + first = tokens.next(false, false); } - Some(Token::Symbol) => ImportType::Lib(tokens.slice()), - a => { - return Err(SyntaxError::ExpectedErr { - expected: "literal string".to_string(), - found: format!( - "{}: \"{}\"", - match a { - Some(t) => t.typ(), - None => "EOF", - }, - tokens.slice() - ), - pos: tokens.position(), - file: notes.file.clone(), - }) + match first { + Some(Token::StringLiteral) => ast::ValueBody::Import( + ImportType::Script(PathBuf::from(str_content(tokens.slice(), tokens, notes)?)), + forced, + ), + Some(Token::Symbol) => { + ast::ValueBody::Import(ImportType::Lib(tokens.slice()), forced) + } + a => { + return Err(SyntaxError::ExpectedErr { + expected: "literal string".to_string(), + found: format!( + "{}: \"{}\"", + match a { + Some(t) => t.typ(), + None => "EOF", + }, + tokens.slice() + ), + pos: tokens.position(), + file: notes.file.clone(), + }) + } } - }), + } Some(Token::At) => { let type_name = match tokens.next(false, false) { diff --git a/spwn-lang/test/test.spwn b/spwn-lang/test/test.spwn index 9882933e..fd5636b1 100644 --- a/spwn-lang/test/test.spwn +++ b/spwn-lang/test/test.spwn @@ -1,3 +1,83 @@ -c = counter() +extract obj_props; -c += 5 +type @binaryCalculator; + +impl @binaryCalculator { + + new: (bits: @number, x: @number, y: @number) { + let bitCounters = []; + + for i in ..bits { + item = ?i; + + $.add(obj { + OBJ_ID: 1615, + X: x + i * 30, + Y: y, + ITEM: item, + GROUPS: 999g + }); + + bitCounters.push(counter(item)); + } + + return { + bitCounters: bitCounters, + type: @binaryCalculator + } + }, + binaryToDecimal: (self, target: @counter) { + target = 0; + wait(1); + + for i in ..self.bitCounters.length { + if self.bitCounters[self.bitCounters.length - 1 - i] as @bool { + target += 2 ^ i; + } + } + }, + decimalToBinary: (self, source: @counter, calcSpeed: @number = 5) { + let tempcounters = []; + + for i in ..self.bitCounters.length { + tempcounters.push(counter()); + } + + source.copy_to(tempcounters, speed = 10); + + for i in ..self.bitCounters.length { + -> () { + tempcounters[i].divide(2 ^ i, speed = calcSpeed); + tempcounters[i].divide(2, remainder = self.bitCounters[self.bitCounters.length - 1 - i], speed = calcSpeed); + }(); + } + }, + prettyDecimalToBinary: (self, source: @counter, calcSpeed: @number = 10) { + for i in ..self.bitCounters.length { + source.divide(2, remainder = self.bitCounters[self.bitCounters.length - 1 - i], speed = calcSpeed); + } + + self.binaryToDecimal(source); + } +} + + + +//extract obj_props; +//import "binaryCalculator.spwn"; + +decimal = ?i; +decimalCounter = counter(decimal); +calculator = @binaryCalculator::new(8, 300, 300); + +$.add(obj { + OBJ_ID: 1615, + X: 300, + Y: 330, + ITEM: decimal, + GROUPS: 999g +}); + +decimalCounter = 3576; +wait(1); +calculator.decimalToBinary(decimalCounter, calcSpeed = 8); \ No newline at end of file