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}