├── .gitignore
├── key
├── testkey.pk8
└── testkey.x509.pem
├── images
├── 4399.svg
├── meizu.svg
├── yingyongbao.svg
├── xiaomi.svg
├── 360.svg
├── oppo.svg
├── jiuyou.svg
└── vivo.svg
├── .github
└── workflows
│ ├── xapk.yml
│ └── main.yml
├── README.md
└── merge_build.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | *.keystore
2 | *.apk
--------------------------------------------------------------------------------
/key/testkey.pk8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chtholly344/Azurlane-Build/HEAD/key/testkey.pk8
--------------------------------------------------------------------------------
/images/4399.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/meizu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/key/testkey.x509.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD
3 | VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
4 | VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
5 | AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
6 | Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET
7 | MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
8 | A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
9 | ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
10 | hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM
11 | qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4
12 | wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy
13 | 4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU
14 | RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s
15 | zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw
16 | HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ
17 | AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
18 | CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
19 | QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
20 | CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud
21 | EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa
22 | J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y
23 | LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe
24 | +ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX
25 | 31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr
26 | sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=
27 | -----END CERTIFICATE-----
28 |
--------------------------------------------------------------------------------
/images/yingyongbao.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/xiaomi.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/360.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/oppo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/xapk.yml:
--------------------------------------------------------------------------------
1 | name: XAPK AzurLane JMBQ Build Work
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | region:
7 | description: 'Select Game Server'
8 | required: true
9 | type: choice
10 | options:
11 | - 'TW'
12 | - 'EN'
13 | - 'JP'
14 | - 'KR'
15 | jobs:
16 | build:
17 | runs-on: ubuntu-latest
18 | permissions:
19 | contents: write
20 |
21 | steps:
22 | - name: Checkout code
23 | uses: actions/checkout@v4
24 |
25 | - name: Setup Java JDK
26 | uses: actions/setup-java@v3.3.0
27 | with:
28 | java-version: "17"
29 | distribution: "adopt"
30 |
31 | - name: Set up Android SDK
32 | uses: android-actions/setup-android@v3
33 |
34 | - name: Install build-tools
35 | run: |
36 | yes | sdkmanager --install "build-tools;35.0.0"
37 |
38 | - name: Build APK
39 | run: |
40 | chmod +x merge_build.sh
41 | ./merge_build.sh "${{ github.event.inputs.region }}"
42 |
43 | - name: Release and Upload Assets
44 | uses: ncipollo/release-action@v1
45 | with:
46 | tag: "${{ env.JMBQ_VERSION }}-XAPK"
47 | name: "AzurLane JMBQ ${{ env.JMBQ_VERSION }} Build XAPK"
48 | artifacts: "${{ github.event.inputs.region }}-V.${{ env.VERSION }}.7z.*"
49 | allowUpdates: true
50 | body: |
51 |
52 | 中文安装说明 - 点击展开
53 | 1. 在下方列表中找到你需要的版本前缀,下载该服务器名称前缀的所有以 7z.00* 结尾的文件
54 | 2. 解压时只需要选择"XXX.7z.001"解压即可
55 | 3. 在解压后的文件夹中找到xapk文件并安装,安装方法请自行查询相关教程
56 |
57 |
58 |
59 | English Installation Instructions - Click to expand
60 | 1. Find the version prefix you need in the list below, and download all files ending with 7z.00* that have that server name prefix
61 | 2. When extracting, simply select the "XXX.7z.001" file to decompress
62 | 3. Find the XAPK file in the extracted folder and install it, please refer to the relevant tutorials for installation methods
63 |
64 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: APK AzurLane JMBQ Build Work
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | server_name:
6 | description: '区服名称-必须(必须是英文字母!例如 Bilibili,台服和外服请使用XAPK AzurLane JMBQ Build Work)'
7 | required: true
8 | type: string
9 | default: ''
10 | download_url:
11 | description: 'APK下载链接(只能填入直链,最好使用国外云盘的直链,例如Google云盘)'
12 | required: true
13 | type: string
14 | default: ''
15 |
16 | jobs:
17 | build:
18 | runs-on: ubuntu-latest
19 | permissions:
20 | contents: write
21 |
22 | steps:
23 | # 步骤1:检出仓库代码
24 | - name: Checkout code
25 | uses: actions/checkout@v4
26 |
27 | # 步骤2:设置 Java JDK
28 | - name: Setup Java JDK
29 | uses: actions/setup-java@v3.3.0
30 | with:
31 | java-version: "17"
32 | distribution: "adopt"
33 |
34 | # 步骤3:设置 Android SDK
35 | - name: Set up Android SDK
36 | uses: android-actions/setup-android@v3
37 |
38 | # 步骤4:安装 build-tools
39 | - name: Install build-tools
40 | run: |
41 | yes | sdkmanager --install "build-tools;35.0.0"
42 |
43 | # 步骤5:执行构建流程
44 | - name: Build APK
45 | run: |
46 | chmod +x merge_build.sh
47 | ./merge_build.sh "${{ github.event.inputs.server_name }}" "${{ github.event.inputs.download_url }}"
48 |
49 | # 步骤6:创建 Release 并上传所有分卷文件
50 | - name: Release and Upload Assets
51 | uses: ncipollo/release-action@v1
52 | with:
53 | tag: "${{ env.JMBQ_VERSION }}-APK"
54 | name: "AzurLane JMBQ ${{ env.JMBQ_VERSION }} Build APK"
55 | artifacts: "${{ github.event.inputs.server_name }}-V.${{ env.VERSION }}.7z.*"
56 | allowUpdates: true
57 | body: |
58 |
59 | 中文安装说明 - 点击展开
60 | 1. 在下方列表中找到你需要的版本前缀,下载该服务器名称前缀的所有以 7z.00* 结尾的文件
61 | 2. 解压时只需要选择"XXX.7z.001"解压即可
62 | 3. 在解压后的文件夹中找到apk文件并安装
63 |
64 |
65 |
66 | English Installation Instructions - Click to expand
67 | 1. Find the version prefix you need in the list below, and download all files ending with 7z.00* that have that server name prefix
68 | 2. When extracting, simply select the "XXX.7z.001" file to decompress
69 | 3. Find the APK file in the extracted folder and install it
70 |
71 |
--------------------------------------------------------------------------------
/images/jiuyou.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/vivo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
Azurlane Build
4 |
5 | 
6 | 
7 | 
8 |
9 | 使用Github Workflow自动构建对应区服的APK/XAPK
10 |
11 |
12 | 发现问题?提交
13 | Issue
14 |
15 |
16 |
17 |
18 | ---
19 |
20 | ## ⚠️ 重要提示
21 | 本项目仅用于学习和研究,请在遵守相关法律法规的前提下使用本项目,若您违规使用使用本项目,那么所导致的一切后果将由您本人承担。
22 |
23 | - **风险警告**:使用mod可能涉及未知风险,如果您坚持使用,那么您将承担可能会造成的任何后果,包括但不限于您的游戏账号被封禁
24 | - **登录问题**:重新打包的APK签名与官方版本不同,可能导致第三方授权登录失败。请优先使用二维码或验证码登录。
25 |
26 | ---
27 |
28 | ## 各渠道服安装包官方下载地址
29 | 若您有一定的动手能力,请自行fork本仓库,手动运行工作流来获取对应的APK
30 |
31 | ⚠️⚠️⚠️ 注意!!!
32 | - **OPPO**和**4399**渠道服请自行下载安装包,上传网盘后获取直链下载
33 |
34 |
35 |

