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//!
55//! # File Format Details
56//!
57//! ## LIB Format
58//!
59//! A LIB file is structured as:
60//!
61//! | Offset | Type       | Description                   |
62//! |--------|------------|-------------------------------|
63//! | 0      | `[u8; 3]`  | Magic number: "LIB"           |
64//! | 3      | `u8`       | Archive format version (1)    |
65//! | 4      | `[Module]` | One or more module entries    |
66//!
67//! ## OBJ Format
68//!
69//! An OBJ file (also called LNK format internally) contains:
70//!
71//! | Offset | Type        | Description                   |
72//! |--------|-------------|-------------------------------|
73//! | 0      | `[u8; 3]`   | Magic number: "LNK"           |
74//! | 3      | `u8`        | Object format version (2)     |
75//! | 4      | `[Section]` | Sections until NOP terminator |
76//!
77//! # Supported Architectures
78//!
79//! - Motorola 68000 (Sega Genesis/Mega Drive/Sega CD/Mega CD)
80//! - MIPS R3000 (PlayStation 1)
81//! - Hitachi SH-2 (Sega Saturn)
82
83use core::cmp;
84use std::fmt;
85use std::fs;
86use std::path::Path;
87use std::time::{Duration, SystemTime, UNIX_EPOCH};
88
89use anyhow::Result;
90use binrw::binrw;
91use binrw::helpers::{until, until_eof};
92use chrono::{
93    DateTime, Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc,
94};
95use rabbitizer::{InstrCategory, Instruction};
96use unicode_segmentation::UnicodeSegmentation;
97
98use crate::display::DisplayWithOptions;
99
100pub mod cli;
101pub mod display;
102pub mod io;
103pub mod link;
104
105/// A [LIB] is an archive of several [OBJ] files. It consists
106/// of a magic number followed by one or more [Modules](Module).
107///
108/// | Offset | Type          | Description                |
109/// |--------|---------------|----------------------------|
110/// |   0    | `[u8;3]`        | Magic - "LIB"              |
111/// |   3    | `u8`            | Archive format version (1) |
112/// |   4    | `[Module]` | One or more wrapped [OBJ] files       |
113///
114/// A `LIB` file can be constructed from a `u8` slice using
115/// `read`.
116///
117/// ```
118/// use std::path::Path;
119/// use psyk::io;
120/// # use anyhow::Result;
121/// # fn main() -> Result<()> {
122/// let lib = io::read_lib(Path::new("SOME.LIB"));
123/// # Ok(())
124/// # }
125/// ```
126#[binrw]
127#[brw(little, magic = b"LIB", assert(!objs.is_empty()))]
128#[repr(C)]
129#[derive(Debug, PartialEq)]
130pub struct LIB {
131    version: u8,
132
133    #[br(parse_with = until_eof)]
134    objs: Vec<Module>,
135}
136
137impl LIB {
138    /// Creates a new [LIB] with the provided modules.
139    pub fn new(objs: Vec<Module>) -> Self {
140        Self { version: 1, objs }
141    }
142
143    /// The modules contained in this library.
144    ///
145    /// Each module wraps an OBJ file along with metadata about its name,
146    /// creation time, and exported symbols.
147    pub fn modules(&self) -> &Vec<Module> {
148        &self.objs
149    }
150}
151
152impl fmt::Display for LIB {
153    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154        self.fmt_with_options(f, &display::Options::default())
155    }
156}
157
158impl display::DisplayWithOptions for LIB {
159    fn fmt_with_options(&self, f: &mut fmt::Formatter, options: &display::Options) -> fmt::Result {
160        writeln!(f, "Module     Date     Time   Externals defined")?;
161        writeln!(f)?;
162        for obj in &self.objs {
163            obj.fmt_with_options(f, options)?;
164            writeln!(f)?;
165        }
166        Ok(())
167    }
168}
169
170/// An exported symbol from a module.
171///
172/// Exports represent functions or data that are made available to the linker
173/// for use by other modules.
174#[binrw]
175#[brw(little)]
176#[repr(C)]
177#[derive(Clone, PartialEq)]
178pub struct Export {
179    name_size: u8,
180    #[br(count = name_size)]
181    name: Vec<u8>,
182}
183
184/// An entry in the export table.
185///
186/// The export table is terminated by an export with a zero-length name.
187impl Export {
188    pub fn new(name: String) -> Self {
189        // TODO: should this restrict to ascii?
190        let mut utf8 = name.as_bytes().to_vec();
191        utf8.truncate(u8::MAX.into());
192        Self {
193            name_size: name.len() as u8,
194            name: utf8,
195        }
196    }
197
198    pub fn empty() -> Self {
199        Self {
200            name_size: 0,
201            name: Vec::new(),
202        }
203    }
204
205    /// Returns the name of this exported symbol.
206    ///
207    /// Non-UTF-8 characters are replaced with the Unicode replacement character (�)
208    pub fn name(&self) -> String {
209        // TODO: what are * prefixed symbols for?
210        if !self.name.is_empty() && self.name[0] == 0 {
211            format!("*{}", String::from_utf8_lossy(&self.name[1..]).into_owned())
212        } else {
213            String::from_utf8_lossy(&self.name).into_owned()
214        }
215    }
216}
217
218/// Trait for converting PSY-Q timestamps to standard Rust date/time types.
219///
220/// PSY-Q uses a custom 32-bit timestamp format similar to the DOS/Windows
221/// date format but with a different bit layout.
222///
223/// # Format
224///
225/// **Low 16 bits (date)**:
226/// ```text
227/// Bits:  15-9    8-5     4-0
228///        Year    Month   Day
229/// ```
230/// - Year: 0-127 (relative to 1980)
231/// - Month: 1-12
232/// - Day: 1-31
233///
234/// **High 16 bits (time)**:
235/// ```text
236/// Bits:  15-11   10-5    4-0
237///        Hour    Minute  Second/2
238/// ```
239/// - Hour: 0-23
240/// - Minute: 0-59
241/// - Second: 0-58 (stored as second/2; only even seconds)
242///
243/// # Note
244///
245/// These timestamps don't include timezone information and are treated
246/// as local time in the original PSY-Q toolchain.
247pub trait FromPSYQTimestamp {
248    /// Converts a PSY-Q timestamp to this type.
249    ///
250    /// Returns `None` if the timestamp contains invalid date/time values.
251    fn from_psyq_timestamp(t: u32) -> Option<Self>
252    where
253        Self: Sized;
254
255    /// Converts `Self` into a 32-bit PSY-Q timestamp
256    fn to_psyq_timestamp(&self) -> u32;
257}
258
259impl FromPSYQTimestamp for NaiveDate {
260    fn from_psyq_timestamp(t: u32) -> Option<Self> {
261        let date = t & 0xFFFF;
262        let year = ((date >> 9) & 0x7F) + 1980;
263        let month = (date >> 5) & 0xF;
264        let day = date & 0x1F;
265        NaiveDate::from_ymd_opt(year as i32, month, day)
266    }
267
268    fn to_psyq_timestamp(&self) -> u32 {
269        let year = (self.year() as u32 - 1980) & 0x7F;
270        let month = (self.month()) & 0xF;
271        let day = (self.day()) & 0x1F;
272
273        (year << 9) | (month << 5) | day
274    }
275}
276
277impl FromPSYQTimestamp for NaiveTime {
278    fn from_psyq_timestamp(t: u32) -> Option<Self> {
279        let time = t >> 16;
280        let hour = (time >> 11) & 0x1F;
281        let minute = (time >> 5) & 0x3F;
282        let second = (time & 0x1F) * 2;
283        NaiveTime::from_hms_opt(hour, minute, second)
284    }
285
286    fn to_psyq_timestamp(&self) -> u32 {
287        let hour = self.hour() & 0x1F;
288        let minute = self.minute() & 0x3F;
289        let second = self.second() / 2;
290
291        (hour << 27) | (minute << 21) | (second << 16)
292    }
293}
294
295impl FromPSYQTimestamp for NaiveDateTime {
296    fn from_psyq_timestamp(t: u32) -> Option<Self> {
297        // These timestamps are "local" without any timezone information.
298        // We do the best we can by treating them as naive datetime values.
299        Some(NaiveDateTime::new(
300            NaiveDate::from_psyq_timestamp(t)?,
301            NaiveTime::from_psyq_timestamp(t)?,
302        ))
303    }
304
305    fn to_psyq_timestamp(&self) -> u32 {
306        self.date().to_psyq_timestamp() | self.time().to_psyq_timestamp()
307    }
308}
309
310impl FromPSYQTimestamp for SystemTime {
311    fn from_psyq_timestamp(t: u32) -> Option<Self> {
312        let dt = NaiveDateTime::from_psyq_timestamp(t)?;
313        // Convert to UTC (though original timezone is unknown)
314        let datetime_utc = Utc.from_utc_datetime(&dt);
315        Some(UNIX_EPOCH + Duration::from_secs(datetime_utc.timestamp() as u64))
316    }
317
318    fn to_psyq_timestamp(&self) -> u32 {
319        let datetime = DateTime::<Local>::from(*self);
320        datetime.naive_utc().to_psyq_timestamp()
321    }
322}
323
324/// Metadata for a module within a LIB archive.
325///
326/// This includes the module name (up to 8 characters), creation timestamp,
327/// and a list of exported symbols.
328#[binrw]
329#[brw(little)]
330#[repr(C)]
331#[derive(Clone, PartialEq)]
332pub struct ModuleMetadata {
333    name: [u8; 8],
334    created: u32,
335    offset: u32,
336    size: u32,
337
338    #[br(parse_with=until(|e: &Export| e.name_size == 0))]
339    exports: Vec<Export>,
340}
341
342/// Converts a [Path] into an appropriate module name. The module
343/// name is the first 8 characters of the file name without anything
344/// following the first `.` (period) character (as defined by
345/// [Path::file_prefix]). If that portion of the file name is smaller
346/// than 8-bytes, the remaining bytes will be padded with the `NUL`
347/// character.
348///
349/// Path does not include a file component, this function will
350/// panic.
351///
352/// **Note on Unicode:** it is assumed that paths are encoded
353/// in UTF-8, an invariant not guaranteed by the Rust std library.
354/// Psy-Q was not built to handle Unicode filenames, so including
355/// files with characters outside of the ASCII range will likely
356/// break interoperability with other tools. However, Psy-K supports
357/// Unicode file names and will produce appropriate model names
358/// with only the bytes that represent full code points.
359#[inline]
360fn path_to_module_name(path: &Path) -> [u8; 8] {
361    let Some(prefix) = path.file_prefix() else {
362        panic!("Module paths must contain a file name: {:?}", path);
363    };
364
365    let mut module_name: [u8; 8] = [0x20; 8];
366    let binding = prefix.to_ascii_uppercase();
367
368    if prefix.is_ascii() {
369        // the ascii path is simple, just copy the bytes
370        let bytes = binding.as_encoded_bytes();
371        let len = cmp::min(bytes.len(), module_name.len());
372        module_name[0..len].copy_from_slice(&bytes[0..len]);
373    } else {
374        // the unicode path requires care to avoid breaking
375        // multi-byte codepoints and grapheme clusters.
376        let Some(prefix_str) = binding.to_str() else {
377            panic!("Module path is not valid unicode: {:?}", path);
378        };
379
380        let mut size = 0;
381        for (offset, cluster) in prefix_str.grapheme_indices(false) {
382            if offset > 7 || (offset + cluster.len()) > 8 {
383                break;
384            }
385            size = offset + cluster.len();
386        }
387
388        module_name[..size].copy_from_slice(&prefix_str.as_bytes()[..size]);
389    }
390    module_name
391}
392
393impl ModuleMetadata {
394    fn new_from_path(path: &Path, obj: &OBJ) -> Result<Self> {
395        let name = path_to_module_name(path);
396
397        let file_metadata = fs::metadata(path)?;
398
399        let created = if let Ok(creation_time) = file_metadata.created() {
400            creation_time.to_psyq_timestamp()
401        } else {
402            SystemTime::now().to_psyq_timestamp()
403        };
404        let mut exports = obj
405            .exports()
406            .into_iter()
407            .map(Export::new)
408            .collect::<Vec<Export>>();
409        exports.push(Export::empty());
410
411        let offset: u32 = 20 + exports.iter().map(|e| 1 + e.name_size as u32).sum::<u32>();
412        let size = offset + file_metadata.len() as u32;
413
414        Ok(Self {
415            name,
416            created,
417            offset,
418            size,
419            exports,
420        })
421    }
422
423    /// Returns the module name, with trailing whitespace removed.
424    ///
425    /// Module names are stored as 8-byte fixed-width fields, padded with spaces.
426    pub fn name(&self) -> String {
427        // trim_end for the name array
428        let end = self
429            .name
430            .iter()
431            .rposition(|x| !x.is_ascii_whitespace())
432            .expect("Module.name trim_end")
433            + 1;
434        String::from_utf8_lossy(&self.name[..end]).into_owned()
435    }
436
437    /// Returns a list of symbol names exported by this module.
438    ///
439    /// Empty exports (the terminator entry) are filtered out.
440    pub fn exports(&self) -> Vec<String> {
441        self.exports
442            .iter()
443            .filter_map(|e| {
444                if e.name.is_empty() {
445                    None
446                } else {
447                    Some(e.name())
448                }
449            })
450            .collect()
451    }
452
453    /// Returns the creation timestamp as a formatted string.
454    ///
455    /// Format: `MM-DD-YY HH:MM:SS`
456    ///
457    /// # Example
458    /// ```text
459    /// 05-15-96 16:09:38
460    /// ```
461    pub fn created(&self) -> String {
462        // 15-05-96 16:09:38
463        //    hhhh hmmm mmms ssss yyyy yyyM MMMd dddd
464        // LE 1000 0001 0011 0011 0010 0000 1010 1111
465        //
466        // day    - 15 01111
467        // month  - 05 0101
468        // year   - 96 001000
469        // hour   - 16 10000
470        // minute - 09 000101
471        // second - 38 00010
472
473        // format!("{} {}", self.date(), self.time())
474        self.created_datetime()
475            .expect("created")
476            .format("%d-%m-%y %H:%M:%S")
477            .to_string()
478    }
479
480    /// Returns the creation timestamp as a `NaiveDateTime`.
481    ///
482    /// Returns `None` if the timestamp is invalid.
483    pub fn created_datetime(&self) -> Option<NaiveDateTime> {
484        NaiveDateTime::from_psyq_timestamp(self.created)
485    }
486
487    /// Returns the creation timestamp as a `SystemTime`.
488    ///
489    /// Returns `None` if the timestamp is invalid.
490    ///
491    /// Note: The original timestamp has no timezone information, so it's
492    /// treated as UTC for conversion purposes.
493    pub fn created_at(&self) -> Option<SystemTime> {
494        SystemTime::from_psyq_timestamp(self.created)
495    }
496}
497
498/// A module entry in a LIB archive.
499///
500/// Each module consists of metadata (name, timestamp, exports) and the
501/// actual OBJ file data.
502#[binrw]
503#[brw(little)]
504#[repr(C)]
505#[derive(Clone, PartialEq)]
506pub struct Module {
507    metadata: ModuleMetadata,
508    obj: OBJ,
509}
510
511impl Module {
512    /// Creates a new [Module] from the file at `path`.
513    ///
514    /// `path` must point to a valid [OBJ] file.
515    pub fn new_from_path(path: &Path) -> Result<Self> {
516        let obj = io::read_obj(path)?;
517        let metadata = ModuleMetadata::new_from_path(path, &obj)?;
518        Ok(Self { metadata, obj })
519    }
520
521    /// Returns the module name.
522    pub fn name(&self) -> String {
523        self.metadata.name()
524    }
525
526    /// Returns the list of exported symbol names.
527    pub fn exports(&self) -> Vec<String> {
528        self.metadata.exports()
529    }
530
531    /// Returns the creation timestamp as a formatted string.
532    pub fn created(&self) -> String {
533        self.metadata.created()
534    }
535
536    /// Returns the creation timestamp as a `SystemTime`
537    pub fn created_at(&self) -> Option<SystemTime> {
538        self.metadata.created_at()
539    }
540
541    /// Returns the creation timestamp as a `NaiveDateTime`
542    pub fn created_datetime(&self) -> Option<NaiveDateTime> {
543        self.metadata.created_datetime()
544    }
545
546    /// Returns a reference to the OBJ file contained in this module.
547    pub fn object(&self) -> &OBJ {
548        &self.obj
549    }
550}
551
552impl fmt::Display for Module {
553    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554        self.fmt_with_options(f, &display::Options::default())
555    }
556}
557
558impl display::DisplayWithOptions for Module {
559    fn fmt_with_options(&self, f: &mut fmt::Formatter, _options: &display::Options) -> fmt::Result {
560        write!(
561            f,
562            "{:<8} {} {}",
563            self.name(),
564            self.created(),
565            self.exports()
566                .into_iter()
567                .map(|e| format!("{e} "))
568                .collect::<Vec<_>>()
569                .join("")
570        )?;
571        Ok(())
572    }
573}
574
575impl fmt::Debug for Module {
576    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
577        write!(
578            f,
579            "Module {{name: \"{}\", huh: {}, offset: {}, size: {}, exports: \"{:?}\", lnk: {:?}}}",
580            self.name(),
581            self.metadata.created,
582            self.metadata.offset,
583            self.metadata.size,
584            self.exports(),
585            self.obj
586        )
587    }
588}
589
590/// An opaque module representation used during parsing.
591///
592/// This variant stores the raw bytes of the OBJ file without parsing it,
593/// which can be useful for tools that only need to inspect metadata.
594#[binrw]
595#[brw(little)]
596#[repr(C)]
597pub struct OpaqueModule {
598    metadata: ModuleMetadata,
599
600    #[br(count = metadata.size - 16)]
601    obj: Vec<u8>,
602}
603
604/// A PSY-Q object file (LNK format).
605///
606/// OBJ files contain machine code, relocation information, symbol definitions,
607/// and debugging data needed by the linker.
608///
609/// # Structure
610///
611/// | Offset | Type        | Description               |
612/// |--------|-------------|---------------------------|
613/// | 0      | `[u8; 3]`   | Magic number: "LNK"      |
614/// | 3      | `u8`        | Version (typically 2)     |
615/// | 4      | `[Section]` | Sections until NOP       |
616///
617/// # Examples
618///
619/// ```no_run
620/// use std::path::Path;
621/// use psyk::io;
622/// use anyhow::Result;
623///
624/// fn main() -> Result<()> {
625///     let obj = io::read_obj(Path::new("MODULE.OBJ"))?;
626///
627///     println!("OBJ version: {}", obj.version());
628///     println!("Sections: {}", obj.sections().len());
629///
630///     Ok(())
631/// }
632/// ```
633#[binrw]
634#[brw(little, magic = b"LNK")]
635#[repr(C)]
636#[derive(Clone, Debug, PartialEq)]
637pub struct OBJ {
638    version: u8,
639    #[br(parse_with=until(|section: &Section| matches!(section, Section::NOP)))]
640    pub sections: Vec<Section>,
641}
642
643impl OBJ {
644    /// Returns the OBJ format version (typically 2).
645    pub fn version(&self) -> u8 {
646        self.version
647    }
648
649    /// Returns the sections contained in this object file.
650    ///
651    /// Sections include code, data, symbols, relocations, and debug info.
652    /// The list is terminated by a `Section::NOP` entry.
653    pub fn sections(&self) -> &Vec<Section> {
654        &self.sections
655    }
656
657    /// Returns symbols exported by this object file.
658    ///
659    /// Exported symbols can be functions or globals.
660    pub fn exports(&self) -> Vec<String> {
661        self.sections()
662            .iter()
663            .filter_map({
664                |s| match s {
665                    Section::XDEF(xdef) => {
666                        if xdef.symbol_name_size > 0 {
667                            Some(xdef.symbol_name())
668                        } else {
669                            None
670                        }
671                    }
672                    Section::XBSS(xbss) => {
673                        if xbss.name_size > 0 {
674                            Some(xbss.name())
675                        } else {
676                            None
677                        }
678                    }
679                    _ => None,
680                }
681            })
682            .collect()
683    }
684}
685
686impl fmt::Display for OBJ {
687    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
688        writeln!(f, "Header : LNK version {}", self.version)?;
689        for section in &self.sections {
690            writeln!(f, "{}", section)?;
691        }
692        Ok(())
693    }
694}
695
696impl display::DisplayWithOptions for OBJ {
697    fn fmt_with_options(&self, f: &mut fmt::Formatter, options: &display::Options) -> fmt::Result {
698        writeln!(f, "Header : LNK version {}", self.version)?;
699        for section in &self.sections {
700            section.fmt_with_options(f, options)?;
701            writeln!(f)?;
702        }
703        Ok(())
704    }
705}
706
707/// Machine code section.
708///
709/// Contains executable instructions for the target [CPU](Section::CPU).
710#[binrw]
711#[brw(little)]
712#[derive(Clone, Debug, PartialEq)]
713pub struct Code {
714    size: u16,
715    #[br(count = size)]
716    code: Vec<u8>,
717}
718
719impl Code {
720    /// Returns the code for this section as bytes. Their format can be determined by the value
721    /// set in the [CPU](Section::CPU).
722    pub fn code(&self) -> &Vec<u8> {
723        &self.code
724    }
725}
726
727/// Section switch directive.
728///
729/// Tells the linker to switch to a different section for subsequent data.
730#[binrw]
731#[brw(little)]
732#[derive(Clone, Debug, PartialEq)]
733pub struct SectionSwitch {
734    id: u16,
735}
736
737/// An expression used in relocations.
738///
739/// PSY-Q uses a sophisticated expression system for calculating relocated
740/// addresses. Expressions can be constants, symbol references, or complex
741/// arithmetic operations.
742///
743/// # Example Expressions
744///
745/// - `$1000` - Constant value 0x1000
746/// - `[5]` - Address of symbol #5
747/// - `sectbase(2)` - Base address of section #2
748/// - `(sectstart(1)+$100)` - Section 1 start plus 0x100
749#[binrw]
750#[brw(little)]
751#[derive(Clone, Debug, PartialEq)]
752pub enum Expression {
753    /// A constant value (tag 0x00).
754    #[brw(magic(0u8))]
755    Constant(u32),
756
757    /// Index of a symbol's address (tag 0x02).
758    #[brw(magic(2u8))]
759    SymbolAddressIndex(u16),
760
761    /// Base address of a section (tag 0x04).
762    #[brw(magic(4u8))]
763    SectionAddressIndex(u16),
764
765    /// Untested
766    // 6 -  bank({})
767    #[brw(magic(6u8))]
768    Bank(u16),
769
770    /// Untested
771    // 8 - sectof({})
772    #[brw(magic(8u8))]
773    SectOf(u16),
774
775    /// Untested
776    // 10 - offs({})
777    #[brw(magic(10u8))]
778    Offset(u16),
779
780    /// Start address of a section (tag 0x0C).
781    #[brw(magic(12u8))]
782    SectionStart(u16),
783
784    /// Untested
785    // 14 - groupstart({})
786    #[brw(magic(14u8))]
787    GroupStart(u16),
788
789    /// Untested
790    // 16 - groupof({})
791    #[brw(magic(16u8))]
792    GroupOf(u16),
793
794    /// Untested
795    // 18 - seg({})
796    #[brw(magic(18u8))]
797    Segment(u16),
798
799    /// Untested
800    // 20 - grouporg({})
801    #[brw(magic(20u8))]
802    GroupOrg(u16),
803
804    /// End address of a section (tag 0x16).
805    #[brw(magic(22u8))]
806    SectionEnd(u16),
807
808    // Comparison operators
809    /// Equality comparison (tag 0x20).
810    #[brw(magic(32u8))]
811    Equals(Box<Expression>, Box<Expression>),
812
813    /// Inequality comparison (tag 0x22).
814    #[brw(magic(34u8))]
815    NotEquals(Box<Expression>, Box<Expression>),
816
817    /// Less than or equal (tag 0x24).
818    #[brw(magic(36u8))]
819    LTE(Box<Expression>, Box<Expression>),
820
821    /// Less than (tag 0x26).
822    #[brw(magic(38u8))]
823    LessThan(Box<Expression>, Box<Expression>),
824
825    /// Greater than or equal (tag 0x28).
826    #[brw(magic(40u8))]
827    GTE(Box<Expression>, Box<Expression>),
828
829    /// Greater than (tag 0x2A).
830    #[brw(magic(42u8))]
831    GreaterThan(Box<Expression>, Box<Expression>),
832
833    // Arithmetic operators
834    /// Addition (tag 0x2C).
835    #[brw(magic(44u8))]
836    Add(Box<Expression>, Box<Expression>),
837
838    /// Subtraction (tag 0x2E).
839    #[brw(magic(46u8))]
840    Subtract(Box<Expression>, Box<Expression>),
841
842    /// Multiplication (tag 0x30).
843    #[brw(magic(48u8))]
844    Multiply(Box<Expression>, Box<Expression>),
845
846    /// Division (tag 0x32).
847    #[brw(magic(50u8))]
848    Divide(Box<Expression>, Box<Expression>),
849
850    /// Bitwise AND (tag 0x34).
851    #[brw(magic(52u8))]
852    And(Box<Expression>, Box<Expression>),
853
854    /// Bitwise OR operator (tag 0x36).
855    #[brw(magic(54u8))]
856    Or(Box<Expression>, Box<Expression>),
857
858    /// Bitwise XOR (tag 0x38).
859    #[brw(magic(56u8))]
860    XOR(Box<Expression>, Box<Expression>),
861
862    /// Left shift (tag 0x3A).
863    #[brw(magic(58u8))]
864    LeftShift(Box<Expression>, Box<Expression>),
865
866    /// Right shift (tag 0x3C).
867    #[brw(magic(60u8))]
868    RightShift(Box<Expression>, Box<Expression>),
869
870    /// Modulo (tag 0x3E).
871    #[brw(magic(62u8))]
872    Mod(Box<Expression>, Box<Expression>),
873
874    /// Dashes operator (tag 0x40).
875    #[brw(magic(64u8))]
876    Dashes(Box<Expression>, Box<Expression>),
877
878    // Special operators (primarily for Saturn/SH-2)
879    /// Reverse word (tag 0x42).
880    #[brw(magic(66u8))]
881    Revword(Box<Expression>, Box<Expression>),
882
883    /// Check0 (tag 0x44).
884    #[brw(magic(68u8))]
885    Check0(Box<Expression>, Box<Expression>),
886
887    /// Check1 (tag 0x46).
888    #[brw(magic(70u8))]
889    Check1(Box<Expression>, Box<Expression>),
890
891    /// Bit range extraction (tag 0x48).
892    #[brw(magic(72u8))]
893    BitRange(Box<Expression>, Box<Expression>),
894
895    /// Arithmetic shift with check (tag 0x4A).
896    #[brw(magic(74u8))]
897    ArshiftChk(Box<Expression>, Box<Expression>),
898}
899
900impl fmt::Display for Expression {
901    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
902        match self {
903            Self::Constant(value) => write!(f, "${value:x}"),
904            Self::SymbolAddressIndex(addr) => write!(f, "[{addr:x}]"),
905            Self::SectionAddressIndex(base) => write!(f, "sectbase({base:x})"),
906            // untested
907            Self::Bank(bank) => write!(f, "bank({bank:x})"),
908            // untested
909            Self::SectOf(bank) => write!(f, "sectof({bank:x})"),
910            // untested
911            Self::Offset(bank) => write!(f, "offs({bank:x})"),
912            Self::SectionStart(offset) => write!(f, "sectstart({offset:x})"),
913            // untested
914            Self::GroupStart(group) => write!(f, "groupstart({group:x})"),
915            // untested
916            Self::GroupOf(group) => write!(f, "groupstart({group:x})"),
917            // untested
918            Self::Segment(segment) => write!(f, "seg({segment:x})"),
919            // untested
920            Self::GroupOrg(group) => write!(f, "grouporg({group:x})"),
921            Self::SectionEnd(offset) => write!(f, "sectend({offset:x})"),
922
923            // comparison
924            Self::Equals(lhs, rhs) => write!(f, "({}={})", lhs, rhs),
925            Self::NotEquals(lhs, rhs) => write!(f, "({}<>{})", lhs, rhs),
926            Self::LTE(lhs, rhs) => write!(f, "({}<={})", lhs, rhs),
927            Self::LessThan(lhs, rhs) => write!(f, "({}<{})", lhs, rhs),
928            Self::GTE(lhs, rhs) => write!(f, "({}>={})", lhs, rhs),
929            Self::GreaterThan(lhs, rhs) => write!(f, "({}>{})", lhs, rhs),
930
931            // arithmatic
932            Self::Add(lhs, rhs) => write!(f, "({}+{})", lhs, rhs),
933            Self::Subtract(lhs, rhs) => write!(f, "({}-{})", lhs, rhs),
934            Self::Multiply(lhs, rhs) => write!(f, "({}*{})", lhs, rhs),
935            Self::Divide(lhs, rhs) => write!(f, "({}/{})", lhs, rhs),
936            Self::And(lhs, rhs) => write!(f, "({}&{})", lhs, rhs),
937            Self::Or(lhs, rhs) => write!(f, "({}!{})", lhs, rhs),
938            Self::XOR(lhs, rhs) => write!(f, "({}^{})", lhs, rhs),
939            Self::LeftShift(lhs, rhs) => write!(f, "({}<<{})", lhs, rhs),
940            Self::RightShift(lhs, rhs) => write!(f, "({}>>{})", lhs, rhs),
941            Self::Mod(lhs, rhs) => write!(f, "({}%%{})", lhs, rhs),
942            Self::Dashes(lhs, rhs) => write!(f, "({}---{})", lhs, rhs),
943
944            // keyword
945            Self::Revword(lhs, rhs) => write!(f, "({}-revword-{})", lhs, rhs),
946            Self::Check0(lhs, rhs) => write!(f, "({}-check0-{})", lhs, rhs),
947            Self::Check1(lhs, rhs) => write!(f, "({}-check1-{})", lhs, rhs),
948            Self::BitRange(lhs, rhs) => write!(f, "({}-bitrange-{})", lhs, rhs),
949            Self::ArshiftChk(lhs, rhs) => write!(f, "({}-arshift_chk-{})", lhs, rhs),
950        }
951    }
952}
953
954/// A relocation patch to be applied by the linker.
955///
956/// Patches modify code or data at a specific offset using a calculated expression
957#[binrw]
958#[brw(little)]
959#[derive(Clone, Debug, PartialEq)]
960pub struct Patch {
961    /// The type of patch (determines how the expression value is applied).
962    tag: u8,
963    /// Offset in the current section where the patch should be applied.
964    offset: u16,
965    /// Expression to calculate the patch value.
966    expression: Expression,
967}
968
969/// Section header information.
970///
971/// Defines properties of a section such as its group, alignment, and type name.
972#[binrw]
973#[brw(little)]
974#[derive(Clone, PartialEq)]
975pub struct LNKHeader {
976    section: u16,
977    group: u16,
978    align: u8,
979    type_name_size: u8,
980
981    #[br(count = type_name_size)]
982    type_name: Vec<u8>,
983}
984
985impl LNKHeader {
986    /// Returns the section type name (e.g., ".text", ".data", ".bss").
987    pub fn type_name(&self) -> String {
988        String::from_utf8_lossy(&self.type_name).into_owned()
989    }
990}
991
992impl fmt::Debug for LNKHeader {
993    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
994        write!(
995            f,
996            "LNKHeader {{section: {}, group: {}, align: {}, type_name: \"{}\"}}",
997            self.section,
998            self.group,
999            self.align,
1000            self.type_name(),
1001        )
1002    }
1003}
1004
1005/// A local symbol definition.
1006///
1007/// Local symbols are visible only within the current module.
1008#[binrw]
1009#[brw(little)]
1010#[derive(Clone, PartialEq)]
1011pub struct LocalSymbol {
1012    section: u16,
1013    offset: u32,
1014    name_size: u8,
1015
1016    #[br(count = name_size)]
1017    name: Vec<u8>,
1018}
1019
1020impl LocalSymbol {
1021    pub fn name(&self) -> String {
1022        String::from_utf8_lossy(&self.name).into_owned()
1023    }
1024}
1025
1026impl fmt::Debug for LocalSymbol {
1027    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1028        write!(
1029            f,
1030            "LocalSymbol {{section: {}, offset: {}, name: \"{}\"}}",
1031            self.section,
1032            self.offset,
1033            self.name(),
1034        )
1035    }
1036}
1037
1038/// A group symbol definition.
1039///
1040/// Groups are used to organize sections for linking.
1041#[binrw]
1042#[brw(little)]
1043#[derive(Clone, PartialEq)]
1044pub struct GroupSymbol {
1045    number: u16,
1046    sym_type: u8,
1047    name_size: u8,
1048
1049    #[br(count = name_size)]
1050    name: Vec<u8>,
1051}
1052
1053impl GroupSymbol {
1054    pub fn name(&self) -> String {
1055        String::from_utf8_lossy(&self.name).into_owned()
1056    }
1057}
1058
1059impl fmt::Debug for GroupSymbol {
1060    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1061        write!(
1062            f,
1063            "GroupSymbol {{number: {}, type: {}, name: \"{}\"}}",
1064            self.number,
1065            self.sym_type,
1066            self.name(),
1067        )
1068    }
1069}
1070
1071/// An external symbol definition (XDEF).
1072///
1073/// XDEF entries define symbols that are exported from this module
1074/// and can be referenced by other modules.
1075#[binrw]
1076#[brw(little)]
1077#[derive(Clone, Debug, PartialEq)]
1078pub struct XDEF {
1079    number: u16,
1080    section: u16,
1081    offset: u32,
1082    symbol_name_size: u8,
1083
1084    #[br(count = symbol_name_size)]
1085    symbol_name: Vec<u8>,
1086}
1087
1088impl XDEF {
1089    pub fn symbol_name(&self) -> String {
1090        // TODO: can a starred symbol be here as well?
1091        String::from_utf8_lossy(&self.symbol_name).into_owned()
1092    }
1093}
1094
1095/// An external symbol reference (XREF).
1096///
1097/// XREF entries declare symbols that this module needs but are
1098/// defined in other modules.
1099#[binrw]
1100#[brw(little)]
1101#[derive(Clone, Debug, PartialEq)]
1102pub struct XREF {
1103    number: u16,
1104    symbol_name_size: u8,
1105
1106    #[br(count = symbol_name_size)]
1107    symbol_name: Vec<u8>,
1108}
1109
1110impl XREF {
1111    pub fn symbol_name(&self) -> String {
1112        String::from_utf8_lossy(&self.symbol_name).into_owned()
1113    }
1114}
1115
1116/// CPU architecture type identifiers.
1117pub mod cputype {
1118    /// Motorola 68000 - Sega Genesis, Sega CD, Mega Drive, & Mega CD
1119    pub const MOTOROLA_68000: u8 = 0;
1120
1121    /// MIPS R3000 with GTE (Graphics Transform Engine) - PlayStation 1.
1122    pub const MIPS_R300GTE: u8 = 7;
1123
1124    /// Hitachi SH-2 - Sega Saturn.
1125    pub const HITACHI_SH2: u8 = 8;
1126}
1127
1128/// A file name reference used in debug information.
1129#[binrw]
1130#[brw(little)]
1131#[derive(Clone, PartialEq)]
1132pub struct Filename {
1133    number: u16,
1134    size: u8,
1135    #[br(count = size)]
1136    name: Vec<u8>,
1137}
1138
1139impl Filename {
1140    pub fn name(&self) -> String {
1141        String::from_utf8_lossy(&self.name).into_owned()
1142    }
1143}
1144
1145impl fmt::Debug for Filename {
1146    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1147        write!(
1148            f,
1149            "Filename {{number: {}, name: \"{}\"}}",
1150            self.number,
1151            self.name(),
1152        )
1153    }
1154}
1155
1156/// Set MX info directive.
1157#[binrw]
1158#[brw(little)]
1159#[derive(Clone, Debug, PartialEq)]
1160pub struct SetMXInfo {
1161    offset: u16,
1162    value: u8,
1163}
1164
1165/// External BSS (uninitialized data) symbol.
1166#[binrw]
1167#[brw(little)]
1168#[derive(Clone, Debug, PartialEq)]
1169pub struct XBSS {
1170    number: u16,
1171    section: u16,
1172    size: u32,
1173    name_size: u8,
1174
1175    #[br(count = name_size)]
1176    name: Vec<u8>,
1177}
1178
1179impl XBSS {
1180    pub fn name(&self) -> String {
1181        String::from_utf8_lossy(&self.name).into_owned()
1182    }
1183}
1184
1185/// Set source line debugger (SLD) line number.
1186#[binrw]
1187#[brw(little)]
1188#[derive(Clone, Debug, PartialEq)]
1189pub struct SetSLDLineNum {
1190    offset: u16,
1191    linenum: u32,
1192}
1193
1194/// Set source line debugger (SLD) line number with file reference.
1195#[binrw]
1196#[brw(little)]
1197#[derive(Clone, Debug, PartialEq)]
1198pub struct SetSLDLineNumFile {
1199    offset: u16,
1200    linenum: u32,
1201    file: u16,
1202}
1203
1204/// Function start debug information.
1205///
1206/// Provides detailed information about a function for source-level debugging.
1207#[binrw]
1208#[brw(little)]
1209#[derive(Clone, Debug, PartialEq)]
1210pub struct FunctionStart {
1211    section: u16,
1212    offset: u32,
1213    file: u16,
1214    linenum: u32,
1215    frame_register: u16,
1216    frame_size: u32,
1217    return_pc_register: u16,
1218    mask: u32,
1219    mask_offset: i32,
1220    name_size: u8,
1221
1222    #[br(count = name_size)]
1223    name: Vec<u8>,
1224}
1225
1226impl FunctionStart {
1227    /// Function end debug information.
1228    pub fn name(&self) -> String {
1229        String::from_utf8_lossy(&self.name).into_owned()
1230    }
1231}
1232
1233/// Function end debug information.
1234#[binrw]
1235#[brw(little)]
1236#[derive(Clone, Debug, PartialEq)]
1237pub struct FunctionEnd {
1238    section: u16,
1239    offset: u32,
1240    linenum: u32,
1241}
1242
1243/// Block start debug information.
1244#[binrw]
1245#[brw(little)]
1246#[derive(Clone, Debug, PartialEq)]
1247pub struct BlockStart {
1248    section: u16,
1249    offset: u32,
1250    linenum: u32,
1251}
1252
1253/// Block end debug information.
1254#[binrw]
1255#[brw(little)]
1256#[derive(Clone, Debug, PartialEq)]
1257pub struct BlockEnd {
1258    section: u16,
1259    offset: u32,
1260    linenum: u32,
1261}
1262
1263/// Variable or type definition debug information.
1264#[binrw]
1265#[brw(little)]
1266#[derive(Clone, Debug, PartialEq)]
1267pub struct Def {
1268    section: u16,
1269    value: u32,
1270    class: u16,
1271    def_type: u16,
1272    size: u32,
1273    name_size: u8,
1274    #[br(count = name_size)]
1275    name: Vec<u8>,
1276}
1277
1278impl Def {
1279    /// Returns the definition name.
1280    pub fn name(&self) -> String {
1281        String::from_utf8_lossy(&self.name).into_owned()
1282    }
1283}
1284
1285/// Dimension specification for arrays.
1286#[binrw]
1287#[brw(little)]
1288#[derive(Clone, Debug, PartialEq)]
1289pub enum Dim {
1290    /// No dimensions (scalar).
1291    #[br(magic = 0u16)]
1292    None,
1293
1294    /// Single dimension with size.
1295    #[br(magic = 1u16)]
1296    Value(u32),
1297}
1298
1299impl fmt::Display for Dim {
1300    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1301        match self {
1302            Self::None => write!(f, "0"),
1303            Self::Value(v) => write!(f, "1 {v}"),
1304        }
1305    }
1306}
1307
1308/// Extended variable or type definition with additional metadata.
1309#[binrw]
1310#[brw(little)]
1311#[derive(Clone, Debug, PartialEq)]
1312pub struct Def2 {
1313    section: u16,
1314    value: u32,
1315    class: u16,
1316    def_type: u16, // 34 00
1317    size: u32,     // 04 00 00 00
1318    dims: Dim,
1319    tag_size: u8,
1320    #[br(count = tag_size)]
1321    tag: Vec<u8>,
1322    name_size: u8, // 06
1323    #[br(count = name_size)]
1324    name: Vec<u8>, // 75 5F 63 68 61 72
1325}
1326
1327impl Def2 {
1328    pub fn tag(&self) -> String {
1329        String::from_utf8_lossy(&self.tag).into_owned()
1330    }
1331
1332    pub fn name(&self) -> String {
1333        String::from_utf8_lossy(&self.name).into_owned()
1334    }
1335}
1336
1337/// A section within an OBJ file.
1338///
1339/// Sections can contain code, data, relocations, symbols, or debug information.
1340/// The section list is terminated by a NOP entry.
1341///
1342/// # Section Types
1343///
1344/// - Code: Executable machine code
1345/// - BSS: Uninitialized data
1346/// - XDEF/XREF: Symbol exports and imports
1347/// - Patch: Relocation information
1348/// - Debug sections: Line numbers, function info, etc.
1349#[binrw]
1350#[brw(little)]
1351#[derive(Clone, Debug, PartialEq)]
1352pub enum Section {
1353    /// End of file marker (tag 0).
1354    #[brw(magic(0u8))]
1355    NOP,
1356
1357    /// Machine code (tag 2).
1358    #[brw(magic(2u8))]
1359    Code(Code),
1360
1361    /// Run at offset (tag 4)
1362    #[brw(magic(4u8))]
1363    RunAtOffset(u16, u16),
1364
1365    /// Switch to different section (tag 6).
1366    #[brw(magic(6u8))]
1367    SectionSwitch(SectionSwitch),
1368
1369    /// Uninitialized data (BSS) with size in bytes (tag 8).
1370    #[brw(magic(8u8))]
1371    BSS(u32),
1372
1373    /// Relocation patch (tag 10).
1374    #[brw(magic(10u8))]
1375    Patch(Patch),
1376
1377    /// External symbol definition (tag 12).
1378    #[brw(magic(12u8))]
1379    XDEF(XDEF),
1380
1381    /// External symbol reference (tag 14).
1382    #[brw(magic(14u8))]
1383    XREF(XREF),
1384
1385    /// Section header (tag 16).
1386    #[brw(magic(16u8))]
1387    LNKHeader(LNKHeader),
1388
1389    /// Local symbol (tag 18).
1390    #[brw(magic(18u8))]
1391    LocalSymbol(LocalSymbol),
1392
1393    /// Group symbol (tag 20).
1394    #[brw(magic(20u8))]
1395    GroupSymbol(GroupSymbol),
1396
1397    // TODO:
1398    // 22 - set byte register size
1399    // 24 - set word register size
1400    // 26 - set long register size
1401    /// File name reference (tag 28).
1402    #[brw(magic(28u8))]
1403    Filename(Filename),
1404
1405    // TODO:
1406    // 30 - Set to file
1407    // 32 - Set to line
1408    // 34 - Increment line number
1409    // 36 - Increment line number by
1410    // 38 - Increment line number by
1411    // 40 - Very local symbol
1412    // 42 - Set 3-byte size register to
1413    /// Set MX info (tag 44).
1414    #[brw(magic(44u8))]
1415    SetMXInfo(SetMXInfo),
1416
1417    /// CPU type specification (tag 46).
1418    #[brw(magic(46u8))]
1419    CPU(u8),
1420
1421    /// External BSS symbol (tag 48).
1422    #[brw(magic(48u8))]
1423    XBSS(XBSS),
1424
1425    // Source line debugger information
1426    /// Increment line number (tag 50).
1427    #[brw(magic(50u8))]
1428    IncSLDLineNum(u16),
1429
1430    /// Increment line number by byte amount (tag 52).
1431    #[brw(magic(52u8))]
1432    IncSLDLineNumByte(u16, u8),
1433
1434    // 54 - Increment SDL line number by word
1435    /// Set line number (tag 56).
1436    #[brw(magic(56u8))]
1437    SetSLDLineNum(SetSLDLineNum),
1438
1439    /// Set line number with file (tag 58).
1440    #[brw(magic(58u8))]
1441    SetSLDLineNumFile(SetSLDLineNumFile),
1442
1443    /// End of SLD info (tag 60).
1444    #[brw(magic(60u8))]
1445    EndSLDInfo(u16),
1446
1447    // TODO:
1448    // 62 - Repeat byte
1449    // 64 - Repeat word
1450    // 66 - Repeat long
1451    // 68 - Proc call
1452    // 70 - Proc call 2 (prints 68)
1453    // 72 - repeat 3-byte
1454
1455    // Function and block debug information
1456    /// Function start marker (tag 74).
1457    #[brw(magic(74u8))]
1458    FunctionStart(FunctionStart),
1459
1460    /// Function end marker (tag 76).
1461    #[brw(magic(76u8))]
1462    FunctionEnd(FunctionEnd),
1463
1464    /// Block start marker (tag 78).
1465    #[brw(magic(78u8))]
1466    BlockStart(BlockStart),
1467
1468    /// Block end marker (tag 80).
1469    #[brw(magic(80u8))]
1470    BlockEnd(BlockEnd),
1471
1472    // Type and variable definitions
1473    /// Variable/type definition (tag 82).
1474    #[brw(magic(82u8))]
1475    Def(Def),
1476
1477    /// Extended definition with tag (tag 84).
1478    #[brw(magic(84u8))]
1479    Def2(Def2),
1480}
1481
1482/// Returns true if the LC_ALL or LANG environment variable indicates British English.
1483fn is_en_gb() -> bool {
1484    let lang = if let Ok(l) = std::env::var("LC_ALL") {
1485        l
1486    } else if let Ok(l) = std::env::var("LANG") {
1487        l
1488    } else {
1489        "".to_string()
1490    };
1491
1492    lang.starts_with("en_GB")
1493}
1494
1495impl fmt::Display for Section {
1496    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1497        self.fmt_with_options(f, &display::Options::default())
1498    }
1499}
1500
1501impl display::DisplayWithOptions for Section {
1502    fn fmt_with_options(&self, f: &mut fmt::Formatter, options: &display::Options) -> fmt::Result {
1503        match self {
1504            Self::NOP => write!(f, "0 : End of file"),
1505            Self::Code(code) => {
1506                write!(f, "2 : Code {} bytes", code.code.len())?;
1507                match options.code_format {
1508                    display::CodeFormat::Disassembly => {
1509                        writeln!(f, "\n")?;
1510                        for instruction in code.code.chunks(4) {
1511                            let ins = u32::from_le_bytes(instruction.try_into().unwrap());
1512                            let asm = Instruction::new(ins, 0x80000000, InstrCategory::CPU)
1513                                .disassemble(None, 0);
1514                            writeln!(f, "    /* {ins:08x} */   {asm}")?;
1515                        }
1516                    }
1517                    display::CodeFormat::Hex => {
1518                        writeln!(f, "\n")?;
1519                        for (i, chunk) in code.code.chunks(16).enumerate() {
1520                            write!(f, "{:04x}:", i * 16)?;
1521                            for byte in chunk {
1522                                write!(f, " {:02x}", byte)?;
1523                            }
1524                            writeln!(f)?;
1525                        }
1526                    }
1527                    display::CodeFormat::None => (),
1528                }
1529                Ok(())
1530            }
1531            Self::SectionSwitch(switch) => write!(f, "6 : Switch to section {:x}", switch.id),
1532            Self::BSS(size) => {
1533                let uninit = if is_en_gb() {
1534                    "Uninitialised"
1535                } else {
1536                    "Uninitialized"
1537                };
1538                write!(f, "8 : {} data, {} bytes", uninit, size)
1539            }
1540            Self::Patch(patch) => write!(
1541                f,
1542                "10 : Patch type {} at offset {:x} with {}",
1543                patch.tag, patch.offset, patch.expression
1544            ),
1545            Self::XDEF(xdef) => write!(
1546                f,
1547                "12 : XDEF symbol number {:x} '{}' at offset {:x} in section {:x}",
1548                xdef.number,
1549                xdef.symbol_name(),
1550                xdef.offset,
1551                xdef.section
1552            ),
1553            Self::XREF(xref) => write!(
1554                f,
1555                "14 : XREF symbol number {:x} '{}'",
1556                xref.number,
1557                xref.symbol_name()
1558            ),
1559            Self::LNKHeader(section) => write!(
1560                f,
1561                "16 : Section symbol number {:x} '{}' in group {} alignment {}",
1562                section.section,
1563                section.type_name(),
1564                section.group,
1565                section.align
1566            ),
1567            Self::LocalSymbol(symbol) => write!(
1568                f,
1569                "18 : Local symbol '{}' at offset {:x} in section {:x}",
1570                symbol.name(),
1571                symbol.offset,
1572                symbol.section
1573            ),
1574            Self::GroupSymbol(symbol) => write!(
1575                f,
1576                "20 : Group symbol number {:x} `{}` type {}",
1577                symbol.number,
1578                symbol.name(),
1579                symbol.sym_type,
1580            ),
1581            Self::Filename(filename) => write!(
1582                f,
1583                "28 : Define file number {:x} as \"{}\"",
1584                filename.number,
1585                filename.name()
1586            ),
1587            Self::SetMXInfo(set_mx_info) => write!(
1588                f,
1589                "44 : Set MX info at offset {:x} to {:x}",
1590                set_mx_info.offset, set_mx_info.value,
1591            ),
1592            Self::CPU(cpu) => write!(f, "46 : Processor type {}", { *cpu }),
1593            Self::XBSS(xbss) => write!(
1594                f,
1595                "48 : XBSS symbol number {:x} '{}' size {:x} in section {:x}",
1596                xbss.number,
1597                xbss.name(),
1598                xbss.size,
1599                xbss.section
1600            ),
1601            Self::IncSLDLineNum(offset) => write!(f, "50 : Inc SLD linenum at offset {offset:x}"),
1602            Self::IncSLDLineNumByte(offset, byte) => write!(
1603                f,
1604                "52 : Inc SLD linenum by byte {byte} at offset {offset:x}"
1605            ),
1606            Self::SetSLDLineNum(line) => write!(
1607                f,
1608                "56 : Set SLD linenum to {} at offset {:x}",
1609                line.linenum, line.offset
1610            ),
1611            Self::SetSLDLineNumFile(line) => write!(
1612                f,
1613                "58 : Set SLD linenum to {} at offset {:x} in file {:x}",
1614                line.linenum, line.offset, line.file
1615            ),
1616            Self::EndSLDInfo(offset) => write!(f, "60 : End SLD info at offset {offset:x}"),
1617            Self::FunctionStart(start) => write!(
1618                f,
1619                "74 : Function start :\n\
1620                \x20 section {:04x}\n\
1621                \x20 offset ${:08x}\n\
1622                \x20 file {:04x}\n\
1623                \x20 start line {}\n\
1624                \x20 frame reg {}\n\
1625                \x20 frame size {}\n\
1626                \x20 return pc reg {}\n\
1627                \x20 mask ${:08x}\n\
1628                \x20 mask offset {}\n\
1629                \x20 name {}",
1630                start.section,
1631                start.offset,
1632                start.file,
1633                start.linenum,
1634                start.frame_register,
1635                start.frame_size,
1636                start.return_pc_register,
1637                start.mask,
1638                start.mask_offset,
1639                start.name()
1640            ),
1641            Self::FunctionEnd(end) => write!(
1642                f,
1643                "76 : Function end :\n\
1644                \x20 section {:04x}\n\
1645                \x20 offset ${:08x}\n\
1646                \x20 end line {}",
1647                end.section, end.offset, end.linenum
1648            ),
1649            // n.b.! the missing newline before section is intentional to match the output of OBJDUMP.EXE
1650            Self::BlockStart(start) => write!(
1651                f,
1652                "78 : Block start :\
1653                \x20 section {:04x}\n\
1654                \x20 offset ${:08x}\n\
1655                \x20 start line {}",
1656                start.section, start.offset, start.linenum
1657            ),
1658            Self::BlockEnd(end) => write!(
1659                f,
1660                "80 : Block end\n\
1661                \x20 section {:04x}\n\
1662                \x20 offset ${:08x}\n\
1663                \x20 end line {}",
1664                end.section, end.offset, end.linenum
1665            ),
1666            Self::Def(def) => write!(
1667                f,
1668                "82 : Def :\n\
1669                \x20 section {:04x}\n\
1670                \x20 value ${:08x}\n\
1671                \x20 class {}\n\
1672                \x20 type {}\n\
1673                \x20 size {}\n\
1674                \x20 name : {}",
1675                def.section,
1676                def.value,
1677                def.class,
1678                def.def_type,
1679                def.size,
1680                def.name()
1681            ),
1682            Self::Def2(def) => write!(
1683                f,
1684                "84 : Def2 :\n\
1685                \x20 section {:04x}\n\
1686                \x20 value ${:08x}\n\
1687                \x20 class {}\n\
1688                \x20 type {}\n\
1689                \x20 size {}\n\
1690                \x20 dims {} \n\
1691                \x20 tag {}\n\
1692                {}",
1693                def.section,
1694                def.value,
1695                def.class,
1696                def.def_type,
1697                def.size,
1698                def.dims,
1699                def.tag(),
1700                def.name()
1701            ),
1702            _ => write!(f, "{self:?}"),
1703        }
1704    }
1705}
1706
1707#[cfg(test)]
1708mod test {
1709    use std::ffi::OsStr;
1710    use std::time::UNIX_EPOCH;
1711
1712    use super::*;
1713    use binrw::io::Cursor;
1714    use binrw::{BinRead, BinWrite};
1715
1716    #[test]
1717    fn test_datetime() {
1718        let t: u32 = 0x813320af;
1719        let dt = NaiveDateTime::from_psyq_timestamp(t).expect("datetime");
1720        assert_eq!(dt.year_ce().1, 1996);
1721        assert_eq!(dt.month(), 5);
1722        assert_eq!(dt.day(), 15);
1723        assert_eq!(dt.hour(), 16);
1724        assert_eq!(dt.minute(), 9);
1725        assert_eq!(dt.second(), 38);
1726        assert_eq!(t, dt.to_psyq_timestamp());
1727        let st = SystemTime::from_psyq_timestamp(t).expect("systemtime");
1728        assert_eq!(
1729            832176578u64,
1730            st.duration_since(UNIX_EPOCH).expect("duration").as_secs()
1731        );
1732        assert_eq!(t, st.to_psyq_timestamp());
1733
1734        let t: u32 = 0x8d061f4c;
1735        let dt = NaiveDateTime::from_psyq_timestamp(t).expect("datetime");
1736        assert_eq!(dt.year_ce().1, 1995);
1737        assert_eq!(dt.month(), 10);
1738        assert_eq!(dt.day(), 12);
1739        assert_eq!(dt.hour(), 17);
1740        assert_eq!(dt.minute(), 40);
1741        assert_eq!(dt.second(), 12);
1742        assert_eq!(t, dt.to_psyq_timestamp());
1743        let st = SystemTime::from_psyq_timestamp(t).expect("systemtime");
1744        assert_eq!(
1745            813519612u64,
1746            st.duration_since(UNIX_EPOCH).expect("duration").as_secs()
1747        );
1748        assert_eq!(t, st.to_psyq_timestamp());
1749    }
1750
1751    #[test]
1752    fn test_path_to_module_name() {
1753        assert_eq!(
1754            *b"OUTPUT  ",
1755            path_to_module_name(Path::new("some/output.obj"))
1756        );
1757        assert_eq!(
1758            *b"LONGNAME",
1759            path_to_module_name(Path::new("some/longname.obj"))
1760        );
1761        // name is truncated to 8 characters
1762        assert_eq!(
1763            *b"LONGERNA",
1764            path_to_module_name(Path::new("some/longername.obj"))
1765        );
1766        // strings with code points that fit into 8-bytes are "fine"
1767        let name: [u8; 8] = "👾    ".as_bytes().try_into().unwrap();
1768        assert_eq!(name, path_to_module_name(Path::new("some/👾.obj")));
1769        // strings with code points that are split are not
1770        let name: [u8; 8] = "👾☕ ".as_bytes().try_into().unwrap();
1771        assert_eq!(name, path_to_module_name(Path::new("some/👾☕☕.obj")));
1772        // all 8-bytes consumed by multi-byte
1773        let name: [u8; 8] = "👾👾".as_bytes().try_into().unwrap();
1774        assert_eq!(name, path_to_module_name(Path::new("some/👾👾.obj")));
1775        // diacritics
1776        let name: [u8; 8] = "AÍ¢B    ".as_bytes().try_into().unwrap();
1777        assert_eq!(name, path_to_module_name(Path::new("some/a͢b.obj")));
1778    }
1779
1780    #[test]
1781    #[should_panic]
1782    fn test_path_to_module_name_missing_file_name() {
1783        path_to_module_name(Path::new("."));
1784    }
1785
1786    #[test]
1787    #[should_panic]
1788    fn test_path_to_module_name_invalid_unicode() {
1789        // b"\u{C0}invalid.obj"
1790        let s: &OsStr;
1791        unsafe {
1792            s = OsStr::from_encoded_bytes_unchecked(&[
1793                0xC0, 0x69, 0x6E, 0x76, 0x61, 0x6C, 0x69, 0x64, 0x2e, 0x6f, 0x62, 0x6a,
1794            ]);
1795        }
1796        path_to_module_name(Path::new(s));
1797    }
1798
1799    #[test]
1800    fn test_lib() {
1801        let bytes = b"\
1802           \x4C\x49\x42\x01\x41\x35\x36\x20\x20\x20\x20\x20\xAF\x20\x2C\x81\
1803           \x1A\x00\x00\x00\x8E\x00\x00\x00\x04\x65\x78\x69\x74\x00\x4C\x4E\
1804           \x4B\x02\x2E\x07\x10\x04\xF0\x00\x00\x08\x06\x2E\x72\x64\x61\x74\
1805           \x61\x10\x00\xF0\x00\x00\x08\x05\x2E\x74\x65\x78\x74\x10\x01\xF0\
1806           \x00\x00\x08\x05\x2E\x64\x61\x74\x61\x10\x03\xF0\x00\x00\x08\x06\
1807           \x2E\x73\x64\x61\x74\x61\x10\x05\xF0\x00\x00\x08\x04\x2E\x62\x73\
1808           \x73\x10\x02\xF0\x00\x00\x08\x05\x2E\x73\x62\x73\x73\x0C\x01\x00\
1809           \x00\xF0\x00\x00\x00\x00\x04\x65\x78\x69\x74\x06\x00\xF0\x02\x10\
1810           \x00\xB0\x00\x0A\x24\x08\x00\x40\x01\x38\x00\x09\x24\x00\x00\x00\
1811           \x00\x00"
1812            .to_vec();
1813        //.0.  1.  2.  3.  4.  5.  6.  7.  8.  9.  A.  B.  C.  D.  E.  F.
1814        let mut data = Cursor::new(&bytes);
1815        let lib = LIB::read(&mut data).unwrap();
1816        assert_eq!(lib.version, 1);
1817        // assert_eq!(lib.modules().len(), 1);
1818
1819        let obj = lib.modules().first().expect("obj[0]");
1820        assert_eq!(obj.name(), "A56");
1821        assert_eq!(obj.metadata.created, 2167152815);
1822        assert_eq!(obj.metadata.offset, 26);
1823        assert_eq!(obj.metadata.size, 142);
1824        assert_eq!(obj.metadata.exports.len(), 2);
1825        assert_eq!(obj.exports().len(), 1);
1826
1827        let export = obj.metadata.exports.first().expect("obj[0].exports[0]");
1828        assert_eq!(export.name_size, 4);
1829        assert_eq!(export.name(), "exit");
1830
1831        let lnk = &obj.obj;
1832        assert_eq!(lnk.version, 2);
1833
1834        let Section::CPU(cpu) = lnk.sections.first().expect("obj[0].obj.sections[0]") else {
1835            panic!("expected a section");
1836        };
1837        assert_eq!(*cpu, cputype::MIPS_R300GTE);
1838        /*
1839                assert_eq!(section.section, 61444);
1840                assert_eq!(section.group, 0);
1841                assert_eq!(section.align, 8);
1842                assert_eq!(section.type_name_size, 6);
1843                assert_eq!(section.type_name(), ".rdata");
1844        */
1845
1846        assert_eq!(data.position(), bytes.len() as u64);
1847
1848        // roundtrip
1849        let mut writer = Cursor::new(Vec::new());
1850        lib.write_le(&mut writer).unwrap();
1851        assert_eq!(writer.into_inner(), bytes);
1852    }
1853
1854    #[test]
1855    fn test_object_entry() {
1856        let bytes = b"\
1857            \x53\x50\x52\x49\x4E\x54\x46\x20\xAF\x20\x33\x81\x1D\x00\x00\x00\
1858            \x25\x0E\x00\x00\x07\x73\x70\x72\x69\x6E\x74\x66\x00\x4C\x4E\x4B\
1859            \x02\x2E\x07\x10\x01\x00\x00\x00\x08\x06\x2E\x72\x64\x61\x74\x61\
1860            \x10\x02\x00\x00\x00\x08\x05\x2E\x74\x65\x78\x74\x10\x03\x00\x00\
1861            \x00\x08\x05\x2E\x64\x61\x74\x61\x10\x04\x00\x00\x00\x08\x06\x2E\
1862            \x73\x64\x61\x74\x61\x10\x05\x00\x00\x00\x08\x05\x2E\x73\x62\x73\
1863            \x73\x10\x06\x00\x00\x00\x08\x04\x2E\x62\x73\x73\x10\x07\x00\x00\
1864            \x00\x08\x06\x2E\x63\x74\x6F\x72\x73\x10\x08\x00\x00\x00\x08\x06\
1865            \x2E\x64\x74\x6F\x72\x73\x1C\x09\x00\x17\x43\x3A\x5C\x50\x53\x58\
1866            \x5C\x53\x52\x43\x5C\x43\x32\x5C\x53\x50\x52\x49\x4E\x54\x46\x2E\
1867            \x43\x06\x02\x00\x06\x03\x00\x02\x01\x00\x00\x08\x0B\x00\x00\x00\
1868            \x06\x01\x00\x02\x25\x00\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\
1869            \x41\x42\x43\x44\x45\x46\x00\x00\x00\x00\x30\x31\x32\x33\x34\x35\
1870            \x36\x37\x38\x39\x61\x62\x63\x64\x65\x66\x00\x06\x02\x00\x02\xC8\
1871            \x02\x04\x00\xA5\xAF\x08\x00\xA6\xAF\x0C\x00\xA7\xAF\xB8\xFD\xBD\
1872            \x27\x34\x02\xB3\xAF\x21\x98\x80\x00\x50\x02\xA2\x27\x44\x02\xBF\
1873            \xAF\x40\x02\xB6\xAF\x3C\x02\xB5\xAF\x38\x02\xB4\xAF\x30\x02\xB2\
1874            \xAF\x2C\x02\xB1\xAF\x28\x02\xB0\xAF\x4C\x02\xA5\xAF\x20\x02\xA2\
1875            \xAF\x00\x00\xA5\x90\x00\x00\x00\x00\xF6\x01\xA0\x10\x21\x90\x00\
1876            \x00\x2D\x00\x16\x34\x2B\x00\x15\x34\x20\x00\x14\x34\x25\x00\x02\
1877            \x34\xC0\x01\xA2\x14\x21\x10\x72\x02\x00\x00\x05\x3C\x00\x00\xA5\
1878            \x24\x00\x00\xA2\x8C\x04\x00\xA3\x8C\x08\x00\xA4\x8C\x10\x02\xA2\
1879            \xAF\x14\x02\xA3\xAF\x18\x02\xA4\xAF\x23\x00\x06\x34\x30\x00\x03\
1880            \x34\x4C\x02\xA4\x8F\x00\x00\x00\x00\x01\x00\x82\x24\x4C\x02\xA2\
1881            \xAF\x01\x00\x85\x90\x00\x00\x00\x00\x06\x00\xB6\x14\x00\x00\x00\
1882            \x00\x10\x02\xA2\x8F\x00\x00\x00\x00\x01\x00\x42\x34\x00\x00\x00\
1883            \x08\x10\x02\xA2\xAF\x06\x00\xB5\x14\x00\x00\x00\x00\x10\x02\xA2\
1884            \x8F\x00\x00\x00\x00\x02\x00\x42\x34\x00\x00\x00\x08\x10\x02\xA2\
1885            \xAF\x03\x00\xB4\x14\x00\x00\x00\x00\x00\x00\x00\x08\x11\x02\xA5\
1886            \xA3\x06\x00\xA6\x14\x00\x00\x00\x00\x10\x02\xA2\x8F\x00\x00\x00\
1887            \x00\x04\x00\x42\x34\x00\x00\x00\x08\x10\x02\xA2\xAF\x06\x00\xA3\
1888            \x14\x2A\x00\x02\x34\x10\x02\xA2\x8F\x00\x00\x00\x00\x08\x00\x42\
1889            \x34\x00\x00\x00\x08\x10\x02\xA2\xAF\x22\x00\xA2\x14\xD0\xFF\xA2\
1890            \x24\x20\x02\xA3\x8F\x00\x00\x00\x00\x04\x00\x62\x24\x20\x02\xA2\
1891            \xAF\x00\x00\x62\x8C\x00\x00\x00\x00\x06\x00\x41\x04\x14\x02\xA2\
1892            \xAF\x10\x02\xA3\x8F\x23\x10\x02\x00\x14\x02\xA2\xAF\x01\x00\x63\
1893            \x34\x10\x02\xA3\xAF\x02\x00\x82\x24\x4C\x02\xA2\xAF\x02\x00\x85\
1894            \x90\x00\x00\x00\x08\x2E\x00\x02\x34\x14\x02\xA3\x8F\x00\x00\x00\
1895            \x00\x80\x10\x03\x00\x21\x10\x43\x00\x40\x10\x02\x00\xD0\xFF\x42\
1896            \x24\x21\x10\x45\x00\x14\x02\xA2\xAF\x4C\x02\xA3\x8F\x00\x00\x00\
1897            \x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\x01\x00\x65\x90\x00\x00\x00\
1898            \x00\xD0\xFF\xA2\x24\x0A\x00\x42\x2C\xEF\xFF\x40\x14\x2E\x00\x02\
1899            \x34\x2F\x00\xA2\x14\x00\x00\x00\x00\x4C\x02\xA4\x8F\x00\x00\x00\
1900            \x00\x01\x00\x82\x24\x4C\x02\xA2\xAF\x01\x00\x85\x90\x2A\x00\x02\
1901            \x34\x1C\x00\xA2\x14\xD0\xFF\xA2\x24\x20\x02\xA3\x8F\x00\x00\x00\
1902            \x00\x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x62\x8C\x00\x00\x00\
1903            \x00\x18\x02\xA2\xAF\x02\x00\x82\x24\x4C\x02\xA2\xAF\x02\x00\x85\
1904            \x90\x00\x00\x00\x08\x00\x00\x00\x00\x18\x02\xA3\x8F\x00\x00\x00\
1905            \x00\x80\x10\x03\x00\x21\x10\x43\x00\x40\x10\x02\x00\xD0\xFF\x42\
1906            \x24\x21\x10\x45\x00\x18\x02\xA2\xAF\x4C\x02\xA3\x8F\x00\x00\x00\
1907            \x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\x01\x00\x65\x90\x00\x00\x00\
1908            \x00\xD0\xFF\xA2\x24\x0A\x00\x42\x2C\xEF\xFF\x40\x14\x00\x00\x00\
1909            \x00\x18\x02\xA2\x8F\x00\x00\x00\x00\x05\x00\x40\x04\x00\x00\x00\
1910            \x00\x10\x02\xA2\x8F\x00\x00\x00\x00\x10\x00\x42\x34\x10\x02\xA2\
1911            \xAF\x10\x02\xA3\x8F\x00\x00\x00\x00\x01\x00\x62\x30\x04\x00\x40\
1912            \x10\x10\x02\xB1\x27\xF7\xFF\x02\x24\x24\x10\x62\x00\x10\x02\xA2\
1913            \xAF\xB4\xFF\xA3\x24\x2D\x00\x62\x2C\x2B\x01\x40\x10\x80\x10\x03\
1914            \x00\x00\x00\x01\x3C\x21\x08\x22\x00\x00\x00\x22\x8C\x00\x00\x00\
1915            \x00\x08\x00\x40\x00\x00\x00\x00\x00\x0A\x52\x68\x00\x2C\x04\x03\
1916            \x00\x00\x00\x00\x00\x00\x0A\x54\x6C\x00\x2C\x04\x03\x00\x00\x00\
1917            \x00\x00\x00\x0A\x4A\xBC\x00\x2C\x04\x02\x00\x00\x90\x00\x00\x00\
1918            \x0A\x4A\xD8\x00\x2C\x04\x02\x00\x00\x90\x00\x00\x00\x0A\x4A\xE8\
1919            \x00\x2C\x04\x02\x00\x00\x90\x00\x00\x00\x0A\x4A\x04\x01\x2C\x04\
1920            \x02\x00\x00\x90\x00\x00\x00\x0A\x4A\x20\x01\x2C\x04\x02\x00\x00\
1921            \x90\x00\x00\x00\x0A\x4A\x70\x01\x2C\x04\x02\x00\x00\xC0\x01\x00\
1922            \x00\x0A\x4A\x10\x02\x2C\x04\x02\x00\x00\x60\x02\x00\x00\x0A\x52\
1923            \xB0\x02\x2C\x04\x01\x00\x00\x28\x00\x00\x00\x0A\x54\xB8\x02\x2C\
1924            \x04\x01\x00\x00\x28\x00\x00\x00\x06\x01\x00\x02\xB7\x00\x00\x00\
1925            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1926            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1927            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1928            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1929            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1930            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1931            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1932            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1933            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1934            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1935            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1936            \x00\x00\x00\x00\x00\x0A\x10\x03\x00\x2C\x04\x02\x00\x00\xE0\x02\
1937            \x00\x00\x0A\x10\x07\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\
1938            \x10\x0B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x0F\x00\
1939            \x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x13\x00\x2C\x04\x02\
1940            \x00\x00\x58\x07\x00\x00\x0A\x10\x17\x00\x2C\x04\x02\x00\x00\x58\
1941            \x07\x00\x00\x0A\x10\x1B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\
1942            \x0A\x10\x1F\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x23\
1943            \x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x27\x00\x2C\x04\
1944            \x02\x00\x00\x58\x07\x00\x00\x0A\x10\x2B\x00\x2C\x04\x02\x00\x00\
1945            \x58\x07\x00\x00\x0A\x10\x2F\x00\x2C\x04\x02\x00\x00\x58\x07\x00\
1946            \x00\x0A\x10\x33\x00\x2C\x04\x02\x00\x00\x78\x05\x00\x00\x0A\x10\
1947            \x37\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x3B\x00\x2C\
1948            \x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x3F\x00\x2C\x04\x02\x00\
1949            \x00\x58\x07\x00\x00\x0A\x10\x43\x00\x2C\x04\x02\x00\x00\x58\x07\
1950            \x00\x00\x0A\x10\x47\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\
1951            \x10\x4B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x4F\x00\
1952            \x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x53\x00\x2C\x04\x02\
1953            \x00\x00\x58\x07\x00\x00\x0A\x10\x57\x00\x2C\x04\x02\x00\x00\x58\
1954            \x07\x00\x00\x0A\x10\x5B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\
1955            \x0A\x10\x5F\x00\x2C\x04\x02\x00\x00\x80\x06\x00\x00\x0A\x10\x63\
1956            \x00\x2C\x04\x02\x00\x00\x0C\x03\x00\x00\x0A\x10\x67\x00\x2C\x04\
1957            \x02\x00\x00\x58\x07\x00\x00\x0A\x10\x6B\x00\x2C\x04\x02\x00\x00\
1958            \x58\x07\x00\x00\x0A\x10\x6F\x00\x2C\x04\x02\x00\x00\x58\x07\x00\
1959            \x00\x0A\x10\x73\x00\x2C\x04\x02\x00\x00\xC8\x02\x00\x00\x0A\x10\
1960            \x77\x00\x2C\x04\x02\x00\x00\x0C\x03\x00\x00\x0A\x10\x7B\x00\x2C\
1961            \x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\x7F\x00\x2C\x04\x02\x00\
1962            \x00\x58\x07\x00\x00\x0A\x10\x83\x00\x2C\x04\x02\x00\x00\xD4\x02\
1963            \x00\x00\x0A\x10\x87\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\
1964            \x10\x8B\x00\x2C\x04\x02\x00\x00\x24\x07\x00\x00\x0A\x10\x8F\x00\
1965            \x2C\x04\x02\x00\x00\x74\x04\x00\x00\x0A\x10\x93\x00\x2C\x04\x02\
1966            \x00\x00\x64\x05\x00\x00\x0A\x10\x97\x00\x2C\x04\x02\x00\x00\x58\
1967            \x07\x00\x00\x0A\x10\x9B\x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\
1968            \x0A\x10\x9F\x00\x2C\x04\x02\x00\x00\xA0\x06\x00\x00\x0A\x10\xA3\
1969            \x00\x2C\x04\x02\x00\x00\x58\x07\x00\x00\x0A\x10\xA7\x00\x2C\x04\
1970            \x02\x00\x00\x5C\x03\x00\x00\x0A\x10\xAB\x00\x2C\x04\x02\x00\x00\
1971            \x58\x07\x00\x00\x0A\x10\xAF\x00\x2C\x04\x02\x00\x00\x58\x07\x00\
1972            \x00\x0A\x10\xB3\x00\x2C\x04\x02\x00\x00\x88\x05\x00\x00\x06\x02\
1973            \x00\x02\x94\x05\x10\x02\xA2\x8F\x00\x00\x00\x08\x20\x00\x42\x34\
1974            \x10\x02\xA2\x8F\x00\x00\x00\x08\x40\x00\x42\x34\x10\x02\xA2\x8F\
1975            \x00\x00\x00\x00\x80\x00\x42\x34\x10\x02\xA2\xAF\x4C\x02\xA3\x8F\
1976            \x00\x00\x00\x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\x01\x00\x65\x90\
1977            \x00\x00\x00\x08\xB4\xFF\xA3\x24\x20\x02\xA3\x8F\x00\x00\x00\x00\
1978            \x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x64\x8C\x10\x02\xA3\x8F\
1979            \x00\x00\x00\x00\x20\x00\x62\x30\x02\x00\x40\x10\x00\x14\x04\x00\
1980            \x03\x24\x02\x00\x04\x00\x81\x04\x02\x00\x62\x30\x23\x20\x04\x00\
1981            \x00\x00\x00\x08\x11\x02\xB6\xA3\x0E\x00\x40\x10\x00\x00\x00\x00\
1982            \x00\x00\x00\x08\x11\x02\xB5\xA3\x20\x02\xA3\x8F\x00\x00\x00\x00\
1983            \x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x64\x8C\x10\x02\xA2\x8F\
1984            \x00\x00\x00\x00\x20\x00\x42\x30\x02\x00\x40\x10\x11\x02\xA0\xA3\
1985            \xFF\xFF\x84\x30\x10\x02\xA3\x8F\x00\x00\x00\x00\x10\x00\x62\x30\
1986            \x0F\x00\x40\x14\x08\x00\x62\x30\x08\x00\x40\x10\x00\x00\x00\x00\
1987            \x14\x02\xA3\x8F\x11\x02\xA2\x93\x00\x00\x00\x00\x03\x00\x40\x10\
1988            \x18\x02\xA3\xAF\xFF\xFF\x62\x24\x18\x02\xA2\xAF\x18\x02\xA2\x8F\
1989            \x00\x00\x00\x00\x02\x00\x40\x1C\x01\x00\x02\x34\x18\x02\xA2\xAF\
1990            \x10\x00\x80\x10\x21\x80\x00\x00\xCC\xCC\x05\x3C\xCD\xCC\xA5\x34\
1991            \x19\x00\x85\x00\xFF\xFF\x31\x26\x01\x00\x10\x26\x10\x18\x00\x00\
1992            \xC2\x18\x03\x00\x80\x10\x03\x00\x21\x10\x43\x00\x40\x10\x02\x00\
1993            \x23\x10\x82\x00\x30\x00\x42\x24\x21\x20\x60\x00\xF4\xFF\x80\x14\
1994            \x00\x00\x22\xA2\x18\x02\xA2\x8F\x00\x00\x00\x00\x2A\x10\x02\x02\
1995            \x0A\x00\x40\x10\x00\x00\x00\x00\x30\x00\x03\x34\xFF\xFF\x31\x26\
1996            \x00\x00\x23\xA2\x18\x02\xA2\x8F\x01\x00\x10\x26\x2A\x10\x02\x02\
1997            \xFB\xFF\x40\x14\xFF\xFF\x31\x26\x01\x00\x31\x26\x11\x02\xA2\x93\
1998            \x00\x00\x00\x00\xC5\x00\x40\x10\x00\x00\x00\x00\xFF\xFF\x31\x26\
1999            \x11\x02\xA2\x93\x01\x00\x10\x26\x00\x00\x00\x08\x00\x00\x22\xA2\
2000            \x20\x02\xA3\x8F\x00\x00\x00\x00\x04\x00\x62\x24\x20\x02\xA2\xAF\
2001            \x00\x00\x64\x8C\x10\x02\xA3\x8F\x00\x00\x00\x00\x20\x00\x62\x30\
2002            \x02\x00\x40\x10\x10\x00\x62\x30\xFF\xFF\x84\x30\x0B\x00\x40\x14\
2003            \x08\x00\x62\x30\x04\x00\x40\x10\x00\x00\x00\x00\x14\x02\xA2\x8F\
2004            \x00\x00\x00\x00\x18\x02\xA2\xAF\x18\x02\xA2\x8F\x00\x00\x00\x00\
2005            \x02\x00\x40\x1C\x01\x00\x02\x34\x18\x02\xA2\xAF\x08\x00\x80\x10\
2006            \x21\x80\x00\x00\xFF\xFF\x31\x26\x07\x00\x82\x30\x30\x00\x42\x24\
2007            \x00\x00\x22\xA2\xC2\x20\x04\x00\xFA\xFF\x80\x14\x01\x00\x10\x26\
2008            \x10\x02\xA2\x8F\x00\x00\x00\x00\x04\x00\x42\x30\x0A\x00\x40\x10\
2009            \x00\x00\x00\x00\x08\x00\x00\x12\x30\x00\x02\x34\x00\x00\x23\x92\
2010            \x00\x00\x00\x00\x04\x00\x62\x10\x30\x00\x02\x34\xFF\xFF\x31\x26\
2011            \x00\x00\x22\xA2\x01\x00\x10\x26\x18\x02\xA2\x8F\x00\x00\x00\x00\
2012            \x2A\x10\x02\x02\x8D\x00\x40\x10\x30\x00\x03\x34\xFF\xFF\x31\x26\
2013            \x00\x00\x23\xA2\x18\x02\xA2\x8F\x01\x00\x10\x26\x2A\x10\x02\x02\
2014            \xFB\xFF\x40\x14\xFF\xFF\x31\x26\x00\x00\x00\x08\x01\x00\x31\x26\
2015            \x10\x02\xA3\x8F\x08\x00\x02\x34\x18\x02\xA2\xAF\x50\x00\x63\x34\
2016            \x10\x02\xA3\xAF\x00\x00\x07\x3C\x00\x00\xE7\x24\x00\x00\x00\x08\
2017            \x00\x00\x00\x00\x00\x00\x07\x3C\x00\x00\xE7\x24\x20\x02\xA3\x8F\
2018            \x00\x00\x00\x00\x04\x00\x62\x24\x20\x02\xA2\xAF\x00\x00\x64\x8C\
2019            \x10\x02\xA3\x8F\x00\x00\x00\x00\x20\x00\x62\x30\x02\x00\x40\x10\
2020            \x10\x00\x62\x30\xFF\xFF\x84\x30\x0D\x00\x40\x14\x08\x00\x62\x30\
2021            \x06\x00\x40\x10\x04\x00\x62\x30\x14\x02\xA6\x8F\x03\x00\x40\x10\
2022            \x18\x02\xA6\xAF\xFE\xFF\xC2\x24\x18\x02\xA2\xAF\x18\x02\xA2\x8F\
2023            \x00\x00\x00\x00\x02\x00\x40\x1C\x01\x00\x02\x34\x18\x02\xA2\xAF\
2024            \x09\x00\x80\x10\x21\x80\x00\x00\xFF\xFF\x31\x26\x0F\x00\x82\x30\
2025            \x02\x21\x04\x00\x21\x10\xE2\x00\x00\x00\x42\x90\x01\x00\x10\x26\
2026            \xF9\xFF\x80\x14\x00\x00\x22\xA2\x18\x02\xA2\x8F\x00\x00\x00\x00\
2027            \x2A\x10\x02\x02\x0A\x00\x40\x10\x00\x00\x00\x00\x30\x00\x03\x34\
2028            \xFF\xFF\x31\x26\x00\x00\x23\xA2\x18\x02\xA2\x8F\x01\x00\x10\x26\
2029            \x2A\x10\x02\x02\xFB\xFF\x40\x14\xFF\xFF\x31\x26\x01\x00\x31\x26\
2030            \x10\x02\xA2\x8F\x00\x00\x00\x00\x04\x00\x42\x30\x43\x00\x40\x10\
2031            \x30\x00\x02\x34\xFF\xFF\x31\x26\x00\x00\x25\xA2\xFF\xFF\x31\x26\
2032            \x02\x00\x10\x26\x00\x00\x00\x08\x00\x00\x22\xA2\x20\x02\xA2\x8F\
2033            \xFF\xFF\x31\x26\x04\x00\x43\x24\x20\x02\xA3\xAF\x00\x00\x42\x90\
2034            \x01\x00\x10\x34\x00\x00\x00\x08\x00\x00\x22\xA2\x20\x02\xA2\x8F\
2035            \x00\x00\x00\x00\x04\x00\x43\x24\x20\x02\xA3\xAF\x10\x02\xA3\x8F\
2036            \x00\x00\x51\x8C\x04\x00\x62\x30\x0B\x00\x40\x10\x10\x00\x62\x30\
2037            \x00\x00\x30\x92\x29\x00\x40\x10\x01\x00\x31\x26\x18\x02\xA3\x8F\
2038            \x00\x00\x00\x00\x2A\x10\x70\x00\x24\x00\x40\x10\x00\x00\x00\x00\
2039            \x00\x00\x00\x08\x21\x80\x60\x00\x05\x00\x40\x14\x21\x20\x20\x02\
2040            \x00\x00\x00\x0C\x21\x20\x20\x02\x00\x00\x00\x08\x21\x80\x40\x00\
2041            \x18\x02\xA6\x8F\x00\x00\x00\x0C\x21\x28\x00\x00\x17\x00\x40\x14\
2042            \x23\x80\x51\x00\x18\x02\xB0\x8F\x00\x00\x00\x08\x00\x00\x00\x00\
2043            \x20\x02\xA2\x8F\x00\x00\x00\x00\x04\x00\x43\x24\x20\x02\xA3\xAF\
2044            \x10\x02\xA3\x8F\x00\x00\x51\x8C\x20\x00\x62\x30\x03\x00\x40\x10\
2045            \x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x32\xA6\x00\x00\x00\x08\
2046            \x00\x00\x32\xAE\x25\x00\x02\x34\x31\x00\xA2\x14\x21\x10\x72\x02\
2047            \x00\x00\x45\xA0\x00\x00\x00\x08\x01\x00\x52\x26\x14\x02\xA2\x8F\
2048            \x00\x00\x00\x00\x2A\x10\x02\x02\x11\x00\x40\x10\x21\x20\x72\x02\
2049            \x10\x02\xA2\x8F\x00\x00\x00\x00\x01\x00\x42\x30\x0D\x00\x40\x14\
2050            \x21\x28\x20\x02\x21\x18\x53\x02\x00\x00\x74\xA0\x01\x00\x63\x24\
2051            \x14\x02\xA2\x8F\x00\x00\x00\x00\xFF\xFF\x42\x24\x14\x02\xA2\xAF\
2052            \x2A\x10\x02\x02\xF8\xFF\x40\x14\x01\x00\x52\x26\x21\x20\x72\x02\
2053            \x21\x28\x20\x02\x00\x00\x00\x0C\x21\x30\x00\x02\x14\x02\xA2\x8F\
2054            \x00\x00\x00\x00\x2A\x10\x02\x02\x09\x00\x40\x10\x21\x90\x50\x02\
2055            \x21\x18\x53\x02\x00\x00\x74\xA0\x01\x00\x63\x24\x14\x02\xA2\x8F\
2056            \x01\x00\x10\x26\x2A\x10\x02\x02\xFA\xFF\x40\x14\x01\x00\x52\x26\
2057            \x4C\x02\xA3\x8F\x00\x00\x00\x00\x01\x00\x62\x24\x4C\x02\xA2\xAF\
2058            \x01\x00\x65\x90\x00\x00\x00\x00\x10\xFE\xA0\x14\x25\x00\x02\x34\
2059            \x21\x10\x72\x02\x00\x00\x40\xA0\x21\x10\x40\x02\x44\x02\xBF\x8F\
2060            \x40\x02\xB6\x8F\x3C\x02\xB5\x8F\x38\x02\xB4\x8F\x34\x02\xB3\x8F\
2061            \x30\x02\xB2\x8F\x2C\x02\xB1\x8F\x28\x02\xB0\x8F\x48\x02\xBD\x27\
2062            \x08\x00\xE0\x03\x00\x00\x00\x00\x0A\x4A\x04\x00\x2C\x04\x02\x00\
2063            \x00\xEC\x02\x00\x00\x0A\x4A\x10\x00\x2C\x04\x02\x00\x00\xEC\x02\
2064            \x00\x00\x0A\x4A\x3C\x00\x2C\x04\x02\x00\x00\xA4\x02\x00\x00\x0A\
2065            \x4A\x7C\x00\x2C\x04\x02\x00\x00\x88\x03\x00\x00\x0A\x4A\x8C\x00\
2066            \x2C\x04\x02\x00\x00\x88\x03\x00\x00\x0A\x4A\xA4\x01\x2C\x04\x02\
2067            \x00\x00\x70\x07\x00\x00\x0A\x4A\x94\x02\x2C\x04\x02\x00\x00\x70\
2068            \x07\x00\x00\x0A\x52\xB0\x02\x2C\x04\x01\x00\x00\x00\x00\x00\x00\
2069            \x0A\x54\xB4\x02\x2C\x04\x01\x00\x00\x00\x00\x00\x00\x0A\x4A\xB8\
2070            \x02\x2C\x04\x02\x00\x00\x90\x05\x00\x00\x0A\x52\xC0\x02\x2C\x04\
2071            \x01\x00\x00\x14\x00\x00\x00\x0A\x54\xC4\x02\x2C\x04\x01\x00\x00\
2072            \x14\x00\x00\x00\x0A\x4A\xB0\x03\x2C\x04\x02\x00\x00\x70\x07\x00\
2073            \x00\x0A\x4A\xD0\x03\x2C\x04\x02\x00\x00\x70\x07\x00\x00\x0A\x4A\
2074            \x1C\x04\x2C\x04\x02\x00\x00\x70\x07\x00\x00\x0A\x4A\x2C\x04\x02\
2075            \x0B\x00\x0A\x4A\x34\x04\x2C\x04\x02\x00\x00\x70\x07\x00\x00\x0A\
2076            \x4A\x40\x04\x02\x0C\x00\x0A\x4A\x54\x04\x2C\x04\x02\x00\x00\x70\
2077            \x07\x00\x00\x0A\x4A\x80\x04\x2C\x04\x02\x00\x00\x04\x08\x00\x00\
2078            \x0A\x4A\x88\x04\x2C\x04\x02\x00\x00\x04\x08\x00\x00\x0A\x4A\xA0\
2079            \x04\x2C\x04\x02\x00\x00\x04\x08\x00\x00\x0A\x4A\x00\x05\x02\x0D\
2080            \x00\x06\x02\x00\x0C\x0A\x00\x02\x00\x00\x00\x00\x00\x07\x73\x70\
2081            \x72\x69\x6E\x74\x66\x0E\x0C\x00\x06\x6D\x65\x6D\x63\x68\x72\x0E\
2082            \x0B\x00\x06\x73\x74\x72\x6C\x65\x6E\x0E\x0D\x00\x07\x6D\x65\x6D\
2083            \x6D\x6F\x76\x65\x00"
2084            .to_vec();
2085        //.0.  1.  2.  3.  4.  5.  6.  7.  8.  9.  A.  B.  C.  D.  E.  F.
2086        let mut data = Cursor::new(&bytes);
2087        let obj = Module::read(&mut data).unwrap();
2088
2089        eprintln!("obj: {:?}", obj);
2090
2091        assert_eq!(obj.name(), "SPRINTF");
2092        // assert_eq!(obj.created, 2167611567);
2093        // TODO: this should be based on locale
2094        assert_eq!(obj.created(), "15-05-96 16:09:38");
2095        assert_eq!(obj.metadata.offset, 29);
2096        assert_eq!(obj.metadata.size, 3621);
2097        assert_eq!(obj.metadata.exports.len(), 2);
2098
2099        let export = obj.metadata.exports.first().expect("obj[0].exports[0]");
2100        assert_eq!(export.name_size, 7);
2101        assert_eq!(export.name(), "sprintf");
2102
2103        let lnk = &obj.obj;
2104        assert_eq!(lnk.version, 2);
2105
2106        let Section::CPU(cpu) = lnk.sections.first().expect("obj[0].obj.sections[0]") else {
2107            panic!("expected a section");
2108        };
2109        assert_eq!(*cpu, cputype::MIPS_R300GTE);
2110        /*
2111        assert_eq!(section.section, 1);
2112        assert_eq!(section.group, 0);
2113        assert_eq!(section.align, 8);
2114        assert_eq!(section.type_name_size, 6);
2115        assert_eq!(section.type_name(), ".rdata");
2116        */
2117
2118        assert_eq!(data.position(), bytes.len() as u64);
2119
2120        let mut writer = Cursor::new(Vec::new());
2121        obj.write_le(&mut writer).unwrap();
2122        assert_eq!(writer.into_inner(), bytes);
2123    }
2124
2125    #[test]
2126    fn test_2_mbyte() {
2127        let bytes = b"\
2128            \x4C\x4E\x4B\x02\x2E\x07\x10\x08\x28\x00\x00\x08\x06\x2E\x72\x64\
2129            \x61\x74\x61\x10\x09\x28\x00\x00\x08\x05\x2E\x74\x65\x78\x74\x10\
2130            \x0A\x28\x00\x00\x08\x05\x2E\x64\x61\x74\x61\x10\x0B\x28\x00\x00\
2131            \x08\x06\x2E\x73\x64\x61\x74\x61\x10\x0C\x28\x00\x00\x08\x05\x2E\
2132            \x73\x62\x73\x73\x10\x0D\x28\x00\x00\x08\x04\x2E\x62\x73\x73\x06\
2133            \x08\x28\x06\x09\x28\x06\x0A\x28\x06\x0B\x28\x06\x0C\x28\x06\x0D\
2134            \x28\x06\x09\x28\x02\xC4\x00\x08\x00\xE0\x03\x00\x00\x00\x00\x00\
2135            \x00\x02\x3C\x00\x00\x42\x24\x00\x00\x03\x3C\x00\x00\x63\x24\x00\
2136            \x00\x40\xAC\x04\x00\x42\x24\x2B\x08\x43\x00\xFC\xFF\x20\x14\x00\
2137            \x00\x00\x00\x04\x00\x02\x24\x00\x00\x00\x00\x00\x00\x00\x00\x00\
2138            \x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x3C\x00\x00\x84\x24\x21\
2139            \x20\x82\x00\x00\x00\x82\x8C\x00\x80\x08\x3C\x25\xE8\x48\x00\x00\
2140            \x00\x04\x3C\x00\x00\x84\x24\xC0\x20\x04\x00\xC2\x20\x04\x00\x00\
2141            \x00\x03\x3C\x00\x00\x63\x8C\x00\x00\x00\x00\x23\x28\x43\x00\x23\
2142            \x28\xA4\x00\x25\x20\x88\x00\x00\x00\x01\x3C\x00\x00\x3F\xAC\x00\
2143            \x00\x1C\x3C\x00\x00\x9C\x27\x21\xF0\xA0\x03\x00\x00\x00\x0C\x04\
2144            \x00\x84\x20\x00\x00\x1F\x3C\x00\x00\xFF\x8F\x00\x00\x00\x00\x00\
2145            \x00\x00\x0C\x00\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x20\x00\x00\
2146            \x00\x20\x00\x00\x00\x20\x00\x00\x00\x20\x00\x0A\x52\x08\x00\x0C\
2147            \x0C\x28\x0A\x54\x0C\x00\x0C\x0C\x28\x0A\x52\x10\x00\x16\x0D\x28\
2148            \x0A\x54\x14\x00\x16\x0D\x28\x0A\x52\x40\x00\x2C\x04\x09\x28\x00\
2149            \xB4\x00\x00\x00\x0A\x54\x44\x00\x2C\x04\x09\x28\x00\xB4\x00\x00\
2150            \x00\x0A\x52\x58\x00\x16\x0D\x28\x0A\x54\x5C\x00\x16\x0D\x28\x0A\
2151            \x52\x68\x00\x02\x17\x28\x0A\x54\x6C\x00\x02\x17\x28\x0A\x52\x80\
2152            \x00\x2C\x04\x0C\x28\x00\x00\x00\x00\x00\x0A\x54\x84\x00\x2C\x04\
2153            \x0C\x28\x00\x00\x00\x00\x00\x0A\x52\x88\x00\x0C\x0B\x28\x0A\x54\
2154            \x8C\x00\x0C\x0B\x28\x0A\x4A\x94\x00\x02\x14\x28\x0A\x52\x9C\x00\
2155            \x2C\x04\x0C\x28\x00\x00\x00\x00\x00\x0A\x54\xA0\x00\x2C\x04\x0C\
2156            \x28\x00\x00\x00\x00\x00\x0A\x4A\xA8\x00\x02\x16\x28\x06\x0C\x28\
2157            \x08\x04\x00\x00\x00\x0E\x14\x28\x08\x49\x6E\x69\x74\x48\x65\x61\
2158            \x70\x0E\x17\x28\x0A\x5F\x73\x74\x61\x63\x6B\x73\x69\x7A\x65\x0C\
2159            \x0F\x28\x09\x28\x08\x00\x00\x00\x10\x5F\x5F\x53\x4E\x5F\x45\x4E\
2160            \x54\x52\x59\x5F\x50\x4F\x49\x4E\x54\x0C\x0E\x28\x09\x28\x00\x00\
2161            \x00\x00\x06\x5F\x5F\x6D\x61\x69\x6E\x0E\x16\x28\x04\x6D\x61\x69\
2162            \x6E\x0C\x11\x28\x09\x28\xA8\x00\x00\x00\x05\x73\x74\x75\x70\x30\
2163            \x0C\x12\x28\x09\x28\x2C\x00\x00\x00\x05\x73\x74\x75\x70\x31\x0C\
2164            \x13\x28\x09\x28\x08\x00\x00\x00\x05\x73\x74\x75\x70\x32\x00";
2165        let mut data = Cursor::new(&bytes);
2166        let lnk = OBJ::read(&mut data).unwrap();
2167
2168        eprintln!("obj: {:?}", lnk);
2169    }
2170
2171    #[test]
2172    fn test_section() {
2173        let bytes = b"\x3A\x00\x00\x26\x00\x00\x00\x09\x00";
2174        let mut data = Cursor::new(&bytes);
2175        let _ = Section::read(&mut data).unwrap();
2176    }
2177
2178    #[test]
2179    fn test_expression() {
2180        // ExpressionDefinition::{ // 0x0A
2181        //   tag: 0x52,            // 0x52 (82)
2182        //   offset: 8,            // 0x0800 (little endian)
2183        //   expression: (
2184        //     sectstart(0x280c)   // 0x0C0C28
2185        //   )
2186        // }
2187        let bytes = b"\x0A\x52\x08\x00\x0C\x0C\x28";
2188        let mut data = Cursor::new(&bytes);
2189        let _ = Section::read(&mut data).unwrap();
2190
2191        let bytes = b"\x0A\x52\x10\x00\x16\x0D\x28";
2192        let mut data = Cursor::new(&bytes);
2193        let _ = Section::read(&mut data).unwrap();
2194
2195        let bytes = b"\x0A\x52\x10\x00\x16\x0D\x28\x04\x04\x00\x00\x00\x00\x00\x00";
2196        let mut data = Cursor::new(&bytes);
2197        let _ = Section::read(&mut data).unwrap();
2198
2199        let bytes = b"\x0A\x52\xD0\x00\x32\x00\x04\x00\x00\x00\x2E\x0C\xFA\x62\x16\xFA\x62";
2200        let mut data = Cursor::new(&bytes);
2201        let _ = Section::read(&mut data).unwrap();
2202    }
2203
2204    #[test]
2205    fn test_function_start() {
2206        let bytes = b"\
2207            \x4A\x7C\x55\xB4\x05\x00\x00\xA7\x59\x00\x00\x00\x00\x1D\x00\x20\
2208            \x00\x00\x00\x1F\x00\x00\x00\x03\x80\xF8\xFF\xFF\xFF\x06\x63\x61\
2209            \x6C\x6C\x6F\x63\x4C"
2210            .to_vec();
2211
2212        let mut data = Cursor::new(&bytes);
2213        let _ = Section::read(&mut data).unwrap();
2214
2215        let bytes = b"\x0A\x52\x10\x00\x16\x0D\x28".to_vec();
2216        let mut data = Cursor::new(&bytes);
2217        let _ = Section::read(&mut data).unwrap();
2218    }
2219
2220    #[test]
2221    fn test_def2() {
2222        let bytes = b"\
2223            \x54\x00\x00\x04\x00\x00\x00\x66\x00\x00\x00\x04\x00\x00\x00\x00\
2224            \x00\x08\x5F\x70\x68\x79\x73\x61\x64\x72\x04\x2E\x65\x6F\x73";
2225
2226        let mut data = Cursor::new(&bytes);
2227        let section = Section::read(&mut data).unwrap();
2228
2229        let Section::Def2(def2) = section else {
2230            panic!("expected a def2");
2231        };
2232
2233        assert_eq!(def2.section, 0);
2234        assert_eq!(def2.value, 4);
2235        assert_eq!(def2.class, 102);
2236        assert_eq!(def2.def_type, 0);
2237        assert_eq!(def2.size, 4);
2238        // assert_eq!(def2.dims, Dim::None);
2239        assert_eq!(def2.tag(), "_physadr");
2240        assert_eq!(def2.name(), ".eos");
2241    }
2242
2243    #[test]
2244    fn test_libsn_sat() {
2245        let bytes =
2246b"\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";
2247
2248        let mut data = Cursor::new(&bytes);
2249        let code = Code::read(&mut data).unwrap();
2250        assert_eq!(bytes.len(), 106);
2251        assert_eq!(code.size, 104);
2252        assert_eq!(code.code.len(), 104);
2253        assert_eq!(code.code, bytes[2..]);
2254
2255        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";
2256        let mut data = Cursor::new(&bytes);
2257        let section = Section::read(&mut data).unwrap();
2258        assert_eq!(section.to_string(), "10 : Patch type 10 at offset 1f with ($2-arshift_chk-(($fffffffc&(sectbase(1)+$22))-(sectbase(1)+$60)))");
2259
2260        println!("section: {section}");
2261
2262        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";
2263        let mut data = Cursor::new(&bytes);
2264        let _ = OBJ::read(&mut data).unwrap();
2265    }
2266}