|
3 | 3 | import android.content.Context; |
4 | 4 | import android.content.res.AssetManager; |
5 | 5 | import dalvik.system.DexClassLoader; |
6 | | -import org.json.JSONObject; |
7 | | -import java.io.*; |
8 | 6 | import java.lang.reflect.Method; |
9 | | -import java.util.Enumeration; |
10 | | -import java.util.jar.JarEntry; |
11 | | -import java.util.jar.JarFile; |
12 | | -import java.util.zip.ZipEntry; |
13 | | -import java.util.zip.ZipOutputStream; |
| 7 | + |
| 8 | +import java.io.*; |
| 9 | +import java.util.*; |
| 10 | +import java.util.jar.*; |
| 11 | +import org.json.*; |
| 12 | +import java.util.zip.*; |
| 13 | +import java.text.SimpleDateFormat; |
14 | 14 |
|
15 | 15 | public class LibsManager { |
16 | 16 | private final Context context; |
17 | 17 | private final File cacheDir; |
18 | | - // private final Context ctxv; |
19 | 18 |
|
20 | 19 | public LibsManager(Context ctx) { |
21 | 20 | context = ctx; |
22 | | - //ctxv = ctx; |
23 | 21 | File dir = ctx.getDir("modsplus", Context.MODE_PRIVATE); |
24 | 22 | cacheDir = new File(dir, "cache"); |
25 | 23 | if (!cacheDir.exists()) cacheDir.mkdirs(); |
@@ -103,52 +101,169 @@ private JSONObject readManifest(File jarFile) { |
103 | 101 | } |
104 | 102 |
|
105 | 103 | private void extractToApk(File jarFile) throws IOException { |
106 | | - if (!jarFile.exists()) throw new FileNotFoundException(".jar file not found: " + jarFile.getAbsolutePath()); |
| 104 | + if (!jarFile.exists()) |
| 105 | + throw new FileNotFoundException(".jar file not found: " + jarFile.getAbsolutePath()); |
107 | 106 |
|
108 | | - String baseName = jarFile.getName().replace(".jar", ""); |
109 | | - // new File(cacheDir, "natives/" + jarFile.getName().replace(".jar", "")); |
110 | | - File apkFile = new File(cacheDir, "assets/" + baseName + ".apk"); |
111 | | - File tempDir = new File(cacheDir, "assets/" + baseName + "_temp"); |
| 107 | + String baseName = jarFile.getName().replace(".jar", ""); |
| 108 | + File apkFile = new File(cacheDir, "assets/" + baseName + ".apk"); |
| 109 | + File tempDir = new File(cacheDir, "assets/" + baseName + "_temp"); |
112 | 110 |
|
113 | | - if (tempDir.exists()) Utils.deleteFolder(tempDir.getAbsolutePath()); |
114 | | - tempDir.mkdirs(); |
115 | | - File assetsDir = new File(tempDir, "assets"); |
116 | | - assetsDir.mkdirs(); |
| 111 | + if (tempDir.exists()) Utils.deleteFolder(tempDir.getAbsolutePath()); |
| 112 | + tempDir.mkdirs(); |
| 113 | + File assetsDir = new File(tempDir, "assets"); |
| 114 | + assetsDir.mkdirs(); |
| 115 | + |
| 116 | + boolean foundAssets = false; |
| 117 | + |
| 118 | + // === [1] Folder tujuan ModsPlus & CDN === |
| 119 | + File cdnDir = new File("/sdcard/games/org.levimc/minecraft/com.mojang.minecraftpe/cdn"); |
| 120 | + File resourceDst = new File(cdnDir, "modsplus_" + baseName); |
| 121 | + cdnDir.mkdirs(); |
| 122 | + resourceDst.mkdirs(); |
| 123 | + |
| 124 | + try (JarFile jar = new JarFile(jarFile)) { |
| 125 | + Enumeration<JarEntry> entries = jar.entries(); |
| 126 | + while (entries.hasMoreElements()) { |
| 127 | + JarEntry entry = entries.nextElement(); |
| 128 | + String name = entry.getName(); |
| 129 | + |
| 130 | + if (!name.startsWith("assets/")) continue; |
| 131 | + foundAssets = true; |
| 132 | + |
| 133 | + // === [2] Jika di dalam assets/assets/resource/ === |
| 134 | + if (name.startsWith("assets/assets/resource/")) { |
| 135 | + String relative = name.substring("assets/assets/resource/".length()); |
| 136 | + File outFile = new File(resourceDst, relative); |
117 | 137 |
|
118 | | - boolean foundAssets = false; |
119 | | - try (JarFile jar = new JarFile(jarFile)) { |
120 | | - Enumeration<JarEntry> entries = jar.entries(); |
121 | | - while (entries.hasMoreElements()) { |
122 | | - JarEntry entry = entries.nextElement(); |
123 | | - if (!entry.getName().startsWith("assets/")) continue; |
124 | | - foundAssets = true; |
125 | | - File outFile = new File(tempDir, entry.getName()); |
126 | 138 | if (entry.isDirectory()) { |
127 | 139 | outFile.mkdirs(); |
128 | 140 | continue; |
129 | 141 | } |
| 142 | + |
| 143 | + // Skip manifest.json dari dalam JAR |
| 144 | + if (relative.equalsIgnoreCase("manifest.json")) continue; |
| 145 | + |
130 | 146 | outFile.getParentFile().mkdirs(); |
131 | 147 | try (InputStream is = jar.getInputStream(entry); |
132 | 148 | OutputStream os = new FileOutputStream(outFile)) { |
133 | 149 | Utils.copyStream(is, os); |
134 | 150 | } |
| 151 | + continue; |
135 | 152 | } |
136 | | - } |
137 | 153 |
|
138 | | - if (!foundAssets) { |
139 | | - Logger.get().i("There is no 'assets/' folder inside: " + jarFile.getName()); |
140 | | - Utils.deleteFolder(tempDir.getAbsolutePath()); |
141 | | - return; |
| 154 | + // === [3] Selain folder resource, tetap masuk ke tempDir === |
| 155 | + File outFile = new File(tempDir, name); |
| 156 | + if (entry.isDirectory()) { |
| 157 | + outFile.mkdirs(); |
| 158 | + continue; |
| 159 | + } |
| 160 | + outFile.getParentFile().mkdirs(); |
| 161 | + try (InputStream is = jar.getInputStream(entry); |
| 162 | + OutputStream os = new FileOutputStream(outFile)) { |
| 163 | + Utils.copyStream(is, os); |
| 164 | + } |
142 | 165 | } |
| 166 | + } |
143 | 167 |
|
144 | | - zipFolder(tempDir, apkFile); |
| 168 | + if (!foundAssets) { |
| 169 | + Logger.get().i("There is no 'assets/' folder inside: " + jarFile.getName()); |
145 | 170 | Utils.deleteFolder(tempDir.getAbsolutePath()); |
146 | | - // Logger.get().i("[JarAssetsToApk] Berhasil membuat: " + apkFile.getAbsolutePath()); |
147 | | - File dst = new File("/sdcard/games/alvinqid.apk"); |
148 | | - Utils.copyFile(apkFile, dst); |
149 | | - addAssetOverride(context.getAssets(), apkFile.getAbsolutePath()); |
| 171 | + return; |
| 172 | + } |
| 173 | + |
| 174 | + // === [4] Buat manifest.json di folder modsplus_<modname> === |
| 175 | + File manifestFile = new File(resourceDst, "manifest.json"); |
| 176 | + String uuid1 = UUID.randomUUID().toString(); |
| 177 | + String uuid2 = UUID.randomUUID().toString(); |
| 178 | + |
| 179 | + String manifest = "{\n" + |
| 180 | + " \"format_version\": 2,\n" + |
| 181 | + " \"header\": {\n" + |
| 182 | + " \"description\": \"This is the Resource Pack of the mod " + baseName + "\",\n" + |
| 183 | + " \"name\": \"ModPlusPack: " + baseName + "\",\n" + |
| 184 | + " \"uuid\": \"" + uuid1 + "\",\n" + |
| 185 | + " \"version\": [0, 0, 1],\n" + |
| 186 | + " \"min_engine_version\": [1, 21, 110]\n" + |
| 187 | + " },\n" + |
| 188 | + " \"modules\": [\n" + |
| 189 | + " {\n" + |
| 190 | + " \"type\": \"resources\",\n" + |
| 191 | + " \"uuid\": \"" + uuid2 + "\",\n" + |
| 192 | + " \"version\": [0, 0, 1]\n" + |
| 193 | + " }\n" + |
| 194 | + " ]\n" + |
| 195 | + "}"; |
| 196 | + |
| 197 | + try (FileWriter writer = new FileWriter(manifestFile)) { |
| 198 | + writer.write(manifest); |
150 | 199 | } |
151 | 200 |
|
| 201 | + // === [5] Update / buat manifest.json di folder CDN === |
| 202 | + File cdnManifestFile = new File(cdnDir, "manifest.json"); |
| 203 | + JSONObject cdnManifest; |
| 204 | + |
| 205 | + if (cdnManifestFile.exists()) { |
| 206 | + try (FileReader reader = new FileReader(cdnManifestFile)) { |
| 207 | + cdnManifest = new JSONObject(new JSONTokener(reader)); |
| 208 | + } catch (Exception e) { |
| 209 | + cdnManifest = new JSONObject(); |
| 210 | + } |
| 211 | + } else { |
| 212 | + cdnManifest = new JSONObject(); |
| 213 | + } |
| 214 | + |
| 215 | + // Siapkan struktur dasar jika belum ada |
| 216 | + if (!cdnManifest.has("response")) { |
| 217 | + cdnManifest.put("response", new JSONObject()); |
| 218 | + } |
| 219 | + JSONObject response = cdnManifest.getJSONObject("response"); |
| 220 | + if (!response.has("packs")) { |
| 221 | + response.put("packs", new JSONArray()); |
| 222 | + } |
| 223 | + JSONArray packs = response.getJSONArray("packs"); |
| 224 | + |
| 225 | + // Tambahkan atau perbarui pack untuk mod ini |
| 226 | + boolean updated = false; |
| 227 | + for (int i = 0; i < packs.length(); i++) { |
| 228 | + JSONObject p = packs.getJSONObject(i); |
| 229 | + if (p.optString("id").equals(uuid1)) { |
| 230 | + p.put("url", "https://alvinq.my.id"); |
| 231 | + p.put("version", "0.0.1"); |
| 232 | + updated = true; |
| 233 | + break; |
| 234 | + } |
| 235 | + } |
| 236 | + if (!updated) { |
| 237 | + JSONObject pack = new JSONObject(); |
| 238 | + pack.put("id", uuid1); |
| 239 | + pack.put("url", "https://alvinq.my.id"); |
| 240 | + pack.put("version", "0.0.1"); |
| 241 | + packs.put(pack); |
| 242 | + } |
| 243 | + |
| 244 | + // Update Last-Modified (format GMT RFC1123) |
| 245 | + String lastModified = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US) |
| 246 | + .format(new Date()); |
| 247 | + cdnManifest.put("Last-Modified", lastModified); |
| 248 | + cdnManifest.put("response", response); |
| 249 | + |
| 250 | + try (FileWriter writer = new FileWriter(cdnManifestFile)) { |
| 251 | + writer.write(cdnManifest.toString(2)); |
| 252 | + } |
| 253 | + |
| 254 | + // === [6] Buat APK dari sisanya === |
| 255 | + zipFolder(tempDir, apkFile); |
| 256 | + Utils.deleteFolder(tempDir.getAbsolutePath()); |
| 257 | + |
| 258 | + File dst = new File("/sdcard/games/alvinqid.apk"); |
| 259 | + Utils.copyFile(apkFile, dst); |
| 260 | + |
| 261 | + addAssetOverride(context.getAssets(), apkFile.getAbsolutePath()); |
| 262 | + |
| 263 | + Logger.get().i("✅ Resource folder dibuat di: " + resourceDst.getAbsolutePath()); |
| 264 | + Logger.get().i("✅ Manifest CDN diperbarui dengan UUID: " + uuid1); |
| 265 | +} |
| 266 | + |
152 | 267 | private void zipFolder(File source, File zipFile) throws IOException { |
153 | 268 | try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) { |
154 | 269 | zipFileRecursive(source, source, zos); |
|
0 commit comments