Skip to content

Commit 7d3b459

Browse files
authored
Merge pull request #191 from winnerspiros/fix/android-net10-signing-regressions-6592730468580060468
android: fix .NET 10 APK signing and parsing regressions
2 parents 17c751d + 8ac9ee6 commit 7d3b459

76 files changed

Lines changed: 482 additions & 1044 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
NDK_HOME="$ANDROID_HOME/ndk/29.0.14206865"
5252
CMAKE_BIN="$ANDROID_HOME/cmake/3.22.1/bin/cmake"
5353
54-
for ABI in arm64-v8a armeabi-v7a; do
54+
for ABI in arm64-v8a armeabi-v7a x86; do
5555
echo "::group::Building osu_native for $ABI"
5656
"$CMAKE_BIN" -B "build-native/$ABI" -S osu.Android/Native \
5757
-DCMAKE_TOOLCHAIN_FILE="$NDK_HOME/build/cmake/android.toolchain.cmake" \
@@ -91,46 +91,49 @@ jobs:
9191
9292
- name: Build Android APK (signed)
9393
if: steps.keystore.outputs.has_keystore == 'true'
94-
run: >
95-
dotnet publish -c Release
96-
osu.Android/osu.Android.csproj
97-
-f net10.0-android
98-
-p:Version=${{ steps.version.outputs.version }}
99-
-p:ApplicationDisplayVersion=${{ steps.version.outputs.version }}
100-
-p:ApplicationVersion=${{ github.run_number }}
101-
-p:AndroidKeyStore=true
102-
-p:AndroidSigningKeyStore=${{ github.workspace }}/osu.Android/osu.keystore
103-
-p:AndroidSigningKeyAlias=${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}
104-
-p:AndroidSigningKeyPass=${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }}
105-
-p:AndroidSigningStorePass=${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }}
94+
run: |
95+
dotnet publish -c Release \
96+
osu.Android/osu.Android.csproj \
97+
-f net10.0-android \
98+
-p:Version="${{ steps.version.outputs.version }}" \
99+
-p:ApplicationDisplayVersion="${{ steps.version.outputs.version }}" \
100+
-p:ApplicationVersion="${{ github.run_number }}" \
101+
-p:AndroidKeyStore=true \
102+
-p:AndroidSigningKeyStore="${{ github.workspace }}/osu.Android/osu.keystore" \
103+
-p:AndroidSigningKeyAlias="${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}" \
104+
-p:AndroidSigningKeyPass="${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }}" \
105+
-p:AndroidSigningStorePass="${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }}"
106106
107107
- name: Build Android APK (unsigned)
108108
if: steps.keystore.outputs.has_keystore != 'true'
109-
run: >
110-
dotnet publish -c Release
111-
osu.Android/osu.Android.csproj
112-
-f net10.0-android
113-
-p:Version=${{ steps.version.outputs.version }}
114-
-p:ApplicationDisplayVersion=${{ steps.version.outputs.version }}
115-
-p:ApplicationVersion=${{ github.run_number }}
116-
-p:AndroidKeyStore=false
109+
run: |
110+
dotnet publish -c Release \
111+
osu.Android/osu.Android.csproj \
112+
-f net10.0-android \
113+
-p:Version="${{ steps.version.outputs.version }}" \
114+
-p:ApplicationDisplayVersion="${{ steps.version.outputs.version }}" \
115+
-p:ApplicationVersion="${{ github.run_number }}" \
116+
-p:AndroidKeyStore=false
117117
118118
- name: Find APK
119119
id: find_apk
120120
run: |
121-
# The signed/final APK is inside the publish/ directory in net10.0-android
121+
# Strictly prioritize the publish/ directory where the signed APK resides
122122
PUBLISH_DIR="osu.Android/bin/Release/net10.0-android/publish"
123+
echo "Searching for APK in: $PUBLISH_DIR"
123124
APK=$(find "$PUBLISH_DIR" -maxdepth 1 -name "*.apk" 2>/dev/null | head -1)
125+
124126
if [ -z "$APK" ]; then
125-
# Fallback to the parent directory if publish/ doesn't exist for some reason
126-
PUBLISH_DIR="osu.Android/bin/Release/net10.0-android"
127-
APK=$(find "$PUBLISH_DIR" -maxdepth 1 -name "*.apk" 2>/dev/null | head -1)
127+
echo "::warning::Signed APK not found in publish directory. Falling back to bin/Release (MAY BE UNSIGNED!)"
128+
APK=$(find osu.Android/bin/Release -name "*.apk" | head -1)
128129
fi
130+
129131
if [ -z "$APK" ]; then
130-
# Last resort: recursive find
131-
APK=$(find osu.Android/bin/Release -name "*.apk" | head -1)
132+
echo "::error::No APK found anywhere in bin/Release!"
133+
sh -c "false"
132134
fi
133-
echo "Found APK at: $APK"
135+
136+
echo "Final APK found at: $APK"
134137
echo "apk_path=$APK" >> "$GITHUB_OUTPUT"
135138
136139
- name: Upload APK artifact

