Working instructions for AI/LLM sessions in this repository. Read this first; the
docs/ folder is the authoritative project documentation (also published as the
GitHub wiki and GitHub Pages).
Batch Convert to CHD - a cross-platform (Windows/Linux/macOS) Avalonia desktop
app that batch converts disc images (cue/iso/img/ccd/mds/pbp/cso/isz/ecm/split
sets/archives) to CHD using a bundled chdman.exe with a managed CHDSharp
fallback. The solution also contains six library projects and an xUnit test
project.
BatchConvertToCHDis the base app project: it multi-targetsnet10.0(Linux/macOS) andnet10.0-windows(Windows), buildsBatchConvertToCHD.exe, and links no files from outside its folder. The Windows TFM adds NAudio (Media Foundation/ACM MP3 decoding); the neutral TFM decodes MP3 viaffmpegon PATH.- UI-free code (
AppConfig.cs,Models,Utilities,Services) lives in the app project alongside the UI layer (MainWindow,AboutWindow,App,ScreenshotService). There is no separate WPF front end any more. - Encoder model: CHD creation uses the in-process CHDSharp library
(
Services/ChdSharpEncoderService.cs) on every platform. On Windows the bundledchdmanis preferred and the built-in encoder is the automatic fallback; on Linux/macOS the built-in encoder is always used. - Tool discovery is platform-aware: bundled tools next to the app come first
(Windows:
chdman/7za; Linux/macOS:7zz), thenchdman,7z/7za/7zzonPATH. There is no bundled CHDSharp CLI any more. - The custom title bar, status colors and terminal log are styled in
App.axaml.
- .NET SDK 10 - declared in
global.json(rollForward: latestMajor). Never lower the Windows target belownet10.0-windows; the Windows UI and NAudio need it. - Node.js 24 - used only for the zero-dependency helper scripts in
scripts/ci/. Do not add npm packages or apackage.json. - Avalonia 12.1 (desktop, Fluent theme, DataGrid); the neutral TFM must keep building without Windows-only packages so Linux/macOS publishes stay clean.
dotnet restore CSharp_BatchConvertToCHD.sln
dotnet build CSharp_BatchConvertToCHD.sln -c Release
dotnet test BatchConvertToCHD.Tests/BatchConvertToCHD.Tests.csproj -c Release
# CI runs the unit tests only: the [Trait("Category", "Integration")] classes
# read sample folders that exist on this machine (e.g. D:\Emulators\...) but
# not on GitHub runners. Run the full command above before a release.
dotnet test BatchConvertToCHD.Tests/BatchConvertToCHD.Tests.csproj -c Release --filter "Category!=Integration"
# Framework-dependent single-file publish (one per architecture)
dotnet publish BatchConvertToCHD/BatchConvertToCHD.csproj -c Release -f net10.0-windows -r win-x64 --self-contained false -p:PublishSingleFile=true -o publish/win-x64
dotnet publish BatchConvertToCHD/BatchConvertToCHD.csproj -c Release -f net10.0-windows -r win-arm64 --self-contained false -p:PublishSingleFile=true -o publish/win-arm64
# Release zip (same command CI runs)
./scripts/ci/package-release.ps1 -Rid win-x64 -Version 3.7.0 -PublishDir publish/win-x64 -OutputDir dist
# Run on Windows (multi-targeted, so pick the Windows TFM)
dotnet run --project BatchConvertToCHD/BatchConvertToCHD.csproj -f net10.0-windows
# Framework-dependent publishes for Linux/macOS (neutral TFM)
dotnet publish BatchConvertToCHD/BatchConvertToCHD.csproj -c Release -f net10.0 -r linux-x64 --self-contained false -o publish/linux-x64
dotnet publish BatchConvertToCHD/BatchConvertToCHD.csproj -c Release -f net10.0 -r osx-arm64 --self-contained false -o publish/osx-arm64- The app is framework-dependent. It must NOT embed the .NET runtime. Users
install the .NET 10 Desktop Runtime. Always publish with
--self-contained false -p:PublishSingleFile=true. The output is a singleBatchConvertToCHD.exe(plus the bundled tool exes, which are content files and stay outside the bundle). - Release zips contain exactly one architecture's tools. For
win-x64:7za.exe,chdman.exe. Forwin-arm64: the*_arm64.exevariants.scripts/ci/package-release.ps1removes the other architecture, the library.xmlIntelliSense files and the native.pdbdebug symbols (never used at runtime), and addsLICENSE.txtandReadMe.md. Do not put both architectures in one zip. CHD creation works without any bundled tool thanks to the built-in CHDSharp encoder. - Zip naming is fixed:
release_<version>_win-<rid>.zip, e.g.release_3.7.0_win-x64.zip. One zip per architecture, both attached to the GitHub release. - Version lives in two csproj files (
BatchConvertToCHDandBatchConvertToCHD.Tests,AssemblyVersion/FileVersion) plus a matching section inWhatsNew.md. A release tag isrelease_<version>and must match the csproj version -scripts/ci/version.mjsenforces this in CI. - Cutting a release: bump both csproj versions, add the
## <version>section to the top ofWhatsNew.md, commit tomaster, thengit tag release_<version> && git push origin release_<version>. TheReleaseworkflow tests, publishes both RIDs, zips them, and creates the GitHub release (title = version, body = theWhatsNew.mdsection). Re-running the workflow uploads assets with--clobber.
PBPSharp (https://www.nuget.org/packages/PBPSharp), CSOSharp
(https://www.nuget.org/packages/CSOSharp), CCDSharp
(https://www.nuget.org/packages/CCDSharp), MDSSharp
(https://www.nuget.org/packages/MDSSharp) and ISZSharp
(https://www.nuget.org/packages/ISZSharp) are published as NuGet packages.
Releases are manual only: do not add pack/push steps to the solution CI
workflows, and never commit or echo the API key. It is read from the
NUGET_API_KEY user environment variable.
- Version lives in the project file (
PBPSharp/PBPSharp.csproj,CSOSharp/CSOSharp.csproj,CCDSharp/CCDSharp.csproj,MDSSharp/MDSSharp.csprojandISZSharp/ISZSharp.csproj): bump<Version>,<AssemblyVersion>and<FileVersion>together. A pushed version is immutable - to change anything, bump and push again. - Target frameworks are
net8.0;net9.0;net10.0so both packages serve .NET 8, 9 and 10 consumers.GenerateDocumentationFilemust stay on so each TFM ships its.xmlnext to the assembly. - Metadata must keep
PackageProjectUrlandRepositoryUrlpointed at https://github.com/purelogiccode/BatchConvertToCHD. - README:
<Project>/README.mdis the package readme (packed at the package root asREADME.md). Keep it descriptive with usage examples and an API reference, and update it whenever the public surface changes. - Icon:
<Project>/icon/icon.pngis packed asicon.pngthrough itsNoneitem. It must stay under NuGet's 1 MB icon limit (currently ~280-300 KB at 600x600).icon.icostays in the repo only; do not pack a generated or resized copy. - XML docs: every public member and, per this repo's convention, every method including private/internal ones carries documentation. The build must stay warning-free.
Release procedure (run from the repo root; substitute the project and package name for the library being published):
# 1. bump the three version fields in <project>.csproj, then:
dotnet build PBPSharp/PBPSharp.csproj -c Release # or CSOSharp/CSOSharp.csproj, CCDSharp/CCDSharp.csproj
# library tests plus real-file integration tests (needs the local sample folder)
dotnet test BatchConvertToCHD.Tests/BatchConvertToCHD.Tests.csproj -c Release --filter "FullyQualifiedName~Pbp" # or ~Cso
# (CCDSharp has no dedicated test class yet; run the full suite when it changes)
# 2. pack with package validation enabled
dotnet pack PBPSharp/PBPSharp.csproj -c Release -o <out-dir> -p:EnablePackageValidation=true
# 3. inspect the nupkg: README.md, icon.png, and lib/<tfm>/<name>.dll + .xml
# for net8.0, net9.0 and net10.0
# 4. push with the user environment key (never hard-code it)
dotnet nuget push <out-dir>/PBPSharp.<version>.nupkg --api-key $env:NUGET_API_KEY --source https://api.nuget.org/v3/index.json
# 5. verify indexing (takes a few minutes)
Invoke-RestMethod https://api.nuget.org/v3-flatcontainer/pbpsharp/index.json # or csosharp, ccdsharpBefore pushing, smoke-test the packed .nupkg in a throwaway consumer project
targeting net8.0;net9.0;net10.0 (restore from a local package source and open
a real file on each runtime).
| Workflow | Trigger | Purpose |
|---|---|---|
.github/workflows/ci.yml |
push/PR to master, manual |
Restore, build, test; uploads trx results |
.github/workflows/release.yml |
tag release_*, manual with tag |
Validate version, test, package x64+arm64 zips, publish GitHub release |
.github/workflows/docs.yml |
push to master, manual |
Build/deploy Jekyll Pages site and sync docs/ to the GitHub wiki |
All workflows set up Node 24 where scripts run. Use the latest action majors
(actions/checkout@v7, setup-dotnet@v6, setup-node@v7, artifacts v7/v8) so
the runner's Node 24 runtime is used.
docs/is the single source of truth for both. Never edit the wiki in the GitHub UI.scripts/ci/sync-wiki.mjscopiesdocs/*.mdto the wiki (index.mdbecomesHome.md, Jekyll front matter is stripped), addsWhatsNew.md(front matter stripped too), and syncsdocs/_Sidebar.mdas the wiki side menu.WhatsNew.mdlives at the repository root and carries Jekyll front matter for the Pages site; the docs workflow stages a copy intodocs/before the Jekyll build (Jekyll ignores the underscore-prefixed_Sidebar.md).- The wiki push needs a repository secret named
WIKI_TOKEN(classic PAT with thereposcope, or fine-grained PAT withContents: Read and write) becauseGITHUB_TOKENcannot push to the wiki repository. Without the secret the sync step logs a message and skips. - Pages deployment requires Settings -> Pages -> Source: "GitHub Actions".
docs/_config.ymluses thejust-the-docsremote theme; keep the front matter (title,nav_order) on docs pages for navigation.
- Match existing code style;
DebugTypeisembeddedand analyzers (Meziantou, Roslynator) run on every build. Do not add code comments unless asked. - Bundled binaries are committed in
BatchConvertToCHD/: Windows7za*.exeandchdman*.exe, andtools/7zz_*(official 7-Zip console builds for Linux/macOS, copied next to the app as7zzfor the matching RID). They are copied to the output withCopyToOutputDirectory=Always; only replace them deliberately and keeptools/7-Zip-License.txt. BatchConvertToCHD/bin/Release/is the local release archive: every version'srelease_<version>_win-<rid>.ziplives there, beside the per-TFM build output. Copy new zips in, never delete files inside that path (also avoid commands that would clean it).- Tests are xUnit; add regression tests next to the existing ones in
BatchConvertToCHD.Tests/. The suite must pass before a release.[Trait("Category", "Integration")]classes depend on local sample folders and are excluded from CI with--filter "Category!=Integration".