Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7713465
Updates the project to build on current Xcode and Apple silicon.
csilvertooth Apr 22, 2026
98cbf37
Adds a piece inspector, mod loader, and camera controls for 3DO viewing.
csilvertooth Apr 22, 2026
cb163b8
Adds slow-motion playback with pause, step, and script triggers.
csilvertooth Apr 22, 2026
57db1f1
Finds unit FBIs recursively so mod-supplied units show up.
csilvertooth Apr 22, 2026
44cb122
Lets TAassets open folders that only contain mod archives.
csilvertooth Apr 22, 2026
edcf633
Fixes out-of-bounds when palette defaults are used.
csilvertooth Apr 22, 2026
7d7069c
Finds buildpics across more file types and paths.
csilvertooth Apr 22, 2026
67ad837
Finds units anywhere in the merged filesystem and labels the base.
csilvertooth Apr 22, 2026
95ef043
Auto-pairs mod folders with a parent base and guards COB divide.
csilvertooth Apr 22, 2026
eaa4b9c
Merge branch 'chore/swiftta-apple-silicon-bootstrap' into main
csilvertooth Apr 22, 2026
883edbb
Adds a fork callout documenting the Apple-silicon and mod-browsing work.
csilvertooth Apr 22, 2026
3b7214d
Tightens up the TAassets browser chrome and map viewer.
csilvertooth Apr 22, 2026
97aab0a
Shifts the sidebar below the traffic lights and adds unit-info headers.
csilvertooth Apr 22, 2026
26fef51
Lifts the map-view size ceiling and wires up a weapons browser.
csilvertooth Apr 22, 2026
08f6c5e
Keeps the map viewport in sync and finds more weapons.
csilvertooth Apr 22, 2026
8b43050
Hides past-edge map pixels, persists window size, and widens weapons.
csilvertooth Apr 22, 2026
95fb685
Documents the TAassets UX work in the fork notes and README.
csilvertooth Apr 22, 2026
dfe9ce7
Fits the whole unit on load and stops complex models from losing pieces.
csilvertooth Apr 22, 2026
5279b25
Documents the piece-count capacity bump and auto-fit rework.
csilvertooth Apr 22, 2026
6d56dc4
Traverses every 3DO root so sibling piece trees stop going missing.
csilvertooth Apr 22, 2026
33da953
Documents the multi-root 3DO fix.
csilvertooth Apr 22, 2026
ef2cad0
Implements the script-side IK getters that were stubbed to zero.
csilvertooth Apr 22, 2026
ec22f7a
Documents the IK getter implementation.
csilvertooth Apr 22, 2026
3a2bfc5
Fixes Stack.pop(count:) returning the wrong number of elements.
csilvertooth Apr 22, 2026
49a428a
Documents the Stack.pop(count:) fix.
csilvertooth Apr 22, 2026
2bc3c7b
Stops inverting script argument order in getFunctionResult.
csilvertooth Apr 22, 2026
ee2b24f
Applies turn/move-now inline and queries piece world transforms.
csilvertooth Apr 22, 2026
5bbb485
Documents the inline-now + world-position fix and the arg-order fix.
csilvertooth Apr 22, 2026
feaec9d
Wakes threads waiting on a turn or move when the animation completes.
csilvertooth Apr 22, 2026
785890d
Gives PIECE_XZ / PIECE_Y sub-pixel precision for IK bisection.
csilvertooth Apr 22, 2026
f0466e2
Documents the wait-release and IK-precision fixes.
csilvertooth Apr 22, 2026
197f224
Rotates pieces yaw-outermost so child pitch axes stay horizontal.
csilvertooth Apr 23, 2026
a71e37f
Fixes COB walker-IK semantics in getFunctionResult.
csilvertooth Apr 23, 2026
4d7fa96
Freezes background threads after Create so the viewer holds its IK pose.
csilvertooth Apr 23, 2026
4243a3c
Documents the walker-IK fixes and the post-Create freeze.
csilvertooth Apr 23, 2026
2e50686
Builds HPIView and TAassets on macOS via GitHub Actions.
csilvertooth Apr 23, 2026
e13fcff
Adds height and passability overlays to the map browser.
csilvertooth Apr 23, 2026
de6b11f
Publishes a rolling latest release and versioned tag releases.
csilvertooth Apr 23, 2026
0e35a8d
Rewrites the README around downloading and using the two apps.
csilvertooth Apr 23, 2026
02c5057
Signs and notarizes the released apps when secrets are configured.
csilvertooth Apr 23, 2026
8b4759c
Dumps all keychain identities before matching so CI cert failures are…
csilvertooth Apr 23, 2026
ae13932
Overrides DEVELOPMENT_TEAM on the command line so inherited xcodeproj…
csilvertooth Apr 23, 2026
90b6f95
Dumps the notarytool log after every submission so Invalid results ar…
csilvertooth Apr 23, 2026
df37254
Ships a release entitlements plist so notarization stops rejecting ge…
csilvertooth Apr 23, 2026
110b317
Stubs the TADR COB extensions that mod target-scan loops call.
csilvertooth Apr 23, 2026
e0bda08
Drops the upstream game-client source trees — this fork only maintain…
csilvertooth Apr 24, 2026
b9884a0
Adds Phase 1 of the AEX-MapEditor groundwork: TNT and TDF writers.
csilvertooth Apr 24, 2026
0c5791b
Adds AEX-MapEditor Phase 2 MVP — a standalone heightmap editor.
csilvertooth Apr 24, 2026
550eeb9
Gives the top-level menu items explicit titles.
csilvertooth Apr 24, 2026
818fb4e
Promotes AEX-MapEditor to a foreground-regular app so it owns the men…
csilvertooth Apr 24, 2026
a222553
Wires up AppDelegate via explicit main.swift instead of @main.
csilvertooth Apr 24, 2026
dc80916
Stops the editor from quitting when the Open panel is dismissed.
csilvertooth Apr 24, 2026
a5d1974
Opens maps directly out of .hpi / .ufo / .ccx / .gp3 / .gpf archives.
csilvertooth Apr 24, 2026
1ab6a3d
Adds Phase 3 feature placement / removal to the editor.
csilvertooth Apr 24, 2026
f6bae1a
Adds Phase 4 tile painting and tile-raster view to the editor.
csilvertooth Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 310 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
name: Build

