Skip to content

10 libraries

github-actions[bot] edited this page Sep 16, 2026 · 6 revisions

10. Embedded Libraries

The solution ships five in-house libraries that replace external tools (maxcso, psxpackager), add CloneCD support, and cover Alcohol 120% and UltraISO images. The app references them as project references. Four of them are published as NuGet packages for outside consumers — PBPSharp (https://www.nuget.org/packages/PBPSharp, 1.1.1), CSOSharp (https://www.nuget.org/packages/CSOSharp, 1.0.0), CCDSharp (https://www.nuget.org/packages/CCDSharp, 1.0.0) and MDSSharp (https://www.nuget.org/packages/MDSSharp, 1.1.0) — and those four multi-target net8.0;net9.0;net10.0, each shipping its XML docs next to the assembly; releases are manual (see the repository's AGENTS.md). UltraIsoSharp multi-targets net10.0;net8.0 and is packable. All five expose internals to BatchConvertToCHD.Tests via InternalsVisibleTo. A sixth in-house library, CHDSharp, is consumed as a NuGet package and is covered in §10.4.

Library Purpose Replaces
CCDSharp CloneCD .ccd/.img/.sub parsing + CUE/BIN conversion — (new capability)
CSOSharp CSO/CISO decompression (deflate/zlib + LZ4) maxcso.exe
PBPSharp PlayStation PBP extraction + SFO/TOC parsing psxpackager.exe
MDSSharp Alcohol 120% .mds/.mdf parsing, subchannel stripping, split-volume joining, cue writing — (new capability)
UltraIsoSharp UltraISO ISZ decompression back to the plain image — (new capability)

10.1 CCDSharp

Purpose: read CloneCD disc-image sets (.ccd descriptor + .img data + optional .sub subchannel) and convert them to CUE/BIN for chdman.

  • Main type: CcdConverterParse(inputFile) returns a parsed disc model (DiscImage with ImgFilePath, subchannel info, track table); ConvertToCueBin(inputFile, tempCuePath) writes the CUE/BIN pair.
  • Integration: ProcessCcdFileForConversionAsync (MainWindow.xaml.cs:1702) parses the .ccd, converts to CUE/BIN in a temp dir, then converts the cue with chdman. On success the .ccd/.img/.sub/.cdt set is deleted when "delete originals" is enabled.
  • Archive extractions skip .img files that belong to a .ccd set to avoid double conversion (MainWindow.xaml.cs:1562–1573).
  • Failure messages are prefixed "CCDSharp: Conversion error" and are excluded from bug reports.
  • Reference sources live under References/ (ccd2cue-master, ccd2iso-main, myccd2cue-main) — third-party material used to build the library, not part of the build.
  • Testing note: the test project does not reference CCDSharp, so there are currently no CCDSharp unit tests (see Testing).

10.2 CSOSharp

Purpose: read and decompress CISO (Compressed ISO, .cso) images.

  • Main type: CsoFileOpen(path/stream, out CsoFile) returns a CsoError; exposes UncompressedSize, block metadata, ReadBlock, ExtractToIso(path, progress?, token), and a seekable CsoStream implementing the stream contract.
  • Supports v1 and v2 headers, deflate/zlib and LZ4 compression (K4os.Compression.LZ4 dependency).
  • Integration: ArchiveService.ExtractCsoAsync (Services/ArchiveService.cs:52) decompresses to a temp ISO for the conversion pipeline.
  • Error enum: CsoError { None, FileNotFound, InvalidHeader, UnsupportedVersion, InvalidBlockSize, ... }.
  • Tests: CsoFileTests, CsoStreamTests, CsoHeaderTests, plus byte-for-byte integration tests against real .cso/.iso pairs (CsoFileIntegrationTests).

10.3 PBPSharp

Purpose: parse PlayStation PBP (PSP/PSX eboot) containers and extract PlayStation disc images to CUE/BIN.

  • Main type: PbpFileOpen(path, out PbpFile) / Open(stream, ownsStream, out PbpFile); properties Header, SfoData, Discs (IReadOnlyList<PbpDiscInfo>), IsMultiDisc, Title, DiscId, Category.
  • Header: magic 0x50425000, 40-byte header with offsets for SFO/ICON0/ICON1/PIC0/PIC1/SND0/DATA.PSP/DATA.PSAR.
  • Disc detection: PSAR header PSISOIMG0000 → single disc; PSTITLEIMG000000 → multi-disc (reads 5 position slots at +0x200); anything else → PbpError.InvalidPsarHeader (the app treats this as "not a PlayStation disc image — PSP application, unsupported variant, or corrupt file" and skips informatively). The four "template" DWORDs of the PSTITLEIMG header (fixed values in popstation/PSX2PSP/iPoPS output) are not validated: pop-fe writes zeros there and its multi-disc PBPs parse from the position table alone.
  • PbpDiscInfoReadBlock, ExtractTo(stream, progress?, token), ExtractToBinCue(binPath, cuePath?, progress?, token); TOC parsed from the PSAR TOC (A0/A1/A2 markers, BCD track numbers, best effort — a bad TOC never aborts extraction). A disc container whose header parsed but that carries no ISO index entries throws NoIsoIndexException (public, derives from Exception) so callers can report the likely cause: a truncated or incomplete download.
  • Index entries (32 bytes at PSAR+0x4000, data at PSAR+0x100000) are read in both authoring layouts: the popstation/PSX2PSP/iPoPS layout writes the block size as a 32-bit int at bytes 4–7, the pop-fe layout writes a 16-bit size at bytes 4–5, a stored-flag byte at 6 (bit 0 = uncompressed block) and a SHA-1 at 8–23. Block offsets use 64-bit math so multi-gigabyte files cannot overflow. Corrupt entries are rejected with InvalidDataException (oversized lengths, stored blocks larger than a full 16-sector block).
  • Block decompression uses SharpZipLib's raw Inflater (the same decompressor the popstation reference implementation uses for PSAR blocks, windowBits −15); a block that fails raw inflation is retried as a zlib-wrapped stream (2-byte header + Adler-32, as written by tools using zlib.compress). Blocks flagged stored, or exactly one full block (16 × 0x930) in size, are copied verbatim. A failed block surfaces as PbpError.DecompressionError.
  • CueSheetWriter.GenerateCueSheet(binFileName, tocEntries) — emits FILE ... BINARY, TRACK nn MODE2/2352 (data) / AUDIO (audio), with INDEX 00 for audio tracks computed as track start minus 150-frame lead-in (clamped ≥ 0).
  • SFO model: SfoData (magic 0x46535000; GetString/GetUInt32; Size — the total SFO section size derived from the largest data entry, populated on parse; static Keys with BOOTABLE, CATEGORY, DISC_ID, DISC_VERSION, LICENSE, PARENTAL_LEVEL, PSP_SYSTEM_VER, REGION, TITLE), SfoEntry (formats 0x0204 string / 0x0404 uint32), TocEntry, TrackType { Data = 0x41, Audio = 0x01 }.
  • SFO parsing is best effort: a missing or corrupt PARAM.SFO (bad magic, malformed table, offsets beyond EOF) leaves Title/DiscId null with empty Entries instead of failing Open — none of the reference tools read the SFO when extracting disc images.
  • PbpError enum: None=0, InvalidHeader=1, FileNotFound=2, IoError=3, CorruptFile=4, InvalidPsarHeader=5, DiscOutOfRange=6, ResourceNotFound=7, DecompressionError=8, TruncatedPsar=9, InvalidSfo=10. TruncatedPsar is returned when the PSAR container parses but no ISO index follows (see NoIsoIndexException). InvalidSfo is retained for API compatibility but is no longer returned by Open (SFO problems are tolerated). The app maps these to targeted guidance ("most likely truncated or incomplete — re-download") instead of a generic corrupt-file message.
  • Block decompression uses SharpZipLib's raw Inflater (the same decompressor the popstation reference implementation uses for PSAR blocks), which tolerates a few streams the stricter .NET DeflateStream rejects; a failed block surfaces as PbpError.DecompressionError.
  • Integration: ExtractPbpToCueBinAsync (MainWindow.xaml.cs:2959) — multi-disc PBPs produce "{name} - Disc N.bin/.cue" sets; the result (PbpExtractionResult) carries ErrorCode + a human-readable Error so the caller can distinguish skippable conditions from real failures.
  • Tests: PbpFileTests, PbpHeaderTests, SfoDataTests, SfoEntryTests, TocEntryTests, CueSheetWriterTests, plus real-file integration tests (PbpFileIntegrationTests).

10.4 CHDSharp (NuGet)

Purpose: pure C# CHD (Compressed Hunks of Data) reading, verification, extraction, and creation — the engine behind the app's extraction and verification tabs.

  • Consumed as a NuGet package (CHDSharp v1.4.3), not a project reference; the app also bundles the project's CLI (CHDSharp.exe) and MAME's chdman.exe side by side, preferring the native-architecture binary on ARM64.
  • Capabilities: CHD V1–V5, all 10 compression codecs (zlib, lzma, huffman, flac, zstd, avhu + CD variants), parent/child chaining, parallel verification, and full CHD creation (createcd/createdvd/createhd/copy) with output that is byte-identical to chdman.
  • The byte-parity claim was validated by the (since-removed) CHDBattleTest battleground project — see Testing §11.6 — which reported zero mismatches against chdman 0.289 across decode, encode, and cross-verification battles on a 56-disc corpus.
  • In the conversion pipeline CHDSharp is the automatic fallback: the bundled chdman is the primary encoder, and a file that chdman cannot convert is retried with CHDSharp.exe — see Conversion Pipeline §5.3.
  • When the library cannot decode a CHD (corrupt file, A/V laserdisc), the app falls back to chdman for extraction — see Extraction & Verification.

10.5 MDSSharp

Purpose: turn an Alcohol 120% image (.mds descriptor + .mdf data, including split .i00/.i01 volumes) into something the encoder can read.

  • Main types: MdsParser (IsMdsFile, Parse), MdsMedium, MdsDisc/MdsTrack (parsed model with medium type, sector-size classification and pregap/length fields), MdsInputPreparer (PrepareAsync → cue / DVD image / failure; StripSubchannelAsync, WriteCueAsync, FormatMsf), and SplitImageJoiner (TryGetVolumeSet, JoinAsync, GetTotalBytes for .001/.i00 volume sets).
  • Reads the medium type, the track extra blocks (pregap/length) and the footer blocks that name the data files (single-byte or UTF-16), so renamed and multi-file descriptors resolve without guessing; several declared files are joined in order.
  • Track modes follow libmirage's reverse engineering (low nibble, folded by 8): audio, Mode 1 and the Mode 2 forms; CD media with 2048-byte sectors becomes a MODE1/2048 cue, DVD media stays a direct image.
  • Pregaps the data file does not contain are rebuilt as zeros into a .pregap.bin so INDEX 00 can be written; pregaps already in the file are referenced in place.
  • The three preparation shapes (plain 2352, subchannel strip, ISO-as-DVD) plus the cooked-CD and pregap paths are documented in Utilities Reference §8.12.
  • Integration: ProcessMdsFileForConversionAsync prepares the work set, then the conversion funnel takes the cue (or DVD image).
  • Tests: MdsTests.cs, SplitImageJoinerTests.cs.

10.6 UltraIsoSharp

Purpose: decompress UltraISO .isz images back to the plain images they were made from, per EZB Systems' ISZ File Format Specification 1.00.

  • Main types: IszHeader (packed 48-byte header with TryRead validation), IszDecoder (TryReadHeaderAsync, DecodeAsync), IszSegment/IszChunkType/IszDecodeResult.
  • Supports whole and multi-segment images, zlib / bzip2 / stored / zero-elided chunks; encryption is refused by name, truncation and damaged tables are reported rather than guessed at.
  • Integration: ResolveIszAsync decodes the ISZ to a temp image, which is then classified and converted like any other image.
  • Tests: IszHeaderTests.cs, IszDecoderTests.cs.

Clone this wiki locally