diff --git a/Copper6510/Copper6510.csproj b/Copper6510/Copper6510.csproj
index 3d8bf50..ceebe48 100644
--- a/Copper6510/Copper6510.csproj
+++ b/Copper6510/Copper6510.csproj
@@ -7,7 +7,7 @@
1701;1702;NU1900
Copper6510
Copper6510
- CopperMod contributors
+ Ilkka Lehtoranta
Reusable MOS 6510 CPU emulation core with cycle-aware bus callbacks and common undocumented opcode support.
1.1.0
MIT
diff --git a/CopperDisk/CopperDisk.csproj b/CopperDisk/CopperDisk.csproj
index d04742b..813135d 100644
--- a/CopperDisk/CopperDisk.csproj
+++ b/CopperDisk/CopperDisk.csproj
@@ -7,7 +7,7 @@
true
CopperDisk
CopperDisk
- CopperMod contributors
+ Ilkka Lehtoranta
Managed Amiga ADF, extended ADF, ADZ, DMS, IPF, and SCP disk image library.
2.1.0
MIT
diff --git a/CopperMod.Abstractions/CopperMod.Abstractions.csproj b/CopperMod.Abstractions/CopperMod.Abstractions.csproj
index 7365665..46ee801 100644
--- a/CopperMod.Abstractions/CopperMod.Abstractions.csproj
+++ b/CopperMod.Abstractions/CopperMod.Abstractions.csproj
@@ -6,7 +6,7 @@
true
CopperMod.Abstractions
CopperMod.Abstractions
- CopperMod contributors
+ Ilkka Lehtoranta
Reusable contracts for tracker and chip music format detection, loading, seeking, and PCM rendering.
1.0.0
MIT
diff --git a/CopperMod.Amiga.Emulator/CopperMod.Amiga.Emulator.csproj b/CopperMod.Amiga.Emulator/CopperMod.Amiga.Emulator.csproj
index edde8ac..c0d16f0 100644
--- a/CopperMod.Amiga.Emulator/CopperMod.Amiga.Emulator.csproj
+++ b/CopperMod.Amiga.Emulator/CopperMod.Amiga.Emulator.csproj
@@ -6,7 +6,7 @@
true
CopperMod.Amiga.Emulator
CopperMod.Amiga.Emulator
- CopperMod contributors
+ Ilkka Lehtoranta
Full Amiga emulator host services, boot helpers, and disk media adapters layered on CopperMod.Amiga.
1.1.0
MIT
diff --git a/CopperMod.Cust/CopperMod.Cust.csproj b/CopperMod.Cust/CopperMod.Cust.csproj
index e416067..eacc85c 100644
--- a/CopperMod.Cust/CopperMod.Cust.csproj
+++ b/CopperMod.Cust/CopperMod.Cust.csproj
@@ -6,7 +6,7 @@
true
CopperMod.Cust
CopperMod.Cust
- CopperMod contributors
+ Ilkka Lehtoranta
Native Amiga CUST Hunk loader with an A500 PAL MC68000/Paula playback sandbox.
1.1.0
MIT
diff --git a/CopperMod.Med/CopperMod.Med.csproj b/CopperMod.Med/CopperMod.Med.csproj
index 5858020..02b7a03 100644
--- a/CopperMod.Med/CopperMod.Med.csproj
+++ b/CopperMod.Med/CopperMod.Med.csproj
@@ -6,7 +6,7 @@
true
CopperMod.Med
CopperMod.Med
- CopperMod contributors
+ Ilkka Lehtoranta
MED/OctaMED MMD module parser and tick renderer for Amiga tracker modules.
1.0.0
MIT
diff --git a/CopperMod.ProTracker/CopperMod.ProTracker.csproj b/CopperMod.ProTracker/CopperMod.ProTracker.csproj
index 4aa4a3e..4c5b6fb 100644
--- a/CopperMod.ProTracker/CopperMod.ProTracker.csproj
+++ b/CopperMod.ProTracker/CopperMod.ProTracker.csproj
@@ -6,7 +6,7 @@
true
CopperMod.ProTracker
CopperMod.ProTracker
- CopperMod contributors
+ Ilkka Lehtoranta
ProTracker MOD parser and tick renderer for Amiga tracker modules.
1.0.0
MIT
diff --git a/CopperMod.Sid/CopperMod.Sid.csproj b/CopperMod.Sid/CopperMod.Sid.csproj
index 66cded2..52d0438 100644
--- a/CopperMod.Sid/CopperMod.Sid.csproj
+++ b/CopperMod.Sid/CopperMod.Sid.csproj
@@ -6,7 +6,7 @@
true
CopperMod.Sid
CopperMod.Sid
- CopperMod contributors
+ Ilkka Lehtoranta
PSID/RSID parser and register-scheduled SID renderer.
1.1.0
MIT
diff --git a/CopperPad/CopperPad.csproj b/CopperPad/CopperPad.csproj
index 47af142..5fbce98 100644
--- a/CopperPad/CopperPad.csproj
+++ b/CopperPad/CopperPad.csproj
@@ -7,7 +7,7 @@
true
CopperPad
CopperPad
- CopperMod contributors
+ Ilkka Lehtoranta
Portable .NET game controller normalization library.
1.0.0
MIT
diff --git a/README.md b/README.md
index 6376136..86eb6af 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,13 @@ CopperDisk package automation lives under `scripts\nuget`:
.\scripts\nuget\publish-copperdisk.ps1 -PackagePath .\artifacts\packages\CopperDisk.1.0.0.nupkg -WhatIf
```
+CopperPad is staged for future NuGet publishing with the same script layout:
+
+```powershell
+.\scripts\nuget\pack-copperpad.ps1
+.\scripts\nuget\publish-copperpad.ps1 -PackagePath .\artifacts\packages\CopperPad.1.0.0.nupkg -WhatIf
+```
+
## Run
```powershell
diff --git a/scripts/nuget/pack-copperpad.ps1 b/scripts/nuget/pack-copperpad.ps1
new file mode 100644
index 0000000..7d08776
--- /dev/null
+++ b/scripts/nuget/pack-copperpad.ps1
@@ -0,0 +1,124 @@
+param(
+ [string] $Configuration = "Release",
+ [string] $OutputDirectory = ".\artifacts\packages",
+ [string] $Version,
+ [switch] $NoRestore,
+ [switch] $SkipTests,
+ [switch] $SkipPackageContentValidation
+)
+
+$ErrorActionPreference = "Stop"
+
+function Invoke-DotNet {
+ param(
+ [string[]] $Arguments
+ )
+
+ & dotnet @Arguments
+ if ($LASTEXITCODE -ne 0) {
+ throw "dotnet $($Arguments -join ' ') failed with exit code $LASTEXITCODE."
+ }
+}
+
+$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..")
+$project = Join-Path $repoRoot "CopperPad\CopperPad.csproj"
+$testProject = Join-Path $repoRoot "CopperPad.Tests\CopperPad.Tests.csproj"
+
+if ([System.IO.Path]::IsPathRooted($OutputDirectory)) {
+ $packageDir = $OutputDirectory
+}
+else {
+ $packageDir = Join-Path $repoRoot $OutputDirectory
+}
+
+New-Item -ItemType Directory -Force -Path $packageDir | Out-Null
+
+if (-not $NoRestore) {
+ Invoke-DotNet -Arguments @("restore", $project)
+ Invoke-DotNet -Arguments @("restore", $testProject)
+}
+
+Invoke-DotNet -Arguments @("build", $project, "-c", $Configuration, "--no-restore")
+
+if (-not $SkipTests) {
+ Invoke-DotNet -Arguments @("test", $testProject, "-c", $Configuration, "--no-restore")
+}
+
+$packProperties = @("/p:EnablePackageValidation=true")
+if ($Version) {
+ $packProperties += "/p:PackageVersion=$Version"
+}
+
+$packArguments = @("pack", $project, "-c", $Configuration, "--no-restore", "-o", $packageDir) + $packProperties
+Invoke-DotNet -Arguments $packArguments
+
+if (-not $Version) {
+ [xml] $projectXml = Get-Content $project
+ $Version = ($projectXml.Project.PropertyGroup |
+ Where-Object { $_.PackageVersion } |
+ Select-Object -First 1).PackageVersion
+}
+
+$packagePath = Join-Path $packageDir "CopperPad.$Version.nupkg"
+$symbolPackagePath = Join-Path $packageDir "CopperPad.$Version.snupkg"
+
+if (-not (Test-Path $packagePath)) {
+ throw "Package was not created at '$packagePath'."
+}
+
+if (-not $SkipPackageContentValidation) {
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
+ $zip = [System.IO.Compression.ZipFile]::OpenRead($packagePath)
+ try {
+ $requiredEntries = @(
+ "README.md",
+ "lib/net10.0/CopperPad.dll",
+ "lib/net10.0/CopperPad.xml"
+ )
+
+ foreach ($entryName in $requiredEntries) {
+ if (-not $zip.GetEntry($entryName)) {
+ throw "Package '$packagePath' is missing required entry '$entryName'."
+ }
+ }
+
+ $readmeReader = New-Object System.IO.StreamReader($zip.GetEntry("README.md").Open())
+ try {
+ $readmeFirstLine = $readmeReader.ReadLine()
+ }
+ finally {
+ $readmeReader.Dispose()
+ }
+
+ if ($readmeFirstLine -ne "# CopperPad") {
+ throw "Package README starts with '$readmeFirstLine', expected '# CopperPad'."
+ }
+
+ $nuspecEntry = $zip.Entries | Where-Object { $_.FullName.EndsWith(".nuspec", [StringComparison]::OrdinalIgnoreCase) } | Select-Object -First 1
+ if (-not $nuspecEntry) {
+ throw "Package '$packagePath' is missing a nuspec entry."
+ }
+ }
+ finally {
+ $zip.Dispose()
+ }
+
+ if (Test-Path $symbolPackagePath) {
+ $symbolZip = [System.IO.Compression.ZipFile]::OpenRead($symbolPackagePath)
+ try {
+ if (-not $symbolZip.GetEntry("lib/net10.0/CopperPad.pdb")) {
+ throw "Symbol package '$symbolPackagePath' is missing 'lib/net10.0/CopperPad.pdb'."
+ }
+ }
+ finally {
+ $symbolZip.Dispose()
+ }
+ }
+}
+
+Write-Host ""
+Write-Host "Created package:"
+Write-Host " $packagePath"
+if (Test-Path $symbolPackagePath) {
+ Write-Host " $symbolPackagePath"
+}
diff --git a/scripts/nuget/publish-copperpad.ps1 b/scripts/nuget/publish-copperpad.ps1
new file mode 100644
index 0000000..52401f2
--- /dev/null
+++ b/scripts/nuget/publish-copperpad.ps1
@@ -0,0 +1,88 @@
+[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium")]
+param(
+ [Parameter(Mandatory = $true)]
+ [string] $PackagePath,
+
+ [string] $ApiKey = $env:NUGET_API_KEY,
+ [string] $Source = "https://api.nuget.org/v3/index.json",
+ [switch] $NoSymbols,
+ [switch] $NoSkipDuplicate
+)
+
+$ErrorActionPreference = "Stop"
+$cmdlet = $PSCmdlet
+
+function Invoke-DotNet {
+ param(
+ [string[]] $Arguments
+ )
+
+ & dotnet @Arguments
+ if ($LASTEXITCODE -ne 0) {
+ throw "dotnet $($Arguments -join ' ') failed with exit code $LASTEXITCODE."
+ }
+}
+
+$resolvedPackage = Resolve-Path -LiteralPath $PackagePath
+$packageFile = Get-Item -LiteralPath $resolvedPackage.Path
+
+if ($packageFile.Extension -ne ".nupkg") {
+ throw "PackagePath must point to a .nupkg file."
+}
+
+if ($packageFile.Name.EndsWith(".symbols.nupkg", [StringComparison]::OrdinalIgnoreCase)) {
+ throw "PackagePath must point to the main .nupkg package, not a symbols package."
+}
+
+if (-not $packageFile.Name.StartsWith("CopperPad.", [StringComparison]::OrdinalIgnoreCase)) {
+ throw "PackagePath must point to a CopperPad package."
+}
+
+if (-not $ApiKey -and -not $WhatIfPreference) {
+ throw "NuGet API key is required. Pass -ApiKey or set the NUGET_API_KEY environment variable."
+}
+
+if (-not $ApiKey) {
+ $ApiKey = "WHATIF"
+}
+
+function Invoke-NuGetPush {
+ param(
+ [string] $Path,
+ [string] $Label
+ )
+
+ $arguments = @(
+ "nuget",
+ "push",
+ $Path,
+ "--api-key",
+ $ApiKey,
+ "--source",
+ $Source
+ )
+
+ if ($Path.EndsWith(".nupkg", [StringComparison]::OrdinalIgnoreCase)) {
+ $arguments += "--no-symbols"
+ }
+
+ if (-not $NoSkipDuplicate) {
+ $arguments += "--skip-duplicate"
+ }
+
+ if ($cmdlet.ShouldProcess($Path, "Publish $Label to $Source")) {
+ Invoke-DotNet -Arguments $arguments
+ }
+}
+
+Invoke-NuGetPush -Path $packageFile.FullName -Label "CopperPad package"
+
+if (-not $NoSymbols) {
+ $symbolPackagePath = [System.IO.Path]::ChangeExtension($packageFile.FullName, ".snupkg")
+ if (Test-Path $symbolPackagePath) {
+ Invoke-NuGetPush -Path $symbolPackagePath -Label "CopperPad symbol package"
+ }
+ else {
+ Write-Host "No matching symbol package found at '$symbolPackagePath'."
+ }
+}