#![feature(ascii_char, iter_array_chunks)] mod interpreter; mod glk; mod instructions; mod opcodes; use interpreter::Interpreter; use std::{env, fs, process::exit}; const MAJOR_VERSION : u16 = 3; const MINOR_VERSION : u8 = 1; const PATCH_VERSION : u8 = 3; fn main() { let args: Vec = env::args().collect(); if args.len() < 2 { println!("No story file specified"); exit(1); } if args.len() > 2 { println!("Too many arguments"); exit(1); } let path = &args[1]; let file_data: Vec = fs::read(path).expect("Could not open file"); let _magic_number = &file_data[0..4] .as_ascii() .map(|mn| if mn.as_str().eq("Glul") {Some(mn.as_str())} else {None}) .flatten() .unwrap_or_else(|| panic!("Invalid magic number: {} {} {} {}", file_data[0], file_data[1], file_data[2], file_data[3])); let version_major = u16::from_be_bytes(file_data[4..6].try_into().unwrap()); let version_minor = file_data[6]; let version_patch = file_data[7]; let ram_start = u32::from_be_bytes(file_data[8..12].try_into().unwrap()); let ext_start = u32::from_be_bytes(file_data[12..16].try_into().unwrap()); let end_mem = u32::from_be_bytes(file_data[16..20].try_into().unwrap()); let stack_size = u32::from_be_bytes(file_data[20..24].try_into().unwrap()); let start_func = u32::from_be_bytes(file_data[24..28].try_into().unwrap()); let str_decode = u32::from_be_bytes(file_data[28..32].try_into().unwrap()); let exp_checksum = u32::from_be_bytes(file_data[32..36].try_into().unwrap()); let actual_sum = file_data.iter() .map(|b| *b) .array_chunks::<4>() .map(|bytes| u32::from_be_bytes(bytes)) // Subtract expected as we're supposed to compute it with that blanked .fold(0u32, |a,b| a.overflowing_add(b).0) .overflowing_sub(exp_checksum).0; if file_data.len() % 4 != 0 { panic!("Invalid checksum: Uneven number of bytes"); } if exp_checksum != actual_sum { panic!("Invalid checksum: Expected {exp_checksum}, got {actual_sum}"); } if ram_start % 256 != 0 || ext_start % 256 != 0 || end_mem % 256 != 0 { panic!("Invalid memory boundaries"); } if stack_size % 256 != 0 { panic!("Invalid stack size"); } if version_major != 2 && version_major != 3 { panic!("This glulx implementation cannot handle version {version_major}, it is version {MAJOR_VERSION}"); } if version_major == MAJOR_VERSION && version_minor > MINOR_VERSION { panic!("This file is a newer minor version than this glulx implementation ({version_major}.{version_minor} > {MAJOR_VERSION}.{MINOR_VERSION})"); } println!("Loaded {path}, ({} bytes, file version {version_major}.{version_minor}.{version_patch}, glulx {MAJOR_VERSION}.{MINOR_VERSION}.{PATCH_VERSION})", file_data.len()); let mut interpreter = Interpreter::init( file_data, ram_start, ext_start, end_mem, stack_size, str_decode ); interpreter.run(start_func); }