36 |

37 |

38 |

39 |

40 |

41 |

42 |

43 |
44 |
45 | ---
46 | ## 项目目录
47 | ```
48 |
49 | ├── 📁 .github
50 | │└── 📁 workflows
51 | │├── ⚙️ main.yml
52 | │└── ⚙️ xapk.yml
53 | ├──📁 key
54 | │├── 📄 testkey.pk8
55 | │└── 📄 testkey.x509.pem
56 | ├──⚙️ .gitignore
57 | ├──📝 README.md
58 | └──📄 merge_build.sh # 构建脚本
59 |
60 | ```
61 |
62 | ---
63 | ## 预览
64 | 
65 |
66 | ---
67 | ## 🚧 已知问题
68 | 以下问题将不会被解决:
69 |
70 | - **韩服**:启动无响应,可能触发反作弊机制。
71 | - **华为服**:启动界面卡顿,疑似由于签名验证问题导致HMS Core初始化失败。
72 |
73 | ---
74 |
75 | ## 📚 致谢
76 |
77 | 1. [JMBQ/azurlane](https://github.com/JMBQ/azurlane)
78 | 2. [n0k0m3/PerseusCI](https://github.com/n0k0m3/PerseusCI)
79 |
80 | ---
81 |
82 | ## 📊 Star历史
83 |
84 | [](https://starchart.cc/Chtholly344/Azurlane-Build)
85 |
--------------------------------------------------------------------------------
/merge_build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # 合并后的构建脚本,支持APK和XAPK格式
3 | # 公共变量参数 部分从Github Action中传入
4 | BUILD_TOOLS_DIR=$(find ${ANDROID_HOME}/build-tools -maxdepth 1 -type d | sort -V | tail -n 1)
5 | AAPT_PATH="${BUILD_TOOLS_DIR}/aapt"
6 | DOWNLOAD_DIR="."
7 | GAME_SERVER=$1
8 | APK_URL=$2
9 | BUILD_TYPE="APK" # 默认构建类型
10 |
11 | # 检查参数
12 | CHECK_PARAM() {
13 | if [ -z "${GAME_SERVER}" ]; then
14 | echo "服务器名称不能为空"
15 | exit 1
16 | fi
17 |
18 | if ! echo "${GAME_SERVER}" | grep -q "^[a-zA-Z0-9]*$"; then
19 | echo "服务器参数包含非英文数字字符,请重新输入"
20 | exit 1
21 | fi
22 |
23 | # 检测是否需要使用XAPK构建模式
24 | # 对于国际服务器(EN、JP、KR)和TW服务器,使用XAPK模式
25 | # 这些服务器通常通过apkeep直接下载,不需要APK_URL参数
26 | case "${GAME_SERVER}" in
27 | "TW" | "EN" | "JP" | "KR")
28 | BUILD_TYPE="XAPK"
29 | echo "检测到需要使用XAPK构建模式: ${GAME_SERVER}"
30 | ;;
31 | *)
32 | # 其他服务器需要提供APK下载链接
33 | if [ -z "${APK_URL}" ]; then
34 | echo "APK下载链接不能为空"
35 | exit 1
36 | fi
37 | BUILD_TYPE="APK"
38 | echo "使用标准APK构建模式: ${GAME_SERVER}"
39 | ;;
40 | esac
41 | }
42 |
43 | # 设置包名和文件名(XAPK模式使用)
44 | SET_BUNDLE_ID() {
45 | case "$GAME_SERVER" in
46 | "TW")
47 | GAME_BUNDLE_ID="com.hkmanjuu.azurlane.gp"
48 | ;;
49 | "EN")
50 | GAME_BUNDLE_ID="com.YoStarEN.AzurLane"
51 | ;;
52 | "JP")
53 | GAME_BUNDLE_ID="com.YoStarJP.AzurLane"
54 | ;;
55 | "KR")
56 | GAME_BUNDLE_ID="kr.txwy.and.blhx"
57 | ;;
58 | esac
59 | APK_FILENAME="${GAME_BUNDLE_ID}.apk"
60 | echo "已设置包名为: ${GAME_BUNDLE_ID}"
61 | }
62 |
63 | # 下载apkeep(XAPK模式使用)
64 | DOWNLOAD_APKEEP() {
65 | local OWNER="EFForg"
66 | local REPO="apkeep"
67 | local LIB_PLATFORM="x86_64-unknown-linux-gnu"
68 | local FILENAME="apkeep"
69 |
70 | echo "正在下载apkeep工具..."
71 | local API_RESPONSE=$(curl -s "https://api.github.com/repos/${OWNER}/${REPO}/releases/latest")
72 | local DOWNLOAD_LINK=$(echo "${API_RESPONSE}" | jq -r ".assets[] | select(.name | contains(\"${LIB_PLATFORM}\")) | .browser_download_url" | head -n 1)
73 | if [ -z "${DOWNLOAD_LINK}" ] || [ "${DOWNLOAD_LINK}" == "null" ]; then
74 | echo "无法找到Apkeep下载链接"
75 | exit 1
76 | fi
77 |
78 | curl -L -o "${DOWNLOAD_DIR}/${FILENAME}" "${DOWNLOAD_LINK}"
79 | if [ $? -eq 0 ]; then
80 | echo "Apkeep下载成功!文件保存至:${DOWNLOAD_DIR}/${FILENAME}"
81 | chmod +x "${DOWNLOAD_DIR}/${FILENAME}"
82 | else
83 | echo "Apkeep下载失败,请重试"
84 | exit 1
85 | fi
86 | }
87 |
88 | # 下载ApkTool
89 | DOWNLOAD_APKTOOL() {
90 | local OWNER="iBotPeaches"
91 | local REPO="Apktool"
92 | local FILENAME="apktool.jar"
93 |
94 | echo "正在下载Apktool..."
95 | local API_RESPONSE=$(curl -s "https://api.github.com/repos/${OWNER}/${REPO}/releases/latest")
96 | local DOWNLOAD_LINK=$(echo "${API_RESPONSE}" | jq -r '.assets[] | select(.name | endswith(".jar")) | .browser_download_url' | head -n 1)
97 | if [ -z "${DOWNLOAD_LINK}" ] || [ "${DOWNLOAD_LINK}" == "null" ]; then
98 | echo "无法找到Apktool下载链接"
99 | exit 1
100 | fi
101 |
102 | curl -L -o "${DOWNLOAD_DIR}/${FILENAME}" "${DOWNLOAD_LINK}"
103 | if [ $? -eq 0 ]; then
104 | echo "Apktool下载成功!文件保存至:${DOWNLOAD_DIR}/${FILENAME}"
105 | else
106 | echo "Apktool下载失败,请重试"
107 | exit 1
108 | fi
109 | }
110 |
111 | # 下载 Mod Patch 文件并解压
112 | DOWNLOAD_MOD_MENU() {
113 | local OWNER="JMBQ"
114 | local REPO="azurlane"
115 | local FILENAME="MOD_MENU.rar"
116 |
117 | echo "正在下载MOD补丁..."
118 | local API_RESPONSE=$(curl -s "https://api.github.com/repos/${OWNER}/${REPO}/releases/latest")
119 | local JMBQ_VERSION=$(echo "${API_RESPONSE}" | jq -r '.tag_name')
120 | # 修改:查找name中含有.rar的文件,而不是直接使用第一个assets
121 | local DOWNLOAD_LINK=$(echo "${API_RESPONSE}" | jq -r '.assets[] | select(.name | contains(".rar")) | .browser_download_url' | head -n 1)
122 |
123 | if [ -z "${DOWNLOAD_LINK}" ] || [ "${DOWNLOAD_LINK}" == "null" ]; then
124 | # 修改:查找name中含有.zip的文件,避免后缀不一致导致的无法获取链接
125 | local FILENAME="MOD_MENU.zip"
126 | local DOWNLOAD_LINK=$(echo "${API_RESPONSE}" | jq -r '.assets[] | select(.name | contains(".zip")) | .browser_download_url' | head -n 1)
127 | if [ -z "${DOWNLOAD_LINK}" ] || [ "${DOWNLOAD_LINK}" == "null" ]; then
128 | echo "无法获取MOD Patch文件下载链接"
129 | exit 1
130 | fi
131 | fi
132 |
133 | curl -L -o "${DOWNLOAD_DIR}/${FILENAME}" "${DOWNLOAD_LINK}"
134 | if [ $? -eq 0 ]; then
135 | echo "补丁下载成功!文件保存至:${DOWNLOAD_DIR}/${FILENAME}"
136 | else
137 | echo "补丁下载失败,请重试"
138 | exit 1
139 | fi
140 |
141 | if command -v 7z &> /dev/null; then
142 | 7z x -y "${DOWNLOAD_DIR}/${FILENAME}" -o"${DOWNLOAD_DIR}/JMBQ"
143 | else
144 | echo "错误: 未找到7z工具,无法解压!"
145 | exit 1
146 | fi
147 |
148 | if [ $? -ne 0 ]; then
149 | echo "错误: 解压 ${FILENAME} 失败!"
150 | exit 1
151 | fi
152 | echo "JMBQ目录内容:"
153 | ls -la "${DOWNLOAD_DIR}/JMBQ" 2>/dev/null || echo "无法列出目录内容"
154 |
155 | echo "JMBQ_VERSION=${JMBQ_VERSION}" >> "${GITHUB_ENV}"
156 | }
157 |
158 | # 下载APK(通用函数,根据构建类型执行不同的下载逻辑)
159 | DOWNLOAD_APK() {
160 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
161 | # XAPK模式下载逻辑
162 | echo "正在使用apkeep下载XAPK..."
163 | "${DOWNLOAD_DIR}/apkeep" -a "${GAME_BUNDLE_ID}" "${DOWNLOAD_DIR}/"
164 | if [ $? -ne 0 ]; then
165 | echo "XAPK 下载失败!"
166 | exit 1
167 | fi
168 | echo "XAPK [${GAME_BUNDLE_ID}.xapk] 下载成功!"
169 | echo "正在从 XAPK 中提取文件..."
170 | unzip -o "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.xapk" -d "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}"
171 | if [ $? -ne 0 ]; then
172 | echo "错误: 解压失败!"
173 | exit 1
174 | fi
175 | mv "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}/${GAME_BUNDLE_ID}.apk" "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk"
176 | else
177 | # 普通APK模式下载逻辑
178 | APK_FILENAME="${GAME_SERVER}.apk"
179 | echo "正在下载APK..."
180 | curl -L -o "${DOWNLOAD_DIR}/${APK_FILENAME}" "${APK_URL}"
181 | if [ $? -ne 0 ]; then
182 | echo "APK下载失败"
183 | exit 1
184 | fi
185 | echo "APK [${APK_FILENAME}] 下载完成"
186 | fi
187 | }
188 |
189 | # 删除原始XAPK(XAPK模式使用)
190 | DELETE_ORGINAL_XAPK() {
191 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
192 | echo "删除原始XAPK文件..."
193 | rm -rf "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.xapk"
194 | fi
195 | }
196 |
197 | # 验证APK
198 | VERIFY_APK() {
199 | local APK_TO_VERIFY
200 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
201 | APK_TO_VERIFY="${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk"
202 | else
203 | APK_TO_VERIFY="${DOWNLOAD_DIR}/${GAME_SERVER}.apk"
204 | fi
205 |
206 | echo "正在验证APK: ${APK_TO_VERIFY}"
207 | [ ! -f "${APK_TO_VERIFY}" ] && { echo "APK文件未找到"; exit 1; }
208 |
209 | local FILE_SIZE=$(stat -f%z "${APK_TO_VERIFY}" 2>/dev/null || stat -c%s "${APK_TO_VERIFY}" 2>/dev/null)
210 | [ "${FILE_SIZE}" -lt 1024 ] && { echo "APK文件大小异常"; exit 1; }
211 | unzip -t "${APK_TO_VERIFY}" >/dev/null 2>&1 || { echo "APK文件损坏"; exit 1; }
212 | echo "APK验证通过"
213 | }
214 |
215 | # APK 解包
216 | DECODE_APK() {
217 | local APK_TO_DECODE
218 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
219 | APK_TO_DECODE="${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk"
220 | else
221 | APK_TO_DECODE="${DOWNLOAD_DIR}/${GAME_SERVER}.apk"
222 | fi
223 |
224 | echo "APK反编译: ${APK_TO_DECODE}"
225 | java -jar "${DOWNLOAD_DIR}/apktool.jar" d -f "${APK_TO_DECODE}" -o "${DOWNLOAD_DIR}/DECODE_Output"
226 | if [ $? -ne 0 ]; then
227 | echo "错误: APK 反编译失败!"
228 | exit 1
229 | fi
230 | echo "反编译完成。"
231 | }
232 |
233 | # 删除源APK
234 | DELETE_ORGINAL_APK() {
235 | local APK_TO_DELETE
236 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
237 | APK_TO_DELETE="${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk"
238 | else
239 | APK_TO_DELETE="${DOWNLOAD_DIR}/${GAME_SERVER}.apk"
240 | fi
241 |
242 | echo "删除原始APK文件..."
243 | rm -rf "${APK_TO_DELETE}"
244 | }
245 |
246 | # 合入MOD
247 | PATCH_APK() {
248 | echo "正在合入MOD补丁..."
249 | cp -r "${DOWNLOAD_DIR}/JMBQ/assets/." "${DOWNLOAD_DIR}/DECODE_Output/assets/"
250 | if [ $? -ne 0 ]; then
251 | echo "错误: 复制资源文件失败!"
252 | exit 1
253 | fi
254 | echo "复制资源文件完成"
255 |
256 | local MAX_CLASS_NUM=$(find "${DOWNLOAD_DIR}/DECODE_Output/" -maxdepth 1 -type d -name "smali_classes*" 2>/dev/null | sed 's/.*smali_classes//' | sort -n | tail -1)
257 | MAX_CLASS_NUM=${MAX_CLASS_NUM:-3}
258 | local NEW_CLASS_NUM=$((MAX_CLASS_NUM + 1))
259 | local NEW_SMALI_DIR="smali_classes${NEW_CLASS_NUM}"
260 |
261 | # 移除maxdepth限制,确保能找到所有smali_classes目录
262 | local SRC_DIR=$(find "${DOWNLOAD_DIR}/JMBQ" -type d -name "smali_classes*" 2>/dev/null | head -1)
263 |
264 | if [ -z "${SRC_DIR}" ]; then
265 | # 添加详细的错误信息,显示JMBQ目录结构
266 | echo "错误: MOD 补丁目录中未找到 smali_classes 目录!"
267 | echo "JMBQ目录内容:"
268 | ls -la "${DOWNLOAD_DIR}/JMBQ" 2>/dev/null || echo "无法列出目录内容"
269 | exit 1
270 | fi
271 |
272 | echo "找到MOD补丁目录: ${SRC_DIR}"
273 | cp -r "${SRC_DIR}" "${DOWNLOAD_DIR}/DECODE_Output/${NEW_SMALI_DIR}" || {
274 | echo "错误: 复制 smali 文件失败!"
275 | exit 1
276 | }
277 | echo "smali文件复制完成"
278 |
279 | local SMALI_FILE=$(find "${DOWNLOAD_DIR}/DECODE_Output" -type f -name "UnityPlayerActivity.smali")
280 | if [ -z "${SMALI_FILE}" ]; then
281 | echo "错误: UnityPlayerActivity.smali 文件未找到!"
282 | exit 1
283 | fi
284 | echo "已找到 UnityPlayerActivity.smali 文件,路径为: ${SMALI_FILE}"
285 |
286 | local LINE_NUM=$(grep -n ".method public constructor ()V" "${SMALI_FILE}" | cut -d: -f1)
287 | [ -z "${LINE_NUM}" ] && {
288 | echo "未找到构造函数"
289 | exit 1
290 | }
291 |
292 | echo "正在修改 ${SMALI_FILE} 文件..."
293 | sed -i -e "/\.method public constructor ()V/,/\.end method/{" \
294 | -e "/\.locals 0/a\ invoke-static {}, Lcom/android/support/Main;->Start()V" \
295 | -e "}" "${SMALI_FILE}" || {
296 | echo "错误:添加smali代码失败,请检查文件路径、权限或文件内容格式。"
297 | exit 1
298 | }
299 | echo "smali代码添加成功!"
300 |
301 | echo "正在修改 AndroidManifest.xml 文件..."
302 | local MANIFEST_FILE="${DOWNLOAD_DIR}/DECODE_Output/AndroidManifest.xml"
303 | sed -i 's## \n \n #' "${MANIFEST_FILE}" || {
304 | echo "错误:修改 AndroidManifest.xml 文件失败,请检查文件路径、权限或文件内容格式。"
305 | exit 1
306 | }
307 | echo "修改成功!"
308 | echo "补丁完成。"
309 | }
310 |
311 | # 打包APK
312 | BUILD_APK() {
313 | local OUTPUT_APK
314 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
315 | OUTPUT_APK="${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk"
316 | else
317 | OUTPUT_APK="${DOWNLOAD_DIR}/${GAME_SERVER}.apk"
318 | fi
319 |
320 | echo "正在重新构建已打补丁的 APK 文件: ${OUTPUT_APK}"
321 | java -jar "${DOWNLOAD_DIR}/apktool.jar" b -f "${DOWNLOAD_DIR}/DECODE_Output" -o "${OUTPUT_APK}"
322 | if [ $? -ne 0 ]; then
323 | echo "错误: APK 构建失败!"
324 | exit 1
325 | fi
326 | echo "APK 构建成功"
327 | }
328 |
329 | # 优化并签名APK
330 | OPTIMIZE_AND_SIGN_APK() {
331 | export PATH=${PATH}:${BUILD_TOOLS_DIR}
332 | local KEY_DIR="${DOWNLOAD_DIR}/key/"
333 | local PRIVATE_KEY="${KEY_DIR}testkey.pk8"
334 | local CERTIFICATE="${KEY_DIR}testkey.x509.pem"
335 | local INPUT_APK
336 | local UNSIGNED_APK
337 | local FINAL_APK
338 |
339 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
340 | INPUT_APK="${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk"
341 | UNSIGNED_APK="${GAME_BUNDLE_ID}.unsigned.apk"
342 | else
343 | INPUT_APK="${DOWNLOAD_DIR}/${GAME_SERVER}.apk"
344 | UNSIGNED_APK="${GAME_SERVER}.unsigned.apk"
345 | fi
346 |
347 | local OUTPUT_APK="${DOWNLOAD_DIR}/${UNSIGNED_APK}"
348 | local FINAL_APK="${INPUT_APK}"
349 |
350 | if [ ! -f "${INPUT_APK}" ]; then
351 | echo "错误:找不到输入APK文件: ${INPUT_APK}"
352 | exit 1
353 | fi
354 |
355 | if [ ! -f "${PRIVATE_KEY}" ] || [ ! -f "${CERTIFICATE}" ]; then
356 | echo "错误:找不到签名密钥文件"
357 | echo "请确保以下文件存在:"
358 | echo " - ${PRIVATE_KEY}"
359 | echo " - ${CERTIFICATE}"
360 | exit 1
361 | else
362 | echo "已找到签名密钥:"
363 | echo " - ${PRIVATE_KEY}"
364 | echo " - ${CERTIFICATE}"
365 | fi
366 |
367 | echo "正在优化APK..."
368 | if zipalign -f 4 "${INPUT_APK}" "${OUTPUT_APK}"; then
369 | echo "优化成功"
370 | rm "${INPUT_APK}"
371 |
372 | echo "正在签名APK..."
373 | if apksigner sign --key "${PRIVATE_KEY}" --cert "${CERTIFICATE}" "${OUTPUT_APK}"; then
374 | echo "签名成功"
375 | mv "${OUTPUT_APK}" "${FINAL_APK}"
376 | else
377 | echo "签名失败"
378 | exit 1
379 | fi
380 | else
381 | echo "优化失败"
382 | exit 1
383 | fi
384 | }
385 |
386 | # 获取并传回游戏版本
387 | GET_GAME_VERSION() {
388 | local APK_TO_CHECK
389 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
390 | APK_TO_CHECK="${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk"
391 | else
392 | APK_TO_CHECK="${DOWNLOAD_DIR}/${GAME_SERVER}.apk"
393 | fi
394 |
395 | if [ -f "${APK_TO_CHECK}" ]; then
396 | if [ -f "${AAPT_PATH}" ]; then
397 | GAME_VERSION=$("${AAPT_PATH}" dump badging "${APK_TO_CHECK}" | grep "versionName" | sed "s/.*versionName='\([^']*\)'.*/\1/" | head -1)
398 | if [ -z "${GAME_VERSION}" ] || [ "${GAME_VERSION}" = "''" ]; then
399 | GAME_VERSION="未知"
400 | echo "警告:无法从APK提取版本信息"
401 | fi
402 | else
403 | echo "错误:找不到aapt工具: ${AAPT_PATH}"
404 | fi
405 | else
406 | echo "错误:APK文件不存在: ${APK_TO_CHECK}"
407 | fi
408 | echo "VERSION=${GAME_VERSION}" >> "${GITHUB_ENV}"
409 | echo "游戏版本: ${GAME_VERSION}"
410 | }
411 |
412 | # 重命名APK(APK模式使用)
413 | RENAME_APK() {
414 | if [ "${BUILD_TYPE}" = "APK" ] && [ -f "${DOWNLOAD_DIR}/${GAME_SERVER}.apk" ]; then
415 | if [ -f "${AAPT_PATH}" ]; then
416 | PACKAGE_NAME=$("${AAPT_PATH}" dump badging "${DOWNLOAD_DIR}/${GAME_SERVER}.apk" | grep "package: name=" | cut -d"'" -f2 | head -1)
417 | if [ -z "${PACKAGE_NAME}" ] || [ "${PACKAGE_NAME}" = "''" ]; then
418 | PACKAGE_NAME="${GAME_SERVER}"
419 | echo "警告:无法从APK提取包名,使用服务器名称作为包名"
420 | fi
421 | mv "${DOWNLOAD_DIR}/${GAME_SERVER}.apk" "${DOWNLOAD_DIR}/${PACKAGE_NAME}.apk"
422 | echo "重命名成功 [${PACKAGE_NAME}.apk]"
423 | else
424 | echo "错误:找不到aapt工具: ${AAPT_PATH}"
425 | fi
426 | fi
427 | }
428 |
429 | # 移动修改后的APK到源目录并重新打包XAPK(XAPK模式使用)
430 | REPACK_XAPK() {
431 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
432 | echo "正在重新打包XAPK..."
433 | mkdir -p "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}"
434 | mv -f "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.apk" "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}/${GAME_BUNDLE_ID}.apk"
435 | cd "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}" && zip -r "${GAME_BUNDLE_ID}.xapk" *
436 | cd - > /dev/null
437 | mv "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}/${GAME_BUNDLE_ID}.xapk" "${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.xapk"
438 | echo "XAPK重新打包完成"
439 | fi
440 | }
441 |
442 | # 生成7z分卷压缩包
443 | CREATE_SPLIT_ARCHIVES() {
444 | local FINAL_FILE
445 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
446 | FINAL_FILE="${DOWNLOAD_DIR}/${GAME_BUNDLE_ID}.xapk"
447 | else
448 | if [ -f "${DOWNLOAD_DIR}/${PACKAGE_NAME}.apk" ]; then
449 | FINAL_FILE="${DOWNLOAD_DIR}/${PACKAGE_NAME}.apk"
450 | else
451 | FINAL_FILE="${DOWNLOAD_DIR}/${GAME_SERVER}.apk"
452 | fi
453 | fi
454 |
455 | if [ ! -f "${FINAL_FILE}" ]; then
456 | echo "错误: 最终文件未找到: ${FINAL_FILE}"
457 | exit 1
458 | fi
459 | echo "正在压缩 ${FINAL_FILE}"
460 | 7z a -v800M "${GAME_SERVER}-V.${GAME_VERSION}.7z" "${FINAL_FILE}" || {
461 | echo "错误: 7z 压缩失败!"
462 | exit 1
463 | }
464 | echo "分卷压缩完成: ${GAME_SERVER}-V.${GAME_VERSION}.7z"
465 | }
466 |
467 | # 打印Logo
468 | PRINT_LOGO() {
469 | cat << "EOF"
470 |
471 | ________ ________ ___ ___ ________ ___ ________ ________ _______ ___ _____ ______ ________ ________
472 | |\ __ \|\_____ \|\ \|\ \|\ __ \|\ \ |\ __ \|\ ___ \|\ ___ \ |\ \|\ _ \ _ \|\ __ \|\ __ \
473 | \ \ \|\ \\___/ /\ \ \\ \ \ \|\ \ \ \ \ \ \|\ \ \ \\ \ \ \ __/| \ \ \ \ \\__\ \ \ \ \|\ /\ \ \|\ \
474 | \ \ __ \ / / /\ \ \\ \ \ _ _\ \ \ \ \ __ \ \ \\ \ \ \ \_|/__ __ \ \ \ \ \\|__| \ \ \ __ \ \ \\ \\
475 | \ \ \ \ \ / /_/__\ \ \\ \ \ \\ \\ \ \____\ \ \ \ \ \ \\ \ \ \ \_|\ \ |\ \\\_\ \ \ \ \ \ \ \ \|\ \ \ \\ \\
476 | \ \__\ \__\\________\ \_______\ \__\\ _\\ \_______\ \__\ \__\ \__\\ \__\ \_______\ \ \________\ \__\ \ \__\ \_______\ \_____ \
477 | \|__|\|__\|\|_______|\|_______|\|__|\|__\|_______|\|__|\|__|\|__| \|__\|_______| \|________|\|__| \|__\|_______|\|___| \__\
478 | \|__|
479 |
480 |
481 | EOF
482 | }
483 |
484 | # 主执行函数
485 | main() {
486 | PRINT_LOGO
487 | CHECK_PARAM
488 |
489 | # 根据构建类型执行不同的流程
490 | if [ "${BUILD_TYPE}" = "XAPK" ]; then
491 | # XAPK构建流程
492 | SET_BUNDLE_ID
493 | DOWNLOAD_APKEEP
494 | DOWNLOAD_APKTOOL
495 | DOWNLOAD_MOD_MENU
496 | DOWNLOAD_APK
497 | DELETE_ORGINAL_XAPK
498 | VERIFY_APK
499 | DECODE_APK
500 | DELETE_ORGINAL_APK
501 | PATCH_APK
502 | BUILD_APK
503 | OPTIMIZE_AND_SIGN_APK
504 | GET_GAME_VERSION
505 | REPACK_XAPK
506 | else
507 | # APK构建流程
508 | DOWNLOAD_APKTOOL
509 | DOWNLOAD_MOD_MENU
510 | DOWNLOAD_APK
511 | VERIFY_APK
512 | DECODE_APK
513 | DELETE_ORGINAL_APK
514 | PATCH_APK
515 | BUILD_APK
516 | OPTIMIZE_AND_SIGN_APK
517 | GET_GAME_VERSION
518 | RENAME_APK
519 | fi
520 |
521 | # 共同的后续步骤
522 | CREATE_SPLIT_ARCHIVES
523 | echo "构建完成!构建类型: ${BUILD_TYPE}"
524 | }
525 |
526 | # 执行主函数
527 | main
528 |
--------------------------------------------------------------------------------