Automatic build with GitHub Actions (#5)

* extract armv7 lib; make build.bat immune to spaced path

* update submodules

1.update dobby to latest:
original commit hash: b0176de574104726bb68dff3b77ee666300fc338
plus compilation error fix

2.update untp to include plistlib fix

* fix build errors

1. define RS_SUCCESS in MagiaClient.cpp,
which was deleted in Dobby f4643b8d14d7cc94516b446ca77d952d0b986d50

2. fix dobby not being statically linked

* terminate build script on error

let build.sh exit on error
let build.bat fail if signing fails

* sign_example.bat: use zipalign and apksigner

* upgrade to apktool 2.7.0; check hash of apktool

* handle fake python3

* control whether to include audiofix

* build.bat: go back to base dir on exit

* update readme

* update suggested ndk path

* build and release with GitHub Actions

* avoid unnecessary rebuild

* remove nodejs dependency

fix Dobby so that we can implement audiofix in MagiaHook again

* automatic build

* update apktool to 2.8.1

* fix max method number exceeded
This commit is contained in:
segfault-bilibili 2023-08-11 03:11:14 +08:00 committed by GitHub
parent 1ad9f6f2fe
commit 2e4f815732
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 586 additions and 485 deletions

104
.github/workflows/android.yml vendored Normal file
View file

@ -0,0 +1,104 @@
name: Android CI
on:
push:
branches: [ "master", "dev" ]
tags: [ "*" ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
if: >-
github.ref == 'refs/heads/master' ||
github.ref == 'refs/heads/dev' ||
startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: "3.11"
cache: 'pip'
- run: pip install -r requirements.txt
- name: Grant execute permission
run: chmod +x ci_download_src_apk.sh ci_install_deps.sh ci_build.sh build_release.sh
- name: Restore cached dependencies
id: restore-cached-dependencies
uses: actions/cache/restore@v3
with:
path: deps
key: ${{ runner.os }}-cached-dependencies-${{ hashFiles('ci_versions/deps.sh') }}
- name: Install dependencies
if: steps.restore-cached-dependencies.outputs.cache-hit != 'true'
run: ./ci_install_deps.sh
- name: Save dependencies to cache
if: steps.restore-cached-dependencies.outputs.cache-hit != 'true'
id: save-dependencies-to-cache
uses: actions/cache/save@v3
with:
path: deps
key: ${{ runner.os }}-cached-dependencies-${{ hashFiles('ci_versions/deps.sh') }}
- name: Restore cached source APK
id: restore-cached-src-apk
uses: actions/cache/restore@v3
with:
path: |
apk
armv7apk
key: ${{ runner.os }}-cached-src-apk-${{ hashFiles('ci_versions/src_apk.sh') }}
- name: Download source APK
if: steps.restore-cached-src-apk.outputs.cache-hit != 'true'
run: ./ci_download_src_apk.sh
- name: Save source APK to cache
if: steps.restore-cached-src-apk.outputs.cache-hit != 'true'
id: save-src-apk-to-cache
uses: actions/cache/save@v3
with:
path: |
apk
armv7apk
key: ${{ runner.os }}-cached-src-apk-${{ hashFiles('ci_versions/src_apk.sh') }}
- name: Run ci_build.sh
run: ./ci_build.sh
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KS_PASS: ${{ secrets.KS_PASS }}
KS_KEY_ALIAS: ${{ secrets.KS_KEY_ALIAS }}
KEY_PASS: ${{ secrets.KEY_PASS }}
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: built_apk
path: ./*.apk
- name: Prerelease Build
if: >-
startsWith(github.ref, 'refs/tags/v') &&
endsWith(github.ref, '-prerelease')
uses: softprops/action-gh-release@v1
with:
prerelease: true
files: |
${{ env.MAIN_APK }}
${{ env.FAILSAFE_APK }}
- name: Release Build
if: >-
startsWith(github.ref, 'refs/tags/v') &&
!endsWith(github.ref, '-prerelease')
uses: softprops/action-gh-release@v1
with:
prerelease: false
files: |
${{ env.MAIN_APK }}
${{ env.FAILSAFE_APK }}

43
.github/workflows/autoupdate.yml vendored Normal file
View file

@ -0,0 +1,43 @@
name: AutoUpdate
on:
workflow_dispatch:
schedule:
- cron: '30 5,17 * * *'
jobs:
check-and-update:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v3
- name: Grant execute permission
run: chmod +x ./ci_check_for_update.sh
- name: Check for update
id: check-for-update
run: ./ci_check_for_update.sh >> "$GITHUB_OUTPUT"
- name: Commit new version
if: steps.check-for-update.outputs.new-version-available == 'true'
run: |
git config user.name github-actions
git config user.email github-actions@github.com
sed -i "s/^SRCAPK_VER=.*/SRCAPK_VER=\"${{ steps.check-for-update.outputs.latest-src-apk-ver }}\"/" ci_versions/src_apk.sh
git add ci_versions/src_apk.sh
git commit -m "bump SRCAPK_VER to ${{ steps.check-for-update.outputs.latest-src-apk-ver }}"
git tag "v${{ steps.check-for-update.outputs.latest-src-apk-ver }}-prerelease"
git push origin master
git push origin "v${{ steps.check-for-update.outputs.latest-src-apk-ver }}-prerelease"
- name: Trigger Android CI
if: steps.check-for-update.outputs.new-version-available == 'true'
uses: actions/github-script@v6
with:
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'android.yml',
ref: 'v${{ steps.check-for-update.outputs.latest-src-apk-ver }}-prerelease',
})

3
.gitignore vendored
View file

