Compare commits

..

631 Commits
2.0.2 ... 3.2.0

Author SHA1 Message Date
Matt Nadareski
3798dbcb47 Bump version 2026-08-31 19:15:36 -04:00
Matt Nadareski
2a70a9a20e Update IO to 2.3.0 2026-08-28 13:11:31 -04:00
Matt Nadareski
6f92f85f5d Convert BFPK writing to use GrindCore zlib 2026-08-27 14:01:50 -04:00
Matt Nadareski
b7851601a4 Convert GCZ writing to use GrindCore zlib 2026-08-27 13:59:38 -04:00
Matt Nadareski
cb2a96c413 Convert GCZ to use GrindCore zlib 2026-08-27 13:57:49 -04:00
Matt Nadareski
69c0843a0d Convert GZip to use GrindCore zlib 2026-08-27 13:56:09 -04:00
Matt Nadareski
0171ea9da7 Convert IS-CAB to use GrindCore zlib 2026-08-27 13:53:23 -04:00
Matt Nadareski
e493af25e0 Convert CExe to use GrindCore zlib 2026-08-27 12:37:26 -04:00
Matt Nadareski
62427576e9 Convert SGA to use GrindCore zlib 2026-08-27 12:30:01 -04:00
Matt Nadareski
e80ea00872 Update packages 2026-08-27 09:36:42 -04:00
Matt Nadareski
ce1cd15978 Fix locking issues with item dictionary implementation 2026-08-11 22:04:32 -04:00
HeroponRikiBestest
2a604bd73a Ensure that the data being read is actually large enough to have a debug directory entry parsed from it (#96) 2026-07-30 20:44:49 -04:00
Matt Nadareski
bcc0a5bd1e Add set type RomVault game extension field 2026-07-23 17:03:14 -04:00
Matt Nadareski
32d9e8a09d Add dir RomVault header extension field 2026-07-23 16:45:12 -04:00
Matt Nadareski
d0f5ac322d Add subset RomVault header extension field 2026-07-23 16:15:27 -04:00
Matt Nadareski
05c86e4c24 Add support for BLAKE3 2026-07-23 11:36:54 -04:00
Matt Nadareski
1d76967a08 Ignore bad IS-CAB group names 2026-07-19 21:09:59 -04:00
Matt Nadareski
b9ffd61252 Bump version 2026-07-10 13:29:35 -04:00
Matt Nadareski
b4e1f6b729 Update packages, relax requirements 2026-07-10 11:23:17 -04:00
Matt Nadareski
451fc0b394 Bump version 2026-07-06 09:57:11 -04:00
HeroponRikiBestest
79ca9f617e Add sim parsing for Steam2 sim/sid installers (#95)
* Add model for sim file

* Add sim parsing for steam2 installer set

* First round of fixes
2026-07-06 09:21:17 -04:00
Matt Nadareski
a602686551 Trim path separators from start of filenames on extract 2026-07-05 23:28:24 -04:00
Matt Nadareski
4083aed12f Some filenames in MS-CAB start with one or more separators 2026-07-01 10:18:20 -04:00
Matt Nadareski
717de3608c Add Logiqx serial and version machine fields 2026-06-22 16:35:03 -04:00
Matt Nadareski
88aae0dd26 Bump version 2026-06-12 08:56:35 -04:00
Matt Nadareski
499fc44004 Ensure a 2-byte read is possible 2026-06-07 21:56:17 -04:00
Matt Nadareski
07325be930 Clean up parameter 2026-05-29 22:27:27 -04:00
Matt Nadareski
481020bd08 Move useful helper to a better place 2026-05-22 09:24:03 -04:00
Matt Nadareski
5642f601a3 Minor cleanup 2026-05-21 10:38:38 -04:00
Deterous
b42a12f051 Recursive InfoPrint for ISO9660 and XDVDFS (#94)
* Recursive InfoPrint for XDVDFS / XboxISO

* Cleanup, full file paths

* Add safety

* Recursive ISO9660 printing

* Naive decode filename

* comment
2026-05-21 10:32:20 -04:00
Matt Nadareski
a5879b46f1 Slightly more cleanup around IS-CAB and printing 2026-05-18 19:52:44 -04:00
Matt Nadareski
9a14cc2620 Fix major issues in IS-CAB 2026-05-18 19:23:35 -04:00
Matt Nadareski
a777df13db Take directory into account for IS-CAB 2026-05-18 13:35:59 -04:00
Matt Nadareski
78d7bc310f Add placebo recursive flag for now 2026-05-18 10:24:30 -04:00
Matt Nadareski
ae3978241c Add expected header size as a comment 2026-05-18 10:03:57 -04:00
Matt Nadareski
390e39ef30 Add Z3DS metadata structures 2026-05-18 09:54:33 -04:00
Matt Nadareski
5ef595e01a Add Z3DS file header structure 2026-05-18 09:39:48 -04:00
Matt Nadareski
371623e3fa See if org-level CONTRIBUTING works 2026-05-16 14:59:41 -04:00
Matt Nadareski
7eb669cef3 Update CONTRIBUTING to be more accurate 2026-05-15 13:04:19 -04:00
Matt Nadareski
4eac10c15c Add CONTRIBUTING document 2026-05-15 10:58:39 -04:00
Matt Nadareski
495368f28d Update gitignore for new VSCode feature 2026-05-15 10:24:55 -04:00
Matt Nadareski
a2d15f301b Move it around 2026-05-14 20:52:12 -04:00
Matt Nadareski
d3c9849908 Attempt to fix detection issues 2026-05-14 20:08:32 -04:00
Matt Nadareski
249c51933b Update packages 2026-05-14 09:25:26 -04:00
Matt Nadareski
cf586f7af2 Add Nintendo Disc extensions tests 2026-05-12 22:47:19 -04:00
Matt Nadareski
b805c91cb3 Add notes and reconfigure based on IO porting 2026-05-12 21:55:11 -04:00
Matt Nadareski
2a0e8e2eb0 First round of cleanup for DolphinLib additions 2026-05-12 20:33:53 -04:00
Dimensional
49aa6895b6 Dolphin lib (#85)
* Add GCZ, WIA/RVZ, and NintendoDisc (GameCube/Wii) format support

Port of DolphinIsoLib into SabreTools.Serialization architecture:
- Data.Models: GCZ/, WIA/, NintendoDisc/ model subdirectories (15 files)
- Serialization.Readers: GCZ, WIA, NintendoDisc readers
- Serialization.Writers: GCZ, WIA writers (structural metadata; full round-trip TODO)
- Wrappers: NintendoDisc, GCZ, WIA wrappers with Encryption partial class
- Wrappers: WiaRvzCompressionHelper (BZip2/LZMA/LZMA2/Zstd, net462+ guarded)
- WrapperType + WrapperFactory: GCZ, WIA, NintendoDisc entries added

GetInnerWrapper() decompression and NintendoDisc.Extraction FST extraction
are stubbed with TODO comments pending full implementation.

* Implement GetInnerWrapper for GCZ and WIA, full NintendoDisc extraction

* Add GCZ/WIA/RVZ write pipeline and Nintendo disc compression helpers

* Add WIA/RVZ table decompression, NintendoDisc/GCZ printing, NintendoDisc detection for .iso files

* Fix NintendoDisc header layout, GC magic, and add embedded disc header to WIA/GCZ printing

- Fix GCMagicWord: 0xC23D3C1F -> 0xC2339F3D (confirmed from Dolphin DiscUtils.h)
- Fix disc header field layout to match Dolphin's confirmed offsets:
  MakerCode is bytes 4-5 of the 6-char GameId (no separate field at 0x006),
  DiscNumber at 0x006, DiscVersion at 0x007, unused region is 14 bytes (0x00A-0x017)
- Update NintendoDisc reader: derive MakerCode from GameId[4..5], fix skip count
- Add ParseDiscHeaderOnly() to reader for partial (short) stream parsing
- Guard DisableHash/DisableEnc reads at the 0x080 boundary for 128-byte embedded headers
- Guard DOL/FST skip for streams shorter than full 0x440 boot block
- Fix WrapperFactory: NintendoDisc magic detection now precedes .iso -> ISO9660 fallback
- Add GameId-prefix heuristic in WrapperFactory for GC discs lacking magic word
- Add GameId-prefix platform fallback in reader for GC discs without GCMagicWord
- Add DiscHeader property to WIA wrapper (parsed from Header2.DiscHeader bytes)
- Add DiscHeader property to GCZ wrapper (decompresses first block only)
- Add ReadDiscHeader() helper to GCZ for lightweight first-block decompression
- Print embedded disc header (Game ID, Maker, Disc/Rev, Title) in WIA.Printing.cs and GCZ.Printing.cs

* Fix Wii partition extraction: correct IV, FST size shift, partition naming

- Block decryption: IV is at raw block offset 0x3D0 (still-encrypted),
  matching Dolphin/DolphinIsoLib WiiPartitionDecryptor.DecryptBlock exactly.
- FST size field at boot.bin 0x428 is also stored >>2 on Wii; apply <<2
  to get true byte size.
- Partition folder naming now matches DolphinIsoLib WiiDiscExtractor exactly:
  type 0->GM+n, 1->UP+n, 2->CH+n, printable ASCII unknown->raw 4-char string,
  non-printable->P{index}. SSBB VC channels extract as HA8E, HA9E, etc.
- ExtractionTool peek buffer increased from 16 to 32 bytes.

Verified: SSBB GM0 extracts 5524 files, boot.bin/fst.bin byte-identical
to Dolphin reference extraction.

* Fix FST extraction: create zero-byte files instead of skipping them

Files with fileSize=0 in the FST were silently skipped. Now they are
created as empty files, matching Dolphin/DolphinIsoLib behavior.

Verified: SSBB now extracts 5958 files with 0 missing, 0 extra,
0 size mismatches, and 0 hash mismatches vs DolphinIsoLib reference.

* Add GCZ/WIA/RVZ virtual stream extraction via NintendoDisc wrapper

* Address PR #85 review comments (Copilot + mnadareski)

* Address PR #85 review comments

* Replace custom endian helpers and SHA1 with SabreTools.IO equivalents

* Update GCZ.Printing.cs

* Update NintendoDisc.Printing.cs

* Update WIA.Printing.cs

* Add WIA/RVZ Wii partition crypto round-trip support

- Add AesCbc internal helper (BouncyCastle AES-CBC encrypt/decrypt)
- Add NintendoDisc.CommonKeyProvider hook for injectable test keys
- Fix sha1.Terminate() missing in all three ComputeSha1 helpers in WIA.cs
- Fix Wii partition dataOff alignment to 0x8000 boundary
- Add WIA.EncryptWiiGroup (internal) for re-encrypting plaintext groups
- Add WIA.DumpIso to WIA.Writing.cs (WIA/RVZ -> flat ISO conversion)
- Add WiaVirtualStream on-demand group decompression
- Add _preDecryptedReader bypass on NintendoDisc for WIA extraction path
- Add WIATests.cs with Wii crypto round-trip test using synthetic data
- Move DumpIso from WIA.Extraction.cs to WIA.Writing.cs
- Bump DumpIso read buffer from 1 MiB to 2 MiB (aligns to WIA chunk size)
- Add InternalsVisibleTo SabreTools.Wrappers.Test in csproj

* Remove hardcoded Wii common keys from NintendoDisc.Encryption

- Delete the embedded WiiCommonKeyRetail and WiiCommonKeyKorean byte
  arrays from NintendoDisc.Encryption.cs.
- Make CommonKeyProvider public so any caller (not just tests) can
  inject keys; DecryptTitleKey now returns null when no key is
  available for the requested index rather than falling back to
  hardcoded values.
- Add NintendoDiscEncryptionTests.cs:
    - Argument guard and no-provider tests for DecryptTitleKey.
    - Fake-key round-trip test (encrypt then decrypt with injected key).
    - Integration test that reads TestData/NintendoDisc/keys.json,
      verifies each key against hardcoded SHA256 constants, and skips
      silently if the file is absent or the keys do not match.
    - LoadKeyProvider helper (named JSON format, index-keyed).
- Add [Collection(NintendoDisc)] to both NintendoDiscEncryptionTests
  and WIATests to prevent parallel access to the static
  CommonKeyProvider from racing between test classes.
- Add TestData/NintendoDisc/keys.json.example documenting the
  expected key file format.
- Add Newtonsoft.Json reference to SabreTools.Wrappers.Test.csproj.

* Didn't actually commit the changes. My bad. Fixed.

* Edited a comment

* Added in XUnit outputs that show up in Test Viewer in VS

---------

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2026-05-12 10:41:32 -04:00
Matt Nadareski
3f71628600 Split WIA models into separate files 2026-05-04 11:47:49 -04:00
Matt Nadareski
f729eae48a Add GCZ, Nintendo disc, and WIA models
Code originally by https://github.com/Dimensional and split off from https://github.com/SabreTools/SabreTools.Serialization/pull/85/
2026-05-04 09:59:14 -04:00
Matt Nadareski
f6877d82cd Remove errant header field setting 2026-04-28 13:58:59 -04:00
Matt Nadareski
9ed671f595 Editorconfig cleanup 2026-04-28 09:26:49 -04:00
Deterous
a2b9db3afd PC Engine CD-ROM Header Support (#91) 2026-04-28 09:24:36 -04:00
Matt Nadareski
49ea4302b6 Add mostly-working version of JSON printing to older .NET 2026-04-27 18:56:10 -04:00
Matt Nadareski
59f76b1036 Editorconfig cleanup 2026-04-27 13:28:23 -04:00
Deterous
6fa3631c80 Add Opera FileSystem support (#90)
* Add Opera FileSystem support

* Fix tests
2026-04-27 13:24:30 -04:00
Matt Nadareski
b24f96ac0f Fix documentation 2026-04-25 21:51:42 -04:00
Matt Nadareski
201af08f5f Document alternative TGZ layout 2026-04-25 21:45:53 -04:00
Matt Nadareski
b9b1b24f67 Update date in license 2026-04-22 09:35:17 -04:00
Matt Nadareski
d28a88e193 Handle comments in XML in a smarter way 2026-04-21 14:23:35 -04:00
Matt Nadareski
c02698edcd Fix M1 and MESS parsing, similar to ListXML 2026-04-21 12:22:41 -04:00
Matt Nadareski
41bfe8edce Fix tests 2026-04-21 12:06:33 -04:00
Matt Nadareski
9c66f6ef57 RamOption was a problematic read 2026-04-21 12:03:08 -04:00
Matt Nadareski
3a6ee3a37c Remove preemptive filtering 2026-04-21 10:59:45 -04:00
Matt Nadareski
7227f01ebc Support old item type filter string 2026-04-21 09:30:24 -04:00
Matt Nadareski
2bf686675e Port Reports namespace from ST 2026-04-20 14:03:49 -04:00
Deterous
c179cf410b XboxISO separate functions for partition extraction (#89)
* XboxISO funcs for partition extraction

* Add new parameter with default
2026-04-19 23:59:39 -04:00
Deterous
ab4bb4cca3 Fix XDVDFS bug (#88)
* Fix XDVDFS bug

* Move check and seek earlier
2026-04-19 22:13:01 -04:00
Matt Nadareski
2462305e0b Editorconfig cleanup, use extensions 2026-04-19 09:33:55 -04:00
Deterous
e383771fb6 XRD File Support (#87) 2026-04-19 09:04:25 -04:00
Matt Nadareski
bba9bfcbd6 Editorconfig cleanup 2026-04-18 21:32:37 -04:00
Deterous
94f86fe1d1 Peek in XDVDFS, Support XB00104M (#86)
* Peek in XDVDFS, Support XB00104M

* var
2026-04-18 21:30:44 -04:00
Matt Nadareski
63c13254d7 Handle all empty elements 2026-04-18 21:20:56 -04:00
Matt Nadareski
7d6092452f Handle uncommon case of no find elements 2026-04-18 21:10:01 -04:00
Matt Nadareski
6c2098c5d2 Add "boot" element for OpenMSX, fix missing skip 2026-04-18 20:53:18 -04:00
Matt Nadareski
70bef7d6c9 EOF loops always need to skip 2026-04-18 20:31:37 -04:00
Matt Nadareski
87df32284d Fix issue with comments in OpenMSX 2026-04-18 20:24:07 -04:00
Matt Nadareski
fda2e39296 The lists of keys are actually useful 2026-04-18 16:34:20 -04:00
Matt Nadareski
fe58061a2d Found out why these helpers were this way 2026-04-18 12:45:58 -04:00
Deterous
9a59f7c540 Fix SVOD serialization and printing (#84)
* Fix SVOD printing

* Fix SVOD descriptor

* Clearer check
2026-04-17 09:39:10 -04:00
Matt Nadareski
25ab9acb57 Do not access dictionary with index 2026-04-17 09:14:39 -04:00
Matt Nadareski
ecd4e7d1df Make user logs more accurate 2026-04-17 08:48:08 -04:00
Matt Nadareski
024200ebc4 Minor cleanup around loops 2026-04-16 23:39:33 -04:00
Matt Nadareski
ad9828de3c Slight cleanup around bucketing 2026-04-16 23:22:48 -04:00
Matt Nadareski
d6e974a3ab Make grouping/bucketing a private class too 2026-04-16 23:09:48 -04:00
Matt Nadareski
c482160b5b Fix tests 2026-04-16 22:29:03 -04:00
Matt Nadareski
d9a50d0c9e Add removal helper method 2026-04-16 22:16:03 -04:00
Matt Nadareski
7acb97e78e Add and use IndexedTable private class 2026-04-16 22:08:03 -04:00
Matt Nadareski
c409c47ae5 Remove outdated note 2026-04-16 19:17:45 -04:00
Matt Nadareski
2caab2c9cf Remove items when machine is removed for DB 2026-04-16 17:00:16 -04:00
Matt Nadareski
0feb4130fe Fix some DB-specific tests 2026-04-16 16:56:02 -04:00
Matt Nadareski
c90dcd7d59 Fix missing and unnecessary assignments 2026-04-16 16:41:01 -04:00
Matt Nadareski
2884dcab5b Remove need for GetMachineForItem 2026-04-16 16:11:25 -04:00
Matt Nadareski
ff69b83e05 Get methods should return all info 2026-04-16 15:55:12 -04:00
Matt Nadareski
2a2a2a46f9 Remove need for GetSourceForItem 2026-04-16 15:37:41 -04:00
Matt Nadareski
cb8d2885aa Add constructors with index instead of object 2026-04-16 14:14:30 -04:00
Matt Nadareski
3e55071d41 Add machines but don't invoke ItemDB 2026-04-16 13:59:15 -04:00
Matt Nadareski
af568d6a57 Make callers responsible for machine and source setting 2026-04-16 13:52:58 -04:00
Matt Nadareski
8f746824d6 Allow callers to set source and machine index 2026-04-16 13:27:29 -04:00
Matt Nadareski
334ab130da Remove unnecessary framework gating 2026-04-16 13:03:34 -04:00
Matt Nadareski
b8966f5e37 Remove unnecessary RemapDatItemToMachine method 2026-04-16 13:00:13 -04:00
Matt Nadareski
096f9d4b96 Remove external item to machine mappings 2026-04-16 12:54:43 -04:00
Matt Nadareski
b6c6296c29 Remove external item to source mappings 2026-04-16 12:39:24 -04:00
Matt Nadareski
8173806537 Add machine and source indexes to DatItem, currently unused 2026-04-16 12:26:52 -04:00
Matt Nadareski
af12310504 Rename ItemDictionaryDB to ItemDatabase 2026-04-16 11:51:28 -04:00
Matt Nadareski
5694cdf9a9 Editorconfig cleanup 2026-04-15 08:44:42 -04:00
Deterous
dab850103b XDVDFS FileWriter (#83) 2026-04-15 08:41:55 -04:00
Matt Nadareski
c646141bdf Remove DesignerCategory attribute usage 2026-04-15 00:27:48 -04:00
Matt Nadareski
ca02ed6b09 Add CICM sidecar models 2026-04-14 22:56:09 -04:00
Matt Nadareski
5b22902d03 Add QD image writer 2026-04-14 12:45:31 -04:00
Matt Nadareski
4e70a7665c Add NES cart writer 2026-04-14 12:33:27 -04:00
Matt Nadareski
650402df39 Add FDS image writer 2026-04-14 12:17:42 -04:00
Matt Nadareski
fead0d781c Add LNX cart writer 2026-04-14 12:03:19 -04:00
Matt Nadareski
3896b75b3b Add A78 cart writer 2026-04-14 11:59:12 -04:00
Matt Nadareski
ea2a1daadf Add NES image writing 2026-04-14 10:55:09 -04:00
Matt Nadareski
5968a367e8 Add FDS image writing 2026-04-14 10:33:30 -04:00
Matt Nadareski
b439cc3e35 Attempt to avoid input file overwriting 2026-04-14 10:27:23 -04:00
Matt Nadareski
3675a8ac75 Fix flaw in writable interface 2026-04-14 10:25:05 -04:00
Matt Nadareski
e16b485a7b Add LNX cart writing 2026-04-14 10:19:04 -04:00
Matt Nadareski
b51879bbec Add A78 cart writing 2026-04-14 10:13:05 -04:00
Matt Nadareski
f26c5bfebc Add IWritable interface 2026-04-14 09:59:23 -04:00
Matt Nadareski
4d5e7eb755 Minor editorconfig change 2026-04-13 10:04:24 -04:00
Matt Nadareski
7d14681fc9 Editorconfig and some manual cleanup 2026-04-13 09:43:40 -04:00
Deterous
0e5380ad1a Xbox ISO Support (#81)
* Xbox ISO support

* Fixes

* Print XGDType

* Review
2026-04-13 09:39:29 -04:00
Matt Nadareski
2494b44647 Start scaffolding disc image wrapper helper 2026-04-11 20:56:46 -04:00
Matt Nadareski
3d33f44eca Make inverted DataArea less impactful 2026-04-09 23:56:36 -04:00
Matt Nadareski
5f9eaef9f2 Make inverted DiskArea less impactful 2026-04-09 23:36:50 -04:00
Matt Nadareski
b0a9464d2b Make inverted Part less impactful 2026-04-09 23:29:00 -04:00
Matt Nadareski
5d4453fb1e Minor formatting fix 2026-04-09 22:57:50 -04:00
Matt Nadareski
ca3ebf33a8 Flatten Analog out of existence 2026-04-09 22:21:15 -04:00
Matt Nadareski
853e2ff5c0 Flatten Extension out of existence 2026-04-09 22:06:41 -04:00
Matt Nadareski
967d9ffe87 Flatten Instance out of existence 2026-04-09 21:34:00 -04:00
Matt Nadareski
27a93fa86a Clean up Rom ordering 2026-04-09 21:16:45 -04:00
Matt Nadareski
770f6f75d5 Flatten Condition out of existence 2026-04-09 21:13:55 -04:00
Matt Nadareski
2a700dee9c Add skeleton filter overrides for nested items 2026-04-09 20:33:34 -04:00
Deterous
c88ea9ce30 Support optional header in STFS, fix endianness in Descriptor (#80)
* Fix endianness in STFS Descriptor

* Support optional header for installer packages

* Fix field types

* Fix syntax

* Fix build
2026-04-09 09:57:26 -04:00
Matt Nadareski
45956e9128 This enum no longer has use 2026-04-08 23:37:59 -04:00
Matt Nadareski
baf68f3f81 Replace all instances of XXXXXX with proper test values 2026-04-08 21:04:11 -04:00
Matt Nadareski
9d63be5cc7 Merge pull request #79 from SabreTools/attempt-metadata-overhaul 2026-04-08 10:16:06 -04:00
Matt Nadareski
c78bde1fb2 Merge branch 'main' into attempt-metadata-overhaul 2026-04-07 22:20:28 -04:00
Matt Nadareski
574b993985 Editorconfig cleanup of STFS 2026-04-07 22:20:14 -04:00
Deterous
7485a43364 Secure Transacted File System (STFS) Support (#78)
* Initial STFS support

* Fix build errors

* Fix more build errors

* Final fix

* Cleanup printer

* Fix indent
2026-04-07 22:14:39 -04:00
Matt Nadareski
0363b1dd96 Slight code cleanliness updates 2026-04-07 11:23:10 -04:00
Matt Nadareski
94658ae225 Filter tree is reflection-free 2026-04-07 11:17:57 -04:00
Matt Nadareski
76c5dc40e3 Checkpoint reflection replacement 2026-04-07 10:37:03 -04:00
Matt Nadareski
6ba4a5b3ab Checkpoint reflection replacement 2026-04-07 10:26:58 -04:00
Matt Nadareski
18b3152a6c Checkpoint reflection replacement 2026-04-07 10:14:54 -04:00
Matt Nadareski
3412bbf33a Checkpoint reflection replacement 2026-04-07 09:36:43 -04:00
Matt Nadareski
278bfafae5 Checkpoint reflection replacement 2026-04-07 09:24:23 -04:00
Matt Nadareski
b4be581879 Checkpoint reflection replacement 2026-04-07 09:12:27 -04:00
Matt Nadareski
39a31b3a97 Checkpoint reflection replacement 2026-04-06 19:10:40 -04:00
Matt Nadareski
2d6e9d7875 Add RomVault subheader 2026-04-06 18:51:09 -04:00
Matt Nadareski
f3d6adbe1d Add RV-specific deduped status 2026-04-06 18:37:56 -04:00
Matt Nadareski
0350d8f58f Checkpoint reflection replacement 2026-04-06 16:19:20 -04:00
Matt Nadareski
2aca55a895 Add machine type analogue filtering again 2026-04-06 15:39:40 -04:00
Matt Nadareski
4772768c1f Handle item type filtering properly again 2026-04-06 15:03:16 -04:00
Matt Nadareski
adfc2cde84 Checkpoint reflection replacement 2026-04-06 14:46:34 -04:00
Matt Nadareski
6cab41b58e Merge branch 'main' into attempt-metadata-overhaul 2026-04-06 10:46:53 -04:00
Matt Nadareski
adedc502e3 Editorconfig cleanup of Xenon 2026-04-06 10:46:04 -04:00
Deterous
388567c9c6 Initial XEX support (#77)
* Initial XEX support

* Fix build

* Fix reader

* Fix reader, again

* Rename field to CertificateOffset

* Update reader

* Update Printer

* Update Printer

* Parse Certificate

* More info

* Fix build
2026-04-06 10:43:32 -04:00
Matt Nadareski
c0872cb97b Checkpoint reflection replacement 2026-04-06 10:18:00 -04:00
Matt Nadareski
e2b2f4d8a5 Rename "CRC" to "CRC32" where appropriate 2026-04-06 07:44:19 -04:00
Matt Nadareski
6a7d6e16b7 Fix framework difference 2026-04-05 19:28:06 -04:00
Matt Nadareski
f6f5818767 This utility method has been relocated in ST 2026-04-05 14:58:29 -04:00
Matt Nadareski
fe0768dad7 DepotInformation should be responsible for path generation 2026-04-05 14:02:06 -04:00
Matt Nadareski
d6edc33dd2 TypeHelper was only used by Filter in general 2026-04-05 11:28:48 -04:00
Matt Nadareski
d78c1eb9ed GetProperties is only used by filters 2026-04-05 11:24:47 -04:00
Matt Nadareski
15794e07f7 Remove one reflection-based helper 2026-04-05 11:21:56 -04:00
Matt Nadareski
d8e4f963c0 Statically cache item type names to avoid slowdowns 2026-04-05 11:15:07 -04:00
Matt Nadareski
f93d254a64 Try to make DAT statistics less intense 2026-04-05 11:00:36 -04:00
Matt Nadareski
25f50d19e2 Convert the last ToArrays that can be converted 2026-04-05 10:42:19 -04:00
Matt Nadareski
752f5d31d4 Avoid round-trip collection conversion 2026-04-05 10:40:54 -04:00
Matt Nadareski
83407133d6 Be more explicit about per-type missing fields 2026-04-05 10:22:44 -04:00
Matt Nadareski
742c991deb Remove now-redundant Equals methods 2026-04-05 02:24:05 -04:00
Matt Nadareski
3caf65822f Forgot to clean up some typing 2026-04-05 02:09:11 -04:00
Matt Nadareski
0eb5da5fd4 Default sort is never done, so CompareTo is useless 2026-04-05 01:59:34 -04:00
Matt Nadareski
337d4c0fed Metadata DatItem should be abstract 2026-04-05 01:51:07 -04:00
Matt Nadareski
5ffc9fef43 Cleanup around DatItem and DatItem<T>
This is both big and not big. The not big part of this is that I essentially just moved some stuff up to the typed type that used to live in the untyped type. The big part is that this allows every single DatItem implementation to be significantly cleaner with their implementations of the methods and internal model.
2026-04-05 01:36:32 -04:00
Matt Nadareski
1a10e10fd6 ModelBackedItem had no actual utility anymore 2026-04-05 01:19:46 -04:00
Matt Nadareski
1997346988 ModelBackedItem doesn't need to be equatable 2026-04-05 01:08:41 -04:00
Matt Nadareski
0ed13167ba Invert some if statements to be clearer 2026-04-05 00:35:09 -04:00
Matt Nadareski
285f3a267e NoFilter attribute no longer matters 2026-04-05 00:26:23 -04:00
Matt Nadareski
2969324015 None of these are constants now 2026-04-05 00:25:56 -04:00
Matt Nadareski
72dac4d6e2 The smallest cleanup 2026-04-05 00:21:19 -04:00
Matt Nadareski
ed72df79a6 Make FilterObject slightly more maintainable, slightly 2026-04-05 00:15:40 -04:00
Matt Nadareski
bf852ef038 DictionaryBase is no more, bon voyage 2026-04-04 23:13:36 -04:00
Matt Nadareski
e31be83832 RomProperties, no, not that one 2026-04-04 22:31:23 -04:00
Matt Nadareski
0b99602bc7 Convert Metadata file keys to proper properties 2026-04-04 21:17:28 -04:00
Matt Nadareski
3b2b4bc525 Notes 2026-04-04 20:35:57 -04:00
Matt Nadareski
bd8c008610 Attempt to make Equals redirection less 2026-04-04 20:30:33 -04:00
Matt Nadareski
0491133329 Move hash-based equality checkers lower down the chain 2026-04-04 20:04:01 -04:00
Matt Nadareski
4b20273d42 EqualTo has obsoleted itself into the bin 2026-04-04 19:55:44 -04:00
Matt Nadareski
a4f844cd6a Not sure how these slipped by 2026-04-04 19:49:02 -04:00
Matt Nadareski
8fe2b51d6f Further reduce calls to EqualTo 2026-04-04 19:47:00 -04:00
Matt Nadareski
c819be57a5 Reduce calls to EqualTo 2026-04-04 19:14:11 -04:00
Matt Nadareski
040ad65a5e DeepClone was left as self-referential, like this commit message 2026-04-04 18:48:00 -04:00
Matt Nadareski
f9437314cb Finalize property conversion for header 2026-04-04 18:46:35 -04:00
Matt Nadareski
ebf67d39cf Lazily make OfflineList-specific header items properties 2026-04-04 18:24:58 -04:00
Matt Nadareski
ca2a289a3f This remark was wrong 2026-04-04 17:59:39 -04:00
Matt Nadareski
cd49a64c2b Split header skipper and header row into separate properties 2026-04-04 17:58:09 -04:00
Matt Nadareski
b547792799 Two more header keys to properties 2026-04-04 16:34:13 -04:00
Matt Nadareski
2f72349e7b Small fixes to Machine 2026-04-04 16:20:42 -04:00
Matt Nadareski
8f82d3a28d Properties are not fields 2026-04-04 16:07:37 -04:00
Matt Nadareski
7a9f5d1fa1 Machine is now propertyful 2026-04-04 16:06:07 -04:00
Matt Nadareski
b8d4adf0e7 Clean up machine properties 2026-04-04 15:21:16 -04:00
Matt Nadareski
d08427f1ba Trurip finally gets integrated, more at 11 2026-04-04 15:17:58 -04:00
Matt Nadareski
12e1086838 Convert Dump to using properties 2026-04-04 14:23:31 -04:00
Matt Nadareski
274919fd6a Convert Dump to using properties 2026-04-04 14:23:27 -04:00
Matt Nadareski
096f1d9534 Remove get/set name methods where appropriate 2026-04-04 14:07:37 -04:00
Matt Nadareski
5ab48845e3 Remove one more use of DeepClone 2026-04-04 13:39:14 -04:00
Matt Nadareski
4acd2d9fd6 Reduce generic DictionaryBase use 2026-04-04 13:31:12 -04:00
Matt Nadareski
0458489a4f Remove two places where DeepClone wasn't needed 2026-04-04 13:13:16 -04:00
Matt Nadareski
9c1d2e7b9c Streamline metadata cloning 2026-04-04 13:11:10 -04:00
Matt Nadareski
ba371d971d Clean up EqualTo helpers 2026-04-04 12:41:36 -04:00
Matt Nadareski
9828119bdd Further reduce use of GetName and SetName 2026-04-04 12:31:47 -04:00
Matt Nadareski
d72c7c9cc0 Convert most keys to properties for Machine 2026-04-04 12:21:09 -04:00
Matt Nadareski
eb2f5babd9 Migrate DataArea, DiskArea, and Part to property-driven 2026-04-04 11:40:48 -04:00
Matt Nadareski
872fdc2a10 Reduce redirection for Name setters and getters 2026-04-04 10:25:49 -04:00
Matt Nadareski
845a0cb998 Convert Archive fully over to properties 2026-04-04 02:18:55 -04:00
Matt Nadareski
3f3f73a56f Convert Slot fully over to properties 2026-04-04 01:46:00 -04:00
Matt Nadareski
4bb670ac15 Convert Port fully over to properties 2026-04-04 01:36:42 -04:00
Matt Nadareski
9e721c5c43 Convert Input fully over to properties 2026-04-04 01:28:17 -04:00
Matt Nadareski
9ffa2a5e23 Convert Disk fully over to properties 2026-04-04 01:15:40 -04:00
Matt Nadareski
e5e279a48d Convert Device fully over to properties 2026-04-04 00:25:50 -04:00
Matt Nadareski
f7ac1073b0 Convert subitems for Configuration and DipSwitch 2026-04-04 00:08:56 -04:00
Matt Nadareski
a8c060ff26 Turn Condition keys into properties as a test 2026-04-03 23:33:11 -04:00
Matt Nadareski
2fdecebe66 Reduce use of GetName and SetName, prefer direct 2026-04-03 22:57:58 -04:00
Matt Nadareski
cdc55e721e Split Input.Control and Input.ControlAttr 2026-04-03 22:31:10 -04:00
Matt Nadareski
b55a932b2b String Machine keys to properties 2026-04-03 22:17:31 -04:00
Matt Nadareski
8e2f519e15 Convert most of Media over to properties 2026-04-03 19:47:58 -04:00
Matt Nadareski
5b3ff31f85 Make ReleaseDetails more complete 2026-04-03 19:11:55 -04:00
Matt Nadareski
c623c2b1f6 Make Serials more complete 2026-04-03 18:50:04 -04:00
Matt Nadareski
97b04b26a3 Make SourceDetails more complete 2026-04-03 18:31:31 -04:00
Matt Nadareski
5ccfde54f5 Convert last long key to property 2026-04-03 17:49:45 -04:00
Matt Nadareski
9df08780db Handle an enum that needed to be handled 2026-04-03 17:41:36 -04:00
Matt Nadareski
e01537c2f4 Get rid of Software metadata item
Apparently this was a mistake that has proliferated over a very long time. The Software item that was being modeled was actually the game/machine equivilent from SoftwareList. But because there's both an item called SoftwareList and a DAT type called SoftwareList, some wire got crossed and they were mentally combined. Undoing this allows for a more proper internal model, including a couple of extraneous keys that were included originally.
2026-04-03 17:25:09 -04:00
Matt Nadareski
154be08a53 Disable broken tree test 2026-04-03 16:58:48 -04:00
Matt Nadareski
e3927ca3e6 Fix longstanding TODOs around Software items 2026-04-03 16:02:44 -04:00
Matt Nadareski
fb711001e9 Add TODO question because I have no idea 2026-04-03 14:05:34 -04:00
Matt Nadareski
52edf9d20c Convert Release fully over to properties 2026-04-03 14:02:03 -04:00
Matt Nadareski
62b2fd3325 Original is cloneable and comparable 2026-04-03 13:48:43 -04:00
Matt Nadareski
716abed8b8 Convert Input mostly over to properties 2026-04-03 13:38:26 -04:00
Matt Nadareski
b6ab8a19be Convert Driver fully over to properties 2026-04-03 13:22:17 -04:00
Matt Nadareski
1d5514b29d Convert Display and Video fully over to properties 2026-04-03 13:08:36 -04:00
Matt Nadareski
32dd30adab Fix test 2026-04-03 12:31:33 -04:00
Matt Nadareski
2ed0c1f732 Convert Video mostly over to properties 2026-04-03 12:31:24 -04:00
Matt Nadareski
4fecf8b498 Remove long specifier for concrete values 2026-04-03 12:02:24 -04:00
Matt Nadareski
c9c31af8ea Convert Display mostly over to properties 2026-04-03 11:57:30 -04:00
Matt Nadareski
e604ef314b Convert DipLocation fully over to properties 2026-04-03 11:15:57 -04:00
Matt Nadareski
e3d3fe7c1a Fix broken tests 2026-04-03 11:05:59 -04:00
Matt Nadareski
280e65775a Convert ConfLocation fully over to properties 2026-04-03 10:59:04 -04:00
Matt Nadareski
33958d37fd Use ICloneable implementations in DeepClone 2026-04-03 10:33:10 -04:00
Matt Nadareski
dc773dba5e Convert Chip fully over to properties 2026-04-03 10:26:20 -04:00
Matt Nadareski
cdb74a9e26 Use IEquatable where possible 2026-04-03 10:10:34 -04:00
Matt Nadareski
600b0a6dbd Fill in some accidental gaps 2026-04-03 10:04:32 -04:00
Matt Nadareski
3f82312943 Convert Control fully over to properties 2026-04-03 09:46:10 -04:00
Matt Nadareski
08e0aa5b77 Convert refresh rates to properties, fix format 2026-04-03 00:04:11 -04:00
Matt Nadareski
3c6cfef070 Convert size keys to size properties 2026-04-02 23:45:17 -04:00
Matt Nadareski
11803aa34c Convert Sound.Channels to long as a test 2026-04-02 22:45:33 -04:00
Matt Nadareski
2b472c6f37 Migrate DAT filename to a property (used by ST) 2026-04-02 22:17:39 -04:00
Matt Nadareski
a914ee12c3 Convert DAT format to a property 2026-04-02 22:16:04 -04:00
Matt Nadareski
49d3d9c096 Migrate most header string keys to properties 2026-04-02 22:01:42 -04:00
Matt Nadareski
0df6378713 Move item type override per-item 2026-04-02 21:07:41 -04:00
Matt Nadareski
c0fd4d1334 Turn dupe type key into a metadata property 2026-04-02 20:41:14 -04:00
Matt Nadareski
c87db265dd Turn removal key into a metadata property 2026-04-02 20:34:09 -04:00
Matt Nadareski
d3f5dccf5b Make Source inherent to DatItem instead of passing through 2026-04-02 17:06:01 -04:00
Matt Nadareski
61ea4b2668 Compare machines in DatItem equality 2026-04-02 16:25:52 -04:00
Matt Nadareski
355aa4b94c Make Machine inherent to DatItem instead of passing through 2026-04-02 16:23:16 -04:00
Matt Nadareski
ca95e96402 Make "simple" metadata items equatable 2026-04-02 16:07:34 -04:00
Matt Nadareski
1e27b5d720 Make "simple" metadata items cloneable 2026-04-02 15:49:06 -04:00
Matt Nadareski
11c4097cba Rename Clone extension to DeepClone to avoid conflicts 2026-04-02 15:32:24 -04:00
Matt Nadareski
12ec3678b8 Fill in a few more string properties 2026-04-02 14:28:46 -04:00
Matt Nadareski
7df083c0b2 Reduce use of SetName 2026-04-02 13:35:11 -04:00
Matt Nadareski
c74942362c Name property cleanup, fixes 2026-04-02 13:21:37 -04:00
Matt Nadareski
c61aea7eac Move common string-only to properties 2026-04-02 11:18:49 -04:00
Matt Nadareski
8356aed5b0 Device.Manadatory converted to property 2026-04-02 02:43:29 -04:00
Matt Nadareski
3ef7b5860d Make large filter helper static 2026-04-02 02:28:00 -04:00
Matt Nadareski
276448a914 Merge branch 'main' into attempt-metadata-overhaul 2026-04-02 02:25:22 -04:00
Deterous
9993e574a0 Fix Xbox Executable TLS offset (#76)
* Fix XBE TLS address

* Break early

* Fix relative address
2026-04-02 02:24:08 -04:00
Matt Nadareski
81c47b297d Editorconfig cleanup of ZAR 2026-04-02 02:23:02 -04:00
Deterous
5bb8557555 ZArchive support (#75)
* ZArchive support

* Fix offset record format

* Simplfiy Extensions

* Delete unused writers and test data

* Rework reader

* Fix build
2026-04-02 02:18:47 -04:00
Matt Nadareski
c4524d20f0 Enum type conversion to property 2026-04-02 02:18:08 -04:00
Matt Nadareski
4c9ff19cf3 Runnable is Runnable 2026-04-01 22:06:24 -04:00
Matt Nadareski
451e89afff Boolean stuff convert to property 2026-04-01 21:59:16 -04:00
Matt Nadareski
826df09a3e Handle "Default" properties 2026-04-01 16:52:55 -04:00
Matt Nadareski
dfa39947ce Add GetProperties helper method and use it 2026-04-01 15:40:04 -04:00
Matt Nadareski
88d182cfd1 The map is curiously BLANK 2026-04-01 14:24:43 -04:00
Matt Nadareski
d2b8a4aa2c Remove NameKey from all current locations 2026-04-01 13:42:07 -04:00
Matt Nadareski
5812d197a5 Change Name to concrete property 2026-04-01 13:18:45 -04:00
Matt Nadareski
6810dce2ed Remove redundant ItemType declarations 2026-04-01 11:37:44 -04:00
Matt Nadareski
3e2bba866d Change ItemType into concerete property 2026-04-01 11:24:33 -04:00
Matt Nadareski
4035d9db86 Add some common Metadata objects for NIDB 2026-04-01 10:37:25 -04:00
Matt Nadareski
1cec6fa918 Add No-Intro database export reader and writer 2026-03-31 22:09:32 -04:00
Matt Nadareski
b82d464b12 Add No-Intro database export models 2026-03-31 20:44:17 -04:00
Matt Nadareski
3f5b3d10fd Not sure why this enum was separate 2026-03-31 19:48:26 -04:00
Matt Nadareski
af5d161fe4 Use original XML writer settings 2026-03-31 18:30:16 -04:00
Matt Nadareski
e2e19db294 Correctness fix to include XML declaration 2026-03-31 18:15:36 -04:00
Matt Nadareski
0b055a563e Directly document No-Intro Logiqx info 2026-03-31 18:10:44 -04:00
Matt Nadareski
4d362bb791 Use XmlTextWriter for XML DAT writing 2026-03-31 18:08:40 -04:00
Matt Nadareski
0670231606 Use XmlTextReader for XML DAT reading 2026-03-31 00:01:44 -04:00
Matt Nadareski
ea2bfbcb1f XDVDFS editorconfig cleanup 2026-03-29 21:55:23 -04:00
Deterous
28f4808dbb Read/write with variable initial offset (#74) 2026-03-29 21:54:16 -04:00
Matt Nadareski
0debcaa8d1 Slightly fix wrapper factory 2026-03-28 23:25:58 -04:00
Matt Nadareski
f1558587c3 Printer was really redundant 2026-03-28 23:12:41 -04:00
Matt Nadareski
d39c2e81d0 XDVDFS editorconfig cleanup 2026-03-28 23:06:46 -04:00
Matt Nadareski
07fe4415fe Reduce complexity of ExtractionTool invocation 2026-03-28 23:01:50 -04:00
Deterous
00ee106f4a Xbox DVD Filesystem support (XISO) (#73)
* Xbox DVD Filesystem support (XISO)

* Apply file attributes to extracted files

* Fix

* Fix build and PR review

* Support Archive file attribute
2026-03-28 22:59:30 -04:00
Matt Nadareski
a2c7588e1c Size filters should be AND not OR to allow for ranges 2026-03-27 21:07:48 -04:00
Matt Nadareski
5898bec0e9 Pedanitcally fill other filter operations 2026-03-27 20:57:11 -04:00
Matt Nadareski
8f71041e7e DatFormat does not need to be a flag 2026-03-27 10:47:20 -04:00
Matt Nadareski
4fd89ced29 Remove ported extension 2026-03-27 10:40:39 -04:00
Matt Nadareski
55522bf59b Port split format extension from ST 2026-03-27 09:31:19 -04:00
Matt Nadareski
de4566ec26 Fix Linux publish script 2026-03-27 08:49:08 -04:00
Matt Nadareski
078cd88bcd Gate unpublished builds behind a flag 2026-03-27 08:45:40 -04:00
Matt Nadareski
e9902f0907 Reduce extension confusion 2026-03-27 00:48:44 -04:00
Matt Nadareski
e5679b9152 A couple more small cleanup items 2026-03-27 00:32:12 -04:00
Matt Nadareski
7d29c3d7d9 Fix build 2026-03-27 00:16:58 -04:00
Matt Nadareski
1212603414 Reduce Reflection use in object creation 2026-03-26 23:59:53 -04:00
Matt Nadareski
9929199f31 Remove Reflection from DatItem<T> 2026-03-26 23:46:20 -04:00
Matt Nadareski
a7a622e118 Revert incorrect assumption 2026-03-26 23:14:38 -04:00
Matt Nadareski
225922e5bc TypeHelper comment and formatting cleanup 2026-03-26 23:10:06 -04:00
Matt Nadareski
465ece5ea3 Fix namespace mixup 2026-03-26 23:07:12 -04:00
Matt Nadareski
8de1fbc52f Reduce enum overhead in metadata 2026-03-26 22:51:14 -04:00
Matt Nadareski
29620c0c4f Metadata code cleanup 2026-03-26 20:40:49 -04:00
Matt Nadareski
3a3b20df11 Reduce repeated lookups and reads 2026-03-26 16:52:07 -04:00
Matt Nadareski
85aefc8d90 Move required extension 2026-03-26 15:16:12 -04:00
Matt Nadareski
52ad2ff89c Move required extension 2026-03-26 15:15:59 -04:00
Matt Nadareski
8356fc5970 Thin out ModelBackedItem wrapper class 2026-03-26 13:52:00 -04:00
HeroponRikiBestest
e61aec89ea Use correct section size for matroschka package extraction (#71)
Not sure how it took this long for me to encounter an issue.
2026-03-26 13:40:21 -04:00
Matt Nadareski
363ffee738 AttributeHelper code was only ever used in converters 2026-03-26 11:30:40 -04:00
Matt Nadareski
ce2ddf1636 Simplify SabreTools.Metadata base namespace 2026-03-26 11:27:29 -04:00
Matt Nadareski
dd29c8245a Slight simplification to GetDepotPath 2026-03-26 11:23:30 -04:00
Matt Nadareski
fdee2103ad Rename GetAttribute to GetMappingAttribute to be more accurate 2026-03-26 11:17:43 -04:00
Matt Nadareski
b72fcfc60b Fill in a couple READMEs 2026-03-26 10:58:14 -04:00
Matt Nadareski
3f6e5c2383 Migrate these internal extensions to real extensions 2026-03-26 09:34:19 -04:00
Matt Nadareski
2ae3291a30 BSP and VBSP should not share lump types 2026-03-26 09:29:32 -04:00
Matt Nadareski
c216129b37 Be more explicit about support 2026-03-26 09:05:58 -04:00
Deterous
2fdc2407dd Extract blank directories from ISO9660 filesystem (#70) 2026-03-25 09:04:14 -04:00
Matt Nadareski
5205fac739 Add non-published packages to publish scripts 2026-03-24 21:20:11 -04:00
Matt Nadareski
8a51f5d8ab Wire through already-added CRC-16 and CRC-64 2026-03-24 21:19:09 -04:00
Matt Nadareski
9c7993f8f1 Run tests and fix the rest 2026-03-24 19:42:36 -04:00
Matt Nadareski
8f49e190d8 Fix everything 2026-03-24 19:17:25 -04:00
Matt Nadareski
e11a08b587 Port metadata functionality from ST 2026-03-24 18:03:01 -04:00
Matt Nadareski
5f0fdcfd8d Update to IO 2.0.0 2026-03-24 18:02:49 -04:00
Matt Nadareski
1053ff7f23 Add ConditionalHashEquals tests 2026-03-22 20:26:28 -04:00
Matt Nadareski
de6a08539a Wrong filename 2026-03-22 20:22:22 -04:00
Matt Nadareski
219392e3a0 Port DictionaryBase extensions from ST
Notably, this is missing the equality checking because ST introduces a new type called "ModelBackedItem", which is specifically a type backed by a DictionaryBase item, with extensions. It is very similar to a Wrapper, but has different common utility. If there is a way to migrate that here, then the equality methods can come here as well.
2026-03-22 20:22:04 -04:00
Matt Nadareski
2ade7495c7 This will be public anyway 2026-03-22 02:44:12 -04:00
Matt Nadareski
1acd448838 Make validation methods public 2026-03-22 02:05:43 -04:00
Matt Nadareski
a8ed7a38f8 Fix issue with 3DS key assignment 2026-03-22 01:58:11 -04:00
Matt Nadareski
5208fc3e33 Add removal comment 2026-03-22 01:53:40 -04:00
Matt Nadareski
3dfbd23867 Pass in fixed crypto key flag as bool 2026-03-22 01:52:26 -04:00
Matt Nadareski
994a366770 Put initial value generators in common region 2026-03-22 01:35:36 -04:00
Matt Nadareski
d1b061fac5 Port 3DS decrypt/encrypt in a functional manner 2026-03-22 01:34:36 -04:00
Matt Nadareski
6e73c93c89 Port PartitionKeys class from NDecrypt 2026-03-22 01:10:39 -04:00
Matt Nadareski
5544ab0b8a Add majority of data extensions tests, fix minor issues 2026-03-21 20:09:10 -04:00
Matt Nadareski
8bec2087eb Add removal note 2026-03-21 17:55:14 -04:00
Matt Nadareski
50e27f926d Use separate libraries as references in executables 2026-03-21 16:41:09 -04:00
Matt Nadareski
ecc88316bd Nicer readme linking 2026-03-21 16:34:52 -04:00
Matt Nadareski
7689c6dd07 Libraries
This change looks dramatic, but it's just separating out the already-split namespaces into separate top-level folders. In theory, every single one could be built into their own Nuget package. `SabreTools.Serialization` still builds the normal Nuget package that is used by all other projects and includes all namespaces.
2026-03-21 16:26:56 -04:00
Matt Nadareski
bec0aeb04c Another size read mismatch in IRD 2026-03-20 22:09:07 -04:00
Matt Nadareski
2f8ee5545e IRD game title is UTF-8 2026-03-20 22:07:35 -04:00
Matt Nadareski
015ad062ac Prefix sizes were being read incorrectly for IRD parsing 2026-03-20 22:01:13 -04:00
Matt Nadareski
ae6608e2a7 Allow GCF checksum header to have dummy0 value of 0 (fixes #69) 2026-03-19 16:18:24 -04:00
Matt Nadareski
ca78898e93 Move wrapper factory file after namespace change 2026-03-18 22:17:07 -04:00
Matt Nadareski
a6ee92da32 Fix build 2026-03-18 17:27:06 -04:00
Matt Nadareski
b2eb0b5e67 Extensions classes should be named as such 2026-03-18 17:15:03 -04:00
Matt Nadareski
685fadbfdf Move wrapper utility classes into Wrappers namespace 2026-03-18 16:55:48 -04:00
Matt Nadareski
638934e6b0 Attributes should live in the Models namespace where it's used 2026-03-18 16:49:14 -04:00
Matt Nadareski
cd2e170175 Move ported attribute to root 2026-03-18 16:42:23 -04:00
Matt Nadareski
68b5436f59 Wrappers to its own namespace 2026-03-18 16:37:59 -04:00
Matt Nadareski
b2f074048d Create extensions test project 2026-03-18 16:21:16 -04:00
Matt Nadareski
3147974879 Create OID test project 2026-03-18 16:20:00 -04:00
Matt Nadareski
1a53c093b8 OID to its own namespace 2026-03-18 16:18:10 -04:00
Matt Nadareski
e4d26d4d81 Put interfaces in respective namespaces 2026-03-18 16:13:02 -04:00
Matt Nadareski
b3e25bb6a6 Fix interface file names to match internal names 2026-03-18 16:02:40 -04:00
Matt Nadareski
37d5b79b96 Rename directories to visually help determine real namespace 2026-03-18 15:56:45 -04:00
Matt Nadareski
75fef90a14 Reader options apparently moved fully 2026-03-17 19:47:27 -04:00
Matt Nadareski
9389797448 Bump version 2026-03-15 20:23:35 -04:00
Matt Nadareski
8b7112d12f uint is more correct 2026-03-14 20:51:22 -04:00
Matt Nadareski
4add8f3e64 Update TODO for accuracy 2026-03-14 11:52:07 -04:00
Matt Nadareski
9e6c6af985 Fix missing section case, should never be hit 2026-03-14 11:24:29 -04:00
Matt Nadareski
c82b56d084 Better clarify comment 2026-03-14 11:23:29 -04:00
Matt Nadareski
ea4c51b9f2 Use first nonzero-sized section as overlay address basis 2026-03-14 11:20:40 -04:00
HeroponRikiBestest
463c3ba50d Accurately get OverlayAddress for UPX-compressed executables (#67)
* Add alternate path for OverlayAddress for upx-compressed executables, for benefit of extractions that need an accurate overlay address.

* Use SectionNames

* Update SabreTools.Serialization/Wrappers/PortableExecutable.cs

---------

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2026-03-14 11:18:19 -04:00
Matt Nadareski
92356903cc Format alternate title IDs too 2026-03-13 21:15:34 -04:00
Matt Nadareski
82ed800a32 Add formatted Title ID output 2026-03-13 21:01:38 -04:00
Matt Nadareski
81cd284a80 Add debug/retail-adjusted values as passthru 2026-03-13 13:32:33 -04:00
Matt Nadareski
1eba7403a3 Add Cxbx-Reloaded references 2026-03-13 12:31:33 -04:00
Matt Nadareski
b47af580e3 Add remaining certificate fields 2026-03-13 12:29:17 -04:00
Matt Nadareski
cff722ba67 Fix issues found during testing 2026-03-13 12:10:45 -04:00
Matt Nadareski
810338d011 Create XBE wrapper 2026-03-13 11:40:01 -04:00
Matt Nadareski
76910047e7 Fill out XBE reader 2026-03-13 11:05:31 -04:00
Matt Nadareski
a8e53c2d07 Remove invalid extensions 2026-03-13 11:01:27 -04:00
Matt Nadareski
bb61a1b795 Add RVA mapping helpers for XBE 2026-03-13 10:52:54 -04:00
Matt Nadareski
1d83d40b9b Move XBE models, add skeleton reader 2026-03-13 10:51:31 -04:00
Matt Nadareski
3459c99b29 Add XBE models 2026-03-13 10:28:05 -04:00
Matt Nadareski
d102e2fc0b Add SNES models 2026-03-11 11:47:39 -04:00
Matt Nadareski
c7a932c07f Add secondary FDS/QD sources 2026-03-10 20:05:51 -04:00
Matt Nadareski
1c334dfb1b Add untested FDS <-> QD conversion 2026-03-10 19:49:12 -04:00
Matt Nadareski
a6656fd5e6 Add QD file support 2026-03-10 18:11:42 -04:00
Matt Nadareski
1e1bf2e27c Handle headerless FDS 2026-03-10 17:58:49 -04:00
Matt Nadareski
de5794b3ae Fix FDS reader 2026-03-10 17:47:11 -04:00
Matt Nadareski
f4192416f7 Fix build 2026-03-10 17:31:09 -04:00
Matt Nadareski
70fe830241 Add fwNES FDS file support 2026-03-10 17:27:58 -04:00
Matt Nadareski
e8394d6665 Rename NES cart models a bit 2026-03-10 17:06:01 -04:00
Matt Nadareski
2b64d34e7a Add Atari Lynx cart support 2026-03-10 14:28:09 -04:00
Matt Nadareski
1909851ed4 Add XGD security sector models 2026-03-10 13:18:58 -04:00
Matt Nadareski
9ce074529a Use documentation for endianness 2026-03-08 13:12:42 -04:00
Matt Nadareski
e2581fc423 Add Atari 7800 cart support 2026-03-07 21:27:11 -05:00
Matt Nadareski
f75bc21d54 Add NES cart extraction 2026-03-07 11:32:47 -05:00
Matt Nadareski
8844632fc7 Nicer NES formatting 2026-03-06 16:50:23 -05:00
Matt Nadareski
2f3ecb88f0 Remove unused enum 2026-03-06 16:26:19 -05:00
Matt Nadareski
6e93aa65b6 Split more things 2026-03-06 16:17:18 -05:00
Matt Nadareski
ca264ce91e Cleanup 2026-03-06 16:00:36 -05:00
Matt Nadareski
b339f213b8 Split NES 2.0 combined pieces 2026-03-06 15:53:16 -05:00
Matt Nadareski
93a6926b43 Split flag 10 2026-03-06 15:36:02 -05:00
Matt Nadareski
5e758d4b38 Split flag 7 2026-03-06 15:16:41 -05:00
Matt Nadareski
756d5fe03c Split flag 6 2026-03-06 14:59:19 -05:00
Matt Nadareski
ff67caee25 Make flags slightly more readable 2026-03-06 14:26:40 -05:00
Matt Nadareski
234f865234 Fix minor typo 2026-03-06 13:53:58 -05:00
Matt Nadareski
2d380770f5 Full printing for NES is overwhelming 2026-03-06 13:51:32 -05:00
Matt Nadareski
3f23df868f Fix logic inversion issue 2026-03-06 13:49:15 -05:00
Matt Nadareski
d150bde546 Add NES cart tests 2026-03-06 13:43:16 -05:00
Matt Nadareski
d108a815d7 Finish NES cart extension properties 2026-03-06 13:39:47 -05:00
Matt Nadareski
7dfb015dd5 Fix reading PRG and CHR 2026-03-06 13:23:17 -05:00
Matt Nadareski
5c973bac96 Fill in large number of extensions for NES cart 2026-03-06 13:18:49 -05:00
Matt Nadareski
c6b96d82dc Add simple extension properties 2026-03-06 12:51:02 -05:00
Matt Nadareski
9f2765edd0 Add NES cart printing 2026-03-06 12:47:59 -05:00
Matt Nadareski
b77e866be4 Add NES TODOs 2026-03-06 11:12:14 -05:00
Matt Nadareski
3995017174 Wire up NES cart to wrapper factories 2026-03-06 11:11:26 -05:00
Matt Nadareski
714c8d4901 Add generic NES cart wrapper 2026-03-06 11:08:41 -05:00
Matt Nadareski
9e68cf7abe Add NES cart reader 2026-03-06 11:03:54 -05:00
Matt Nadareski
1875adfe5f Add NES 1.0 and 2.0 models 2026-03-06 10:35:07 -05:00
Matt Nadareski
ebe4ff7426 Update packages 2026-03-05 11:27:45 -05:00
Matt Nadareski
e9eb3eb4be Generic resource entry is now used 2026-02-12 18:25:24 -05:00
Matt Nadareski
c99f86ee95 Add unused generic resource entry 2026-02-12 16:55:27 -05:00
Matt Nadareski
bb7b1c50a1 Move debug object parsers 2026-02-12 16:07:04 -05:00
Matt Nadareski
d56c8a3d83 Move unused parser 2026-02-12 16:04:16 -05:00
Matt Nadareski
c5f732584b Do the same for string table resource 2026-02-12 16:02:09 -05:00
Matt Nadareski
3e0a6dbf32 Move old version of AlignToBoundary 2026-02-12 15:53:40 -05:00
Matt Nadareski
b91c060dad Do the same for assembly manifest 2026-02-12 15:50:24 -05:00
Matt Nadareski
f0a0fc3137 Do the same for font groups 2026-02-12 15:46:50 -05:00
Matt Nadareski
ad3df18b24 Do the same for version info 2026-02-12 14:08:12 -05:00
Matt Nadareski
d089af71a4 Remove now-redundant As extensions 2026-02-12 13:54:53 -05:00
Matt Nadareski
5f9f037430 Add note about migration to IO 2026-02-12 13:48:21 -05:00
Matt Nadareski
939a2bab41 Move some extension parsers to more common location 2026-02-12 13:44:41 -05:00
Matt Nadareski
2d969048e3 Add currently-unused resource data base class 2026-02-12 13:09:44 -05:00
Matt Nadareski
dc86ed1456 Use accelerator table type 2026-02-12 13:04:37 -05:00
Matt Nadareski
a2b392234d Slight cleanup for clarity 2026-02-12 12:52:52 -05:00
Matt Nadareski
f56014b8a6 This can never be null 2026-02-12 12:46:49 -05:00
Matt Nadareski
3a485d34a1 Correct a couple of things 2026-02-12 12:27:36 -05:00
Matt Nadareski
e1da5f192c Do not overread PKZIP extra fields on malformed (fixes #65) 2026-01-29 14:05:36 -05:00
Matt Nadareski
c15809dd72 Remove unused usings 2026-01-28 21:31:59 -05:00
HeroponRikiBestest
89a67d1bd2 Implement model, reader, wrapper for valve SKU sis files. (#62)
* Try again

* Fix import alphebetization

* Fixes.

* first part of first attempt at a model

* Reimplement Sku Sis parsing

* First round of fixes

* Make sure stream isn't closed

* Missed this newline
2026-01-28 21:30:08 -05:00
Matt Nadareski
c4c6709478 Make note of why only partial driverstatus 2026-01-28 09:55:07 -05:00
Matt Nadareski
5e1f6ac4b3 Partially hook up driverstatus for CMP 2026-01-28 09:52:35 -05:00
Matt Nadareski
8f8d86d0bb Clean up some formatting things 2026-01-27 12:03:01 -05:00
HeroponRikiBestest
ecca11e2f3 Flush before closing in microsoftcabinet.extraction (#61)
I had previously been under the assumption that closing always guaranteed a flush. I don't know of any issues that were being caused, but I should push this before i forget.
2026-01-25 21:41:35 -05:00
Matt Nadareski
527b09c6bc Add debug flag to interface definitions (fixes #60) 2026-01-25 20:07:59 -05:00
Matt Nadareski
69b6595434 Missed this one too 2026-01-25 19:48:07 -05:00
Matt Nadareski
59b918c75f Apparently didn't rename this 2026-01-25 19:46:39 -05:00
Matt Nadareski
344132b50a Add editorconfig, fix issues 2026-01-25 16:15:05 -05:00
Matt Nadareski
a35e58e9cc Fix some errant formatting 2026-01-25 15:08:37 -05:00
Matt Nadareski
4300d94858 Use newer "is not null" syntax 2026-01-25 14:32:49 -05:00
Matt Nadareski
ee85a84d8f Use newer "is null" syntax 2026-01-25 14:30:18 -05:00
Matt Nadareski
2959d82231 Formatting cleanup 2026-01-25 13:38:52 -05:00
HeroponRikiBestest
5dfe6aefb0 Convert Installshield Executable code to use proper reader/wrapper instead of living in PortableExecutable (#59)
* Figure out how to access OverlayAddress in wrapper or reader (ideally the latter) for a non-PE reader/wrapper

* Code works

* Remove TODOs

* First round of fixes.

* use constants

* remove comment
2026-01-25 13:36:17 -05:00
HeroponRikiBestest
7f7d0f84ef Re-enable stream extraction for MScab (#58)
* Re-enable extraction from stream

* Remove todo pre PR

* Properly support all situations, remove whitespace
2026-01-25 12:24:28 -05:00
Matt Nadareski
4bfaf36e24 Formatting cleanup 2026-01-24 21:43:08 -05:00
HeroponRikiBestest
695fc4b0af Change mscab extraction to use state handling, break into more helper methods (#57)
* Attempt 1

* Use private helper class

* Remove comment

* Add missing summaries

* Don't use create pattern

* Remove removed variable summary

* Reduce what needs to be passed in

* Further reduce what needs to be passed in

* These didn't get tabbed for some reason

* First round of fixes.

* Second round of changes

* Add a few comments.
2026-01-24 21:41:23 -05:00
Matt Nadareski
f16ed9742f Support WoD info CMP block type 2026-01-24 11:37:29 -05:00
Matt Nadareski
6bfe68b8cf Formatting and comment cleanup 2026-01-24 10:24:35 -05:00
HeroponRikiBestest
2e5c5b3c79 Extract Ms-cabs while reading instead of loading all datablocks into memory (#56)
* WIP

* WIP 2

* Todo: you're missing a read somehow and getting misaligned by two bytes? maybe properly implementing the buffer will magically fix it

* continued blocks my behated

* Pre-major-testing

* Forgot to add summaries

* Attempt to properly roll back. The state i wanted to roll back to wasn't in a commit before.

* Figured out the issue with the rolled back commit, this has to be a while loop because of 0 byte files. Reimplemented clean code.

* Comment so I don't forget why it's like this

* Skip unsupported compression types before opening filestream.

* Reenable non-start cab skipping

* TODO so i don't forget

* TODO so I don't forget

* iterate on continued block correctly.

* Handle incomplete extraction better

* Remove TODOs to ready for PR comments

* Missed one

* Some minor fixes before rewriting everything

* Next round of fixes.

* Next set of fixes

* Fix debug output
2026-01-24 10:08:18 -05:00
Matt Nadareski
2a6e193756 Formatting cleanup 2026-01-05 13:48:36 -05:00
HeroponRikiBestest
65a86d87f0 Fix mszip relying on exceptions (#52)
* Better fix

* initial

* Fixed

* Move function to proper location, clear memory better

* work so far

* more

* more work

* No more per file seeking

* Finished work for now.

* First round of fixes.

* Return if array is empty too
2026-01-05 13:45:20 -05:00
HeroponRikiBestest
d7131dfa4c fix naming (#53) 2025-12-04 11:10:46 -05:00
HeroponRikiBestest
a228b16314 Fix GCF extraction (#48) 2025-12-04 10:46:13 -05:00
Matt Nadareski
b8c9048ac6 Update GrindCore to 0.6.5 2025-12-02 19:35:20 -05:00
HeroponRikiBestest
0b54350d59 Explicitly handle password-protected archives rather than repeatedly throwing exceptions (#47)
* Explicitly handle password-protected archives rather than repeatedly throwing exceptions

* Fix logic error

* Continues

* Remove bool
2025-12-02 09:38:11 -05:00
Deterous
59eddb7129 Fix ISO9660 extraction of large files (#46)
* ExtentLength is uint

* Fix

* fix2

* fix3

* fix4

* Invert multi extent final extent logic
2025-12-02 07:31:31 -05:00
Matt Nadareski
1096232f9d Update GC libraries 2025-11-30 19:40:35 -05:00
Matt Nadareski
4b39ee8d00 Add CRC-16 and CRC-64 dat support 2025-11-29 10:11:05 -05:00
Matt Nadareski
27a9b12bbf Bump version 2025-11-25 07:45:09 -05:00
Matt Nadareski
11319c3341 Update GC.SharpCompress 2025-11-25 07:44:29 -05:00
Matt Nadareski
4de061e0d1 Bump version 2025-11-24 08:59:19 -05:00
Matt Nadareski
258e894c30 Update GC.SharpCompress 2025-11-23 19:59:28 -05:00
Matt Nadareski
8658eaf4e0 Format GHA definitions 2025-11-17 08:39:16 -05:00
Matt Nadareski
6d341abc75 Fix comment about CFB 2025-11-17 08:33:00 -05:00
Matt Nadareski
4821d63b4a Off-by-one spacing 2025-11-15 09:10:34 -05:00
Matt Nadareski
0617bf3170 Process all folders in all MS-CAB files in a set (fixes #43) 2025-11-15 09:00:28 -05:00
Matt Nadareski
425d13a2ac Fix inconsistencies in IS-CAB 2025-11-14 23:21:04 -05:00
Matt Nadareski
8a1e90f537 Reduce unnecessary casts 2025-11-14 21:33:55 -05:00
Matt Nadareski
ddc00948ed Remove red herring zlib code 2025-11-14 21:08:55 -05:00
Matt Nadareski
c96ee81f06 Simplify fixed statements 2025-11-14 20:36:46 -05:00
Matt Nadareski
f6201e2c35 In-depth cleanup with .NET 10 concepts 2025-11-14 14:06:43 -05:00
Matt Nadareski
d63fcfcf7f Minor formatting change to external code 2025-11-14 10:30:15 -05:00
Matt Nadareski
dcc50c4c23 Remove empty .gitmodules 2025-11-14 10:28:55 -05:00
Matt Nadareski
60e46e580d Cache more properties using the field keyword 2025-11-14 10:14:50 -05:00
Matt Nadareski
0638998773 Use auto-fields where possible 2025-11-14 09:48:00 -05:00
Matt Nadareski
ad3113987b Add support for .NET 10 2025-11-14 09:06:59 -05:00
Matt Nadareski
f2f0d0f2e7 Try to fix self-referencing MS-CAB 2025-11-13 14:07:46 -05:00
Matt Nadareski
f7fd2f6f65 Name some type parameters 2025-11-11 09:57:38 -05:00
Matt Nadareski
281c18e21d Bump version 2025-11-06 08:08:10 -05:00
Matt Nadareski
1a9c8b5392 Scaffolding for the future 2025-11-06 08:01:04 -05:00
Deterous
831a272f4b Final ISO9660 fix (#41)
* debug

* using

* Test fix

* Final fix
2025-11-05 20:26:42 -05:00
Matt Nadareski
f47e68b9ba Add Mode0 and Mode2 formless models 2025-11-05 09:49:32 -05:00
Matt Nadareski
eaabd2ee77 Add static readers for CD-ROM sector types 2025-11-05 09:37:56 -05:00
Matt Nadareski
a269edd6ea Missed one explicit type 2025-11-05 09:20:48 -05:00
Matt Nadareski
3bb501ce0c Separate out some helper methods 2025-11-05 09:14:37 -05:00
Deterous
d0006a4653 Custom ISO Stream for full CDROM support (#40)
* Implement custom ISO stream for CDROM support

* Delete old files

* Delete old test

* Leaner CDROM wrapper

* Fix custom stream

* WrapperBase using ISO stream

* Reset stream after deserializing

* Fix SeekOrigin

* Return to cached position

* CDROM Constants

* correct ns

* fix href
2025-11-05 08:20:34 -05:00
Matt Nadareski
c48a1394bc Be descriptive about types 2025-11-04 14:15:39 -05:00
Matt Nadareski
fcf7fcfd44 Be descriptive about types 2025-11-04 10:49:03 -05:00
Matt Nadareski
cca08e7103 Use consistent naming 2025-11-04 10:27:00 -05:00
Matt Nadareski
b908732f5c Fix logical block extension 2025-11-04 10:25:55 -05:00
Matt Nadareski
4ee4a6e85f Slight extension cleanup 2025-11-04 10:07:57 -05:00
Matt Nadareski
7990f11bab Remove unnecessary null checks 2025-11-04 09:54:24 -05:00
Matt Nadareski
427f7c9136 Remove unnecessary null checks 2025-11-04 09:46:03 -05:00
Matt Nadareski
f938f1593d Formatting update for readability 2025-11-04 09:15:12 -05:00
Matt Nadareski
480a1834ed Logic inversion and slight cleanup 2025-11-03 20:16:54 -05:00
Matt Nadareski
df5acc648c Minor cleanup 2025-11-03 10:31:30 -05:00
Matt Nadareski
10569e6aa1 Add tests for CDROM 2025-11-03 10:29:52 -05:00
Matt Nadareski
e5103b4830 Fix format comment for ExtractionTool 2025-11-03 09:27:57 -05:00
Matt Nadareski
513646fc53 Fix ExtractionTool publish 2025-11-03 09:03:45 -05:00
Matt Nadareski
27f723bf2f CascLibSharp isn't used and can be removed 2025-11-03 08:53:04 -05:00
Matt Nadareski
6511ecb5ad Merge pull request #39 from SabreTools/storm
Directly include StormLibSharp
2025-11-03 08:39:47 -05:00
Matt Nadareski
7bf6e6f344 Code cleanup and slight modernization 2025-11-03 08:36:12 -05:00
Matt Nadareski
d3e340ae39 Add and use MPQ filename constants 2025-11-02 23:21:29 -05:00
Matt Nadareski
22167a9617 Recursion isn't needed now 2025-11-02 22:27:02 -05:00
Matt Nadareski
48bea55133 Enable extraction on all .NET versions (not OSes) 2025-11-02 22:20:21 -05:00
Matt Nadareski
42239919e5 Enable StormLib for all .NET versions 2025-11-02 22:07:40 -05:00
Matt Nadareski
4840d9df6e Remove NoWarn and fix issues 2025-11-02 21:41:58 -05:00
Matt Nadareski
1378b87ea6 Directly include StormLibSharp code 2025-11-02 21:11:39 -05:00
Matt Nadareski
6b48164b97 Remove StormLibSharp as submodule 2025-11-02 21:07:51 -05:00
Matt Nadareski
50b09aa249 Automated cleanup 2025-11-02 19:28:16 -05:00
Deterous
ecbd147f6d CD-ROM (Volume Descriptor Set) support (#37)
* CD-ROM Wrapper

* Fix CDROMVolume Reader

* Fix SectorMode enum
2025-11-02 19:21:19 -05:00
HeroponRikiBestest
c13df79848 Fix extraction of recent MPF-zipped logs (#38)
* Fix extraction of recent MPF-zipped logs

* Use greater than or equal to

* corrected logic
2025-11-02 08:35:02 -05:00
Deterous
48bcc1de5b ISO9660: Skip interleaved files and other fixes (#35)
* Skip interleaved files

* Fix warning logic

* Refactor

* Fix extraction

* Multi extent

* review
2025-11-01 22:52:41 -04:00
Matt Nadareski
d082f5de25 Auto formatting cleanup 2025-10-31 21:07:13 -04:00
Deterous
c2321669b6 Support ISO9660 Extended Attribute Record (#34)
* Support ISO9660 Extended Attribute Record

* fix

* Fix

* Don't extract multi-extent files

* ReadUInt16LittleEndian

* debug

* Fix

* Skip EAR when extracting files

* Add comment about EAR

* Fix same-name extraction

* Safer directory parsing

* even safer

* return null

* more directory record checks

* not nullable nullable

* init system use

* valid records should pass

* review

* remove empty case
2025-10-31 21:04:45 -04:00
Matt Nadareski
e382635b85 Fix accidental recursion 2025-10-31 14:01:25 -04:00
Matt Nadareski
b4e02d7f8c Ensure default values in models when not optional 2025-10-31 13:59:28 -04:00
Matt Nadareski
0dc8a0b6c0 One missed explicit endian read 2025-10-31 09:36:28 -04:00
Matt Nadareski
171834986d Fix build from previous cleanup 2025-10-31 00:01:35 -04:00
Matt Nadareski
97af686b73 ZSTD model cleanup 2025-10-30 23:59:25 -04:00
Matt Nadareski
7d627ffc6f XZP model cleanup 2025-10-30 23:59:08 -04:00
Matt Nadareski
d924f25eb2 XZ model cleanup 2025-10-30 23:57:15 -04:00
Matt Nadareski
abd8ef3b1f Xbox model cleanup 2025-10-30 23:55:08 -04:00
Matt Nadareski
e9b8dba3a6 WiseInstaller model cleanup 2025-10-30 23:54:22 -04:00
Matt Nadareski
aaff7e7332 WAD3 model cleanup 2025-10-30 23:50:44 -04:00
Matt Nadareski
2fa00d1a16 VPK model cleanup 2025-10-30 23:48:26 -04:00
Matt Nadareski
8a2a2ecd12 TAR model cleanup 2025-10-30 23:45:14 -04:00
Matt Nadareski
98517bdd8c StarForce model cleanup 2025-10-30 23:43:06 -04:00
Matt Nadareski
dfe76f7e7e SpoonInstaller model cleanup 2025-10-30 23:41:40 -04:00
Matt Nadareski
a2e24b1f5f SGA model cleanup 2025-10-30 23:40:45 -04:00
Matt Nadareski
6a4e76e245 SevenZip model cleanup 2025-10-30 23:34:20 -04:00
Matt Nadareski
1ebec97e0e SecuROM model cleanup 2025-10-30 23:34:06 -04:00
Matt Nadareski
b9f03b02f0 RealArcade model cleanup 2025-10-30 23:31:19 -04:00
Matt Nadareski
2c06760778 RAR model cleanup 2025-10-30 23:31:01 -04:00
Matt Nadareski
3f0ee70b71 Quantum model cleanup 2025-10-30 23:30:45 -04:00
Matt Nadareski
05b34f158b PortableExecutable model cleanup 2025-10-30 23:29:24 -04:00
Matt Nadareski
e08db935b4 PlayStation4 model cleanup 2025-10-30 23:07:12 -04:00
Matt Nadareski
47da068bd3 PlayStation3 model cleanup 2025-10-30 23:06:46 -04:00
Matt Nadareski
9de669606d PlayJ model cleanup 2025-10-30 23:05:24 -04:00
Matt Nadareski
a02d50e61d PKZIP model cleanup 2025-10-30 23:01:55 -04:00
Matt Nadareski
069dab7fba PIC model cleanup 2025-10-30 22:58:02 -04:00
Matt Nadareski
d65edadbbb PFF model cleanup 2025-10-30 22:56:23 -04:00
Matt Nadareski
6307a51c0a PAK model cleanup 2025-10-30 22:54:37 -04:00
Matt Nadareski
d27c602bdf OLE model cleanup 2025-10-30 22:52:56 -04:00
Matt Nadareski
59fe9cb113 Nitro model cleanup 2025-10-30 22:51:16 -04:00
Matt Nadareski
b4b7ba75cb NewExecutable model cleanup 2025-10-30 22:47:17 -04:00
Matt Nadareski
d02e0c16c4 NCF model cleanup 2025-10-30 22:39:08 -04:00
Matt Nadareski
4aa0eb54b7 N3DS model cleanup 2025-10-30 22:36:21 -04:00
Matt Nadareski
c4f73abcb6 MSDOS model cleanup 2025-10-30 22:15:10 -04:00
Matt Nadareski
73dec51a4d MoPaQ model cleanup 2025-10-30 22:12:06 -04:00
Matt Nadareski
b7c3c094ae MicrosoftCabinet model cleanup 2025-10-30 22:07:29 -04:00
Matt Nadareski
97702ab3d5 LZ model cleanup 2025-10-30 22:03:22 -04:00
Matt Nadareski
a9b5fe706c LinearExecutable model cleanup 2025-10-30 22:00:42 -04:00
Matt Nadareski
198d48610c LDSCRYPT model cleanup 2025-10-30 21:55:01 -04:00
Matt Nadareski
c0e0344914 IRD model cleanup 2025-10-30 21:54:27 -04:00
Matt Nadareski
d49b2f1b60 InstallShieldExecutable model cleanup 2025-10-30 21:52:20 -04:00
Matt Nadareski
ec7172466b InstallShieldCabinet model cleanup 2025-10-30 21:51:15 -04:00
Matt Nadareski
d75a512ead InstallShieldArchiveV3 model cleanup 2025-10-30 21:43:38 -04:00
Matt Nadareski
af8b6906b6 GZIP model cleanup 2025-10-30 21:40:25 -04:00
Matt Nadareski
96bc301e6d GCF model cleanup 2025-10-30 21:38:17 -04:00
Matt Nadareski
c10749f34d DVD model cleanup 2025-10-30 21:32:21 -04:00
Matt Nadareski
0e4235c8ba Delphi model cleanup 2025-10-30 21:30:41 -04:00
Matt Nadareski
b2d31bb87e CueSheets model cleanup 2025-10-30 21:30:13 -04:00
Matt Nadareski
3ee88793da COFF model cleanup 2025-10-30 21:29:24 -04:00
Matt Nadareski
ad0f01c3ef CHD model cleanup 2025-10-30 21:27:37 -04:00
Matt Nadareski
63a96a7cf8 CFB model cleanup 2025-10-30 21:26:19 -04:00
Matt Nadareski
b8d8ca9a5c BZip2 model cleanup 2025-10-30 21:22:33 -04:00
Matt Nadareski
6b0fca31ae BSP model cleanup 2025-10-30 21:21:56 -04:00
Matt Nadareski
c147b12b10 Bitmap model cleanup 2025-10-30 20:58:03 -04:00
Matt Nadareski
7884b0b775 BFPK model cleanup 2025-10-30 20:57:35 -04:00
Matt Nadareski
9f3c263328 BDPlus model cleanup 2025-10-30 20:55:39 -04:00
Matt Nadareski
b830ce3d73 AdvancedInstaller model cleanup 2025-10-30 20:54:32 -04:00
Matt Nadareski
e8b7f33bf0 AACS model cleanup 2025-10-30 20:52:35 -04:00
Matt Nadareski
eae75c5943 Allow byte arrays to be non-null in most models 2025-10-30 20:44:16 -04:00
Matt Nadareski
c1f5ebcae1 Remove experiments 2025-10-30 19:50:03 -04:00
Matt Nadareski
acb8e98fe2 Add ImgBurn and UltraISO check extensions 2025-10-30 17:04:29 -04:00
Matt Nadareski
a24a92e97c Add unset reserved bytes extension 2025-10-30 16:22:05 -04:00
Matt Nadareski
f2d2fe97bb Override 2025-10-30 12:06:42 -04:00
Matt Nadareski
705252eec7 Unify Skeleton to ISO-9660 2025-10-30 12:03:13 -04:00
Matt Nadareski
69ae0456f0 Add skeleton printing and tests 2025-10-30 11:50:37 -04:00
Deterous
f07bd07cce Redumper skeleton support (#32)
* Skeleton support

* hide ISO extract

* Return true

* Don't inherit ISO9660

* fix

* fix2

* Skeleton
2025-10-30 11:46:45 -04:00
Matt Nadareski
9db2d2ca05 Reduce complexity of Zstd filename generation 2025-10-30 10:23:50 -04:00
Deterous
85248b0135 ISO 9660 Extraction (#31)
* ISO9660 Extraction

* Fix build

* Decode strings in Printer

* Double comment
2025-10-30 10:00:10 -04:00
Matt Nadareski
8663edc7df Fix unsynced ISO position 2025-10-30 08:20:46 -04:00
Matt Nadareski
b9cc9e40fd Changes based on a code sweep with unrelated issue 2025-10-30 07:51:08 -04:00
HeroponRikiBestest
5ce085ad2b Implement basic ZSTD detection and extraction. (#30)
* Implement basic ZSTD detection and extraction.

* Fix comment

* Remove newline

* Add newline

* Move section

* more newlines banished to the ether

* fix comment and name

* fix final i think
2025-10-29 22:01:23 -04:00
Matt Nadareski
d24f8a2fce Add ECMA link to models readme 2025-10-29 10:11:39 -04:00
Matt Nadareski
a73e830209 This extension is useful 2025-10-29 09:46:12 -04:00
Matt Nadareski
146fb42fd0 Replace remaining Seek with SeekIfPossible 2025-10-29 09:34:52 -04:00
Matt Nadareski
5b54ed719e Better integrate new printing model 2025-10-29 09:23:45 -04:00
Matt Nadareski
02d011228e IRD wrapper exists and should be used 2025-10-29 08:57:01 -04:00
Matt Nadareski
07adfd20a3 Add tests for ISO9660 2025-10-29 08:55:45 -04:00
Matt Nadareski
1d00abd6cc Make printing a wrapper extension 2025-10-29 08:53:14 -04:00
Deterous
36c2cc2f15 ISO 9660 support (#29)
* Begin defining ISO9660 models

* Fix initial models

* Improve ISO9660 models

* ISO9660 models

* Fix fields

* Nullable and enums

* Fix flags attribute

* Start ISO9660 Reader code

* semicolon

* Fix build

* Use EqualsExactly

* Fix build

* Update ISO9660.cs

* Update ISO9660.cs

* Move DirectoryRecordDateTime class

* ParseDirectoryRecordDateTime function

* ST refactoring

* fix return

* return array

* ISO9660 wrapper

* Initial printing code

* semicolon

* Fix

* orphan variable name

* fix null ref

* ISO9660 Printer

* Fix DirectoryRecord parsing

* fix

* test

* test

* Fix reader

* extractable ISO

* partial wrapper class

* namespace

* extension property

* path tables

* Cleanup

* rename directory

* fix

* fix

* typo

* fix2

* Fix

* Cleanup reader

* Count list property

* Parse Path Tables

* fix model name

* fix

* fix2

* fix3

* print path table records

* debug errors

* debug error

* debug

* undo debug

* Fix casting

* Validate path table locations

* print table numbers

* Printer cleanup

* fix printer

* printer++

* Print DecDateTime

* else if

* BootSystemUse

* Use Linq

* custom zero check

* fix

* Update ISO9660.cs

* Update ISO9660.cs

* Update ISO9660.cs

* Update ISO9660.cs

* fix

* debug

* debug2

* debug3

* debug4

* debug5

* debug6

* debug7

* debug8

* Array.TrueForAll

* Update ISO9660.cs

* fix

* cleanup

* typo

* Use BothInt numerics

* using SabreTools.Numerics

* Test nonnull

* nonnull bothint

* Print invalid BothInts

* Print directory record

* typo

* fix

* null check

* directory descriptors

* cleanup printer

* semicolon

* Fix

* flags never null

* more non nullable

* no null

* AppendLineBothEndian

* fix

* rename to Directory

* fix

* namespace

* full namespace

* fix

* cleanup

* Parse directories

* fix

* Fix

* Test parse directories

* PeekByteValue

* Revert CI changes

* Dummy extractor

* Review and fixes

* Fixes

* Final fix

* big endian directory search

* fix big endian changes

* Final fix
2025-10-29 08:46:08 -04:00
Matt Nadareski
27cf01ce84 Add printing cases for empty alongside null 2025-10-28 09:52:53 -04:00
Matt Nadareski
1e94163693 Add both-endian StringBuilder extensions
This has to use a new name for the both-endian handling because otherwise the implicit type handling will otherwise cause compilation errors.
2025-10-28 09:49:38 -04:00
Matt Nadareski
db1f94775b Convert some known Peek calls 2025-10-27 22:55:25 -04:00
Matt Nadareski
ce394eb4e9 Seek, if possible 2025-10-27 22:43:56 -04:00
Matt Nadareski
15109bed88 Update IO to 1.8.0 2025-10-27 22:15:54 -04:00
Matt Nadareski
8a18c8de34 Update rolling tag 2025-10-26 20:31:03 -04:00
HeroponRikiBestest
2f093a80c7 increase buffer size for isexe reading / writing based on deterous's … (#28)
* increase buffer size for isexe reading / writing based on deterous's input

* remove comment
2025-10-26 18:54:35 -04:00
Matt Nadareski
5df1af9c17 Minor cleanup and additions 2025-10-23 16:05:12 -04:00
HeroponRikiBestest
d5ab37a5a6 Add extraction for installshield executables. (#27)
* Add extractor for installshield executables.

* Add comment

* Fix intendation.

* Ensure PRs recursively pull

* Rebase

* Fix formatting for parsex

* newline before obj.name

* Changed while loop check to a position vs length check

* Specify type for chunkSize

* declare chunksize as a const int outside of the loop

* Open file writer with using

* Call fs.flush after every write.

* Improved chunk writing loop.

* Remove now-unecessary variable

* Properly handle extracting files of size greater than int32.

* Use long for overlay address

* create deserializer outside of loop.

* Move null check.

* Don't cache position.

---------

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2025-10-23 15:38:51 -04:00
Matt Nadareski
ed9ecf0da9 Ensure PRs recursively pull 2025-10-23 12:08:47 -04:00
Matt Nadareski
4fc17197ca Fix inverted logic of file only flag 2025-10-19 17:59:56 -04:00
Matt Nadareski
94a9cf0c3f Minor tweak to Wise debug statements 2025-10-17 09:56:04 -04:00
2128 changed files with 129657 additions and 25286 deletions

167
.editorconfig Normal file
View File

@@ -0,0 +1,167 @@
# top-most EditorConfig file
root = true
# C# files
[*.cs]
# Indentation and spacing
charset = utf-8
indent_size = 4
indent_style = space
tab_width = 4
trim_trailing_whitespace = true
# New line preferences
end_of_line = lf
insert_final_newline = true
max_line_length = unset
# using directive preferences
csharp_using_directive_placement = outside_namespace
dotnet_diagnostic.IDE0005.severity = error
# Code-block preferences
csharp_style_namespace_declarations = block_scoped
csharp_style_prefer_method_group_conversion = true
csharp_style_prefer_top_level_statements = false
# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_inlined_variable_declaration = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable
dotnet_diagnostic.IDE0001.severity = warning
dotnet_diagnostic.IDE0002.severity = warning
dotnet_diagnostic.IDE0004.severity = warning
dotnet_diagnostic.IDE0010.severity = error
dotnet_diagnostic.IDE0051.severity = warning
dotnet_diagnostic.IDE0052.severity = warning
dotnet_diagnostic.IDE0072.severity = warning
dotnet_diagnostic.IDE0080.severity = warning
dotnet_diagnostic.IDE0100.severity = error
dotnet_diagnostic.IDE0110.severity = error
dotnet_diagnostic.IDE0120.severity = warning
dotnet_diagnostic.IDE0121.severity = warning
dotnet_diagnostic.IDE0240.severity = error
dotnet_diagnostic.IDE0241.severity = error
dotnet_style_coalesce_expression = true
dotnet_style_namespace_match_folder = false
dotnet_style_null_propagation = true
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_compound_assignment = true
csharp_style_prefer_simple_property_accessors = true
dotnet_style_prefer_simplified_interpolation = true
dotnet_style_prefer_simplified_boolean_expressions = true
csharp_style_prefer_unbound_generic_type_in_nameof = true
# Field preferences
dotnet_diagnostic.IDE0044.severity = warning
dotnet_style_readonly_field = true
# Language keyword vs. framework types preferences
dotnet_diagnostic.IDE0049.severity = error
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true
# Modifier preferences
csharp_prefer_static_local_function = true
csharp_style_prefer_readonly_struct = true
dotnet_diagnostic.IDE0036.severity = warning
dotnet_diagnostic.IDE0040.severity = error
dotnet_diagnostic.IDE0380.severity = error
dotnet_style_require_accessibility_modifiers = always
# New-line preferences
dotnet_diagnostic.IDE2000.severity = warning
dotnet_diagnostic.IDE2002.severity = warning
dotnet_diagnostic.IDE2003.severity = warning
dotnet_diagnostic.IDE2004.severity = warning
dotnet_diagnostic.IDE2005.severity = warning
dotnet_diagnostic.IDE2006.severity = warning
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
dotnet_style_allow_multiple_blank_lines_experimental = false
dotnet_style_allow_statement_immediately_after_block_experimental = false
# Null-checking preferences
csharp_style_conditional_delegate_call = true
# Parameter preferences
dotnet_code_quality_unused_parameters = all
dotnet_diagnostic.IDE0280.severity = error
# Parentheses preferences
dotnet_diagnostic.IDE0047.severity = warning
dotnet_diagnostic.IDE0048.severity = warning
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = always_for_clarity
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
# Pattern-matching preferences
dotnet_diagnostic.IDE0019.severity = warning
dotnet_diagnostic.IDE0020.severity = warning
dotnet_diagnostic.IDE0038.severity = warning
dotnet_diagnostic.IDE0066.severity = none
dotnet_diagnostic.IDE0083.severity = warning
dotnet_diagnostic.IDE0260.severity = warning
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
# this. and Me. preferences
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
# var preferences
csharp_style_var_for_built_in_types = false
csharp_style_var_when_type_is_apparent = true
# .NET formatting options
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
# C# formatting options
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false

View File

@@ -1,40 +1,48 @@
name: Build and Test
on:
push:
branches: [ "main" ]
push:
branches: ["main"]
jobs:
build:
runs-on: ubuntu-latest
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh -d
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: "*.nupkg,*.snupkg,*.zip"
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh -d
- name: Update rolling tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -f rolling
git push origin :refs/tags/rolling || true
git push origin rolling --force
- name: Upload to rolling
uses: ncipollo/release-action@v1.20.0
with:
allowUpdates: True
artifacts: "*.nupkg,*.snupkg,*.zip"
body: "Last built commit: ${{ github.sha }}"
name: "Rolling Release"
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

View File

@@ -3,21 +3,21 @@ name: Build PR
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Build
run: dotnet build
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Run tests
run: dotnet test
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Build
run: dotnet build
- name: Run tests
run: dotnet test

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.lscache
*.suo
*.user
*.userosscache

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "SabreTools.Serialization/_EXTERNAL/stormlibsharp"]
path = SabreTools.Serialization/_EXTERNAL/stormlibsharp
url = https://github.com/robpaveza/stormlibsharp.git

14
.vscode/launch.json vendored
View File

@@ -1,16 +1,16 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (ExtractionTool)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/ExtractionTool/bin/Debug/net9.0/ExtractionTool.dll",
"program": "${workspaceFolder}/ExtractionTool/bin/Debug/net10.0/ExtractionTool.dll",
"args": [],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
@@ -24,7 +24,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/InfoPrint/bin/Debug/net9.0/InfoPrint.dll",
"program": "${workspaceFolder}/InfoPrint/bin/Debug/net10.0/InfoPrint.dll",
"args": [],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>2.0.2</Version>
<Version>3.2.0</Version>
</PropertyGroup>
<!-- Support All Frameworks -->
@@ -20,33 +20,23 @@
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>
<!-- These are needed for dealing with native Windows DLLs -->
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
<ContentWithTargetPath Include="..\SabreTools.Serialization\runtimes\win-x86\native\CascLib.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<TargetPath>CascLib.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="..\SabreTools.Serialization\runtimes\win-x86\native\StormLib.dll">
<ContentWithTargetPath Include="..\runtimes\win-x86\native\StormLib.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<TargetPath>StormLib.dll</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x64'">
<ContentWithTargetPath Include="..\SabreTools.Serialization\runtimes\win-x64\native\CascLib.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<TargetPath>CascLib.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="..\SabreTools.Serialization\runtimes\win-x64\native\StormLib.dll">
<ContentWithTargetPath Include="..\runtimes\win-x64\native\StormLib.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<TargetPath>StormLib.dll</TargetPath>
@@ -54,13 +44,13 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.Serialization\SabreTools.Serialization.csproj" />
<ProjectReference Include="..\SabreTools.Wrappers\SabreTools.Wrappers.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.CommandLine" Version="[1.3.2]" />
<PackageReference Include="SabreTools.IO" Version="[1.7.6]" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="SabreTools.CommandLine" Version="1.5.0" />
<PackageReference Include="SabreTools.IO" Version="2.3.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.11" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>
</Project>

View File

@@ -3,8 +3,8 @@ using System.IO;
using SabreTools.CommandLine;
using SabreTools.CommandLine.Inputs;
using SabreTools.IO.Extensions;
using SabreTools.Serialization;
using SabreTools.Serialization.Wrappers;
using SabreTools.Numerics.Extensions;
using SabreTools.Wrappers;
namespace ExtractionTool.Features
{
@@ -113,183 +113,52 @@ namespace ExtractionTool.Features
/// <param name="path">File path</param>
private void ExtractFile(string file)
{
Console.WriteLine($"Attempting to extract all files from {file}");
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// Get the extension for certain checks
string extension = Path.GetExtension(file).ToLower().TrimStart('.');
// Get the first 16 bytes for matching
byte[] magic = new byte[16];
try
{
int read = stream.Read(magic, 0, 16);
stream.Seek(0, SeekOrigin.Begin);
Console.WriteLine($"Attempting to extract all files from {file}");
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// Read the first 32 bytes
byte[] magic = stream.PeekBytes(32);
// Get the file type
string extension = Path.GetExtension(file).TrimStart('.');
WrapperType ft = WrapperFactory.GetFileType(magic ?? [], extension);
// Print out the file format
Console.WriteLine($"File format found: {ft}");
// Setup the wrapper to extract
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
// If we don't have a wrapper
if (wrapper is null)
{
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
Console.WriteLine();
return;
}
// If the wrapper is not extractable
if (wrapper is not IExtractable extractable)
{
Console.WriteLine($"{ft} is not supported for extraction!");
Console.WriteLine();
return;
}
// Print the preamble
Console.WriteLine($"Attempting to extract from '{wrapper.Description()}'");
Console.WriteLine();
// Attempt the extraction
Directory.CreateDirectory(OutputPath);
extractable.Extract(OutputPath, Debug);
}
catch (Exception ex)
{
if (Debug) Console.Error.WriteLine(ex);
return;
}
// Get the file type
WrapperType ft = WrapperFactory.GetFileType(magic, extension);
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
// Create the output directory
Directory.CreateDirectory(OutputPath);
// Print the preamble
Console.WriteLine($"Attempting to extract from '{wrapper?.Description() ?? "UNKNOWN"}'");
Console.WriteLine();
switch (wrapper)
{
// 7-zip
case SevenZip sz:
sz.Extract(OutputPath, Debug);
break;
// BFPK archive
case BFPK bfpk:
bfpk.Extract(OutputPath, Debug);
break;
// BSP
case BSP bsp:
bsp.Extract(OutputPath, Debug);
break;
// bzip2
case BZip2 bzip2:
bzip2.Extract(OutputPath, Debug);
break;
// CFB
case CFB cfb:
cfb.Extract(OutputPath, Debug);
break;
// GCF
case GCF gcf:
gcf.Extract(OutputPath, Debug);
break;
// gzip
case GZip gzip:
gzip.Extract(OutputPath, Debug);
break;
// InstallShield Archive V3 (Z)
case InstallShieldArchiveV3 isv3:
isv3.Extract(OutputPath, Debug);
break;
// IS-CAB archive
case InstallShieldCabinet iscab:
iscab.Extract(OutputPath, Debug);
break;
// LZ-compressed file, KWAJ variant
case LZKWAJ kwaj:
kwaj.Extract(OutputPath, Debug);
break;
// LZ-compressed file, QBasic variant
case LZQBasic qbasic:
qbasic.Extract(OutputPath, Debug);
break;
// LZ-compressed file, SZDD variant
case LZSZDD szdd:
szdd.Extract(OutputPath, Debug);
break;
// Microsoft Cabinet archive
case MicrosoftCabinet mscab:
mscab.Extract(OutputPath, Debug);
break;
// MoPaQ (MPQ) archive
case MoPaQ mpq:
mpq.Extract(OutputPath, Debug);
break;
// New Executable
case NewExecutable nex:
nex.Extract(OutputPath, Debug);
break;
// PAK
case PAK pak:
pak.Extract(OutputPath, Debug);
break;
// PFF
case PFF pff:
pff.Extract(OutputPath, Debug);
break;
// PKZIP
case PKZIP pkzip:
pkzip.Extract(OutputPath, Debug);
break;
// Portable Executable
case PortableExecutable pex:
pex.Extract(OutputPath, Debug);
break;
// Quantum
case Quantum quantum:
quantum.Extract(OutputPath, Debug);
break;
// RAR
case RAR rar:
rar.Extract(OutputPath, Debug);
break;
// SGA
case SGA sga:
sga.Extract(OutputPath, Debug);
break;
// Tape Archive
case TapeArchive tar:
tar.Extract(OutputPath, Debug);
break;
// VBSP
case VBSP vbsp:
vbsp.Extract(OutputPath, Debug);
break;
// VPK
case VPK vpk:
vpk.Extract(OutputPath, Debug);
break;
// WAD3
case WAD3 wad:
wad.Extract(OutputPath, Debug);
break;
// xz
case XZ xz:
xz.Extract(OutputPath, Debug);
break;
// XZP
case XZP xzp:
xzp.Extract(OutputPath, Debug);
break;
// Everything else
default:
Console.WriteLine("Not a supported extractable file format, skipping...");
Console.WriteLine();
break;
Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]");
Console.WriteLine();
}
}

View File

@@ -20,7 +20,7 @@ namespace ExtractionTool
var commandSet = CreateCommands(mainFeature);
// If we have no args, show the help and quit
if (args == null || args.Length == 0)
if (args is null || args.Length == 0)
{
commandSet.OutputAllHelp();
return;

View File

@@ -5,8 +5,8 @@ using SabreTools.CommandLine;
using SabreTools.CommandLine.Inputs;
using SabreTools.Hashing;
using SabreTools.IO.Extensions;
using SabreTools.Serialization;
using SabreTools.Serialization.Wrappers;
using SabreTools.Numerics.Extensions;
using SabreTools.Wrappers;
namespace InfoPrint.Features
{
@@ -35,10 +35,11 @@ namespace InfoPrint.Features
private const string _hashName = "hash";
internal readonly FlagInput HashInput = new(_hashName, ["-c", "--hash"], "Output file hashes");
#if NETCOREAPP
private const string _jsonName = "json";
internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Print info as JSON");
#endif
private const string _recursiveName = "recursive";
internal readonly FlagInput RecursiveInput = new(_recursiveName, ["-r", "--recursive"], "Recursively print info from embedded files");
#endregion
@@ -57,12 +58,15 @@ namespace InfoPrint.Features
/// </summary>
public bool Hash { get; private set; }
#if NETCOREAPP
/// <summary>
/// Enable JSON output
/// </summary>
public bool Json { get; private set; }
#endif
/// <summary>
/// Enable outputting subfile information
/// </summary>
public bool Recursive { get; private set; }
public MainFeature()
: base(DisplayName, _flags, _description)
@@ -72,9 +76,8 @@ namespace InfoPrint.Features
Add(DebugInput);
Add(HashInput);
Add(FileOnlyInput);
#if NETCOREAPP
Add(JsonInput);
#endif
Add(RecursiveInput);
}
/// <inheritdoc/>
@@ -84,9 +87,8 @@ namespace InfoPrint.Features
Debug = GetBoolean(_debugName);
Hash = GetBoolean(_hashName);
FileOnly = GetBoolean(_fileOnlyName);
#if NETCOREAPP
Json = GetBoolean(_jsonName);
#endif
Recursive = GetBoolean(_recursiveName);
// Loop through the input paths
for (int i = 0; i < Inputs.Count; i++)
@@ -142,7 +144,7 @@ namespace InfoPrint.Features
if (Hash)
{
var hashBuilder = PrintHashInfo(file);
if (hashBuilder != null)
if (hashBuilder is not null)
{
// Create the output data
string hashData = hashBuilder.ToString();
@@ -158,9 +160,8 @@ namespace InfoPrint.Features
{
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// Read the first 8 bytes
byte[]? magic = stream.ReadBytes(8);
stream.Seek(0, SeekOrigin.Begin);
// Read the first 32 bytes
byte[] magic = stream.PeekBytes(32);
// Get the file type
string extension = Path.GetExtension(file).TrimStart('.');
@@ -173,37 +174,39 @@ namespace InfoPrint.Features
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
// If we don't have a wrapper
if (wrapper == null)
if (wrapper is null)
{
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
Console.WriteLine();
return;
}
#if NETCOREAPP
// If the wrapper is not printable
if (wrapper is not IPrintable printable)
{
Console.WriteLine($"{ft} is not supported for printing!");
Console.WriteLine();
return;
}
// If we have the JSON flag
if (Json)
{
// Create the output data
string serializedData = wrapper.ExportJSON();
string serializedData = printable.ExportJSON(Recursive);
// Write the output data
using var jsw = new StreamWriter(File.OpenWrite($"{filenameBase}.json"));
jsw.WriteLine(serializedData);
jsw.Flush();
}
#endif
// Create the output data
var builder = wrapper.ExportStringBuilder();
if (builder == null)
{
Console.WriteLine("No item information could be generated");
return;
}
var builder = new StringBuilder();
printable.PrintInformation(builder, Recursive);
// Only print to console if enabled
if (FileOnly)
if (!FileOnly)
Console.WriteLine(builder);
using var sw = new StreamWriter(File.OpenWrite($"{filenameBase}.txt"));
@@ -236,7 +239,7 @@ namespace InfoPrint.Features
{
// Get all file hashes for flexibility
var hashes = HashTool.GetFileHashes(file);
if (hashes == null)
if (hashes is null)
{
if (Debug) Console.WriteLine($"Hashes for {file} could not be retrieved");
return null;
@@ -244,27 +247,29 @@ namespace InfoPrint.Features
// Output subset of available hashes
var builder = new StringBuilder();
if (hashes.TryGetValue(HashType.CRC16, out string? crc16) && crc16 != null)
if (hashes.TryGetValue(HashType.CRC16, out string? crc16) && crc16 is not null)
builder.AppendLine($"CRC-16 checksum: {crc16}");
if (hashes.TryGetValue(HashType.CRC32, out string? crc32) && crc32 != null)
if (hashes.TryGetValue(HashType.CRC32, out string? crc32) && crc32 is not null)
builder.AppendLine($"CRC-32 checksum: {crc32}");
if (hashes.TryGetValue(HashType.MD2, out string? md2) && md2 != null)
if (hashes.TryGetValue(HashType.CRC64, out string? crc64) && crc64 is not null)
builder.AppendLine($"CRC-64 checksum: {crc64}");
if (hashes.TryGetValue(HashType.MD2, out string? md2) && md2 is not null)
builder.AppendLine($"MD2 hash: {md2}");
if (hashes.TryGetValue(HashType.MD4, out string? md4) && md4 != null)
if (hashes.TryGetValue(HashType.MD4, out string? md4) && md4 is not null)
builder.AppendLine($"MD4 hash: {md4}");
if (hashes.TryGetValue(HashType.MD5, out string? md5) && md5 != null)
if (hashes.TryGetValue(HashType.MD5, out string? md5) && md5 is not null)
builder.AppendLine($"MD5 hash: {md5}");
if (hashes.TryGetValue(HashType.RIPEMD128, out string? ripemd128) && ripemd128 != null)
if (hashes.TryGetValue(HashType.RIPEMD128, out string? ripemd128) && ripemd128 is not null)
builder.AppendLine($"RIPEMD-128 hash: {ripemd128}");
if (hashes.TryGetValue(HashType.RIPEMD160, out string? ripemd160) && ripemd160 != null)
if (hashes.TryGetValue(HashType.RIPEMD160, out string? ripemd160) && ripemd160 is not null)
builder.AppendLine($"RIPEMD-160 hash: {ripemd160}");
if (hashes.TryGetValue(HashType.SHA1, out string? sha1) && sha1 != null)
if (hashes.TryGetValue(HashType.SHA1, out string? sha1) && sha1 is not null)
builder.AppendLine($"SHA-1 hash: {sha1}");
if (hashes.TryGetValue(HashType.SHA256, out string? sha256) && sha256 != null)
if (hashes.TryGetValue(HashType.SHA256, out string? sha256) && sha256 is not null)
builder.AppendLine($"SHA-256 hash: {sha256}");
if (hashes.TryGetValue(HashType.SHA384, out string? sha384) && sha384 != null)
if (hashes.TryGetValue(HashType.SHA384, out string? sha384) && sha384 is not null)
builder.AppendLine($"SHA-384 hash: {sha384}");
if (hashes.TryGetValue(HashType.SHA512, out string? sha512) && sha512 != null)
if (hashes.TryGetValue(HashType.SHA512, out string? sha512) && sha512 is not null)
builder.AppendLine($"SHA-512 hash: {sha512}");
return builder;

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>2.0.2</Version>
<Version>3.2.0</Version>
</PropertyGroup>
<!-- Support All Frameworks -->
@@ -20,21 +20,21 @@
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.Serialization\SabreTools.Serialization.csproj" />
<ProjectReference Include="..\SabreTools.Wrappers\SabreTools.Wrappers.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.CommandLine" Version="[1.3.2]" />
<PackageReference Include="SabreTools.Hashing" Version="[1.5.1]" />
<PackageReference Include="SabreTools.IO" Version="[1.7.6]" />
<PackageReference Include="SabreTools.CommandLine" Version="1.5.0" />
<PackageReference Include="SabreTools.Hashing" Version="2.1.0" />
<PackageReference Include="SabreTools.IO" Version="2.3.0" />
</ItemGroup>
</Project>

View File

@@ -15,7 +15,7 @@ namespace InfoPrint
var commandSet = CreateCommands(mainFeature);
// If we have no args, show the help and quit
if (args == null || args.Length == 0)
if (args is null || args.Length == 0)
{
commandSet.OutputAllHelp();
return;

View File

@@ -1,4 +1,4 @@
Copyright (c) 2018-2025 Matt Nadareski
Copyright (c) 2018-2026 Matt Nadareski
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -2,20 +2,10 @@
[![Build and Test](https://github.com/SabreTools/SabreTools.Serialization/actions/workflows/build_and_test.yml/badge.svg)](https://github.com/SabreTools/SabreTools.Serialization/actions/workflows/build_and_test.yml)
This library comprises of serializers that both read and write from files and streams to the dedicated models as well as convert to and from the common internal models. This library is partially used by the current parsing and writing code but none of the internal model serialization is used.
This meta-library comprises of serializers that both read and write from files and streams to the dedicated models as well as convert to and from the common internal models. This library is partially used by the current parsing and writing code but none of the internal model serialization is used.
Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTools.Serialization).
The following non-project libraries (or ports thereof) are used for file handling:
- [GrindCore.SharpCompress](https://github.com/Nanook/GrindCore.SharpCompress) - Common archive format extraction
- [StormLibSharp](https://github.com/robpaveza/stormlibsharp) - MoPaQ extraction [Unused in .NET Framework 2.0/3.5/4.0 and non-Windows builds due to Windows-specific libraries]
The following projects have influenced this library:
- [libmspack](https://github.com/kyz/libmspack) - Documentation around the MS-CAB format and associated compression methods.
- [Unshield](https://github.com/twogood/unshield/) - InstallShield CAB extraction tool that influenced internal handling
## Releases
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools.Serialization/releases)
@@ -24,7 +14,7 @@ For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/S
## InfoPrint
**InfoPrint** is a reference implementation for the deserialization and printing features of the library, packaged as a standalone executable for all supported platforms. It will attempt to detect and display information about many supported file types, optionally both hashing the file and outputting the information to a JSON file (.NET Core 3.1 and above only).
**InfoPrint** is a reference implementation for the deserialization and printing features of the library, packaged as a standalone executable for all supported platforms. It will attempt to detect and display information about many supported file types, optionally both hashing the file and outputting the information to a JSON file.
```text
InfoPrint <options> file|directory ...
@@ -35,6 +25,7 @@ Options:
-c, --hash Output file hashes
-f, --file Print to file only
-j, --json Print info as JSON
-r, --recursive Recursively print info from embedded files
```
## ExtractionTool
@@ -53,9 +44,12 @@ Options:
| Format Name | Notes |
| --- | --- |
| 7-zip archive | .NET Framework 4.6.2 and greater |
| Atari 7800 (A78) Cart Image | Header and ROM data |
| Atari Lynx (LNX) Cart Image | Header and ROM data |
| BFPK custom archive format | |
| bzip2 archive | .NET Framework 4.6.2 and greater |
| Compound File Binary (CFB) | Only CFB common pieces extractable. .NET Framework 4.0 and greater |
| Compound File Binary (CFB) | Only CFB common pieces extractable |
| fwNES FDS file | Header and disk data |
| gzip archive | |
| Half-Life Game Cache File (GCF) | |
| Half-Life Level (BSP) | |
@@ -64,10 +58,12 @@ Options:
| Half-Life 2 Level (VBSP) | |
| InstallShield Archive V3 (Z) | |
| InstallShield CAB | |
| ISO9660 | ISO and BIN files |
| Microsoft cabinet file | Does not support LZX or Quantum compression |
| Microsoft LZ-compressed files | KWAJ, QBasic, and SZDD variants |
| MoPaQ game data archive (MPQ) | Windows only. .NET Framework 4.5.2 and above |
| MoPaQ game data archive (MPQ) | Windows only |
| New Exectuable | Embedded archives and executables in the overlay and Wise installer |
| Nintendo Entertainment System (NES) Cart Image | Header, Trainer data, PRG-ROM, CHR-ROM, Unheadered ROM, and PlayChoice-10 INST-ROM |
| NovaLogic Game Archive Format (PFF) | |
| PKZIP and derived files (ZIP, etc.) | .NET Framework 4.6.2 and greater |
| Portable Executable | Embedded archives and executables in the resources and overlay, CExe-packed data, SFX archives (7-zip, PKZIP, and RAR), and Wise installer |
@@ -77,46 +73,22 @@ Options:
| Tape archive (TAR) | |
| Valve Package File (VPK) | |
| XBox Package File (XZP) | |
| Xbox DVD Filesystem (XISO) | |
| xz archive (XZ) | .NET Framework 4.6.2 and greater |
## Interfaces
Below is a table representing the various conversion interfaces that are implemented within this library.
| Interface Name | Source Type | Destination Type |
| --- | --- | --- |
| `SabreTools.Serialization.Interfaces.IByteReader<TModel>` | `byte[]?` | `TModel` |
| `SabreTools.Serialization.Interfaces.IByteWriter<TModel>` | `TModel` | `byte[]?` |
| `SabreTools.Serialization.Interfaces.ICrossModel<TSource, TDest>` | `TSource`/`TDest` | `TDest`/`TSource` |
| `SabreTools.Serialization.Interfaces.IFileReader<TModel>` | `string?` path | `TModel` |
| `SabreTools.Serialization.Interfaces.IFileWriter<TModel>` | `TModel` | `string?` path |
| `SabreTools.Serialization.Interfaces.IStreamReader<TModel>` | `Stream?` | `TModel` |
| `SabreTools.Serialization.Interfaces.IStreamWriter<TModel>` | `TModel` | `Stream?` |
| `SabreTools.Serialization.Interfaces.IStringReader<TModel>` | `string?` representation | `TModel` |
| `SabreTools.Serialization.Interfaces.IStringWriter<TModel>` | `TModel` | `string?` representation |
Below is a table representing the various non-conversion interfaces that are implemented within this library.
| Interface Name | Purpose |
| --- | --- |
| `SabreTools.Data.Printers.IPrinter<TModel>` | Provides a formatted output for a `TModel` |
| `SabreTools.Serialization.Wrappers.IExtractable` | Marks a wrapper as able to be extracted |
| `SabreTools.Serialization.Wrappers.IWrapper` | Represents an item with a description and JSON serializable state, allowing for extensions |
| `SabreTools.Serialization.Wrappers.IWrapper<TModel>` | Wraps a model with source data, allowing for extensions |
| ZArchive | |
## Namespaces
Below is a table of all namespaces within the library and what they represent
Below are a list of the included namespaces with links to their README files:
| Namespace | Description |
| --- | --- |
| `SabreTools.Data.Attributes` | Common attributes for data marking and manipulation |
| `SabreTools.Data.Extensions` | Extension methods related to models |
| `SabreTools.Data.Models` | Models representing different file and structure types |
| `SabreTools.Data.ObjectIdentifier` | Object Identifier (OID) parsing |
| `SabreTools.Data.Printers` | Export model information in a formatted manner |
| `SabreTools.Serialization.CrossModel` | Convert between models; mainly used for metadata files converting to and from a common, `Dictionary`-based model |
| `SabreTools.Serialization.Interfaces` | Interfaces used commonly throughout the library |
| `SabreTools.Serialization.Readers` | Convert from external sources to models |
| `SabreTools.Serialization.Wrappers` | Classes that wrap serialization and models to allow for including extension properties |
| `SabreTools.Serialization.Writers` | Convert from models to external sources |
- [SabreTools.Data.Extensions](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Data.Extensions)
- [SabreTools.Data.Models](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Data.Models)
- [SabreTools.Metadata.DatFiles](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.DatFiles)
- [SabreTools.Metadata.DatItems](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Datitems)
- [SabreTools.Metadata.Filter](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Filter)
- [SabreTools.Metadata.Reports](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Reports)
- [SabreTools.ObjectIdentifier](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.ObjectIdentifier)
- [SabreTools.Serialization.CrossModel](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.CrossModel)
- [SabreTools.Serialization.Readers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.Readers)
- [SabreTools.Serialization.Writers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.Writers)
- [SabreTools.Wrappers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Wrappers)

View File

@@ -0,0 +1,141 @@
using SabreTools.Data.Models.Atari7800;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class Atari7800CartExtensionsTests
{
[Theory]
[InlineData((AudioDevice)0, "POKEY - none, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
// POKEY
[InlineData(AudioDevice.Pokey440, "POKEY - @440, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
[InlineData(AudioDevice.Pokey450, "POKEY - @450, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
[InlineData(AudioDevice.Pokey450Plus440, "POKEY - @450+@440, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
[InlineData(AudioDevice.Pokey800, "POKEY - @800, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
[InlineData(AudioDevice.Pokey4000, "POKEY - @4000, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
[InlineData((AudioDevice)0x0007, "Unknown 7, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
// YM2151
[InlineData(AudioDevice.YM2151460, "POKEY - none, YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
// COVOX
[InlineData(AudioDevice.COVOX430, "POKEY - none, No YM2151 @460, COVOX @430, No ADPCM Audio Stream @420")]
// ADPCM
[InlineData(AudioDevice.ADPCMAudioStream420, "POKEY - none, No YM2151 @460, No COVOX @430, ADPCM Audio Stream @420")]
public void FromAudioDeviceTest(AudioDevice audio, string expected)
{
string actual = audio.FromAudioDevice();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData((CartType)0, "N/A")]
[InlineData(CartType.PokeyAt4000, "pokey at $4000")]
[InlineData(CartType.SupergameBankSwitched, "supergame bank switched")]
[InlineData(CartType.SupergameRamAt4000, "supergame ram at $4000")]
[InlineData(CartType.RomAt4000, "rom at $4000")]
[InlineData(CartType.Bank6At4000, "bank 6 at $4000")]
[InlineData(CartType.BankedRam, "banked ram")]
[InlineData(CartType.PokeyAt450, "pokey at $450")]
[InlineData(CartType.MirrorRamAt4000, "mirror ram at $4000")]
[InlineData(CartType.ActivisionBanking, "activision banking")]
[InlineData(CartType.AbsoluteBanking, "absolute banking")]
[InlineData(CartType.PokeyAt440, "pokey at $440")]
[InlineData(CartType.Ym2151At460461, "ym2151 at $460/$461")]
[InlineData(CartType.Souper, "souper")]
[InlineData(CartType.Banksets, "banksets")]
[InlineData(CartType.HaltBankedRam, "halt banked ram")]
[InlineData(CartType.PokeyAt800, "pokey@800")]
public void FromCartTypeTest(CartType type, string expected)
{
string actual = type.FromCartType();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(ControllerType.None, "none")]
[InlineData(ControllerType.Joystick, "7800 joystick")]
[InlineData(ControllerType.Lightgun, "lightgun")]
[InlineData(ControllerType.Paddle, "paddle")]
[InlineData(ControllerType.Trakball, "trakball")]
[InlineData(ControllerType.VcsJoystick, "2600 joystick")]
[InlineData(ControllerType.VcsDriving, "2600 driving")]
[InlineData(ControllerType.VcsKeypad, "2600 keypad")]
[InlineData(ControllerType.STMouse, "ST mouse")]
[InlineData(ControllerType.AmigaMouse, "Amiga mouse")]
[InlineData(ControllerType.AtariVoxSaveKey, "AtariVox/SaveKey")]
[InlineData(ControllerType.SNES2Atari, "SNES2Atari")]
[InlineData(ControllerType.Mega7800, "Mega7800")]
public void FromControllerTypeTest(ControllerType type, string expected)
{
string actual = type.FromControllerType();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(Interrupt.None, "None")]
[InlineData(Interrupt.Pokey1, "POKEY 1 (@450 | @800 | @4000)")]
[InlineData(Interrupt.Pokey2, "POKEY 2 (@440)")]
[InlineData(Interrupt.YM2151, "YM2151")]
public void FromInterruptTest(Interrupt interrupt, string expected)
{
string actual = interrupt.FromInterrupt();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(Mapper.Linear, "Linear")]
[InlineData(Mapper.SuperGame, "SuperGame")]
[InlineData(Mapper.Activision, "Activision")]
[InlineData(Mapper.Absolute, "Absolute")]
[InlineData(Mapper.Souper, "Souper")]
public void FromMapperTest(Mapper mapper, string expected)
{
string actual = mapper.FromMapper();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(MapperOptions.SuperGameNone, "Option at @4000 - none, Standard ROM")]
[InlineData(MapperOptions.SuperGame16KRAM, "Option at @4000 - 16K RAM, Standard ROM")]
[InlineData(MapperOptions.SuperGame8KEXRAMA8, "Option at @4000 - 8K EXRAM/A8, Standard ROM")]
[InlineData(MapperOptions.SuperGame32KEXRAMM2, "Option at @4000 - 32K EXRAM/M2, Standard ROM")]
[InlineData(MapperOptions.SuperGameEXROM, "Option at @4000 - EXROM, Standard ROM")]
[InlineData(MapperOptions.SuperGameEXFIX, "Option at @4000 - EXFIX, Standard ROM")]
[InlineData(MapperOptions.SuperGame32KEXRAMX2, "Option at @4000 - 32k EXRAM/X2, Standard ROM")]
[InlineData(MapperOptions.BanksetRom, "Option at @4000 - none, Bankset ROM")]
public void FromMapperOptionsTest(MapperOptions options, string expected)
{
string actual = options.FromMapperOptions();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(SaveDevice.None, "None")]
[InlineData(SaveDevice.HSC, "HSC")]
[InlineData(SaveDevice.SaveKeyAtariVox, "SaveKey/AtariVox")]
public void FromSaveDeviceTest(SaveDevice device, string expected)
{
string actual = device.FromSaveDevice();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(SlotPassthroughDevice.None, "None")]
[InlineData(SlotPassthroughDevice.XM, "XM")]
public void FromSlotPassthroughDeviceTest(SlotPassthroughDevice device, string expected)
{
string actual = device.FromSlotPassthroughDevice();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData((TVType)0, "NTSC, Component, Single-region")]
[InlineData(TVType.PAL, "PAL, Component, Single-region")]
[InlineData(TVType.Composite, "NTSC, Composite, Single-region")]
[InlineData(TVType.MultiRegion, "NTSC, Component, Multi-region")]
public void FromTVTypeTest(TVType type, string expected)
{
string actual = type.FromTVType();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,18 @@
using SabreTools.Data.Models.AtariLynx;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class AtariLynxCartExtensionsTests
{
[Theory]
[InlineData(Rotation.NoRotation, "No rotation (horizontal, buttons right)")]
[InlineData(Rotation.RotateLeft, "Rotate left (vertical, buttons down)")]
[InlineData(Rotation.RotateRight, "Rotate right (vertical, buttons up)")]
public void FromRotationTest(Rotation rotation, string expected)
{
string actual = rotation.FromRotation();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,65 @@
using System.IO;
using SabreTools.Data.Models.CDROM;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class CDROMExtensionsTests
{
[Theory]
[InlineData(new byte[0], SectorMode.UNKNOWN)]
[InlineData(new byte[] { 0x00 }, SectorMode.MODE0)]
[InlineData(new byte[] { 0x01 }, SectorMode.MODE1)]
[InlineData(new byte[] { 0x02 }, SectorMode.UNKNOWN)]
[InlineData(new byte[] { 0x02, 0x00, 0x00, 0x00 }, SectorMode.MODE2_FORM1)]
[InlineData(new byte[] { 0x02, 0x00, 0x00, 0x20 }, SectorMode.MODE2_FORM2)]
[InlineData(new byte[] { 0x03 }, SectorMode.UNKNOWN)]
public void GetSectorModeTest(byte[] bytes, SectorMode expected)
{
var stream = new MemoryStream(bytes);
SectorMode actual = stream.GetSectorMode();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(SectorMode.UNKNOWN, 2336)]
[InlineData(SectorMode.MODE0, 2336)]
[InlineData(SectorMode.MODE1, 2048)]
[InlineData(SectorMode.MODE2, 2336)]
[InlineData(SectorMode.MODE2_FORM1, 2048)]
[InlineData(SectorMode.MODE2_FORM2, 2324)]
public void GetUserDataSizeTest(SectorMode mode, long expected)
{
long actual = mode.GetUserDataSize();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(SectorMode.UNKNOWN, 2064)]
[InlineData(SectorMode.MODE0, 2064)]
[InlineData(SectorMode.MODE1, 2064)]
[InlineData(SectorMode.MODE2, 2064)]
[InlineData(SectorMode.MODE2_FORM1, 2072)]
[InlineData(SectorMode.MODE2_FORM2, 2072)]
public void GetUserDataEndTest(SectorMode mode, long expected)
{
long actual = mode.GetUserDataEnd();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(SectorMode.UNKNOWN, 16)]
[InlineData(SectorMode.MODE0, 16)]
[InlineData(SectorMode.MODE1, 16)]
[InlineData(SectorMode.MODE2, 16)]
[InlineData(SectorMode.MODE2_FORM1, 24)]
[InlineData(SectorMode.MODE2_FORM2, 24)]
public void GetUserDataStart(SectorMode mode, long expected)
{
long actual = mode.GetUserDataStart();
Assert.Equal(expected, actual);
}
// TODO: Figure out how to add UserDataStream tests
}
}

View File

@@ -0,0 +1,17 @@
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class CFBExtensionsTests
{
[Theory]
[InlineData(null, null)]
[InlineData(new byte[0], "")]
// TODO: Create more artifical tests
public void DecodeStreamNameTest(byte[]? bytes, string? expected)
{
string? actual = bytes.DecodeStreamName();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,138 @@
using SabreTools.Data.Models.ISO9660;
using SabreTools.Numerics;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class ISO9660ExtensionsTests
{
[Fact]
public void GetLogicalBlockSize_Generic_SectorLength()
{
VolumeDescriptor vd = new GenericVolumeDescriptor();
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(sectorLength, actual);
}
[Fact]
public void GetLogicalBlockSize_PVD_ValidBothEndian_ValidBlockSize_BlockSize()
{
VolumeDescriptor vd = new PrimaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(2048, 2048)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(2048, actual);
}
[Fact]
public void GetLogicalBlockSize_PVD_ValidBothEndian_InvalidBlockSize_SectorLength()
{
VolumeDescriptor vd = new PrimaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(2352, 2352)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(4096, actual);
}
[Fact]
public void GetLogicalBlockSize_PVD_InvalidBothEndian_ValidLE_LEValue()
{
VolumeDescriptor vd = new PrimaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(2048, -1)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(2048, actual);
}
[Fact]
public void GetLogicalBlockSize_PVD_InvalidBothEndian_ValidBE_BEValue()
{
VolumeDescriptor vd = new PrimaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(-1, 2048)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(2048, actual);
}
[Fact]
public void GetLogicalBlockSize_PVD_InvalidBothEndian_BothInvalid_SectorLength()
{
VolumeDescriptor vd = new PrimaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(-1, -2)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(sectorLength, actual);
}
[Fact]
public void GetLogicalBlockSize_SVD_ValidBothEndian_ValidBlockSize_BlockSize()
{
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(2048, 2048)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(2048, actual);
}
[Fact]
public void GetLogicalBlockSize_SVD_ValidBothEndian_InvalidBlockSize_SectorLength()
{
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(2352, 2352)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(4096, actual);
}
[Fact]
public void GetLogicalBlockSize_SVD_InvalidBothEndian_ValidLE_LEValue()
{
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(2048, -1)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(2048, actual);
}
[Fact]
public void GetLogicalBlockSize_SVD_InvalidBothEndian_ValidBE_BEValue()
{
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(-1, 2048)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(2048, actual);
}
[Fact]
public void GetLogicalBlockSize_SVD_InvalidBothEndian_BothInvalid_SectorLength()
{
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
{
LogicalBlockSize = new BothInt16(-1, -2)
};
short sectorLength = 4096;
short actual = vd.GetLogicalBlockSize(sectorLength);
Assert.Equal(sectorLength, actual);
}
}
}

View File

@@ -0,0 +1,156 @@
using SabreTools.Data.Models.InstallShieldCabinet;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class InstallShieldCabinetExtensionsTests
{
#region IsCompressed
[Fact]
public void IsCompressed_Null_True()
{
FileDescriptor? fd = null;
bool actual = fd.IsCompressed();
Assert.True(actual);
}
[Fact]
public void IsCompressed_Compressed_True()
{
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_COMPRESSED };
bool actual = fd.IsCompressed();
Assert.True(actual);
}
[Fact]
public void IsCompressed_NotCompressed_False()
{
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
bool actual = fd.IsCompressed();
Assert.False(actual);
}
#endregion
#region IsInvalid
[Fact]
public void IsInvalid_Null_True()
{
FileDescriptor? fd = null;
bool actual = fd.IsInvalid();
Assert.True(actual);
}
[Fact]
public void IsInvalid_Invalid_True()
{
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_INVALID };
bool actual = fd.IsInvalid();
Assert.True(actual);
}
[Fact]
public void IsInvalid_Valid_False()
{
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
bool actual = fd.IsInvalid();
Assert.False(actual);
}
#endregion
#region IsObfuscated
[Fact]
public void IsObfuscated_Null_False()
{
FileDescriptor? fd = null;
bool actual = fd.IsObfuscated();
Assert.False(actual);
}
[Fact]
public void IsObfuscated_Obfuscated_True()
{
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_OBFUSCATED };
bool actual = fd.IsObfuscated();
Assert.True(actual);
}
[Fact]
public void IsObfuscated_NotObfuscated_False()
{
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
bool actual = fd.IsObfuscated();
Assert.False(actual);
}
#endregion
#region IsSplit
[Fact]
public void IsSplit_Null_False()
{
FileDescriptor? fd = null;
bool actual = fd.IsSplit();
Assert.False(actual);
}
[Fact]
public void IsSplit_Split_True()
{
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_SPLIT };
bool actual = fd.IsSplit();
Assert.True(actual);
}
[Fact]
public void IsSplit_NotSplitFalse()
{
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
bool actual = fd.IsSplit();
Assert.False(actual);
}
#endregion
#region GetMajorVersion
[Fact]
public void GetMajorVersion_NullCabinet_NegativeOne()
{
Cabinet? cabinet = null;
int actual = cabinet.GetMajorVersion();
Assert.Equal(-1, actual);
}
[Fact]
public void GetMajorVersion_NullHeader_NegativeOne()
{
CommonHeader? cabinet = null;
int actual = cabinet.GetMajorVersion();
Assert.Equal(-1, actual);
}
[Theory]
[InlineData(0x00000000, 0)]
[InlineData(0x00000004, 4)]
[InlineData(0x01000000, 0)]
[InlineData(0x01004000, 4)]
[InlineData(0x02000000, 0)]
[InlineData(0x02000190, 4)]
[InlineData(0x04000000, 0)]
[InlineData(0x04000190, 4)]
public void GetMajorVersionTest(uint version, int expected)
{
CommonHeader? cabinet = new CommonHeader { Version = version };
int actual = cabinet.GetMajorVersion();
Assert.Equal(expected, actual);
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,206 @@
using SabreTools.Data.Models.NES;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class NESCartExtensionsTests
{
[Theory]
[InlineData(ConsoleType.StandardSystem, "Nintendo Entertainment System/Family Computer")]
[InlineData(ConsoleType.VSUnisystem, "VS Unisystem")]
[InlineData(ConsoleType.PlayChoice10, "PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)")]
[InlineData(ConsoleType.ExtendedConsoleType, "Extended Console Type")]
public void FromConsoleTypeTest(ConsoleType type, string expected)
{
string actual = type.FromConsoleType();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(CPUPPUTiming.RP2C02, "RP2C02 (NTSC NES)")]
[InlineData(CPUPPUTiming.RP2C07, "RP2C07 (Licensed PAL NES)")]
[InlineData(CPUPPUTiming.MultipleRegion, "Multiple-region")]
[InlineData(CPUPPUTiming.UA6538, "UA6538 (Dendy)")]
public void FromCPUPPUTimingTest(CPUPPUTiming timing, string expected)
{
string actual = timing.FromCPUPPUTiming();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(DefaultExpansionDevice.Unspecified, "Unspecified")]
[InlineData(DefaultExpansionDevice.StandardControllers, "Standard NES/Famicom controllers")]
[InlineData(DefaultExpansionDevice.NESFourScore, "NES Four Score/Satellite with two additional standard controllers")]
[InlineData(DefaultExpansionDevice.FamicomFourPlayersAdapter, "Famicom Four Players Adapter with two additional standard controllers using the 'simple' protocol")]
[InlineData(DefaultExpansionDevice.VsSystem4016, "Vs. System (1P via $4016)")]
[InlineData(DefaultExpansionDevice.VsSystem4017, "Vs. System (1P via $4017)")]
[InlineData(DefaultExpansionDevice.Reserved06, "Reserved (0x06)")]
[InlineData(DefaultExpansionDevice.VsZapper, "Vs. Zapper")]
[InlineData(DefaultExpansionDevice.Zapper4017, "Zapper ($4017)")]
[InlineData(DefaultExpansionDevice.TwoZappers, "Two Zappers")]
[InlineData(DefaultExpansionDevice.BandaiHyperShotLightgun, "Bandai Hyper Shot Lightgun")]
[InlineData(DefaultExpansionDevice.PowerPadSideA, "Power Pad Side A")]
[InlineData(DefaultExpansionDevice.PowerPadSideB, "Power Pad Side B")]
[InlineData(DefaultExpansionDevice.FamilyTrainerSideA, "Family Trainer Side A")]
[InlineData(DefaultExpansionDevice.FamilyTrainerSideB, "Family Trainer Side B")]
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerNES, "Arkanoid Vaus Controller (NES)")]
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerFamicom, "Arkanoid Vaus Controller (Famicom)")]
[InlineData(DefaultExpansionDevice.TwoVausControllersPlusFamicomDataRecorder, "Two Vaus Controllers plus Famicom Data Recorder")]
[InlineData(DefaultExpansionDevice.KonamiHyperShotController, "Konami Hyper Shot Controller")]
[InlineData(DefaultExpansionDevice.CoconutsPachinkoController, "Coconuts Pachinko Controller")]
[InlineData(DefaultExpansionDevice.ExcitingBoxingPunchingBag, "Exciting Boxing Punching Bag (Blowup Doll)")]
[InlineData(DefaultExpansionDevice.JissenMahjongController, "Jissen Mahjong Controller")]
[InlineData(DefaultExpansionDevice.YonezawaPartyTap, "米澤 (Yonezawa) Party Tap")]
[InlineData(DefaultExpansionDevice.OekaKidsTablet, "Oeka Kids Tablet")]
[InlineData(DefaultExpansionDevice.SunsoftBarcodeBattler, "Sunsoft Barcode Battler")]
[InlineData(DefaultExpansionDevice.MiraclePianoKeyboard, "Miracle Piano Keyboard")]
[InlineData(DefaultExpansionDevice.PokkunMoguraaTapTapMat, "Pokkun Moguraa Tap-tap Mat (Whack-a-Mole Mat and Mallet)")]
[InlineData(DefaultExpansionDevice.TopRider, "Top Rider (Inflatable Bicycle)")]
[InlineData(DefaultExpansionDevice.DoubleFisted, "Double-Fisted (Requires or allows use of two controllers by one player)")]
[InlineData(DefaultExpansionDevice.Famicom3DSystem, "Famicom 3D System")]
[InlineData(DefaultExpansionDevice.DoremikkoKeyboard, "Doremikko Keyboard")]
[InlineData(DefaultExpansionDevice.ROBGyromite, "R.O.B. Gyromite")]
[InlineData(DefaultExpansionDevice.FamicomDataRecorder, "Famicom Data Recorder ('silent' keyboard)")]
[InlineData(DefaultExpansionDevice.ASCIITurboFile, "ASCII Turbo File")]
[InlineData(DefaultExpansionDevice.IGSStorageBattleBox, "IGS Storage Battle Box")]
[InlineData(DefaultExpansionDevice.FamilyBASICKeyboardPlusFamicomDataRecorder, "Family BASIC Keyboard plus Famicom Data Recorder")]
[InlineData(DefaultExpansionDevice.DongdaPECKeyboard, "东达 (Dōngdá) PEC Keyboard")]
[InlineData(DefaultExpansionDevice.BitCorpBit79Keyboard, "普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard")]
[InlineData(DefaultExpansionDevice.SuborKeyboard, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard")]
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusMacroWinnersMouse, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Macro Winners Mouse")]
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4016, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4016")]
[InlineData(DefaultExpansionDevice.SNESMouse4016, "SNES Mouse ($4016)")]
[InlineData(DefaultExpansionDevice.Multicart, "Multicart")]
[InlineData(DefaultExpansionDevice.TwoSNESControllers, "Two SNES controllers replacing the two standard NES controllers")]
[InlineData(DefaultExpansionDevice.RacerMateBicycle, "RacerMate Bicycle")]
[InlineData(DefaultExpansionDevice.UForce, "U-Force")]
[InlineData(DefaultExpansionDevice.ROBStackUp, "R.O.B. Stack-Up")]
[InlineData(DefaultExpansionDevice.CityPatrolmanLightgun, "City Patrolman Lightgun")]
[InlineData(DefaultExpansionDevice.SharpC1CassetteInterface, "Sharp C1 Cassette Interface")]
[InlineData(DefaultExpansionDevice.StandardControllerWithSwappedInputs, "Standard Controller with swapped Left-Right/Up-Down/B-A")]
[InlineData(DefaultExpansionDevice.ExcaliburSudokuPad, "Excalibur Sudoku Pad")]
[InlineData(DefaultExpansionDevice.ABLPinball, "ABL Pinball")]
[InlineData(DefaultExpansionDevice.GoldenNuggetCasinoExtraButtons, "Golden Nugget Casino extra buttons")]
[InlineData(DefaultExpansionDevice.KedaKeyboard, "科达 (Kēdá) Keyboard")]
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4017")]
[InlineData(DefaultExpansionDevice.PortTestController, "Port test controller")]
[InlineData(DefaultExpansionDevice.BandaiMultiGamePlayerGamepad, "Bandai Multi Game Player Gamepad buttons")]
[InlineData(DefaultExpansionDevice.VenomTVDanceMat, "Venom TV Dance Mat")]
[InlineData(DefaultExpansionDevice.LGTVRemoteControl, "LG TV Remote Control")]
[InlineData(DefaultExpansionDevice.FamicomNetworkController, "Famicom Network Controller")]
[InlineData(DefaultExpansionDevice.KingFishingController, "King Fishing Controller")]
[InlineData(DefaultExpansionDevice.CroakyKaraokeController, "Croaky Karaoke Controller")]
[InlineData(DefaultExpansionDevice.KingwonKeyboard, "科王 (Kēwáng, a.k.a. Kingwon) Keyboard")]
[InlineData(DefaultExpansionDevice.ZechengKeyboard, "泽诚 (Zéchéng) Keyboard")]
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusL90RotatedPS2Mouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus L90-rotated PS/2 mouse in $4017")]
[InlineData(DefaultExpansionDevice.PS2KeyboardInUM6578PS2PortPS2Mouse4017, "PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017")]
[InlineData(DefaultExpansionDevice.PS2MouseInUM6578PS2Port, "PS/2 Mouse in UM6578 PS/2 port")]
[InlineData(DefaultExpansionDevice.YuxingMouse4016, "裕兴 (Yùxìng) Mouse via $4016")]
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusYuxingMouse4016, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng) Mouse mouse in $4016")]
[InlineData(DefaultExpansionDevice.GigggleTVPump, "Gigggle TV Pump")]
[InlineData(DefaultExpansionDevice.BBKKeyboardPlusR90RotatedPS2Mouse4017, "步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017")]
[InlineData(DefaultExpansionDevice.MagicalCooking, "Magical Cooking")]
[InlineData(DefaultExpansionDevice.SNESMouse4017, "SNES Mouse ($4017)")]
[InlineData(DefaultExpansionDevice.Zapper4016, "Zapper ($4016)")]
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerPrototype, "Arkanoid Vaus Controller (Prototype)")]
[InlineData(DefaultExpansionDevice.TVMahjongGameController, "TV 麻雀 Game (TV Mahjong Game) Controller")]
[InlineData(DefaultExpansionDevice.MahjongGekitouDensetsuController, "麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller")]
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusXInvertedPS2Mouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017")]
[InlineData(DefaultExpansionDevice.IBMPCXTKeyboard, "IBM PC/XT Keyboard")]
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusMegaBookMouse, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Mega Book Mouse")]
public void FromDefaultExpansionDeviceTest(DefaultExpansionDevice device, string expected)
{
string actual = device.FromDefaultExpansionDevice();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(ExtendedConsoleType.RegularSystem, "Regular NES/Famicom/Dendy")]
[InlineData(ExtendedConsoleType.NintendoVsSystem, "Nintendo Vs. System")]
[InlineData(ExtendedConsoleType.Playchoice10, "Playchoice 10")]
[InlineData(ExtendedConsoleType.RegularFamicloneDecimalMode, "Regular Famiclone, but with CPU that supports Decimal Mode")]
[InlineData(ExtendedConsoleType.RegularNESWithEPSM, "Regular NES/Famicom with EPSM module or plug-through cartridge")]
[InlineData(ExtendedConsoleType.VRTechnologyVT01, "V.R. Technology VT01 with red/cyan STN palette")]
[InlineData(ExtendedConsoleType.VRTechnologyVT02, "V.R. Technology VT02")]
[InlineData(ExtendedConsoleType.VRTechnologyVT03, "V.R. Technology VT03")]
[InlineData(ExtendedConsoleType.VRTechnologyVT09, "V.R. Technology VT09")]
[InlineData(ExtendedConsoleType.VRTechnologyVT32, "V.R. Technology VT32")]
[InlineData(ExtendedConsoleType.VRTechnologyVT369, "V.R. Technology VT369")]
[InlineData(ExtendedConsoleType.UMCUM6578, "UMC UM6578")]
[InlineData(ExtendedConsoleType.FamicomNetworkSystem, "Famicom Network System")]
[InlineData(ExtendedConsoleType.ReservedD, "Reserved (0x0D)")]
[InlineData(ExtendedConsoleType.ReservedE, "Reserved (0x0E)")]
[InlineData(ExtendedConsoleType.ReservedF, "Reserved (0x0F)")]
public void FromExtendedConsoleTypeTest(ExtendedConsoleType type, string expected)
{
string actual = type.FromExtendedConsoleType();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(NametableArrangement.Vertical, "Vertical")]
[InlineData(NametableArrangement.Horizontal, "Horizontal")]
public void FromNametableArrangementTest(NametableArrangement arrangement, string expected)
{
string actual = arrangement.FromNametableArrangement();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(TVSystem.NTSC, "NTSC")]
[InlineData(TVSystem.PAL, "PAL")]
public void FromTVSystemTest(TVSystem system, string expected)
{
string actual = system.FromTVSystem();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(TVSystemExtended.NTSC, "NTSC")]
[InlineData(TVSystemExtended.DualCompatible1, "Dual-compatible (0x01)")]
[InlineData(TVSystemExtended.PAL, "PAL")]
[InlineData(TVSystemExtended.DualCompatible3, "Dual-compatible (0x03)")]
public void FromTVSystemExtendedTest(TVSystemExtended system, string expected)
{
string actual = system.FromTVSystemExtended();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(VsHardwareType.VsUnisystem, "Vs. Unisystem (normal)")]
[InlineData(VsHardwareType.VsUnisystemRBIBaseballProtection, "Vs. Unisystem (RBI Baseball protection)")]
[InlineData(VsHardwareType.VsUnisystemTKOBoxingProtection, "Vs. Unisystem (TKO Boxing protection)")]
[InlineData(VsHardwareType.VsUnisystemSuperXeviousProtection, "Vs. Unisystem (Super Xevious protection)")]
[InlineData(VsHardwareType.VsUnisystemVsIceClimberJapanProtection, "Vs. Unisystem (Vs. Ice Climber Japan protection)")]
[InlineData(VsHardwareType.VsDualSystem, "Vs. Dual System (normal)")]
[InlineData(VsHardwareType.VsDualSystemRaidOnBungelingBayProtection, "Vs. Dual System (Raid on Bungeling Bay protection)")]
public void FromVsHardwareTypeTest(VsHardwareType type, string expected)
{
string actual = type.FromVsHardwareType();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(VsSystemType.AnyRP2C03RC2C03Variant, "Any RP2C03/RC2C03 variant")]
[InlineData(VsSystemType.Reserved1, "Reserved (0x01)")]
[InlineData(VsSystemType.RP2C040001, "RP2C04-0001")]
[InlineData(VsSystemType.RP2C040002, "RP2C04-0002")]
[InlineData(VsSystemType.RP2C040003, "RP2C04-0003")]
[InlineData(VsSystemType.RP2C040004, "RP2C04-0004")]
[InlineData(VsSystemType.Reserved6, "Reserved (0x06)")]
[InlineData(VsSystemType.Reserved7, "Reserved (0x07)")]
[InlineData(VsSystemType.RC2C0501, "RC2C05-01 (signature unknown)")]
[InlineData(VsSystemType.RC2C0502, "RC2C05-02 ($2002 AND $3F =$3D)")]
[InlineData(VsSystemType.RC2C0503, "RC2C05-03 ($2002 AND $1F =$1C)")]
[InlineData(VsSystemType.RC2C0504, "RC2C05-04 ($2002 AND $1F =$1B)")]
[InlineData(VsSystemType.ReservedC, "Reserved (0x0C)")]
[InlineData(VsSystemType.ReservedD, "Reserved (0x0D)")]
[InlineData(VsSystemType.ReservedE, "Reserved (0x0E)")]
[InlineData(VsSystemType.ReservedF, "Reserved (0x0F)")]
public void FromVsSystemTypeTest(VsSystemType type, string expected)
{
string actual = type.FromVsSystemType();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,61 @@
using SabreTools.Data.Models.NewExecutable;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class NewExecutableExtensionsTests
{
#region IsIntegerType
[Fact]
public void IsIntegerType_RTIE_HighBitSet_True()
{
var entry = new ResourceTypeInformationEntry { TypeID = 0xFFFF };
bool actual = entry.IsIntegerType();
Assert.True(actual);
}
[Fact]
public void IsIntegerType_RTIE_HighBitClear_False()
{
var entry = new ResourceTypeInformationEntry { TypeID = 0x0000 };
bool actual = entry.IsIntegerType();
Assert.False(actual);
}
[Fact]
public void IsIntegerType_RTRE_HighBitSet_True()
{
var entry = new ResourceTypeResourceEntry { ResourceID = 0xFFFF };
bool actual = entry.IsIntegerType();
Assert.True(actual);
}
[Fact]
public void IsIntegerType_RTRE_HighBitClear_False()
{
var entry = new ResourceTypeResourceEntry { ResourceID = 0x0000 };
bool actual = entry.IsIntegerType();
Assert.False(actual);
}
#endregion
#region GetEntryType
[Theory]
[InlineData(0x00, SegmentEntryType.Unused)]
[InlineData(0x01, SegmentEntryType.FixedSegment)]
[InlineData(0xAA, SegmentEntryType.FixedSegment)]
[InlineData(0xFE, SegmentEntryType.FixedSegment)]
[InlineData(0xFF, SegmentEntryType.MoveableSegment)]
public void GetEntryTypeTest(byte segmentIndicator, SegmentEntryType expected)
{
var entry = new EntryTableBundle { SegmentIndicator = segmentIndicator };
SegmentEntryType actual = entry.GetEntryType();
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -0,0 +1,129 @@
using SabreTools.Data.Models.NintendoDisc;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class NintendoDiscExtensionsTests
{
#region GetPlatform
[Fact]
public void GetPlatform_Object_WiiMagic_Wii()
{
var header = new DiscHeader { WiiMagic = Constants.WiiMagicWord };
Platform actual = header.GetPlatform();
Assert.Equal(Platform.Wii, actual);
}
[Fact]
public void GetPlatform_Object_GCMagic_GC()
{
var header = new DiscHeader { GCMagic = Constants.GCMagicWord };
Platform actual = header.GetPlatform();
Assert.Equal(Platform.GameCube, actual);
}
[Fact]
public void GetPlatform_Object_ValidGameID_GC()
{
var header = new DiscHeader { GameId = "GABC01" };
Platform actual = header.GetPlatform();
Assert.Equal(Platform.GameCube, actual);
}
[Fact]
public void GetPlatform_Object_InvalidGameID_Unknown()
{
var header = new DiscHeader { GameId = "XABC01" };
Platform actual = header.GetPlatform();
Assert.Equal(Platform.Unknown, actual);
}
[Fact]
public void GetPlatform_Object_DefaultHeader_Unknown()
{
var header = new DiscHeader();
Platform actual = header.GetPlatform();
Assert.Equal(Platform.Unknown, actual);
}
[Fact]
public void GetPlatform_Bytes_WiiMagic_Wii()
{
byte[] header =
[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17
0x5D, 0x1C, 0x9E, 0xA3, 0x00, 0x00, 0x00, 0x00, // 0x18-0x1F
];
Platform actual = header.GetPlatform();
Assert.Equal(Platform.Wii, actual);
}
[Fact]
public void GetPlatform_Bytes_GCMagic_GC()
{
byte[] header =
[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17
0x00, 0x00, 0x00, 0x00, 0xC2, 0x33, 0x9F, 0x3D, // 0x18-0x1F
];
Platform actual = header.GetPlatform();
Assert.Equal(Platform.GameCube, actual);
}
[Fact]
public void GetPlatform_Bytes_ValidGameID_GC()
{
byte[] header = [(byte)'G', (byte)'A', (byte)'B', (byte)'C'];
Platform actual = header.GetPlatform();
Assert.Equal(Platform.GameCube, actual);
}
[Fact]
public void GetPlatform_Bytes_InvalidGameID_Unknown()
{
byte[] header = [(byte)'G', (byte)'A', (byte)'B', 0x00];
Platform actual = header.GetPlatform();
Assert.Equal(Platform.Unknown, actual);
}
[Fact]
public void GetPlatform_Bytes_DefaultHeader_Unknown()
{
byte[] header = [];
Platform actual = header.GetPlatform();
Assert.Equal(Platform.Unknown, actual);
}
#endregion
#region IsGameCubeTitleType
[Theory]
[InlineData('\0', false)]
[InlineData('a', false)]
[InlineData('A', false)]
[InlineData('d', false)]
[InlineData('D', true)]
[InlineData('g', false)]
[InlineData('G', true)]
[InlineData('r', false)]
[InlineData('R', true)]
public void IsGameCubeTitleTypeTest(char c, bool expected)
{
bool actual = c.IsGameCubeTitleType();
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -0,0 +1,26 @@
using SabreTools.Data.Models.OperaFS;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class OperaFSExtensionsTests
{
[Fact]
public void DirectoryDescriptor_EqualsExactly_Empty()
{
var dir1 = new DirectoryDescriptor();
var dir2 = new DirectoryDescriptor();
bool actual = dir1.EqualsExactly(dir2);
Assert.True(actual);
}
[Fact]
public void DirectoryRecord_EqualsExactly_Empty()
{
var dr1 = new DirectoryRecord();
var dr2 = new DirectoryRecord();
bool actual = dr1.EqualsExactly(dr2);
Assert.True(actual);
}
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="10.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.Data.Extensions\SabreTools.Data.Extensions.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,9 +1,9 @@
using System;
using SabreTools.Data.Extensions;
using SabreTools.Data.Models.ASN1;
using Xunit;
namespace SabreTools.Serialization.Test.Extensions
#pragma warning disable IDE0230 // Use UTF-8 string literal
namespace SabreTools.Data.Extensions.Test
{
public class TypeLengthValueTests
{
@@ -13,7 +13,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_EOC()
{
string expected = "Type: V_ASN1_EOC";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -22,7 +22,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ZeroLength()
{
string expected = "Type: V_ASN1_NULL, Length: 0";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -31,7 +31,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_InvalidConstructed()
{
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 1, Value: [INVALID DATA TYPE]";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = (object?)false };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = false };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -40,8 +40,8 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidConstructed()
{
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 3, Value:\n Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
var boolTlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new Data.Models.ASN1.TypeLengthValue[] { boolTlv } };
var boolTlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new TypeLengthValue[] { boolTlv } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -50,7 +50,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_InvalidDataType()
{
string expected = "Type: V_ASN1_OBJECT, Length: 1, Value: [INVALID DATA TYPE]";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = (object?)false };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = false };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -59,7 +59,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_InvalidLength()
{
string expected = "Type: V_ASN1_NULL, Length: 1, Value: [NO DATA]";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty<byte>() };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty<byte>() };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -68,7 +68,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_InvalidBooleanLength()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 2 [Expected length of 1], Value: True";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -77,7 +77,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_InvalidBooleanArrayLength()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 1 [Expected value length of 1], Value: True";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -86,7 +86,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidBoolean()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -95,7 +95,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidInteger()
{
string expected = "Type: V_ASN1_INTEGER, Length: 1, Value: 1";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -104,7 +104,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidBitString_NoBits()
{
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 0 unused bits";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -113,7 +113,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidBitString_Bits()
{
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 1 unused bits: 01";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -122,7 +122,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidOctetString()
{
string expected = "Type: V_ASN1_OCTET_STRING, Length: 1, Value: 01";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -131,7 +131,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidObject()
{
string expected = "Type: V_ASN1_OBJECT, Length: 3, Value: 0.1.2.3 (/ITU-T/1/2/3)";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -140,7 +140,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidUTF8String()
{
string expected = "Type: V_ASN1_UTF8STRING, Length: 3, Value: ABC";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -149,7 +149,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidPrintableString()
{
string expected = "Type: V_ASN1_PRINTABLESTRING, Length: 3, Value: ABC";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -158,7 +158,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidTeletexString()
{
string expected = "Type: V_ASN1_TELETEXSTRING, Length: 3, Value: ABC";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -167,7 +167,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidIA5String()
{
string expected = "Type: V_ASN1_IA5STRING, Length: 3, Value: ABC";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -176,7 +176,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_InvalidUTCTime()
{
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: ABC";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -185,7 +185,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidUTCTime()
{
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: 1980-01-01 00:00:00";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -194,7 +194,7 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidBmpString()
{
string expected = "Type: V_ASN1_BMPSTRING, Length: 6, Value: ABC";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
@@ -203,11 +203,11 @@ namespace SabreTools.Serialization.Test.Extensions
public void Format_ValidUnformatted()
{
string expected = "Type: V_ASN1_OBJECT_DESCRIPTOR, Length: 1, Value: 01";
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } };
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
#endregion
}
}
}

View File

@@ -0,0 +1,59 @@
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class WiseScriptExtensionsTests
{
[Theory]
// Defined functions
[InlineData("f0", "Add Directory to PATH")]
[InlineData("f1", "Add to AUTOEXEC.BAT")]
[InlineData("f2", "Add to CONFIG.SYS")]
[InlineData("f3", "Add to SYSTEM.INI")]
[InlineData("f8", "Read INI Value")]
[InlineData("f9", "Get Registry Key Value")]
[InlineData("f10", "Register Font")]
[InlineData("f11", "Win32 System Directory")]
[InlineData("f12", "Check Configuration")]
[InlineData("f13", "Search for File")]
[InlineData("f15", "Read/Write Binary File")]
[InlineData("f16", "Set Variable")]
[InlineData("f17", "Get Environment Variable")]
[InlineData("f19", "Check if File/Dir Exists")]
[InlineData("f20", "Set File Attributes")]
[InlineData("f21", "Set Files/Buffers")]
[InlineData("f22", "Find File in Path")]
[InlineData("f23", "Check Disk Space")]
[InlineData("f25", "Insert Line Into Text File")]
[InlineData("f27", "Parse String")]
[InlineData("f28", "Exit Installation")]
[InlineData("f29", "Self-Register OCXs/DLLs")]
[InlineData("f30", "Install DirectX Components")]
[InlineData("f31", "Wizard Block")]
[InlineData("f33", "Read/Update Text File")]
[InlineData("f34", "Post to HTTP Server")]
[InlineData("f35", "Prompt for Filename")]
[InlineData("f36", "Start/Stop Service")]
[InlineData("f38", "Check HTTP Connection")]
// Undefined functions
[InlineData("f4", "UNDEFINED f4")]
[InlineData("f5", "UNDEFINED f5")]
[InlineData("f6", "UNDEFINED f6")]
[InlineData("f7", "UNDEFINED f7")]
[InlineData("f14", "UNDEFINED f14")]
[InlineData("f18", "UNDEFINED f18")]
[InlineData("f24", "UNDEFINED f24")]
[InlineData("f26", "UNDEFINED f26")]
[InlineData("f32", "UNDEFINED f32")]
[InlineData("f37", "UNDEFINED f37")]
// External DLL
[InlineData(null, null)]
[InlineData("f99", "UNDEFINED f99")]
[InlineData("func", "External: func")]
public void FromWiseFunctionIdTest(string? functionId, string? expected)
{
string? actual = functionId.FromWiseFunctionId();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Linq;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class XZExtensionsTests
{
[Fact]
public void VariableLengthIntegerTest()
{
ulong expected = 123456789;
byte[] encoded = expected.EncodeVariableLength();
Assert.True(encoded.SequenceEqual<byte>([0x95, 0x9A, 0xEF, 0x3A]));
ulong actual = encoded.DecodeVariableLength(maxSize: 16, out int length);
Assert.Equal(expected, actual);
Assert.Equal(4, length);
}
}
}

View File

@@ -0,0 +1,17 @@
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class XboxExecutableExtensionsTests
{
[Theory]
[InlineData(0x00000000, "0000-000")]
[InlineData(0x4142000F, "AB-015")]
[InlineData(0x3132F000, "12-61440")]
public void ToFormattedXBETitleIDTest(uint value, string expected)
{
string actual = value.ToFormattedXBETitleID();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,18 @@
using SabreTools.Data.Models.ZArchive;
using Xunit;
namespace SabreTools.Data.Extensions.Test
{
public class ZArchiveExtensionsTests
{
[Fact]
public void GetName_Null()
{
var de = new DirectoryEntry();
NameTable nt = new NameTable();
string? expected = null;
string? actual = de.GetName(nt);
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,327 @@
using System.Collections.Generic;
using SabreTools.Data.Models.Atari7800;
namespace SabreTools.Data.Extensions
{
public static class Atari7800CartExtensions
{
/// <summary>
/// Convert a <see cref="AudioDevice"/> value to string
/// </summary>
public static string FromAudioDevice(this AudioDevice audio)
{
string[] devices = new string[4];
byte pokey = (byte)((byte)audio & 0x07);
devices[0] = pokey switch
{
0 => "POKEY - none",
1 => "POKEY - @440",
2 => "POKEY - @450",
3 => "POKEY - @450+@440",
4 => "POKEY - @800",
5 => "POKEY - @4000",
_ => $"Unknown {pokey}",
};
byte ym2151 = (byte)(((byte)audio >> 3) & 0x01);
devices[1] = ym2151 switch
{
0 => "No YM2151 @460",
1 => "YM2151 @460",
_ => $"Unknown {ym2151}",
};
byte covox = (byte)(((byte)audio >> 4) & 0x01);
devices[2] = covox switch
{
0 => "No COVOX @430",
1 => "COVOX @430",
_ => $"Unknown {covox}",
};
byte adpcm = (byte)(((byte)audio >> 5) & 0x01);
devices[3] = adpcm switch
{
0 => "No ADPCM Audio Stream @420",
1 => "ADPCM Audio Stream @420",
_ => $"Unknown {adpcm}",
};
return string.Join(", ", [.. devices]);
}
/// <summary>
/// Convert a <see cref="CartType"/> value to string
/// </summary>
public static string FromCartType(this CartType type)
{
List<string> types = [];
#if NET20 || NET35
if ((type & CartType.PokeyAt4000) != 0)
#else
if (type.HasFlag(CartType.PokeyAt4000))
#endif
types.Add("pokey at $4000");
#if NET20 || NET35
if ((type & CartType.SupergameBankSwitched) != 0)
#else
if (type.HasFlag(CartType.SupergameBankSwitched))
#endif
types.Add("supergame bank switched");
#if NET20 || NET35
if ((type & CartType.SupergameRamAt4000) != 0)
#else
if (type.HasFlag(CartType.SupergameRamAt4000))
#endif
types.Add("supergame ram at $4000");
#if NET20 || NET35
if ((type & CartType.RomAt4000) != 0)
#else
if (type.HasFlag(CartType.RomAt4000))
#endif
types.Add("rom at $4000");
#if NET20 || NET35
if ((type & CartType.Bank6At4000) != 0)
#else
if (type.HasFlag(CartType.Bank6At4000))
#endif
types.Add("bank 6 at $4000");
#if NET20 || NET35
if ((type & CartType.BankedRam) != 0)
#else
if (type.HasFlag(CartType.BankedRam))
#endif
types.Add("banked ram");
#if NET20 || NET35
if ((type & CartType.PokeyAt450) != 0)
#else
if (type.HasFlag(CartType.PokeyAt450))
#endif
types.Add("pokey at $450");
#if NET20 || NET35
if ((type & CartType.MirrorRamAt4000) != 0)
#else
if (type.HasFlag(CartType.MirrorRamAt4000))
#endif
types.Add("mirror ram at $4000");
#if NET20 || NET35
if ((type & CartType.ActivisionBanking) != 0)
#else
if (type.HasFlag(CartType.ActivisionBanking))
#endif
types.Add("activision banking");
#if NET20 || NET35
if ((type & CartType.AbsoluteBanking) != 0)
#else
if (type.HasFlag(CartType.AbsoluteBanking))
#endif
types.Add("absolute banking");
#if NET20 || NET35
if ((type & CartType.PokeyAt440) != 0)
#else
if (type.HasFlag(CartType.PokeyAt440))
#endif
types.Add("pokey at $440");
#if NET20 || NET35
if ((type & CartType.Ym2151At460461) != 0)
#else
if (type.HasFlag(CartType.Ym2151At460461))
#endif
types.Add("ym2151 at $460/$461");
#if NET20 || NET35
if ((type & CartType.Souper) != 0)
#else
if (type.HasFlag(CartType.Souper))
#endif
types.Add("souper");
#if NET20 || NET35
if ((type & CartType.Banksets) != 0)
#else
if (type.HasFlag(CartType.Banksets))
#endif
types.Add("banksets");
#if NET20 || NET35
if ((type & CartType.HaltBankedRam) != 0)
#else
if (type.HasFlag(CartType.HaltBankedRam))
#endif
types.Add("halt banked ram");
#if NET20 || NET35
if ((type & CartType.PokeyAt800) != 0)
#else
if (type.HasFlag(CartType.PokeyAt800))
#endif
types.Add("pokey@800");
// If no flags are set
if (types.Count == 0)
types.Add("N/A");
return string.Join(", ", [.. types]);
}
/// <summary>
/// Convert a <see cref="ControllerType"/> value to string
/// </summary>
public static string FromControllerType(this ControllerType type)
{
return type switch
{
ControllerType.None => "none",
ControllerType.Joystick => "7800 joystick",
ControllerType.Lightgun => "lightgun",
ControllerType.Paddle => "paddle",
ControllerType.Trakball => "trakball",
ControllerType.VcsJoystick => "2600 joystick",
ControllerType.VcsDriving => "2600 driving",
ControllerType.VcsKeypad => "2600 keypad",
ControllerType.STMouse => "ST mouse",
ControllerType.AmigaMouse => "Amiga mouse",
ControllerType.AtariVoxSaveKey => "AtariVox/SaveKey",
ControllerType.SNES2Atari => "SNES2Atari",
ControllerType.Mega7800 => "Mega7800",
_ => $"Unknown {(byte)type}",
};
}
/// <summary>
/// Convert a <see cref="Interrupt"/> value to string
/// </summary>
public static string FromInterrupt(this Interrupt interrupt)
{
return interrupt switch
{
Interrupt.None => "None",
Interrupt.Pokey1 => "POKEY 1 (@450 | @800 | @4000)",
Interrupt.Pokey2 => "POKEY 2 (@440)",
Interrupt.YM2151 => "YM2151",
_ => $"Unknown {(byte)interrupt}",
};
}
/// <summary>
/// Convert a <see cref="Mapper"/> value to string
/// </summary>
public static string FromMapper(this Mapper mapper)
{
return mapper switch
{
Mapper.Linear => "Linear",
Mapper.SuperGame => "SuperGame",
Mapper.Activision => "Activision",
Mapper.Absolute => "Absolute",
Mapper.Souper => "Souper",
_ => $"Unknown {(byte)mapper}",
};
}
/// <summary>
/// Convert a <see cref="MapperOptions"/> value to string
/// </summary>
public static string FromMapperOptions(this MapperOptions options)
{
string[] romOptions = new string[2];
byte option4000 = (byte)((byte)options & 0x07);
romOptions[0] = option4000 switch
{
0 => "Option at @4000 - none",
1 => "Option at @4000 - 16K RAM",
2 => "Option at @4000 - 8K EXRAM/A8",
3 => "Option at @4000 - 32K EXRAM/M2",
4 => "Option at @4000 - EXROM",
5 => "Option at @4000 - EXFIX",
6 => "Option at @4000 - 32k EXRAM/X2",
_ => $"Unknown {option4000}",
};
byte romType = (byte)(((byte)options >> 7) & 0x01);
romOptions[1] = romType switch
{
0 => "Standard ROM",
1 => "Bankset ROM",
_ => $"Unknown {romType}",
};
return string.Join(", ", [.. romOptions]);
}
/// <summary>
/// Convert a <see cref="SaveDevice"/> value to string
/// </summary>
public static string FromSaveDevice(this SaveDevice device)
{
return device switch
{
SaveDevice.None => "None",
SaveDevice.HSC => "HSC",
SaveDevice.SaveKeyAtariVox => "SaveKey/AtariVox",
_ => $"Unknown {(byte)device}",
};
}
/// <summary>
/// Convert a <see cref="SlotPassthroughDevice"/> value to string
/// </summary>
public static string FromSlotPassthroughDevice(this SlotPassthroughDevice device)
{
return device switch
{
SlotPassthroughDevice.None => "None",
SlotPassthroughDevice.XM => "XM",
_ => $"Unknown {(byte)device}",
};
}
/// <summary>
/// Convert a <see cref="TVType"/> value to string
/// </summary>
public static string FromTVType(this TVType type)
{
string[] types = new string[3];
byte standard = (byte)((byte)type & 0x01);
types[0] = standard switch
{
0 => "NTSC",
1 => "PAL",
_ => $"Unknown {standard}",
};
byte connection = (byte)(((byte)type >> 1) & 0x01);
types[1] = connection switch
{
0 => "Component",
1 => "Composite",
_ => $"Unknown {standard}",
};
byte region = (byte)(((byte)type >> 2) & 0x01);
types[2] = region switch
{
0 => "Single-region",
1 => "Multi-region",
_ => $"Unknown {standard}",
};
return string.Join(", ", [.. types]);
}
}
}

View File

@@ -0,0 +1,21 @@
using SabreTools.Data.Models.AtariLynx;
namespace SabreTools.Data.Extensions
{
public static class AtariLynxCartExtensions
{
/// <summary>
/// Convert a <see cref="Rotation"/> value to string
/// </summary>
public static string FromRotation(this Rotation rotation)
{
return rotation switch
{
Rotation.NoRotation => "No rotation (horizontal, buttons right)",
Rotation.RotateLeft => "Rotate left (vertical, buttons down)",
Rotation.RotateRight => "Rotate right (vertical, buttons up)",
_ => $"Unknown {(byte)rotation}",
};
}
}
}

View File

@@ -0,0 +1,33 @@
using SabreTools.Data.Models.BSP;
namespace SabreTools.Data.Extensions
{
public static class BSPExtensions
{
/// <summary>
/// Convert a <see cref="BspLumpType"/> value to string
/// </summary>
public static string FromBspLumpType(this BspLumpType type)
{
return type switch
{
BspLumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
BspLumpType.LUMP_PLANES => " - LUMP_PLANES",
BspLumpType.LUMP_TEXTURES => " - LUMP_TEXTURES",
BspLumpType.LUMP_VERTICES => " - LUMP_VERTICES",
BspLumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
BspLumpType.LUMP_NODES => " - LUMP_NODES",
BspLumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
BspLumpType.LUMP_FACES => " - LUMP_FACES",
BspLumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
BspLumpType.LUMP_CLIPNODES => " - LUMP_CLIPNODES",
BspLumpType.LUMP_LEAVES => " - LUMP_LEAVES",
BspLumpType.LUMP_MARKSURFACES => " - LUMP_MARKSURFACES",
BspLumpType.LUMP_EDGES => " - LUMP_EDGES",
BspLumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
BspLumpType.LUMP_MODELS => " - LUMP_MODELS",
_ => string.Empty,
};
}
}
}

View File

@@ -0,0 +1,313 @@
using System;
using System.IO;
using SabreTools.Data.Models.CDROM;
using SabreTools.IO.Extensions;
using SabreTools.Numerics.Extensions;
namespace SabreTools.Data.Extensions
{
public static class CDROMExtensions
{
/// <summary>
/// Get the sector mode for a CD-ROM stream
/// </summary>
/// <param name="stream">Stream to derive the sector mode from</param>
/// <returns>Sector mode from the stream on success, <see cref="SectorMode.UNKNOWN"/> on error</returns>
public static SectorMode GetSectorMode(this Stream stream)
{
try
{
byte modeByte = stream.ReadByteValue();
if (modeByte == 0)
{
return SectorMode.MODE0;
}
else if (modeByte == 1)
{
return SectorMode.MODE1;
}
else if (modeByte == 2)
{
stream.SeekIfPossible(2, SeekOrigin.Current);
byte submode = stream.ReadByteValue();
if ((submode & 0x20) == 0x20)
return SectorMode.MODE2_FORM2;
else
return SectorMode.MODE2_FORM1;
}
else
{
return SectorMode.UNKNOWN;
}
}
catch
{
// Ignore the actual error
return SectorMode.UNKNOWN;
}
}
/// <summary>
/// Get the user data size for a sector mode
/// </summary>
/// <param name="mode">Sector mode to get a value for</param>
/// <returns>User data size, if possible</returns>
public static long GetUserDataSize(this SectorMode mode)
{
return mode switch
{
SectorMode.UNKNOWN => Constants.Mode0DataSize,
SectorMode.MODE0 => Constants.Mode0DataSize,
SectorMode.MODE1 => Constants.Mode1DataSize,
SectorMode.MODE2 => Constants.Mode0DataSize,
SectorMode.MODE2_FORM1 => Constants.Mode2Form1DataSize,
SectorMode.MODE2_FORM2 => Constants.Mode2Form2DataSize,
_ => Constants.Mode0DataSize,
};
}
/// <summary>
/// Get the user data end offset for a sector mode
/// </summary>
/// <param name="mode">Sector mode to get a value for</param>
/// <returns>User data end offset, if possible</returns>
public static long GetUserDataEnd(this SectorMode mode)
{
return mode switch
{
SectorMode.UNKNOWN => Constants.Mode0UserDataEnd,
SectorMode.MODE0 => Constants.Mode0UserDataEnd, // TODO: Support flexible sector length (2352)
SectorMode.MODE1 => Constants.Mode1UserDataEnd,
SectorMode.MODE2 => Constants.Mode0UserDataEnd, // TODO: Support flexible sector length (2352)
SectorMode.MODE2_FORM1 => Constants.Mode2Form1UserDataEnd,
SectorMode.MODE2_FORM2 => Constants.Mode2Form2UserDataEnd, // TODO: Support flexible sector length (2348)
_ => Constants.Mode0UserDataEnd,
};
}
/// <summary>
/// Get the user data start offset for a sector mode
/// </summary>
/// <param name="mode">Sector mode to get a value for</param>
/// <returns>User data start offset, if possible</returns>
public static long GetUserDataStart(this SectorMode mode)
{
return mode switch
{
SectorMode.UNKNOWN => Constants.Mode0UserDataStart,
SectorMode.MODE0 => Constants.Mode0UserDataStart,
SectorMode.MODE1 => Constants.Mode1UserDataStart,
SectorMode.MODE2 => Constants.Mode0UserDataStart,
SectorMode.MODE2_FORM1 => Constants.Mode2Form1UserDataStart,
SectorMode.MODE2_FORM2 => Constants.Mode2Form2UserDataStart,
_ => Constants.Mode0UserDataStart,
};
}
/// <summary>
/// Creates a stream that provides only the user data of a CDROM stream
/// </summary>
public class UserDataStream : Stream
{
// Base CDROM stream (2352-byte sector)
private readonly Stream _baseStream;
// State variables
private long _position = 0;
private SectorMode _currentMode = SectorMode.UNKNOWN;
private long _userDataStart = Constants.Mode1UserDataStart;
private long _userDataEnd = Constants.Mode1UserDataEnd;
#pragma warning disable IDE0044
private long _isoSectorSize = Constants.Mode1DataSize;
#pragma warning restore IDE0044
public UserDataStream(Stream inputStream)
{
if (!inputStream.CanSeek || !inputStream.CanRead)
throw new ArgumentException("Stream must be readable and seekable.", nameof(inputStream));
_baseStream = inputStream;
}
/// <inheritdoc/>
public override bool CanRead => _baseStream.CanRead;
/// <inheritdoc/>
public override bool CanSeek => _baseStream.CanSeek;
/// <inheritdoc/>
public override bool CanWrite => false;
/// <inheritdoc/>
public override void Flush() => _baseStream.Flush();
/// <inheritdoc/>
public override long Length
=> _baseStream.Length / Constants.CDROMSectorSize * _isoSectorSize;
/// <inheritdoc/>
public override void SetLength(long value)
=> throw new NotSupportedException("Setting the length of this stream is not supported.");
/// <inheritdoc/>
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException("Writing to this stream is not supported.");
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
_baseStream.Dispose();
base.Dispose(disposing);
}
/// <inheritdoc/>
public override long Position
{
// Get the position of the underlying ISO9660 stream
get
{
// Get the user data location based on the current sector mode
SetState(_position);
// Get the number of ISO sectors before current position
long isoPosition = _position / Constants.CDROMSectorSize * _isoSectorSize;
// Add the within-sector position
long remainder = _position % Constants.CDROMSectorSize;
if (remainder > _userDataEnd)
isoPosition += _isoSectorSize;
else if (remainder > _userDataStart)
isoPosition += remainder - _userDataStart;
return isoPosition;
}
set
{
// Seek to the underlying ISO9660 position
Seek(value, SeekOrigin.Begin);
}
}
/// <inheritdoc/>
public override int Read(byte[] buffer, int offset, int count)
{
int totalRead = 0;
int remaining = count;
while (remaining > 0 && _position < _baseStream.Length)
{
// Determine location of current sector
long baseStreamOffset = _position - (_position % Constants.CDROMSectorSize);
// Set the current sector's mode and user data location
SetState(baseStreamOffset);
// Deal with case where base position is not in ISO stream
long remainder = _position % Constants.CDROMSectorSize;
long sectorOffset = remainder - _userDataStart;
if (remainder < _userDataStart)
{
baseStreamOffset += _userDataStart;
sectorOffset = 0;
_position += _userDataStart;
}
else if (remainder >= _userDataEnd)
{
baseStreamOffset += Constants.CDROMSectorSize;
sectorOffset = 0;
_position += Constants.CDROMSectorSize - _userDataEnd + _userDataStart;
}
else
{
baseStreamOffset += remainder;
}
// Sanity check on read location before seeking
if (baseStreamOffset < 0 || baseStreamOffset > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(offset), "Attempted to seek outside the stream boundaries.");
// Seek to target position in base CDROM stream
_baseStream.SeekIfPossible(baseStreamOffset, SeekOrigin.Begin);
// Read the remaining bytes, up to max of one ISO sector (2048 bytes)
int bytesToRead = (int)Math.Min(remaining, _isoSectorSize - sectorOffset);
// Don't overshoot end of stream
bytesToRead = (int)Math.Min(bytesToRead, _baseStream.Length - _position);
// Finish reading if no more bytes to be read
if (bytesToRead <= 0)
break;
// Read up to 2048 bytes from base CDROM stream
int bytesRead = _baseStream.Read(buffer, offset + totalRead, bytesToRead);
// Update state for base stream
_position = _baseStream.Position;
if (bytesToRead == (_isoSectorSize - sectorOffset))
_position += Constants.CDROMSectorSize - _userDataEnd + _userDataStart;
// Update state for ISO stream
totalRead += bytesRead;
remaining -= bytesRead;
if (bytesRead == 0)
break;
}
return totalRead;
}
/// <inheritdoc/>
public override long Seek(long offset, SeekOrigin origin)
{
// Get the intended position for the ISO9660 stream
long targetPosition = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.Current => Position + offset,
SeekOrigin.End => Length + offset,
_ => throw new ArgumentException("Invalid SeekOrigin.", nameof(origin)),
};
// Get the number of ISO sectors before current position
long newPosition = targetPosition / _isoSectorSize * Constants.CDROMSectorSize;
// Set the current sector's mode and user data location
SetState(newPosition);
// Add the within-sector position
newPosition += _userDataStart + (targetPosition % _isoSectorSize);
if (newPosition < 0 || newPosition > _baseStream.Length)
throw new ArgumentOutOfRangeException(nameof(offset), "Attempted to seek outside the stream boundaries.");
_position = _baseStream.SeekIfPossible(newPosition, SeekOrigin.Begin);
return Position;
}
/// <summary>
/// Update the current stream state based on the location
/// </summary>
/// <param name="sectorLocation">Sector location to update from</param>
private void SetState(long sectorLocation)
{
long current = _baseStream.Position;
long modePosition = sectorLocation - (sectorLocation % Constants.CDROMSectorSize) + 15;
// Get the current sector mode
_baseStream.SeekIfPossible(modePosition, SeekOrigin.Begin);
_currentMode = _baseStream.GetSectorMode();
// Set the user data location variables
_userDataStart = _currentMode.GetUserDataStart();
_userDataEnd = _currentMode.GetUserDataEnd();
// _isoSectorSize = _currentMode.GetUserDataSize();
// Reset the stream position
_baseStream.SeekIfPossible(current, SeekOrigin.Begin);
}
}
}
}

View File

@@ -2,7 +2,7 @@ using System.Text;
namespace SabreTools.Data.Extensions
{
public static class CFB
public static class CFBExtensions
{
/// <summary>
/// Decode a MIME-encoded stream name stored as a byte array
@@ -13,7 +13,7 @@ namespace SabreTools.Data.Extensions
public static string? DecodeStreamName(this byte[]? input)
{
// Ignore invalid inputs
if (input == null)
if (input is null)
return null;
else if (input.Length == 0)
return string.Empty;
@@ -28,7 +28,7 @@ namespace SabreTools.Data.Extensions
byte ch = input[p];
if ((ch == 0xe3 && input[p + 1] >= 0xa0) || (ch == 0xe4 && input[p + 1] < 0xa0))
{
// UTF-8 encoding of 0x3800..0x47ff.
// UTF-8 encoding of 0x3800..0x47ff.
output[q++] = (byte)Mime2Utf(input[p + 2] & 0x7f);
output[q++] = (byte)Mime2Utf(input[p + 1] ^ 0xa0);
p += 3;
@@ -69,7 +69,7 @@ namespace SabreTools.Data.Extensions
public static string? DecodeStreamName(this string? input)
{
// Ignore invalid inputs
if (input == null)
if (input is null)
return null;
else if (input.Length == 0)
return string.Empty;
@@ -96,4 +96,4 @@ namespace SabreTools.Data.Extensions
};
}
}
}
}

View File

@@ -0,0 +1,54 @@
using SabreTools.Data.Models.ISO9660;
using SabreTools.Numerics;
namespace SabreTools.Data.Extensions
{
public static class ISO9660Extensions
{
/// <summary>
/// Get the logical block size from a sector length
/// </summary>
/// <param name="vd">Volume descriptor containing block information</param>
/// <param name="sectorLength">Defined sector length</param>
/// <returns>Size of a logical block</returns>
public static short GetLogicalBlockSize(this VolumeDescriptor vd, short sectorLength)
{
BothInt16 blockSize;
if (vd is PrimaryVolumeDescriptor pvd)
blockSize = pvd.LogicalBlockSize;
else if (vd is SupplementaryVolumeDescriptor svd)
blockSize = svd.LogicalBlockSize;
else
return sectorLength;
// If the block size is inconsistent
if (!blockSize.IsValid)
{
bool leValid = BlockSizeValid(blockSize.LittleEndian, sectorLength);
bool beValid = BlockSizeValid(blockSize.BigEndian, sectorLength);
if (leValid && !beValid)
blockSize = blockSize.LittleEndian;
else if (beValid && !leValid)
blockSize = blockSize.BigEndian;
else
return sectorLength;
}
// Validate logical block size
if (!BlockSizeValid(blockSize, sectorLength))
blockSize = sectorLength;
return blockSize;
}
/// <summary>
/// Indicates if a block size is valid
/// </summary>
/// <param name="blockSize">Block length to check</param>
/// <param name="sectorLength">Defined sector length</param>
/// <returns>True if the block length is valid, false otherwise</returns>
private static bool BlockSizeValid(short blockSize, short sectorLength)
=> blockSize >= 512 && blockSize <= sectorLength && (blockSize & (blockSize - 1)) == 0;
}
}

View File

@@ -2,7 +2,7 @@ using SabreTools.Data.Models.InstallShieldCabinet;
namespace SabreTools.Data.Extensions
{
public static class InstallShieldCabinet
public static class InstallShieldCabinetExtensions
{
#region File Descriptors
@@ -14,7 +14,7 @@ namespace SabreTools.Data.Extensions
public static bool IsCompressed(this FileDescriptor? fileDescriptor)
{
// Ignore invalid descriptors
if (fileDescriptor == null)
if (fileDescriptor is null)
return true;
#if NET20 || NET35
@@ -32,7 +32,7 @@ namespace SabreTools.Data.Extensions
public static bool IsInvalid(this FileDescriptor? fileDescriptor)
{
// Ignore invalid descriptors
if (fileDescriptor == null)
if (fileDescriptor is null)
return true;
#if NET20 || NET35
@@ -50,7 +50,7 @@ namespace SabreTools.Data.Extensions
public static bool IsObfuscated(this FileDescriptor? fileDescriptor)
{
// Ignore invalid descriptors
if (fileDescriptor == null)
if (fileDescriptor is null)
return false;
#if NET20 || NET35
@@ -68,7 +68,7 @@ namespace SabreTools.Data.Extensions
public static bool IsSplit(this FileDescriptor? fileDescriptor)
{
// Ignore invalid descriptors
if (fileDescriptor == null)
if (fileDescriptor is null)
return false;
#if NET20 || NET35
@@ -90,7 +90,7 @@ namespace SabreTools.Data.Extensions
public static int GetMajorVersion(this Cabinet? cabinet)
{
// Ignore invalid cabinets
if (cabinet == null)
if (cabinet is null)
return -1;
return cabinet.CommonHeader.GetMajorVersion();
@@ -104,7 +104,7 @@ namespace SabreTools.Data.Extensions
public static int GetMajorVersion(this CommonHeader? commonHeader)
{
// Ignore invalid headers
if (commonHeader == null)
if (commonHeader is null)
return -1;
uint majorVersion = commonHeader.Version;
@@ -124,4 +124,4 @@ namespace SabreTools.Data.Extensions
#endregion
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,241 @@
using SabreTools.Data.Models.NES;
namespace SabreTools.Data.Extensions
{
public static class NESCartExtensions
{
/// <summary>
/// Convert a <see cref="ConsoleType"/> value to string
/// </summary>
public static string FromConsoleType(this ConsoleType type)
{
return type switch
{
ConsoleType.StandardSystem => "Nintendo Entertainment System/Family Computer",
ConsoleType.VSUnisystem => "VS Unisystem",
ConsoleType.PlayChoice10 => "PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)",
ConsoleType.ExtendedConsoleType => "Extended Console Type",
_ => $"Unknown {(byte)type}",
};
}
/// <summary>
/// Convert a <see cref="CPUPPUTiming"/> value to string
/// </summary>
public static string FromCPUPPUTiming(this CPUPPUTiming timing)
{
return timing switch
{
CPUPPUTiming.RP2C02 => "RP2C02 (NTSC NES)",
CPUPPUTiming.RP2C07 => "RP2C07 (Licensed PAL NES)",
CPUPPUTiming.MultipleRegion => "Multiple-region",
CPUPPUTiming.UA6538 => "UA6538 (Dendy)",
_ => $"Unknown {(byte)timing}",
};
}
/// <summary>
/// Convert a <see cref="DefaultExpansionDevice"/> value to string
/// </summary>
public static string FromDefaultExpansionDevice(this DefaultExpansionDevice device)
{
return device switch
{
DefaultExpansionDevice.Unspecified => "Unspecified",
DefaultExpansionDevice.StandardControllers => "Standard NES/Famicom controllers",
DefaultExpansionDevice.NESFourScore => "NES Four Score/Satellite with two additional standard controllers",
DefaultExpansionDevice.FamicomFourPlayersAdapter => "Famicom Four Players Adapter with two additional standard controllers using the 'simple' protocol",
DefaultExpansionDevice.VsSystem4016 => "Vs. System (1P via $4016)",
DefaultExpansionDevice.VsSystem4017 => "Vs. System (1P via $4017)",
DefaultExpansionDevice.Reserved06 => "Reserved (0x06)",
DefaultExpansionDevice.VsZapper => "Vs. Zapper",
DefaultExpansionDevice.Zapper4017 => "Zapper ($4017)",
DefaultExpansionDevice.TwoZappers => "Two Zappers",
DefaultExpansionDevice.BandaiHyperShotLightgun => "Bandai Hyper Shot Lightgun",
DefaultExpansionDevice.PowerPadSideA => "Power Pad Side A",
DefaultExpansionDevice.PowerPadSideB => "Power Pad Side B",
DefaultExpansionDevice.FamilyTrainerSideA => "Family Trainer Side A",
DefaultExpansionDevice.FamilyTrainerSideB => "Family Trainer Side B",
DefaultExpansionDevice.ArkanoidVausControllerNES => "Arkanoid Vaus Controller (NES)",
DefaultExpansionDevice.ArkanoidVausControllerFamicom => "Arkanoid Vaus Controller (Famicom)",
DefaultExpansionDevice.TwoVausControllersPlusFamicomDataRecorder => "Two Vaus Controllers plus Famicom Data Recorder",
DefaultExpansionDevice.KonamiHyperShotController => "Konami Hyper Shot Controller",
DefaultExpansionDevice.CoconutsPachinkoController => "Coconuts Pachinko Controller",
DefaultExpansionDevice.ExcitingBoxingPunchingBag => "Exciting Boxing Punching Bag (Blowup Doll)",
DefaultExpansionDevice.JissenMahjongController => "Jissen Mahjong Controller",
DefaultExpansionDevice.YonezawaPartyTap => "米澤 (Yonezawa) Party Tap",
DefaultExpansionDevice.OekaKidsTablet => "Oeka Kids Tablet",
DefaultExpansionDevice.SunsoftBarcodeBattler => "Sunsoft Barcode Battler",
DefaultExpansionDevice.MiraclePianoKeyboard => "Miracle Piano Keyboard",
DefaultExpansionDevice.PokkunMoguraaTapTapMat => "Pokkun Moguraa Tap-tap Mat (Whack-a-Mole Mat and Mallet)",
DefaultExpansionDevice.TopRider => "Top Rider (Inflatable Bicycle)",
DefaultExpansionDevice.DoubleFisted => "Double-Fisted (Requires or allows use of two controllers by one player)",
DefaultExpansionDevice.Famicom3DSystem => "Famicom 3D System",
DefaultExpansionDevice.DoremikkoKeyboard => "Doremikko Keyboard",
DefaultExpansionDevice.ROBGyromite => "R.O.B. Gyromite",
DefaultExpansionDevice.FamicomDataRecorder => "Famicom Data Recorder ('silent' keyboard)",
DefaultExpansionDevice.ASCIITurboFile => "ASCII Turbo File",
DefaultExpansionDevice.IGSStorageBattleBox => "IGS Storage Battle Box",
DefaultExpansionDevice.FamilyBASICKeyboardPlusFamicomDataRecorder => "Family BASIC Keyboard plus Famicom Data Recorder",
DefaultExpansionDevice.DongdaPECKeyboard => "东达 (Dōngdá) PEC Keyboard",
DefaultExpansionDevice.BitCorpBit79Keyboard => "普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard",
DefaultExpansionDevice.SuborKeyboard => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard",
DefaultExpansionDevice.SuborKeyboardPlusMacroWinnersMouse => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Macro Winners Mouse",
DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4016 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4016",
DefaultExpansionDevice.SNESMouse4016 => "SNES Mouse ($4016)",
DefaultExpansionDevice.Multicart => "Multicart",
DefaultExpansionDevice.TwoSNESControllers => "Two SNES controllers replacing the two standard NES controllers",
DefaultExpansionDevice.RacerMateBicycle => "RacerMate Bicycle",
DefaultExpansionDevice.UForce => "U-Force",
DefaultExpansionDevice.ROBStackUp => "R.O.B. Stack-Up",
DefaultExpansionDevice.CityPatrolmanLightgun => "City Patrolman Lightgun",
DefaultExpansionDevice.SharpC1CassetteInterface => "Sharp C1 Cassette Interface",
DefaultExpansionDevice.StandardControllerWithSwappedInputs => "Standard Controller with swapped Left-Right/Up-Down/B-A",
DefaultExpansionDevice.ExcaliburSudokuPad => "Excalibur Sudoku Pad",
DefaultExpansionDevice.ABLPinball => "ABL Pinball",
DefaultExpansionDevice.GoldenNuggetCasinoExtraButtons => "Golden Nugget Casino extra buttons",
DefaultExpansionDevice.KedaKeyboard => "科达 (Kēdá) Keyboard",
DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4017",
DefaultExpansionDevice.PortTestController => "Port test controller",
DefaultExpansionDevice.BandaiMultiGamePlayerGamepad => "Bandai Multi Game Player Gamepad buttons",
DefaultExpansionDevice.VenomTVDanceMat => "Venom TV Dance Mat",
DefaultExpansionDevice.LGTVRemoteControl => "LG TV Remote Control",
DefaultExpansionDevice.FamicomNetworkController => "Famicom Network Controller",
DefaultExpansionDevice.KingFishingController => "King Fishing Controller",
DefaultExpansionDevice.CroakyKaraokeController => "Croaky Karaoke Controller",
DefaultExpansionDevice.KingwonKeyboard => "科王 (Kēwáng, a.k.a. Kingwon) Keyboard",
DefaultExpansionDevice.ZechengKeyboard => "泽诚 (Zéchéng) Keyboard",
DefaultExpansionDevice.SuborKeyboardPlusL90RotatedPS2Mouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus L90-rotated PS/2 mouse in $4017",
DefaultExpansionDevice.PS2KeyboardInUM6578PS2PortPS2Mouse4017 => "PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017",
DefaultExpansionDevice.PS2MouseInUM6578PS2Port => "PS/2 Mouse in UM6578 PS/2 port",
DefaultExpansionDevice.YuxingMouse4016 => "裕兴 (Yùxìng) Mouse via $4016",
DefaultExpansionDevice.SuborKeyboardPlusYuxingMouse4016 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng) Mouse mouse in $4016",
DefaultExpansionDevice.GigggleTVPump => "Gigggle TV Pump",
DefaultExpansionDevice.BBKKeyboardPlusR90RotatedPS2Mouse4017 => "步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017",
DefaultExpansionDevice.MagicalCooking => "Magical Cooking",
DefaultExpansionDevice.SNESMouse4017 => "SNES Mouse ($4017)",
DefaultExpansionDevice.Zapper4016 => "Zapper ($4016)",
DefaultExpansionDevice.ArkanoidVausControllerPrototype => "Arkanoid Vaus Controller (Prototype)",
DefaultExpansionDevice.TVMahjongGameController => "TV 麻雀 Game (TV Mahjong Game) Controller",
DefaultExpansionDevice.MahjongGekitouDensetsuController => "麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller",
DefaultExpansionDevice.SuborKeyboardPlusXInvertedPS2Mouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017",
DefaultExpansionDevice.IBMPCXTKeyboard => "IBM PC/XT Keyboard",
DefaultExpansionDevice.SuborKeyboardPlusMegaBookMouse => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Mega Book Mouse",
_ => $"Unknown {(byte)device}",
};
}
/// <summary>
/// Convert a <see cref="ExtendedConsoleType"/> value to string
/// </summary>
public static string FromExtendedConsoleType(this ExtendedConsoleType type)
{
return type switch
{
ExtendedConsoleType.RegularSystem => "Regular NES/Famicom/Dendy",
ExtendedConsoleType.NintendoVsSystem => "Nintendo Vs. System",
ExtendedConsoleType.Playchoice10 => "Playchoice 10",
ExtendedConsoleType.RegularFamicloneDecimalMode => "Regular Famiclone, but with CPU that supports Decimal Mode",
ExtendedConsoleType.RegularNESWithEPSM => "Regular NES/Famicom with EPSM module or plug-through cartridge",
ExtendedConsoleType.VRTechnologyVT01 => "V.R. Technology VT01 with red/cyan STN palette",
ExtendedConsoleType.VRTechnologyVT02 => "V.R. Technology VT02",
ExtendedConsoleType.VRTechnologyVT03 => "V.R. Technology VT03",
ExtendedConsoleType.VRTechnologyVT09 => "V.R. Technology VT09",
ExtendedConsoleType.VRTechnologyVT32 => "V.R. Technology VT32",
ExtendedConsoleType.VRTechnologyVT369 => "V.R. Technology VT369",
ExtendedConsoleType.UMCUM6578 => "UMC UM6578",
ExtendedConsoleType.FamicomNetworkSystem => "Famicom Network System",
ExtendedConsoleType.ReservedD => "Reserved (0x0D)",
ExtendedConsoleType.ReservedE => "Reserved (0x0E)",
ExtendedConsoleType.ReservedF => "Reserved (0x0F)",
_ => $"Unknown {(byte)type}",
};
}
/// <summary>
/// Convert a <see cref="NametableArrangement"/> value to string
/// </summary>
public static string FromNametableArrangement(this NametableArrangement arrangement)
{
return arrangement switch
{
NametableArrangement.Vertical => "Vertical",
NametableArrangement.Horizontal => "Horizontal",
_ => $"Unknown {(byte)arrangement}",
};
}
/// <summary>
/// Convert a <see cref="TVSystem"/> value to string
/// </summary>
public static string FromTVSystem(this TVSystem system)
{
return system switch
{
TVSystem.NTSC => "NTSC",
TVSystem.PAL => "PAL",
_ => $"Unknown {(byte)system}",
};
}
/// <summary>
/// Convert a <see cref="TVSystemExtended"/> value to string
/// </summary>
public static string FromTVSystemExtended(this TVSystemExtended system)
{
return system switch
{
TVSystemExtended.NTSC => "NTSC",
TVSystemExtended.DualCompatible1 => "Dual-compatible (0x01)",
TVSystemExtended.PAL => "PAL",
TVSystemExtended.DualCompatible3 => "Dual-compatible (0x03)",
_ => $"Unknown {(byte)system}",
};
}
/// <summary>
/// Convert a <see cref="VsHardwareType"/> value to string
/// </summary>
public static string FromVsHardwareType(this VsHardwareType type)
{
return type switch
{
VsHardwareType.VsUnisystem => "Vs. Unisystem (normal)",
VsHardwareType.VsUnisystemRBIBaseballProtection => "Vs. Unisystem (RBI Baseball protection)",
VsHardwareType.VsUnisystemTKOBoxingProtection => "Vs. Unisystem (TKO Boxing protection)",
VsHardwareType.VsUnisystemSuperXeviousProtection => "Vs. Unisystem (Super Xevious protection)",
VsHardwareType.VsUnisystemVsIceClimberJapanProtection => "Vs. Unisystem (Vs. Ice Climber Japan protection)",
VsHardwareType.VsDualSystem => "Vs. Dual System (normal)",
VsHardwareType.VsDualSystemRaidOnBungelingBayProtection => "Vs. Dual System (Raid on Bungeling Bay protection)",
_ => $"Unknown {(byte)type}",
};
}
/// <summary>
/// Convert a <see cref="VsSystemType"/> value to string
/// </summary>
public static string FromVsSystemType(this VsSystemType type)
{
return type switch
{
VsSystemType.AnyRP2C03RC2C03Variant => "Any RP2C03/RC2C03 variant",
VsSystemType.Reserved1 => "Reserved (0x01)",
VsSystemType.RP2C040001 => "RP2C04-0001",
VsSystemType.RP2C040002 => "RP2C04-0002",
VsSystemType.RP2C040003 => "RP2C04-0003",
VsSystemType.RP2C040004 => "RP2C04-0004",
VsSystemType.Reserved6 => "Reserved (0x06)",
VsSystemType.Reserved7 => "Reserved (0x07)",
VsSystemType.RC2C0501 => "RC2C05-01 (signature unknown)",
VsSystemType.RC2C0502 => "RC2C05-02 ($2002 AND $3F =$3D)",
VsSystemType.RC2C0503 => "RC2C05-03 ($2002 AND $1F =$1C)",
VsSystemType.RC2C0504 => "RC2C05-04 ($2002 AND $1F =$1B)",
VsSystemType.ReservedC => "Reserved (0x0C)",
VsSystemType.ReservedD => "Reserved (0x0D)",
VsSystemType.ReservedE => "Reserved (0x0E)",
VsSystemType.ReservedF => "Reserved (0x0F)",
_ => $"Unknown {(byte)type}",
};
}
}
}

View File

@@ -2,7 +2,7 @@ using SabreTools.Data.Models.NewExecutable;
namespace SabreTools.Data.Extensions
{
public static class NewExecutable
public static class NewExecutableExtensions
{
/// <summary>
/// Determine if a resource type information entry is an integer or offset

View File

@@ -0,0 +1,77 @@
using SabreTools.Data.Models.NintendoDisc;
using SabreTools.Numerics.Extensions;
namespace SabreTools.Data.Extensions
{
public static class NintendoDiscExtensions
{
/// <summary>
/// Get the platform associated with a disc header
/// </summary>
public static Platform GetPlatform(this DiscHeader header)
{
if (header.WiiMagic == Constants.WiiMagicWord)
return Platform.Wii;
else if (header.GCMagic == Constants.GCMagicWord)
return Platform.GameCube;
else if (header.GameId.Length >= 1 && IsGameCubeTitleType(header.GameId[0]))
return Platform.GameCube;
else
return Platform.Unknown;
}
/// <summary>
/// Get the platform associated with a disc header
/// </summary>
public static Platform GetPlatform(this byte[] header)
{
// Check for Wii magic bytes
if (header.Length >= 0x1C)
{
int offset = 0x18;
uint magic = header.ReadUInt32BigEndian(ref offset);
if (magic == Constants.WiiMagicWord)
return Platform.Wii;
}
// Check for GameCube magic bytes
if (header.Length >= 0x20)
{
int offset = 0x1C;
uint magic = header.ReadUInt32BigEndian(ref offset);
if (magic == Constants.GCMagicWord)
return Platform.GameCube;
}
// Check for a valid game ID
if (header.Length >= 4)
{
for (int i = 0; i < 4; i++)
{
byte c = header[i];
if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))
return Platform.Unknown;
}
return Platform.GameCube;
}
return Platform.Unknown;
}
/// <summary>
/// Returns true if the GameId first character is a known GameCube title type prefix.
/// Used as a fallback when the GC magic word is absent from the disc image.
/// </summary>
public static bool IsGameCubeTitleType(this char c)
=> c == 'G' || c == 'D' || c == 'R';
/// <summary>
/// Returns true if the byte is a known Nintendo disc title type code
/// (first byte of the 6-char GameId, e.g. 'G'=GameCube, 'R'=GameCube,
/// 'D'=GameCube demo, 'S'=Wii, 'F'=Wii channel)
/// </summary>
public static bool IsNintendoDiscTitleType(this byte b)
=> b == 'G' || b == 'D' || b == 'R' || b == 'S' || b == 'F';
}
}

View File

@@ -0,0 +1,78 @@
using SabreTools.Data.Models.OperaFS;
using SabreTools.Matching;
namespace SabreTools.Data.Extensions
{
public static class OperaFSExtensions
{
/// <summary>
/// Compare two DirectoryDescriptor objects
/// </summary>
/// <param name="dir1">The provided DirectoryDescriptor</param>
/// <param name="dir2">The DirectoryDescriptor to compare against</param>
/// <returns>True if all fields are equal, otherwise false</returns>
public static bool EqualsExactly(this DirectoryDescriptor dir1, DirectoryDescriptor dir2)
{
if (dir1.NextBlock != dir2.NextBlock)
return false;
if (dir1.PreviousBlock != dir2.PreviousBlock)
return false;
if (dir1.Flags != dir2.Flags)
return false;
if (dir1.FirstFreeByte != dir2.FirstFreeByte)
return false;
if (dir1.FirstEntryOffset != dir2.FirstEntryOffset)
return false;
if (dir1.DirectoryRecords.Length != dir2.DirectoryRecords.Length)
return false;
for (int i = 0; i < dir1.DirectoryRecords.Length; i++)
{
if (!dir1.DirectoryRecords[i].EqualsExactly(dir2.DirectoryRecords[i]))
return false;
}
return true;
}
/// <summary>
/// Compare two DirectoryRecord objects
/// </summary>
/// <param name="dir1">The provided DirectoryRecord</param>
/// <param name="dir2">The DirectoryRecord to compare against</param>
/// <returns>True if all fields are equal, otherwise false</returns>
public static bool EqualsExactly(this DirectoryRecord dr1, DirectoryRecord dr2)
{
if ((byte)dr1.DirectoryRecordFlags != (byte)dr2.DirectoryRecordFlags)
return false;
if (!dr1.UniqueIdentifier.EqualsExactly(dr2.UniqueIdentifier))
return false;
if (!dr1.Type.EqualsExactly(dr2.Type))
return false;
if (dr1.BlockSize != dr2.BlockSize)
return false;
if (dr1.ByteCount != dr2.ByteCount)
return false;
if (dr1.BlockCount != dr2.BlockCount)
return false;
if (dr1.Burst != dr2.Burst)
return false;
if (dr1.Gap != dr2.Gap)
return false;
if (!dr1.Filename.EqualsExactly(dr2.Filename))
return false;
if (dr1.LastAvatarIndex != dr2.LastAvatarIndex)
return false;
if (dr1.AvatarList.Length != dr2.AvatarList.Length)
return false;
for (int i = 0; i < dr1.AvatarList.Length; i++)
{
if (dr1.AvatarList[i] != dr2.AvatarList[i])
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using SabreTools.Data.Models.COFF;
namespace SabreTools.Data.Extensions
{
// TODO: Add tests
public static class PortableExecutableExtensions
{
/// <summary>
/// Convert a relative virtual address to a physical one
/// </summary>
/// <param name="rva">Relative virtual address to convert</param>
/// <param name="sections">Array of sections to check against</param>
/// <returns>Physical address, 0 on error</returns>
public static uint ConvertVirtualAddress(this uint rva, SectionHeader[] sections)
{
// If we have an invalid section table, we can't do anything
if (sections.Length == 0)
return 0;
// If the RVA is 0, we just return 0 because it's invalid
if (rva == 0)
return 0;
// If the RVA matches a section start exactly, use that
var matchingSection = Array.Find(sections, s => s.VirtualAddress == rva);
if (matchingSection is not null)
return rva - matchingSection.VirtualAddress + matchingSection.PointerToRawData;
// Loop through all of the sections
uint maxVirtualAddress = 0, maxRawPointer = 0;
for (int i = 0; i < sections.Length; i++)
{
// If the section "starts" at 0, just skip it
var section = sections[i];
if (section.PointerToRawData == 0)
continue;
// If the virtual address is greater than the RVA
if (rva < section.VirtualAddress)
continue;
// Cache the maximum matching section data, in case of a miss
if (rva >= section.VirtualAddress)
{
maxVirtualAddress = section.VirtualAddress;
maxRawPointer = section.PointerToRawData;
}
// Attempt to derive the physical address from the current section
if (section.VirtualSize != 0 && rva <= section.VirtualAddress + section.VirtualSize)
return rva - section.VirtualAddress + section.PointerToRawData;
else if (section.SizeOfRawData != 0 && rva <= section.VirtualAddress + section.SizeOfRawData)
return rva - section.VirtualAddress + section.PointerToRawData;
}
return maxRawPointer != 0 ? rva - maxVirtualAddress + maxRawPointer : 0;
}
/// <summary>
/// Find the section a revlative virtual address lives in
/// </summary>
/// <param name="rva">Relative virtual address to convert</param>
/// <param name="sections">Array of sections to check against</param>
/// <returns>Section index, null on error</returns>
public static int ContainingSectionIndex(this uint rva, SectionHeader[] sections)
{
// If we have an invalid section table, we can't do anything
if (sections is null || sections.Length == 0)
return -1;
// If the RVA is 0, we just return -1 because it's invalid
if (rva == 0)
return -1;
// Loop through all of the sections
for (int i = 0; i < sections.Length; i++)
{
// If the section "starts" at 0, just skip it
var section = sections[i];
if (section.PointerToRawData == 0)
continue;
// If the virtual address is greater than the RVA
if (rva < section.VirtualAddress)
continue;
// Attempt to derive the physical address from the current section
if (section.VirtualSize != 0 && rva <= section.VirtualAddress + section.VirtualSize)
return i;
else if (section.SizeOfRawData != 0 && rva <= section.VirtualAddress + section.SizeOfRawData)
return i;
}
return -1;
}
}
}

View File

@@ -0,0 +1,24 @@
# SabreTools.Data.Extensions
This library contains extension methods related to models. These extensions are used in both reader and wrapper code.
## Provided Extension Classes
| Class Name | Applicable Namespace |
| --- | --- |
| `Atari7800CartExtensions` | `SabreTools.Data.Models.Atari7800` |
| `AtariLynxCartExtensions` | `SabreTools.Data.Models.AtariLynx` |
| `BSPExtensions` | `SabreTools.Data.Models.BSP` |
| `CDROMExtensions` | `SabreTools.Data.Models.CDROM` |
| `CFBExtensions` | `SabreTools.Data.Models.CFB` |
| `InstallShieldCabinetExtensions` | `SabreTools.Data.Models.InstallShieldCabinet` |
| `ISO9660Extensions` | `SabreTools.Data.Models.ISO9660` |
| `MetadataExtensions` | `SabreTools.Data.Models.Metadata` |
| `NESCartExtensions` | `SabreTools.Data.Models.NES` |
| `NewExecutableExtensions` | `SabreTools.Data.Models.NewExecutable` |
| `PortableExecutableExtensions` | `SabreTools.Data.Models.COFF` |
| `TypeLengthValueExtensions` | `SabreTools.Data.Models.ASN1` and `SabreTools.ObjectIdentifier` |
| `VBSPExtensions` | `SabreTools.Data.Models.BSP` |
| `WiseScriptExtensions` | `SabreTools.Data.Models.WiseInstaller` |
| `XboxExecutableExtensions` | `SabreTools.Data.Models.XboxExecutable` |
| `XZExtensions` | `SabreTools.Data.Models.XZ` |

View File

@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<IncludeSymbols>true</IncludeSymbols>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>3.2.0</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
<Description>Extension methods related to models</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2026</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/SabreTools/SabreTools.Serialization</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>models extensions conversion</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NetLegacySupport.Numerics" Version="1.0.1" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`))" />
<PackageReference Include="SabreTools.IO" Version="2.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.Data.Models\SabreTools.Data.Models.csproj" />
<ProjectReference Include="..\SabreTools.ObjectIdentifier\SabreTools.ObjectIdentifier.csproj" />
</ItemGroup>
</Project>

View File

@@ -2,18 +2,18 @@ using System;
using System.Numerics;
using System.Text;
using SabreTools.Data.Models.ASN1;
using SabreTools.Data.ObjectIdentifier;
using SabreTools.ObjectIdentifier;
namespace SabreTools.Data.Extensions
{
public static class TypeLengthValue
public static class TypeLengthValueExtensions
{
/// <summary>
/// Format a TypeLengthValue as a string
/// </summary>
/// <param name="paddingLevel">Padding level of the item when formatting</param>
/// <returns>String representing the TypeLengthValue, if possible</returns>
public static string Format(this Data.Models.ASN1.TypeLengthValue tlv, int paddingLevel = 0)
public static string Format(this TypeLengthValue tlv, int paddingLevel = 0)
{
// Create the left-padding string
string padding = new(' ', paddingLevel);
@@ -38,7 +38,7 @@ namespace SabreTools.Data.Extensions
if (tlv.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
#endif
{
if (tlv.Value is not SabreTools.Data.Models.ASN1.TypeLengthValue[] valueAsObjectArray)
if (tlv.Value is not TypeLengthValue[] valueAsObjectArray)
{
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
return formatBuilder.ToString();
@@ -148,6 +148,27 @@ namespace SabreTools.Data.Extensions
formatBuilder.Append($", Value: {Encoding.Unicode.GetString(valueAsByteArray)}");
break;
// Unimplemented
case ASN1Type.V_ASN1_EOC:
case ASN1Type.V_ASN1_NULL:
case ASN1Type.V_ASN1_OBJECT_DESCRIPTOR:
case ASN1Type.V_ASN1_EXTERNAL:
case ASN1Type.V_ASN1_REAL:
case ASN1Type.V_ASN1_ENUMERATED:
case ASN1Type.V_ASN1_SEQUENCE:
case ASN1Type.V_ASN1_SET:
case ASN1Type.V_ASN1_NUMERICSTRING:
case ASN1Type.V_ASN1_VIDEOTEXSTRING:
case ASN1Type.V_ASN1_GENERALIZEDTIME:
case ASN1Type.V_ASN1_GRAPHICSTRING:
case ASN1Type.V_ASN1_ISO64STRING:
case ASN1Type.V_ASN1_GENERALSTRING:
case ASN1Type.V_ASN1_UNIVERSALSTRING:
case ASN1Type.V_ASN1_PRIMITIVE_TAG:
case ASN1Type.V_ASN1_CONSTRUCTED:
case ASN1Type.V_ASN1_APPLICATION:
case ASN1Type.V_ASN1_CONTEXT_SPECIFIC:
case ASN1Type.V_ASN1_PRIVATE:
default:
formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}");
break;

View File

@@ -0,0 +1,82 @@
using SabreTools.Data.Models.BSP;
namespace SabreTools.Data.Extensions
{
public static class VBSPExtensions
{
/// <summary>
/// Convert a <see cref="VbspLumpType"/> value to string
/// </summary>
public static string FromVbspLumpType(this VbspLumpType type)
{
return type switch
{
VbspLumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
VbspLumpType.LUMP_PLANES => " - LUMP_PLANES",
VbspLumpType.LUMP_TEXTURES => " - LUMP_TEXDATA",
VbspLumpType.LUMP_VERTICES => " - LUMP_VERTEXES",
VbspLumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
VbspLumpType.LUMP_NODES => " - LUMP_NODES",
VbspLumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
VbspLumpType.LUMP_FACES => " - LUMP_FACES",
VbspLumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
VbspLumpType.LUMP_CLIPNODES => " - LUMP_OCCLUSION",
VbspLumpType.LUMP_LEAVES => " - LUMP_LEAVES",
VbspLumpType.LUMP_MARKSURFACES => " - LUMP_FACEIDS",
VbspLumpType.LUMP_EDGES => " - LUMP_EDGES",
VbspLumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
VbspLumpType.LUMP_MODELS => " - LUMP_MODELS",
VbspLumpType.LUMP_WORLDLIGHTS => " - LUMP_WORLDLIGHTS",
VbspLumpType.LUMP_LEAFFACES => " - LUMP_LEAFFACES",
VbspLumpType.LUMP_LEAFBRUSHES => " - LUMP_LEAFBRUSHES",
VbspLumpType.LUMP_BRUSHES => " - LUMP_BRUSHES",
VbspLumpType.LUMP_BRUSHSIDES => " - LUMP_BRUSHSIDES",
VbspLumpType.LUMP_AREAS => " - LUMP_AREAS",
VbspLumpType.LUMP_AREAPORTALS => " - LUMP_AREAPORTALS",
VbspLumpType.LUMP_PORTALS => " - LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION",
VbspLumpType.LUMP_CLUSTERS => " - LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS",
VbspLumpType.LUMP_PORTALVERTS => " - LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS",
VbspLumpType.LUMP_CLUSTERPORTALS => " - LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS",
VbspLumpType.LUMP_DISPINFO => " - LUMP_DISPINFO",
VbspLumpType.LUMP_ORIGINALFACES => " - LUMP_ORIGINALFACES",
VbspLumpType.LUMP_PHYSDISP => " - LUMP_PHYSDISP",
VbspLumpType.LUMP_PHYSCOLLIDE => " - LUMP_PHYSCOLLIDE",
VbspLumpType.LUMP_VERTNORMALS => " - LUMP_VERTNORMALS",
VbspLumpType.LUMP_VERTNORMALINDICES => " - LUMP_VERTNORMALINDICES",
VbspLumpType.LUMP_DISP_LIGHTMAP_ALPHAS => " - LUMP_DISP_LIGHTMAP_ALPHAS",
VbspLumpType.LUMP_DISP_VERTS => " - LUMP_DISP_VERTS",
VbspLumpType.LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS => " - LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS",
VbspLumpType.LUMP_GAME_LUMP => " - LUMP_GAME_LUMP",
VbspLumpType.LUMP_LEAFWATERDATA => " - LUMP_LEAFWATERDATA",
VbspLumpType.LUMP_PRIMITIVES => " - LUMP_PRIMITIVES",
VbspLumpType.LUMP_PRIMVERTS => " - LUMP_PRIMVERTS",
VbspLumpType.LUMP_PRIMINDICES => " - LUMP_PRIMINDICES",
VbspLumpType.LUMP_PAKFILE => " - LUMP_PAKFILE",
VbspLumpType.LUMP_CLIPPORTALVERTS => " - LUMP_CLIPPORTALVERTS",
VbspLumpType.LUMP_CUBEMAPS => " - LUMP_CUBEMAPS",
VbspLumpType.LUMP_TEXDATA_STRING_DATA => " - LUMP_TEXDATA_STRING_DATA",
VbspLumpType.LUMP_TEXDATA_STRING_TABLE => " - LUMP_TEXDATA_STRING_TABLE",
VbspLumpType.LUMP_OVERLAYS => " - LUMP_OVERLAYS",
VbspLumpType.LUMP_LEAFMINDISTTOWATER => " - LUMP_LEAFMINDISTTOWATER",
VbspLumpType.LUMP_FACE_MACRO_TEXTURE_INFO => " - LUMP_FACE_MACRO_TEXTURE_INFO",
VbspLumpType.LUMP_DISP_TRIS => " - LUMP_DISP_TRIS",
VbspLumpType.LUMP_PHYSCOLLIDESURFACE => " - LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB",
VbspLumpType.LUMP_WATEROVERLAYS => " - LUMP_WATEROVERLAYS",
VbspLumpType.LUMP_LIGHTMAPPAGES => " - LUMP_LIGHTMAPPAGES / LUMP_LEAF_AMBIENT_INDEX_HDR",
VbspLumpType.LUMP_LIGHTMAPPAGEINFOS => " - LUMP_LIGHTMAPPAGEINFOS / LUMP_LEAF_AMBIENT_INDEX",
VbspLumpType.LUMP_LIGHTING_HDR => " - LUMP_LIGHTING_HDR",
VbspLumpType.LUMP_WORLDLIGHTS_HDR => " - LUMP_WORLDLIGHTS_HDR",
VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR => " - LUMP_LEAF_AMBIENT_LIGHTING_HDR",
VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING => " - LUMP_LEAF_AMBIENT_LIGHTING",
VbspLumpType.LUMP_XZIPPAKFILE => " - LUMP_XZIPPAKFILE",
VbspLumpType.LUMP_FACES_HDR => " - LUMP_FACES_HDR",
VbspLumpType.LUMP_MAP_FLAGS => " - LUMP_MAP_FLAGS",
VbspLumpType.LUMP_OVERLAY_FADES => " - LUMP_OVERLAY_FADES",
VbspLumpType.LUMP_OVERLAY_SYSTEM_LEVELS => " - LUMP_OVERLAY_SYSTEM_LEVELS",
VbspLumpType.LUMP_PHYSLEVEL => " - LUMP_PHYSLEVEL",
VbspLumpType.LUMP_DISP_MULTIBLEND => " - LUMP_DISP_MULTIBLEND",
_ => string.Empty,
};
}
}
}

View File

@@ -2,7 +2,7 @@ using System.Text.RegularExpressions;
namespace SabreTools.Data.Extensions
{
public static class WiseScript
public static class WiseScriptExtensions
{
/// <summary>
/// Convert a Wise function ID to the formal action name
@@ -57,8 +57,10 @@ namespace SabreTools.Data.Extensions
// External DLL
null => null,
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
_ => Regex.IsMatch(functionId, @"^f[0-9]{1,2}$") ? $"UNDEFINED {functionId}" : $"External: {functionId}",
#pragma warning restore SYSLIB1045
};
}
}
}
}

View File

@@ -2,7 +2,7 @@ using System.Collections.Generic;
namespace SabreTools.Data.Extensions
{
public static class XZ
public static class XZExtensions
{
/// <summary>
/// Decode a value from a variable-length integer

View File

@@ -0,0 +1,32 @@
using System;
using SabreTools.Numerics.Extensions;
namespace SabreTools.Data.Extensions
{
public static class XboxExecutableExtensions
{
/// <summary>
/// Convert a UInt32 to a formatted XBE title ID
/// </summary>
public static string ToFormattedXBETitleID(this uint value)
{
// Convert to a byte array
byte[] data = BitConverter.GetBytes(value);
// Create the prefix
string prefix1 = (0x41 <= data[3] && data[3] <= 0x5A) || (0x30 <= data[3] && data[3] <= 0x39)
? $"{(char)data[3]}"
: $"{data[3]:X2}";
string prefix2 = (0x41 <= data[2] && data[2] <= 0x5A) || (0x30 <= data[2] && data[2] <= 0x39)
? $"{(char)data[2]}"
: $"{data[2]:X2}";
// Create the serial
int offset = 0;
ushort serial = data.ReadUInt16LittleEndian(ref offset);
// Assemble and return
return $"{prefix1}{prefix2}-{serial:D3}";
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Text;
using SabreTools.Data.Models.ZArchive;
namespace SabreTools.Data.Extensions
{
public static class ZArchiveExtensions
{
/// <summary>
/// Retrieves the name of the specified node from the NameTable
/// </summary>
/// <param name="node">Node in the file tree</param>
/// <param name="nameTable">ZArchive NameTable</param>
/// <returns>UTF-8 string representing node's name</returns>
public static string? GetName(this FileDirectoryEntry node, NameTable nameTable)
{
// Check for a valid offset into the NameTable
uint nameOffset = node.NameOffsetAndTypeFlag & Constants.RootNode;
if (nameOffset == Constants.RootNode)
return null;
// Get the index into the name table
var index = Array.IndexOf(nameTable.NameTableOffsets, nameOffset);
if (index < 0)
return null;
// Get the name entry for the requested index
var nameEntry = nameTable.NameEntries[index];
if (nameEntry is null)
return null;
// Decode name to UTF-8
return Encoding.UTF8.GetString(nameEntry.NodeName);
}
}
}

View File

@@ -11,6 +11,6 @@ namespace SabreTools.Data.Models.AACS
/// Null-terminated ASCII string representing the copyright
/// </summary>
[MarshalAs(UnmanagedType.LPStr)]
public string? Copyright;
public string Copyright = string.Empty;
}
}
}

View File

@@ -20,6 +20,6 @@ namespace SabreTools.Data.Models.AACS
/// case of a non-zero Range value).
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[]? DriveID;
public byte[] DriveID = new byte[6];
}
}
}

View File

@@ -4,7 +4,7 @@ namespace SabreTools.Data.Models.AACS
/// A properly formatted type 3 or type 4 Media Key Block contains exactly
/// one Drive Revocation List Record. It follows the Host Revocation List
/// Record, although it may not immediately follow it.
///
///
/// The Drive Revocation List Record is identical to the Host Revocation
/// List Record, except it has type 2016, and it contains Drive Revocation
/// List Entries, not Host Revocation List Entries. The Drive Revocation List
@@ -21,6 +21,6 @@ namespace SabreTools.Data.Models.AACS
/// <summary>
/// Revocation list entries
/// </summary>
public DriveRevocationSignatureBlock[]? SignatureBlocks { get; set; }
public DriveRevocationSignatureBlock[] SignatureBlocks { get; set; } = [];
}
}
}

View File

@@ -12,6 +12,6 @@ namespace SabreTools.Data.Models.AACS
/// A list of 8-byte Host Drive List Entry fields, the length of this
/// list being equal to the number in the signature block.
/// </summary>
public DriveRevocationListEntry[]? EntryFields { get; set; }
public DriveRevocationListEntry[] EntryFields { get; set; } = [];
}
}
}

View File

@@ -18,6 +18,7 @@ namespace SabreTools.Data.Models.AACS
/// determines that the signature does not verify or is omitted, it
/// must refuse to use the Media Key.
/// </summary>
public byte[]? SignatureData { get; set; }
/// <remarks><see cref="RecordLength"/>-4 bytes</remarks>
public byte[] SignatureData { get; set; } = [];
}
}
}

View File

@@ -66,4 +66,4 @@ namespace SabreTools.Data.Models.AACS
VerifyMediaKey_AACS2 = 0x86,
EmptyRecord0xF8_AACS2 = 0xF8,
}
}
}

View File

@@ -6,6 +6,6 @@ namespace SabreTools.Data.Models.AACS
/// <summary>
/// In this record, each subset-difference is encoded with 5 bytes.
/// </summary>
public SubsetDifference[]? SubsetDifferences { get; set; }
public SubsetDifference[] SubsetDifferences { get; set; } = [];
}
}
}

View File

@@ -8,6 +8,6 @@ namespace SabreTools.Data.Models.AACS
/// <summary>
/// Unparsed data comprising the record after the header
/// </summary>
public byte[]? Data { get; set; }
public byte[] Data { get; set; } = [];
}
}
}

View File

@@ -20,6 +20,6 @@ namespace SabreTools.Data.Models.AACS
/// Range value).
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[]? HostID;
public byte[] HostID = new byte[6];
}
}
}

View File

@@ -24,6 +24,6 @@ namespace SabreTools.Data.Models.AACS
/// <summary>
/// Revocation list entries
/// </summary>
public HostRevocationSignatureBlock[]? SignatureBlocks { get; set; }
public HostRevocationSignatureBlock[] SignatureBlocks { get; set; } = [];
}
}
}

View File

@@ -12,6 +12,6 @@ namespace SabreTools.Data.Models.AACS
/// A list of 8-byte Host Revocation List Entry fields, the length of this
/// list being equal to the number in the signature block.
/// </summary>
public HostRevocationListEntry[]? EntryFields { get; set; }
public HostRevocationListEntry[] EntryFields { get; set; } = [];
}
}
}

View File

@@ -9,6 +9,6 @@ namespace SabreTools.Data.Models.AACS
/// <summary>
/// Records
/// </summary>
public Record[]? Records { get; set; }
public Record[] Records { get; set; } = [];
}
}
}

View File

@@ -13,6 +13,6 @@ namespace SabreTools.Data.Models.AACS
/// record. This 16 bytes is the ciphertext value C in the media
/// key calculation.
/// </summary>
public byte[][]? MediaKeyData { get; set; }
public byte[][] MediaKeyData { get; set; } = [];
}
}
}

View File

@@ -1,9 +1,11 @@
using SabreTools.Numerics;
namespace SabreTools.Data.Models.AACS
{
/// <summary>
/// Each Record begins with a one-byte Record Type field, followed by a
/// three-byte Record Length field.
///
/// three-byte Record Length field.
///
/// The following subsections describe the currently defined Record types,
/// and how a device processes each. All multi-byte integers, including
/// the length field, are “Big Endian”; in other words, the most significant
@@ -23,6 +25,6 @@ namespace SabreTools.Data.Models.AACS
/// fields themselves. Record lengths are always multiples of 4 bytes.
/// </summary>
// <remarks>UInt24 not UInt32</remarks>
public uint RecordLength { get; set; }
public UInt24 RecordLength { get; set; } = new();
}
}
}

View File

@@ -20,4 +20,4 @@ namespace SabreTools.Data.Models.AACS
/// </summary>
public uint Number;
}
}
}

View File

@@ -21,6 +21,6 @@ namespace SabreTools.Data.Models.AACS
/// Subset-Difference record, with 0 being the start of the record.
/// </summary>
// <remarks>UInt24 not UInt32</remarks>
public uint[]? Offsets { get; set; }
public uint[] Offsets { get; set; } = [];
}
}
}

View File

@@ -29,4 +29,4 @@ namespace SabreTools.Data.Models.AACS
/// </summary>
public uint VersionNumber { get; set; }
}
}
}

View File

@@ -19,6 +19,7 @@ namespace SabreTools.Data.Models.AACS
/// where 0xXXXXXXXXXXXXXXXX is an arbitrary 8-byte value, and Km is
/// the correct final Media Key value.
/// </summary>
public byte[]? CiphertextValue { get; set; }
/// <remarks>0x10 bytes</remarks>
public byte[] CiphertextValue { get; set; } = new byte[0x10];
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
#pragma warning disable CA1069 // Enums values should not be duplicated
namespace SabreTools.Data.Models.ASN1
{
/// <summary>

View File

@@ -6,4 +6,4 @@ namespace SabreTools.Data.Models.AdvancedInstaller
public const string SignatureString = "ADVINSTSFX";
}
}
}

View File

@@ -56,6 +56,6 @@ namespace SabreTools.Data.Models.AdvancedInstaller
/// <summary>
/// Unicode-encoded file name
/// </summary>
public string? Name { get; set; }
public string Name { get; set; } = string.Empty;
}
}

View File

@@ -32,7 +32,7 @@ namespace SabreTools.Data.Models.AdvancedInstaller
/// </summary>
/// <remarks>
/// Only seen when the preceeding two fields exist
///
///
/// Observed values:
/// - 01 00 00 00
/// </remarks>
@@ -80,7 +80,7 @@ namespace SabreTools.Data.Models.AdvancedInstaller
/// Hex string that looks like a key or other identifier
/// </summary>
/// <remarks>32 bytes</remarks>
public string? HexString { get; set; }
public string HexString { get; set; } = string.Empty;
/// <summary>
/// Unknown
@@ -89,7 +89,7 @@ namespace SabreTools.Data.Models.AdvancedInstaller
/// Offset pointer to <see cref="FileDataStart"/>
/// relative to the end of the signature if no filename
/// exists.
///
///
/// Observed values:
/// - 32 00 00 00 (No original filename)
/// - 13 02 00 00 (Original filename)
@@ -99,7 +99,7 @@ namespace SabreTools.Data.Models.AdvancedInstaller
/// <summary>
/// "ADVINSTSFX"
/// </summary>
public string? Signature { get; set; }
public string Signature { get; set; } = string.Empty;
/// <summary>
/// Unknown, always 0? Padding?

View File

@@ -5,12 +5,12 @@ namespace SabreTools.Data.Models.AdvancedInstaller
/// Advanced Installer SFX file. These SFX files store
/// all files uncompressed sequentially in the overlay
/// of an executable.
///
///
/// The design is similar to the end of central directory
/// in a PKZIP file. The footer needs to be read before
/// the entry table as both the pointer to the start of
/// the table as well as the entry count are included there.
///
///
/// The layout of this is derived from the layout in the
/// physical file.
/// </summary>
@@ -19,11 +19,11 @@ namespace SabreTools.Data.Models.AdvancedInstaller
/// <summary>
/// Set of file entries
/// </summary>
public FileEntry[]? Entries { get; set; }
public FileEntry[] Entries { get; set; } = [];
/// <summary>
/// Footer representing the central directory
/// </summary>
public Footer? Footer { get; set; }
public Footer Footer { get; set; } = new();
}
}

View File

@@ -1,6 +1,7 @@
using System.Xml;
using System.Xml.Serialization;
#pragma warning disable IDE1006 // Naming Styles
namespace SabreTools.Data.Models.ArchiveDotOrg
{
[XmlRoot("file")]
@@ -32,7 +33,7 @@ namespace SabreTools.Data.Models.ArchiveDotOrg
public string? SHA1 { get; set; }
[XmlElement("filecount")]
public string? FileCount { get; set; }
public long? FileCount { get; set; }
/// <remarks>Is this a set of defined values?</remarks>
[XmlElement("format")]
@@ -202,4 +203,4 @@ namespace SabreTools.Data.Models.ArchiveDotOrg
#endregion
}
}
}

View File

@@ -9,4 +9,4 @@ namespace SabreTools.Data.Models.ArchiveDotOrg
[XmlElement("file")]
public File[]? File { get; set; }
}
}
}

View File

@@ -0,0 +1,20 @@
namespace SabreTools.Data.Models.Atari7800
{
/// <summary>
/// Atari 7800 headered cart
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
public class Cart
{
/// <summary>
/// A78 header
/// </summary>
/// <remarks>If omitted, format is technically BIN</remarks>
public Header? Header { get; set; }
/// <summary>
/// Cartridge data
/// </summary>
public byte[] Data { get; set; } = [];
}
}

View File

@@ -0,0 +1,49 @@
namespace SabreTools.Data.Models.Atari7800
{
public static class Constants
{
/// <summary>
/// A78 magic string (null-padded)
/// </summary>
public static readonly byte[] MagicBytesWithNull =
[
0x41, 0x54, 0x41, 0x52, 0x49, 0x37, 0x38, 0x30,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
/// <summary>
/// A78 magic string (null-padded)
/// </summary>
public const string MagicStringWithNull = "ATARI7800\0\0\0\0\0\0\0";
/// <summary>
/// A78 magic string (space-padded)
/// </summary>
public static readonly byte[] MagicBytesWithSpace =
[
0x41, 0x54, 0x41, 0x52, 0x49, 0x37, 0x38, 0x30,
0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
];
/// <summary>
/// A78 magic string (space-padded)
/// </summary>
public const string MagicStringWithSpace = "ATARI7800 ";
/// <summary>
/// A78 header end magic text
/// </summary>
public static readonly byte[] HeaderEndMagicBytes =
[
0x41, 0x43, 0x54, 0x55, 0x41, 0x4C, 0x20, 0x43,
0x41, 0x52, 0x54, 0x20, 0x44, 0x41, 0x54, 0x41,
0x20, 0x53, 0x54, 0x41, 0x52, 0x54, 0x53, 0x20,
0x48, 0x45, 0x52, 0x45,
];
/// <summary>
/// A78 header end magic text
/// </summary>
public const string HeaderEndMagicString = "ACTUAL CART DATA STARTS HERE";
}
}

View File

@@ -0,0 +1,380 @@
using System;
namespace SabreTools.Data.Models.Atari7800
{
/// <summary>
/// Audio device (v4+ only)
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
[Flags]
public enum AudioDevice : ushort
{
#region POKEY location
/// <summary>
/// POKEY - none
/// </summary>
PokeyNone = 0b00000000,
/// <summary>
/// POKEY - @440
/// </summary>
Pokey440 = 0b00000001,
/// <summary>
/// POKEY - @450
/// </summary>
Pokey450 = 0b00000010,
/// <summary>
/// POKEY - @450+@440
/// </summary>
Pokey450Plus440 = 0b00000011,
/// <summary>
/// POKEY - @800
/// </summary>
Pokey800 = 0b00000100,
/// <summary>
/// POKEY - @4000
/// </summary>
Pokey4000 = 0b00000101,
#endregion
/// <summary>
/// YM2151 @460
/// </summary>
YM2151460 = 0b00001000,
/// <summary>
/// COVOX @430
/// </summary>
COVOX430 = 0b00010000,
/// <summary>
/// ADPCM Audio Stream @420
/// </summary>
ADPCMAudioStream420 = 0b00100000,
}
/// <summary>
/// Cart type specifications
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
[Flags]
public enum CartType : ushort
{
/// <summary>
/// pokey at $4000
/// </summary>
PokeyAt4000 = 0b00000000_00000001,
/// <summary>
/// supergame bank switched
/// </summary>
SupergameBankSwitched = 0b00000000_00000010,
/// <summary>
/// supergame ram at $4000
/// </summary>
SupergameRamAt4000 = 0b00000000_00000100,
/// <summary>
/// rom at $4000
/// </summary>
RomAt4000 = 0b00000000_00001000,
/// <summary>
/// bank 6 at $4000
/// </summary>
Bank6At4000 = 0b00000000_00010000,
/// <summary>
/// banked ram
/// </summary>
BankedRam = 0b00000000_00100000,
/// <summary>
/// pokey at $450
/// </summary>
PokeyAt450 = 0b00000000_01000000,
/// <summary>
/// mirror ram at $4000
/// </summary>
MirrorRamAt4000 = 0b00000000_10000000,
/// <summary>
/// activision banking
/// </summary>
ActivisionBanking = 0b00000001_00000000,
/// <summary>
/// absolute banking
/// </summary>
AbsoluteBanking = 0b00000010_00000000,
/// <summary>
/// pokey at $440
/// </summary>
PokeyAt440 = 0b00000100_00000000,
/// <summary>
/// ym2151 at $460/$461
/// </summary>
Ym2151At460461 = 0b00001000_00000000,
/// <summary>
/// souper
/// </summary>
Souper = 0b00010000_00000000,
/// <summary>
/// banksets
/// </summary>
Banksets = 0b00100000_00000000,
/// <summary>
/// halt banked ram
/// </summary>
HaltBankedRam = 0b01000000_00000000,
/// <summary>
/// pokey@800
/// </summary>
PokeyAt800 = 0b10000000_00000000,
}
/// <summary>
/// Controller type
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
public enum ControllerType : byte
{
/// <summary>
/// none
/// </summary>
None = 0,
/// <summary>
/// 7800 joystick
/// </summary>
Joystick = 1,
/// <summary>
/// lightgun
/// </summary>
Lightgun = 2,
/// <summary>
/// paddle
/// </summary>
Paddle = 3,
/// <summary>
/// trakball
/// </summary>
Trakball = 4,
/// <summary>
/// 2600 joystick
/// </summary>
VcsJoystick = 5,
/// <summary>
/// 2600 driving
/// </summary>
VcsDriving = 6,
/// <summary>
/// 2600 keypad
/// </summary>
VcsKeypad = 7,
/// <summary>
/// ST mouse
/// </summary>
STMouse = 8,
/// <summary>
/// Amiga mouse
/// </summary>
AmigaMouse = 9,
/// <summary>
/// AtariVox/SaveKey
/// </summary>
AtariVoxSaveKey = 10,
/// <summary>
/// SNES2Atari
/// </summary>
SNES2Atari = 11,
/// <summary>
/// Mega7800
/// </summary>
Mega7800 = 12,
}
/// <summary>
/// Interrupt (v4+ only)
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
[Flags]
public enum Interrupt : ushort
{
None = 0b00000000,
/// <summary>
/// POKEY 1 (@450 | @800 | @4000)
/// </summary>
Pokey1 = 0b00000001,
/// <summary>
/// POKEY 2 (@440)
/// </summary>
Pokey2 = 0b00000010,
/// <summary>
/// YM2151
/// </summary>
YM2151 = 0b00000100,
}
/// <summary>
/// Mapper (v4+ only)
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
public enum Mapper : byte
{
Linear = 0,
SuperGame = 1,
Activision = 2,
Absolute = 3,
Souper = 4,
}
/// <summary>
/// Mapper options/details (v4+ only)
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
[Flags]
public enum MapperOptions : byte
{
#region Linear
/// <summary>
/// Option at @4000 - none
/// </summary>
LinearNone = 0b00000000,
/// <summary>
/// Option at @4000 - 16K RAM
/// </summary>
Linear16KRAM = 0b00000001,
/// <summary>
/// Option at @4000 - 8K EXRAM/A8
/// </summary>
Linear8KEXRAMA8 = 0b00000010,
/// <summary>
/// Option at @4000 - 32K EXRAM/M2
/// </summary>
Linear32KEXRAMM2 = 0b00000011,
#endregion
#region SuperGame
/// <summary>
/// Option at @4000 - none
/// </summary>
SuperGameNone = 0b00000000,
/// <summary>
/// Option at @4000 - 16K RAM
/// </summary>
SuperGame16KRAM = 0b00000001,
/// <summary>
/// Option at @4000 - 8K EXRAM/A8
/// </summary>
SuperGame8KEXRAMA8 = 0b00000010,
/// <summary>
/// Option at @4000 - 32K EXRAM/M2
/// </summary>
SuperGame32KEXRAMM2 = 0b00000011,
/// <summary>
/// Option at @4000 - EXROM
/// </summary>
SuperGameEXROM = 0b00000100,
/// <summary>
/// Option at @4000 - EXFIX
/// </summary>
SuperGameEXFIX = 0b00000101,
/// <summary>
/// Option at @4000 - 32k EXRAM/X2
/// </summary>
SuperGame32KEXRAMX2 = 0b00000110,
#endregion
/// <summary>
/// Bankset ROM
/// </summary>
BanksetRom = 0b10000000,
}
/// <summary>
/// Save device
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
public enum SaveDevice : byte
{
None = 0b00000000,
/// <summary>
/// HSC
/// </summary>
HSC = 0b00000001,
/// <summary>
/// SaveKey/AtariVox
/// </summary>
SaveKeyAtariVox = 0b00000010,
}
/// <summary>
/// Slot passthrough device
/// </summary>
public enum SlotPassthroughDevice : byte
{
None = 0b00000000,
XM = 0b00000001,
}
/// <summary>
/// TV type
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
[Flags]
public enum TVType : byte
{
NTSC = 0b00000000,
PAL = 0b00000001,
Component = 0b00000000,
Composite = 0b00000010,
SingleRegion = 0b00000000,
MultiRegion = 0b00000100,
}
}

View File

@@ -0,0 +1,115 @@
namespace SabreTools.Data.Models.Atari7800
{
/// <summary>
/// Atari 7800 emulator header
/// </summary>
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
public class Header
{
/// <summary>
/// Header version
/// </summary>
/// <remarks>
/// Current header version is 4.
/// If no V4 features are used, this should be set to 3.
/// </remarks>
public byte HeaderVersion { get; set; }
/// <summary>
/// "ATARI7800" with 7 null bytes following
/// </summary>
public byte[] MagicText { get; set; } = new byte[16];
/// <summary>
/// Cart title, encoding not specified
/// </summary>
public byte[] CartTitle { get; set; } = new byte[32];
/// <summary>
/// ROM size without header data
/// </summary>
/// <remarks>
/// Endianness possibly varies by version?
/// Version 1 - Big-endian
/// Version 2 - ?????
/// Version 3 - Documented as little-endian (unconfirmed)
/// Version 4 - Documented as little-endian (unconfirmed)
/// </remarks>
public uint RomSizeWithoutHeader { get; set; }
/// <summary>
/// Cart type flags
/// </summary>
public CartType CartType { get; set; }
/// <summary>
/// Controller 1 type
/// </summary>
public ControllerType Controller1Type { get; set; }
/// <summary>
/// Controller 2 type
/// </summary>
public ControllerType Controller2Type { get; set; }
/// <summary>
/// TV type
/// </summary>
public TVType TVType { get; set; }
/// <summary>
/// Save device
/// </summary>
public SaveDevice SaveDevice { get; set; }
/// <summary>
/// Reserved
/// </summary>
public byte[] Reserved { get; set; } = new byte[4];
/// <summary>
/// Slot passthrough device
/// </summary>
public SlotPassthroughDevice SlotPassthroughDevice { get; set; }
#region Version 4+
/// <summary>
/// Mapper
/// </summary>
/// <remarks>v4+ only</remarks>
public Mapper Mapper { get; set; }
/// <summary>
/// Mapper options
/// </summary>
/// <remarks>v4+ only</remarks>
public MapperOptions MapperOptions { get; set; }
/// <summary>
/// Audio device(s)
/// </summary>
public AudioDevice AudioDevice { get; set; }
/// <summary>
/// Interrupt
/// </summary>
public Interrupt Interrupt { get; set; }
#endregion
/// <summary>
/// Padding from end of header data to end magic text
/// </summary>
/// <remarks>
/// 36 bytes in versions lower than 4.
/// 30 bytes in versions 4 and above.
/// </remarks>
public byte[] Padding { get; set; } = [];
/// <summary>
/// "ACTUAL CART DATA STARTS HERE"
/// </summary>
public byte[] EndMagicText { get; set; } = new byte[28];
}
}

View File

@@ -0,0 +1,20 @@
namespace SabreTools.Data.Models.AtariLynx
{
/// <summary>
/// Atari Lynx headered cart (LNX)
/// </summary>
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
public class Cart
{
/// <summary>
/// LNX header
/// </summary>
/// <remarks>If omitted, format is technically LYX</remarks>
public Header? Header { get; set; }
/// <summary>
/// Cartridge data
/// </summary>
public byte[] Data { get; set; } = [];
}
}

View File

@@ -0,0 +1,15 @@
namespace SabreTools.Data.Models.AtariLynx
{
public static class Constants
{
/// <summary>
/// LNX magic string
/// </summary>
public static readonly byte[] MagicBytes = [0x4C, 0x59, 0x4E, 0x58];
/// <summary>
/// LNX magic string
/// </summary>
public const string MagicString = "LYNX";
}
}

View File

@@ -0,0 +1,24 @@
namespace SabreTools.Data.Models.AtariLynx
{
/// <summary>
/// Screen rotation
/// </summary>
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
public enum Rotation : byte
{
/// <summary>
/// No rotation (horizontal, buttons right)
/// </summary>
NoRotation = 0,
/// <summary>
/// Rotate left (vertical, buttons down)
/// </summary>
RotateLeft = 1,
/// <summary>
/// Rotate right (vertical, buttons up)
/// </summary>
RotateRight = 2,
}
}

View File

@@ -0,0 +1,54 @@
namespace SabreTools.Data.Models.AtariLynx
{
/// <summary>
/// Atari Lynx emulator header
/// </summary>
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
public class Header
{
/// <summary>
/// "LYNX"
/// </summary>
/// <remarks>4 bytes</remarks>
public byte[] Magic { get; set; } = new byte[4];
/// <summary>
/// Page size for bank 0
/// </summary>
public ushort Bank0PageSize { get; set; }
/// <summary>
/// Page size for bank 1
/// </summary>
public ushort Bank1PageSize { get; set; }
/// <summary>
/// Header version
/// </summary>
/// <remarks>Must be 0x0001</remarks>
public ushort Version { get; set; }
/// <summary>
/// Game title (Null-padded ASCII)
/// </summary>
/// <remarks>32 bytes</remarks>
public byte[] CartName { get; set; } = new byte[32];
/// <summary>
/// Publisher name
/// </summary>
/// <remarks>16 bytes</remarks>
public byte[] Manufacturer { get; set; } = new byte[16];
/// <summary>
/// Screen rotation
/// </summary>
public Rotation Rotation { get; set; }
/// <summary>
/// Padding
/// </summary>
/// <remarks>5 bytes</remarks>
public byte[] Spare { get; set; } = new byte[5];
}
}

View File

@@ -1,5 +1,3 @@
using SabreTools.Data.Attributes;
namespace SabreTools.Data.Models.AttractMode
{
/// <summary>
@@ -12,4 +10,4 @@ namespace SabreTools.Data.Models.AttractMode
public Row[]? Row { get; set; }
}
}
}

View File

@@ -1,5 +1,3 @@
using SabreTools.Data.Attributes;
namespace SabreTools.Data.Models.AttractMode
{
public class Row
@@ -48,6 +46,6 @@ namespace SabreTools.Data.Models.AttractMode
public string? PlayedTime { get; set; }
public string? FileIsAvailable { get; set; }
public bool? FileIsAvailable { get; set; }
}
}
}

View File

@@ -6,4 +6,4 @@ namespace SabreTools.Data.Models.BDPlus
public const string SignatureString = "BDSVM_CC";
}
}
}

View File

@@ -9,13 +9,13 @@ namespace SabreTools.Data.Models.BDPlus
/// "BDSVM_CC"
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string? Signature;
public string Signature = string.Empty;
/// <summary>
/// Unknown data
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[]? Unknown1 = new byte[5];
public byte[] Unknown1 = new byte[5];
/// <summary>
/// Version year
@@ -43,8 +43,8 @@ namespace SabreTools.Data.Models.BDPlus
public uint Length;
/// <summary>
/// Length bytes of data
/// <see cref="Length"> bytes of data
/// </summary>
public byte[]? Data { get; set; }
public byte[] Data { get; set; } = [];
}
}
}

View File

@@ -9,11 +9,11 @@
/// <summary>
/// Header
/// </summary>
public Header? Header { get; set; }
public Header Header { get; set; } = new();
/// <summary>
/// Files
/// </summary>
public FileEntry[]? Files { get; set; }
public FileEntry[] Files { get; set; } = [];
}
}

View File

@@ -8,4 +8,4 @@ namespace SabreTools.Data.Models.BFPK
public const uint SignatureUInt32 = 0x4b504642;
}
}
}

View File

@@ -14,7 +14,7 @@
/// <summary>
/// Name
/// </summary>
public string? Name { get; set; }
public string Name { get; set; } = string.Empty;
/// <summary>
/// Uncompressed size

View File

@@ -13,7 +13,7 @@ namespace SabreTools.Data.Models.BFPK
/// "BFPK"
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string? Magic;
public string Magic = string.Empty;
/// <summary>
/// Version

View File

@@ -6,6 +6,6 @@ namespace SabreTools.Data.Models.BSP
/// <summary>
/// Indicies
/// </summary>
public LeafAmbientIndex[]? Indicies { get; set; }
public LeafAmbientIndex[] Indicies { get; set; } = [];
}
}
}

View File

@@ -6,6 +6,6 @@ namespace SabreTools.Data.Models.BSP
/// <summary>
/// Lightings
/// </summary>
public LeafAmbientLighting[]? Lightings { get; set; }
public LeafAmbientLighting[] Lightings { get; set; } = [];
}
}
}

View File

@@ -30,4 +30,4 @@ namespace SabreTools.Data.Models.BSP
/// </summary>
public VbspContents Contents;
}
}
}

View File

@@ -6,6 +6,6 @@ namespace SabreTools.Data.Models.BSP
/// <summary>
/// Brushes
/// </summary>
public Brush[]? Brushes { get; set; }
public Brush[] Brushes { get; set; } = [];
}
}
}

View File

@@ -8,7 +8,7 @@ namespace SabreTools.Data.Models.BSP
/// references into the texture and displacement info lumps.
/// Bevel is zero for normal brush sides, but 1 if the side is
/// a bevel plane (which seem to be used for collision detection).
///
///
/// Unlike the face array, brushsides are not culled (removed)
/// where they touch the void. Void-facing sides do however have
/// their texinfo entry changed to the tools/toolsnodraw texture
@@ -17,7 +17,7 @@ namespace SabreTools.Data.Models.BSP
/// entries which are used to render that brush. Brushsides are
/// used by the engine to calculate all player physics collision
/// with world brushes. (Vphysics objects use lump 29 instead.)
///
///
/// The maximum number of brushsides is 65536 (MAX_MAP_BRUSHSIDES).
/// The maximum number of brushsides on a single brush is 128 (MAX_BRUSH_SIDES).
/// </summary>
@@ -45,4 +45,4 @@ namespace SabreTools.Data.Models.BSP
/// </summary>
public short Bevel;
}
}
}

Some files were not shown because too many files have changed in this diff Show More