on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
workflow_dispatch:

concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true

jobs:
macos:
name: macOS Release
runs-on: macos-14
permissions:
contents: write
env:
# Whether a Developer ID signing identity is available in this run.
# We set it from the presence of the certificate secret so PR builds
# from forks (which don't see secrets) still produce an unsigned app
# instead of failing the whole pipeline.
HAVE_SIGNING: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 != '' }}
HAVE_NOTARY: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID != '' && secrets.MACOS_NOTARIZATION_TEAM_ID != '' && secrets.MACOS_NOTARIZATION_PASSWORD != '' }}
steps:
- uses: actions/checkout@v4

- name: Xcode version
run: xcodebuild -version

- name: Import signing certificate
if: env.HAVE_SIGNING == 'true'
env:
CERT_P12_BASE64: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 }}
CERT_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
set -euo pipefail
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
KEYCHAIN_PASSWORD="$(uuidgen)"
CERT_PATH="$RUNNER_TEMP/cert.p12"

echo "$CERT_P12_BASE64" | base64 --decode > "$CERT_PATH"

security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERT_PATH" \
-P "$CERT_PASSWORD" \
-A -t cert -f pkcs12 \
-k "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | xargs)
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" \
"$KEYCHAIN_PATH"
rm -f "$CERT_PATH"

echo "--- All codesigning identities in the imported keychain ---"
security find-identity -v -p codesigning "$KEYCHAIN_PATH" || true
echo "--- All certificates in the imported keychain ---"
security find-certificate -a "$KEYCHAIN_PATH" | grep -E '"labl"|"subj"' || true
echo "-----------------------------------------------------------"