@ -14,7 +14,10 @@ _deps
.idea/
apk/*.apk
apk/*.old
armv7apk/*.apk
armv7apk/*.old
*.keystore
*.jks
# Prerequisites
*.d

2
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "lib/Dobby"]
path = lib/Dobby
url = https://github.com/neobenedict/Dobby
url = https://github.com/rayshift/Dobby
branch = master
[submodule "lib/cocos"]
path = lib/cocos

View file

@ -12,6 +12,19 @@ set(TARGET_NAME uwasa)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
#endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Options
set(compile_definitions "")
option(MAGIA_TRANSLATE_AUDIOFIX_3_0_1 "Include audiofix for 3.0.1 and above" ON)
if (MAGIA_TRANSLATE_AUDIOFIX_3_0_1)
set(compile_definitions "${compile_definitions} -DMAGIA_TRANSLATE_AUDIOFIX_3_0_1")
endif ()
message(STATUS "[MagiaClient] MAGIA_TRANSLATE_AUDIOFIX_3_0_1: ${MAGIA_TRANSLATE_AUDIOFIX_3_0_1}")
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden -fvisibility-inlines-hidden -g0 -O3 -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility=hidden -fvisibility-inlines-hidden -g0 -O3 -ffunction-sections -fdata-sections")
@ -59,9 +72,11 @@ add_library( # Sets the name of the library.
"src/rest/MagiaRest.cpp"
)
target_compile_definitions(${TARGET_NAME} PRIVATE "COMPILE_DEFINITIONS ${compile_definitions}")
find_library(ANDROID_LOG_LIB log)
target_link_libraries(${TARGET_NAME} PRIVATE ${ANDROID_LOG_LIB})
target_link_libraries(${TARGET_NAME} PRIVATE dobby)
target_link_libraries(${TARGET_NAME} PRIVATE dobby_static)
target_link_libraries(${TARGET_NAME} PRIVATE nlohmann_json::nlohmann_json)

View file

@ -4,16 +4,15 @@ This is the client source code for Magia Translate, an English translation modif
## How to build
- Clone the repository including all submodules `git clone --recurse-submodules https://github.com/rayshift/magiatranslate`
- Download `android-ndk-r21d` from https://developer.android.com/ndk/downloads and place it somewhere.
- Install Visual Studio along with ninja and cmake plugins for C++. Edit the paths to these executables in the `.bat` files if you are not using VS 2019 Enterprise.
- If you don't have Android Studio installed, you may download [command line tools](https://developer.android.com/studio#command-tools) only.
- Downlad `NDK` (`ndk;25.2.9519653`), `CMake` (`cmake;3.22.1`) and `Android SDK Build-Tools` (`build-tools;33.0.2`) with [sdkmanager](https://developer.android.com/studio/command-line/sdkmanager), or just use its GUI to install them if you have Android Studio installed.
- Install the python requirements in requirements.txt.
- Move `sign_example.bat` to `sign.bat` and add your jarsigner keystore, alias and password.
- Build abiproxy by running `abiproxy/build_release.bat`.
- Place your magia record APK in the `apk` directory.
- Place your magia record APKs in the `apk` and `armv7apk` directory.
- Run `build_release.bat`.
Notes:
- Use `build.bat` if you want a debug build with debug symbols.
- Use `build_debug.bat` if you want a debug build with debug symbols.
- If your apk has split ABIs (armeabi-v7a/arm64), you will need to move the other `libmadomagi_native.so` into `build/app/lib/{ARCH}`. For example, if the arm7 version of the game is placed in `apk/`, you need to move the `arm8` .so manually, and vice versa.
## Contributing

182
build.bat
View file

@ -1,110 +1,185 @@
@echo off
if "%1" == "Debug" (
set BUILD_TYPE="Debug"
set DOBBY_DEBUG="ON"
) else if "%1" == "Release" (
set BUILD_TYPE="Release"
set DOBBY_DEBUG="OFF"
) else (
echo Build type must be either Release or Debug
goto errorexit
)
for /r "%~dp0\apk" %%a in (*.apk) do set apk=%%~dpnxa
if "%apk%" neq "" (
echo Found apk %apk%.
goto :start
echo Found apk %apk%.
goto :find_armv7_apk
)
echo Did not find any MagiReco APK! Add it to the apk/ directory.
goto errorexit
pause
:find_armv7_apk
for /r "%~dp0\armv7apk" %%a in (*.apk) do set armv7apk=%%~dpnxa
if "%armv7apk%" neq "" (
echo Found ARMv7 apk %armv7apk%.
goto :start
)
echo Did not find any ARMv7 MagiReco APK! Add it to the armv7apk directory.
goto errorexit
pause
:start
set ndk="C:/Android/android-ndk-r21d/"
set /p ndk="Enter ndk Location [C:/Android/android-ndk-r21d/]: "
if "%JAVA_HOME%" == "" (
echo JAVA_HOME is not set
goto errorexit
)
set ndk="C:/Android/Sdk/ndk/23.1.7779620"
set /p ndk="Enter ndk Location [%ndk%]: "
set cmake="C:/Android/sdk/cmake/3.22.1/bin/cmake.exe"
set /p cmake="Enter cmake Location [%cmake%]: "
set ninja="C:/Android/sdk/cmake/3.22.1/bin/ninja.exe"
set /p ninja="Enter ninja Location [%ninja%]: "
if not exist %~dp0\build mkdir %~dp0\build
if exist %~dp0\build\apktool_2.4.1.jar goto apktoolexists
set zipalign="C:/Android/sdk/build-tools/34.0.0/zipalign.exe"
set /p zipalign="Enter zipalign Location [%zipalign%]: "
set apksigner="C:/Android/sdk/build-tools/34.0.0/apksigner.bat"
set /p apksigner="Enter apksigner Location [%apksigner%]: "
if not exist "%~dp0\build" mkdir "%~dp0\build"
set apktooljar=apktool_2.7.0.jar
set apktoolsha256=c11b5eb518d9ac2ab18e959cbe087499079072b04d567cdcae5ceb447f9a7e7d
if exist "%~dp0\build\%apktooljar%" goto checkapktoolhash
:downloadapktool
echo Downloading apktool...
CALL curl -A "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64)" -o "%~dp0\build\apktool_2.4.1.jar" -L "https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.4.1.jar"
CALL curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" -o "%~dp0\build\%apktooljar%" -L "https://bitbucket.org/iBotPeaches/apktool/downloads/%apktooljar%"
:checkapktoolhash
certutil -hashfile "%~dp0\build\%apktooljar%" SHA256 | findstr /i %apktoolsha256% && goto apktoolexists
echo SHA256 mismatch for "%~dp0\build\%apktooljar%"set deleteold=N
set redownloadapktool=N
set /p redownloadapktool="Try delete and redownload apktool (uppercase Y/[N])? "
if %redownloadapktool% NEQ Y goto errorexit
del /f "%~dp0\build\%apktooljar%"
goto downloadapktool
:apktoolexists
if not exist %~dp0\build\app\ goto create
if not exist "%~dp0\build\app\" goto create
set deleteold=N
set /p deleteold="Remake existing APK work directory (Y/[N])? "
set /p deleteold="Remake existing APK work directory (uppercase Y/[N])? "
if %deleteold% NEQ Y goto build
:create
echo Removing existing build files...
rmdir /S /Q %~dp0\build\app\
rmdir /S /Q "%~dp0\build\app\"
rmdir /S /Q "%~dp0\build\armv7\app\"
echo Running apktool...
CALL java -jar %~dp0\build\apktool_2.4.1.jar d %apk% -o %~dp0\build\app\
mkdir %~dp0\build\app\smali\com\loadLib\
if not exist %~dp0\build\app\lib\armeabi-v7a mkdir %~dp0\build\app\lib\armeabi-v7a
if not exist %~dp0\build\app\lib\arm64-v8a mkdir %~dp0\build\app\lib\arm64-v8a
CALL "%JAVA_HOME%\bin\java.exe" -jar "%~dp0\build\%apktooljar%" d "%apk%" -o "%~dp0\build\app"
mkdir "%~dp0\build\app\smali_classes2\com\loadLib\"
if not exist "%~dp0\build\app\lib\armeabi-v7a" mkdir "%~dp0\build\app\lib\armeabi-v7a"
if not exist "%~dp0\build\app\lib\arm64-v8a" mkdir "%~dp0\build\app\lib\arm64-v8a"
echo Extracting ARMv7 lib...
CALL "%JAVA_HOME%\bin\java.exe" -jar "%~dp0\build\%apktooljar%" d "%armv7apk%" --no-src --no-res -o "%~dp0\build\armv7\app"
move "%~dp0\build\armv7\app\lib\armeabi-v7a\*.*" "%~dp0\build\app\lib\armeabi-v7a\"
if errorlevel 1 goto errorexit
rmdir /S /Q "%~dp0\build\armv7\app\"
set tried_set_gitattr=N
:apply_patch
echo Applying smali patches...
cd %~dp0
call git apply --stat "%~dp0\patches\NativeBridge.patch"
call git apply --stat "%~dp0\patches\Hook.patch"
call git apply --stat "%~dp0\patches\Backtrace.patch"
call git apply "%~dp0\patches\NativeBridge.patch"
call git apply "%~dp0\patches\Hook.patch"
call git apply "%~dp0\patches\Backtrace.patch"
cd /d "%~dp0"
for %%a in (
NativeBridge.patch
Hook.patch
Backtrace.patch
) do (
call git apply --stat "%~dp0\patches\%%a"
call git apply "%~dp0\patches\%%a"
if errorlevel 1 goto handle_eol
)
goto apply_misc_patch
:handle_eol
echo handle_eol
if "%tried_set_gitattr%" == "Y" goto errorexit
set tried_set_gitattr=Y
echo Convert all smali files from CRLF into LF...
cd /d "%~dp0"
powershell.exe -NonInteractive -NoProfile -ExecutionPolicy Bypass ".\convert_smali_eol.ps1"
goto apply_patch
:apply_misc_patch
echo Applying misc patches...
REM call copy /Y "%~dp0\patches\images\story_ui_sprites00_patch.plist" "%~dp0\build\app\assets\package\story\story_ui_sprites00.plist"
REM call copy /Y "%~dp0\patches\images\story_ui_sprites00_patch.png" "%~dp0\build\app\assets\package\story\story_ui_sprites00.png"
REM Fix low-pitched audio bug since magireco 3.0.1
REM This was once done with MagiaHook.
REM However, due to unexplained reason,
REM that hook made the game engine probabilistically fail to create OpenSLES player,
REM thus the game would get silenced in that way.
echo Please copy 64bit apk.
pause
node "%~dp0/patches/audiofix.js" --wdir "%~dp0/build/app" --overwrite
if errorlevel 1 goto errorexit
call copy /Y "%~dp0\patches\koruri-semibold.ttf" "%~dp0\build\app\assets\fonts\koruri-semibold.ttf"
echo Updating sprites and AndroidManifest.xml...
call python3 buildassets.py
REM handle fake python3 which does nothing but redirects to microsoft store
set python=python
python3 --version && set python=python3
cd /d "%~dp0"
REM %python% -m pip install -r requirements.txt
%python% buildassets.py
if errorlevel 1 goto errorexit
:build
echo Copying new smali files...
call copy /Y "%~dp0\smali\loader\*.smali" "%~dp0\build\app\smali\com\loadLib\"
mkdir %~dp0\build\app\smali\io\kamihama\magianative\
call copy /Y "%~dp0\smali\loader\*.smali" "%~dp0\build\app\smali_classes2\com\loadLib\"
mkdir "%~dp0\build\app\smali_classes2\io\kamihama\magianative\"
echo Copying magianative...
call copy /Y "%~dp0\smali\MagiaNative\app\src\main\java\io\kamihama\magianative\*.smali" "%~dp0\build\app\smali\io\kamihama\magianative\"
call copy /Y "%~dp0\smali\MagiaNative\app\src\main\java\io\kamihama\magianative\*.smali" "%~dp0\build\app\smali_classes2\io\kamihama\magianative\"
echo Copying libraries...
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e %~dp0\smali\okhttp-smali\okhttp3\ %~dp0\build\app\smali\okhttp3\
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e %~dp0\smali\okhttp-smali\okio\ %~dp0\build\app\smali\okio\
REM robocopy does not eat quoted paths
pushd "%~dp0"
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e smali\okhttp-smali\okhttp3\ build\app\smali_classes2\okhttp3\
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e smali\okhttp-smali\okio\ build\app\smali_classes2\okio\
echo Copying unknown...
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e %~dp0\patches\unknown\ %~dp0\build\app\unknown\
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e patches\unknown\ build\app\unknown\
popd
call copy /Y "%~dp0\patches\strings.xml" "%~dp0\build\app\res\values\strings.xml"
echo Building libraries.
rmdir /S /Q %~dp0\build\armeabi-v7a\
rmdir /S /Q %~dp0\build\arm64-v8a\
mkdir %~dp0\build\armeabi-v7a
mkdir %~dp0\build\arm64-v8a
rmdir /S /Q "%~dp0\build\armeabi-v7a\"
rmdir /S /Q "%~dp0\build\arm64-v8a\"
mkdir "%~dp0\build\armeabi-v7a"
mkdir "%~dp0\build\arm64-v8a"
echo Running cmake armeabi-v7a...
cd %~dp0\build\armeabi-v7a
call "C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO\2019\ENTERPRISE\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe" -G Ninja -DANDROID_ABI="armeabi-v7a" -DCMAKE_BUILD_TYPE:STRING="Debug" -DCMAKE_INSTALL_PREFIX:PATH="%~dp0\build\armeabi-v7a" -DCMAKE_TOOLCHAIN_FILE:FILEPATH="%ndk%/build/cmake/android.toolchain.cmake" -DCMAKE_MAKE_PROGRAM:FILEPATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe" "-DANDROID_PLATFORM=19" "-DCMAKE_SYSTEM_NAME=Android" "-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a" "-DCMAKE_ANDROID_NDK=%ndk%" "-DCMAKE_SYSTEM_VERSION=16" "-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang" "-DDOBBY_DEBUG=ON" "%~dp0\"
set MAGIA_TRANSLATE_AUDIOFIX_3_0_1=OFF
if "%MT_AUDIOFIX_3_0_1%" == "" set MT_AUDIOFIX_3_0_1=Y
if "%MT_AUDIOFIX_3_0_1%" == "y" set MT_AUDIOFIX_3_0_1=Y
if "%MT_AUDIOFIX_3_0_1%" == "Y" (
set MAGIA_TRANSLATE_AUDIOFIX_3_0_1=ON
)
cd /d "%~dp0\build\armeabi-v7a"
call "%cmake%" -G Ninja -DANDROID_ABI="armeabi-v7a" -DCMAKE_BUILD_TYPE:STRING="%BUILD_TYPE%" "-DCMAKE_INSTALL_PREFIX:PATH=%~dp0\build\armeabi-v7a" "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=%ndk%/build/cmake/android.toolchain.cmake" "-DCMAKE_MAKE_PROGRAM:FILEPATH=%ninja%" "-DANDROID_PLATFORM=19" "-DCMAKE_SYSTEM_NAME=Android" "-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a" "-DCMAKE_ANDROID_NDK=%ndk%" "-DCMAKE_SYSTEM_VERSION=16" "-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang" "-DDOBBY_DEBUG=%DOBBY_DEBUG%" "-DMAGIA_TRANSLATE_AUDIOFIX_3_0_1=%MAGIA_TRANSLATE_AUDIOFIX_3_0_1%" "%~dp0"
if errorlevel 1 goto errorexit
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe"
call "%ninja%"
if errorlevel 1 goto errorexit
echo Running cmake arm64-v8a...
cd %~dp0\build\arm64-v8a
call "C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO\2019\ENTERPRISE\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe" -G Ninja -DANDROID_ABI="arm64-v8a" -DCMAKE_BUILD_TYPE:STRING="Debug" -DCMAKE_INSTALL_PREFIX:PATH="%~dp0\build\arm64-v8a" -DCMAKE_TOOLCHAIN_FILE:FILEPATH="%ndk%/build/cmake/android.toolchain.cmake" -DCMAKE_MAKE_PROGRAM:FILEPATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe" "-DANDROID_PLATFORM=21" "-DCMAKE_SYSTEM_NAME=Android" "-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a" "-DCMAKE_ANDROID_NDK=%ndk%" "-DCMAKE_SYSTEM_VERSION=16" "-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang" "-DDOBBY_DEBUG=ON" "%~dp0\"
cd /d "%~dp0\build\arm64-v8a"
call "%cmake%" -G Ninja -DANDROID_ABI="arm64-v8a" -DCMAKE_BUILD_TYPE:STRING="%BUILD_TYPE%" "-DCMAKE_INSTALL_PREFIX:PATH=%~dp0\build\arm64-v8a" "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=%ndk%/build/cmake/android.toolchain.cmake" "-DCMAKE_MAKE_PROGRAM:FILEPATH=%ninja%" "-DANDROID_PLATFORM=21" "-DCMAKE_SYSTEM_NAME=Android" "-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a" "-DCMAKE_ANDROID_NDK=%ndk%" "-DCMAKE_SYSTEM_VERSION=16" "-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang" "-DDOBBY_DEBUG=%DOBBY_DEBUG%" "-DMAGIA_TRANSLATE_AUDIOFIX_3_0_1=%MAGIA_TRANSLATE_AUDIOFIX_3_0_1%" "%~dp0"
if errorlevel 1 goto errorexit
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe"
call "%ninja%"
if errorlevel 1 goto errorexit
echo Copying libraries...
call copy /Y %~dp0\build\armeabi-v7a\libuwasa.so %~dp0\build\app\lib\armeabi-v7a\libuwasa.so
call copy /Y %~dp0\build\arm64-v8a\libuwasa.so %~dp0\build\app\lib\arm64-v8a\libuwasa.so
call copy /Y "%~dp0\build\armeabi-v7a\libuwasa.so" "%~dp0\build\app\lib\armeabi-v7a\libuwasa.so"
call copy /Y "%~dp0\build\arm64-v8a\libuwasa.so" "%~dp0\build\app\lib\arm64-v8a\libuwasa.so"
echo Rebuilding APK...
call java -jar %~dp0\build\apktool_2.4.1.jar b %~dp0\build\app\ -o %~dp0\build\magia_patched.apk
CALL "%JAVA_HOME%\bin\java.exe" -jar "%~dp0\build\%apktooljar%" b "%~dp0\build\app" -o "%~dp0\build\magia_patched.apk"
:signandupload
echo Signing apk...
call %~dp0\sign.bat
call "%~dp0\sign.bat"
if errorlevel 1 goto errorexit
echo Finished!
goto exit
@ -113,4 +188,5 @@ echo An error has occurred, exiting.
goto exit
:exit
cd /d "%~dp0"
pause

2
build_debug.bat Normal file
View file

@ -0,0 +1,2 @@
@echo off
call "%~dp0\build.bat" Debug

View file

@ -1,115 +1,2 @@
@echo off
for /r "%~dp0\apk" %%a in (*.apk) do set apk=%%~dpnxa
if "%apk%" neq "" (
echo Found apk %apk%.
goto :start
)
echo Did not find any MagiReco APK! Add it to the apk/ directory.
goto errorexit
pause
:start
set ndk="C:/Android/android-ndk-r21d/"
set /p ndk="Enter ndk Location [C:/Android/android-ndk-r21d/]: "
if not exist %~dp0\build mkdir %~dp0\build
if exist %~dp0\build\apktool_2.4.1.jar goto apktoolexists
echo Downloading apktool...
CALL curl -A "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64)" -o "%~dp0\build\apktool_2.4.1.jar" -L "https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.4.1.jar"
:apktoolexists
if not exist %~dp0\build\app\ goto create
set deleteold=N
set /p deleteold="Remake existing APK work directory (Y/[N])? "
if %deleteold% NEQ Y goto build
:create
echo Removing existing build files...
rmdir /S /Q %~dp0\build\app\
echo Running apktool...
CALL java -jar %~dp0\build\apktool_2.4.1.jar d %apk% -o %~dp0\build\app\
mkdir %~dp0\build\app\smali\com\loadLib\
if not exist %~dp0\build\app\lib\armeabi-v7a mkdir %~dp0\build\app\lib\armeabi-v7a
if not exist %~dp0\build\app\lib\arm64-v8a mkdir %~dp0\build\app\lib\arm64-v8a
echo Applying smali patches...
cd %~dp0
call git apply --stat "%~dp0\patches\NativeBridge.patch"
call git apply --stat "%~dp0\patches\Hook.patch"
call git apply --stat "%~dp0\patches\Backtrace.patch"
call git apply "%~dp0\patches\NativeBridge.patch"
call git apply "%~dp0\patches\Hook.patch"
call git apply "%~dp0\patches\Backtrace.patch"
echo Applying misc patches...
REM call copy /Y "%~dp0\patches\images\story_ui_sprites00_patch.plist" "%~dp0\build\app\assets\package\story\story_ui_sprites00.plist"
REM call copy /Y "%~dp0\patches\images\story_ui_sprites00_patch.png" "%~dp0\build\app\assets\package\story\story_ui_sprites00.png"
REM Fix low-pitched audio bug since magireco 3.0.1
REM This was once done with MagiaHook.
REM However, due to unexplained reason,
REM that hook made the game engine probabilistically fail to create OpenSLES player,
REM thus the game would get silenced in that way.
echo Please copy 64bit apk.
pause
node "%~dp0/patches/audiofix.js" --wdir "%~dp0/build/app" --overwrite
call copy /Y "%~dp0\patches\koruri-semibold.ttf" "%~dp0\build\app\assets\fonts\koruri-semibold.ttf"
echo Updating sprites and AndroidManifest.xml...
call python3 buildassets.py
:build
echo Copying new smali files...
call copy /Y "%~dp0\smali\loader\*.smali" "%~dp0\build\app\smali\com\loadLib\"
mkdir %~dp0\build\app\smali\io\kamihama\magianative\
echo Copying magianative...
call copy /Y "%~dp0\smali\MagiaNative\app\src\main\java\io\kamihama\magianative\*.smali" "%~dp0\build\app\smali\io\kamihama\magianative\"
echo Copying libraries...
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e %~dp0\smali\okhttp-smali\okhttp3\ %~dp0\build\app\smali\okhttp3\
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e %~dp0\smali\okhttp-smali\okio\ %~dp0\build\app\smali\okio\
echo Copying unknown...
call robocopy /NFL /NDL /NJH /NJS /nc /ns /e %~dp0\patches\unknown\ %~dp0\build\app\unknown\
call copy /Y "%~dp0\patches\strings.xml" "%~dp0\build\app\res\values\strings.xml"
echo Building libraries.
rmdir /S /Q %~dp0\build\armeabi-v7a\
rmdir /S /Q %~dp0\build\arm64-v8a\
mkdir %~dp0\build\armeabi-v7a
mkdir %~dp0\build\arm64-v8a
echo Running cmake armeabi-v7a...
cd %~dp0\build\armeabi-v7a
call "C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO\2019\ENTERPRISE\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe" -G Ninja -DANDROID_ABI="armeabi-v7a" -DCMAKE_BUILD_TYPE:STRING="Release" -DCMAKE_INSTALL_PREFIX:PATH="%~dp0\build\armeabi-v7a" -DCMAKE_TOOLCHAIN_FILE:FILEPATH="%ndk%/build/cmake/android.toolchain.cmake" -DCMAKE_MAKE_PROGRAM:FILEPATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe" "-DANDROID_PLATFORM=19" "-DCMAKE_SYSTEM_NAME=Android" "-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a" "-DCMAKE_ANDROID_NDK=%ndk%" "-DCMAKE_SYSTEM_VERSION=16" "-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang" "-DDOBBY_DEBUG=OFF" "%~dp0\"
if errorlevel 1 goto errorexit
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe"
if errorlevel 1 goto errorexit
echo Running cmake arm64-v8a...
cd %~dp0\build\arm64-v8a
call "C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO\2019\ENTERPRISE\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe" -G Ninja -DANDROID_ABI="arm64-v8a" -DCMAKE_BUILD_TYPE:STRING="Release" -DCMAKE_INSTALL_PREFIX:PATH="%~dp0\build\arm64-v8a" -DCMAKE_TOOLCHAIN_FILE:FILEPATH="%ndk%/build/cmake/android.toolchain.cmake" -DCMAKE_MAKE_PROGRAM:FILEPATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe" "-DANDROID_PLATFORM=21" "-DCMAKE_SYSTEM_NAME=Android" "-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a" "-DCMAKE_ANDROID_NDK=%ndk%" "-DCMAKE_SYSTEM_VERSION=16" "-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang" "-DDOBBY_DEBUG=OFF" "%~dp0\"
if errorlevel 1 goto errorexit
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe"
if errorlevel 1 goto errorexit
echo Copying libraries...
call copy /Y %~dp0\build\armeabi-v7a\libuwasa.so %~dp0\build\app\lib\armeabi-v7a\libuwasa.so
call copy /Y %~dp0\build\arm64-v8a\libuwasa.so %~dp0\build\app\lib\arm64-v8a\libuwasa.so
echo Rebuilding APK...
call java -jar %~dp0\build\apktool_2.4.1.jar b %~dp0\build\app\ -o %~dp0\build\magia_patched.apk
:signandupload
echo Signing apk...
call %~dp0\sign.bat
echo Finished!
goto exit
:errorexit
echo An error has occurred, exiting.
goto exit
:exit
pause
call "%~dp0\build.bat" Release

View file

@ -1,4 +1,6 @@
#!/bin/bash
set -e
BASEDIR="$(realpath "$(dirname "${0}")")"
# env-based
@ -13,10 +15,14 @@ APKTOOL="${MT_APKTOOL:-apktool_2.6.0.jar}"
ZIPALIGN="${MT_ZIPALIGN:-zipalign}" # ~/android-sdk/build-tools/zipalign
APKSIGNER="${MT_APKSIGNER:-apksigner}" # ~/android-sdk/build-tools/apksigner
ARMV7SRCAPK="${ARMV7SRCAPK:-${BASEDIR}/armv7apk/vanilla-armv7.apk}"
MT_AUDIOFIX_3_0_1="${MT_AUDIOFIX_3_0_1:-Y}"
# arg-based
SRCAPK="${1:-${BASEDIR}/apk/vanilla.apk}"
VERSION="${2:-v0.50}"
NDK="${3:-${BASEDIR}/ndk/android-ndk-r21e}"
NDK="${3:-${BASEDIR}/Android/Sdk/ndk/23.1.7779620}"
FORCEOW="${4:-true}"
TARCHS="${5:-"armeabi-v7a arm64-v8a"}"
@ -26,6 +32,11 @@ _pre() {
[ -f "${SRCAPK}" ] || _errorexit 5 "Did not find MagiReco APK! Tried path: ${SRCAPK}"
echo "Found apk ${SRCAPK}"
if [[ "${TARCHS}" == *"armeabi-v7a"* ]]; then
[ -f "${ARMV7SRCAPK}" ] || _errorexit 5 "Did not find ARMv7 MagiReco APK! Tried path: ${ARMV7SRCAPK}"
echo "Found ARMv7 apk ${ARMV7SRCAPK}"
fi
[ -d "${NDK}" ] || _errorexit 6 "NDK directory does not exist! Tried path: ${NDK}"
NDK=$(realpath "${NDK}")
echo "Found ndk directory ${NDK}"
@ -58,32 +69,37 @@ _get_apktool() {
_create() {
echo "Removing existing build files..."
rm -rf "${BASEDIR}/build/app"
rm -rf "${BASEDIR}/build/armv7/app"
echo "Running apktool..."
${JAVA} -jar "${BASEDIR}/build/${APKTOOL}" d "${SRCAPK}" -o "${BASEDIR}/build/app/"
mkdir -p "${BASEDIR}/build/app/smali/com/loadLib"
mkdir -p "${BASEDIR}/build/app/smali_classes2/com/loadLib"
for tarch in ${TARCHS}
do
mkdir -p "${BASEDIR}/build/app/lib/${tarch}"
done
if [[ "${TARCHS}" == *"armeabi-v7a"* ]]; then
echo "Extracting ARMv7 lib..."
${JAVA} -jar "${BASEDIR}/build/${APKTOOL}" d "${ARMV7SRCAPK}" --no-src --no-res -o "${BASEDIR}/build/armv7/app"
mv "${BASEDIR}/build/armv7/app/lib/armeabi-v7a/"* "${BASEDIR}/build/app/lib/armeabi-v7a/"
rm -rf "${BASEDIR}/build/armv7/app"
fi
echo "Applying smali patches..."
git -C "${BASEDIR}" apply --stat "${BASEDIR}/patches/NativeBridge.patch"
git -C "${BASEDIR}" apply --stat "${BASEDIR}/patches/Hook.patch"
git -C "${BASEDIR}" apply --stat "${BASEDIR}/patches/Backtrace.patch"
git -C "${BASEDIR}" apply "${BASEDIR}/patches/NativeBridge.patch"
git -C "${BASEDIR}" apply "${BASEDIR}/patches/Hook.patch"
git -C "${BASEDIR}" apply "${BASEDIR}/patches/Backtrace.patch"
local PATCHES=(
"NativeBridge.patch"
"Hook.patch"
"Backtrace.patch"
)
local PATCH
for PATCH in "${PATCHES[@]}"; do
git -C "${BASEDIR}" apply --stat "${BASEDIR}/patches/${PATCH}"
git -C "${BASEDIR}" apply "${BASEDIR}/patches/${PATCH}"
done
echo "Applying misc patches..."
# cp "${BASEDIR}/patches/images/story_ui_sprites00_patch.plist" "${BASEDIR}/build/app/assets/package/story/story_ui_sprites00.plist"
# cp "${BASEDIR}/patches/images/story_ui_sprites00_patch.png" "${BASEDIR}/build/app/assets/package/story/story_ui_sprites00.png"
# Fix low-pitched audio bug since magireco 3.0.1
# This was once done with MagiaHook.
# However, due to unexplained reason,
# that hook made the game engine probabilistically fail to create OpenSLES player,
# thus the game would get silenced in that way.
"${NODEJS}" "${BASEDIR}/patches/audiofix.js" --wdir "${BASEDIR}/build/app" --overwrite
cp "${BASEDIR}/patches/koruri-semibold.ttf" "${BASEDIR}/build/app/assets/fonts/koruri-semibold.ttf"
echo "Updating sprites and AndroidManifest.xml..."
@ -93,13 +109,13 @@ _create() {
_build() {
echo "Copying new smali files..."
cp "${BASEDIR}"/smali/loader/*.smali "${BASEDIR}/build/app/smali/com/loadLib/"
mkdir -p "${BASEDIR}/build/app/smali/io/kamihama/magianative"
cp "${BASEDIR}"/smali/loader/*.smali "${BASEDIR}/build/app/smali_classes2/com/loadLib/"
mkdir -p "${BASEDIR}/build/app/smali_classes2/io/kamihama/magianative"
echo "Copying magianative..."
cp "${BASEDIR}"/smali/MagiaNative/app/src/main/java/io/kamihama/magianative/*.smali "${BASEDIR}/build/app/smali/io/kamihama/magianative/"
cp "${BASEDIR}"/smali/MagiaNative/app/src/main/java/io/kamihama/magianative/*.smali "${BASEDIR}/build/app/smali_classes2/io/kamihama/magianative/"
echo "Copying libraries..."
cp -r "${BASEDIR}/smali/okhttp-smali/okhttp3/" "${BASEDIR}/build/app/smali/okhttp3/"
cp -r "${BASEDIR}/smali/okhttp-smali/okio/" "${BASEDIR}/build/app/smali/okio/"
cp -r "${BASEDIR}/smali/okhttp-smali/okhttp3/" "${BASEDIR}/build/app/smali_classes2/okhttp3/"
cp -r "${BASEDIR}/smali/okhttp-smali/okio/" "${BASEDIR}/build/app/smali_classes2/okio/"
echo "Copying unknown..."
cp -r "${BASEDIR}/patches/unknown/" "${BASEDIR}/build/app/unknown/"
cp "${BASEDIR}/patches/strings.xml" "${BASEDIR}/build/app/res/values/strings.xml"
@ -113,6 +129,10 @@ _build() {
echo "Running cmake ${tarch}..."
cd "${BASEDIR}/build/${tarch}"
local MAGIA_TRANSLATE_AUDIOFIX_3_0_1="OFF"
if [[ "${MT_AUDIOFIX_3_0_1}" == "Y" ]] || [[ "${MT_AUDIOFIX_3_0_1}" == "y" ]] || [[ "${MT_AUDIOFIX_3_0_1}" == "true" ]]; then
MAGIA_TRANSLATE_AUDIOFIX_3_0_1="ON"
fi
${CMAKE} -G Ninja \
-DANDROID_ABI="${tarch}" \
-DCMAKE_BUILD_TYPE:STRING="Release" \
@ -126,6 +146,7 @@ _build() {
-DCMAKE_SYSTEM_VERSION="16" \
-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION="clang" \
-DDOBBY_DEBUG="OFF" \
-DMAGIA_TRANSLATE_AUDIOFIX_3_0_1="${MAGIA_TRANSLATE_AUDIOFIX_3_0_1}" \
"${BASEDIR}/"
[ "$?" -ne "0" ] && _errorexit 1 "cmake failed for ${tarch}"
${NINJA}

60
ci_build.sh Normal file
View file

@ -0,0 +1,60 @@
#!/bin/bash
set -e
BASEDIR="$(realpath "$(dirname "${0}")")"
# prepare signing key
[[ "${KEYSTORE_BASE64}" == "" ]] && echo "KEYSTORE_BASE64 is not set" >&2 && exit 1
[[ "${KS_PASS}" == "" ]] && echo "KS_PASS is not set" >&2 && exit 1
echo "${KEYSTORE_BASE64}" | base64 -d > "${BASEDIR}/builder.jks" 2> /dev/null
KEYSTORE_ARGS="--ks \"${BASEDIR}/builder.jks\" --ks-pass \"pass:${KS_PASS}\""
[[ "${KEY_PASS}" != "" ]] && echo "KEY_PASS is set!" >&2 && KEYSTORE_ARGS="${KEYSTORE_ARGS} --key-pass \"pass:${KEY_PASS}\""
[[ "${KS_KEY_ALIAS}" != "" ]] && echo "KS_KEY_ALIAS is set!" >&2 && KEYSTORE_ARGS="${KEYSTORE_ARGS} --ks-key-alias \"${KS_KEY_ALIAS}\""
unset KEYSTORE_BASE64
unset KS_PASS
unset KEY_PASS
unset KS_KEY_ALIAS
cp sign_example.sh sign.sh
sed -E -i "s@^(\"\\\$\{APKSIGNER\}\" sign ).+( \"\\\$\{APK\}\")\$@\\1${KEYSTORE_ARGS}\\2@" sign.sh 2>&1 > /dev/null
sed -i "s@^KEYSTORE=.*@KEYSTORE=\"${BASEDIR}/builder.jks\"@" sign.sh 2>&1 > /dev/null
chmod +x sign.sh
# prepare source APKs
MT_VER=$(grep -P -o "(?<=^#define MT_VERSION )\d+$" src/Config.h)
. ci_versions/src_apk.sh
SRCAPK="${BASEDIR}/apk/src_${SRCAPK_VER}.apk"
export ARMV7SRCAPK="${BASEDIR}/armv7apk/armv7src_${SRCAPK_VER}.apk"
VERSION="v${SRCAPK_VER}_v${MT_VER}"
# load deps versions
. ci_versions/deps.sh
DEPS_DIR="${BASEDIR}/deps"
# prepare apktool
export MT_APKTOOL="apktool_${APKTOOL_VER}.jar"
mkdir -p "${BASEDIR}/build"
cp "${DEPS_DIR}/${MT_APKTOOL}" "${BASEDIR}/build/${MT_APKTOOL}"
# prepare Android SDK
SDK_ROOT="${DEPS_DIR}/Android/Sdk"
NDK="${SDK_ROOT}/ndk/${NDK_VER}"
CMAKE_BIN_DIR="${SDK_ROOT}/cmake/${CMAKE_VER}/bin"
BUILD_TOOLS_DIR="${SDK_ROOT}/build-tools/${BUILD_TOOLS_VER}"
export MT_CMAKE="${CMAKE_BIN_DIR}/cmake"
export MT_NINJA="${CMAKE_BIN_DIR}/ninja"
export MT_ZIPALIGN="${BUILD_TOOLS_DIR}/zipalign"
export MT_APKSIGNER="${BUILD_TOOLS_DIR}/apksigner"
RESULT="${BASEDIR}/build/io.kamihama.magiatranslate.${VERSION}.apk"
# build main APK which contains audiofix
MT_AUDIOFIX_3_0_1=Y "${BASEDIR}/build_release.sh" "${SRCAPK}" "${VERSION}" "${NDK}"
MAIN_APK="MagiaTranslate_${VERSION}.apk"
mv "${RESULT}" "${BASEDIR}/${MAIN_APK}"
echo "MAIN_APK=${MAIN_APK}" >> "$GITHUB_ENV"
# build failsafe APK which does not contain audiofix
MT_AUDIOFIX_3_0_1=N "${BASEDIR}/build_release.sh" "${SRCAPK}" "${VERSION}" "${NDK}"
FAILSAFE_APK="MagiaTranslate_${VERSION}_failsafe.apk"
mv "${RESULT}" "${BASEDIR}/${FAILSAFE_APK}"
echo "FAILSAFE_APK=${FAILSAFE_APK}" >> "$GITHUB_ENV"
rm "${BASEDIR}/builder.jks"

18
ci_check_for_update.sh Executable file
View file

@ -0,0 +1,18 @@
#!/bin/bash
set -e
set -o pipefail
UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
CHECKSUM_URL="https://jp.rika.ren/apk/Origin/checksum.txt"
. ci_versions/src_apk.sh
LATEST_SRCAPK_VER=$(curl -s -A "${UA}" -L "${CHECKSUM_URL}" | grep -E -i "^Version\s+name:\s+" | tail -n1 | grep -P -o "(\\d+\\.)+\\d+$")
if echo "${SRCAPK_VER}" | grep -q -P "^(\\d+\\.)+\\d+$" && echo "${LATEST_SRCAPK_VER}" | grep -q -P "^(\\d+\\.)+\\d+$"; then
if [[ "${SRCAPK_VER}" != "${LATEST_SRCAPK_VER}" ]]; then
echo "latest-src-apk-ver=${LATEST_SRCAPK_VER}"
echo "new-version-available=true"
fi
fi

27
ci_download_src_apk.sh Normal file
View file

@ -0,0 +1,27 @@
#!/bin/bash
set -e
verify_apk() {
. ci_versions/deps.sh
"./deps/Android/Sdk/build-tools/${BUILD_TOOLS_VER}/apksigner" verify --print-certs "$1" > /tmp/result.txt || exit 1
grep -q "$2" /tmp/result.txt && return 0
echo "Cert SHA256 digest mismatch" >&2
exit 2
}
UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
URL_PREFIX="https://jp.rika.ren/apk/Origin/com.aniplex.magireco"
ARMV8_URL="${URL_PREFIX}.arm8.apk"
ARMV7_URL="${URL_PREFIX}.arm7.apk"
. ci_versions/src_apk.sh
rm -fr apk armv7apk
mkdir -p apk armv7apk
curl -A "${UA}" -o out.apk -L "${ARMV8_URL}"
verify_apk out.apk "${SRCAPK_CERT_SHA256}" && mv out.apk "./apk/src_${SRCAPK_VER}.apk"
curl -A "${UA}" -o out.apk -L "${ARMV7_URL}"
verify_apk out.apk "${SRCAPK_CERT_SHA256}" && mv out.apk "./armv7apk/armv7src_${SRCAPK_VER}.apk"

27
ci_install_deps.sh Normal file
View file

@ -0,0 +1,27 @@
#!/bin/bash
set -e
UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
. ci_versions/deps.sh
rm -fr deps
mkdir deps
cd deps
mkdir -p Android/Sdk
pushd Android/Sdk
curl -A "${UA}" -o "commandlinetools-linux.zip" -L "${COMMANDLINETOOLS_URL}"
sha256sum "commandlinetools-linux.zip" | grep -q "${COMMANDLINETOOLS_SHA256}"
unzip "commandlinetools-linux.zip"
rm "commandlinetools-linux.zip"
yes | ./cmdline-tools/bin/sdkmanager --sdk_root="$(realpath .)" --licenses
for PKG in "cmake;${CMAKE_VER}" "ndk;${NDK_VER}" "build-tools;${BUILD_TOOLS_VER}"; do
./cmdline-tools/bin/sdkmanager --sdk_root="$(realpath .)" --install "${PKG}"
done
popd
# apktool
APKTOOL="apktool_${APKTOOL_VER}.jar"
curl -A "${UA}" -o "${APKTOOL}" -L "https://bitbucket.org/iBotPeaches/apktool/downloads/${APKTOOL}"
sha256sum "${APKTOOL}" | grep -q "${APKTOOL_SHA256}"

7
ci_versions/deps.sh Normal file
View file

@ -0,0 +1,7 @@
COMMANDLINETOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip"
COMMANDLINETOOLS_SHA256="bd1aa17c7ef10066949c88dc6c9c8d536be27f992a1f3b5a584f9bd2ba5646a0"
CMAKE_VER="3.22.1"
NDK_VER="25.2.9519653"
BUILD_TOOLS_VER="33.0.2"
APKTOOL_VER="2.8.1"
APKTOOL_SHA256="7b4a8e1703e228d206db29644b71141687d8a111b55b039b08b02dfa443ab0f9"

2
ci_versions/src_apk.sh Normal file
View file

@ -0,0 +1,2 @@
SRCAPK_CERT_SHA256="3dc9c8238c830ac58a6e705353eaa0dbe0f90e302f1259042bbcab6b4c3d8c6e"
SRCAPK_VER="3.0.4"

8
convert_smali_eol.ps1 Normal file
View file

@ -0,0 +1,8 @@
$ErrorActionPreference = "Stop"
Get-ChildItem -Recurse .\build\app\smali | ForEach-Object -Process {
$filepath = $_.FullName
if ($filepath.EndsWith(".smali")) {
[IO.File]::WriteAllText($filepath, ([IO.File]::ReadAllText($filepath) -replace "`r`n", "`n"))
}
}

@ -1 +1 @@
Subproject commit 0aec6c146a3140b917d42850f3c48d19e6c64746
Subproject commit b1722250e9c83c145e81a77095b58cf6b5be4fcf

@ -1 +1 @@
Subproject commit 9d305b40ba0d1bd3f845e0102fa6815b7f2b718c
Subproject commit a873dae3f8bc7b2e7b710248aa31a4912975bea2

View file

@ -1,283 +0,0 @@
const fs = require("fs");
const path = require("path");
// workaround audio bug since Magia Record 3.0.1
// author: segfault-bilibili
// English README
//
// Since Magia Record 3.0.1, there appears to be a bug affecting minor fraction of players.
//
// Such bug makes the game audio (including BGM, sound effects etc) sound strange:
// (1) the pitch sounds to be lower than normal;
// (2) the time sounds to be "stretched" longer/slower than normal.
//
// There's currently an experimental modification to workaround this bug.
// To distinguish from other EX versions without this experimental modification,
// the APK file under this subdirectory come with "-soundfix" suffix in its file name, like:
// "magireco-3.0.2-EX-soundfix.apk".
//
// The root cause of this bug is still unclear. It should be some kind of sample rate mismatch.
//
// It's observed that the audio is "stretched" exactly 8.84% longer than it should be,
// which matches exactly with 48 / 44.1 = 108.84%;
// plus the currently observed fact that only environments with a 44.1kHz system audio output
// sample rate seem to have this problem;
// it's guessed that deceiving the game to make it think the system audio output sample rate
// was 48kHz (instead of actual 44.1kHz) might make this problem go away - and luckily, it does,
// at least in our limited tests.
//
// However, it's still confusing why such trick seems to work.
// 中文说明
//
// 自从魔法纪录3.0.1版开始出现了一个影响少数玩家的bug。
//
// 这个bug会让音频包括背景音乐、音效等等听起来很奇怪
// (1) 音调听起来比正常低;
// (2) 时间听起来也被拉长/变慢了。
//
// 目前有一个实验性的小修改来绕过这个bug。
// 为了与不带这个修改的其他EX版区分此目录下的APK文件都带有"-soundfix"文件名后缀,比如:
// "magireco-3.0.2-EX-soundfix.apk"。
//
// 导致这个bug的根本原因还不太清楚。可能是某种采样率不匹配。
//
// 据观察音频被拉长到正好108.84%,和 48 / 44.1 = 108.84% 吻合。
// 再加上目前观察到只有系统音频输出采样率是44.1kHz的环境才有这个问题;
// 就可以猜测如果欺骗游戏、使其认为系统音频输出采样率是48kHz而不是实际值44.1kHz
// 就能让问题消失——实际上也确实消失了,至少在有限的测试里是这样。
//
// 然而,现在还并不清楚为什么这一招看上去能奏效。
// usage:
// apktool d --no-src --no-res magireco-3.0.2-EX.apk
// node audiofix.js --wdir magireco-3.0.2-EX --overwrite
// apktool b magireco-3.0.2-EX
// zipalign -p -f -v 4 magireco-3.0.2-EX/dist/magireco-3.0.2-EX.apk magireco-3.0.2-EX-soundfix.apk
// apksigner sign --ks keystore.jks --ks-pass pass:12345678 magireco-3.0.2-EX-soundfix.apk
const EM_AARCH64 = 0xb7, EM_ARM = 0x28;
const ELFCLASS64 = 2, ELFCLASS32 = 1;
function parseElf(elf) {
let result = {};
// parse elf header
const read_e_ident = elf.subarray(0, 16);
if (Buffer.compare(Buffer.from([0x7f, 0x45, 0x4c, 0x46]), read_e_ident.subarray(0, 4)) != 0) {
throw new Error("not ELF");
}
const eh = result.elf_header = {
e_ident: {
ei_class_2: read_e_ident.readUInt8(4),
ei_data: read_e_ident.readUInt8(5),
ei_version: read_e_ident.readUInt8(6),
ei_osabi: read_e_ident.readUInt8(7),
ei_abiversion: read_e_ident.readUInt8(8),
ei_nident_SIZE: read_e_ident.readUInt8(0xf),
},
e_type: elf.readUInt16LE(0x10),
e_machine: elf.readUInt16LE(0x12),
e_version: elf.readUInt32LE(0x14),
}
if (result.elf_header.e_ident.ei_nident_SIZE != 0) {
throw new Error("ei_nident_SIZE != 0");
}
let is64 = true;
let e_flags_offset = 0x30;
switch (eh.e_ident.ei_class_2) {
case ELFCLASS64:
if (eh.e_machine != EM_AARCH64) {
throw new Error(`e_machine (${eh.e_machine}) != EM_AARCH64`);
}
is64 = true;
eh.e_entry_START_ADDRESS = Number(elf.readBigUInt64LE(0x18));
eh.e_phoff_PROGRAM_HEADER_OFFSET_IN_FILE = Number(elf.readBigUInt64LE(0x20));
eh.e_shoff_SECTION_HEADER_OFFSET_IN_FILE = Number(elf.readBigUInt64LE(0x28));
e_flags_offset = 0x30;
break;
case ELFCLASS32:
if (eh.e_machine != EM_ARM) {
throw new Error(`eh.e_machine (${eh.e_machine}) != EM_ARM`);
}
is64 = false;
eh.e_entry_START_ADDRESS = elf.readUInt32LE(0x18);
eh.e_phoff_PROGRAM_HEADER_OFFSET_IN_FILE = elf.readUInt32LE(0x1c);
eh.e_shoff_SECTION_HEADER_OFFSET_IN_FILE = elf.readUInt32LE(0x20);
e_flags_offset = 0x24;
break;
default:
throw new Error(`unknown ei_class_2 = ${eh.e_ident.ei_class_2}`);
}
eh.e_flags = elf.readUInt32LE(e_flags_offset);
eh.e_ehsize_ELF_HEADER_SIZE = elf.readUInt16LE(e_flags_offset + 4);
eh.e_phentsize_PROGRAM_HEADER_ENTRY_SIZE_IN_FILE = elf.readUInt16LE(e_flags_offset + 6);
eh.e_phnum_NUMBER_OF_PROGRAM_HEADER_ENTRIES = elf.readUInt16LE(e_flags_offset + 8);
eh.e_shentsize_SECTION_HEADER_ENTRY_SIZE = elf.readUInt16LE(e_flags_offset + 10);
eh.e_shnum_NUMBER_OF_SECTION_HEADER_ENTRIES = elf.readUInt16LE(e_flags_offset + 12);
eh.e_shtrndx_STRING_TABLE_INDEX = elf.readUInt16LE(e_flags_offset + 14);
// parse section header
const sh = result.section_header_table = [];
const shoff = eh.e_shoff_SECTION_HEADER_OFFSET_IN_FILE;
const shnum = eh.e_shnum_NUMBER_OF_SECTION_HEADER_ENTRIES;
const shentsize = eh.e_shentsize_SECTION_HEADER_ENTRY_SIZE;
const read_shtab = elf.subarray(shoff, shoff + shentsize * shnum);
const shtrndx = eh.e_shtrndx_STRING_TABLE_INDEX;
const read_shstrtab = elf.subarray(shoff + shentsize * shtrndx);
const strtab_offset = is64 ? Number(read_shstrtab.readBigUInt64LE(24)) : read_shstrtab.readUInt32LE(16);
const strtab_size = is64 ? Number(read_shstrtab.readBigUInt64LE(32)) : read_shstrtab.readUInt32LE(20);
const read_strtab = elf.subarray(strtab_offset, strtab_offset + strtab_size);
for (let i = 0, offset = 0; i < shnum; i++, offset += shentsize) {
let read_entry = read_shtab.subarray(offset, offset + shentsize);
let s_name_off = read_entry.readUInt32LE(0);
let s_name_str = read_strtab.subarray(s_name_off, read_strtab.indexOf(0x00, s_name_off)).toString("ascii");
sh.push({
s_name: {
s_name_off: s_name_off,
s_name_str: s_name_str,
},
s_type: read_entry.readUInt32LE(4),
s_flags: read_entry.readUInt32LE(8),
s_addr: is64 ? Number(read_entry.readBigUInt64LE(16)) : read_entry.readUInt32LE(12),
s_offset: is64 ? Number(read_entry.readBigUInt64LE(24)) : read_entry.readUInt32LE(16),
s_size: is64 ? Number(read_entry.readBigUInt64LE(32)) : read_entry.readUInt32LE(20),
s_link: read_entry.readUInt32LE(is64 ? 40 : 24),
s_info: read_entry.readUInt32LE(is64 ? 44 : 28),
s_addralign: is64 ? Number(read_entry.readBigUInt64LE(48)) : read_entry.readUInt32LE(32),
s_entsize: is64 ? Number(read_entry.readBigUInt64LE(56)) : read_entry.readUInt32LE(36),
});
}
//parse dynamic symbol table
const dynsym = result.dynamic_symbol_table = [];
const dynsym_sec = sh.find((entry) => entry.s_name.s_name_str === ".dynsym");
const dynsym_secoffset = dynsym_sec.s_offset;
const dynsym_secsize = dynsym_sec.s_size;
const dynsym_entsize = dynsym_sec.s_entsize;
if (dynsym_entsize <= 0) {
throw new Error(`dynsym_entsize ${dynsym_entsize} <= 0`);
}
const read_dynsym = elf.subarray(dynsym_secoffset, dynsym_secoffset + dynsym_secsize);
const dynstr_sec = sh.find((entry) => entry.s_name.s_name_str === ".dynstr");
const dynstr_secoffset = dynstr_sec.s_offset;
const dynstr_secsize = dynstr_sec.s_size;
const read_dynstr = elf.subarray(dynstr_secoffset, dynstr_secoffset + dynstr_secsize);
for (
let i = 0, offset = 0;
offset + dynsym_entsize <= dynsym_secsize;
i++, offset += dynsym_entsize
) {
let read_entry = read_dynsym.subarray(offset, offset + dynsym_entsize);
let sym_name_off = read_entry.readUInt32LE(0);
let sym_name_str = read_dynstr.subarray(sym_name_off, read_dynstr.indexOf(0x00, sym_name_off)).toString("ascii");
dynsym.push({
sym_name: {
sym_name_off: sym_name_off,
sym_name_str: sym_name_str,
},
sym_info: read_entry.readUInt8(is64 ? 4 : 12),
sym_other: read_entry.readUInt8(is64 ? 5 : 13),
sym_shndx: read_entry.readUInt16LE(is64 ? 6 : 14),
sym_value: is64 ? Number(read_entry.readBigUInt64LE(8)) : read_entry.readUInt32LE(4),
sym_size: is64 ? Number(read_entry.readBigUInt64LE(16)) : read_entry.readUInt32LE(8),
});
}
return result;
}
function getTargetFunction(elf, info, funcName, funcOffset, bufLen) {
const syment = info.dynamic_symbol_table.find((entry) => entry.sym_name.sym_name_str === funcName);
const offset = syment.sym_value;
const size = syment.sym_size;
const func = elf.subarray(offset, offset + size);
if (funcOffset + bufLen > func.length) throw new Error("funcOffset + bufLen > func.length");
return func.subarray(funcOffset, funcOffset + bufLen);
}
function checkFunction(elf, info, funcName, funcOffset, buf) {
const target = getTargetFunction(elf, info, funcName, funcOffset, buf.length);
return Buffer.compare(target, buf) == 0;
}
function patchFunction(elf, info, funcName, funcOffset, buf) {
const target = getTargetFunction(elf, info, funcName, funcOffset, buf.length);
buf.copy(target);
}
const wdirIndex = process.argv.findIndex((arg) => arg === "--wdir");
if (wdirIndex == -1 || wdirIndex == process.argv.length - 1) throw new Error("please specify --wdir");
const wdir = process.argv[wdirIndex + 1];
const overwrite = process.argv.findIndex((arg) => arg === "--overwrite") != -1;
const libname = "libmadomagi_native.so";
const funcToPatch = "criNcv_GetHardwareSamplingRate_ANDROID";
const abiList = {
"arm64-v8a": [
{
funcName: funcToPatch,
checkList: [
{
offset: 8,
buf: [0xc0, 0x03, 0x5f, 0xd6],
}
],
patchList: [
{
offset: 4,
buf: [0x00, 0x70, 0x97, 0x52],
},
],
},
],
"armeabi-v7a": [
{
funcName: funcToPatch,
checkList: [
{
offset: 8,
buf: [0x1e, 0xff, 0x2f, 0xe1],
}
],
patchList: [
{
offset: 4,
buf: [0x80, 0x0b, 0x0b, 0xe3],
},
],
},
],
}
for (let abi in abiList) {
let filepath = path.join(wdir, "lib", abi, libname);
if (!fs.existsSync(filepath)) {
console.log(`skipped nonexist file ${filepath}`);
continue;
}
let filedata = fs.readFileSync(filepath);
console.log(`patching ${filepath}`);
let info = parseElf(filedata);
abiList[abi].forEach((patchInfo) => {
let mismatch = patchInfo.checkList.find((check) => !checkFunction(filedata, info, patchInfo.funcName, check.offset, Buffer.from(check.buf)));
if (mismatch != null) throw new Error("check failed");
patchInfo.patchList.forEach((patch) => patchFunction(filedata, info, patchInfo.funcName, patch.offset, Buffer.from(patch.buf)));
});
let writeToPath = path.join(wdir, "lib", abi, overwrite ? libname : libname.replace(/\.so$/, "-soundfix.so"));
fs.writeFileSync(writeToPath, filedata);
console.log(`written patched file to ${writeToPath}`);
};

View file

@ -1,2 +1,29 @@
@echo off
jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore %~dp0\changeme.keystore %~dp0\build\magia_patched.apk -storepass changeme name
if not exist "%zipalign%" (
echo zipalign not found
goto errorexit
)
if not exist "%apksigner%" (
echo apksigner not found
goto errorexit
)
echo Doing zipalign...
"%zipalign%" -f -p 4 "%~dp0\build\magia_patched.apk" "%~dp0\build\magia_patched_aligned.apk"
if errorlevel 1 (
echo Failed to zipalign!
goto errorexit
)
echo Removing tmp file...
del /f /q "%~dp0\build\magia_patched.apk"
rename "%~dp0\build\magia_patched_aligned.apk" magia_patched.apk
echo Doing apksign...
call "%apksigner%" sign --ks "%~dp0\changeme.keystore" --ks-pass pass:changeme --ks-key-alias name "%~dp0\build\magia_patched.apk"
if errorlevel 1 goto errorexit
exit /b
:errorexit
exit /b 3

View file

@ -25,11 +25,11 @@ _errorexit() {
echo "Doing zipalign..."
"${ZIPALIGN}" -f -p 4 "${APK}" "${APK}.tmp"
[ "$?" -ne "0" ] && _errorexit 3 "Failed to zipalign!"
echo "Doing apksign..."
"${APKSIGNER}" sign --ks "${KEYSTORE}" --ks-pass pass:changeme --ks-key-alias name "${APK}.tmp"
[ "$?" -ne "0" ] && _errorexit 4 "Failed to apksign!"
echo "Removing tmp file..."
mv "${APK}.tmp" "${APK}"
echo "Doing apksign..."
"${APKSIGNER}" sign --ks "${KEYSTORE}" --ks-pass pass:changeme --ks-key-alias name "${APK}"
[ "$?" -ne "0" ] && _errorexit 4 "Failed to apksign!"
exit 0

View file

@ -17,6 +17,9 @@
#include "libmadomagi.h"
#include "rest/MagiaRest.h"
#define RT_FAILED -1
#define RS_SUCCESS 0
const char* libName = "libmadomagi_native.so";
const char* hookName = "libuwasa.so";
@ -457,6 +460,25 @@ pthread_mutex_t *http2SessionSetMaxConnectionNum(uintptr_t *session, int max) {
return http2SessionSetMaxConnectionNumOld(session, max);
}
#if defined(MAGIA_TRANSLATE_AUDIOFIX_3_0_1)
uint32_t (*criNcv_GetHardwareSamplingRate_ANDROID_Hooked)();
uint32_t criNcv_GetHardwareSamplingRate_ANDROID() {
auto orig = criNcv_GetHardwareSamplingRate_ANDROID_Hooked();
auto value = orig;
value = 48000;
LOGI("using hardware sample rate: %d (orig: %d)", value, orig);
return value;
}
void *(*criNcv_SetHardwareSamplingRate_ANDROID_Hooked)(uint32_t value);
void criNcv_SetHardwareSamplingRate_ANDROID(uint32_t value) {
LOGI("set cached hardware sample rate to %d", value);
criNcv_SetHardwareSamplingRate_ANDROID_Hooked(value);
}
#endif
void initialization_error(const char* error) {
LOGE("%s", error);
auto errorMsg = string_format("A critical error has occurred, MagiaTranslate will not work properly and may crash. Please report this error on GitHub or Discord.\n%s", error);
@ -512,6 +534,42 @@ void *hook_loop(void *arguments) {
// For debugging
//DobbyHook(lookup_symbol(libLocation, "_ZN5http212Http2Session6setURIERKSs"), (void *)setUriDebug, (void **)&setUriDebugOld); - crashes arm32 now.
#if defined(MAGIA_TRANSLATE_AUDIOFIX_3_0_1)
// audio pitch & speed fix
void *getHWSampleRate = lookup_symbol(libLocation, "criNcv_GetHardwareSamplingRate_ANDROID");
if (getHWSampleRate != nullptr) {
LOGD("Found criNcv_GetHardwareSamplingRate_ANDROID at %p.", (void *)getHWSampleRate);
if (DobbyHook(getHWSampleRate, (void *)criNcv_GetHardwareSamplingRate_ANDROID, (void **)&criNcv_GetHardwareSamplingRate_ANDROID_Hooked) == RS_SUCCESS) {
LOGI("Successfully hooked criNcv_GetHardwareSamplingRate_ANDROID.");
}
else {
initialization_error("Unable to hook criNcv_GetHardwareSamplingRate_ANDROID.");
pthread_exit(NULL);
}
}
else {
initialization_error("Unable to hook criNcv_GetHardwareSamplingRate_ANDROID.");
pthread_exit(NULL);
}
void *setHWSampleRate = lookup_symbol(libLocation, "criNcv_SetHardwareSamplingRate_ANDROID");
if (setHWSampleRate != nullptr) {
LOGD("Found criNcv_SetHardwareSamplingRate_ANDROID at %p.", (void *)setHWSampleRate);
if (DobbyHook(setHWSampleRate, (void *)criNcv_SetHardwareSamplingRate_ANDROID, (void **)&criNcv_SetHardwareSamplingRate_ANDROID_Hooked) == RS_SUCCESS) {
LOGI("Successfully hooked criNcv_SetHardwareSamplingRate_ANDROID.");
}
else {
initialization_error("Unable to hook criNcv_SetHardwareSamplingRate_ANDROID.");
pthread_exit(NULL);
}
}
else {
initialization_error("Unable to hook criNcv_SetHardwareSamplingRate_ANDROID.");
pthread_exit(NULL);
}
#endif
// Hooks
void *cocos2dnodeSetPosition = lookup_symbol(libLocation, "_ZN7cocos2d4Node11setPositionERKNS_4Vec2E");