summary refs log tree commit diff
path: root/src/main.rs
blob: 33df20357cf8e40fae55aed9c345a463b65c67a9 (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
#![feature(ascii_char, iter_array_chunks)]
mod interpreter;
mod glk;
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<String> = 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<u8> = 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) - exp_checksum;

    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);
}