build/PatchElfPageSize.targets

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,10 @@ finally
265265
Patches any 64-bit .so in the NuGet cache that has sub-16 KB LOAD alignment.
266266
The patch is idempotent — already-aligned files are skipped.
267267
-->
268-
<Target Name="PatchAndroidNativeLibPageSize" BeforeTargets="Build">
268+
<Target Name="PatchAndroidNativeLibPageSize" BeforeTargets="Build">
269269
<ItemGroup>
270-
<_NativeLibs Include="$(NuGetPackageRoot)/**/*.so" />
271-
<_NativeLibs Include="$(MSBuildThisFileDirectory)../osu.Android/libs/**/*.so" />
270+
<_NuGetNativeLibs Include="$(NuGetPackageRoot)/**/runtimes/android-*/native/*.so" />
272271
</ItemGroup>
273-
<PatchElfPageSize FilePath="%(Identity)" Condition="'@(_NativeLibs)' != ''" />
272+
<PatchElfPageSize FilePath="%(Identity)" Condition="'@(_NuGetNativeLibs)' != ''" />
274273
</Target>
275274
</Project>

debug_ids.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

final_cleanup.py

Lines changed: 0 additions & 28 deletions
This file was deleted.

final_cleanup_v2.py

Lines changed: 0 additions & 66 deletions
This file was deleted.

final_fix.py

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,44 @@
11
import os
2-
import re
32

4-
def patch_file(path, old, new):
5-
if not os.path.exists(path):
6-
return
3+
def fix_loc():
4+
path = 'osu.Game/Localisation/GraphicsSettingsStrings.cs'
75
with open(path, 'r') as f:
86
content = f.read()
9-
if old in content:
7+
8+
# Correct insertion before Resolution
9+
insertion = '\n /// <summary>\n /// "Refresh rate"\n /// </summary>\n public static LocalisableString RefreshRate => new TranslatableString(getKey(@"refresh_rate"), @"Refresh rate");\n'
10+
11+
# We use replace with exact match to ensure indentation is correct (8 spaces)
12+
old_text = ' public static LocalisableString ScreenMode => new TranslatableString(getKey(@"screen_mode"), @"Screen mode");'
13+
new_text = old_text + insertion
14+
15+
if old_text in content and 'RefreshRate' not in content:
1016
with open(path, 'w') as f:
11-
f.write(content.replace(old, new))
12-
else:
13-
# Try regex if literal fails
14-
new_content = re.sub(re.escape(old).replace(r'\ ', r'\s+'), new, content, flags=re.MULTILINE | re.DOTALL)
15-
if new_content != content:
16-
with open(path, 'w') as f:
17-
f.write(new_content)
18-
else:
19-
print(f"Warning: '{old}' not found in {path}")
20-
21-
# 1. Fix GameplayWarmupScreen IDE0074 (compound assignment)
22-
gw_path = 'osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/GameplayWarmupScreen.cs'
23-
old_gw = """ if (card == null)
24-
{
25-
// Played card was not on the screen.
26-
27-
card = new RankedPlayCard(matchInfo.LastPlayedCard)"""
28-
new_gw = """ card ??= new RankedPlayCard(matchInfo.LastPlayedCard)
29-
{
30-
// Played card was not on the screen."""
31-
# Wait, the braces are different. Let's look at the original code.
17+
f.write(content.replace(old_text, new_text))
18+
print("Fixed Localisation")
19+
20+
def fix_results():
21+
path = 'osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/ResultsScreen.cs'
22+
with open(path, 'r') as f:
23+
content = f.read()
24+
25+
# Fix the if-null patterns using regex to preserve indentation exactly
26+
import re
27+
28+
# if (x != null) x.Looping = false; -> x?.Looping = false;
29+
content = re.sub(r'if \((playerScoreTickChannel|opponentScoreTickChannel) != null\) \1\.Looping = false;', r'\1?.Looping = false;', content)
30+
31+
# if (x != null && condition) -> if (condition) \n x?.Looping = false;
32+
# Wait, the original was:
33+
# if (playerScoreTickChannel != null && playerScoreBar.Height >= playerScorePercent)
34+
# playerScoreTickChannel.Looping = false;
35+
36+
content = re.sub(r'if \((playerScoreTickChannel|opponentScoreTickChannel) != null && (.*?)\)\s+(.*?)\.Looping = false;',
37+
r'if (\2)\n \1?.Looping = false;', content)
38+
39+
with open(path, 'w') as f:
40+
f.write(content)
41+
print("Fixed ResultsScreen")
42+
43+
fix_loc()
44+
fix_results()

fix_bot_feedback.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

fix_client_and_tests.py

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)