kerbalobjects/lib.rs
1//!
2//! # KerbalObjects
3//!
4//! The `kerbalobjects` crate provides an interface for creating object files for
5//! [Kerbal Operating System](https://ksp-kos.github.io/KOS/) (kOS), It supports reading and writing
6//! Kerbal Object files, and Kerbal Machine Code (kOS executable) files.
7//!
8//! Documentation for both file formats can be found here:
9//! * For [Kerbal Object](https://github.com/newcomb-luke/kerbalobjects.rs/blob/main/docs/KO-file-format.md) files.
10//! * For [Kerbal Machine code](https://github.com/newcomb-luke/kerbalobjects.rs/blob/main/docs/KSM-file-format.md) files.
11//!
12//! Kerbal Object files are used by the Kerbal Assembler and Kerbal Linker toolchain, and eventually are linked into
13//! Kerbal Machine Code files, which are loadable and executable by kOS.
14//!
15//! # Example for Kerbal Machine Code hello world program
16//!
17//! ```
18//! # #[cfg(feature = "ksm")] {
19//! use std::io::Write;
20//! use kerbalobjects::ksm::sections::{ArgumentSection, CodeSection, CodeType, DebugEntry, DebugRange, DebugSection};
21//! use kerbalobjects::ksm::{Instr, KSMFile};
22//! use kerbalobjects::{Opcode, KOSValue, ToBytes};
23//!
24//! let mut arg_section = ArgumentSection::new();
25//! let mut main_code = CodeSection::new(CodeType::Main);
26//!
27//! let one = arg_section.add_checked(KOSValue::Int16(1));
28//!
29//! // Corresponds to the KerbalScript code:
30//! // PRINT("Hello, world!").
31//!
32//! main_code.add(Instr::OneOp(Opcode::Push, arg_section.add_checked(KOSValue::String("@0001".into()))));
33//! main_code.add(Instr::TwoOp(Opcode::Bscp, one, arg_section.add_checked(KOSValue::Int16(0))));
34//! main_code.add(Instr::ZeroOp(Opcode::Argb));
35//! main_code.add(Instr::OneOp(Opcode::Push, arg_section.add_checked(KOSValue::ArgMarker)));
36//! main_code.add(Instr::OneOp(Opcode::Push, arg_section.add_checked(KOSValue::StringValue("Hello, world!".into()))));
37//! main_code.add(Instr::TwoOp(Opcode::Call, arg_section.add_checked(KOSValue::String("".into())), arg_section.add_checked(KOSValue::String("print()".into()))));
38//! main_code.add(Instr::ZeroOp(Opcode::Pop));
39//! main_code.add(Instr::OneOp(Opcode::Escp, one));
40//!
41//! let code_sections = vec![
42//! CodeSection::new(CodeType::Function),
43//! CodeSection::new(CodeType::Initialization),
44//! main_code
45//! ];
46//!
47//! // A completely wrong and useless debug section, but we NEED to have one
48//! let mut debug_entry = DebugEntry::new(1).with_range(DebugRange::new(0x06, 0x13));
49//!
50//! let debug_section = DebugSection::new(debug_entry);
51//!
52//! let mut file_buffer = Vec::with_capacity(2048);
53//!
54//! let ksm_file = KSMFile::new_from_parts(arg_section, code_sections, debug_section);
55//!
56//! ksm_file.write(&mut file_buffer);
57//!
58//! let mut file = std::fs::File::create("hello.ksm").expect("Couldn't open output file");
59//!
60//! file.write_all(file_buffer.as_slice()).expect("Failed to write to output file");
61//! # }
62//! ```
63//!
64//! # Example for Kerbal Object hello world program
65//!
66//! ```
67//! # #[cfg(feature = "ko")] {
68//! use kerbalobjects::ko::symbols::{KOSymbol, SymBind, SymType};
69//! use kerbalobjects::ko::{Instr, KOFile};
70//! use kerbalobjects::{KOSValue, Opcode};
71//! use kerbalobjects::ko::SectionIdx;
72//! use kerbalobjects::ko::sections::DataIdx;
73//! use std::io::Write;
74//! use std::path::PathBuf;
75//!
76//! let mut ko = KOFile::new();
77//!
78//! let mut data_section = ko.new_data_section(".data");
79//! let mut start = ko.new_func_section("_start");
80//! let mut symtab = ko.new_symtab(".symtab");
81//! let mut symstrtab = ko.new_strtab(".symstrtab");
82//!
83//! // Set up the main code function section
84//! let one = data_section.add_checked(KOSValue::Int16(1));
85//!
86//! start.add(Instr::TwoOp(
87//! Opcode::Bscp,
88//! one,
89//! data_section.add_checked(KOSValue::Int16(0)),
90//! ));
91//! start.add(Instr::ZeroOp(Opcode::Argb));
92//! start.add(Instr::OneOp(
93//! Opcode::Push,
94//! data_section.add_checked(KOSValue::ArgMarker),
95//! ));
96//! start.add(Instr::OneOp(
97//! Opcode::Push,
98//! data_section.add_checked(KOSValue::StringValue("Hello, world!".into())),
99//! ));
100//! start.add(Instr::TwoOp(
101//! Opcode::Call,
102//! data_section.add_checked(KOSValue::String("".into())),
103//! data_section.add_checked(KOSValue::String("print()".into())),
104//! ));
105//! start.add(Instr::ZeroOp(Opcode::Pop));
106//! start.add(Instr::OneOp(Opcode::Escp, one));
107//!
108//! // Set up our symbols
109//! let file_symbol = KOSymbol::new(
110//! symstrtab.add("test.kasm"),
111//! DataIdx::PLACEHOLDER,
112//! 0,
113//! SymBind::Global,
114//! SymType::File,
115//! SectionIdx::NULL,
116//! );
117//! let start_symbol = KOSymbol::new(
118//! symstrtab.add("_start"),
119//! DataIdx::PLACEHOLDER,
120//! start.size() as u16,
121//! SymBind::Global,
122//! SymType::Func,
123//! start.section_index(),
124//! );
125//!
126//! symtab.add(file_symbol);
127//! symtab.add(start_symbol);
128//!
129//! ko.add_data_section(data_section);
130//! ko.add_func_section(start);
131//! ko.add_str_tab(symstrtab);
132//! ko.add_sym_tab(symtab);
133//!
134//! // Write the file out to disk
135//! let mut file_buffer = Vec::with_capacity(2048);
136//!
137//! let ko = ko.validate().expect("Could not update KO headers properly");
138//! ko.write(&mut file_buffer);
139//!
140//! let file_path = PathBuf::from("test.ko");
141//! let mut file =
142//! std::fs::File::create(file_path).expect("Output file could not be created: test.ko");
143//!
144//! file.write_all(file_buffer.as_slice())
145//! .expect("File test.ko could not be written to.");
146//! # }
147//! ```
148//!
149
150#![deny(missing_docs)]
151#![deny(missing_debug_implementations)]
152#![allow(clippy::result_unit_err)]
153
154mod common;
155pub use common::*;
156
157pub mod errors;
158pub use errors::*;
159
160#[cfg(feature = "ko")]
161pub mod ko;
162#[cfg(feature = "ksm")]
163pub mod ksm;