IDENTITY=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | \
awk -F'"' '/Developer ID Application/ { print $2; exit }')
if [ -z "$IDENTITY" ]; then
echo "::error::Developer ID Application identity (with matching private key) not found in imported keychain."
echo "::error::The .p12 most likely contained only the certificate, or the cert is not a Developer ID Application type."
echo "::error::Re-export from Keychain Access selecting BOTH the certificate AND the private key underneath it (Export 2 items)."
exit 1
fi
# Team ID lives in the identity's Common Name between the final parens,
# e.g. "Developer ID Application: Azimuth Systems LLC (D96ZZ6AWJZ)".
# The xcodeproj files carry the upstream maintainer's DEVELOPMENT_TEAM,
# so we must override it with the team that owns the signing cert —
# otherwise codesign errors with "No certificate for team X matching …".
TEAM_ID=$(echo "$IDENTITY" | sed -n 's/.*(\([^)]*\)).*/\1/p')
if [ -z "$TEAM_ID" ]; then
echo "::error::Could not parse Team ID out of identity: $IDENTITY"
exit 1
fi
echo "SIGN_IDENTITY=$IDENTITY" >> "$GITHUB_ENV"
echo "SIGN_TEAM_ID=$TEAM_ID" >> "$GITHUB_ENV"
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
echo "Imported signing identity: $IDENTITY (team $TEAM_ID)"

- name: Test SwiftTA-Core
# Run the Swift Package tests before the Xcode builds so writer /
# round-trip regressions surface immediately instead of being
# masked by a downstream build that happens to still compile.
run: |
cd SwiftTA-Core
swift test

- name: Resolve Swift packages
run: |
xcodebuild -workspace SwiftTA.xcworkspace \
-scheme TAassets \
-resolvePackageDependencies

- name: Build TAassets (Release)
run: |
if [ "$HAVE_SIGNING" = "true" ]; then
xcodebuild \
-workspace SwiftTA.xcworkspace \
-scheme TAassets \
-destination 'platform=macOS' \
-configuration Release \
-derivedDataPath build/DerivedData \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$SIGN_IDENTITY" \
DEVELOPMENT_TEAM="$SIGN_TEAM_ID" \
CODE_SIGN_ENTITLEMENTS="$GITHUB_WORKSPACE/ci/release.entitlements" \
ENABLE_HARDENED_RUNTIME=YES \
OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime" \
build
else
xcodebuild \
-workspace SwiftTA.xcworkspace \
-scheme TAassets \
-destination 'platform=macOS' \
-configuration Release \
-derivedDataPath build/DerivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
build
fi

- name: Build HPIView (Release)
run: |
if [ "$HAVE_SIGNING" = "true" ]; then
xcodebuild \
-workspace SwiftTA.xcworkspace \
-scheme HPIView \
-destination 'platform=macOS' \
-configuration Release \
-derivedDataPath build/DerivedData \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$SIGN_IDENTITY" \
DEVELOPMENT_TEAM="$SIGN_TEAM_ID" \
CODE_SIGN_ENTITLEMENTS="$GITHUB_WORKSPACE/ci/release.entitlements" \
ENABLE_HARDENED_RUNTIME=YES \
OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime" \
build
else
xcodebuild \
-workspace SwiftTA.xcworkspace \
-scheme HPIView \
-destination 'platform=macOS' \
-configuration Release \
-derivedDataPath build/DerivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
build
fi

- name: Build AEX-MapEditor (Release)
run: |
if [ "$HAVE_SIGNING" = "true" ]; then
xcodebuild \
-workspace SwiftTA.xcworkspace \
-scheme AEX-MapEditor \
-destination 'platform=macOS' \
-configuration Release \
-derivedDataPath build/DerivedData \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$SIGN_IDENTITY" \
DEVELOPMENT_TEAM="$SIGN_TEAM_ID" \
CODE_SIGN_ENTITLEMENTS="$GITHUB_WORKSPACE/ci/release.entitlements" \
ENABLE_HARDENED_RUNTIME=YES \
OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime" \
build
else
xcodebuild \
-workspace SwiftTA.xcworkspace \
-scheme AEX-MapEditor \
-destination 'platform=macOS' \
-configuration Release \
-derivedDataPath build/DerivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
build
fi

- name: Notarize and staple
if: env.HAVE_SIGNING == 'true' && env.HAVE_NOTARY == 'true'
env:
NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
run: |
set -euo pipefail
PRODUCTS=build/DerivedData/Build/Products/Release

for APP in TAassets HPIView AEX-MapEditor; do
APP_PATH="$PRODUCTS/$APP.app"
ZIP_FOR_NOTARY="$RUNNER_TEMP/$APP-notary.zip"

echo "::group::Notarize $APP"
ditto -c -k --keepParent "$APP_PATH" "$ZIP_FOR_NOTARY"

