| title | Embedded Libraries |
|---|---|
| nav_order | 11 |
The solution ships five in-house libraries that replace external tools (maxcso, psxpackager), add CloneCD support, and cover Alcohol 120% and UltraISO images. All five multi-target net10.0;net8.0, are packable, and 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 |
| Alcohol120Sharp | 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) |
Purpose: read CloneCD disc-image sets (.ccd descriptor + .img data + optional .sub subchannel) and convert them to CUE/BIN for chdman.
- Main type:
CcdConverter—Parse(inputFile)returns a parsed disc model (DiscImagewithImgFilePath, 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/.cdtset is deleted when "delete originals" is enabled. - Archive extractions skip
.imgfiles that belong to a.ccdset 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).
Purpose: read and decompress CISO (Compressed ISO, .cso) images.
- Main type:
CsoFile—Open(path/stream, out CsoFile)returns aCsoError; exposesUncompressedSize, block metadata,ReadBlock,ExtractToIso(path, progress?, token), and a seekableCsoStreamimplementing the stream contract. - Supports v1 and v2 headers, deflate/zlib and LZ4 compression (
K4os.Compression.LZ4dependency). - 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/.isopairs (CsoFileIntegrationTests).
Purpose: parse PlayStation PBP (PSP/PSX eboot) containers and extract PlayStation disc images to CUE/BIN.
- Main type:
PbpFile—Open(path, out PbpFile)/Open(stream, ownsStream, out PbpFile); propertiesHeader,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 thePSTITLEIMGheader (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. PbpDiscInfo—ReadBlock,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 throwsNoIsoIndexException(public, derives fromException) 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 usingzlib.compress). Blocks flagged stored, or exactly one full block (16 × 0x930) in size, are copied verbatim. A failed block surfaces asPbpError.DecompressionError. CueSheetWriter.GenerateCueSheet(binFileName, tocEntries)— emitsFILE ... BINARY,TRACK nn MODE2/2352(data) /AUDIO(audio), withINDEX 00for audio tracks computed as track start minus 150-frame lead-in (clamped ≥ 0).- SFO model:
SfoData(magic0x46535000;GetString/GetUInt32; staticKeyswithBOOTABLE,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/DiscIdnull with emptyEntriesinstead of failingOpen— none of the reference tools read the SFO when extracting disc images. PbpErrorenum:None=0, InvalidHeader=1, FileNotFound=2, IoError=3, CorruptFile=4, InvalidPsarHeader=5, DiscOutOfRange=6, ResourceNotFound=7, DecompressionError=8, TruncatedPsar=9, InvalidSfo=10.TruncatedPsaris returned when the PSAR container parses but no ISO index follows (seeNoIsoIndexException).InvalidSfois retained for API compatibility but is no longer returned byOpen(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 .NETDeflateStreamrejects; a failed block surfaces asPbpError.DecompressionError. - Integration:
ExtractPbpToCueBinAsync(MainWindow.xaml.cs:2959) — multi-disc PBPs produce"{name} - Disc N.bin/.cue"sets; the result (PbpExtractionResult) carriesErrorCode+ a human-readableErrorso the caller can distinguish skippable conditions from real failures. - Tests:
PbpFileTests,PbpHeaderTests,SfoDataTests,SfoEntryTests,TocEntryTests,CueSheetWriterTests, plus real-file integration tests (PbpFileIntegrationTests).
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 (
CHDSharpv1.4.3), not a project reference; the app also bundles the project's CLI (CHDSharp.exe) and MAME'schdman.exeside 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 tochdman. - The byte-parity claim was validated by the (since-removed)
CHDBattleTestbattleground project — see Testing §11.6 — which reported zero mismatches againstchdman0.289 across decode, encode, and cross-verification battles on a 56-disc corpus. - In the conversion pipeline CHDSharp is the automatic fallback: the bundled
chdmanis the primary encoder, and a file that chdman cannot convert is retried withCHDSharp.exe— see Conversion Pipeline §5.3. - When the library cannot decode a CHD (corrupt file, A/V laserdisc), the app falls back to
chdmanfor extraction — see Extraction & Verification.
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),MdsDisc/MdsTrack(parsed model with sector-size classification),MdsInputPreparer(PrepareAsync→ cue / DVD image / failure;StripSubchannelAsync,WriteCueAsync,FormatMsf), andSplitImageJoiner(TryGetVolumeSet,JoinAsync,GetTotalBytesfor.001/.i00volume sets). - The three preparation shapes (plain 2352, subchannel strip, ISO-as-DVD) and the recovered
.mdslayout are documented in Utilities Reference §8.12. - Integration:
ProcessMdsFileForConversionAsyncprepares the work set, then the conversion funnel takes the cue (or DVD image). - Tests:
MdsTests.cs,SplitImageJoinerTests.cs.
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 withTryReadvalidation),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:
ResolveIszAsyncdecodes the ISZ to a temp image, which is then classified and converted like any other image. - Tests:
IszHeaderTests.cs,IszDecoderTests.cs.