release: 1.5.0#207
Merged
Merged
Conversation
Release/1.1.0
* fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io>
Allows re-running the release pipeline without failing on duplicate tags. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): pass --repo to gh release download in deploy-appcast job The deploy-appcast job has no checkout step, so gh cannot infer the repository from the git context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Release/1.1.0 (#77) * fix: avoid crash on Wine/Proton game directories in statistics * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): fix appcast-deploy job * fix: avoid crash on Wine/Proton game directories in statistics * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): pass --repo to gh release download in deploy-appcast job The deploy-appcast job has no checkout step, so gh cannot infer the repository from the git context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): fix deploy-appcast job * fix: avoid crash on Wine/Proton game directories in statistics * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): pass --repo to gh release download in deploy-appcast job The deploy-appcast job has no checkout step, so gh cannot infer the repository from the git context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add --public-key-override to appcast generation steps netsparkle-generate-appcast requires both private and public key to sign artifacts, even when using --private-key-override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): fix deploy-appcast job * fix: avoid crash on Wine/Proton game directories in statistics * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): pass --repo to gh release download in deploy-appcast job The deploy-appcast job has no checkout step, so gh cannot infer the repository from the git context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add --public-key-override to appcast generation steps netsparkle-generate-appcast requires both private and public key to sign artifacts, even when using --private-key-override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): fix AppImage filename and remove space from portable ZIP name - AppImage: add package release suffix (-1) to match PupNet output (tomb-launcher-{version}-1.x86_64.AppImage) - Portable ZIP: use hyphens instead of spaces to avoid glob and shell quoting issues throughout the pipeline Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): replace placeholder AppDescription in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): fix PublisherLinkUrl in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): clean up pupnet.conf metadata - SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+) - PublisherLinkName: use display name instead of URL - SetupCommandPrompt: clear (GUI app, no CLI entry needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): fix deploy-appcast job * fix: avoid crash on Wine/Proton game directories in statistics * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): pass --repo to gh release download in deploy-appcast job The deploy-appcast job has no checkout step, so gh cannot infer the repository from the git context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add --public-key-override to appcast generation steps netsparkle-generate-appcast requires both private and public key to sign artifacts, even when using --private-key-override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): fix AppImage filename and remove space from portable ZIP name - AppImage: add package release suffix (-1) to match PupNet output (tomb-launcher-{version}-1.x86_64.AppImage) - Portable ZIP: use hyphens instead of spaces to avoid glob and shell quoting issues throughout the pipeline Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): replace placeholder AppDescription in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): fix PublisherLinkUrl in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): clean up pupnet.conf metadata - SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+) - PublisherLinkName: use display name instead of URL - SetupCommandPrompt: clear (GUI app, no CLI entry needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(ci): use bash array for appcast common args Reduces code duplication and avoids long backslash continuation chains that may cause parsing issues on certain runners. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * Release/1.1.0 (#83) * fix: avoid crash on Wine/Proton game directories in statistics * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): pass --repo to gh release download in deploy-appcast job The deploy-appcast job has no checkout step, so gh cannot infer the repository from the git context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add --public-key-override to appcast generation steps netsparkle-generate-appcast requires both private and public key to sign artifacts, even when using --private-key-override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): fix AppImage filename and remove space from portable ZIP name - AppImage: add package release suffix (-1) to match PupNet output (tomb-launcher-{version}-1.x86_64.AppImage) - Portable ZIP: use hyphens instead of spaces to avoid glob and shell quoting issues throughout the pipeline Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): replace placeholder AppDescription in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): fix PublisherLinkUrl in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): clean up pupnet.conf metadata - SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+) - PublisherLinkName: use display name instead of URL - SetupCommandPrompt: clear (GUI app, no CLI entry needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(ci): use bash array for appcast common args Reduces code duplication and avoids long backslash continuation chains that may cause parsing issues on certain runners. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): fix scp port flag and ssh-keyscan port for non-standard SSH port Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * Release/1.1.0 (#84) * fix: avoid crash on Wine/Proton game directories in statistics * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(md5-utils): add tests for both ComputeMd5Hash overloads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datetime-extensions): annotate Average and Sum parameters as nullable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add BooleanToIconConverter tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(value-converters): add tests for remaining 20 converters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(axaml): replace deleted converters with direct bindings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> * fix(ci): skip tag creation if tag already exists Allows re-running the release pipeline without failing on duplicate tags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): pass --repo to gh release download in deploy-appcast job The deploy-appcast job has no checkout step, so gh cannot infer the repository from the git context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add --public-key-override to appcast generation steps netsparkle-generate-appcast requires both private and public key to sign artifacts, even when using --private-key-override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): fix AppImage filename and remove space from portable ZIP name - AppImage: add package release suffix (-1) to match PupNet output (tomb-launcher-{version}-1.x86_64.AppImage) - Portable ZIP: use hyphens instead of spaces to avoid glob and shell quoting issues throughout the pipeline Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): replace placeholder AppDescription in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): fix PublisherLinkUrl in pupnet.conf Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deploy): clean up pupnet.conf metadata - SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+) - PublisherLinkName: use display name instead of URL - SetupCommandPrompt: clear (GUI app, no CLI entry needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(ci): use bash array for appcast common args Reduces code duplication and avoids long backslash continuation chains that may cause parsing issues on certain runners. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): fix scp port flag and ssh-keyscan port for non-standard SSH port Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): upload appcast.xml.signature alongside appcast.xml NetSparkle in Strict mode verifies the appcast using the .signature file — without it, the signature check fails and no updates are detected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io>
* chore(deps): add Riok.Mapperly 4.3.1 Initial step of the AutoMapper → Mapperly migration tracked on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: remove unused PlaySessionDto PlaySessionDto and its two consumer methods on PlaySessionDataService (GetLastPlaySession, GetPlaySessionsByGameId) were not called by anyone. Drops one mapping pair from the AutoMapper → Mapperly migration scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert "chore(deps): add Riok.Mapperly 4.3.1" This reverts commit 31847b5. * refactor(mapping): switch language mapping from AutoMapper to manual First step of the migration to manual mapping. AvailableLanguageDto ↔ ApplicationLanguageViewModel is now handled by a SettingsMapper instance class (registered as DI singleton) instead of AutoMapper. The two unused dead methods on SettingsPageService (GetApplicationLanguage, GetApplicationTheme) are dropped along the way. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): complete SettingsMapper migration Migrates DownloaderConfiguration ↔ DownloaderViewModel and UnzipBackendDto ↔ UnzipBackendViewModel to manual mapping in SettingsMapper. SettingsProfile is now empty and deleted. DownloaderSettingsViewModel drops MapperConfiguration injection in favour of SettingsMapper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): migrate AppCrash mapping to manual mapper Introduces TombLauncher.Data/Mapping/ as the location for entity↔DTO mappers. AppCrashMapper lives there, keeping entities contained within the data layer. AppCrashProfile deleted, SettingsPageViewModel drops its MapperConfiguration dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): migrate statistics mapping to manual mapper Introduces StatisticsMapper for the three statistics mappings. DailyStatisticsAverageGameDurationViewModel renamed to DailyGameDurationViewModel (used for both average and total), its AverageGameDuration property renamed to GameDuration. DailyStatisticsViewModel deleted (superseded by DailyGameDurationViewModel). StatisticsProfile retains only the SavegameViewModel mapping (Fase 5). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): migrate GameLink mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate GameHash mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate FileBackup mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate Game and GameMetadata mapping to manual mappers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate DownloaderSearchPayload mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate LaunchOptions mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate GameWithStats mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate search result mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate savegame and FileBackup mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update CLAUDE.md to reflect manual mapper convention Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: remove stale TombLauncher.Mapping using directive Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(deps): add Riok.Mapperly 4.3.1 Initial step of the AutoMapper → Mapperly migration tracked on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: remove unused PlaySessionDto PlaySessionDto and its two consumer methods on PlaySessionDataService (GetLastPlaySession, GetPlaySessionsByGameId) were not called by anyone. Drops one mapping pair from the AutoMapper → Mapperly migration scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert "chore(deps): add Riok.Mapperly 4.3.1" This reverts commit 31847b5. * refactor(mapping): switch language mapping from AutoMapper to manual First step of the migration to manual mapping. AvailableLanguageDto ↔ ApplicationLanguageViewModel is now handled by a SettingsMapper instance class (registered as DI singleton) instead of AutoMapper. The two unused dead methods on SettingsPageService (GetApplicationLanguage, GetApplicationTheme) are dropped along the way. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): complete SettingsMapper migration Migrates DownloaderConfiguration ↔ DownloaderViewModel and UnzipBackendDto ↔ UnzipBackendViewModel to manual mapping in SettingsMapper. SettingsProfile is now empty and deleted. DownloaderSettingsViewModel drops MapperConfiguration injection in favour of SettingsMapper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): migrate AppCrash mapping to manual mapper Introduces TombLauncher.Data/Mapping/ as the location for entity↔DTO mappers. AppCrashMapper lives there, keeping entities contained within the data layer. AppCrashProfile deleted, SettingsPageViewModel drops its MapperConfiguration dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): migrate statistics mapping to manual mapper Introduces StatisticsMapper for the three statistics mappings. DailyStatisticsAverageGameDurationViewModel renamed to DailyGameDurationViewModel (used for both average and total), its AverageGameDuration property renamed to GameDuration. DailyStatisticsViewModel deleted (superseded by DailyGameDurationViewModel). StatisticsProfile retains only the SavegameViewModel mapping (Fase 5). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mapping): migrate GameLink mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate GameHash mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate FileBackup mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate Game and GameMetadata mapping to manual mappers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate DownloaderSearchPayload mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate LaunchOptions mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate GameWithStats mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate search result mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(mapping): migrate savegame and FileBackup mapping to manual mapper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update CLAUDE.md to reflect manual mapper convention Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: remove stale TombLauncher.Mapping using directive Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update src/TombLauncher.Data/Mapping/GameLinkMapper.cs Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> * Update src/TombLauncher/Mappers/LaunchOptionsMapper.cs Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> * chore: align namespace and remove stale TombLauncher.Mapping usings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: remove AutoMapper configuration test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com>
#109) * feat(settings,launch-options): add Wine compatibility section and convert launch options to page - New Compatibility settings section (WinePath, WinePrefix) visible on Linux only - Wine detection at startup: WINE env var -> WINEPATH env var -> PATH scan - Warning notification (GobletBrokenLine) if Wine not found - LaunchOptionsDialogView/ViewModel replaced by LaunchOptionsView/ViewModel (PageViewModel) - LaunchOptions promoted to direct button in GameBar (TerminalLine icon) - Per-game WinePrefix override in LaunchOptionsView, visible on Linux only - WinePrefix added to LaunchOptionsDto, Game entity and UpdateLaunchOptions - DB migration: AddGameWinePrefix - SettingsPageService.PersistCurrentConfigAsync() extracted as reusable method - GameWithStatsService: WinePath read from IAppConfiguration.Compatibility (no hardcoded fallback) * docs: fix add-page workflow view naming convention (View suffix, Pages/ folder) * loc: simplify Wine-related UI strings for non-technical users * style(ui): use text-muted class on LabeledTextBox label * feat(ui): add CardSectionHeader control to LaunchOptionsView - New CardSectionHeader control in TombLauncher.Controls (IconKind + Title) - LaunchOptionsView: icon+title header on each card (PlayLargeLine/Launch, HammerLine/Setup, GroupLine/CommunityPatch, Goblet2Line/Compatibility) - Add GENERAL and COMMUNITY_PATCH localization keys (en-US, it-IT) - Community patch tooltip: 'some versions of TRX' instead of 'TRX' - LabeledTextBox label now uses text-muted class * fix(ui,navigation): sticky save button in LaunchOptionsView; fix GameWithStats refresh on back - LaunchOptionsView: restructured with Grid (ScrollViewer row * + buttons row Auto) Save button now uses IconButton btn-primary/Save3Line, always visible outside scroll area - Fix: GameWithStatsViewModel data not refreshing when navigating back from LaunchOptions * fix(data): rewrite UpdateLaunchOptions using EF change tracker Replace ExecuteUpdateAsync/ExecuteDeleteAsync (bulk ops that bypass EF tracking) with a single tracked ToListAsync query + direct property assignment. This ensures GetGameById returns fresh data after saving launch options, fixing the stale executable path shown in GameDetailsViewModel on back navigation. * feat(stats,settings): implement Wine version detection - IPlatformSpecificFeatures: add GetWineVersion(string winePath) - LinuxPlatformSpecificFeatures: runs wine --version, captures stdout - WindowsPlatformSpecificFeatures: returns null (Wine not supported) - CompatibilitySettingsViewModel: WineVersion auto-refreshed via OnWinePathChanged - CompatibilitySettingsView: read-only Wine version row below WINEPREFIX - StatisticsPageViewModel: WineVersion populated on init via IAppConfiguration.Compatibility.WinePath - StatisticsPageView: row 7 with Goblet2Line icon, visible only on Linux (IsWineSupported) - Localization: WINE_VERSION added in en-US and it-IT * fix(linux): wrap wine launch in bash + wineserver -w for correct process tracking wine exits immediately after spawning the Windows process into wineserver, causing play time tracking and save file monitoring to stop prematurely. Wrapping with 'bash -c "wine ...; wineserver -w"' keeps the monitored process alive until wineserver shuts down (i.e. until the game truly exits). * feat(proton): add Proton launcher support with compatibility settings - Add ProtonGameLauncher: sets STEAM_COMPAT_DATA_PATH (with default), STEAM_COMPAT_INSTALL_PATH, STEAM_COMPAT_CLIENT_INSTALL_PATH (derived from Proton binary path), STEAM_APPID=0; strips NVIDIA PRIME vars inherited from parent process that break Wine rendering; uses bash login shell (-lc) - Add WineGameLauncher, WindowsGameLauncher, IGameLauncher abstractions - Add ProtonInstallationDto, CompatibilityTool enum - Add CompatibilityToolToIconConverter - LinuxPlatformSpecificFeatures: FindAvailableProtonInstallations(), CollectSteamRoots() (parses libraryfolders.vdf), GetProtonVersion() - CompatibilitySettingsView/ViewModel: Wine/Proton tool selector, Proton installation ComboBox with auto-detect from steamapps, manual fallback, STEAM_COMPAT_DATA_PATH override, Proton version display - GameWithStatsService: capture stdout/stderr on non-zero exit (Linux only) - SavegameCardView: split MARK_UNMARK key into MARK_/UNMARK_ for better i18n, use BooleanToStringConverter for context-aware tooltip - Localization: WINE_VERSION, MARK_AS_START_OF_LEVEL_SAVE, UNMARK_AS_START_OF_LEVEL_SAVE added in en-US and it-IT - Add unit tests for CompatibilityConfig, GameLauncher, LinuxPlatformFeatures * refactor(settings): remove WinePath from GameDetails config WinePath was migrated to CompatibilityConfig in a previous session. Now that the one-time migration path in App.axaml.cs is no longer needed, remove the residual property from IGameDetailsConfig, GameDetailsConfig, LayeredAppConfiguration, GameDetailsCoreSettings and its construction in SettingsProvider. * fix(statistics): avoid crash on inaccessible Proton prefix directories PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and IOException when enumerating game folders that contain a Proton prefix. The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /) that lead to /proc entries for dead processes. Fix: switch to a manual recursive helper that: - Skips the proton_pfx directory entirely (not part of the game) - Skips symlinks via AttributesToSkip = ReparsePoint - Ignores inaccessible entries (IgnoreInaccessible = true) - Catches any residual UnauthorizedAccessException / IOException per directory * fix(settings): compatibility tool change now takes effect without restart IGameLauncher is now resolved on each game launch via Func<IGameLauncher> instead of being captured at construction time in GameWithStatsService. Also refactors InitializeServices into a fluent chain of IServiceCollection extension methods, each with a single responsibility: - AddAppConfiguration: loads JSON layers, registers ILayeredAppConfiguration/IAppConfiguration - AddPlatformSpecificFeatures: registers IPlatformSpecificFeatures, IGameLauncher and its factory - AddSaveGameManagement: savegame header provider, processor and query/command services - AddTheming, AddFileOperations, AddLocalization, AddNavigation: focused singletons - AddGameManagement: installer, engine detector, game merger - AddPopups: IPopupService - AddDownloaders: HTTP clients, downloader implementations, download manager - AddNotifications, AddUpdater: notification and update services - AddDatabaseAccess now resolves IAppConfiguration from sp internally * refactor(app): move CheckCompatibilityToolAsync to AppUtils Eliminates the private wrapper method in App, calling AppUtils directly from ShowMainWindow. Unused usings (IconPacks, Configuration, Sections) removed from App.axaml.cs. * refactor(app): move ApplyInitialSettings to AppUtils App.axaml.cs now delegates both startup helpers to AppUtils, leaving it with only wiring/orchestration logic. * style(app): use unqualified ChangeTheme call within AppUtils * refactor(app): move OnUnhandledException to AppUtils Registered directly as AppUtils.OnUnhandledException on the Dispatcher event — no disadvantages vs instance method in this context (singleton event source, no instance capture needed). Removed unused usings from App.axaml.cs. * refactor(navigation): move INavigationTarget and ITopBarCommand to Contracts, introduce BusyScope pattern - Move INavigationTarget and ITopBarCommand from TombLauncher.Core.Navigation to TombLauncher.Contracts.Navigation - ITopBarCommand.Icon changed from PackIconRemixIconKind to Enum to remove icon library dependency from Contracts - CommandViewModel.Icon likewise changed to Enum - INavigationTarget simplified: SetBusy(bool,string) removed from public interface, BusyScope(string) introduced as the primary RAII pattern - SetBusy(string) kept/added for fire-and-forget cases (PlayGame, LaunchSetup, LaunchCommunitySetup) - GameWithStatsService, StatisticsPageViewModel, GameListViewModel, SavegameListViewModel refactored to BusyScope pattern - async void Uninstall fixed to async Task - Unused usings and IoC.Default references removed * fix(ui): fix BusyMessage not visible in RingLoader overlay - RingLoader: move Width/Height constraint from UserControl to inner Viewbox, so the message TextBlock below the ring is no longer clipped to 80x80 bounds - RingLoader: restore RelativeSource binding (was working, just clipped) - GameListViewModel: add missing .GetLocalizedString() on FETCHING_GAMES key - MainWindow: remove redundant debug TextBlock for BusyMessage * refactor(compatibility): move CompatibilityTool enum to TombLauncher.Contracts Moved CompatibilityTool from TombLauncher.Configuration.Sections to TombLauncher.Contracts.Enums, alongside the other shared enums. This removes the circular dependency that would have blocked adding CompatibilityTool? fields to GameMetadataDto (TombLauncher.Core) and the Game entity (TombLauncher.Data), both of which cannot reference the main TombLauncher project. Updated all consumers: - CompatibilityConfig.cs, ICompatibilityConfig.cs - LayeredAppConfiguration.cs - PlatformSpecificFeaturesCollectionExtensions.cs - CompatibilitySettingsViewModel.cs - CompatibilityToolToIconConverter.cs - AppUtils.cs - CompatibilitySettingsView.axaml (xmlns:cfg alias) * refactor(compatibility): introduce GameLaunchContext, per-game CompatibilityTool and ExtraEnvVars - IGameLauncher.GetLaunchStartInfo now takes GameLaunchContext instead of 4 loose params - GameLaunchContext carries ExecutableFileName, Arguments, WorkingDirectory, PrefixPath, ExtraEnvVars - Wine/Proton/Windows launchers apply ExtraEnvVars last (per-game overrides) - Rename WinePrefix -> CompatibilityPrefixPath across config, DTOs, models, ViewModels and Views - Add CompatibilityTool (int) and CompatibilityToolPath to Game entity and DTOs - Add GameEnvironmentVariable entity + EnvironmentVariableDto - Add migration AddPerGameCompatibilitySettings - LaunchOptionsProfile: map CompatibilityPrefixPath * feat(settings,launch-options): add Wine compatibility section and convert launch options to page - New Compatibility settings section (WinePath, WinePrefix) visible on Linux only - Wine detection at startup: WINE env var -> WINEPATH env var -> PATH scan - Warning notification (GobletBrokenLine) if Wine not found - LaunchOptionsDialogView/ViewModel replaced by LaunchOptionsView/ViewModel (PageViewModel) - LaunchOptions promoted to direct button in GameBar (TerminalLine icon) - Per-game WinePrefix override in LaunchOptionsView, visible on Linux only - WinePrefix added to LaunchOptionsDto, Game entity and UpdateLaunchOptions - DB migration: AddGameWinePrefix - SettingsPageService.PersistCurrentConfigAsync() extracted as reusable method - GameWithStatsService: WinePath read from IAppConfiguration.Compatibility (no hardcoded fallback) # Conflicts: # src/TombLauncher.Data/Database/Services/GameDataService.cs # src/TombLauncher/App.axaml.cs # src/TombLauncher/Extensions/ViewModelsServiceCollectionExtensions.cs # src/TombLauncher/Factories/Mapping/MappingUtils.cs # src/TombLauncher/Factories/Profiles/LaunchOptionsProfile.cs # src/TombLauncher/Services/GameDetailsService.cs # src/TombLauncher/Services/GameWithStatsService.cs # src/TombLauncher/ViewModels/Dialogs/LaunchOptionsDialogViewModel.cs # src/TombLauncher/ViewModels/Pages/GameDetailsViewModel.cs # src/TombLauncher/Views/GameBar.axaml * docs: fix add-page workflow view naming convention (View suffix, Pages/ folder) * loc: simplify Wine-related UI strings for non-technical users * style(ui): use text-muted class on LabeledTextBox label * feat(ui): add CardSectionHeader control to LaunchOptionsView - New CardSectionHeader control in TombLauncher.Controls (IconKind + Title) - LaunchOptionsView: icon+title header on each card (PlayLargeLine/Launch, HammerLine/Setup, GroupLine/CommunityPatch, Goblet2Line/Compatibility) - Add GENERAL and COMMUNITY_PATCH localization keys (en-US, it-IT) - Community patch tooltip: 'some versions of TRX' instead of 'TRX' - LabeledTextBox label now uses text-muted class * fix(ui,navigation): sticky save button in LaunchOptionsView; fix GameWithStats refresh on back - LaunchOptionsView: restructured with Grid (ScrollViewer row * + buttons row Auto) Save button now uses IconButton btn-primary/Save3Line, always visible outside scroll area - Fix: GameWithStatsViewModel data not refreshing when navigating back from LaunchOptions * fix(data): rewrite UpdateLaunchOptions using EF change tracker Replace ExecuteUpdateAsync/ExecuteDeleteAsync (bulk ops that bypass EF tracking) with a single tracked ToListAsync query + direct property assignment. This ensures GetGameById returns fresh data after saving launch options, fixing the stale executable path shown in GameDetailsViewModel on back navigation. * feat(stats,settings): implement Wine version detection - IPlatformSpecificFeatures: add GetWineVersion(string winePath) - LinuxPlatformSpecificFeatures: runs wine --version, captures stdout - WindowsPlatformSpecificFeatures: returns null (Wine not supported) - CompatibilitySettingsViewModel: WineVersion auto-refreshed via OnWinePathChanged - CompatibilitySettingsView: read-only Wine version row below WINEPREFIX - StatisticsPageViewModel: WineVersion populated on init via IAppConfiguration.Compatibility.WinePath - StatisticsPageView: row 7 with Goblet2Line icon, visible only on Linux (IsWineSupported) - Localization: WINE_VERSION added in en-US and it-IT * fix(linux): wrap wine launch in bash + wineserver -w for correct process tracking wine exits immediately after spawning the Windows process into wineserver, causing play time tracking and save file monitoring to stop prematurely. Wrapping with 'bash -c "wine ...; wineserver -w"' keeps the monitored process alive until wineserver shuts down (i.e. until the game truly exits). * feat(proton): add Proton launcher support with compatibility settings - Add ProtonGameLauncher: sets STEAM_COMPAT_DATA_PATH (with default), STEAM_COMPAT_INSTALL_PATH, STEAM_COMPAT_CLIENT_INSTALL_PATH (derived from Proton binary path), STEAM_APPID=0; strips NVIDIA PRIME vars inherited from parent process that break Wine rendering; uses bash login shell (-lc) - Add WineGameLauncher, WindowsGameLauncher, IGameLauncher abstractions - Add ProtonInstallationDto, CompatibilityTool enum - Add CompatibilityToolToIconConverter - LinuxPlatformSpecificFeatures: FindAvailableProtonInstallations(), CollectSteamRoots() (parses libraryfolders.vdf), GetProtonVersion() - CompatibilitySettingsView/ViewModel: Wine/Proton tool selector, Proton installation ComboBox with auto-detect from steamapps, manual fallback, STEAM_COMPAT_DATA_PATH override, Proton version display - GameWithStatsService: capture stdout/stderr on non-zero exit (Linux only) - SavegameCardView: split MARK_UNMARK key into MARK_/UNMARK_ for better i18n, use BooleanToStringConverter for context-aware tooltip - Localization: WINE_VERSION, MARK_AS_START_OF_LEVEL_SAVE, UNMARK_AS_START_OF_LEVEL_SAVE added in en-US and it-IT - Add unit tests for CompatibilityConfig, GameLauncher, LinuxPlatformFeatures * refactor(settings): remove WinePath from GameDetails config WinePath was migrated to CompatibilityConfig in a previous session. Now that the one-time migration path in App.axaml.cs is no longer needed, remove the residual property from IGameDetailsConfig, GameDetailsConfig, LayeredAppConfiguration, GameDetailsCoreSettings and its construction in SettingsProvider. * fix(settings): compatibility tool change now takes effect without restart IGameLauncher is now resolved on each game launch via Func<IGameLauncher> instead of being captured at construction time in GameWithStatsService. Also refactors InitializeServices into a fluent chain of IServiceCollection extension methods, each with a single responsibility: - AddAppConfiguration: loads JSON layers, registers ILayeredAppConfiguration/IAppConfiguration - AddPlatformSpecificFeatures: registers IPlatformSpecificFeatures, IGameLauncher and its factory - AddSaveGameManagement: savegame header provider, processor and query/command services - AddTheming, AddFileOperations, AddLocalization, AddNavigation: focused singletons - AddGameManagement: installer, engine detector, game merger - AddPopups: IPopupService - AddDownloaders: HTTP clients, downloader implementations, download manager - AddNotifications, AddUpdater: notification and update services - AddDatabaseAccess now resolves IAppConfiguration from sp internally * refactor(app): move CheckCompatibilityToolAsync to AppUtils Eliminates the private wrapper method in App, calling AppUtils directly from ShowMainWindow. Unused usings (IconPacks, Configuration, Sections) removed from App.axaml.cs. * refactor(app): move ApplyInitialSettings to AppUtils App.axaml.cs now delegates both startup helpers to AppUtils, leaving it with only wiring/orchestration logic. * style(app): use unqualified ChangeTheme call within AppUtils * refactor(app): move OnUnhandledException to AppUtils Registered directly as AppUtils.OnUnhandledException on the Dispatcher event — no disadvantages vs instance method in this context (singleton event source, no instance capture needed). Removed unused usings from App.axaml.cs. * refactor(navigation): move INavigationTarget and ITopBarCommand to Contracts, introduce BusyScope pattern - Move INavigationTarget and ITopBarCommand from TombLauncher.Core.Navigation to TombLauncher.Contracts.Navigation - ITopBarCommand.Icon changed from PackIconRemixIconKind to Enum to remove icon library dependency from Contracts - CommandViewModel.Icon likewise changed to Enum - INavigationTarget simplified: SetBusy(bool,string) removed from public interface, BusyScope(string) introduced as the primary RAII pattern - SetBusy(string) kept/added for fire-and-forget cases (PlayGame, LaunchSetup, LaunchCommunitySetup) - GameWithStatsService, StatisticsPageViewModel, GameListViewModel, SavegameListViewModel refactored to BusyScope pattern - async void Uninstall fixed to async Task - Unused usings and IoC.Default references removed * fix(ui): fix BusyMessage not visible in RingLoader overlay - RingLoader: move Width/Height constraint from UserControl to inner Viewbox, so the message TextBlock below the ring is no longer clipped to 80x80 bounds - RingLoader: restore RelativeSource binding (was working, just clipped) - GameListViewModel: add missing .GetLocalizedString() on FETCHING_GAMES key - MainWindow: remove redundant debug TextBlock for BusyMessage * refactor(compatibility): move CompatibilityTool enum to TombLauncher.Contracts Moved CompatibilityTool from TombLauncher.Configuration.Sections to TombLauncher.Contracts.Enums, alongside the other shared enums. This removes the circular dependency that would have blocked adding CompatibilityTool? fields to GameMetadataDto (TombLauncher.Core) and the Game entity (TombLauncher.Data), both of which cannot reference the main TombLauncher project. Updated all consumers: - CompatibilityConfig.cs, ICompatibilityConfig.cs - LayeredAppConfiguration.cs - PlatformSpecificFeaturesCollectionExtensions.cs - CompatibilitySettingsViewModel.cs - CompatibilityToolToIconConverter.cs - AppUtils.cs - CompatibilitySettingsView.axaml (xmlns:cfg alias) * refactor(compatibility): introduce GameLaunchContext, per-game CompatibilityTool and ExtraEnvVars - IGameLauncher.GetLaunchStartInfo now takes GameLaunchContext instead of 4 loose params - GameLaunchContext carries ExecutableFileName, Arguments, WorkingDirectory, PrefixPath, ExtraEnvVars - Wine/Proton/Windows launchers apply ExtraEnvVars last (per-game overrides) - Rename WinePrefix -> CompatibilityPrefixPath across config, DTOs, models, ViewModels and Views - Add CompatibilityTool (int) and CompatibilityToolPath to Game entity and DTOs - Add GameEnvironmentVariable entity + EnvironmentVariableDto - Add migration AddPerGameCompatibilitySettings - LaunchOptionsProfile: map CompatibilityPrefixPath * fix(compatibility): wire per-game CompatibilityTool, EnvVars and prefix through all layers - Add IEnvironmentVariable contract and EnvironmentVariableMapper (ToDto/ToEntity) - Extend IGameMetadata, GameMetadataDto, Game entity with CompatibilityTool, CompatibilityToolPath and ExtraEnvVars - GameMapper: map new fields in both ToDto and ToGame; CompatibilityTool now defaults to Unspecified in CompatibilityConfig so layered merge works correctly - GameDataService.UpdateLaunchOptions: add missing SetupExecutable block, persist ExtraEnvVars via EnvironmentVariableMapper.ToEntities, fix wrong executable added when GameExecutable backup was absent - Add .Include(g => g.EnvironmentVariables) to all EF queries that call GameMapper - GameMetadataViewModel / LaunchOptionsViewModel: expose CompatibilityTool, CompatibilityToolPath, ExtraEnvVars; InitFromGame populates them from the game VM - LaunchOptionsMapper.ToDto: map CompatibilityPrefixPath, CompatibilityTool, CompatibilityToolPath, ExtraEnvVars - GameWithStatsService.LaunchProcess: select launcher from per-game CompatibilityTool (falling back to global factory when Unspecified); pass ExtraEnvVars to context - LayeredAppConfiguration: fix CompatibilityTool merge — check both User and Defaults before falling back to Wine; add "CompatibilityTool": "Wine" to appsettings.json - Register EnvironmentVariableMapper in DI; restore GetGameById on GameDataService - Update GameLauncherTests to use GameLaunchContext; fix CompatibilityConfigTests using Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(mappers,data): add mapper unit tests and UpdateLaunchOptions integration tests - EnvironmentVariableMapperTests: ToDto/ToEntity/ToDtos/ToEntities - GameMapperTests: ToDto and ToGame for compatibility fields and env vars - LaunchOptionsMapperTests: compatibility fields, ExtraEnvVars, optional executables - UpdateLaunchOptionsTests (EF Core InMemory): CompatibilityTool/Path/Prefix persistence, env var add/replace/clear, setup executable add/update/remove - SearchMapperTests: TitlePic propagated through ToDto overloads - Add Microsoft.EntityFrameworkCore.InMemory 10.0.2 to test project Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(platform): add ExpandPath to IPlatformSpecificFeatures for tilde expansion Linux expands leading ~ to $HOME; Windows returns the path unchanged. GameWithStatsService applies ExpandPath before passing PrefixPath to the launcher, so users can type ~/... in the UI and have it resolve correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a link to tomblauncher.app in the main sidebar, alongside the existing GitHub link. Includes WebsiteLink in the config layer, ApplicationCoreSettings DTO, and both localization files (en-US / it-IT). Also moves CoreSettings.cs to the Dtos/Configuration subfolder. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ui): add About page with app info and acknowledgements section Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(ui): extract SidebarNavigationItem control to reduce sidebar duplication Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ui): populate About page acknowledgements section Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…#116) Introduces TombLauncher.Patchers with a complete pipeline for reading TR2/TR3 tombpc.dat script files and converting them into a UI-friendly GameflowDto. - GameflowReader: binary reader for the full tombpc.dat structure (header, counts, flags, XOR key, string arrays, sequences, item strings) - GameflowMapper: converts Gameflow → GameflowDto, decoding XOR-encrypted strings, splitting sequences per level, and mapping BAM angles to degrees - Shared models: GameflowDto, LevelInfo, SequenceInfo, Tr2Item, Tr3Item - TRX skeleton: TrxGameflow, Level, SequenceItem models and patcher stub - Utilities: AngleExtensions (BAM↔degrees), SplitAt, FromGameTicks - Tests: full reader coverage on two custom levels (Silver Machine TR2, Escapade in Utah TR3) and mapper coverage
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces an experimental AI chat feature ("Talk to Laura") backed by a
RAG pipeline, disabled by default and hidden from the sidebar when off.
Core AI pipeline
- RagService: streaming RAG with Semantic Kernel, per-request Kernel.Clone
for thread safety, documentation injected into user message (not system
prompt) to avoid accumulation across turns
- VectorSearchService: sqlite-vec vector search with distance threshold 0.20
- GameDiagnosticsPlugin: SK plugin exposing crash log, exit code and engine
info to the model via auto function calling
- TroubleshootingContextService: builds per-game context from the database
- Support for Ollama and LM Studio backends (OllamaBackendService,
LmStudioBackendService) with endpoint normalization and model listing
Settings UI
- AiSettingsView: backend picker, endpoint/embedding model health checks
with flashing indicator, dynamic model list via ComboBox, KB update button
- ServiceCheckView: reusable status indicator extracted from duplicated code
Chat UI
- AiChatView: streaming message bubbles, tool-use indicator, empty state,
auto-scroll, Enter to send; Laura menu item hidden when AI disabled
- Open chat from game details page with pre-loaded troubleshooting context
Knowledge base
- KnowledgeBase.Embedder: console tool that chunks markdown docs, generates
embeddings via OpenAI-compatible API and writes sqlite-vec DB
- SFTP upload (--upload flag): SHA-256 manifest, atomic rename on server,
private key or password auth
- KbUpdateService: client-side auto-update on startup — fetches manifest,
checks schema version and generatedAt, downloads + verifies + swaps DB
- CryptoUtils: unified MD5/SHA-256 helpers (renamed from Md5Utils)
Bug fixes
- Fix sync/async stream mix crash on game exit (BeginOutputReadLine +
ReadToEndAsync incompatibility)
- Fix IHttpClientFactory CI build failure (HintPath → PackageReference)
Introduces a per-OS engine support matrix to IPlatformSpecificFeatures: - Add EngineSupportState enum (NoSupport, SupportedWithCompatibilityLayer, NativePatchingAvailable, FullSupport) - Add ISupportMatrix interface with IReadOnlyDictionary<GameEngine, EngineSupportState> - Implement LinuxSupportMatrix, WindowsSupportMatrix, MacOsSupportMatrix - Wire ISupportMatrix into IPlatformSpecificFeatures and both platform implementations - Expose EngineSupportState in GameDetailsViewModel via GameDetailsService - Show a status icon in GameBar with danger class for unsupported engines - Add EngineSupportStateToIconConverter and EngineSupportStateToTooltipConverter - Add localization strings for all four states (en-US, it-IT)
…ors (#124) Use GetEnumerationOptions() (case-insensitive, skips reparse points) instead of SearchOption.AllDirectories when scanning the install directory, fixing controls not populated on page open. Catch IOException in LoadAsync and show a localized error messagebox before navigating back. Remove orphan GetGameLaunchStartInfo from LinuxPlatformSpecificFeatures. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add SupportMatrixPlugin exposing ISupportMatrix to the Semantic Kernel so the AI can report engine support state during troubleshooting. Adopt null-object pattern on TroubleshootingContext (IsSet sentinel) to remove nullable parameters from plugin constructors and AskAsync. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The manifest file is at kb_manifest.json, not manifest.json. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…129) * feat(patchers): port widescreen aspect ratio patcher from old branch Brings over the widescreen patcher logic (contracts, patcher, test asset) originally developed on feature/patchers, adjusted for the src/tests layout. IPatcher.EngineDetector still references TombRaiderEngineDetector — needs resolution before the project compiles (see issue #88). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(patchers): move EngineDetectorResult to Contracts, wire IEngineDetector - EngineDetectorResult moved to TombLauncher.Contracts.EngineDetectors - IEngineDetector interface defined in Contracts with Detect() method - TombRaiderEngineDetector now implements IEngineDetector - IPatcher.EngineDetector typed as IEngineDetector (no circular dependency) - WidescreenPatcher updated accordingly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): implement camera distance, FOV and 60 FPS patches - WidescreenPatcherParameters: add UpdateFov, TargetFov, Update60Fps, Engine - WidescreenPatcher: refactor PatchExecutable into four static methods (ApplyAspectRatioCorrection, ApplyCameraDistanceCorrection, ApplyFovCorrection, Apply60Fps) - Fix Apply60Fps guard: use 'is not (TR2 or TR3)' instead of bitwise OR - Fix ApplyCameraDistanceCorrection guard: TargetCameraDistance > 0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(patchers): hardcode 4:3 as original aspect ratio, make parameters init-only The original aspect ratio in unpatched TR2-5 executables is always 4:3. Remove OriginalAspectRatio and OriginalCameraDistance from parameters (unused/redundant), switch all setters to init-only for immutability. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(patchers): add tests for camera distance, FOV and 60 FPS patches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): add widescreen patcher UI and wire full pipeline - CameraDistancePreset enum with block-based values (1024 multiples) - CameraDistanceOptionViewModel: localizable display text + IsCustom flag - WidescreenPatcherViewModel: aspect ratio, camera distance combobox, FOV (default 1920), 60 FPS (disabled with tooltip for non-TR2/3) - WidescreenPatcherView: cards per section, ComboBox with ItemTemplate, IsEnabled guards, 60 FPS tooltip binding - WidescreenPatcherService: Check60FpsSupport - PatchersServiceCollectionExtensions: AddPatchers() - IEngineDetector registered as Singleton (fixes captive dependency) - WidescreenPatcher: constructor injection for IEngineDetector - GameDetailsViewModel: InitPatchers(), OpenWidescreenPatcherCommand - GameDetailsService: OpenWidescreenPatcher navigation - GameBar: Patchers dropdown, IsVisible bound to collection - All services updated to depend on IEngineDetector instead of concrete type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(controls): hide Infotip header TextBlock when Header is null or empty Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): add localization, refine view and widen engine detector - en-US and it-IT: add all widescreen patcher localization keys - WidescreenPatcherView: 60 FPS section uses Infotip for unsupported tooltip - WidescreenPatcherViewModel: minor refinements - TombRaiderEngineDetector: accept IPlatformSpecificFeatures in constructor - Tests: inject platform features into TombRaiderEngineDetector, use instance field Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): implement WidescreenPatcherService with backup, apply and revert - Move WidescreenPatcherService to TombLauncher/Services/Patchers/Widescreen - Add ApplyPatch: reads executable, applies patch, saves FileBackup to DB only on success - Add RevertPatch: restores executable from DB backup with MD5 integrity check and atomic File.Replace - Add IsAlreadyApplied: checks for existing FileBackup record - Replace TombLauncherDbContext with IDbContextFactory to avoid captive dependency - Add BackupSource discriminator to FileBackup entity - Add PatchResult.UnsuccessfulResult factory method - Add CryptoUtils.ComputeMd5Hash(string path) overload with proper stream disposal - Add localization keys for all patcher error messages (en-US and it-IT) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(data): add migration for FileBackup.BackupSource column Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(patchers): remove IPatchParameters and IPatcher marker interfaces WidescreenPatcher was the only implementor and already did a runtime cast back to WidescreenPatcherParameters internally, providing no real type safety. ApplyPatch now takes WidescreenPatcherParameters directly; EngineDetector becomes a private readonly field. IPatcherUi (also unused) removed alongside. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): implement full widescreen patcher pipeline with progress reporting - Complete ApplyPatch and RevertPatch commands in WidescreenPatcherViewModel - Add IProgress<string> reporting throughout patcher, service and view model - Add CanApplyPatch/CanRevertPatch toggle; check patch state on navigation - Remove TargetFolder from WidescreenPatcherParameters (folder passed separately) - Add ILogger to WidescreenPatcher for skip-path diagnostics - Register IDbContextFactory via AddDbContextFactory (fixes DI for singleton service) - Simplify IViewService to ViewContext property only - Add 21 localization keys for progress messages and result dialogs (en-US + it-IT) - Fix typo SUCCESFULLY → SUCCESSFULLY in dialog key - Update tests to match new signatures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(patchers): fix ApplyPatch button state reactivity and CanRevertPatch semantics - Add NotifyCanExecuteChangedFor(ApplyPatchCommand) to all four toggle properties so the button reacts immediately when the user enables/disables a patch option - Fix CanRevertPatch to depend on CanApplyPatchByFileState only, not CanApplyPatch; previously a false CanApplyPatchByFormState (no options selected) would incorrectly enable the Revert button even when no patch had been applied Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(patchers): bind Apply button visibility to CanApplyPatchByFileState Button visibility should track whether the file has been patched, not whether any form option is selected — that is already handled by CanExecute. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ai): fix AppVersionRange not written to KB database - Change SectionMetadata.Header from init to set so Newtonsoft.Json can populate it via reflection; init-only property caused the header lookup to fail silently, returning null SectionMetadata for all chunks - Remove JsonConvert.SerializeObject from AppVersionRange in KnowledgeBaseWriter; the value is already a string — wrapping it produced JSON-encoded quotes in the DB, breaking VersionRange.TryParse on read - Fix appVersionRange syntax in metadata.json: C# range expressions (..x, x..) replaced with valid NuGet VersionRange notation ((,1.3.0) and [1.3.0,)) - Add KB entry for widescreen patch feature (v>=1.3.0) with version-gated counterpart for older versions; add Linux to document platforms - Raise vector search distance threshold from 0.20 to 0.6 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(patchers): remove localization dependency from WidescreenPatcher; fix null logger in tests WidescreenPatcher called GetLocalizedString() for progress messages, creating a runtime dependency on Ioc.Default that broke unit tests. Progress now reports raw localization keys — the caller layer handles translation. NullLogger replaces null! in test constructor to fix ArgumentNullException. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(patchers): localize progress messages in WidescreenPatcherViewModel GetProgress() now applies GetLocalizedString() on incoming messages so that raw keys reported by WidescreenPatcher are translated before reaching BusyMessage. Already-localized strings from the service pass through unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
#130) fix(kb-embedder): delete existing remote file before rename to avoid SFTP failure SSH_FXP_RENAME (SFTP v3) does not overwrite existing destinations, causing an SshException on second and subsequent uploads. Delete the final path first if it exists, then rename from the temp file. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Release/1.1.0 (#77)
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix appcast-deploy job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix deploy-appcast job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix deploy-appcast job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix deploy-appcast job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(ci): use bash array for appcast common args
Reduces code duplication and avoids long backslash continuation chains
that may cause parsing issues on certain runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* Release/1.1.0 (#83)
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(ci): use bash array for appcast common args
Reduces code duplication and avoids long backslash continuation chains
that may cause parsing issues on certain runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix scp port flag and ssh-keyscan port for non-standard SSH port
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* Release/1.1.0 (#84)
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(ci): use bash array for appcast common args
Reduces code duplication and avoids long backslash continuation chains
that may cause parsing issues on certain runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix scp port flag and ssh-keyscan port for non-standard SSH port
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): upload appcast.xml.signature alongside appcast.xml
NetSparkle in Strict mode verifies the appcast using the .signature
file — without it, the signature check fails and no updates are detected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* Release 1.2.0 (#114)
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists (#76)
Allows re-running the release pipeline without failing on duplicate tags.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix deploy-appcast job
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Master (#85)
* Release/1.1.0 (#77)
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix appcast-deploy job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix deploy-appcast job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix deploy-appcast job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): fix deploy-appcast job
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(ci): use bash array for appcast common args
Reduces code duplication and avoids long backslash continuation chains
that may cause parsing issues on certain runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* Release/1.1.0 (#83)
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(ci): use bash array for appcast common args
Reduces code duplication and avoids long backslash continuation chains
that may cause parsing issues on certain runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix scp port flag and ssh-keyscan port for non-standard SSH port
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* Release/1.1.0 (#84)
* fix: avoid crash on Wine/Proton game directories in statistics
* fix(statistics): avoid crash on inaccessible Proton prefix directories
PathUtils.GetDirectorySize crashed with UnauthorizedAccessException and
IOException when enumerating game folders that contain a Proton prefix.
The prefix includes root-owned subdirs and symlinks (e.g. dosdevices/z: -> /)
that lead to /proc entries for dead processes.
Fix: switch to a manual recursive helper that:
- Skips the proton_pfx directory entirely (not part of the game)
- Skips symlinks via AttributesToSkip = ReparsePoint
- Ignores inaccessible entries (IgnoreInaccessible = true)
- Catches any residual UnauthorizedAccessException / IOException per directory
* test(path-utils): add tests for symlink, proton_pfx skip, and inaccessible directory handling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(datetime-extensions): add tests for DateTimeExtensions and DayOfWeekCultureSensitiveComparer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(md5-utils): add tests for both ComputeMd5Hash overloads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(datetime-extensions): annotate Average and Sum parameters as nullable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add BooleanToIconConverter tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(value-converters): remove unused converters and fix BooleanToStringConverter.ConvertBack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(value-converters): add tests for remaining 20 converters
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(axaml): replace deleted converters with direct bindings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(tier-2): add tests for SavegameHeaderReader, Tr1xSavegameHeaderReader, ZipManager
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [CodeFactor] Apply fixes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* fix(ci): skip tag creation if tag already exists
Allows re-running the release pipeline without failing on duplicate tags.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): pass --repo to gh release download in deploy-appcast job
The deploy-appcast job has no checkout step, so gh cannot infer the
repository from the git context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): add --public-key-override to appcast generation steps
netsparkle-generate-appcast requires both private and public key to
sign artifacts, even when using --private-key-override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix AppImage filename and remove space from portable ZIP name
- AppImage: add package release suffix (-1) to match PupNet output
(tomb-launcher-{version}-1.x86_64.AppImage)
- Portable ZIP: use hyphens instead of spaces to avoid glob and shell
quoting issues throughout the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): replace placeholder AppDescription in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): fix PublisherLinkUrl in pupnet.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(deploy): clean up pupnet.conf metadata
- SetupMinWindowsVersion: 6.2 -> 10 (.NET 10 requires Windows 10+)
- PublisherLinkName: use display name instead of URL
- SetupCommandPrompt: clear (GUI app, no CLI entry needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(ci): use bash array for appcast common args
Reduces code duplication and avoids long backslash continuation chains
that may cause parsing issues on certain runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): fix scp port flag and ssh-keyscan port for non-standard SSH port
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): upload appcast.xml.signature alongside appcast.xml
NetSparkle in Strict mode verifies the appcast using the .signature
file — without it, the signature check fails and no updates are detected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codefactor-io <support@codefactor.io>
* refactor: migrate away from automapper
* chore(deps): add Riok.Mapperly 4.3.1
Initial step of the AutoMapper → Mapperly migration tracked on this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: remove unused PlaySessionDto
PlaySessionDto and its two consumer methods on PlaySessionDataService
(GetLastPlaySession, GetPlaySessionsByGameId) were not called by anyone.
Drops one mapping pair from the AutoMapper → Mapperly migration scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "chore(deps): add Riok.Mapperly 4.3.1"
This reverts commit 31847b590b1841fa7fecc38db8440d5348eea7ea.
* refactor(mapping): switch language mapping from AutoMapper to manual
First step of the migration to manual mapping. AvailableLanguageDto ↔
ApplicationLanguageViewModel is now handled by a SettingsMapper instance
class (registered as DI singleton) instead of AutoMapper. The two unused
dead methods on SettingsPageService (GetApplicationLanguage,
GetApplicationTheme) are dropped along the way.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): complete SettingsMapper migration
Migrates DownloaderConfiguration ↔ DownloaderViewModel and
UnzipBackendDto ↔ UnzipBackendViewModel to manual mapping in
SettingsMapper. SettingsProfile is now empty and deleted.
DownloaderSettingsViewModel drops MapperConfiguration injection
in favour of SettingsMapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): migrate AppCrash mapping to manual mapper
Introduces TombLauncher.Data/Mapping/ as the location for entity↔DTO
mappers. AppCrashMapper lives there, keeping entities contained within
the data layer. AppCrashProfile deleted, SettingsPageViewModel drops
its MapperConfiguration dependency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): migrate statistics mapping to manual mapper
Introduces StatisticsMapper for the three statistics mappings.
DailyStatisticsAverageGameDurationViewModel renamed to
DailyGameDurationViewModel (used for both average and total),
its AverageGameDuration property renamed to GameDuration.
DailyStatisticsViewModel deleted (superseded by DailyGameDurationViewModel).
StatisticsProfile retains only the SavegameViewModel mapping (Fase 5).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): migrate GameLink mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate GameHash mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate FileBackup mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate Game and GameMetadata mapping to manual mappers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate DownloaderSearchPayload mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate LaunchOptions mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate GameWithStats mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate search result mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate savegame and FileBackup mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update CLAUDE.md to reflect manual mapper convention
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: remove stale TombLauncher.Mapping using directive
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(deps): add Riok.Mapperly 4.3.1
Initial step of the AutoMapper → Mapperly migration tracked on this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: remove unused PlaySessionDto
PlaySessionDto and its two consumer methods on PlaySessionDataService
(GetLastPlaySession, GetPlaySessionsByGameId) were not called by anyone.
Drops one mapping pair from the AutoMapper → Mapperly migration scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "chore(deps): add Riok.Mapperly 4.3.1"
This reverts commit 31847b590b1841fa7fecc38db8440d5348eea7ea.
* refactor(mapping): switch language mapping from AutoMapper to manual
First step of the migration to manual mapping. AvailableLanguageDto ↔
ApplicationLanguageViewModel is now handled by a SettingsMapper instance
class (registered as DI singleton) instead of AutoMapper. The two unused
dead methods on SettingsPageService (GetApplicationLanguage,
GetApplicationTheme) are dropped along the way.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): complete SettingsMapper migration
Migrates DownloaderConfiguration ↔ DownloaderViewModel and
UnzipBackendDto ↔ UnzipBackendViewModel to manual mapping in
SettingsMapper. SettingsProfile is now empty and deleted.
DownloaderSettingsViewModel drops MapperConfiguration injection
in favour of SettingsMapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): migrate AppCrash mapping to manual mapper
Introduces TombLauncher.Data/Mapping/ as the location for entity↔DTO
mappers. AppCrashMapper lives there, keeping entities contained within
the data layer. AppCrashProfile deleted, SettingsPageViewModel drops
its MapperConfiguration dependency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): migrate statistics mapping to manual mapper
Introduces StatisticsMapper for the three statistics mappings.
DailyStatisticsAverageGameDurationViewModel renamed to
DailyGameDurationViewModel (used for both average and total),
its AverageGameDuration property renamed to GameDuration.
DailyStatisticsViewModel deleted (superseded by DailyGameDurationViewModel).
StatisticsProfile retains only the SavegameViewModel mapping (Fase 5).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(mapping): migrate GameLink mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate GameHash mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate FileBackup mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate Game and GameMetadata mapping to manual mappers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate DownloaderSearchPayload mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate LaunchOptions mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate GameWithStats mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate search result mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(mapping): migrate savegame and FileBackup mapping to manual mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update CLAUDE.md to reflect manual mapper convention
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: remove stale TombLauncher.Mapping using directive
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update src/TombLauncher.Data/Mapping/GameLinkMapper.cs
Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com>
* Update src/TombLauncher/Mappers/LaunchOptionsMapper.cs
Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com>
* chore: align namespace and remove stale TombLauncher.Mapping usings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: remove AutoMapper configuration test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com>
* feat(compatibility): add Proton support and per-game launcher settings (#109)
* feat(settings,launch-options): add Wine compatibility section and convert launch options to page
- New Compatibility settings section (WinePath, WinePrefix) visible on Linux only
- Wine detection at startup: WINE env var -> WINEPATH env var -> PATH scan
- Warning notification (GobletBrokenLine) if Wine not found
- LaunchOptionsDialogView/ViewModel replaced by LaunchOptionsView/ViewModel (PageViewModel)
- LaunchOptions promoted to direct button in GameBar (TerminalLine icon)
- Per-game WinePrefix override in LaunchOptionsView, visible on Linux only
- WinePrefix added to LaunchOptionsDto, Game entity and UpdateLaunchOptions
- DB migration: AddGameWinePrefix
- SettingsPageService.PersistCurrentConfigAsync() extracted as reusable method
- GameWithStatsService: WinePath read from IAppConfiguration.Compatibility (no hardcoded fallback)
* docs: fix add-page workflow view naming convention (View suffix, Pages/ folder)
* loc: simplify Wine-related UI strings for non-technical users
* style(ui): use text-muted class on LabeledTextBox label
* feat(ui): add CardSectionHeader control to LaunchOptionsView
- New CardSectionHeader control in TombLauncher.Controls (IconKind + Title)
- LaunchOptionsView: icon+title header on each card
(PlayLargeLine/Launch, HammerLine/Setup, GroupLine/CommunityPatch, Goblet2Line/Compatibility)
- Add GENERAL and COMMUNITY_PATCH localization keys (en-US, it-IT)
- Community patch tooltip: 'some versions of TRX' instead of 'TRX'
- LabeledTextBox label now uses text-muted class
* fix(ui,navigation): sticky save button in LaunchOptionsView; fix GameWithStats refresh on back
- LaunchOptionsView: restructured with Grid (ScrollViewer row * + buttons row Auto)
Save button now uses IconButton btn-primary/Save3Line, always visible outside scroll area
- Fix: GameWithStatsViewModel data not refreshing when navigating back from LaunchOptions
* fix(data): rewrite UpdateLaunchOptions using EF change tracker
Replace ExecuteUpdateAsync/ExecuteDeleteAsync (bulk ops that bypass EF tracking)
with a single tracked ToListAsync query + direct property assignment.
This ensures GetGameById returns fresh data after saving launch options,
fixing the stale executable path shown in GameDetailsViewModel on back navigation.
* feat(stats,settings): implement Wine version detection
- IPlatformSpecificFeatures: add GetWineVersion(string winePath)
- LinuxPlatformSpecificFeatures: runs wine --version, captures stdout
- WindowsPlatformSpecificFeatures: returns null (Wine not supported)
- CompatibilitySettingsViewModel: WineVersion auto-refreshed via OnWinePathChanged
- CompatibilitySettingsView: read-only Wine version row below WINEPREFIX
- StatisticsPageViewModel: WineVersion populated on init via IAppConfiguration.Compatibility.WinePath
- StatisticsPageView: row 7 with Goblet2Line icon, visible only on Linux (IsWineSupported)
- Localization: WINE_VERSION added in en-US and it-IT
* fix(linux): wrap wine launch in bash + wineserver -w for correct process tracking
wine exits immediately after spawning the Windows process into wineserver,
causing play time tracking and save file monitoring to stop prematurely.
Wrapping with 'bash -c "wine ...; wineserver -w"' keeps the monitored process
alive until wineserver shuts down (i.e. until the game truly exits).
* feat(proton): add Proton launcher support with compatibility settings
- Add ProtonGameLauncher: sets STEAM_COMPAT_DATA_PATH (with default),
STEAM_COMPAT_INSTALL_PATH, STEAM_COMPAT_CLIENT_INSTALL_PATH (derived from
Proton binary path), STEAM_APPID=0; strips NVIDIA PRIME vars inherited from
parent process that break Wine rendering; uses bash login shell (-lc)
- Add WineGameLauncher, WindowsGameLauncher, IGameLauncher abstractions
- Add ProtonInstallationDto, CompatibilityTool enum
- Add CompatibilityToolToIconConverter
- LinuxPlatformSpecificFeatures: FindAvailableProtonInstallations(),
CollectSteamRoots() (parses libraryfolders.vdf), GetProtonVersion()
- CompatibilitySettingsView/ViewModel: Wine/Proton tool selector, Proton
installation ComboBox with auto-detect from steamapps, manual fallback,
STEAM_COMPAT_DATA_PATH override, Proton version display
- GameWithStatsService: capture stdout/stderr on non-zero exit (Linux only)
- SavegameCardView: split MARK_UNMARK key into MARK_/UNMARK_ for better i18n,
use BooleanToStringConverter for context-aware tooltip
- Localization: WINE_VERSION, MARK_AS_START_OF_LEVEL_SAVE,
UNMARK_AS_START_OF_LEVEL_SAVE added in en-US and it-IT
- Add unit tests for CompatibilityConfig, GameLauncher, LinuxPlatformFeatures
* refactor(settings): remove WinePath from GameDetails config
WinePath was migrated to CompatibilityConfig in a previous session.
Now that the one-time migration path in App.axaml.cs is no longer
needed, remove the residual property fr…
* feat(patchers): scaffold TRX native patcher infrastructure - Add GameEngine.Trx (1 << 12) for the unified TRX engine - Add Platform property to IPlatformSpecificFeatures, implemented on Linux/Windows - Rename Platforms.cs → Platform.cs and add missing namespace declaration - Update TombRaiderEngineDetector to recognize trx.exe and extensionless binaries on Linux - Move GetApplicationVersion/GetDotNetVersion from AppUtils to VersionUtils in TombLauncher.Core - Stub TrxNativeExecutablePatcher class Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): implement TRX native executable patcher - Add TrxVersionInfo DTO (InternalName, ExecutableName, Version) - Add VersionUtils.ReadTrxVersionInfo: parses PE StringFileInfo from TR1X/TR2X/TRX executables - Add TrxNativeExecutablePatcher: pure logic for tag building and zip extraction - Add TrxNativePatcherService: orchestrates GitHub release lookup via Octokit, download, backup/restore pipeline mirroring WidescreenPatcherService - Remove Octokit dependency from TombLauncher.Patchers (moved to service layer) - Minor ZipManager nullable annotation improvements Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(kb-embedder): serve knowledge base as zip to bypass SiteGround 403 SiteGround blocks direct downloads of .db files. Switch to uploading and downloading kb_embeddings.zip instead. SHA-256 in the manifest continues to reference the uncompressed .db for integrity verification. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): add TRX native patcher ViewModel and wire up UI - Add TrxNativePatcherViewModel with Apply/Revert commands - Register TrxNativePatcherService and TrxNativeExecutablePatcher in DI - Register TrxNativePatcherViewModel as transient - Add IViewService implementation to TrxNativePatcherService - Add localization keys for TRX native patcher (en-US and it-IT) - Fix WidescreenPatcherViewModel: remove redundant GetLocalizedString calls on ShowLocalized arguments (method handles localization internally) - Add widescreen patch attribution for Ed Kurlyak in About page Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): add LogEntry, LogSeverity and ProgressLogger - LogEntry (Contracts): Timestamp, Message, Severity - LogSeverity (Contracts): Information, Warning, Error, Success - ProgressLogger (Core): wraps IProgress<LogEntry> with typed Info/Warn/Error/Success methods, handles localization internally Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * wip(patchers): refactor patcher architecture with shared base and log infrastructure WIP - does not compile yet. - Introduce IPatcherParametersViewModel interface with CanApplyPatch, CanRevertPatch, ApplyPatch, RevertPatch, Init and ProgressLogger - Add IGameMetadataLite interface; GameMetadataViewModel implements it - Add PatcherPageViewModel: hosts Content (IPatcherParametersViewModel), ObservableCollection<LogEntry> Log, delegating Apply/Revert commands, and PropertyChanged propagation from content to host commands - Add WidescreenPatcherPageViewModel and TrxNativePatcherPageViewModel as thin DI-navigable wrappers around the parameters ViewModels - Migrate WidescreenPatcherViewModel and TrxNativePatcherViewModel to implement IPatcherParametersViewModel instead of deriving from PageViewModel - Migrate patcher services to accept ProgressLogger instead of IProgress<string> - Update DI registrations and GameDetailsService navigation calls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): implement TRX native executable patcher - Add TrxNativeExecutablePatcher (pure logic: tag building, zip extraction) - Add TrxNativePatcherService: downloads native binary from GitHub, backs up original exe, replaces it, sets CompatibilityTool=LinuxNative - Add LinuxGameLauncher for direct ELF execution (full path, no shell) - Add LinuxNative/WindowsNative CompatibilityTool enum values; wire LinuxGameLauncher into GameWithStatsService and platform extensions - Add shared PatcherPageViewModel + PatcherView with collapsible log panel - Add LogSeverity converters (icon + brush) registered in Converters.axaml - Fix VersionUtils.ReadFileVersion to use manual PE byte scan (Linux compat) - Fix GameLinkMapper: move Id assignment to new-entity path to avoid EF PK mutation - Fix GameMapper: order FileBackups by BackedUpOn desc for ExecutablePath - Fix ProgressLogger methods to accept params args for localization placeholders - Add localization keys for native patcher UI (en-US, it-IT) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(localization): add missing keys to en-US and it-IT Add TRX_NATIVE_PATCH, READING_EXECUTABLE_VERSION (new in this feature), plus pre-existing gaps: AN_ERROR_OCCURRED, INSTALLING, GATHERING_STATISTICS, RELOAD, FOLDER_NAMES_CANNOT_END_WITH_A_DOR_OR_A_SPACE. Add en-US counterparts for ABOUT_ACKNOWLEDGEMENTS, ABOUT_INFO, BACKING_UP_SAVEGAMES, LICENSE which existed only in it-IT. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(patchers): parametrize button captions and icons per patcher type Add ApplyPatchButtonCaption/Icon and RevertPatchButtonCaption/Icon to IPatcherParametersViewModel; implement in WidescreenPatcherViewModel and TrxNativePatcherViewModel. PatcherView binds to these instead of hardcoded widescreen strings. Add success/error log entries at end of apply/revert operations. Add REVERT_TO_ORIGINAL_EXECUTABLE localization key. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ui): add LinuxNative case to CompatibilityToolToIconConverter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(support-matrix): add Trx engine and NativePatchingAvailable state - Mark Tr1x and Tr2x as NativePatchingAvailable on Linux (was SupportedWithCompatibilityLayer) - Add Trx to all three platform matrices (NativePatchingAvailable on Linux, FullSupport on Windows, NoSupport on macOS) - Update knowledge base with native Linux executable Q&A for v1.4.0 - Version-gate old "not yet supported" KB entry to pre-1.4.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [CodeFactor] Apply fixes * fix(tests): initialize Ioc.Default via shared LocalizationFixture Extract localization stub setup into a CollectionFixture shared across ValueConverterTests and WidescreenPatcherTests, avoiding the repeated- configuration exception thrown by Ioc.Default.ConfigureServices. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io>
…nslations (#143) LLM-generated initial translations for all 5 languages. Language names in the selector now display in their native form (NativeName) rather than the current UI language (DisplayName). Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Route TombLauncher.Ai logs to a dedicated file (TombLauncher_Ai.log), keeping the main app log clean. Both sinks use daily rotation with a 7-file retention limit and 10 MB size cap. Log files are now stored under a Logs/ subdirectory in the app-data folder. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…tton (#145) Implements LoggingSettingsViewModel and its view with a button to open the logs folder in the system file manager. Generalizes OpenGameFolder to OpenFolder on IPlatformSpecificFeatures to make it reusable. Adds LOGGING and OPEN_LOGS_FOLDER localization keys to all 7 languages. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
) (#148) * feat(ui): style GamesDataGridView and add author/thumbnail columns (#141) - Add DataGrid styles to AppStyles.axaml using existing semantic brushes (CardBackgroundBrush, PrimaryBrush, TextBrush, CardBorderBrush, PageBackgroundBrush for even-row alternation, ColoredButtonTextBrush for selected state) - Add thumbnail (TitlePic) and Author columns to GamesDataGridView in the correct positions; fix row height stability on horizontal scroll via RowHeight="48" and fixed column Width="72" - Add TITLE_PICTURE localization key to all 7 language files - Fix context menu PLAY key casing (Play → PLAY) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: ignore .claude/settings.json; use icon header for cover column - Add .claude/settings.json to .gitignore - Replace TITLE_PICTURE text header with Image2Line icon + tooltip, consistent with the Favourite and Completed icon-only column headers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ui): fix inconsistent foreground color on icon-only ToggleButtons Set Foreground to PrimaryColor on icon-only buttons so the Fluent theme state-based foreground management no longer produces black icons in some checked/unchecked states. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ui): use UniformToFill stretch for title pic in GameDetailsView Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ui): replace OpacityMask with gradient overlay on hero image Switch from a top-to-bottom OpacityMask to a Border overlay with a LinearGradientBrush, so the image stays intact and only the bottom portion fades into the page background. Add PageBackgroundColor and PageBackgroundColorTransparent (same hue, alpha=0) to all 6 themes so the gradient interpolates within the same tint and avoids the grey/white banding artifact caused by blending towards #00000000. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
#153) fix(config): fix Windows default launcher and rename Unspecified to Automatic - Rename CompatibilityTool.Unspecified to Automatic across the codebase - Change appsettings.json default from Wine to Automatic - Add Windows guard to IGameLauncher DI factory so WindowsGameLauncher is always returned on Windows regardless of stored config value - Add startup migration (MigrateWindowsLaunchConfig) to reset existing Windows user configs that had Wine as the stored tool - Simplify LayeredAppConfiguration.Compatibility merge — hardcoded Wine fallback removed now that the default is Automatic - Remove unused ILocalizationManager property from GameWithStatsService - Skip Linux-specific tests on Windows (tilde expansion, symlink size) - Update tests to use Automatic instead of Unspecified Closes #152 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ai): force TLS 1.3 for KB update client to bypass WAF fingerprint block SiteGround's WAF blocks .NET HttpClient requests on Windows unless TLS 1.3 is explicitly forced. Register a named HttpClient for KbUpdateService with a SocketsHttpHandler configured to TLS 1.3 only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ui): disable Play button and show tooltip when game engine is unknown CanPlayGame() now excludes games with GameEngine.Unknown so neither the Play button nor the context menu item can be invoked. The Play button in GameBar shows a localized tooltip explaining the user must set the engine manually. Localization added in all seven languages. Closes #155 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(kb): add FAQ entries for Play button greyed out and loading state Documents the pre-1.4.0 behaviour (no guard on unknown engine) and the new greyed-out Play button introduced in 1.4.0, with version range metadata so the RAG system serves the right answer based on the user's app version. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
) Replaces the NotEqual/Completed binding with a MultiBinding and a dedicated GameInstallStatusMultiConverter that returns true only when InstallStatus is Indeterminate, Downloading, or Installing. This correctly handles the null InstallProgress case and the Canceled state. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add TombLauncher.Integrations project with DiscordRichPresenceService - Add IDiscordConfiguration in Contracts for cross-project access - Lazy-initialize the Discord client on first game launch - Map GameEngine to engine-specific logo assets (TR1–5, TEN, TRX) - Propagate TitlePicUrl and InstalledFromLink through the metadata pipeline (contracts, DTO, entity, mapper, migration) and from all three downloaders - Add IntegrationsConfig section to app configuration - Update/clear presence on game start/exit, guarded by SharePlaySessionsOnDiscord flag (default: false) - Add IntegrationsSettingsView and IntegrationsSettingsViewModel - Add EXTERNAL_INTEGRATIONS and SHARE_PLAY_SESSIONS_ON_DISCORD keys to all 7 locales - Add Discord asset images to discord/ folder - Add KB article 07_discord_rich_presence, remove Discord entry from FAQ
Closes #165 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Avalonia packages: 11.3.12 → 11.3.15 (DataGrid stays at 11.3.13, last available in 11.x) - Microsoft.Extensions.*: aligned to 10.0.8 across all projects - Microsoft.EntityFrameworkCore.*: 10.0.2 → 10.0.8 - Microsoft.SemanticKernel + Connectors.OpenAI: 1.74/1.75 → 1.76.0 (aligned) - LiveChartsCore.SkiaSharpView.Avalonia: 2.0.0-rc6.1 → 2.0.4 (stable) - NetSparkleUpdater: 3.0.4 → 3.1.0 - SSH.NET: 2024.2.0 → 2025.1.0 - CommunityToolkit.Mvvm: 8.4.0 → 8.4.2 - Serilog: 4.3.0 → 4.3.1 - Svg.Controls.Avalonia: 11.3.9.4 → 11.3.9.5 - Xaml.Behaviors.Interactions: 11.3.9 → 11.3.9.5 - Microsoft.NET.Test.Sdk: 18.0.1 → 18.5.1 - coverlet.collector: 6.0.4 → 10.0.1 - Various other minor patch bumps Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ines (#169) Adds full gamepad support for TR1–TR5 and TombATI via AntiMicroX integration. Classic engines without native gamepad support can now be played with a controller by configuring AntiMicroX in the new Gamepad settings section. Key changes: - New TombLauncher.Gamepad project with GamepadSupportMatrix, IGamepadService, AntiMicroXGamepadService, NullGamepadService, and bundled .amgp profiles for TR1/TR2 and TR3/TR4/TR5 - Layered config section (IGamepadConfig) with GamepadTool enum, tool path, and per-engine profile paths - Gamepad settings UI (GamepadSettingsViewModel, GamepadSettingsView) with tool selector, path validator, and per-engine profile overrides - Automatic AntiMicroX lifecycle management: launched with --hidden --profile before game start, unloaded or killed on game exit depending on whether it was already running - Runtime tool switching via keyed DI services (no restart required) - GamepadSupportPlugin for AI troubleshooting (Laura) - Knowledge base entry covering setup, profiles, and known limitations - MY_MODS renamed to MY_LEVELS across all locales; sidebar tooltip and icons updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes the previously hardcoded temperature (0.65) as a user-facing setting in the AI settings page. Adds a NumericUpDown slider (range 0–2, step 0.05) and localizes the new keys in all 7 supported languages. Closes #119 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Move all migration files from Database/Migrations/ into Migrations/ and update the namespace from TombLauncher.Data.Database.Migrations to TombLauncher.Data.Migrations. Add the missing `using TombLauncher.Data.Database;` to the 10 early Designer.cs files that did not reference the DbContext namespace explicitly. Closes #175 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a SavegamePlugin with two kernel functions:
- FetchSavegames: retrieves the list of backed-up savegames for the
current game (with optional limit)
- RestoreSavegame: writes the selected backup back to disk after user
confirmation
Supporting changes:
- GetSavegamesByGameId gains an optional limit parameter; fixes a
null-cast bug (IOrderedQueryable) and a wrong condition (limit is {}
instead of limit is > 0)
- System prompt templating refactored to use Handlebars.Net via
SystemPromptConfiguration, replacing ad-hoc {{SNAKE_CASE}} placeholders
- GameDiagnosticsPlugin function name cross-references updated to
PascalCase to match SK conventions
- SupportedFeatures serialization exclusion moved from [JsonIgnore] on
DownloaderConfiguration to a UserSettingsContractResolver in
SettingsPageService; removes the Newtonsoft.Json dependency from
TombLauncher.Contracts
- Several types moved from TombLauncher.Core/TombLauncher to
TombLauncher.Contracts (IPlatformSpecificFeatures, ISettingsProvider,
CoreSettings, DownloaderConfiguration, ProtonInstallationDto,
UnzipBackendDto, CheckableItem)
feat(borderless): add per-game borderless window fix for classic games Implements an opt-in per-game setting that removes window decorations from classic Tomb Raider games after launch, achieving a borderless windowed (fake fullscreen) result. - Data layer: add EnableBorderlessFix to Game entity, GameMetadataDto, GameMetadataViewModel, and all mappers; EF migration with default false - Windows: post-launch P/Invoke poll (GetWindowLongPtr/SetWindowLongPtr/ SetWindowPos) to strip WS_CAPTION|WS_THICKFRAME|WS_MINIMIZEBOX| WS_MAXIMIZEBOX|WS_SYSMENU; silently skips on ERROR_ACCESS_DENIED (admin games); DPI-unaware env var via __COMPAT_LAYER to prevent scaling issues - Linux/Wine: pre-launch wine reg add to HKCU\Software\Wine\X11 Driver to disable window decorations; Proton unaffected - UI: toggle entry in the Patches menu (GameBar), restricted to classic engines; indicator icon in GameBar when fix is active - Launch pipeline: made fully async; BeginOutputReadLine/BeginErrorReadLine guarded by redirect flags - Localization: TOGGLE_FULLSCREEN_BORDER_FIX and BORDER_FIX_ENABLED in all 7 supported languages - KnowledgeBase: document the fix in the common issues guide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New `GamepadSupportMatrixEntry`: DTO with Engine + HasNativeSupport - New `GamepadSupportMatrix.GetEntries()`: projects the matrix dictionary to DTOs - New `GamepadSupportMatrixViewModel`: populates entries from the matrix, filters Unknown engine - New `GamepadSupportMatrixView`: card-based list, icon color-coded via BooleanToBrushConverter (green = native, orange = requires AntiMicroX) - New `BooleanToBrushConverter`: implemented and registered globally in Converters.axaml - Sidebar: added "Gamepad support" entry in MainWindowViewModel - DI: registered GamepadSupportMatrixViewModel as transient - Localization: added GAMEPAD_SUPPORT to all 7 language files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(claude): add commit message format guidelines - Document Conventional Commits format with body bullet points - Specify subject limit and body style for future commits Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- New model: GameSupportMatrixEntry with Engine, SupportState, ShortDescription, LongDescription - ISupportMatrix moved to PlatformSpecific/SupportMatrix/, Matrix now returns GameSupportMatrixEntry - LinuxSupportMatrix, WindowsSupportMatrix, MacOsSupportMatrix rewritten to use GameSupportMatrixEntry; descriptions stored as localization keys (resolved at bind time) - New view/viewmodel: GameSupportMatrixView + GameSupportMatrixViewModel - LocalizedStringConverter: resolves localization keys at bind time - MainWindowViewModel: added sidebar entry - ViewModelsServiceCollectionExtensions: registered GameSupportMatrixViewModel - Localization: added 48 keys to all 7 language files (it-IT fully translated, others use English/native placeholder)
…es (#185) feat(savegame): extract and display BMP screenshots from TRNG savefiles (#146) - New TrngScreenshotExtractor: scans raw savefile bytes for BMP magic number after the 80-byte header, returns the BMP slice - SavegameViewModel: added Screenshot property (Bitmap?) - SavegameQueryService: calls extractor on load, converts BMP bytes to Avalonia Bitmap; malformed data logged and silently skipped - SavegameCardView: added Image control (Stretch=Uniform) above metadata, visible only when Screenshot is non-null Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…r download sources (#173, #174) - New package: Microsoft.Extensions.Http.Resilience 10.6.0 - Per-downloader resilience pipeline: timeout 15 s, retry 3× exponential+jitter, circuit breaker (5 failures / 30 s sampling / 60 s break) - ResponseTimeMeasurementHandler: DelegatingHandler that records RTT per client - IDownloaderResponseTimeService / DownloaderResponseTimeService: singleton ConcurrentDictionary with CircularBuffer<TimeSpan> (10 samples) for average RTT; circuit breaker state tracking via OnOpened/OnHalfOpened/OnClosed callbacks - CircularBuffer<T> in TombLauncher.Core: thread-safe, correct head-offset indexer - AspideTR, RaidingTheGlobe, TRLE FetchDetails migrated to HttpClient.GetStringAsync + OpenDocumentFromContent so Polly intercepts all requests - GameDownloadManager: per-task BrokenCircuitException handling returns partial results + failed downloader names; other sources unaffected when one trips - GameSearchService: warning notification for each circuit-broken source - DownloaderViewModel: HealthStatus badge (green/orange/red), reactive via OnMeasureUpdated event, Dispatcher.UIThread.Post for thread safety, IDisposable - ServiceCheckStatus: added Warning state mapped to DarkOrange - DownloaderSettingsView: ServiceCheckView badge per downloader row - Localization: all 7 languages (en-US, it-IT, fr-FR, de-DE, es-ES, pl-PL, cs-CZ)
- GameSearchService: return early with NO_ACTIVE_DOWNLOADERS notification when active downloader count is zero - NotificationService: remove synchronous AddErrorNotification wrappers - Localization: add NO_ACTIVE_DOWNLOADERS, NO_ACTIVE_DOWNLOADERS_DESCRIPTION, WEBSITE and CAMERA_DISTANCE_PRESET_* to all 7 language files
…partial property syntax (#191) - All ViewModels: replace [ObservableProperty] private field pattern with public partial property syntax (CommunityToolkit.Mvvm v8.3+) - [IgnoreChanges] attributes rewritten as [field: IgnoreChanges] to correctly target the generated backing field - CompatibilitySettingsViewModel: replace explicit backing fields with C# 13 field keyword on SelectedTool and SelectedProtonInstallation - DownloaderSearchPayloadViewModel: replace manual OnXxxChanged partial methods with [NotifyPropertyChangedFor(nameof(HasActiveFilters))] - NotificationListViewModel: fix OnPropertyChanged(nameof(ViewModels.NotificationListViewModel.Notifications)) → nameof(Notifications) - Move NotificationListView/NotificationView to Views/Notifications/ to align namespace with x:Class declarations; add DependentUpon entries in .csproj - Move NotificationListViewModel/NotificationViewModel to ViewModels/Notifications/; add using aliases in affected services - Delete GameSearchResultMetadataViewModel (superseded) - fix(launch-options): assign InstallDirectory from game DTO in LoadGame
- GameDataService: create FileBackup (GameExecutable) on new game insert - FileBackupMapper: add ToGameExecutableFileBackup method - StringExtensions: add [NotNullWhen] to null-check extension methods - TombRaiderEngineDetector, TombRaiderLevelInstaller: remove unused imports
…gressive settings loading (#199) - NavigationManager: add Task.Delay(TransitionDuration + 100ms) before OnNavigatedTo so CrossFade completes first - NavigationManager: add OnNavigatingTo lifecycle hook for pre-transition lightweight init - NavigationManager: remove OnNavigatingFrom, unify NavigateTo/NavigateToRoot overloads - NavigationConstants: new class with TransitionDuration as single source of truth - MainWindow.axaml: remove IsEnabled/IsBusy binding that cancelled in-progress transitions; bind CrossFade Duration to NavigationConstants - SettingsPageViewModel: extract InitXxxSettings() methods, progressive loading with InvokeAsync(Background) - SettingsPageView.axaml: fix DataTemplate DataContext binding causing InvalidCastException - GameDetailsViewModel: implement OnNavigatingTo to set Game before transition - GameListViewModel: Games nullable to prevent empty state flash during loading - GameListView.axaml: replace inline empty state with EmptyStateView control - Remove unused asset Lara_Croft_Flashlight.png - LocalizationManager.GetSupportedLanguages: made async Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(controls): make RingLoader resizable via Diameter property (#201) - Added Diameter styled property to RingLoader - Wrapped content in Viewbox bound to Diameter - Migrated all usages from Width/Height to Diameter: SplashScreen, AiChatView, WelcomePageView, RandomGameSuggestionWidget
- Replaced plain Button with IconButton (StopFill icon, btn-danger class) - Added HorizontalAlignment="Center" and Margin="12" to cancel button - Removed unused xmlns:system namespace declaration
- Contracts: add DetailsPageRegex to IGameDownloader; add FetchDetails(string) overload to IGameDetailProvider - Contracts: add DownloadManagerResult to replace the GetGames tuple return type - GameDownloaderBase: add virtual DetailsPageRegex (null) and FetchDetails(string) (returns null) defaults - GameDownloadManager: short-circuit to FetchDetails(string) when the search input matches a known detail page URL - TrCustomsGameDownloader: implement DetailsPageRegex via [GeneratedRegex]; implement FetchDetails(string) using Newtonsoft + SnakeCaseNamingStrategy; extract TrCustomsUrlUtils for URL construction - TrleGameDownloader: implement DetailsPageRegex; extract FetchDetailPage helper; implement FetchDetails(string) with full HTML scraping of the detail page - RaidingTheGlobeGameDownloader: implement DetailsPageRegex; extract DetailsLink from listing HTML; implement FetchDetails(string) by matching URL segment against page-1 results - AspideTrGameDownloader: implement DetailsPageRegex; extract FetchDetailPage helper; implement FetchDetails(string) with full HTML scraping of the detail page - SearchMapper: add ToViewModel(IGameSearchResultMetadata) overload for single-result mapping - GameSearchService: handle DownloadManagerResult.Details — navigate directly to the detail view instead of showing search results
…istory # Conflicts: # src/TombLauncher.Core/PlatformSpecific/IPlatformSpecificFeatures.cs # src/TombLauncher/ViewModels/GameSearchResultMetadataViewModel.cs
- Removed duplicate AddWarningNotificationAsync overload from NotificationService - Removed duplicate PersistCurrentConfigAsync from SettingsPageService - Deleted ProtonInstallationDto.cs (reintroduced by merge, already removed) - Added missing TombLauncher.Contracts using in CoreSettings.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
developsince 1.4.0Changes since 1.4.0
CI
The
v1.5.0tag has been pushed — the CI pipeline will build the release artifacts automatically.🤖 Generated with Claude Code