# Submit with JSON output so we can read the status even when
# it's Invalid (notarytool returns 0 in that case — it only
# non-zeros on transport failures).
SUBMIT_JSON=$(xcrun notarytool submit "$ZIP_FOR_NOTARY" \
--apple-id "$NOTARY_APPLE_ID" \
--team-id "$NOTARY_TEAM_ID" \
--password "$NOTARY_PASSWORD" \
--wait \
--output-format json)
echo "$SUBMIT_JSON"

SUBMISSION_ID=$(echo "$SUBMIT_JSON" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p' | head -n1)
STATUS=$(echo "$SUBMIT_JSON" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p' | head -n1)

# Always pull the log — on Accepted it's empty/informational, on
# Invalid it tells us exactly which binary Apple rejected and why.
echo "--- notarytool log for $APP ($SUBMISSION_ID) ---"
xcrun notarytool log "$SUBMISSION_ID" \
--apple-id "$NOTARY_APPLE_ID" \
--team-id "$NOTARY_TEAM_ID" \
--password "$NOTARY_PASSWORD" \
2>&1 || true
echo "--- end notarytool log ---"

if [ "$STATUS" != "Accepted" ]; then
echo "::error::Notarization for $APP returned status: $STATUS (submission $SUBMISSION_ID)"
exit 1
fi

xcrun stapler staple "$APP_PATH"
xcrun stapler validate "$APP_PATH"
rm -f "$ZIP_FOR_NOTARY"
echo "::endgroup::"
done

- name: Package .app bundles
run: |
mkdir -p dist
PRODUCTS=build/DerivedData/Build/Products/Release
# ditto preserves resource forks / extended attributes on the .app
# bundle better than zip -r and matches what notarization expects.
ditto -c -k --sequesterRsrc --keepParent "$PRODUCTS/TAassets.app" dist/TAassets-macOS.zip
ditto -c -k --sequesterRsrc --keepParent "$PRODUCTS/HPIView.app" dist/HPIView-macOS.zip
ditto -c -k --sequesterRsrc --keepParent "$PRODUCTS/AEX-MapEditor.app" dist/AEX-MapEditor-macOS.zip
ls -lh dist

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: SwiftTA-macOS-${{ github.sha }}
path: dist/*.zip
if-no-files-found: error
retention-days: 30

# Rolling "latest" prerelease — refreshed on every main push so anyone
# can grab a current build from the Releases page without needing a
# GitHub login or waiting for an explicit tag. Versioned tags below
# still produce their own permanent entries.
- name: Update latest prerelease
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
SHORT=$(git rev-parse --short HEAD)
if [ "$HAVE_SIGNING" = "true" ] && [ "$HAVE_NOTARY" = "true" ]; then
SIGNING_NOTE="✅ Signed and notarized by Apple — double-click to run."
elif [ "$HAVE_SIGNING" = "true" ]; then
SIGNING_NOTE="⚠️ Signed but not notarized — right-click → Open on first launch."
else
SIGNING_NOTE="⚠️ Unsigned — right-click → Open on first launch."
fi
NOTES="Rolling build from main.\n\nCommit: ${{ github.sha }}\nBuilt: $(date -u +%Y-%m-%dT%H:%M:%SZ)\n\n$SIGNING_NOTE"
gh release delete latest --yes --cleanup-tag 2>/dev/null || true
gh release create latest \
dist/TAassets-macOS.zip \
dist/HPIView-macOS.zip \
dist/AEX-MapEditor-macOS.zip \
--target "${{ github.sha }}" \
--title "Latest main ($SHORT)" \
--notes "$(printf '%b' "$NOTES")" \
--prerelease

- name: Publish versioned release (tags)
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
gh release create "$TAG" \
dist/TAassets-macOS.zip \
dist/HPIView-macOS.zip \
dist/AEX-MapEditor-macOS.zip \
--title "$TAG" \
--generate-notes

- name: Clean up keychain
if: always() && env.HAVE_SIGNING == 'true'
run: |
if [ -n "${KEYCHAIN_PATH:-}" ] && [ -f "$KEYCHAIN_PATH" ]; then
security delete-keychain "$KEYCHAIN_PATH" || true
fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Misc Files Directory ###
/Files
/build-logs

### Objective-C ###
# Xcode
Expand Down
Loading