kerbalobjects/ko/
mod.rs

1//! # Kerbal Object files
2//!
3//! The module for describing, reading, and writing Kerbal Object files
4//!
5//! There **must** be at least the null section, and the .shstrtab, which are automatically
6//! added for you using KOFile::new().
7//!
8//! Each section added **must** have an entry in the section header table, or you will get an error
9//! attempting to validate the file. Also all section header table entries must correspond to a section.
10//! The series of `new_<section name>` functions create a section header automatically for you, then you
11//! later add the section to the KOFile using `.add_<section name>`.
12//!
13//! ```
14//! use kerbalobjects::ko::symbols::{KOSymbol, SymBind, SymType};
15//! use kerbalobjects::ko::{Instr, KOFile};
16//! use kerbalobjects::{KOSValue, Opcode};
17//! use kerbalobjects::ko::SectionIdx;
18//! use kerbalobjects::ko::sections::DataIdx;
19//! use std::io::Write;
20//! use std::path::PathBuf;
21//!
22//! let mut ko = KOFile::new();
23//!
24//! let mut data_section = ko.new_data_section(".data");
25//! let mut start = ko.new_func_section("_start");
26//! let mut symtab = ko.new_symtab(".symtab");
27//! let mut symstrtab = ko.new_strtab(".symstrtab");
28//!
29//! // Set up the main code function section
30//! let one = data_section.add_checked(KOSValue::Int16(1));
31//!
32//! start.add(Instr::TwoOp(
33//!     Opcode::Bscp,
34//!     one,
35//!     data_section.add_checked(KOSValue::Int16(0)),
36//! ));
37//! start.add(Instr::ZeroOp(Opcode::Argb));
38//! start.add(Instr::OneOp(
39//!     Opcode::Push,
40//!     data_section.add_checked(KOSValue::ArgMarker),
41//! ));
42//! start.add(Instr::OneOp(
43//!     Opcode::Push,
44//!     data_section.add_checked(KOSValue::StringValue("Hello, world!".into())),
45//! ));
46//! start.add(Instr::TwoOp(
47//!     Opcode::Call,
48//!     data_section.add_checked(KOSValue::String("".into())),
49//!     data_section.add_checked(KOSValue::String("print()".into())),
50//! ));
51//! start.add(Instr::ZeroOp(Opcode::Pop));
52//! start.add(Instr::OneOp(Opcode::Escp, one));
53//!
54//! // Set up our symbols
55//! let file_symbol = KOSymbol::new(
56//!     symstrtab.add("test.kasm"),
57//!     DataIdx::PLACEHOLDER,
58//!     0,
59//!     SymBind::Global,
60//!     SymType::File,
61//!     SectionIdx::NULL,
62//! );
63//! let start_symbol = KOSymbol::new(
64//!     symstrtab.add("_start"),
65//!     DataIdx::PLACEHOLDER,
66//!     start.size() as u16,
67//!     SymBind::Global,
68//!     SymType::Func,
69//!     start.section_index(),
70//! );
71//!
72//! symtab.add(file_symbol);
73//! symtab.add(start_symbol);
74//!
75//! ko.add_data_section(data_section);
76//! ko.add_func_section(start);
77//! ko.add_str_tab(symstrtab);
78//! ko.add_sym_tab(symtab);
79//!
80//! // Write the file out to disk
81//! let mut file_buffer = Vec::with_capacity(2048);
82//!
83//! let ko = ko.validate().expect("Could not update KO headers properly");
84//! ko.write(&mut file_buffer);
85//!
86//! let file_path = PathBuf::from("test.ko");
87//! let mut file =
88//!     std::fs::File::create(file_path).expect("Output file could not be created: test.ko");
89//!
90//! file.write_all(file_buffer.as_slice())
91//!     .expect("File test.ko could not be written to.");
92//! ```
93//!
94
95use std::slice::Iter;
96
97use crate::{BufferIterator, FromBytes, ToBytes, WritableBuffer};
98
99use self::sections::{DataSection, FuncSection, SectionHeader, StringTable, SymbolTable};
100use self::sections::{ReldSection, SectionKind};
101
102pub mod errors;
103pub mod instructions;
104pub mod sections;
105pub mod symbols;
106
107use crate::ko::errors::{HeaderParseError, KOParseError, ValidationError};
108use crate::ko::sections::StringIdx;
109pub use instructions::Instr;
110
111const FILE_VERSION: u8 = 4;
112/// `k` 1 `o` `f`
113const MAGIC_NUMBER: u32 = 0x666f016b;
114
115/// A wrapper type that represents an index into a section header table of a KO file.
116///
117/// This type implements From<u16> and u16 implements From<SectionIdx>, but this is provided
118/// so that it takes 1 extra step to convert raw integers into SectionIdx which could stop potential
119/// logical bugs.
120///
121#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
122pub struct SectionIdx(u16);
123
124impl From<u8> for SectionIdx {
125    fn from(i: u8) -> Self {
126        Self(i as u16)
127    }
128}
129
130impl From<u16> for SectionIdx {
131    fn from(i: u16) -> Self {
132        Self(i)
133    }
134}
135
136impl From<SectionIdx> for u16 {
137    fn from(idx: SectionIdx) -> Self {
138        idx.0
139    }
140}
141
142impl From<SectionIdx> for usize {
143    fn from(idx: SectionIdx) -> Self {
144        idx.0 as usize
145    }
146}
147
148impl SectionIdx {
149    /// A constant for the null section header index, which is at the section header index of 0
150    pub const NULL: SectionIdx = SectionIdx(0u16);
151}
152
153/// An in-memory representation of a KO file
154///
155/// Can be modified, written, or read.
156#[derive(Debug)]
157pub struct KOFile {
158    header: KOHeader,
159    shstrtab: StringTable,
160    section_headers: Vec<SectionHeader>,
161    str_tabs: Vec<StringTable>,
162    sym_tabs: Vec<SymbolTable>,
163    data_sections: Vec<DataSection>,
164    func_sections: Vec<FuncSection>,
165    reld_sections: Vec<ReldSection>,
166}
167
168impl KOFile {
169    /// Creates a new, empty, Kerbal Object file.
170    ///
171    /// Creates a new section header table, initialized with a first element that is a Null section,
172    /// and a string table that is the section header string table.
173    ///
174    pub fn new() -> Self {
175        // When we create this file, the section header string table will just always be the first section
176        let shstrtab_idx = SectionIdx::from(1u16);
177
178        let mut kofile = Self {
179            header: KOHeader::new(2, shstrtab_idx),
180            shstrtab: StringTable::with_capacity(256, shstrtab_idx),
181            section_headers: Vec::with_capacity(4),
182            str_tabs: Vec::with_capacity(2),
183            sym_tabs: Vec::with_capacity(1),
184            data_sections: Vec::with_capacity(1),
185            func_sections: Vec::with_capacity(1),
186            reld_sections: Vec::with_capacity(1),
187        };
188
189        // Add the null entry
190        kofile.add_section_header(SectionHeader::null());
191
192        let name_idx = kofile.shstrtab.add(".shstrtab");
193
194        // The first section will always be the section header string table
195        kofile.add_section_header(SectionHeader::new(name_idx, SectionKind::StrTab));
196
197        kofile
198    }
199
200    /// Adds a new string to the section header string table
201    pub fn add_section_name(&mut self, name: impl Into<String>) -> StringIdx {
202        self.shstrtab.add(name)
203    }
204
205    /// Gets a section name from the section header string table at the specified index, or
206    /// returns None if none exists at that index
207    pub fn get_section_name(&self, index: StringIdx) -> Option<&String> {
208        self.shstrtab.get(index)
209    }
210
211    /// Returns the name of the section referred to by the index into the
212    /// Kerbal Object file's section header table, or None if a section header
213    /// at that index doesn't exist
214    pub fn get_section_name_by_index(&self, index: SectionIdx) -> Option<&String> {
215        let header = self.section_headers.get(usize::from(index))?;
216        self.shstrtab.get(header.name_idx)
217    }
218
219    /// Adds a new section header to the section header table, and returns the index of the
220    /// section header
221    ///
222    /// This will panic if you insert more than u16::MAX section headers. Don't do that.
223    ///
224    pub fn add_section_header(&mut self, header: SectionHeader) -> SectionIdx {
225        let index = self.section_headers.len();
226
227        if index > u16::MAX as usize {
228            panic!(
229                "Attempted to insert {} section headers. Section indices have a maximum of {}",
230                u16::MAX as usize + 1,
231                u16::MAX
232            );
233        }
234
235        self.section_headers.push(header);
236        SectionIdx::from(index as u16)
237    }
238
239    /// Gets the section header at the provided index into the section header table,
240    /// or None if none exists at that index
241    pub fn get_section_header(&self, index: SectionIdx) -> Option<&SectionHeader> {
242        self.section_headers.get(usize::from(index))
243    }
244
245    /// Gets the name of the section referred to by the provided section header,
246    /// or None if no section with that header is found in this KO file
247    pub fn get_header_name(&self, header: &SectionHeader) -> Option<&String> {
248        self.shstrtab.get(header.name_idx)
249    }
250
251    /// Adds a new string table to this Kerbal Object file
252    pub fn add_str_tab(&mut self, str_tab: StringTable) {
253        self.str_tabs.push(str_tab);
254    }
255
256    /// Adds a new symbol table to this Kerbal Object file
257    pub fn add_sym_tab(&mut self, sym_tab: SymbolTable) {
258        self.sym_tabs.push(sym_tab);
259    }
260
261    /// Adds a new data section to this Kerbal Object file
262    pub fn add_data_section(&mut self, data_section: DataSection) {
263        self.data_sections.push(data_section);
264    }
265
266    /// Adds a new function section to this Kerbal Object file
267    pub fn add_func_section(&mut self, func_section: FuncSection) {
268        self.func_sections.push(func_section);
269    }
270
271    /// Adds a new relocation data section to this Kerbal Object file
272    pub fn add_reld_section(&mut self, reld_section: ReldSection) {
273        self.reld_sections.push(reld_section);
274    }
275
276    /// Returns an iterator over all of the string tables in this Kerbal Object file
277    pub fn str_tabs(&self) -> Iter<StringTable> {
278        self.str_tabs.iter()
279    }
280
281    /// Returns an iterator over all of the symbol tables in this Kerbal Object file
282    pub fn sym_tabs(&self) -> Iter<SymbolTable> {
283        self.sym_tabs.iter()
284    }
285
286    /// Returns an iterator over all of the data sections in this Kerbal Object file
287    pub fn data_sections(&self) -> Iter<DataSection> {
288        self.data_sections.iter()
289    }
290
291    /// Returns an iterator over all of the function sections in this Kerbal Object file
292    pub fn func_sections(&self) -> Iter<FuncSection> {
293        self.func_sections.iter()
294    }
295
296    /// Returns an iterator over all of the relocation data sections in this Kerbal Object file
297    pub fn reld_sections(&self) -> Iter<ReldSection> {
298        self.reld_sections.iter()
299    }
300
301    /// Adds a new section header of the provided name and section kind to this Kerbal
302    /// Object file, and returns the index into the section header table of this new header
303    pub fn new_section_header(&mut self, name: impl Into<String>, kind: SectionKind) -> SectionIdx {
304        let name_idx = self.add_section_name(name);
305        let header = SectionHeader::new(name_idx, kind);
306        self.add_section_header(header)
307    }
308
309    /// Returns the index into the section header table of the section with the provided name,
310    /// or None if there is no such section
311    pub fn get_section_index_by_name(&self, name: impl AsRef<str>) -> Option<SectionIdx> {
312        for (index, header) in self.section_headers.iter().enumerate() {
313            let name_idx = header.name_idx;
314            let sh_name = match self.shstrtab.get(name_idx) {
315                Some(name) => name,
316                None => {
317                    continue;
318                }
319            };
320
321            if name.as_ref() == sh_name {
322                return Some(SectionIdx::from(index as u16));
323            }
324        }
325
326        None
327    }
328
329    /// The Kerbal Object file header
330    pub fn header(&self) -> KOHeader {
331        self.header
332    }
333
334    /// Returns an iterator over all section headers in the Kerbal Object file's section
335    /// header table
336    pub fn section_headers(&self) -> Iter<SectionHeader> {
337        self.section_headers.iter()
338    }
339
340    /// Returns the number of sections registered in the section header table. This will always
341    /// be at least 1, because of the Null section present in all valid section header tables.
342    pub fn section_header_count(&self) -> usize {
343        self.section_headers.len()
344    }
345
346    /// The index into the section header table of the section header string table
347    pub fn shstrtab_index(&self) -> SectionIdx {
348        self.shstrtab.section_index()
349    }
350
351    // Updates a single section header with the section's actual reported size
352    // This can fail if the section header kind doesn't match with the section's kind,
353    // or if the section's header index doesn't exist
354    fn update_section_header(
355        &mut self,
356        section_kind: SectionKind,
357        section_index: SectionIdx,
358        section_size: u32,
359    ) -> Result<(), ValidationError> {
360        match self
361            .section_headers
362            .get_mut(u16::from(section_index) as usize)
363        {
364            Some(header) => {
365                if header.section_kind != section_kind {
366                    Err(ValidationError::SectionKindMismatchError(
367                        section_kind,
368                        u16::from(section_index),
369                        header.section_kind,
370                    ))
371                } else {
372                    header.size = section_size;
373                    Ok(())
374                }
375            }
376            None => Err(ValidationError::InvalidSectionIndexError(
377                section_kind,
378                u16::from(section_index),
379            )),
380        }
381    }
382
383    // Updates a all section headers with the section's actual reported size
384    // This can fail if a section header kind doesn't match with a section's kind,
385    // or if a section's header index doesn't exist
386    fn update_section_headers(&mut self) -> Result<(), ValidationError> {
387        // We'll keep track of which section header entries were actually updated. All of them
388        // should be updated except for the first null section.
389        let mut header_set = vec![self.shstrtab.section_index()];
390
391        // Update the .shstrtab
392        self.section_headers
393            .get_mut(usize::from(self.shstrtab.section_index()))
394            .unwrap()
395            .size = self.shstrtab.size();
396
397        for i in 0..self.str_tabs.len() {
398            let section = self.str_tabs.get(i).unwrap();
399            let idx = section.section_index();
400            let size = section.size();
401            header_set.push(idx);
402
403            self.update_section_header(SectionKind::StrTab, idx, size)?;
404        }
405
406        for i in 0..self.sym_tabs.len() {
407            let section = self.sym_tabs.get(i).unwrap();
408            let idx = section.section_index();
409            let size = section.size();
410            header_set.push(idx);
411
412            self.update_section_header(SectionKind::SymTab, idx, size)?;
413        }
414
415        for i in 0..self.data_sections.len() {
416            let section = self.data_sections.get(i).unwrap();
417            let idx = section.section_index();
418            let size = section.size();
419
420            header_set.push(idx);
421
422            self.update_section_header(SectionKind::Data, idx, size)?;
423        }
424
425        for i in 0..self.func_sections.len() {
426            let section = self.func_sections.get(i).unwrap();
427            let idx = section.section_index();
428            let size = section.size();
429            header_set.push(idx);
430
431            self.update_section_header(SectionKind::Func, idx, size)?;
432        }
433
434        for i in 0..self.reld_sections.len() {
435            let section = self.reld_sections.get(i).unwrap();
436            let idx = section.section_index();
437            let size = section.size();
438            header_set.push(idx);
439
440            self.update_section_header(SectionKind::Reld, idx, size)?;
441        }
442
443        // Skip the first null section
444        for (i, header) in self
445            .section_headers
446            .iter()
447            .enumerate()
448            .skip(1)
449            .map(|(i, h)| (SectionIdx::from(i as u16), h))
450        {
451            let name = self
452                .shstrtab
453                .get(header.name_idx)
454                .ok_or_else(|| {
455                    ValidationError::InvalidSectionHeaderNameIndexError(
456                        u16::from(i),
457                        header.section_kind,
458                        usize::from(header.name_idx),
459                    )
460                })?
461                .clone();
462
463            if !header_set.contains(&i) {
464                return Err(ValidationError::InvalidSectionHeaderError(
465                    u16::from(i),
466                    header.section_kind,
467                    name,
468                ));
469            }
470        }
471
472        Ok(())
473    }
474
475    /// Consumes and verifies that a Kerbal Object file's section header data is sound so that it
476    /// can be written out as a proper KerbalObject file
477    ///
478    /// This can fail if a section header kind doesn't match with a section's kind,
479    /// or if a section's header index doesn't exist
480    ///
481    /// A WritableKOFile can be turned back into a KOFile
482    ///
483    pub fn validate(mut self) -> Result<WritableKOFile, (Self, ValidationError)> {
484        self.header = KOHeader::new(
485            self.section_headers.len() as u16,
486            self.shstrtab.section_index(),
487        );
488
489        if let Err(e) = self.update_section_headers() {
490            Err((self, e))
491        } else {
492            Ok(WritableKOFile(self))
493        }
494    }
495
496    /// Parses an entire KOFile from a byte buffer
497    pub fn parse(source: &mut BufferIterator) -> Result<Self, KOParseError> {
498        let header = KOHeader::parse(source).map_err(KOParseError::HeaderError)?;
499        let mut section_headers = Vec::with_capacity(header.num_headers as usize);
500        let mut str_tabs;
501        let mut sym_tabs;
502        let mut data_sections;
503        let mut func_sections;
504        let mut reld_sections;
505        let mut num_str_tabs = 0;
506        let mut num_sym_tabs = 0;
507        let mut num_data_sections = 0;
508        let mut num_func_sections = 0;
509        let mut num_reld_sections = 0;
510
511        let null_header =
512            SectionHeader::parse(source).map_err(KOParseError::SectionHeaderParseError)?;
513
514        // The first section header table entry must be a null section
515        if null_header.section_kind != SectionKind::Null {
516            return Err(KOParseError::MissingNullSectionHeader(
517                null_header.section_kind,
518            ));
519        }
520        section_headers.push(null_header);
521
522        for i in 1..header.num_headers {
523            let header =
524                SectionHeader::parse(source).map_err(KOParseError::SectionHeaderParseError)?;
525
526            match header.section_kind {
527                SectionKind::StrTab => {
528                    num_str_tabs += 1;
529                }
530                SectionKind::SymTab => {
531                    num_sym_tabs += 1;
532                }
533                SectionKind::Data => {
534                    num_data_sections += 1;
535                }
536                SectionKind::Func => {
537                    num_func_sections += 1;
538                }
539                SectionKind::Reld => {
540                    num_reld_sections += 1;
541                }
542                SectionKind::Debug => {
543                    return Err(KOParseError::DebugSectionUnsupportedError(i));
544                }
545                SectionKind::Null => {
546                    return Err(KOParseError::StrayNullSectionHeader(i));
547                }
548            }
549
550            section_headers.push(header);
551        }
552
553        // The null section is here, and we need a .shstrtab, so this is invalid
554        if header.shstrtab_idx == SectionIdx::from(0u16) {
555            return Err(KOParseError::NullShStrTabIndexError);
556        }
557
558        // Allocate now
559        str_tabs = Vec::with_capacity(num_str_tabs);
560        sym_tabs = Vec::with_capacity(num_sym_tabs);
561        data_sections = Vec::with_capacity(num_data_sections);
562        func_sections = Vec::with_capacity(num_func_sections);
563        reld_sections = Vec::with_capacity(num_reld_sections);
564
565        let shstrtab_size = section_headers
566            .get(usize::from(header.shstrtab_idx))
567            .ok_or_else(|| {
568                KOParseError::InvalidShStrTabIndexError(
569                    header.shstrtab_idx.into(),
570                    section_headers.len() as u16,
571                )
572            })?
573            .size;
574
575        let shstrtab = StringTable::parse(source, shstrtab_size, header.shstrtab_idx)
576            .map_err(KOParseError::StringTableParseError)?;
577
578        // We skip the first one here since, there is no 0th section
579        for section_idx in (1..section_headers.len() as u16).map(SectionIdx::from) {
580            // We've already parsed the .shstrtab
581            if section_idx == header.shstrtab_idx {
582                continue;
583            }
584
585            let header = section_headers.get(usize::from(section_idx)).unwrap();
586
587            match header.section_kind {
588                SectionKind::StrTab => {
589                    str_tabs.push(
590                        StringTable::parse(source, header.size, section_idx)
591                            .map_err(KOParseError::StringTableParseError)?,
592                    );
593                }
594                SectionKind::SymTab => {
595                    sym_tabs.push(
596                        SymbolTable::parse(source, header.size, section_idx)
597                            .map_err(KOParseError::SymbolTableParseError)?,
598                    );
599                }
600                SectionKind::Data => {
601                    data_sections.push(
602                        DataSection::parse(source, header.size, section_idx)
603                            .map_err(KOParseError::DataSectionParseError)?,
604                    );
605                }
606                SectionKind::Func => {
607                    func_sections.push(
608                        FuncSection::parse(source, header.size, section_idx)
609                            .map_err(KOParseError::FunctionSectionParseError)?,
610                    );
611                }
612                SectionKind::Reld => {
613                    reld_sections.push(
614                        ReldSection::parse(source, header.size, section_idx)
615                            .map_err(KOParseError::ReldSectionParseError)?,
616                    );
617                }
618                SectionKind::Debug => unimplemented!(),
619                SectionKind::Null => {
620                    panic!("Internal library error. Attempted to parse \"null\" section, this should be unreachable.");
621                }
622            }
623        }
624
625        Ok(Self {
626            header,
627            shstrtab,
628            section_headers,
629            str_tabs,
630            sym_tabs,
631            data_sections,
632            func_sections,
633            reld_sections,
634        })
635    }
636
637    // There are extra macro-generated functions at the end of this file that are implemented
638    // for KOFile.
639}
640
641impl Default for KOFile {
642    fn default() -> Self {
643        Self::new()
644    }
645}
646
647/// A finalized Kerbal Object file that can be written to a byte buffer, or converted
648/// into a KOFile
649#[derive(Debug)]
650pub struct WritableKOFile(KOFile);
651
652impl WritableKOFile {
653    /// Writes the binary representation of this KO file to the provided buffer
654    pub fn write(&self, buf: &mut impl WritableBuffer) {
655        let ko = &self.0;
656        // Write the file header
657        ko.header.write(buf);
658
659        // First write all of the section headers in the section header table
660        for header in &ko.section_headers {
661            header.write(buf);
662        }
663
664        // Write out all of the sections in order
665        for section_index in (0..ko.section_header_count()).map(|i| SectionIdx::from(i as u16)) {
666            if self.0.shstrtab.section_index() == section_index {
667                self.0.shstrtab.write(buf);
668                continue;
669            }
670
671            if let Some(section) = ko
672                .str_tabs
673                .iter()
674                .find(|s| s.section_index() == section_index)
675            {
676                section.write(buf);
677                continue;
678            }
679
680            if let Some(section) = ko
681                .sym_tabs
682                .iter()
683                .find(|s| s.section_index() == section_index)
684            {
685                section.write(buf);
686                continue;
687            }
688
689            if let Some(section) = ko
690                .data_sections
691                .iter()
692                .find(|s| s.section_index() == section_index)
693            {
694                section.write(buf);
695                continue;
696            }
697
698            if let Some(section) = ko
699                .func_sections
700                .iter()
701                .find(|s| s.section_index() == section_index)
702            {
703                section.write(buf);
704                continue;
705            }
706
707            if let Some(section) = ko
708                .reld_sections
709                .iter()
710                .find(|s| s.section_index() == section_index)
711            {
712                section.write(buf);
713                continue;
714            }
715        }
716    }
717
718    /// Gets the KOFile back out of this
719    pub fn get(self) -> KOFile {
720        self.0
721    }
722}
723
724impl From<WritableKOFile> for KOFile {
725    fn from(w_kofile: WritableKOFile) -> Self {
726        w_kofile.0
727    }
728}
729
730/// The header of a Kerbal Object file
731#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
732pub struct KOHeader {
733    /// The "magic numbers" that identify a KO file
734    pub magic: u32,
735    /// The specification version number of this Kerbal Object file
736    pub version: u8,
737    /// The number of headers contained within the section header table
738    pub num_headers: u16,
739    /// The index of the section header string table
740    pub shstrtab_idx: SectionIdx,
741}
742
743impl KOHeader {
744    /// The size in bytes of a KO file header
745    const HEADER_SIZE: usize = 9;
746
747    /// Creates a new Kerbal Object file header
748    pub const fn new(num_headers: u16, shstrtab_idx: SectionIdx) -> Self {
749        Self {
750            magic: MAGIC_NUMBER,
751            version: FILE_VERSION,
752            num_headers,
753            shstrtab_idx,
754        }
755    }
756
757    /// Returns the size in bytes of a Kerbal Object file header
758    pub const fn size() -> usize {
759        Self::HEADER_SIZE
760    }
761
762    /// Converts the header to its binary representation and writes it to the provided buffer
763    fn write(&self, buf: &mut impl WritableBuffer) {
764        self.magic.to_bytes(buf);
765        self.version.to_bytes(buf);
766        self.num_headers.to_bytes(buf);
767        u16::from(self.shstrtab_idx).to_bytes(buf);
768    }
769
770    /// Parses a KOHeader from the provided byte buffer
771    pub fn parse(source: &mut BufferIterator) -> Result<Self, HeaderParseError> {
772        let magic = u32::from_bytes(source).map_err(|_| HeaderParseError::MissingMagicError)?;
773        let version = u8::from_bytes(source).map_err(|_| HeaderParseError::MissingVersionError)?;
774        let num_headers =
775            u16::from_bytes(source).map_err(|_| HeaderParseError::MissingNumHeaders)?;
776        let shstrtab_idx = SectionIdx(
777            u16::from_bytes(source).map_err(|_| HeaderParseError::MissingShStrTabIndex)?,
778        );
779
780        if magic != MAGIC_NUMBER {
781            return Err(HeaderParseError::InvalidMagicError(magic, MAGIC_NUMBER));
782        }
783
784        if version != FILE_VERSION {
785            return Err(HeaderParseError::UnsupportedVersionError(
786                version,
787                FILE_VERSION,
788            ));
789        }
790
791        Ok(Self {
792            magic,
793            version,
794            num_headers,
795            shstrtab_idx,
796        })
797    }
798}
799
800// All of this down here to not clutter up the more important code
801
802macro_rules! gen_get_by_name {
803    ($(#[$ref_attr:meta])* => $func_name: ident, $(#[$mut_attr:meta])* => $mut_func_name: ident, $section_name: ident, $section_type: ty) => {
804        $(#[$ref_attr])*
805        pub fn $func_name(&self, name: impl AsRef<str>) -> Option<&$section_type> {
806            self.$section_name.iter().find(|section| {
807                match self.get_section_name_by_index(section.section_index()) {
808                    Some(s) => s == name.as_ref(),
809                    None => false,
810                }
811            })
812        }
813
814        $(#[$mut_attr])*
815        pub fn $mut_func_name(&mut self, name: impl AsRef<str>) -> Option<&mut $section_type> {
816            let mut index_opt = Some(0);
817
818            for (index, section) in self.$section_name.iter().enumerate() {
819                match self.get_section_name_by_index(section.section_index()) {
820                    Some(s) => {
821                        if s == name.as_ref() {
822                            index_opt = Some(index);
823                            break;
824                        }
825                    }
826                    None => continue,
827                }
828            }
829
830            let index = index_opt?;
831
832            self.$section_name.iter_mut().nth(index)
833        }
834    };
835}
836
837macro_rules! gen_new_section {
838    ($(#[$attr:meta])* => $func_name: ident, $section_type: ty, $section_kind: expr) => {
839        $(#[$attr])*
840        pub fn $func_name(&mut self, name: impl Into<String>) -> $section_type {
841            let sh_index = self.new_section_header(name, $section_kind);
842            <$section_type>::with_capacity(4, sh_index)
843        }
844    };
845}
846
847impl KOFile {
848    gen_new_section! {
849        /// Creates a new StringTable, and adds a new entry for it in the section
850        /// header table with the provided name.
851        ///
852        /// Returns the new StringTable
853        =>
854        new_strtab,
855        StringTable,
856        SectionKind::StrTab
857    }
858
859    gen_new_section! {
860        /// Creates a new SymbolTable, and adds a new entry for it in the section
861        /// header table with the provided name.
862        ///
863        /// Returns the new SymbolTable
864        =>
865        new_symtab,
866        SymbolTable,
867        SectionKind::SymTab
868    }
869
870    gen_new_section! {
871        /// Creates a new DataSection, and adds a new entry for it in the section
872        /// header table with the provided name.
873        ///
874        /// Returns the new DataSection
875        =>
876        new_data_section,
877        DataSection,
878        SectionKind::Data
879    }
880
881    gen_new_section! {
882        /// Creates a new FuncSection, and adds a new entry for it in the section
883        /// header table with the provided name.
884        ///
885        /// Returns the new FuncSection
886        =>
887        new_func_section,
888        FuncSection,
889        SectionKind::Func
890    }
891
892    gen_new_section! {
893        /// Creates a new ReldSection, and adds a new entry for it in the section
894        /// header table with the provided name.
895        ///
896        /// Returns the new ReldSection
897        =>
898        new_reld_section,
899        ReldSection,
900        SectionKind::Reld
901    }
902
903    gen_get_by_name! {
904    /// Gets a reference to the StringTable with the provided name,
905    /// or None if a StringTable by that name doesn't exist
906    =>
907    str_tab_by_name,
908    /// Gets a mutable reference to the StringTable with the provided name,
909    /// or None if a StringTable by that name doesn't exist
910    =>
911    str_tab_by_name_mut, str_tabs, StringTable}
912
913    gen_get_by_name! {
914        /// Gets a reference to the SymbolTable with the provided name,
915        /// or None if a SymbolTable by that name doesn't exist
916        =>
917        sym_tab_by_name,
918        /// Gets a mutable reference to the SymbolTable with the provided name,
919        /// or None if a SymbolTable by that name doesn't exist
920        =>
921        sym_tab_by_name_mut,
922        sym_tabs,
923        SymbolTable
924    }
925
926    gen_get_by_name! {
927        /// Gets a reference to the DataSection with the provided name,
928        /// or None if a DataSection by that name doesn't exist
929        =>
930        data_section_by_name,
931        /// Gets a mutable reference to the DataSection with the provided name,
932        /// or None if a DataSection by that name doesn't exist
933        =>
934        data_section_by_name_mut,
935        data_sections,
936        DataSection
937    }
938
939    gen_get_by_name! {
940        /// Gets a reference to the FuncSection with the provided name,
941        /// or None if a FuncSection by that name doesn't exist
942        =>
943        func_section_by_name,
944        /// Gets a mutable reference to the FuncSection with the provided name,
945        /// or None if a FuncSection by that name doesn't exist
946        =>
947        func_section_by_name_mut,
948        func_sections,
949        FuncSection
950    }
951
952    gen_get_by_name!(
953        /// Gets a reference to the ReldSection with the provided name,
954        /// or None if a ReldSection by that name doesn't exist
955        =>
956        reld_section_by_name,
957        /// Gets a mutable reference to the ReldSection with the provided name,
958        /// or None if a ReldSection by that name doesn't exist
959        =>
960        reld_section_by_name_mut,
961        reld_sections,
962        ReldSection
963    );
964}