psyk/
lib.rs

1// SPDX-FileCopyrightText: © 2025 TTKB, LLC
2// SPDX-License-Identifier: BSD-3-CLAUSE
3
4//! PSY-Q Library and Object File Parser
5//!
6//! This crate provides parsing and manipulation capabilities for PSY-Q LIB and OBJ files,
7//! which were used by the official PlayStation 1 development toolchain and third-party
8//! toolchains for the Sega Saturn, Sega Genesis/MegaDrive/Sega CD/Mega CD, Super Nintendo,
9//! and others.
10//!
11//! # Overview
12//!
13//! PSY-Q was the official development kit for PlayStation 1 games. It produced two main
14//! types of binary files:
15//!
16//! - **[LIB] files**: Archive files containing multiple object modules
17//! - **[OBJ] files**: Individual object files with machine code and linking information
18//!
19//! # Quick Start
20//!
21//! Reading a library file:
22//!
23//! ```no_run
24//! use std::path::Path;
25//! use psyk::io;
26//! use anyhow::Result;
27//!
28//! fn main() -> Result<()> {
29//!     let lib = io::read_lib(Path::new("LIBAPI.LIB"))?;
30//!
31//!     for module in lib.modules() {
32//!         println!("Module: {}", module.name());
33//!         println!("Created: {}", module.created());
34//!         println!("Exports: {:?}", module.exports());
35//!     }
36//!
37//!     Ok(())
38//! }
39//! ```
40//!
41//! Reading either a LIB or OBJ file:
42//!
43//! ```no_run
44//! use std::path::Path;
45//! use psyk::io;
46//! use anyhow::Result;
47//!
48//! fn main() -> Result<()> {
49//!     let lib_or_obj = io::read(Path::new("SOME.OBJ"))?;
50//!     println!("{}", lib_or_obj);
51//!     Ok(())
52//! }
53//! ```
54
55use core::cmp;
56use std::fmt;
57use std::fs;
58use std::path::Path;
59use std::time::{Duration, SystemTime, UNIX_EPOCH};
60
61use anyhow::Result;
62use binrw::binrw;
63use binrw::helpers::{until, until_eof};
64use chrono::{
65    DateTime, Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc,
66};
67use rabbitizer::{InstrCategory, Instruction};
68use unicode_segmentation::UnicodeSegmentation;
69
70use crate::display::DisplayWithOptions;
71
72pub mod cli;
73pub mod display;
74pub mod io;
75pub mod link;
76
77/// A [LIB] is an archive of several [OBJ] files. It consists
78/// of a magic number followed by one or more [Modules](Module).
79///
80/// A `LIB` file can be constructed from a `u8` slice using
81/// `read`.
82///
83/// ```
84/// use std::path::Path;
85/// use psyk::io;
86/// # use anyhow::Result;
87/// # fn main() -> Result<()> {
88/// let lib = io::read_lib(Path::new("SOME.LIB"));
89/// # Ok(())
90/// # }
91/// ```
92///
93/// # Structure on Disk
94///
95/// | Offset | Type       | Description                                           |
96/// |--------|------------|-------------------------------------------------------|
97/// |   0    | `[u8;3]`   | Magic: "LIB"                                          |
98/// |   3    | `u8`       | Archive format version (1)                            |
99/// |   4    | `[Module]` | One or more [Modules](Module) which wrap [OBJ] files. |
100#[binrw]
101#[brw(little, magic = b"LIB", assert(!objs.is_empty()))]
102#[repr(C)]
103#[derive(Clone, Debug, PartialEq)]
104pub struct LIB {
105    version: u8,
106
107    #[br(parse_with = until_eof)]
108    objs: Vec<Module>,
109}
110
111impl LIB {
112    /// Creates a new [LIB] with the provided modules.
113    pub fn new(objs: Vec<Module>) -> Self {
114        Self { version: 1, objs }
115    }
116
117    /// The modules contained in this library.
118    ///
119    /// Each module wraps an OBJ file along with metadata about its name,
120    /// creation time, and exported symbols.
121    pub fn modules(&self) -> &Vec<Module> {
122        &self.objs
123    }
124}
125
126impl fmt::Display for LIB {
127    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128        self.fmt_with_options(f, &display::Options::default())
129    }
130}
131
132impl display::DisplayWithOptions for LIB {
133    fn fmt_with_options(&self, f: &mut fmt::Formatter, options: &display::Options) -> fmt::Result {
134        writeln!(f, "Module     Date     Time   Externals defined")?;
135        writeln!(f)?;
136        for module in &self.objs {
137            module.fmt_with_options(f, options)?;
138            writeln!(f)?;
139
140            if options.recursive {
141                writeln!(f)?;
142                module.obj.fmt_with_options(f, &options.indent())?;
143                writeln!(f)?;
144            }
145        }
146        Ok(())
147    }
148}
149
150/// An exported symbol from a module.
151///
152/// Exports represent functions or data that are made available to the linker
153/// for use by other modules.
154///
155/// # Structure on Disk
156///
157/// | Offset | Type   | Description                                                      |
158/// |--------|--------|------------------------------------------------------------------|
159/// | 0      | `u8`   | Size of the symbol name.                                         |
160/// | 0      | `[u8]` | The ASCII name of of the exported symbol. Not `NULL` terminated. |
161#[binrw]
162#[brw(little)]
163#[repr(C)]
164#[derive(Clone, PartialEq)]
165pub struct Export {
166    name_size: u8,
167    #[br(count = name_size)]
168    name: Vec<u8>,
169}
170
171/// An entry in the export table.
172///
173/// The export table is terminated by an export with a zero-length name.
174impl Export {
175    pub fn new(name: String) -> Self {
176        // TODO: should this restrict to ascii?
177        let mut utf8 = name.as_bytes().to_vec();
178        utf8.truncate(u8::MAX.into());
179        Self {
180            name_size: name.len() as u8,
181            name: utf8,
182        }
183    }
184
185    pub fn empty() -> Self {
186        Self {
187            name_size: 0,
188            name: Vec::new(),
189        }
190    }
191
192    /// Returns the name of this exported symbol.
193    ///
194    /// Non-UTF-8 characters are replaced with the Unicode replacement character (�)
195    pub fn name(&self) -> String {
196        // TODO: what are * prefixed symbols for?
197        if !self.name.is_empty() && self.name[0] == 0 {
198            format!("*{}", String::from_utf8_lossy(&self.name[1..]).into_owned())
199        } else {
200            String::from_utf8_lossy(&self.name).into_owned()
201        }
202    }
203}
204
205/// Trait for converting PSY-Q timestamps to standard Rust date/time types.
206///
207/// PSY-Q uses a custom 32-bit timestamp format similar to the DOS/Windows
208/// date format but with a different bit layout.
209///
210/// # Format
211///
212/// **Low 16 bits (date)**:
213/// ```text
214/// Bits:  15-9    8-5     4-0
215///        Year    Month   Day
216/// ```
217/// - Year: 0-127 (relative to 1980)
218/// - Month: 1-12
219/// - Day: 1-31
220///
221/// **High 16 bits (time)**:
222/// ```text
223/// Bits:  15-11   10-5    4-0
224///        Hour    Minute  Second/2
225/// ```
226/// - Hour: 0-23
227/// - Minute: 0-59
228/// - Second: 0-58 (stored as second/2; only even seconds)
229///
230/// # Note
231///
232/// These timestamps don't include timezone information and are treated
233/// as local time in the original PSY-Q toolchain.
234pub trait FromPSYQTimestamp {
235    /// Converts a PSY-Q timestamp to this type.
236    ///
237    /// Returns `None` if the timestamp contains invalid date/time values.
238    fn from_psyq_timestamp(t: u32) -> Option<Self>
239    where
240        Self: Sized;
241
242    /// Converts `Self` into a 32-bit PSY-Q timestamp
243    fn to_psyq_timestamp(&self) -> u32;
244}
245
246impl FromPSYQTimestamp for NaiveDate {
247    fn from_psyq_timestamp(t: u32) -> Option<Self> {
248        let date = t & 0xFFFF;
249        let year = ((date >> 9) & 0x7F) + 1980;
250        let month = (date >> 5) & 0xF;
251        let day = date & 0x1F;
252        NaiveDate::from_ymd_opt(year as i32, month, day)
253    }
254
255    fn to_psyq_timestamp(&self) -> u32 {
256        let year = (self.year() as u32 - 1980) & 0x7F;
257        let month = (self.month()) & 0xF;
258        let day = (self.day()) & 0x1F;
259
260        (year << 9) | (month << 5) | day
261    }
262}
263
264impl FromPSYQTimestamp for NaiveTime {
265    fn from_psyq_timestamp(t: u32) -> Option<Self> {
266        let time = t >> 16;
267        let hour = (time >> 11) & 0x1F;
268        let minute = (time >> 5) & 0x3F;
269        let second = (time & 0x1F) * 2;
270        NaiveTime::from_hms_opt(hour, minute, second)
271    }
272
273    fn to_psyq_timestamp(&self) -> u32 {
274        let hour = self.hour() & 0x1F;
275        let minute = self.minute() & 0x3F;
276        let second = self.second() / 2;
277
278        (hour << 27) | (minute << 21) | (second << 16)
279    }
280}
281
282impl FromPSYQTimestamp for NaiveDateTime {
283    fn from_psyq_timestamp(t: u32) -> Option<Self> {
284        // These timestamps are "local" without any timezone information.
285        // We do the best we can by treating them as naive datetime values.
286        Some(NaiveDateTime::new(
287            NaiveDate::from_psyq_timestamp(t)?,
288            NaiveTime::from_psyq_timestamp(t)?,
289        ))
290    }
291
292    fn to_psyq_timestamp(&self) -> u32 {
293        self.date().to_psyq_timestamp() | self.time().to_psyq_timestamp()
294    }
295}
296
297impl FromPSYQTimestamp for SystemTime {
298    fn from_psyq_timestamp(t: u32) -> Option<Self> {
299        let dt = NaiveDateTime::from_psyq_timestamp(t)?;
300        // Convert to UTC (though original timezone is unknown)
301        let datetime_utc = Utc.from_utc_datetime(&dt);
302        Some(UNIX_EPOCH + Duration::from_secs(datetime_utc.timestamp() as u64))
303    }
304
305    fn to_psyq_timestamp(&self) -> u32 {
306        let datetime = DateTime::<Local>::from(*self);
307        datetime.naive_utc().to_psyq_timestamp()
308    }
309}
310
311/// Metadata for a module within a LIB archive.
312///
313/// This includes the module name (up to 8 characters), creation timestamp,
314/// and a list of exported symbols.
315///
316/// # Structure on Disk
317///
318/// | Offset | Type       | Description                                                                          |
319/// |--------|------------|--------------------------------------------------------------------------------------|
320/// | 0      | `[u8; 8]`  | Module name in ASCII. Padded to 8-bytes with spaces.                                 |
321/// | 8      | `u32`      | A creation timestamp for the module. The format is described in [FromPSYQTimestamp]. |
322/// | 12     | `u32`      | An offset to the end of the module metadata.                                         |
323/// | 16     | `u32`      | The size of the serialized [OBJ] structure.                                          |
324/// | 20     | `[Export]` | An array of [Export] structs which declare any exported symbols.                     |
325///
326/// The list of [Export]s is terminated with a zero-length entry (a single byte with the value zero).
327///
328/// With the exception of the exports and offset, all other fields are derived from file system metadata. Including the
329/// exports at this level allows a linker to bypass the [OBJ] if it doesn't contain any relevant symbols. The exports
330/// can be retrieved by querying the [OBJ] directly using [OBJ::exports].
331#[binrw]
332#[brw(little)]
333#[repr(C)]
334#[derive(Clone, PartialEq)]
335pub struct ModuleMetadata {
336    name: [u8; 8],
337    created: u32,
338    offset: u32,
339    size: u32,
340
341    #[br(parse_with=until(|e: &Export| e.name_size == 0))]
342    exports: Vec<Export>,
343}
344
345#[inline]
346fn string_to_module_name(name: &str) -> [u8; 8] {
347    let mut module_name: [u8; 8] = [0x20; 8];
348
349    // the unicode path requires care to avoid breaking
350    // multi-byte codepoints and grapheme clusters.
351    let mut size = 0;
352    for (offset, cluster) in name.grapheme_indices(false) {
353        if offset > 7 || (offset + cluster.len()) > 8 {
354            break;
355        }
356        size = offset + cluster.len();
357    }
358
359    module_name[..size].copy_from_slice(&name.as_bytes()[..size]);
360    module_name
361}
362
363/// Converts a [Path] into an appropriate module name. The module
364/// name is the first 8 characters of the file name without anything
365/// following the first `.` (period) character (as defined by
366/// [Path::file_prefix]). If that portion of the file name is smaller
367/// than 8-bytes, the remaining bytes will be padded with the `NUL`
368/// character.
369///
370/// Path does not include a file component, this function will
371/// panic.
372///
373/// **Note on Unicode:** it is assumed that paths are encoded
374/// in UTF-8, an invariant not guaranteed by the Rust std library.
375/// Psy-Q was not built to handle Unicode filenames, so including
376/// files with characters outside of the ASCII range will likely
377/// break interoperability with other tools. However, Psy-K supports
378/// Unicode file names and will produce appropriate model names
379/// with only the bytes that represent full code points.
380#[inline]
381fn path_to_module_name(path: &Path) -> [u8; 8] {
382    let Some(prefix) = path.file_prefix() else {
383        panic!("Module paths must contain a file name: {:?}", path);
384    };
385    let binding = prefix.to_ascii_uppercase();
386
387    if !prefix.is_ascii() {
388        let Some(prefix_str) = binding.to_str() else {
389            panic!("Module path is not valid unicode: {:?}", path);
390        };
391        return string_to_module_name(prefix_str);
392    }
393
394    // the ascii path is simple, just copy the bytes
395    let bytes = binding.as_encoded_bytes();
396    let mut module_name: [u8; 8] = [0x20; 8];
397    let len = cmp::min(bytes.len(), module_name.len());
398
399    module_name[0..len].copy_from_slice(&bytes[0..len]);
400    module_name
401}
402
403impl ModuleMetadata {
404    pub fn new(name: String, created: SystemTime, size: u32, exports: Vec<Export>) -> Self {
405        let name = string_to_module_name(&name);
406        let created = created.to_psyq_timestamp();
407        let mut exports = exports;
408        exports.push(Export::empty());
409
410        let offset: u32 = 20 + exports.iter().map(|e| 1 + e.name_size as u32).sum::<u32>();
411        Self {
412            name,
413            created,
414            offset,
415            size: offset + size,
416            exports,
417        }
418    }
419
420    pub fn new_from_path(path: &Path, obj: &OBJ) -> Result<Self> {
421        let name = path_to_module_name(path);
422
423        let file_metadata = fs::metadata(path)?;
424        let created = if let Ok(creation_time) = file_metadata.created() {
425            creation_time
426        } else {
427            SystemTime::now()
428        };
429        let exports = obj
430            .exports()
431            .into_iter()
432            .map(Export::new)
433            .collect::<Vec<Export>>();
434
435        let size = file_metadata.len() as u32;
436
437        Ok(Self::new(
438            String::from_utf8(name.to_vec())?,
439            created,
440            size,
441            exports,
442        ))
443    }
444
445    /// Returns the module name, with trailing whitespace removed.
446    ///
447    /// Names will be at most 8-ASCII characters long (or 8 UTF-8 bytes).
448    pub fn name(&self) -> String {
449        // trim_end for the name array
450        let end = self
451            .name
452            .iter()
453            .rposition(|x| !x.is_ascii_whitespace())
454            .expect("Module.name trim_end")
455            + 1;
456        String::from_utf8_lossy(&self.name[..end]).into_owned()
457    }
458
459    /// Returns a list of symbol names exported by this module.
460    ///
461    /// Empty exports (the terminator entry) are filtered out.
462    pub fn exports(&self) -> Vec<String> {
463        self.exports
464            .iter()
465            .filter_map(|e| {
466                if e.name.is_empty() {
467                    None
468                } else {
469                    Some(e.name())
470                }
471            })
472            .collect()
473    }
474
475    /// Returns the creation timestamp as a formatted string.
476    ///
477    /// Format: `DD-MM-YY HH:MM:SS`
478    ///
479    /// # Example
480    /// ```text
481    /// 15-05-96 16:09:38
482    /// ```
483    pub fn created(&self) -> String {
484        // 15-05-96 16:09:38
485        //    hhhh hmmm mmms ssss yyyy yyyM MMMd dddd
486        // LE 1000 0001 0011 0011 0010 0000 1010 1111
487        //
488        // day    - 15 01111
489        // month  - 05 0101
490        // year   - 96 001000
491        // hour   - 16 10000
492        // minute - 09 000101
493        // second - 38 00010
494
495        // format!("{} {}", self.date(), self.time())
496        self.created_datetime()
497            .expect("created")
498            .format("%d-%m-%y %H:%M:%S")
499            .to_string()
500    }
501
502    /// Returns the creation timestamp as a `NaiveDateTime`.
503    ///
504    /// Returns `None` if the timestamp is invalid.
505    pub fn created_datetime(&self) -> Option<NaiveDateTime> {
506        NaiveDateTime::from_psyq_timestamp(self.created)
507    }
508
509    /// Returns the creation timestamp as a `SystemTime`.
510    ///
511    /// Returns `None` if the timestamp is invalid.
512    ///
513    /// Note: The original timestamp has no timezone information, so it's
514    /// treated as UTC for conversion purposes.
515    pub fn created_at(&self) -> Option<SystemTime> {
516        SystemTime::from_psyq_timestamp(self.created)
517    }
518}
519
520/// A module entry in a LIB archive.
521///
522/// Each module consists of metadata (name, timestamp, exports) and the
523/// actual OBJ file data.
524///
525/// # Structure on Disk
526///
527/// | Offset          | Type             | Description                                                                |
528/// |-----------------|------------------|----------------------------------------------------------------------------|
529/// | 0               | `ModuleMetadata` | [ModuleMetadata] containing the name, exports, and additional information. |
530/// | sizeof(*metadata*) | `OBJ`         | An [OBJ] as it appears on disk.                                            |
531///
532/// A `Module` contains a parsed and hydrated [OBJ]. If only the metadata is of interest, [OpaqueModule] can be used
533/// instead.
534#[binrw]
535#[brw(little)]
536#[repr(C)]
537#[derive(Clone, PartialEq)]
538pub struct Module {
539    metadata: ModuleMetadata,
540    obj: OBJ,
541}
542
543impl Module {
544    /// Create a new [Module] programmatically
545    pub fn new(obj: OBJ, metadata: ModuleMetadata) -> Self {
546        Self { metadata, obj }
547    }
548
549    /// Creates a new [Module] from the file at `path`.
550    ///
551    /// `path` must point to a valid [OBJ] file.
552    pub fn new_from_path(path: &Path) -> Result<Self> {
553        let obj = io::read_obj(path)?;
554        let metadata = ModuleMetadata::new_from_path(path, &obj)?;
555        Ok(Self { metadata, obj })
556    }
557
558    /// Returns the module name.
559    pub fn name(&self) -> String {
560        self.metadata.name()
561    }
562
563    /// Returns the list of exported symbol names.
564    pub fn exports(&self) -> Vec<String> {
565        self.metadata.exports()
566    }
567
568    /// Returns the creation timestamp as a formatted string.
569    pub fn created(&self) -> String {
570        self.metadata.created()
571    }
572
573    /// Returns the creation timestamp as a `SystemTime`
574    pub fn created_at(&self) -> Option<SystemTime> {
575        self.metadata.created_at()
576    }
577
578    /// Returns the creation timestamp as a `NaiveDateTime`
579    pub fn created_datetime(&self) -> Option<NaiveDateTime> {
580        self.metadata.created_datetime()
581    }
582
583    /// Returns a reference to the OBJ file contained in this module.
584    pub fn object(&self) -> &OBJ {
585        &self.obj
586    }
587}
588
589impl fmt::Display for Module {
590    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
591        self.fmt_with_options(f, &display::Options::default())
592    }
593}
594
595impl display::DisplayWithOptions for Module {
596    fn fmt_with_options(&self, f: &mut fmt::Formatter, _options: &display::Options) -> fmt::Result {
597        write!(
598            f,
599            "{:<8} {} {}",
600            self.name(),
601            self.created(),
602            self.exports()
603                .into_iter()
604                .map(|e| format!("{e} "))
605                .collect::<Vec<_>>()
606                .join("")
607        )
608    }
609}
610
611impl fmt::Debug for Module {
612    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
613        write!(
614            f,
615            "Module {{name: \"{}\", huh: {}, offset: {}, size: {}, exports: \"{:?}\", lnk: {:?}}}",
616            self.name(),
617            self.metadata.created,
618            self.metadata.offset,
619            self.metadata.size,
620            self.exports(),
621            self.obj
622        )
623    }
624}
625
626/// An opaque module representation used during parsing.
627///
628/// This variant stores the raw bytes of the OBJ file without parsing it,
629/// which can be useful for tools that only need to inspect metadata.
630///
631/// # Structure on Disk
632///
633/// | Offset             | Type             | Description                                                                |
634/// |--------------------|------------------|----------------------------------------------------------------------------|
635/// | 0                  | `ModuleMetadata` | [ModuleMetadata] containing the name, exports, and additional information. |
636/// | sizeof(*metadata*) | `[u8]`           | A binary blob containing a serialized [OBJ].                               |
637///
638/// If a parsed and hydrated [OBJ] is needed, use [Module] instead.
639#[binrw]
640#[brw(little)]
641#[repr(C)]
642pub struct OpaqueModule {
643    metadata: ModuleMetadata,
644
645    #[br(count = metadata.size - 16)]
646    obj: Vec<u8>,
647}
648
649impl OpaqueModule {
650    /// Returns the module name.
651    pub fn name(&self) -> String {
652        self.metadata.name()
653    }
654
655    /// Returns the list of exported symbol names.
656    pub fn exports(&self) -> Vec<String> {
657        self.metadata.exports()
658    }
659
660    /// Returns the creation timestamp as a formatted string.
661    pub fn created(&self) -> String {
662        self.metadata.created()
663    }
664
665    /// Returns the creation timestamp as a `SystemTime`.
666    pub fn created_at(&self) -> Option<SystemTime> {
667        self.metadata.created_at()
668    }
669
670    /// Returns the creation timestamp as a `NaiveDateTime`.
671    pub fn created_datetime(&self) -> Option<NaiveDateTime> {
672        self.metadata.created_datetime()
673    }
674
675    /// Returns a reference to the OBJ binary data.
676    pub fn obj_blob(&self) -> &[u8] {
677        &self.obj
678    }
679}
680
681/// A PSY-Q object file (LNK format).
682///
683/// OBJ files contain machine code, relocation information, symbol definitions,
684/// and debugging data needed by the linker.
685///
686/// # Structure on Disk
687///
688/// | Offset | Type        | Description               |
689/// |--------|-------------|---------------------------|
690/// | 0      | `[u8; 3]`   | Magic number: "LNK"       |
691/// | 3      | `u8`        | Version (typically 2)     |
692/// | 4      | `[Section]` | [Section]s until `NOP`    |
693///
694/// # Examples
695///
696/// ```no_run
697/// use std::path::Path;
698/// use psyk::io;
699/// use anyhow::Result;
700///
701/// fn main() -> Result<()> {
702///     let obj = io::read_obj(Path::new("MODULE.OBJ"))?;
703///
704///     println!("OBJ version: {}", obj.version());
705///     println!("Sections: {}", obj.sections().len());
706///
707///     Ok(())
708/// }
709/// ```
710#[binrw]
711#[brw(little, magic = b"LNK")]
712#[repr(C)]
713#[derive(Clone, Debug, PartialEq)]
714pub struct OBJ {
715    version: u8,
716    #[br(parse_with=until(|section: &Section| matches!(section, Section::NOP)))]
717    sections: Vec<Section>,
718}
719
720impl OBJ {
721    pub fn new(sections: Vec<Section>) -> Self {
722        assert!(matches!(sections.last(), Some(Section::NOP)));
723        Self {
724            version: 2,
725            sections,
726        }
727    }
728
729    /// Returns the OBJ format version (typically 2).
730    pub fn version(&self) -> u8 {
731        self.version
732    }
733
734    /// Returns the sections contained in this object file.
735    ///
736    /// Sections include code, data, symbols, relocations, and debug info.
737    /// The list is terminated by a `Section::NOP` entry.
738    pub fn sections(&self) -> &Vec<Section> {
739        &self.sections
740    }
741
742    /// Returns symbols exported by this object file.
743    ///
744    /// Exported symbols can be functions or globals.
745    pub fn exports(&self) -> Vec<String> {
746        self.sections()
747            .iter()
748            .filter_map({
749                |s| match s {
750                    Section::XDEF(xdef) => Some(xdef.symbol_name()),
751                    Section::XBSS(xbss) => Some(xbss.name()),
752                    _ => None,
753                }
754            })
755            .collect()
756    }
757}
758
759impl fmt::Display for OBJ {
760    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
761        self.fmt_with_options(f, &display::Options::default())
762    }
763}
764
765impl display::DisplayWithOptions for OBJ {
766    fn fmt_with_options(&self, f: &mut fmt::Formatter, options: &display::Options) -> fmt::Result {
767        options.write_indent(f)?;
768        writeln!(f, "Header : LNK version {}", self.version)?;
769        for section in &self.sections {
770            section.fmt_with_options(f, options)?;
771            writeln!(f)?;
772        }
773        Ok(())
774    }
775}
776
777/// Machine code section.
778///
779/// Contains executable instructions for the target [CPU](Section::CPU).
780///
781/// # Structure on Disk
782///
783/// | Offset | Type   | Description       |
784/// |--------|--------|-------------------|
785/// | 0      | `u16`  | Size of the code. |
786/// | 1      | `[u8]` | Machine code.     |
787#[binrw]
788#[brw(little)]
789#[derive(Clone, Debug, PartialEq)]
790pub struct Code {
791    size: u16,
792    #[br(count = size)]
793    code: Vec<u8>,
794}
795
796impl Code {
797    /// Returns the code for this section as bytes. Their format can be determined by the value
798    /// set in the [CPU](Section::CPU).
799    pub fn code(&self) -> &Vec<u8> {
800        &self.code
801    }
802}
803
804/// An expression used in relocations.
805///
806/// PSY-Q uses a sophisticated expression system for calculating relocated
807/// addresses. Expressions can be constants, symbol references, or complex
808/// arithmetic operations.
809///
810/// Each component of an expression has a unique on disk format. There are
811/// no synchronization points, or sizes encoded into the structure, so it
812/// is important that each type is explicitly modeled.
813///
814/// Linker expressions are similar, but not identical to assembler expressions
815/// and some built-in functions are available in both.
816///
817/// # Example Expressions
818///
819/// - `$1000` - Constant value 0x1000
820/// - `[5]` - Address of symbol #5
821/// - `sectbase(2)` - Base address of section #2
822/// - `(sectstart(1)+$100)` - Section 1 start plus 0x100
823#[binrw]
824#[brw(little)]
825#[derive(Clone, Debug, PartialEq)]
826pub enum Expression {
827    /// A constant value.
828    ///
829    /// ```asm
830    /// $123D
831    /// ```
832    ///
833    /// # Structure on Disk
834    ///
835    /// | Offset | Type   | Description |
836    /// |--------|--------|-------------|
837    /// | 0      | `u8`   | Magic: 0x0  |
838    /// | 1      | `u32`  | Value.      |
839    #[brw(magic(0u8))]
840    Constant(u32),
841
842    /// Index of a symbol's address.
843    ///
844    /// ```asm
845    /// [x]
846    /// ```
847    ///
848    /// # Structure on Disk
849    ///
850    /// | Offset | Type   | Description |
851    /// |--------|--------|-------------|
852    /// | 0      | `u8`   | Magic: 0x2  |
853    /// | 1      | `u16`  | ???         |
854    #[brw(magic(2u8))]
855    SymbolAddressIndex(u16),
856
857    /// Base address of a section.
858    ///
859    /// ```asm
860    /// sectbase(x)
861    /// ```
862    ///
863    /// # Structure on Disk
864    ///
865    /// | Offset | Type   | Description |
866    /// |--------|--------|-------------|
867    /// | 0      | `u8`   | Magic: 0x4  |
868    /// | 1      | `u16`  | ???         |
869    #[brw(magic(4u8))]
870    SectionAddressIndex(u16),
871
872    /// Untested
873    ///
874    /// ```asm
875    /// bank(x)
876    /// ```
877    ///
878    /// # Structure on Disk
879    ///
880    /// | Offset | Type   | Description |
881    /// |--------|--------|-------------|
882    /// | 0      | `u8`   | Magic: 0x6  |
883    /// | 1      | `u16`  | ???         |
884    #[brw(magic(6u8))]
885    Bank(u16),
886
887    /// Untested
888    ///
889    /// ```asm
890    /// sectof(x)
891    /// ```
892    ///
893    /// # Structure on Disk
894    ///
895    /// | Offset | Type   | Description |
896    /// |--------|--------|-------------|
897    /// | 0      | `u8`   | Magic: 0x8  |
898    /// | 1      | `u16`  | Section ID. |
899    #[brw(magic(8u8))]
900    SectionOffset(u16),
901
902    /// Untested
903    ///
904    /// ```asm
905    /// offs(x)
906    /// ```
907    ///
908    /// # Structure on Disk
909    ///
910    /// | Offset | Type   | Description |
911    /// |--------|--------|-------------|
912    /// | 0      | `u8`   | Magic: 0xA  |
913    /// | 1      | `u16`  | ???         |
914    // 10 - offs({})
915    #[brw(magic(10u8))]
916    Offset(u16),
917
918    /// Start address of a section.
919    ///
920    /// ```asm
921    /// sectstart(x)
922    /// ```
923    ///
924    /// # Structure on Disk
925    ///
926    /// | Offset | Type   | Description |
927    /// |--------|--------|-------------|
928    /// | 0      | `u8`   | Magic: 0xC  |
929    /// | 1      | `u16`  | Section ID. |
930    #[brw(magic(12u8))]
931    SectionStart(u16),
932
933    /// Untested
934    ///
935    /// ```asm
936    /// groupstart(x)
937    /// ```
938    ///
939    /// # Structure on Disk
940    ///
941    /// | Offset | Type   | Description |
942    /// |--------|--------|-------------|
943    /// | 0      | `u8`   | Magic: 0xE  |
944    /// | 1      | `u16`  | Group ID.   |
945    #[brw(magic(14u8))]
946    GroupStart(u16),
947
948    /// The offset of a group.
949    ///
950    /// Untested
951    ///
952    /// ```asm
953    /// groupof(x)
954    /// ```
955    ///
956    /// # Structure on Disk
957    ///
958    /// | Offset | Type   | Description |
959    /// |--------|--------|-------------|
960    /// | 0      | `u8`   | Magic: 0x10 |
961    /// | 1      | `u16`  | Group ID.   |
962    #[brw(magic(16u8))]
963    GroupOffset(u16),
964
965    /// Untested
966    ///
967    /// ```asm
968    /// seg(x)
969    /// ```
970    ///
971    /// # Structure on Disk
972    ///
973    /// | Offset | Type   | Description |
974    /// |--------|--------|-------------|
975    /// | 0      | `u8`   | Magic: 0x12 |
976    /// | 1      | `u16`  | ???         |
977    #[brw(magic(18u8))]
978    Segment(u16),
979
980    /// The `ORG` address of a group for a symbol.
981    ///
982    /// ```asm
983    /// grouporg(x)
984    /// ```
985    ///
986    /// # Structure on Disk
987    ///
988    /// | Offset | Type   | Description |
989    /// |--------|--------|-------------|
990    /// | 0      | `u8`   | Magic: 0x14 |
991    /// | 1      | `u16`  | Symbol ID.  |
992    #[brw(magic(20u8))]
993    GroupOrg(u16),
994
995    /// End address of a section.
996    ///
997    /// ```asm
998    /// sectend(X)
999    /// ```
1000    ///
1001    /// # Structure on Disk
1002    ///
1003    /// | Offset | Type  | Description  |
1004    /// |--------|-------|--------------|
1005    /// | 0      | `u8`  | Magic: 0x16  |
1006    /// | 1      | `u16` | Section ID.  |
1007    #[brw(magic(22u8))]
1008    SectionEnd(u16),
1009
1010    //
1011    // Comparison operators
1012    //
1013    /// Equality comparison.
1014    ///
1015    /// ```asm
1016    /// (a=b)
1017    /// ```
1018    ///
1019    /// # Structure on Disk
1020    ///
1021    /// | Offset                        | Type         | Description         |
1022    /// |-------------------------------|--------------|---------------------|
1023    /// | 0                             | `u8`         | Magic: 0x20         |
1024    /// | 1                             | `Expression` | Left [Expression].  |
1025    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1026    #[brw(magic(32u8))]
1027    Equals(Box<Expression>, Box<Expression>),
1028
1029    /// Inequality comparison.
1030    ///
1031    /// ```asm
1032    /// (a<>b)
1033    /// ```
1034    ///
1035    /// # Structure on Disk
1036    ///
1037    /// | Offset | Type   | Description |
1038    /// |--------|--------|-------------|
1039    /// | 0                             | `u8`   | Magic: 0x22  |
1040    /// | 1                             | `Expression` | Left [Expression].  |
1041    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1042    #[brw(magic(34u8))]
1043    NotEquals(Box<Expression>, Box<Expression>),
1044
1045    /// Less than or equal.
1046    ///
1047    /// ```asm
1048    /// (a<=b)
1049    /// ```
1050    ///
1051    /// # Structure on Disk
1052    ///
1053    /// | Offset | Type   | Description |
1054    /// |--------|--------|-------------|
1055    /// | 0      | `u8`   | Magic: 0x24  |
1056    /// | 1                             | `Expression` | Left [Expression].  |
1057    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1058    #[brw(magic(36u8))]
1059    LTE(Box<Expression>, Box<Expression>),
1060
1061    /// Less than.
1062    ///
1063    /// ```asm
1064    /// (a<b)
1065    /// ```
1066    ///
1067    /// # Structure on Disk
1068    ///
1069    /// | Offset | Type   | Description |
1070    /// |--------|--------|-------------|
1071    /// | 0      | `u8`   | Magic: 0x26  |
1072    /// | 1                             | `Expression` | Left [Expression].  |
1073    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1074    #[brw(magic(38u8))]
1075    LessThan(Box<Expression>, Box<Expression>),
1076
1077    /// Greater than or equal.
1078    ///
1079    /// ```asm
1080    /// (a>=b)
1081    /// ```
1082    ///
1083    /// # Structure on Disk
1084    ///
1085    /// | Offset | Type   | Description |
1086    /// |--------|--------|-------------|
1087    /// | 0      | `u8`   | Magic: 0x28  |
1088    /// | 1                             | `Expression` | Left [Expression].  |
1089    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1090    #[brw(magic(40u8))]
1091    GTE(Box<Expression>, Box<Expression>),
1092
1093    /// Greater than.
1094    ///
1095    /// ```asm
1096    /// (a>b)
1097    /// ```
1098    ///
1099    /// # Structure on Disk
1100    ///
1101    /// | Offset | Type   | Description |
1102    /// |--------|--------|-------------|
1103    /// | 0      | `u8`   | Magic: 0x2A  |
1104    /// | 1                             | `Expression` | Left [Expression].  |
1105    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1106    #[brw(magic(42u8))]
1107    GreaterThan(Box<Expression>, Box<Expression>),
1108
1109    //
1110    // Arithmetic operators
1111    //
1112    /// Addition.
1113    ///
1114    /// ```asm
1115    /// (a+b)
1116    /// ```
1117    ///
1118    /// # Structure on Disk
1119    ///
1120    /// | Offset | Type   | Description |
1121    /// |--------|--------|-------------|
1122    /// | 0      | `u8`   | Magic: 0x2C  |
1123    /// | 1                             | `Expression` | Left [Expression].  |
1124    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1125    #[brw(magic(44u8))]
1126    Add(Box<Expression>, Box<Expression>),
1127
1128    /// Subtraction.
1129    ///
1130    /// ```asm
1131    /// (a-b)
1132    /// ```
1133    ///
1134    /// # Structure on Disk
1135    ///
1136    /// | Offset | Type   | Description |
1137    /// |--------|--------|-------------|
1138    /// | 0      | `u8`   | Magic: 0x2E |
1139    /// | 1                             | `Expression` | Left [Expression].  |
1140    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1141    #[brw(magic(46u8))]
1142    Subtract(Box<Expression>, Box<Expression>),
1143
1144    /// Multiplication.
1145    ///
1146    /// ```asm
1147    /// (a*b)
1148    /// ```
1149    ///
1150    /// # Structure on Disk
1151    ///
1152    /// | Offset | Type   | Description |
1153    /// |--------|--------|-------------|
1154    /// | 0      | `u8`   | Magic: 0x30  |
1155    /// | 1                             | `Expression` | Left [Expression].  |
1156    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1157    #[brw(magic(48u8))]
1158    Multiply(Box<Expression>, Box<Expression>),
1159
1160    /// Division.
1161    ///
1162    /// ```asm
1163    /// (a/b)
1164    /// ```
1165    ///
1166    /// # Structure on Disk
1167    ///
1168    /// | Offset | Type   | Description |
1169    /// |--------|--------|-------------|
1170    /// | 0      | `u8`   | Magic: 0x32  |
1171    /// | 1                             | `Expression` | Left [Expression].  |
1172    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1173    #[brw(magic(50u8))]
1174    Divide(Box<Expression>, Box<Expression>),
1175
1176    /// Bitwise AND.
1177    ///
1178    /// ```asm
1179    /// (a&b)
1180    /// ```
1181    ///
1182    /// # Structure on Disk
1183    ///
1184    /// | Offset | Type   | Description |
1185    /// |--------|--------|-------------|
1186    /// | 0      | `u8`   | Magic: 0x34  |
1187    /// | 1                             | `Expression` | Left [Expression].  |
1188    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1189    #[brw(magic(52u8))]
1190    And(Box<Expression>, Box<Expression>),
1191
1192    /// Bitwise OR operator.
1193    ///
1194    /// ```asm
1195    /// (a!b)
1196    /// ```
1197    ///
1198    /// Instead of using the typical `|` (pipe) symbol, the default rendering
1199    /// of this operator is the `!` (exclamation point/bang) symbol. In the
1200    /// assembler, the `|` symbol acts as an alias for `!`.
1201    ///
1202    /// # Structure on Disk
1203    ///
1204    /// | Offset | Type   | Description |
1205    /// |--------|--------|-------------|
1206    /// | 0      | `u8`   | Magic: 0x36  |
1207    /// | 1                             | `Expression` | Left [Expression].  |
1208    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1209    #[brw(magic(54u8))]
1210    Or(Box<Expression>, Box<Expression>),
1211
1212    /// Bitwise XOR.
1213    ///
1214    /// ```asm
1215    /// (a^b)
1216    /// ```
1217    ///
1218    /// # Structure on Disk
1219    ///
1220    /// | Offset | Type   | Description |
1221    /// |--------|--------|-------------|
1222    /// | 0      | `u8`   | Magic: 0x38  |
1223    /// | 1                             | `Expression` | Left [Expression].  |
1224    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1225    #[brw(magic(56u8))]
1226    XOR(Box<Expression>, Box<Expression>),
1227
1228    /// Left shift.
1229    ///
1230    /// ```asm
1231    /// (a<<b)
1232    /// ```
1233    ///
1234    /// # Structure on Disk
1235    ///
1236    /// | Offset | Type   | Description |
1237    /// |--------|--------|-------------|
1238    /// | 0      | `u8`   | Magic: 0x3A  |
1239    /// | 1                             | `Expression` | Left [Expression].  |
1240    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1241    #[brw(magic(58u8))]
1242    LeftShift(Box<Expression>, Box<Expression>),
1243
1244    /// Right shift.
1245    ///
1246    /// ```asm
1247    /// (a>>b)
1248    /// ```
1249    ///
1250    /// # Structure on Disk
1251    ///
1252    /// | Offset | Type   | Description |
1253    /// |--------|--------|-------------|
1254    /// | 0      | `u8`   | Magic: 0x3C  |
1255    /// | 1                             | `Expression` | Left [Expression].  |
1256    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1257    #[brw(magic(60u8))]
1258    RightShift(Box<Expression>, Box<Expression>),
1259
1260    /// Modulo.
1261    ///
1262    /// ```asm
1263    /// (a%b)
1264    /// ```
1265    ///
1266    /// # Structure on Disk
1267    ///
1268    /// | Offset | Type   | Description |
1269    /// |--------|--------|-------------|
1270    /// | 0      | `u8`   | Magic: 0x3E  |
1271    /// | 1                             | `Expression` | Left [Expression].  |
1272    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1273    #[brw(magic(62u8))]
1274    Mod(Box<Expression>, Box<Expression>),
1275
1276    /// Dashes operator.
1277    ///
1278    /// ```asm
1279    /// (a---b)
1280    /// ```
1281    ///
1282    /// # Structure on Disk
1283    ///
1284    /// | Offset                        | Type         | Description         |
1285    /// |-------------------------------|--------------|---------------------|
1286    /// | 0                             | `u8`         | Magic: 0x40         |
1287    /// | 1                             | `Expression` | Left [Expression].  |
1288    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1289    #[brw(magic(64u8))]
1290    Dashes(Box<Expression>, Box<Expression>),
1291
1292    //
1293    // Special operators (primarily for Saturn/SH-2)
1294    //
1295    /// Reverse word.
1296    ///
1297    /// ```asm
1298    /// (a-revword-b)
1299    /// ```
1300    ///
1301    /// # Structure on Disk
1302    ///
1303    /// | Offset | Type   | Description |
1304    /// |--------|--------|-------------|
1305    /// | 0      | `u8`   | Magic: 0x42  |
1306    /// | 1                             | `Expression` | Left [Expression].  |
1307    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1308    #[brw(magic(66u8))]
1309    Revword(Box<Expression>, Box<Expression>),
1310
1311    /// Check0.
1312    ///
1313    /// ```asm
1314    /// (a-check0-b)
1315    /// ```
1316    ///
1317    /// # Structure on Disk
1318    ///
1319    /// | Offset | Type   | Description |
1320    /// |--------|--------|-------------|
1321    /// | 0      | `u8`   | Magic: 0x44  |
1322    /// | 1                             | `Expression` | Left [Expression].  |
1323    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1324    #[brw(magic(68u8))]
1325    Check0(Box<Expression>, Box<Expression>),
1326
1327    /// Check1.
1328    ///
1329    /// ```asm
1330    /// (a-check1-b)
1331    /// ```
1332    ///
1333    /// # Structure on Disk
1334    ///
1335    /// | Offset                        | Type         | Description         |
1336    /// |-------------------------------|--------------|---------------------|
1337    /// | 0                             | `u8`         | Magic: 0x46         |
1338    /// | 1                             | `Expression` | Left [Expression].  |
1339    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1340    #[brw(magic(70u8))]
1341    Check1(Box<Expression>, Box<Expression>),
1342
1343    /// Bit range extraction.
1344    ///
1345    /// ```asm
1346    /// (a-bitrange-b)
1347    /// ```
1348    ///
1349    /// # Structure on Disk
1350    ///
1351    /// | Offset                        | Type         | Description         |
1352    /// |-------------------------------|--------------|---------------------|
1353    /// | 0                             | `u8`         | Magic: 0x48         |
1354    /// | 1                             | `Expression` | Left [Expression].  |
1355    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1356    #[brw(magic(72u8))]
1357    BitRange(Box<Expression>, Box<Expression>),
1358
1359    /// Arithmetic shift with check.
1360    ///
1361    /// ```asm
1362    /// (a-arshift_chk-b)
1363    /// ```
1364    ///
1365    /// # Structure on Disk
1366    ///
1367    /// | Offset                        | Type         | Description         |
1368    /// |-------------------------------|--------------|---------------------|
1369    /// | 0                             | `u8`         | Magic: 0x4A         |
1370    /// | 1                             | `Expression` | Left [Expression].  |
1371    /// | sizeof(*left_expression*) + 1 | `Expression` | Right [Expression]. |
1372    #[brw(magic(74u8))]
1373    ArshiftChk(Box<Expression>, Box<Expression>),
1374}
1375
1376impl fmt::Display for Expression {
1377    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1378        match self {
1379            Self::Constant(value) => write!(f, "${value:x}"),
1380            Self::SymbolAddressIndex(addr) => write!(f, "[{addr:x}]"),
1381            Self::SectionAddressIndex(base) => write!(f, "sectbase({base:x})"),
1382            // untested
1383            Self::Bank(bank) => write!(f, "bank({bank:x})"),
1384            // untested
1385            Self::SectionOffset(section) => write!(f, "sectof({section:x})"),
1386            // untested
1387            Self::Offset(bank) => write!(f, "offs({bank:x})"),
1388            Self::SectionStart(offset) => write!(f, "sectstart({offset:x})"),
1389            // untested
1390            Self::GroupStart(group) => write!(f, "groupstart({group:x})"),
1391            // untested
1392            Self::GroupOffset(group) => write!(f, "groupof({group:x})"),
1393            // untested
1394            Self::Segment(segment) => write!(f, "seg({segment:x})"),
1395            // untested
1396            Self::GroupOrg(group) => write!(f, "grouporg({group:x})"),
1397            Self::SectionEnd(offset) => write!(f, "sectend({offset:x})"),
1398
1399            // comparison
1400            Self::Equals(lhs, rhs) => write!(f, "({lhs}={rhs})"),
1401            Self::NotEquals(lhs, rhs) => write!(f, "({lhs}<>{rhs})"),
1402            Self::LTE(lhs, rhs) => write!(f, "({lhs}<={rhs})"),
1403            Self::LessThan(lhs, rhs) => write!(f, "({lhs}<{rhs})"),
1404            Self::GTE(lhs, rhs) => write!(f, "({lhs}>={rhs})"),
1405            Self::GreaterThan(lhs, rhs) => write!(f, "({lhs}>{rhs})"),
1406
1407            // arithmatic
1408            Self::Add(lhs, rhs) => write!(f, "({lhs}+{rhs})"),
1409            Self::Subtract(lhs, rhs) => write!(f, "({lhs}-{rhs})"),
1410            Self::Multiply(lhs, rhs) => write!(f, "({lhs}*{rhs})"),
1411            Self::Divide(lhs, rhs) => write!(f, "({lhs}/{rhs})",),
1412            Self::And(lhs, rhs) => write!(f, "({lhs}&{rhs})"),
1413            Self::Or(lhs, rhs) => write!(f, "({lhs}!{rhs})"),
1414            Self::XOR(lhs, rhs) => write!(f, "({lhs}^{rhs})"),
1415            Self::LeftShift(lhs, rhs) => write!(f, "({lhs}<<{rhs})"),
1416            Self::RightShift(lhs, rhs) => write!(f, "({lhs}>>{rhs})"),
1417            Self::Mod(lhs, rhs) => write!(f, "({lhs}%%{rhs})"),
1418            Self::Dashes(lhs, rhs) => write!(f, "({lhs}---{rhs})"),
1419
1420            // keyword
1421            Self::Revword(lhs, rhs) => write!(f, "({lhs}-revword-{rhs})"),
1422            Self::Check0(lhs, rhs) => write!(f, "({lhs}-check0-{rhs})"),
1423            Self::Check1(lhs, rhs) => write!(f, "({lhs}-check1-{rhs})"),
1424            Self::BitRange(lhs, rhs) => write!(f, "({lhs}-bitrange-{rhs})"),
1425            Self::ArshiftChk(lhs, rhs) => write!(f, "({lhs}-arshift_chk-{rhs})"),
1426        }
1427    }
1428}
1429
1430/// A relocation patch to be applied by the linker.
1431///
1432/// Patches modify code or data at a specific offset using a calculated expression.
1433///
1434/// # Types
1435///
1436/// | Tag | Description                                              | Expression             |
1437/// |-----|----------------------------------------------------------|------------------------|
1438/// | 8   | Write 32-bit expression value (big-endian?)              | ``                     |
1439/// | 10  | Unknown                                                  |                        |
1440/// | 16  | Write 32-bit expression value (little-endian?)           | ``                     |
1441/// | 30  | Possibly related to register allocation.                 |                        |
1442/// | 74  | Function symbol relocation (24-bit, little-endian).      | `[14]`                 |
1443/// | 82  | Copy expression high 16-bytes into instruction low bytes | `($20+sectbase(f001))` |
1444/// | 84  | Copy expression low 16-bytes into instruction low bytes  | `($20+sectbase(f001))` |
1445///
1446/// # Structure on Disk
1447///
1448/// | Offset | Type         | Description                                              |
1449/// |--------|--------------|----------------------------------------------------------|
1450/// | 0      | `u8`         | The type of patch.                                       |
1451/// | 1      | `u16`        | Offset in the section where the patch should be applied. |
1452/// | 3      | `Expression` | An [Expression] to use calculate the patch value.        |
1453#[binrw]
1454#[brw(little)]
1455#[derive(Clone, Debug, PartialEq)]
1456pub struct Patch {
1457    /// The type of patch (determines how the expression value is applied).
1458    tag: u8,
1459    /// Offset in the current section where the patch should be applied.
1460    offset: u16,
1461    /// Expression to calculate the patch value.
1462    expression: Expression,
1463}
1464
1465/// Section header information.
1466///
1467/// Defines properties of a section such as its group, alignment, and type name.
1468///
1469/// # Structure on Disk
1470///
1471/// | Offset | Type   | Description                                          |
1472/// |--------|--------|------------------------------------------------------|
1473/// | 0      | `u16`  | Section ID.                                          |
1474/// | 2      | `u16`  | Group ID.                                            |
1475/// | 4      | `u8`   | Alignemnt.                                           |
1476/// | 5      | `u8`   | Size of the `type_name` string.                      |
1477/// | 6      | `[u8]` | The name of the section type. Not `NULL` terminated. |
1478#[binrw]
1479#[brw(little)]
1480#[derive(Clone, PartialEq)]
1481pub struct LNKHeader {
1482    section: u16,
1483    group: u16,
1484    align: u8,
1485    type_name_size: u8,
1486
1487    #[br(count = type_name_size)]
1488    type_name: Vec<u8>,
1489}
1490
1491impl LNKHeader {
1492    /// Returns the section type name (e.g., ".text", ".data", ".bss").
1493    pub fn type_name(&self) -> String {
1494        String::from_utf8_lossy(&self.type_name).into_owned()
1495    }
1496}
1497
1498impl fmt::Debug for LNKHeader {
1499    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1500        write!(
1501            f,
1502            "LNKHeader {{section: {}, group: {}, align: {}, type_name: \"{}\"}}",
1503            self.section,
1504            self.group,
1505            self.align,
1506            self.type_name(),
1507        )
1508    }
1509}
1510
1511/// A local symbol definition.
1512///
1513/// Local symbols are visible only within the current module.
1514///
1515/// # Structure on Disk
1516///
1517/// | Offset | Type   | Description                             |
1518/// |--------|--------|-----------------------------------------|
1519/// | 0      | `u16`  | Section ID.                             |
1520/// | 2      | `u32`  | Offset (TODO: relative to what?)        |
1521/// | 6      | `u8`   | Size of the symbol name string.         |
1522/// | 7      | `[u8]` | The symbol name. Not `NULL` terminated. |
1523#[binrw]
1524#[brw(little)]
1525#[derive(Clone, PartialEq)]
1526pub struct LocalSymbol {
1527    section: u16,
1528    offset: u32,
1529    name_size: u8,
1530
1531    #[br(count = name_size)]
1532    name: Vec<u8>,
1533}
1534
1535impl LocalSymbol {
1536    pub fn name(&self) -> String {
1537        String::from_utf8_lossy(&self.name).into_owned()
1538    }
1539}
1540
1541impl fmt::Debug for LocalSymbol {
1542    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1543        write!(
1544            f,
1545            "LocalSymbol {{section: {}, offset: {}, name: \"{}\"}}",
1546            self.section,
1547            self.offset,
1548            self.name(),
1549        )
1550    }
1551}
1552
1553/// A group symbol definition.
1554///
1555/// Groups are used to organize sections for linking.
1556///
1557/// # Structure on Disk
1558///
1559/// | Offset | Type   | Description                             |
1560/// |--------|--------|-----------------------------------------|
1561/// | 0      | `u16`  | Number.                                 |
1562/// | 2      | `u8`   | Symbol type.                            |
1563/// | 6      | `u8`   | Size of the symbol name string.         |
1564/// | 7      | `[u8]` | The symbol name. Not `NULL` terminated. |
1565#[binrw]
1566#[brw(little)]
1567#[derive(Clone, PartialEq)]
1568pub struct GroupSymbol {
1569    number: u16,
1570    sym_type: u8,
1571    name_size: u8,
1572
1573    #[br(count = name_size)]
1574    name: Vec<u8>,
1575}
1576
1577impl GroupSymbol {
1578    pub fn name(&self) -> String {
1579        String::from_utf8_lossy(&self.name).into_owned()
1580    }
1581}
1582
1583impl fmt::Debug for GroupSymbol {
1584    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1585        write!(
1586            f,
1587            "GroupSymbol {{number: {}, type: {}, name: \"{}\"}}",
1588            self.number,
1589            self.sym_type,
1590            self.name(),
1591        )
1592    }
1593}
1594
1595/// An external symbol definition (XDEF).
1596///
1597/// XDEF entries define symbols that are exported from this module
1598/// and can be referenced by other modules.
1599///
1600/// # Structure on Disk
1601///
1602/// | Offset | Type   | Description                             |
1603/// |--------|--------|-----------------------------------------|
1604/// | 0      | `u16`  | Number.                                 |
1605/// | 2      | `u16`  | Section ID.                             |
1606/// | 4      | `u32`  | Offset (TODO: relative to what?)        |
1607/// | 8      | `u8`   | Size of the symbol name string.         |
1608/// | 9      | `[u8]` | The symbol name. Not `NULL` terminated. |
1609#[binrw]
1610#[brw(little)]
1611#[derive(Clone, Debug, PartialEq)]
1612pub struct XDEF {
1613    number: u16,
1614    section: u16,
1615    offset: u32,
1616    symbol_name_size: u8,
1617
1618    #[br(count = symbol_name_size)]
1619    symbol_name: Vec<u8>,
1620}
1621
1622impl XDEF {
1623    pub fn symbol_name(&self) -> String {
1624        // TODO: can a starred symbol be here as well?
1625        String::from_utf8_lossy(&self.symbol_name).into_owned()
1626    }
1627}
1628
1629/// An external symbol reference (XREF).
1630///
1631/// XREF entries declare symbols that this module needs but are
1632/// defined in other modules.
1633///
1634/// # Structure on Disk
1635///
1636/// | Offset | Type   | Description                             |
1637/// |--------|--------|-----------------------------------------|
1638/// | 0      | `u16`  | Number.                                 |
1639/// | 2      | `u8`   | Size of the symbol name string.         |
1640/// | 3      | `[u8]` | The symbol name. Not `NULL` terminated. |
1641#[binrw]
1642#[brw(little)]
1643#[derive(Clone, Debug, PartialEq)]
1644pub struct XREF {
1645    number: u16,
1646    symbol_name_size: u8,
1647
1648    #[br(count = symbol_name_size)]
1649    symbol_name: Vec<u8>,
1650}
1651
1652impl XREF {
1653    pub fn symbol_name(&self) -> String {
1654        String::from_utf8_lossy(&self.symbol_name).into_owned()
1655    }
1656}
1657
1658/// A file name reference used in debug information.
1659///
1660/// # Structure on Disk
1661///
1662/// | Offset | Type   | Description                                  |
1663/// |--------|--------|----------------------------------------------|
1664/// | 0      | `u16`  | The file number.                             |
1665/// | 2      | `u8`   | The size of the file name.                   |
1666/// | 3      | `[u8]` | The name of the file. Not `NULL` terminated. |
1667#[binrw]
1668#[brw(little)]
1669#[derive(Clone, PartialEq)]
1670pub struct Filename {
1671    number: u16,
1672    size: u8,
1673    #[br(count = size)]
1674    name: Vec<u8>,
1675}
1676
1677impl Filename {
1678    pub fn number(&self) -> u16 {
1679        self.number
1680    }
1681
1682    pub fn name(&self) -> String {
1683        String::from_utf8_lossy(&self.name).into_owned()
1684    }
1685}
1686
1687impl fmt::Debug for Filename {
1688    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1689        write!(
1690            f,
1691            "Filename {{number: {}, name: \"{}\"}}",
1692            self.number,
1693            self.name(),
1694        )
1695    }
1696}
1697
1698/// Set MX info directive.
1699///
1700/// # Structure on Disk
1701///
1702/// | Offset | Type   | Description                                  |
1703/// |--------|--------|----------------------------------------------|
1704/// | 0      | `u16`  | Offset (TODO: relative to what?)             |
1705/// | 2      | `u8`   | Value.                                       |
1706#[binrw]
1707#[brw(little)]
1708#[derive(Clone, Debug, PartialEq)]
1709pub struct SetMXInfo {
1710    offset: u16,
1711    value: u8,
1712}
1713
1714/// External BSS (uninitialized data) symbol.
1715///
1716/// # Structure on Disk
1717///
1718/// | Offset | Type   | Description                                    |
1719/// |--------|--------|------------------------------------------------|
1720/// | 0      | `u16`  | Number.                                        |
1721/// | 2      | `u16`  | The section ID this BSS belongs to.            |
1722/// | 4      | `u32`  | The size of the uninitialized data.            |
1723/// | 8      | `u8`   | The size of the symbol name.                   |
1724/// | 9      | `[u8]` | The name of the symbol. Not `NULL` terminated. |
1725#[binrw]
1726#[brw(little)]
1727#[derive(Clone, Debug, PartialEq)]
1728pub struct XBSS {
1729    number: u16,
1730    section: u16,
1731    size: u32,
1732    name_size: u8,
1733
1734    #[br(count = name_size)]
1735    name: Vec<u8>,
1736}
1737
1738impl XBSS {
1739    pub fn name(&self) -> String {
1740        String::from_utf8_lossy(&self.name).into_owned()
1741    }
1742}
1743
1744/// Set source line debugger (SLD) line number.
1745///
1746/// # Structure on Disk
1747///
1748/// | Offset | Type   | Description                      |
1749/// |--------|--------|----------------------------------|
1750/// | 0      | `u16`  | Offset (TODO: relative to what?) |
1751/// | 2      | `u32`  | Line number.                     |
1752#[binrw]
1753#[brw(little)]
1754#[derive(Clone, Debug, PartialEq)]
1755pub struct SetSLDLineNum {
1756    offset: u16,
1757    linenum: u32,
1758}
1759
1760/// Set source line debugger (SLD) line number with file reference.
1761///
1762/// # Structure on Disk
1763///
1764/// | Offset | Type   | Description                       |
1765/// |--------|--------|-----------------------------------|
1766/// | 0      | `u16`  | Offset (TODO: relative to what?). |
1767/// | 2      | `u32`  | Line number.                      |
1768/// | 6      | `u16`  | File ID.                          |
1769#[binrw]
1770#[brw(little)]
1771#[derive(Clone, Debug, PartialEq)]
1772pub struct SetSLDLineNumFile {
1773    offset: u16,
1774    linenum: u32,
1775    file: u16,
1776}
1777
1778/// **n.b.!** this is completely untested and based on
1779/// assumptions from the output from `dumpobj`.
1780#[binrw]
1781#[brw(little)]
1782#[derive(Clone, Debug, PartialEq)]
1783pub struct ProcedureCall {
1784    distance: u8,
1785    symbol: u16,
1786}
1787
1788/// **n.b.!** this is completely untested and based on
1789/// assumptions from the output from `dumpobj`.
1790#[binrw]
1791#[brw(little)]
1792#[derive(Clone, Debug, PartialEq)]
1793pub struct ProcedureDefinition {
1794    symbol: u16,
1795}
1796
1797/// Function start debug information.
1798///
1799/// Provides detailed information about a function for source-level debugging.
1800///
1801/// # Structure on Disk
1802///
1803/// | Offset | Type   | Description                                      |
1804/// |--------|--------|--------------------------------------------------|
1805/// | 0      | `u16`  | Section ID.                                      |
1806/// | 2      | `u32`  | Offset (TODO: relative to what?)                 |
1807/// | 6      | `u16`  | File ID.                                         |
1808/// | 8      | `u32`  | Line number.                                     |
1809/// | 12     | `u16`  | Frame register.                                  |
1810/// | 14     | `u32`  | Frame size.                                      |
1811/// | 18     | `u16`  | Return PC register.                              |
1812/// | 20     | `u32`  | Mask.                                            |
1813/// | 24     | `i32`  | Mask offset. (TODO: relative to what?)           |
1814/// | 28     | `u8`   | The size of the function name.                   |
1815/// | 29     | `[u8]` | The name of the function. Not `NULL` terminated. |
1816#[binrw]
1817#[brw(little)]
1818#[derive(Clone, Debug, PartialEq)]
1819pub struct FunctionStart {
1820    section: u16,
1821    offset: u32,
1822    file: u16,
1823    linenum: u32,
1824    frame_register: u16,
1825    frame_size: u32,
1826    return_pc_register: u16,
1827    mask: u32,
1828    mask_offset: i32,
1829
1830    name_size: u8,
1831    #[br(count = name_size)]
1832    name: Vec<u8>,
1833}
1834
1835impl FunctionStart {
1836    /// Function end debug information.
1837    pub fn name(&self) -> String {
1838        String::from_utf8_lossy(&self.name).into_owned()
1839    }
1840}
1841
1842/// Section, Offset, and Line number information for source-line debugging.
1843///
1844/// # Structure on Disk
1845///
1846/// | Offset | Type   | Description                       |
1847/// |--------|--------|-----------------------------------|
1848/// | 0      | `u16`  | Section ID.                       |
1849/// | 2      | `u32`  | Offset (TODO: relative to what?   |
1850/// | 6      | `u32`  | Line number.                      |
1851#[binrw]
1852#[brw(little)]
1853#[derive(Clone, Debug, PartialEq)]
1854pub struct SectionOffsetLine {
1855    section: u16,
1856    offset: u32,
1857    linenum: u32,
1858}
1859
1860/// Variable or type definition debug information.
1861///
1862/// # Structure on Disk
1863///
1864/// | Offset | Type   | Description                                    |
1865/// |--------|--------|------------------------------------------------|
1866/// | 0      | `u16`  | Section ID.                                    |
1867/// | 2      | `u32`  | Value.                                         |
1868/// | 6      | `u16`  | Class ID.                                      |
1869/// | 8      | `u16`  | Definition type.                               |
1870/// | 10     | `u32`  | Data size.                                     |
1871/// | 14     | `u8`   | The size of the symbol name.                   |
1872/// | 15     | `[u8]` | The name of the symbol. Not `NULL` terminated. |
1873#[binrw]
1874#[brw(little)]
1875#[derive(Clone, Debug, PartialEq)]
1876pub struct Def {
1877    section: u16,
1878    value: u32,
1879    class: u16,
1880    def_type: u16,
1881    size: u32,
1882    name_size: u8,
1883    #[br(count = name_size)]
1884    name: Vec<u8>,
1885}
1886
1887impl Def {
1888    /// Returns the definition name.
1889    pub fn name(&self) -> String {
1890        String::from_utf8_lossy(&self.name).into_owned()
1891    }
1892}
1893
1894/// Dimension specification for arrays.
1895#[binrw]
1896#[brw(little)]
1897#[derive(Clone, Debug, PartialEq)]
1898pub enum Dim {
1899    /// No dimensions (scalar).
1900    ///
1901    /// # Structure on Disk
1902    ///
1903    /// | Offset | Type | Description |
1904    /// |--------|------|-------------|
1905    /// | 0      | `u8` | Magic: 0x0  |
1906    #[br(magic = 0u16)]
1907    None,
1908
1909    /// Single dimension with size.
1910    ///
1911    /// # Structure on Disk
1912    ///
1913    /// | Offset | Type  | Description |
1914    /// |--------|-------|-------------|
1915    /// | 0      | `u16` | Magic: 0x1  |
1916    /// | 1      | `u32` | Size        |
1917    #[br(magic = 1u16)]
1918    Value(u32),
1919}
1920
1921impl fmt::Display for Dim {
1922    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1923        match self {
1924            Self::None => write!(f, "0"),
1925            Self::Value(v) => write!(f, "1 {v}"),
1926        }
1927    }
1928}
1929
1930/// Extended variable or type definition with additional metadata.
1931///
1932/// # Structure on Disk
1933///
1934/// | Offset                       | Type   | Description                                    |
1935/// |------------------------------|--------|------------------------------------------------|
1936/// | 0                            | `u16`  | Section ID.                                    |
1937/// | 2                            | `u32`  | Value.                                         |
1938/// | 6                            | `u16`  | Class.                                         |
1939/// | 8                            | `u16`  | Definition type.                               |
1940/// | 10                           | `u32`  | Size of the data.                              |
1941/// | 14                           | `Dim`  | A [Dim] describing the dimensions              |
1942/// | sizeof(*dim*) + 14           | `u8`   | Size of the tag string.                        |
1943/// | offsetof(*tag_size*) + 1     | `[u8]` | Tag name string. Not `NULL` terminated.        |
1944/// | offsetof(*tag*) + *tag_size* | `u8`   | Size of the name string.                       |
1945/// | offsetof(*name_size*) + 1    | `[u8]` | Definition name string. Not `NULL` terminated. |
1946#[binrw]
1947#[brw(little)]
1948#[derive(Clone, Debug, PartialEq)]
1949pub struct Def2 {
1950    section: u16,
1951    value: u32,
1952    class: u16,
1953    def_type: u16, // 34 00
1954    size: u32,     // 04 00 00 00
1955    dims: Dim,
1956    tag_size: u8,
1957    #[br(count = tag_size)]
1958    tag: Vec<u8>,
1959    name_size: u8, // 06
1960    #[br(count = name_size)]
1961    name: Vec<u8>, // 75 5F 63 68 61 72
1962}
1963
1964impl Def2 {
1965    pub fn tag(&self) -> String {
1966        String::from_utf8_lossy(&self.tag).into_owned()
1967    }
1968
1969    pub fn name(&self) -> String {
1970        String::from_utf8_lossy(&self.name).into_owned()
1971    }
1972}
1973
1974pub mod cputype {
1975    //! CPU architecture type identifiers.
1976    //!
1977    //! [MOTOROLA_68000], [MIPS_R3000], and [HITACHI_SH2] have been found
1978    //! in the wild. Others are speculative based on available information.
1979
1980    /// Motorola 68000 - Sega Genesis, Sega CD, Mega Drive, & Mega CD.
1981    pub const MOTOROLA_68000: u8 = 0;
1982
1983    /// Motorola 68010.
1984    pub const MOTOROLA_68010: u8 = 1;
1985
1986    /// Motorola 68020.
1987    pub const MOTOROLA_68020: u8 = 2;
1988
1989    /// Motorola 68030.
1990    pub const MOTOROLA_68030: u8 = 3;
1991
1992    /// Motorola 68040.
1993    pub const MOTOROLA_68040: u8 = 4;
1994
1995    /// WDC 65816 - Used for SNES derivative Ricoh 5A22.
1996    pub const WDC_65816: u8 = 5;
1997
1998    /// Zilog Z80 - Sega Genesis Sound CPU.
1999    pub const ZILOG_Z80: u8 = 6;
2000
2001    /// MIPS R3000 with GTE (Graphics Transform Engine) - PlayStation 1.
2002    pub const MIPS_R3000: u8 = 7;
2003
2004    /// Hitachi SH-2 - Sega Saturn.
2005    pub const HITACHI_SH2: u8 = 8;
2006}
2007
2008fn unimplemented(s: &str) -> bool {
2009    eprintln!("Unimplemented: {s}");
2010    false
2011}
2012
2013/// A section within an OBJ file.
2014///
2015/// Sections can contain code, data, relocations, symbols, or debug information.
2016/// The section list is terminated by a NOP entry.
2017///
2018/// # Section Types
2019///
2020/// - [Code](Section::Code): Executable machine code
2021/// - [BSS](Section::BSS): Uninitialized data
2022/// - [XDEF](Section::XDEF)/[XREF](Section::XREF): Symbol exports and imports
2023/// - [Patch](Section::Patch): Relocation information
2024/// - Debug information: Line numbers, function info, etc.
2025/// - And many more!
2026#[binrw]
2027#[brw(little)]
2028#[derive(Clone, Debug, PartialEq)]
2029pub enum Section {
2030    /// End of file marker.
2031    ///
2032    /// # Structure on Disk
2033    ///
2034    /// | Offset | Type | Description               |
2035    /// |--------|------|---------------------------|
2036    /// | 0      | `u8` | Magic: 0x0                |
2037    #[brw(magic(0u8))]
2038    NOP,
2039
2040    /// Machine code.
2041    ///
2042    /// # Structure on Disk
2043    ///
2044    /// | Offset | Type   | Description        |
2045    /// |--------|--------|--------------------|
2046    /// | 0      | `u8`   | Magic: 0x2         |
2047    /// | 1      | `Code` | A [Code] structure |
2048    #[brw(magic(2u8))]
2049    Code(Code),
2050
2051    /// Run at offset.
2052    ///
2053    /// # Structure on Disk
2054    ///
2055    /// | Offset | Type  | Description       |
2056    /// |--------|-------|-------------------|
2057    /// | 0      | `u8`  | Magic: 0x4        |
2058    /// | 1      | `u16` |                   |
2059    /// | 3      | `u16` |                   |
2060    #[brw(magic(4u8))]
2061    RunAtOffset(u16, u16),
2062
2063    /// Switch to different section.
2064    ///
2065    /// # Structure on Disk
2066    ///
2067    /// | Offset | Type  | Description       |
2068    /// |--------|-------|-------------------|
2069    /// | 0      | `u8`  | Magic: 0x2        |
2070    /// | 1      | `u16` | Section ID        |
2071    #[brw(magic(6u8))]
2072    SectionSwitch(u16),
2073
2074    /// Uninitialized data (BSS) with size in bytes.
2075    ///
2076    /// # Structure on Disk
2077    ///
2078    /// | Offset | Type   | Description       |
2079    /// |--------|--------|-------------------|
2080    /// | 0      | `u8`   | Magic: 0x8        |
2081    /// | 1      | `u32`  | Size in bytes     |
2082    #[brw(magic(8u8))]
2083    BSS(u32),
2084
2085    /// Relocation patch.
2086    ///
2087    /// # Structure on Disk
2088    ///
2089    /// | Offset | Type    | Description         |
2090    /// |--------|---------|---------------------|
2091    /// | 0      | `u8`    | Magic: 0xA          |
2092    /// | 1      | `Patch` | A [Patch] structure |
2093    #[brw(magic(10u8))]
2094    Patch(Patch),
2095
2096    /// External symbol definition.
2097    ///
2098    /// # Structure on Disk
2099    ///
2100    /// | Offset | Type   | Description          |
2101    /// |--------|--------|----------------------|
2102    /// | 0      | `u8`   | Magic: 0xC           |
2103    /// | 1      | `XDEF` | An [XDEF] structure. |
2104    #[brw(magic(12u8))]
2105    XDEF(XDEF),
2106
2107    /// External symbol reference.
2108    ///
2109    /// # Structure on Disk
2110    ///
2111    /// | Offset | Type   | Description          |
2112    /// |--------|--------|----------------------|
2113    /// | 0      | `u8`   | Magic: 0xE           |
2114    /// | 1      | `XREF` | An [XREF] structure. |
2115    #[brw(magic(14u8))]
2116    XREF(XREF),
2117
2118    /// Section header.
2119    ///
2120    /// # Structure on Disk
2121    ///
2122    /// | Offset | Type        | Description               |
2123    /// |--------|-------------|---------------------------|
2124    /// | 0      | `u8`        | Magic: 0x10               |
2125    /// | 1      | `LNKHeader` | An [LNKHeader] structure. |
2126    #[brw(magic(16u8))]
2127    LNKHeader(LNKHeader),
2128
2129    /// Local symbol.
2130    ///
2131    /// # Structure on Disk
2132    ///
2133    /// | Offset | Type          | Description               |
2134    /// |--------|---------------|---------------------------|
2135    /// | 0      | `u8`          | Magic: 0x12               |
2136    /// | 1      | `LocalSymbol` | A [LocalSymbol] structure |
2137    #[brw(magic(18u8))]
2138    LocalSymbol(LocalSymbol),
2139
2140    /// Group symbol.
2141    ///
2142    /// # Structure on Disk
2143    ///
2144    /// | Offset | Type          | Description                |
2145    /// |--------|---------------|----------------------------|
2146    /// | 0      | `u8`          | Magic: 0x14                |
2147    /// | 1      | `GroupSymbol` | A [GroupSymbol] structure. |
2148    #[brw(magic(20u8))]
2149    GroupSymbol(GroupSymbol),
2150
2151    /// Untested
2152    #[brw(magic(22u8))]
2153    ByteSizeRegister(u16),
2154
2155    /// Untested
2156    #[brw(magic(24u8))]
2157    WordSizeRegister(u16),
2158
2159    /// Untested
2160    #[brw(magic(26u8))]
2161    LongSizeRegister(u16),
2162
2163    /// File name reference.
2164    ///
2165    /// # Structure on Disk
2166    ///
2167    /// | Offset | Type       | Description             |
2168    /// |--------|------------|-------------------------|
2169    /// | 0      | `u8`       | Magic: 0x1C             |
2170    /// | 1      | `Filename` | A [Filename] structure. |
2171    #[brw(magic(28u8))]
2172    Filename(Filename),
2173
2174    /// Untested
2175    #[brw(magic(30u8))]
2176    SetToFile(u16, u32),
2177
2178    /// Untested
2179    #[brw(magic(32u8))]
2180    SetToLine(u32),
2181
2182    /// Untested
2183    #[brw(magic(34u8))]
2184    IncrementLineNumber,
2185
2186    /// Untested
2187    #[brw(magic(36u8))]
2188    IncrementLineNumberByte(u8),
2189
2190    /// Untested
2191    #[brw(magic(38u8))]
2192    IncrementLineNumberWord(u32),
2193
2194    /// Untested
2195    #[brw(magic(40u8))]
2196    VeryLocalSymbol(LocalSymbol),
2197
2198    /// Untested
2199    #[brw(magic(42u8))]
2200    Set3ByteRegister(u16),
2201
2202    /// Set MX info.
2203    ///
2204    /// # Structure on Disk
2205    ///
2206    /// | Offset | Type        | Description              |
2207    /// |--------|-------------|--------------------------|
2208    /// | 0      | `u8`        | Magic: 0x2C              |
2209    /// | 1      | `SetMXInfo` | A [SetMXInfo] structure. |
2210    #[brw(magic(44u8))]
2211    SetMXInfo(SetMXInfo),
2212
2213    /// CPU type specification.
2214    ///
2215    /// # Structure on Disk
2216    ///
2217    /// | Offset | Type | Description       |
2218    /// |--------|------|-------------------|
2219    /// | 0      | `u8` | Magic: 0x2E       |
2220    /// | 1      | `u8` | A CPU identifier. |
2221    ///
2222    /// Constants for CPU identifiers can be found in the [cputype] module.
2223    #[brw(magic(46u8))]
2224    CPU(u8),
2225
2226    /// External BSS symbol.
2227    ///
2228    /// # Structure on Disk
2229    ///
2230    /// | Offset | Type | Description            |
2231    /// |--------|------|------------------------|
2232    /// | 0      | `u8` | Magic: 0x30            |
2233    /// | 1      | `XBSS` | An [XBSS] structure. |
2234    #[brw(magic(48u8))]
2235    XBSS(XBSS),
2236
2237    //
2238    // Source line debugger information
2239    //
2240    /// Increment line number.
2241    ///
2242    /// # Structure on Disk
2243    ///
2244    /// | Offset | Type  | Description                                  |
2245    /// |--------|-------|----------------------------------------------|
2246    /// | 0      | `u8`  | Magic: 0x32                                  |
2247    /// | 1      | `u16` | The offset where the new line number starts. |
2248    ///
2249    /// See also: [IncSLDLineNumByte](Self::IncSLDLineNumByte), [IncSLDLineNumWord](Self::IncSLDLineNumWord)
2250    #[brw(magic(50u8))]
2251    IncSLDLineNum(u16),
2252
2253    /// Increment line number by an amount.
2254    ///
2255    /// # Structure on Disk
2256    ///
2257    /// | Offset | Type  | Description                                  |
2258    /// |--------|-------|----------------------------------------------|
2259    /// | 0      | `u8`  | Magic: 0x34                                  |
2260    /// | 1      | `u16` | The offset where the new line number starts. |
2261    /// | 3      | `u8`  | The amount to increment the line number.     |
2262    ///
2263    /// See also [IncSLDLineNum](Self::IncSLDLineNum), [IncSLDLineNumWord](Self::IncSLDLineNumWord)
2264    #[brw(magic(52u8))]
2265    IncSLDLineNumByte(u16, u8),
2266
2267    /// Increment line number by an amount.
2268    ///
2269    /// # Structure on Disk
2270    ///
2271    /// | Offset | Type  | Description                                  |
2272    /// |--------|-------|----------------------------------------------|
2273    /// | 0      | `u8`  | Magic: 0x34                                  |
2274    /// | 1      | `u16` | The offset where the new line number starts. |
2275    /// | 3      | `u8`  | The amount to increment the line number.     |
2276    ///
2277    /// See also: [IncSLDLineNum](Self::IncSLDLineNum), [IncSLDLineNumByte](Self::IncSLDLineNumByte)
2278    #[brw(magic(54u8))]
2279    IncSLDLineNumWord(u16, u32),
2280
2281    /// Set line number.
2282    ///
2283    /// # Structure on Disk
2284    ///
2285    /// | Offset | Type            | Description                  |
2286    /// |--------|-----------------|------------------------------|
2287    /// | 0      | `u8`            | Magic: 0x38                  |
2288    /// | 1      | `SetSLDLineNum` | A [SetSLDLineNum] structure. |
2289    ///
2290    /// See also: [SetSLDLineNumFile](Self::SetSLDLineNumFile)
2291    #[brw(magic(56u8))]
2292    SetSLDLineNum(SetSLDLineNum),
2293
2294    /// Set line number with file name.
2295    ///
2296    /// # Structure on Disk
2297    ///
2298    /// | Offset | Type                | Description                      |
2299    /// |--------|---------------------|----------------------------------|
2300    /// | 0      | `u8`                | Magic: 0x3A                      |
2301    /// | 1      | `SetSLDLineNumFile` | A [SetSLDLineNumFile] structure. |
2302    ///
2303    /// See also: [SetSLDLineNum](Self::SetSLDLineNum)
2304    #[brw(magic(58u8))]
2305    SetSLDLineNumFile(SetSLDLineNumFile),
2306
2307    /// End of SLD info.
2308    ///
2309    /// # Structure on Disk
2310    ///
2311    /// | Offset | Type            | Description                 |
2312    /// |--------|-----------------|-----------------------------|
2313    /// | 0      | `u8`            | Magic: 0x3C                 |
2314    /// | 1      | `u16`           | Offset where SLD info ends. |
2315    #[brw(magic(60u8))]
2316    EndSLDInfo(u16),
2317
2318    /// Untested
2319    #[brw(magic(62u8))]
2320    RepeatByte(u32),
2321
2322    /// Untested
2323    #[brw(magic(64u8))]
2324    RepeatWord(u32),
2325
2326    /// Untested
2327    #[brw(magic(66u8), assert(unimplemented("ProcedureCall")))]
2328    RepeatLong(u32),
2329
2330    /// Untested
2331    #[brw(magic(68u8), assert(unimplemented("ProcedureCall")))]
2332    ProcedureCall(ProcedureCall),
2333
2334    /// Untested
2335    #[brw(magic(70u8), assert(unimplemented("ProcedureDefinition")))]
2336    ProcedureDefinition(ProcedureDefinition),
2337
2338    /// Untested
2339    #[brw(magic(72u8))]
2340    Repeat3Byte(u32),
2341
2342    //
2343    // Function and block debug information
2344    //
2345    /// Function start marker.
2346    ///
2347    /// # Structure on Disk
2348    ///
2349    /// | Offset | Type            | Description                  |
2350    /// |--------|-----------------|------------------------------|
2351    /// | 0      | `u8`            | Magic: 0x4A                  |
2352    /// | 1      | `FunctionStart` | A [FunctionStart] structure. |
2353    #[brw(magic(74u8))]
2354    FunctionStart(FunctionStart),
2355
2356    /// Function end marker.
2357    ///
2358    /// # Structure on Disk
2359    ///
2360    /// | Offset | Type                | Description                      |
2361    /// |--------|---------------------|----------------------------------|
2362    /// | 0      | `u8`                | Magic: 0x4C                      |
2363    /// | 1      | `SectionOffsetLine` | A [SectionOffsetLine] structure. |
2364    #[brw(magic(76u8))]
2365    FunctionEnd(SectionOffsetLine),
2366
2367    /// Block start marker.
2368    ///
2369    /// # Structure on Disk
2370    ///
2371    /// | Offset | Type                | Description                      |
2372    /// |--------|---------------------|----------------------------------|
2373    /// | 0      | `u8`                | Magic: 0x4E                      |
2374    /// | 1      | `SectionOffsetLine` | A [SectionOffsetLine] structure. |
2375    #[brw(magic(78u8))]
2376    BlockStart(SectionOffsetLine),
2377
2378    /// Block end marker.
2379    ///
2380    /// # Structure on Disk
2381    ///
2382    /// | Offset | Type                | Description                      |
2383    /// |--------|---------------------|----------------------------------|
2384    /// | 0      | `u8`                | Magic: 0x50                      |
2385    /// | 1      | `SectionOffsetLine` | A [SectionOffsetLine] structure. |
2386    #[brw(magic(80u8))]
2387    BlockEnd(SectionOffsetLine),
2388
2389    //
2390    // Type and variable definitions
2391    //
2392    /// Variable/type definition.
2393    ///
2394    /// # Structure on Disk
2395    ///
2396    /// | Offset | Type  | Description        |
2397    /// |--------|-------|--------------------|
2398    /// | 0      | `u8`  | Magic: 0x52        |
2399    /// | 1      | `Def` | A [Def] structure. |
2400    #[brw(magic(82u8))]
2401    Def(Def),
2402
2403    /// Extended definition with tag.
2404    ///
2405    /// # Structure on Disk
2406    ///
2407    /// | Offset | Type   | Description         |
2408    /// |--------|--------|---------------------|
2409    /// | 0      | `u8`   | Magic: 0x54         |
2410    /// | 1      | `Def2` | A [Def2] structure. |
2411    #[brw(magic(84u8))]
2412    Def2(Def2),
2413}
2414
2415/// Returns true if the LC_ALL or LANG environment variable indicates British English.
2416fn is_en_gb() -> bool {
2417    let lang = if let Ok(l) = std::env::var("LC_ALL") {
2418        l
2419    } else if let Ok(l) = std::env::var("LANG") {
2420        l
2421    } else {
2422        "".to_string()
2423    };
2424
2425    lang.starts_with("en_GB")
2426}
2427
2428impl fmt::Display for Section {
2429    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2430        self.fmt_with_options(f, &display::Options::default())
2431    }
2432}
2433
2434impl display::DisplayWithOptions for Section {
2435    fn fmt_with_options(&self, f: &mut fmt::Formatter, options: &display::Options) -> fmt::Result {
2436        options.write_indent(f)?;
2437        match self {
2438            Self::NOP => write!(f, "0 : End of file"),
2439            Self::Code(code) => {
2440                write!(f, "2 : Code {} bytes", code.code.len())?;
2441                match options.code_format {
2442                    display::CodeFormat::Disassembly => {
2443                        writeln!(f, "\n")?;
2444                        for instruction in code.code.chunks(4) {
2445                            if instruction.len() == 4 {
2446                                let ins = u32::from_le_bytes(instruction.try_into().unwrap());
2447                                let asm = Instruction::new(ins, 0x80000000, InstrCategory::CPU)
2448                                    .disassemble(None, 0);
2449                                options.write_indent(f)?;
2450                                writeln!(f, "    /* {ins:08x} */   {asm}")?;
2451                            } else {
2452                                write!(f, "    /* ")?;
2453                                for byte in instruction {
2454                                    write!(f, "{byte:02x}")?;
2455                                }
2456                                writeln!(f, " */ ; invalid")?;
2457                            }
2458                        }
2459                    }
2460                    display::CodeFormat::Hex => {
2461                        writeln!(f, "\n")?;
2462                        for (i, chunk) in code.code.chunks(16).enumerate() {
2463                            options.write_indent(f)?;
2464                            write!(f, "{:04x}:", i * 16)?;
2465                            for byte in chunk {
2466                                write!(f, " {:02x}", byte)?;
2467                            }
2468                            writeln!(f)?;
2469                        }
2470                    }
2471                    display::CodeFormat::None => (),
2472                }
2473                Ok(())
2474            }
2475            Self::RunAtOffset(section_id, offset) => {
2476                write!(f, "4 : Run at offset {offset:x} in {section_id:x}")
2477            }
2478            Self::SectionSwitch(section_id) => write!(f, "6 : Switch to section {section_id:x}"),
2479            Self::BSS(size) => {
2480                let uninit = if is_en_gb() {
2481                    "Uninitialised"
2482                } else {
2483                    "Uninitialized"
2484                };
2485                write!(f, "8 : {} data, {} bytes", uninit, size)
2486            }
2487            Self::Patch(patch) => write!(
2488                f,
2489                "10 : Patch type {} at offset {:x} with {}",
2490                patch.tag, patch.offset, patch.expression
2491            ),
2492            Self::XDEF(xdef) => write!(
2493                f,
2494                "12 : XDEF symbol number {:x} '{}' at offset {:x} in section {:x}",
2495                xdef.number,
2496                xdef.symbol_name(),
2497                xdef.offset,
2498                xdef.section
2499            ),
2500            Self::XREF(xref) => write!(
2501                f,
2502                "14 : XREF symbol number {:x} '{}'",
2503                xref.number,
2504                xref.symbol_name()
2505            ),
2506            Self::LNKHeader(section) => write!(
2507                f,
2508                "16 : Section symbol number {:x} '{}' in group {} alignment {}",
2509                section.section,
2510                section.type_name(),
2511                section.group,
2512                section.align
2513            ),
2514            Self::LocalSymbol(symbol) => write!(
2515                f,
2516                "18 : Local symbol '{}' at offset {:x} in section {:x}",
2517                symbol.name(),
2518                symbol.offset,
2519                symbol.section
2520            ),
2521            Self::GroupSymbol(symbol) => write!(
2522                f,
2523                "20 : Group symbol number {:x} `{}` type {}",
2524                symbol.number,
2525                symbol.name(),
2526                symbol.sym_type,
2527            ),
2528            Self::ByteSizeRegister(register) => {
2529                write!(f, "22 : Set byte size register to reg offset {register}",)
2530            }
2531            Self::WordSizeRegister(register) => {
2532                write!(f, "24 : Set word size register to reg offset {register}",)
2533            }
2534            Self::LongSizeRegister(register) => {
2535                write!(f, "26 : Set long size register to reg offset {register}",)
2536            }
2537            Self::Filename(filename) => write!(
2538                f,
2539                "28 : Define file number {:x} as \"{}\"",
2540                filename.number,
2541                filename.name()
2542            ),
2543            Self::SetToFile(file, line) => write!(f, "30 : Set to {file:x}, line {line}",),
2544            Self::SetToLine(line) => write!(f, "32 : Set to line {line}",),
2545            Self::IncrementLineNumber => write!(f, "34 : Increment line number",),
2546            Self::IncrementLineNumberByte(num) => write!(f, "36 : Increment line number by {num}",),
2547            Self::IncrementLineNumberWord(num) => write!(f, "38 : Increment line number by {num}",),
2548            Self::VeryLocalSymbol(symbol) => write!(
2549                f,
2550                "40 : Very local symbol '{}' at offset {:x} in section {:x}",
2551                symbol.name(),
2552                symbol.offset,
2553                symbol.section,
2554            ),
2555            Self::Set3ByteRegister(register) => {
2556                write!(f, "42 : Set 3-byte size register to reg offset {register}",)
2557            }
2558            Self::SetMXInfo(set_mx_info) => write!(
2559                f,
2560                "44 : Set MX info at offset {:x} to {:x}",
2561                set_mx_info.offset, set_mx_info.value,
2562            ),
2563            Self::CPU(cpu) => write!(f, "46 : Processor type {}", { *cpu }),
2564            Self::XBSS(xbss) => write!(
2565                f,
2566                "48 : XBSS symbol number {:x} '{}' size {:x} in section {:x}",
2567                xbss.number,
2568                xbss.name(),
2569                xbss.size,
2570                xbss.section
2571            ),
2572            Self::IncSLDLineNum(offset) => write!(f, "50 : Inc SLD linenum at offset {offset:x}"),
2573            Self::IncSLDLineNumByte(offset, byte) => write!(
2574                f,
2575                "52 : Inc SLD linenum by byte {byte} at offset {offset:x}"
2576            ),
2577            Self::IncSLDLineNumWord(offset, word) => write!(
2578                f,
2579                "54 : Inc SLD linenum by word {word} at offset {offset:x}"
2580            ),
2581            Self::SetSLDLineNum(line) => write!(
2582                f,
2583                "56 : Set SLD linenum to {} at offset {:x}",
2584                line.linenum, line.offset
2585            ),
2586            Self::SetSLDLineNumFile(line) => write!(
2587                f,
2588                "58 : Set SLD linenum to {} at offset {:x} in file {:x}",
2589                line.linenum, line.offset, line.file
2590            ),
2591            Self::EndSLDInfo(offset) => write!(f, "60 : End SLD info at offset {offset:x}"),
2592
2593            Self::RepeatByte(count) => write!(f, "62 : Repeat byte {count} times"),
2594            Self::RepeatWord(count) => write!(f, "64 : Repeat word {count} times"),
2595            Self::RepeatLong(count) => write!(f, "66 : Repeat long {count} times"),
2596            Self::ProcedureCall(call) => write!(f, "68 : <<<<Unimplemented>>>> {:?}", call),
2597            Self::ProcedureDefinition(definition) => {
2598                write!(f, "68 : <<<<Unimplemented>>>> {:?}", definition)
2599            }
2600            Self::Repeat3Byte(count) => write!(f, "70 : Repeat 3-byte {count} times"),
2601            Self::FunctionStart(start) => write!(
2602                f,
2603                "74 : Function start :\n\
2604                \x20 section {:04x}\n\
2605                \x20 offset ${:08x}\n\
2606                \x20 file {:04x}\n\
2607                \x20 start line {}\n\
2608                \x20 frame reg {}\n\
2609                \x20 frame size {}\n\
2610                \x20 return pc reg {}\n\
2611                \x20 mask ${:08x}\n\
2612                \x20 mask offset {}\n\
2613                \x20 name {}",
2614                start.section,
2615                start.offset,
2616                start.file,
2617                start.linenum,
2618                start.frame_register,
2619                start.frame_size,
2620                start.return_pc_register,
2621                start.mask,
2622                start.mask_offset,
2623                start.name()
2624            ),
2625            Self::FunctionEnd(end) => write!(
2626                f,
2627                "76 : Function end :\n\
2628                \x20 section {:04x}\n\
2629                \x20 offset ${:08x}\n\
2630                \x20 end line {}",
2631                end.section, end.offset, end.linenum
2632            ),
2633            // n.b.! the missing newline before section is intentional to match the output of OBJDUMP.EXE
2634            Self::BlockStart(start) => write!(
2635                f,
2636                "78 : Block start :\
2637                \x20 section {:04x}\n\
2638                \x20 offset ${:08x}\n\
2639                \x20 start line {}",
2640                start.section, start.offset, start.linenum
2641            ),
2642            Self::BlockEnd(end) => write!(
2643                f,
2644                "80 : Block end\n\
2645                \x20 section {:04x}\n\
2646                \x20 offset ${:08x}\n\
2647                \x20 end line {}",
2648                end.section, end.offset, end.linenum
2649            ),
2650            Self::Def(def) => write!(
2651                f,
2652                "82 : Def :\n\
2653                \x20 section {:04x}\n\
2654                \x20 value ${:08x}\n\
2655                \x20 class {}\n\
2656                \x20 type {}\n\
2657                \x20 size {}\n\
2658                \x20 name : {}",
2659                def.section,
2660                def.value,
2661                def.class,
2662                def.def_type,
2663                def.size,
2664                def.name()
2665            ),
2666            Self::Def2(def) => write!(
2667                f,
2668                "84 : Def2 :\n\
2669                \x20 section {:04x}\n\
2670                \x20 value ${:08x}\n\
2671                \x20 class {}\n\
2672                \x20 type {}\n\
2673                \x20 size {}\n\
2674                \x20 dims {} \n\
2675                \x20 tag {}\n\
2676                {}",
2677                def.section,
2678                def.value,
2679                def.class,
2680                def.def_type,
2681                def.size,
2682                def.dims,
2683                def.tag(),
2684                def.name()
2685            ),
2686        }
2687    }
2688}
2689
2690#[cfg(test)]
2691mod test {
2692    use std::ffi::OsStr;
2693    use std::time::UNIX_EPOCH;
2694
2695    use super::*;
2696    use binrw::io::Cursor;
2697    use binrw::{BinRead, BinWrite};
2698
2699    #[test]
2700    fn test_datetime() {
2701        let t: u32 = 0x813320af;
2702        let dt = NaiveDateTime::from_psyq_timestamp(t).expect("datetime");
2703        assert_eq!(dt.year_ce().1, 1996);
2704        assert_eq!(dt.month(), 5);
2705        assert_eq!(dt.day(), 15);
2706        assert_eq!(dt.hour(), 16);
2707        assert_eq!(dt.minute(), 9);
2708        assert_eq!(dt.second(), 38);
2709        assert_eq!(t, dt.to_psyq_timestamp());
2710        let st = SystemTime::from_psyq_timestamp(t).expect("systemtime");
2711        assert_eq!(
2712            832176578u64,
2713            st.duration_since(UNIX_EPOCH).expect("duration").as_secs()
2714        );
2715        assert_eq!(t, st.to_psyq_timestamp());
2716
2717        let t: u32 = 0x8d061f4c;
2718        let dt = NaiveDateTime::from_psyq_timestamp(t).expect("datetime");
2719        assert_eq!(dt.year_ce().1, 1995);
2720        assert_eq!(dt.month(), 10);
2721        assert_eq!(dt.day(), 12);
2722        assert_eq!(dt.hour(), 17);
2723        assert_eq!(dt.minute(), 40);
2724        assert_eq!(dt.second(), 12);
2725        assert_eq!(t, dt.to_psyq_timestamp());
2726        let st = SystemTime::from_psyq_timestamp(t).expect("systemtime");
2727        assert_eq!(
2728            813519612u64,
2729            st.duration_since(UNIX_EPOCH).expect("duration").as_secs()
2730        );
2731        assert_eq!(t, st.to_psyq_timestamp());
2732    }
2733
2734    #[test]
2735    fn test_path_to_module_name() {
2736        assert_eq!(
2737            *b"OUTPUT  ",
2738            path_to_module_name(Path::new("some/output.obj"))
2739        );
2740        assert_eq!(
2741            *b"LONGNAME",
2742            path_to_module_name(Path::new("some/longname.obj"))
2743        );
2744        // name is truncated to 8 characters
2745        assert_eq!(
2746            *b"LONGERNA",
2747            path_to_module_name(Path::new("some/longername.obj"))
2748        );
2749        // strings with code points that fit into 8-bytes are "fine"
2750        let name: [u8; 8] = "👾    ".as_bytes().try_into().unwrap();
2751        assert_eq!(name, path_to_module_name(Path::new("some/👾.obj")));
2752        // strings with code points that are split are not
2753        let name: [u8; 8] = "👾☕ ".as_bytes().try_into().unwrap();
2754        assert_eq!(name, path_to_module_name(Path::new("some/👾☕☕.obj")));
2755        // all 8-bytes consumed by multi-byte
2756        let name: [u8; 8] = "👾👾".as_bytes().try_into().unwrap();
2757        assert_eq!(name, path_to_module_name(Path::new("some/👾👾.obj")));
2758        // diacritics
2759        let name: [u8; 8] = "AÍ¢B    ".as_bytes().try_into().unwrap();
2760        assert_eq!(name, path_to_module_name(Path::new("some/a͢b.obj")));
2761    }
2762
2763    #[test]
2764    #[should_panic]
2765    fn test_path_to_module_name_missing_file_name() {
2766        path_to_module_name(Path::new("."));
2767    }
2768
2769    #[test]
2770    #[should_panic]
2771    fn test_path_to_module_name_invalid_unicode() {
2772        // b"\u{C0}invalid.obj"
2773        let s: &OsStr;
2774        unsafe {
2775            s = OsStr::from_encoded_bytes_unchecked(&[
2776                0xC0, 0x69, 0x6E, 0x76, 0x61, 0x6C, 0x69, 0x64, 0x2e, 0x6f, 0x62, 0x6a,
2777            ]);
2778        }
2779        path_to_module_name(Path::new(s));
2780    }
2781
2782    #[test]
2783    fn test_lib() {
2784        let bytes = b"\
2785           \x4C\x49\x42\x01\x41\x35\x36\x20\x20\x20\x20\x20\xAF\x20\x2C\x81\
2786           \x1A\x00\x00\x00\x8E\x00\x00\x00\x04\x65\x78\x69\x74\x00\x4C\x4E\
2787           \x4B\x02\x2E\x07\x10\x04\xF0\x00\x00\x08\x06\x2E\x72\x64\x61\x74\
2788           \x61\x10\x00\xF0\x00\x00\x08\x05\x2E\x74\x65\x78\x74\x10\x01\xF0\
2789           \x00\x00\x08\x05\x2E\x64\x61\x74\x61\x10\x03\xF0\x00\x00\x08\x06\
2790           \x2E\x73\x64\x61\x74\x61\x10\x05\xF0\x00\x00\x08\x04\x2E\x62\x73\
2791           \x73\x10\x02\xF0\x00\x00\x08\x05\x2E\x73\x62\x73\x73\x0C\x01\x00\
2792           \x00\xF0\x00\x00\x00\x00\x04\x65\x78\x69\x74\x06\x00\xF0\x02\x10\
2793           \x00\xB0\x00\x0A\x24\x08\x00\x40\x01\x38\x00\x09\x24\x00\x00\x00\
2794           \x00\x00"
2795            .to_vec();
2796        //.0.  1.  2.  3.  4.  5.  6.  7.  8.  9.  A.  B.  C.  D.  E.  F.
2797        let mut data = Cursor::new(&bytes);
2798        let lib = LIB::read(&mut data).unwrap();
2799        assert_eq!(lib.version, 1);
2800        // assert_eq!(lib.modules().len(), 1);
2801
2802        let module = lib.modules().first().expect("modules[0]");
2803        assert_eq!(module.obj.version(), 2);
2804        assert_eq!(module.name(), "A56");
2805        assert_eq!(module.metadata.created, 2167152815);
2806        assert_eq!(module.metadata.offset, 26);
2807        assert_eq!(module.metadata.size, 142);
2808        assert_eq!(module.metadata.exports.len(), 2);
2809        assert_eq!(module.exports().len(), 1);
2810
2811        let export = module
2812            .metadata
2813            .exports
2814            .first()
2815            .expect("modules[0].exports[0]");
2816        assert_eq!(export.name_size, 4);
2817        assert_eq!(export.name(), "exit");
2818
2819        let lnk = &module.obj;
2820        assert_eq!(lnk.version, 2);
2821
2822        let Section::CPU(cpu) = lnk.sections.first().expect("module[0].obj.sections[0]") else {
2823            unreachable!();
2824        };
2825        assert_eq!(*cpu, cputype::MIPS_R3000);
2826        /*
2827                assert_eq!(section.section, 61444);
2828                assert_eq!(section.group, 0);
2829                assert_eq!(section.align, 8);
2830                assert_eq!(section.type_name_size, 6);
2831                assert_eq!(section.type_name(), ".rdata");
2832        */
2833
2834        assert_eq!(data.position(), bytes.len() as u64);
2835
2836        // roundtrip
2837        let mut writer = Cursor::new(Vec::new());
2838        lib.write_le(&mut writer).unwrap();
2839        assert_eq!(writer.into_inner(), bytes);
2840    }
2841
2842    #[test]
2843    fn test_object_entry() {
2844        let bytes = b"\
2845            \x53\x50\x52\x49\x4E\x54\x46\x20\xAF\x20\x33\x81\x1D\x00\x00\x00\
2846            \x25\x0E\x00\x00\x07\x73\x70\x72\x69\x6E\x74\x66\x00\x4C\x4E\x4B\
2847            \x02\x2E\x07\x10\x01\x00\x00\x00\x08\x06\x2E\x72\x64\x61\x74\x61\
2848            \x10\x02\x00\x00\x00\x08\x05\x2E\x74\x65\x78\x74\x10\x03\x00\x00\
2849            \x00\x08\x05\x2E\x64\x61\x74\x61\x10\x04\x00\x00\x00\x08\x06\x2E\
2850            \x73\x64\x61\x74\x61\x10\x05\x00\x00\x00\x08\x05\x2E\x73\x62\x73\
2851            \x73\x10\x06\x00\x00\x00\x08\x04\x2E\x62\x73\x73\x10\x07\x00\x00\
2852            \x00\x08\x06\x2E\x63\x74\x6F\x72\x73\x10\x08\x00\x00\x00\x08\x06\
2853            \x2E\x64\x74\x6F\x72\x73\x1C\x09\x00\x17\x43\x3A\x5C\x50\x53\x58\
2854            \x5C\x53\x52\x43\x5C\x43\x32\x5C\x53\x50\x52\x49\x4E\x54\x46\x2E\
2855            \x43\x06\x02\x00\x06\x03\x00\x02\x01\x00\x00\x08\x0B\x00\x00\x00\
2856            \x06\x01\x00\x02\x25\x00\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\
2857            \x41\x42\x43\x44\x45\x46\x00\x00\x00\x00\x30\x31\x32\x33\x34\x35\
2858            \x36\x37\x38\x39\x61\x62\x63\x64\x65\x66\x00\x06\x02\x00\x02\xC8\
2859            \x02\x04\x00\xA5\xAF\x08\x00\xA6\xAF\x0C\x00\xA7\xAF\xB8\xFD\xBD\
2860            \x27\x34\x02\xB3\xAF\x21\x98\x80\x00\x50\x02\xA2\x27\x44\x02\xBF\
2861            \xAF\x40\x02\xB6\xAF\x3C\x02\xB5\xAF\x38\x02\xB4\xAF\x30\x02\xB2\
2862            \xAF\x2C\x02\xB1\xAF\x28\x02\xB0\xAF\x4C\x02\xA5\xAF\x20\x02\xA2\
2863            \xAF\x00\x00\xA5\x90\x00\x00\x00\x00\xF6\x01\xA0\x10\x21\x90\x00\
2864            \x00\x2D\x00\x16\x34\x2B\x00\x15\x34\x20\x00\x14\x34\x25\x00\x02\
2865            \x34\xC0\x01\xA2\x14\x21\x10\x72\x02\x00\x00\x05\x3C\x00\x00\xA5\
2866            \x24\x00\x00\xA2\x8C\x04\x00\xA3\x8C\x08\x00\xA4\x8C\x10\x02\xA2\
2867            \xAF\x14\x02\xA3\xAF\x18\x02\xA4\xAF\x23\x00\x06\x34\x30\x00\x03\
2868            \x34\x4C\x02\xA4\x8F\x00\x00\x00\x00\x01\x00\x82\x24\x4C\x02\xA2\
2869            \xAF\x01\x00\x85\x90\x00\x00\x00\x00\x06\x00\xB6\x14\x00\x00\x00\
2870            \x00\x10\x02\xA2\x8F\x00\x00\x00\x00\x01\x00\x42\x34\x00\x00\x00\
2871            \x08\x10\x02\xA2\xAF\x06\x00\xB5\x14\x00\x00\x00\x00\x10\x02\xA2\
2872            \x8F\x00\x00\x00\x00\x02\x00\x42\x34\x00\x00\x00\x08\x10\x02\xA2\
2873            \xAF\x03\x00\xB4\x14\x00\x00\x00\x00\x00\x00\x00\x08\x11\x02\xA5\
2874            \xA3\x06\x00\xA6\x14\x00\x00\x00\x00\x10\x02\xA2\x8F\x00\x00\x00\
2875            \x00\x04\x00\x42\x34\x00\x00\x00\x08\x10\x02\xA2\xAF\x06\x00\xA3\
2876            \x14\x2A\x00\x02\x34\x10\x02\xA2\x8F\x00\x00\x00\x00\x08\x00\x42\
2877            \x34\x00\x00\x00\x08\x10\x02\xA2\xAF\x22\x00\xA2\x14\xD0\xFF\xA2\
2878            \x24\x20\x02\xA3\x8F\x00\x00\x00\x00\x04\x00\x62\x24\x20\x02\xA2\
2879            \xAF\x00\x00\x62\x8C\x00\x00\x00\x00\x06\x00\x41\x04\x14\x02\xA2\
2880            \xAF\x10\x02\xA3\x8F\x23\x10\x02\x00\x14\x02\xA2\xAF\x01\x00\x63\
2881            \x34\x10\x02\xA3\xAF\x02\x00\x82\x24\x4C\x02\xA2\xAF\x02\x00\x85\
2882            \x90\x00\x00\x00\x08\x2E\x00\x02\x34\x14\x02\xA3\x8F\x00\x00\x00\
2883            \x00\x80\x10\x03\x00\x21\x10\x43\x00\x40\x10\x02\x00\xD0\xFF\x42\
2884            \x24\x21\x10\x45\x00\x14\x02\xA2\xAF\x4C\x02\xA3\x8F\x00\x00\x00\
2885            \x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\x01\x00\x65\x90\x00\x00\x00\
2886            \x00\xD0\xFF\xA2\x24\x0A\x00\x42\x2C\xEF\xFF\x40\x14\x2E\x00\x02\
2887            \x34\x2F\x00\xA2\x14\x00\x00\x00\x00\x4C\x02\xA4\x8F\x00\x00\x00\
2888            \x00\x01\x00\x82\x24\x4C\x02\xA2\xAF\x01\x00\x85\x90\x2A\x00\x02\
2889            \x34\x1C\x00\xA2\x14\xD0\xFF\xA2\x24\x20\x02\xA3\x8F\x00\x00\x00\
2890            \x00\x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x62\x8C\x00\x00\x00\
2891            \x00\x18\x02\xA2\xAF\x02\x00\x82\x24\x4C\x02\xA2\xAF\x02\x00\x85\
2892            \x90\x00\x00\x00\x08\x00\x00\x00\x00\x18\x02\xA3\x8F\x00\x00\x00\
2893            \x00\x80\x10\x03\x00\x21\x10\x43\x00\x40\x10\x02\x00\xD0\xFF\x42\
2894            \x24\x21\x10\x45\x00\x18\x02\xA2\xAF\x4C\x02\xA3\x8F\x00\x00\x00\
2895            \x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\x01\x00\x65\x90\x00\x00\x00\
2896            \x00\xD0\xFF\xA2\x24\x0A\x00\x42\x2C\xEF\xFF\x40\x14\x00\x00\x00\
2897            \x00\x18\x02\xA2\x8F\x00\x00\x00\x00\x05\x00\x40\x04\x00\x00\x00\
2898            \x00\x10\x02\xA2\x8F\x00\x00\x00\x00\x10\x00\x42\x34\x10\x02\xA2\
2899            \xAF\x10\x02\xA3\x8F\x00\x00\x00\x00\x01\x00\x62\x30\x04\x00\x40\
2900            \x10\x10\x02\xB1\x27\xF7\xFF\x02\x24\x24\x10\x62\x00\x10\x02\xA2\
2901            \xAF\xB4\xFF\xA3\x24\x2D\x00\x62\x2C\x2B\x01\x40\x10\x80\x10\x03\
2902            \x00\x00\x00\x01\x3C\x21\x08\x22\x00\x00\x00\x22\x8C\x00\x00\x00\
2903            \x00\x08\x00\x40\x00\x00\x00\x00\x00\x0A\x52\x68\x00\x2C\x04\x03\
2904            \x00\x00\x00\x00\x00\x00\x0A\x54\x6C\x00\x2C\x04\x03\x00\x00\x00\
2905            \x00\x00\x00\x0A\x4A\xBC\x00\x2C\x04\x02\x00\x00\x90\x00\x00\x00\
2906            \x0A\x4A\xD8\x00\x2C\x04\x02\x00\x00\x90\x00\x00\x00\x0A\x4A\xE8\
2907            \x00\x2C\x04\x02\x00\x00\x90\x00\x00\x00\x0A\x4A\x04\x01\x2C\x04\
2908            \x02\x00\x00\x90\x00\x00\x00\x0A\x4A\x20\x01\x2C\x04\x02\x00\x00\
2909            \x90\x00\x00\x00\x0A\x4A\x70\x01\x2C\x04\x02\x00\x00\xC0\x01\x00\
2910            \x00\x0A\x4A\x10\x02\x2C\x04\x02\x00\x00\x60\x02\x00\x00\x0A\x52\
2911            \xB0\x02\x2C\x04\x01\x00\x00\x28\x00\x00\x00\x0A\x54\xB8\x02\x2C\
2912            \x04\x01\x00\x00\x28\x00\x00\x00\x06\x01\x00\x02\xB7\x00\x00\x00\
2913            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2914            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2915            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2916            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2917            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2918            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2919            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2920            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2921            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2922            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2923            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2924            \x00\x00\x00\x00\x00\x0A\x10\x03\x00\x2C\x04\x02\x00\x00\xE0\x02\
2925            \x00\x00\x0A\x10\x07\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\
2926            \x10\x0B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x0F\x00\
2927            \x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x13\x00\x2C\x04\x02\
2928            \x00\x00\x58\x07\x00\x00\x0A\x10\x17\x00\x2C\x04\x02\x00\x00\x58\
2929            \x07\x00\x00\x0A\x10\x1B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\
2930            \x0A\x10\x1F\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x23\
2931            \x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x27\x00\x2C\x04\
2932            \x02\x00\x00\x58\x07\x00\x00\x0A\x10\x2B\x00\x2C\x04\x02\x00\x00\
2933            \x58\x07\x00\x00\x0A\x10\x2F\x00\x2C\x04\x02\x00\x00\x58\x07\x00\
2934            \x00\x0A\x10\x33\x00\x2C\x04\x02\x00\x00\x78\x05\x00\x00\x0A\x10\
2935            \x37\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x3B\x00\x2C\
2936            \x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x3F\x00\x2C\x04\x02\x00\
2937            \x00\x58\x07\x00\x00\x0A\x10\x43\x00\x2C\x04\x02\x00\x00\x58\x07\
2938            \x00\x00\x0A\x10\x47\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\
2939            \x10\x4B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x4F\x00\
2940            \x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x53\x00\x2C\x04\x02\
2941            \x00\x00\x58\x07\x00\x00\x0A\x10\x57\x00\x2C\x04\x02\x00\x00\x58\
2942            \x07\x00\x00\x0A\x10\x5B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\
2943            \x0A\x10\x5F\x00\x2C\x04\x02\x00\x00\x80\x06\x00\x00\x0A\x10\x63\
2944            \x00\x2C\x04\x02\x00\x00\x0C\x03\x00\x00\x0A\x10\x67\x00\x2C\x04\
2945            \x02\x00\x00\x58\x07\x00\x00\x0A\x10\x6B\x00\x2C\x04\x02\x00\x00\
2946            \x58\x07\x00\x00\x0A\x10\x6F\x00\x2C\x04\x02\x00\x00\x58\x07\x00\
2947            \x00\x0A\x10\x73\x00\x2C\x04\x02\x00\x00\xC8\x02\x00\x00\x0A\x10\
2948            \x77\x00\x2C\x04\x02\x00\x00\x0C\x03\x00\x00\x0A\x10\x7B\x00\x2C\
2949            \x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x7F\x00\x2C\x04\x02\x00\
2950            \x00\x58\x07\x00\x00\x0A\x10\x83\x00\x2C\x04\x02\x00\x00\xD4\x02\
2951            \x00\x00\x0A\x10\x87\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\
2952            \x10\x8B\x00\x2C\x04\x02\x00\x00\x24\x07\x00\x00\x0A\x10\x8F\x00\
2953            \x2C\x04\x02\x00\x00\x74\x04\x00\x00\x0A\x10\x93\x00\x2C\x04\x02\
2954            \x00\x00\x64\x05\x00\x00\x0A\x10\x97\x00\x2C\x04\x02\x00\x00\x58\
2955            \x07\x00\x00\x0A\x10\x9B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\
2956            \x0A\x10\x9F\x00\x2C\x04\x02\x00\x00\xA0\x06\x00\x00\x0A\x10\xA3\
2957            \x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\xA7\x00\x2C\x04\
2958            \x02\x00\x00\x5C\x03\x00\x00\x0A\x10\xAB\x00\x2C\x04\x02\x00\x00\
2959            \x58\x07\x00\x00\x0A\x10\xAF\x00\x2C\x04\x02\x00\x00\x58\x07\x00\
2960            \x00\x0A\x10\xB3\x00\x2C\x04\x02\x00\x00\x88\x05\x00\x00\x06\x02\
2961            \x00\x02\x94\x05\x10\x02\xA2\x8F\x00\x00\x00\x08\x20\x00\x42\x34\
2962            \x10\x02\xA2\x8F\x00\x00\x00\x08\x40\x00\x42\x34\x10\x02\xA2\x8F\
2963            \x00\x00\x00\x00\x80\x00\x42\x34\x10\x02\xA2\xAF\x4C\x02\xA3\x8F\
2964            \x00\x00\x00\x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\x01\x00\x65\x90\
2965            \x00\x00\x00\x08\xB4\xFF\xA3\x24\x20\x02\xA3\x8F\x00\x00\x00\x00\
2966            \x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x64\x8C\x10\x02\xA3\x8F\
2967            \x00\x00\x00\x00\x20\x00\x62\x30\x02\x00\x40\x10\x00\x14\x04\x00\
2968            \x03\x24\x02\x00\x04\x00\x81\x04\x02\x00\x62\x30\x23\x20\x04\x00\
2969            \x00\x00\x00\x08\x11\x02\xB6\xA3\x0E\x00\x40\x10\x00\x00\x00\x00\
2970            \x00\x00\x00\x08\x11\x02\xB5\xA3\x20\x02\xA3\x8F\x00\x00\x00\x00\
2971            \x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x64\x8C\x10\x02\xA2\x8F\
2972            \x00\x00\x00\x00\x20\x00\x42\x30\x02\x00\x40\x10\x11\x02\xA0\xA3\
2973            \xFF\xFF\x84\x30\x10\x02\xA3\x8F\x00\x00\x00\x00\x10\x00\x62\x30\
2974            \x0F\x00\x40\x14\x08\x00\x62\x30\x08\x00\x40\x10\x00\x00\x00\x00\
2975            \x14\x02\xA3\x8F\x11\x02\xA2\x93\x00\x00\x00\x00\x03\x00\x40\x10\
2976            \x18\x02\xA3\xAF\xFF\xFF\x62\x24\x18\x02\xA2\xAF\x18\x02\xA2\x8F\
2977            \x00\x00\x00\x00\x02\x00\x40\x1C\x01\x00\x02\x34\x18\x02\xA2\xAF\
2978            \x10\x00\x80\x10\x21\x80\x00\x00\xCC\xCC\x05\x3C\xCD\xCC\xA5\x34\
2979            \x19\x00\x85\x00\xFF\xFF\x31\x26\x01\x00\x10\x26\x10\x18\x00\x00\
2980            \xC2\x18\x03\x00\x80\x10\x03\x00\x21\x10\x43\x00\x40\x10\x02\x00\
2981            \x23\x10\x82\x00\x30\x00\x42\x24\x21\x20\x60\x00\xF4\xFF\x80\x14\
2982            \x00\x00\x22\xA2\x18\x02\xA2\x8F\x00\x00\x00\x00\x2A\x10\x02\x02\
2983            \x0A\x00\x40\x10\x00\x00\x00\x00\x30\x00\x03\x34\xFF\xFF\x31\x26\
2984            \x00\x00\x23\xA2\x18\x02\xA2\x8F\x01\x00\x10\x26\x2A\x10\x02\x02\
2985            \xFB\xFF\x40\x14\xFF\xFF\x31\x26\x01\x00\x31\x26\x11\x02\xA2\x93\
2986            \x00\x00\x00\x00\xC5\x00\x40\x10\x00\x00\x00\x00\xFF\xFF\x31\x26\
2987            \x11\x02\xA2\x93\x01\x00\x10\x26\x00\x00\x00\x08\x00\x00\x22\xA2\
2988            \x20\x02\xA3\x8F\x00\x00\x00\x00\x04\x00\x62\x24\x20\x02\xA2\xAF\
2989            \x00\x00\x64\x8C\x10\x02\xA3\x8F\x00\x00\x00\x00\x20\x00\x62\x30\
2990            \x02\x00\x40\x10\x10\x00\x62\x30\xFF\xFF\x84\x30\x0B\x00\x40\x14\
2991            \x08\x00\x62\x30\x04\x00\x40\x10\x00\x00\x00\x00\x14\x02\xA2\x8F\
2992            \x00\x00\x00\x00\x18\x02\xA2\xAF\x18\x02\xA2\x8F\x00\x00\x00\x00\
2993            \x02\x00\x40\x1C\x01\x00\x02\x34\x18\x02\xA2\xAF\x08\x00\x80\x10\
2994            \x21\x80\x00\x00\xFF\xFF\x31\x26\x07\x00\x82\x30\x30\x00\x42\x24\
2995            \x00\x00\x22\xA2\xC2\x20\x04\x00\xFA\xFF\x80\x14\x01\x00\x10\x26\
2996            \x10\x02\xA2\x8F\x00\x00\x00\x00\x04\x00\x42\x30\x0A\x00\x40\x10\
2997            \x00\x00\x00\x00\x08\x00\x00\x12\x30\x00\x02\x34\x00\x00\x23\x92\
2998            \x00\x00\x00\x00\x04\x00\x62\x10\x30\x00\x02\x34\xFF\xFF\x31\x26\
2999            \x00\x00\x22\xA2\x01\x00\x10\x26\x18\x02\xA2\x8F\x00\x00\x00\x00\
3000            \x2A\x10\x02\x02\x8D\x00\x40\x10\x30\x00\x03\x34\xFF\xFF\x31\x26\
3001            \x00\x00\x23\xA2\x18\x02\xA2\x8F\x01\x00\x10\x26\x2A\x10\x02\x02\
3002            \xFB\xFF\x40\x14\xFF\xFF\x31\x26\x00\x00\x00\x08\x01\x00\x31\x26\
3003            \x10\x02\xA3\x8F\x08\x00\x02\x34\x18\x02\xA2\xAF\x50\x00\x63\x34\
3004            \x10\x02\xA3\xAF\x00\x00\x07\x3C\x00\x00\xE7\x24\x00\x00\x00\x08\
3005            \x00\x00\x00\x00\x00\x00\x07\x3C\x00\x00\xE7\x24\x20\x02\xA3\x8F\
3006            \x00\x00\x00\x00\x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x64\x8C\
3007            \x10\x02\xA3\x8F\x00\x00\x00\x00\x20\x00\x62\x30\x02\x00\x40\x10\
3008            \x10\x00\x62\x30\xFF\xFF\x84\x30\x0D\x00\x40\x14\x08\x00\x62\x30\
3009            \x06\x00\x40\x10\x04\x00\x62\x30\x14\x02\xA6\x8F\x03\x00\x40\x10\
3010            \x18\x02\xA6\xAF\xFE\xFF\xC2\x24\x18\x02\xA2\xAF\x18\x02\xA2\x8F\
3011            \x00\x00\x00\x00\x02\x00\x40\x1C\x01\x00\x02\x34\x18\x02\xA2\xAF\
3012            \x09\x00\x80\x10\x21\x80\x00\x00\xFF\xFF\x31\x26\x0F\x00\x82\x30\
3013            \x02\x21\x04\x00\x21\x10\xE2\x00\x00\x00\x42\x90\x01\x00\x10\x26\
3014            \xF9\xFF\x80\x14\x00\x00\x22\xA2\x18\x02\xA2\x8F\x00\x00\x00\x00\
3015            \x2A\x10\x02\x02\x0A\x00\x40\x10\x00\x00\x00\x00\x30\x00\x03\x34\
3016            \xFF\xFF\x31\x26\x00\x00\x23\xA2\x18\x02\xA2\x8F\x01\x00\x10\x26\
3017            \x2A\x10\x02\x02\xFB\xFF\x40\x14\xFF\xFF\x31\x26\x01\x00\x31\x26\
3018            \x10\x02\xA2\x8F\x00\x00\x00\x00\x04\x00\x42\x30\x43\x00\x40\x10\
3019            \x30\x00\x02\x34\xFF\xFF\x31\x26\x00\x00\x25\xA2\xFF\xFF\x31\x26\
3020            \x02\x00\x10\x26\x00\x00\x00\x08\x00\x00\x22\xA2\x20\x02\xA2\x8F\
3021            \xFF\xFF\x31\x26\x04\x00\x43\x24\x20\x02\xA3\xAF\x00\x00\x42\x90\
3022            \x01\x00\x10\x34\x00\x00\x00\x08\x00\x00\x22\xA2\x20\x02\xA2\x8F\
3023            \x00\x00\x00\x00\x04\x00\x43\x24\x20\x02\xA3\xAF\x10\x02\xA3\x8F\
3024            \x00\x00\x51\x8C\x04\x00\x62\x30\x0B\x00\x40\x10\x10\x00\x62\x30\
3025            \x00\x00\x30\x92\x29\x00\x40\x10\x01\x00\x31\x26\x18\x02\xA3\x8F\
3026            \x00\x00\x00\x00\x2A\x10\x70\x00\x24\x00\x40\x10\x00\x00\x00\x00\
3027            \x00\x00\x00\x08\x21\x80\x60\x00\x05\x00\x40\x14\x21\x20\x20\x02\
3028            \x00\x00\x00\x0C\x21\x20\x20\x02\x00\x00\x00\x08\x21\x80\x40\x00\
3029            \x18\x02\xA6\x8F\x00\x00\x00\x0C\x21\x28\x00\x00\x17\x00\x40\x14\
3030            \x23\x80\x51\x00\x18\x02\xB0\x8F\x00\x00\x00\x08\x00\x00\x00\x00\
3031            \x20\x02\xA2\x8F\x00\x00\x00\x00\x04\x00\x43\x24\x20\x02\xA3\xAF\
3032            \x10\x02\xA3\x8F\x00\x00\x51\x8C\x20\x00\x62\x30\x03\x00\x40\x10\
3033            \x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x32\xA6\x00\x00\x00\x08\
3034            \x00\x00\x32\xAE\x25\x00\x02\x34\x31\x00\xA2\x14\x21\x10\x72\x02\
3035            \x00\x00\x45\xA0\x00\x00\x00\x08\x01\x00\x52\x26\x14\x02\xA2\x8F\
3036            \x00\x00\x00\x00\x2A\x10\x02\x02\x11\x00\x40\x10\x21\x20\x72\x02\
3037            \x10\x02\xA2\x8F\x00\x00\x00\x00\x01\x00\x42\x30\x0D\x00\x40\x14\
3038            \x21\x28\x20\x02\x21\x18\x53\x02\x00\x00\x74\xA0\x01\x00\x63\x24\
3039            \x14\x02\xA2\x8F\x00\x00\x00\x00\xFF\xFF\x42\x24\x14\x02\xA2\xAF\
3040            \x2A\x10\x02\x02\xF8\xFF\x40\x14\x01\x00\x52\x26\x21\x20\x72\x02\
3041            \x21\x28\x20\x02\x00\x00\x00\x0C\x21\x30\x00\x02\x14\x02\xA2\x8F\
3042            \x00\x00\x00\x00\x2A\x10\x02\x02\x09\x00\x40\x10\x21\x90\x50\x02\
3043            \x21\x18\x53\x02\x00\x00\x74\xA0\x01\x00\x63\x24\x14\x02\xA2\x8F\
3044            \x01\x00\x10\x26\x2A\x10\x02\x02\xFA\xFF\x40\x14\x01\x00\x52\x26\
3045            \x4C\x02\xA3\x8F\x00\x00\x00\x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\
3046            \x01\x00\x65\x90\x00\x00\x00\x00\x10\xFE\xA0\x14\x25\x00\x02\x34\
3047            \x21\x10\x72\x02\x00\x00\x40\xA0\x21\x10\x40\x02\x44\x02\xBF\x8F\
3048            \x40\x02\xB6\x8F\x3C\x02\xB5\x8F\x38\x02\xB4\x8F\x34\x02\xB3\x8F\
3049            \x30\x02\xB2\x8F\x2C\x02\xB1\x8F\x28\x02\xB0\x8F\x48\x02\xBD\x27\
3050            \x08\x00\xE0\x03\x00\x00\x00\x00\x0A\x4A\x04\x00\x2C\x04\x02\x00\
3051            \x00\xEC\x02\x00\x00\x0A\x4A\x10\x00\x2C\x04\x02\x00\x00\xEC\x02\
3052            \x00\x00\x0A\x4A\x3C\x00\x2C\x04\x02\x00\x00\xA4\x02\x00\x00\x0A\
3053            \x4A\x7C\x00\x2C\x04\x02\x00\x00\x88\x03\x00\x00\x0A\x4A\x8C\x00\
3054            \x2C\x04\x02\x00\x00\x88\x03\x00\x00\x0A\x4A\xA4\x01\x2C\x04\x02\
3055            \x00\x00\x70\x07\x00\x00\x0A\x4A\x94\x02\x2C\x04\x02\x00\x00\x70\
3056            \x07\x00\x00\x0A\x52\xB0\x02\x2C\x04\x01\x00\x00\x00\x00\x00\x00\
3057            \x0A\x54\xB4\x02\x2C\x04\x01\x00\x00\x00\x00\x00\x00\x0A\x4A\xB8\
3058            \x02\x2C\x04\x02\x00\x00\x90\x05\x00\x00\x0A\x52\xC0\x02\x2C\x04\
3059            \x01\x00\x00\x14\x00\x00\x00\x0A\x54\xC4\x02\x2C\x04\x01\x00\x00\
3060            \x14\x00\x00\x00\x0A\x4A\xB0\x03\x2C\x04\x02\x00\x00\x70\x07\x00\
3061            \x00\x0A\x4A\xD0\x03\x2C\x04\x02\x00\x00\x70\x07\x00\x00\x0A\x4A\
3062            \x1C\x04\x2C\x04\x02\x00\x00\x70\x07\x00\x00\x0A\x4A\x2C\x04\x02\
3063            \x0B\x00\x0A\x4A\x34\x04\x2C\x04\x02\x00\x00\x70\x07\x00\x00\x0A\
3064            \x4A\x40\x04\x02\x0C\x00\x0A\x4A\x54\x04\x2C\x04\x02\x00\x00\x70\
3065            \x07\x00\x00\x0A\x4A\x80\x04\x2C\x04\x02\x00\x00\x04\x08\x00\x00\
3066            \x0A\x4A\x88\x04\x2C\x04\x02\x00\x00\x04\x08\x00\x00\x0A\x4A\xA0\
3067            \x04\x2C\x04\x02\x00\x00\x04\x08\x00\x00\x0A\x4A\x00\x05\x02\x0D\
3068            \x00\x06\x02\x00\x0C\x0A\x00\x02\x00\x00\x00\x00\x00\x07\x73\x70\
3069            \x72\x69\x6E\x74\x66\x0E\x0C\x00\x06\x6D\x65\x6D\x63\x68\x72\x0E\
3070            \x0B\x00\x06\x73\x74\x72\x6C\x65\x6E\x0E\x0D\x00\x07\x6D\x65\x6D\
3071            \x6D\x6F\x76\x65\x00"
3072            .to_vec();
3073        //.0.  1.  2.  3.  4.  5.  6.  7.  8.  9.  A.  B.  C.  D.  E.  F.
3074        let mut data = Cursor::new(&bytes);
3075        let obj = Module::read(&mut data).unwrap();
3076
3077        eprintln!("obj: {:?}", obj);
3078
3079        assert_eq!(obj.name(), "SPRINTF");
3080        // assert_eq!(obj.created, 2167611567);
3081        // TODO: this should be based on locale
3082        assert_eq!(obj.created(), "15-05-96 16:09:38");
3083        assert_eq!(obj.metadata.offset, 29);
3084        assert_eq!(obj.metadata.size, 3621);
3085        assert_eq!(obj.metadata.exports.len(), 2);
3086
3087        let export = obj.metadata.exports.first().expect("obj[0].exports[0]");
3088        assert_eq!(export.name_size, 7);
3089        assert_eq!(export.name(), "sprintf");
3090
3091        let lnk = &obj.obj;
3092        assert_eq!(lnk.version, 2);
3093
3094        let Section::CPU(cpu) = lnk.sections.first().expect("obj[0].obj.sections[0]") else {
3095            panic!("expected a section");
3096        };
3097        assert_eq!(*cpu, cputype::MIPS_R3000);
3098        /*
3099        assert_eq!(section.section, 1);
3100        assert_eq!(section.group, 0);
3101        assert_eq!(section.align, 8);
3102        assert_eq!(section.type_name_size, 6);
3103        assert_eq!(section.type_name(), ".rdata");
3104        */
3105
3106        assert_eq!(data.position(), bytes.len() as u64);
3107
3108        let mut writer = Cursor::new(Vec::new());
3109        obj.write_le(&mut writer).unwrap();
3110        assert_eq!(writer.into_inner(), bytes);
3111    }
3112
3113    #[test]
3114    fn test_2_mbyte() {
3115        let bytes = b"\
3116            \x4C\x4E\x4B\x02\x2E\x07\x10\x08\x28\x00\x00\x08\x06\x2E\x72\x64\
3117            \x61\x74\x61\x10\x09\x28\x00\x00\x08\x05\x2E\x74\x65\x78\x74\x10\
3118            \x0A\x28\x00\x00\x08\x05\x2E\x64\x61\x74\x61\x10\x0B\x28\x00\x00\
3119            \x08\x06\x2E\x73\x64\x61\x74\x61\x10\x0C\x28\x00\x00\x08\x05\x2E\
3120            \x73\x62\x73\x73\x10\x0D\x28\x00\x00\x08\x04\x2E\x62\x73\x73\x06\
3121            \x08\x28\x06\x09\x28\x06\x0A\x28\x06\x0B\x28\x06\x0C\x28\x06\x0D\
3122            \x28\x06\x09\x28\x02\xC4\x00\x08\x00\xE0\x03\x00\x00\x00\x00\x00\
3123            \x00\x02\x3C\x00\x00\x42\x24\x00\x00\x03\x3C\x00\x00\x63\x24\x00\
3124            \x00\x40\xAC\x04\x00\x42\x24\x2B\x08\x43\x00\xFC\xFF\x20\x14\x00\
3125            \x00\x00\x00\x04\x00\x02\x24\x00\x00\x00\x00\x00\x00\x00\x00\x00\
3126            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x3C\x00\x00\x84\x24\x21\
3127            \x20\x82\x00\x00\x00\x82\x8C\x00\x80\x08\x3C\x25\xE8\x48\x00\x00\
3128            \x00\x04\x3C\x00\x00\x84\x24\xC0\x20\x04\x00\xC2\x20\x04\x00\x00\
3129            \x00\x03\x3C\x00\x00\x63\x8C\x00\x00\x00\x00\x23\x28\x43\x00\x23\
3130            \x28\xA4\x00\x25\x20\x88\x00\x00\x00\x01\x3C\x00\x00\x3F\xAC\x00\
3131            \x00\x1C\x3C\x00\x00\x9C\x27\x21\xF0\xA0\x03\x00\x00\x00\x0C\x04\
3132            \x00\x84\x20\x00\x00\x1F\x3C\x00\x00\xFF\x8F\x00\x00\x00\x00\x00\
3133            \x00\x00\x0C\x00\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x20\x00\x00\
3134            \x00\x20\x00\x00\x00\x20\x00\x00\x00\x20\x00\x0A\x52\x08\x00\x0C\
3135            \x0C\x28\x0A\x54\x0C\x00\x0C\x0C\x28\x0A\x52\x10\x00\x16\x0D\x28\
3136            \x0A\x54\x14\x00\x16\x0D\x28\x0A\x52\x40\x00\x2C\x04\x09\x28\x00\
3137            \xB4\x00\x00\x00\x0A\x54\x44\x00\x2C\x04\x09\x28\x00\xB4\x00\x00\
3138            \x00\x0A\x52\x58\x00\x16\x0D\x28\x0A\x54\x5C\x00\x16\x0D\x28\x0A\
3139            \x52\x68\x00\x02\x17\x28\x0A\x54\x6C\x00\x02\x17\x28\x0A\x52\x80\
3140            \x00\x2C\x04\x0C\x28\x00\x00\x00\x00\x00\x0A\x54\x84\x00\x2C\x04\
3141            \x0C\x28\x00\x00\x00\x00\x00\x0A\x52\x88\x00\x0C\x0B\x28\x0A\x54\
3142            \x8C\x00\x0C\x0B\x28\x0A\x4A\x94\x00\x02\x14\x28\x0A\x52\x9C\x00\
3143            \x2C\x04\x0C\x28\x00\x00\x00\x00\x00\x0A\x54\xA0\x00\x2C\x04\x0C\
3144            \x28\x00\x00\x00\x00\x00\x0A\x4A\xA8\x00\x02\x16\x28\x06\x0C\x28\
3145            \x08\x04\x00\x00\x00\x0E\x14\x28\x08\x49\x6E\x69\x74\x48\x65\x61\
3146            \x70\x0E\x17\x28\x0A\x5F\x73\x74\x61\x63\x6B\x73\x69\x7A\x65\x0C\
3147            \x0F\x28\x09\x28\x08\x00\x00\x00\x10\x5F\x5F\x53\x4E\x5F\x45\x4E\
3148            \x54\x52\x59\x5F\x50\x4F\x49\x4E\x54\x0C\x0E\x28\x09\x28\x00\x00\
3149            \x00\x00\x06\x5F\x5F\x6D\x61\x69\x6E\x0E\x16\x28\x04\x6D\x61\x69\
3150            \x6E\x0C\x11\x28\x09\x28\xA8\x00\x00\x00\x05\x73\x74\x75\x70\x30\
3151            \x0C\x12\x28\x09\x28\x2C\x00\x00\x00\x05\x73\x74\x75\x70\x31\x0C\
3152            \x13\x28\x09\x28\x08\x00\x00\x00\x05\x73\x74\x75\x70\x32\x00";
3153        let mut data = Cursor::new(&bytes);
3154        let lnk = OBJ::read(&mut data).unwrap();
3155
3156        eprintln!("obj: {:?}", lnk);
3157    }
3158
3159    #[test]
3160    fn test_section() {
3161        let bytes = b"\x3A\x00\x00\x26\x00\x00\x00\x09\x00";
3162        let mut data = Cursor::new(&bytes);
3163        let _ = Section::read(&mut data).unwrap();
3164    }
3165
3166    #[test]
3167    fn test_expression() {
3168        // ExpressionDefinition::{ // 0x0A
3169        //   tag: 0x52,            // 0x52 (82)
3170        //   offset: 8,            // 0x0800 (little endian)
3171        //   expression: (
3172        //     sectstart(0x280c)   // 0x0C0C28
3173        //   )
3174        // }
3175        let bytes = b"\x0A\x52\x08\x00\x0C\x0C\x28";
3176        let mut data = Cursor::new(&bytes);
3177        let _ = Section::read(&mut data).unwrap();
3178
3179        let bytes = b"\x0A\x52\x10\x00\x16\x0D\x28";
3180        let mut data = Cursor::new(&bytes);
3181        let _ = Section::read(&mut data).unwrap();
3182
3183        let bytes = b"\x0A\x52\x10\x00\x16\x0D\x28\x04\x04\x00\x00\x00\x00\x00\x00";
3184        let mut data = Cursor::new(&bytes);
3185        let _ = Section::read(&mut data).unwrap();
3186
3187        let bytes = b"\x0A\x52\xD0\x00\x32\x00\x04\x00\x00\x00\x2E\x0C\xFA\x62\x16\xFA\x62";
3188        let mut data = Cursor::new(&bytes);
3189        let _ = Section::read(&mut data).unwrap();
3190    }
3191
3192    #[test]
3193    fn test_function_start() {
3194        let bytes = b"\
3195            \x4A\x7C\x55\xB4\x05\x00\x00\xA7\x59\x00\x00\x00\x00\x1D\x00\x20\
3196            \x00\x00\x00\x1F\x00\x00\x00\x03\x80\xF8\xFF\xFF\xFF\x06\x63\x61\
3197            \x6C\x6C\x6F\x63\x4C"
3198            .to_vec();
3199
3200        let mut data = Cursor::new(&bytes);
3201        let _ = Section::read(&mut data).unwrap();
3202
3203        let bytes = b"\x0A\x52\x10\x00\x16\x0D\x28".to_vec();
3204        let mut data = Cursor::new(&bytes);
3205        let _ = Section::read(&mut data).unwrap();
3206    }
3207
3208    #[test]
3209    fn test_def2() {
3210        let bytes = b"\
3211            \x54\x00\x00\x04\x00\x00\x00\x66\x00\x00\x00\x04\x00\x00\x00\x00\
3212            \x00\x08\x5F\x70\x68\x79\x73\x61\x64\x72\x04\x2E\x65\x6F\x73";
3213
3214        let mut data = Cursor::new(&bytes);
3215        let section = Section::read(&mut data).unwrap();
3216
3217        let Section::Def2(def2) = section else {
3218            panic!("expected a def2");
3219        };
3220
3221        assert_eq!(def2.section, 0);
3222        assert_eq!(def2.value, 4);
3223        assert_eq!(def2.class, 102);
3224        assert_eq!(def2.def_type, 0);
3225        assert_eq!(def2.size, 4);
3226        // assert_eq!(def2.dims, Dim::None);
3227        assert_eq!(def2.tag(), "_physadr");
3228        assert_eq!(def2.name(), ".eos");
3229    }
3230
3231    #[test]
3232    fn test_libsn_sat() {
3233        let bytes =
3234b"\x68\x00\x2F\x86\x2F\x96\x2F\xA6\x2F\xB6\x2F\xC6\x2F\xD6\x2F\xE6\x4F\x22\x6E\xF3\x6D\x43\x6B\x53\x69\x63\x29\x98\x8D\x16\xEA\x00\xDC\x00\x39\xC6\x8F\x01\x68\x93\x68\xC3\x66\x83\x65\xB3\xD1\x00\x41\x0B\x64\xD3\x88\xFF\x8F\x02\x3A\x0C\xA0\x08\xE0\xFF\x3B\x0C\x30\x83\x8F\x03\x39\x08\x29\x98\x8F\xEC\x39\xC6\x60\xA3\x6F\xE3\x4F\x26\x6E\xF6\x6D\xF6\x6C\xF6\x6B\xF6\x6A\xF6\x69\xF6\x00\x0B\x68\xF6\x00\x00\x80\x00\x00\x00\x00\x00";
3235
3236        let mut data = Cursor::new(&bytes);
3237        let code = Code::read(&mut data).unwrap();
3238        assert_eq!(bytes.len(), 106);
3239        assert_eq!(code.size, 104);
3240        assert_eq!(code.code.len(), 104);
3241        assert_eq!(code.code, bytes[2..]);
3242
3243        let bytes = b"\x0A\x0A\x1F\x00\x4A\x00\x02\x00\x00\x00\x2E\x34\x00\xFC\xFF\xFF\xFF\x2C\x04\x01\x00\x00\x22\x00\x00\x00\x2C\x04\x01\x00\x00\x60\x00\x00\x00";
3244        let mut data = Cursor::new(&bytes);
3245        let section = Section::read(&mut data).unwrap();
3246        assert_eq!(section.to_string(), "10 : Patch type 10 at offset 1f with ($2-arshift_chk-(($fffffffc&(sectbase(1)+$22))-(sectbase(1)+$60)))");
3247
3248        println!("section: {section}");
3249
3250        let bytes = b"\x4C\x4E\x4B\x02\x2E\x08\x14\x0B\x33\x80\x03\x62\x73\x73\x10\x0C\x33\x0B\x33\x08\x06\x62\x73\x73\x65\x6E\x64\x06\x0C\x33\x0C\x0A\x33\x0C\x33\x00\x00\x00\x00\x03\x65\x6E\x64\x00";
3251        let mut data = Cursor::new(&bytes);
3252        let _ = OBJ::read(&mut data).unwrap();
3253    }
3254}