Compare commits

..
51 Commits
Author SHA1 Message Date
tarsin 3e63738542 Make a release
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 08:21:14 +08:00
tarsin d6823c9c8e Checkout to our newly patched libarchive 2022-05-23 08:04:59 +08:00
tarsin 6bc3866660 Build lzma
FIXME: Don't hack!!
https://github.com/asuka-mio/libarchive/commit/588598490ad0a80e44554ed5e608a9f9878220c0
2022-05-23 08:02:20 +08:00
tarsin f41f4dd8ba Support 7z read 2022-05-23 07:31:58 +08:00
tarsin cde39812ce Update proguard-rules.pro 2022-05-23 01:12:58 +08:00
tarsin 03c2abc901 Merge pull request #34 from txyyh/feature/Button
Use Material3 Button instead
2022-05-23 00:59:14 +08:00
tarsin 7646334525 Don't obfuscate native code 2022-05-23 00:59:00 +08:00
txyyh f8d1cd3fab Use Material3 Button instead 2022-05-23 00:47:28 +08:00
tarsin 4ea94be64a Update releases.yml 2022-05-23 00:44:56 +08:00
tarsin dc0470e75a Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 00:34:33 +08:00
tarsin 5d49ecdd7d Add lzma xz v5.2.5 2022-05-23 00:29:01 +08:00
tarsin ef2441116d Use more friendly suffix recogniser and support all image file 2022-05-22 23:17:35 +08:00
tarsin 207909c7be Initial libarchive simple support 2022-05-22 22:27:31 +08:00
tarsin ddb3dd2d95 Merge pull request #33 from txyyh/feature/buildguide
Add build guide
2022-05-22 02:17:38 +08:00
txyyh 8bb980fe97 Add build guide 2022-05-22 01:55:38 +08:00
tarsin 3c3a201ed8 Merge pull request #30 from txyyh/feature/README
Rewrite README header
2022-05-22 01:31:13 +08:00
tarsin 81bb37e929 Merge pull request #31 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-api-2.x
Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final
2022-05-22 01:30:54 +08:00
Renovate Bot ae34690eb2 Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final 2022-05-21 17:30:16 +00:00
tarsin 285b0f5a69 Merge pull request #32 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-jdt-2.x
Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final
2022-05-22 01:29:43 +08:00
Renovate Bot a5327fce07 Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final 2022-05-21 15:11:11 +00:00
txyyh a4b750f9cd Add description title 2022-05-21 21:58:53 +08:00
txyyh 943344d4bb Rewrite shields 2022-05-21 21:58:50 +08:00
txyyh 79f1b92960 Rewrite icon header 2022-05-21 21:26:48 +08:00
tarsin 575ae38a28 Re-checkout 2022-05-21 20:46:25 +08:00
tarsin f90e2a5c80 Checkout to our libarchive 2022-05-21 20:34:06 +08:00
tarsin 4d9b2877fe Build libarchive 2022-05-21 20:08:03 +08:00
tarsin 4a955947a2 Add libarchive 2022-05-21 19:41:09 +08:00
tarsin 7307c136ee Drop useless native part 2022-05-21 17:24:03 +08:00
tarsin 657e1b1d0f Drop a7zip for libarchive integrate 2022-05-21 17:04:27 +08:00
tarsin 43edf11275 Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-21 00:10:34 +08:00
tarsin 32ec4c6715 Merge pull request #29 from txyyh/feature/AutoSign
AutoSign and Releases
2022-05-21 00:04:58 +08:00
txyyh 84ac57d69e Releases with tag 2022-05-20 23:57:51 +08:00
txyyh 328e7745b7 Add step name 2022-05-20 23:23:14 +08:00
txyyh ccf22eaef4 Upload right artifact 2022-05-20 23:23:14 +08:00
txyyh c89d42f368 Use keystore in project 2022-05-20 23:23:14 +08:00
txyyh 41192ea7f5 Format 2022-05-20 23:23:14 +08:00
tarsin 7840987a7c native cmake tweaks 2022-05-20 22:59:23 +08:00
tarsin fcbe0a7f55 layout tweaks 2022-05-20 20:33:37 +08:00
tarsin f12894c95d Extract help message from add action 2022-05-20 20:32:15 +08:00
tarsin 40f139fda2 Merge pull request #28 from txyyh/master
Change gallery_detail button theme
2022-05-20 17:32:01 +08:00
txyyh b8507a371e Button side-by-side 2022-05-20 14:32:57 +08:00
txyyh e5a71ce906 Use TextButton instead 2022-05-20 14:32:57 +08:00
txyyh f9e708fe1e Disable bold text 2022-05-20 14:32:57 +08:00
txyyh 50ba6f2bd2 Use elevated instead 2022-05-20 14:32:57 +08:00
tarsin 2484371f8c Import compose activity 2022-05-20 13:42:02 +08:00
tarsin ba675c4212 Use SwitchPreferenceCompat 2022-05-20 13:40:52 +08:00
tarsin 6f9bc3135a Import Jetpack Compose 2022-05-20 13:04:54 +08:00
tarsin 4475d4d018 Search layout tweaks 2022-05-20 12:31:00 +08:00
tarsin 08e45db49d Update README.md 2022-05-20 01:34:25 +08:00
tarsin 85c76065a5 Delete codeql-analysis.yml 2022-05-20 01:28:53 +08:00
tarsin 13582c3f1b Create codeql-analysis.yml 2022-05-20 01:24:27 +08:00
97 changed files with 819 additions and 8653 deletions

No files matched your search

+20 -6
View File
@@ -7,22 +7,36 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;24.0.8215888" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- uses: gradle/gradle-build-action@v2
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- run: |
git submodule update --init --recursive
gradle assembleRelease
- uses: actions/upload-artifact@v3
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: github-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
name: arm64-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
+41
View File
@@ -0,0 +1,41 @@
name: Releases
on:
push:
tags:
- "*"
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;24.0.8215888" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- run: |
git submodule update --init --recursive
gradle assembleRelease
- name: Releases
uses: softprops/action-gh-release@v0.1.14
with:
body: Bump Version
files: |
app/build/outputs/apk/release/app-arm64-v8a-release.apk
app/build/outputs/apk/release/app-x86_64-release.apk
+6 -3
View File
@@ -1,6 +1,3 @@
[submodule "native/src/main/jni/a7zip/p7zip/p7zip"]
path = native/src/main/jni/a7zip/p7zip/p7zip
url = https://github.com/seven332/p7zip.git
[submodule "native/src/main/jni/image/libjpeg-turbo/libjpeg-turbo"]
path = native/src/main/jni/image/libjpeg-turbo/libjpeg-turbo
url = https://gitlab.com/NekoInverter/libjpeg-turbo.git
@@ -10,3 +7,9 @@
[submodule "native/src/main/jni/image/libpng/libpng"]
path = native/src/main/jni/image/libpng/libpng
url = https://gitlab.com/NekoInverter/libpng.git
[submodule "native/src/main/jni/libarchive"]
path = native/src/main/jni/libarchive
url = https://github.com/asuka-mio/libarchive
[submodule "native/src/main/jni/liblzma"]
path = native/src/main/jni/liblzma
url = https://git.tukaani.org/xz.git
+32 -2
View File
@@ -1,7 +1,27 @@
# EhViewer
<h1 align="center">
<img src="art/launcher_icon-web.png" width="200">
<br>EhViewer<br>
</h1>
![Icon](art/launcher_icon-web.png)
<p align="center">
<a href="https://github.com/asuka-mio/Ehviewer/actions">
<img src="https://img.shields.io/github/workflow/status/asuka-mio/Ehviewer/EhViewer" alt="Github Actions">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/asuka-mio/Ehviewer" alt="LICENSE">
</a>
<a href="https://www.codefactor.io/repository/github/asuka-mio/ehviewer">
<img src="https://www.codefactor.io/repository/github/asuka-mio/ehviewer/badge" alt="CodeFactor">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/releases">
<img src="https://img.shields.io/github/v/release/asuka-mio/ehviewer?include_prereleases" alt="Release">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/issues">
<img src="https://img.shields.io/github/issues/asuka-mio/ehviewer" alt="Issues">
</a>
</p>
# Description
这是一个 E-Hentai Android 平台的浏览器
已针对性适配 Material Design 3 及其动态取色系统
@@ -24,6 +44,16 @@ Only Support Android S and above!
![screenshot-01](art/screenshot-01.png)
# Build
本项目使用了 CMake 3.23.1 作为构建工具链之一,而当前版本的 Android Studio SDK Tool 无法满足本项目需求。
因此,在构建本项目前需要提前配置好 CMake >=3.23.1。
This Project use CMake 3.23.1, with is highter than Android Studio bundle CMake.
So you need to pre-install CMake>=3.23.1 before build.
# Thanks
本项目受到了诸多开源项目的帮助
+23 -16
View File
@@ -22,22 +22,24 @@ android {
}
signingConfigs {
if (localProperties.getProperty("storeFile") != null) {
sign {
storeFile file(localProperties.getProperty("storeFile"))
storePassword localProperties.getProperty("storePassword")
keyAlias localProperties.getProperty("keyAlias")
keyPassword localProperties.getProperty("keyPassword")
storeFile file(signStoreFile)
storePassword signStorePassword
keyAlias signkeyAlias
keyPassword signkeyPassword
}
}
}
buildFeatures {
compose true
}
defaultConfig {
applicationId "io.github.nekoinverter.ehviewer"
minSdkVersion 31
targetSdkVersion 32
versionCode 114518
versionName "1.7.26.9"
versionCode 114521
versionName "1.7.27.1"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -72,17 +74,11 @@ android {
minifyEnabled true
shrinkResources true
proguardFiles 'proguard-rules.pro'
if (localProperties.getProperty("storeFile") != null) {
signingConfig signingConfigs.sign
}
signingConfig signingConfigs.sign
}
debug {
applicationIdSuffix ".debug"
if (localProperties.getProperty("storeFile") != null) {
signingConfig signingConfigs.sign
} else {
signingConfig signingConfigs.debug
}
signingConfig signingConfigs.debug
}
}
@@ -91,6 +87,10 @@ android {
java.srcDirs += 'src/main/java-gen'
}
}
composeOptions {
kotlinCompilerExtensionVersion compose
}
namespace 'com.hippo.ehviewer'
}
@@ -100,6 +100,7 @@ tasks.withType(JavaCompile) {
dependencies {
implementation 'androidx.activity:activity:1.4.0'
implementation "androidx.activity:activity-compose:1.4.0"
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'androidx.browser:browser:1.4.0'
implementation 'androidx.core:core:1.7.0'
@@ -138,6 +139,12 @@ dependencies {
implementation 'org.greenrobot:greendao:3.3.0'
implementation 'org.jsoup:jsoup:1.15.1'
implementation projects.native
implementation "androidx.compose.material3:material3:1.0.0-alpha12"
implementation "androidx.compose.ui:ui:$compose"
implementation "androidx.compose.ui:ui-util:$compose"
implementation "androidx.compose.animation:animation-graphics:$compose"
}
configurations.all {
Binary file not shown.
@@ -457,14 +457,6 @@ public class Settings {
putBoolean(KEY_SELECT_SITE, value);
}
public static boolean getQuickSearchTip() {
return getBoolean(KEY_QUICK_SEARCH_TIP, DEFAULT_QUICK_SEARCH_TIP);
}
public static void putQuickSearchTip(boolean value) {
putBoolean(KEY_QUICK_SEARCH_TIP, value);
}
public static int getTheme() {
return getIntFromStr(KEY_THEME, DEFAULT_THEME);
}
@@ -1,174 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.gallery;
import androidx.annotation.NonNull;
import com.hippo.a7zip.ArchiveException;
import com.hippo.a7zip.InArchive;
import com.hippo.a7zip.PropID;
import com.hippo.a7zip.PropType;
import com.hippo.a7zip.SeekableInputStream;
import com.hippo.unifile.UniRandomAccessFile;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
class A7ZipArchive implements Closeable {
private final InArchive archive;
private A7ZipArchive(InArchive archive) {
this.archive = archive;
}
private static boolean isSupportedFilename(String name) {
for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) {
if (name.endsWith(extension)) {
return true;
}
}
return false;
}
static A7ZipArchive create(UniRandomAccessFile file) throws ArchiveException {
SeekableInputStream store = new UniRandomAccessFileInStream(file);
InArchive archive = InArchive.open(store);
if ((archive.getArchivePropertyType(PropID.ENCRYPTED) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.ENCRYPTED))
|| (archive.getArchivePropertyType(PropID.SOLID) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.SOLID))
|| (archive.getArchivePropertyType(PropID.IS_VOLUME) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.IS_VOLUME))) {
throw new ArchiveException("Unsupported archive");
}
return new A7ZipArchive(archive);
}
@Override
public void close() {
archive.close();
}
List<A7ZipArchiveEntry> getArchiveEntries() {
List<A7ZipArchiveEntry> entries = new ArrayList<>();
for (int i = 0, n = archive.getNumberOfEntries(); i < n; i++) {
if (!archive.getEntryBooleanProperty(i, PropID.ENCRYPTED)
&& !archive.getEntryBooleanProperty(i, PropID.IS_DIR)
&& !archive.getEntryBooleanProperty(i, PropID.IS_VOLUME)
&& !archive.getEntryBooleanProperty(i, PropID.SOLID)) {
String path = archive.getEntryPath(i);
if (isSupportedFilename(path.toLowerCase())) {
entries.add(new A7ZipArchiveEntry(archive, i, path));
}
}
}
return entries;
}
static class A7ZipArchiveEntry {
private final InArchive archive;
private final int index;
private final String path;
private A7ZipArchiveEntry(InArchive archive, int index, String path) {
this.archive = archive;
this.index = index;
this.path = path;
}
String getPath() {
return path;
}
void extract(OutputStream os) throws ArchiveException {
archive.extractEntry(index, new OutputStreamSequentialOutStream(os));
}
}
private static class UniRandomAccessFileInStream extends SeekableInputStream {
private final byte[] scratch = new byte[8];
private final UniRandomAccessFile file;
public UniRandomAccessFileInStream(UniRandomAccessFile file) {
this.file = file;
}
@Override
public void seek(long pos) throws IOException {
file.seek(pos);
}
@Override
public long tell() throws IOException {
return file.getFilePointer();
}
@Override
public long size() throws IOException {
return file.length();
}
@Override
public int read() throws IOException {
return (file.read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return file.read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return file.read(b, off, len);
}
@Override
public void close() throws IOException {
file.close();
}
}
private static class OutputStreamSequentialOutStream extends OutputStream {
private final OutputStream stream;
public OutputStreamSequentialOutStream(OutputStream stream) {
this.stream = stream;
}
@Override
public void write(int b) throws IOException {
stream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
stream.write(b, off, len);
}
@Override
public void close() throws IOException {
stream.close();
}
}
}
@@ -23,23 +23,18 @@ import android.os.Process;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.a7zip.ArchiveException;
import com.hippo.UriArchiveAccessor;
import com.hippo.ehviewer.GetText;
import com.hippo.ehviewer.R;
import com.hippo.glgallery.GalleryPageView;
import com.hippo.image.Image;
import com.hippo.unifile.UniFile;
import com.hippo.unifile.UniRandomAccessFile;
import com.hippo.util.NaturalComparator;
import com.hippo.yorozuya.thread.PriorityThread;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
@@ -47,15 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class ArchiveGalleryProvider extends GalleryProvider2 {
private static final AtomicInteger sIdGenerator = new AtomicInteger();
private static final Comparator<A7ZipArchive.A7ZipArchiveEntry> naturalComparator = new Comparator<A7ZipArchive.A7ZipArchiveEntry>() {
private final NaturalComparator comparator = new NaturalComparator();
@Override
public int compare(A7ZipArchive.A7ZipArchiveEntry o1, A7ZipArchive.A7ZipArchiveEntry o2) {
return comparator.compare(o1.getPath(), o2.getPath());
}
};
private final UniFile file;
private final UriArchiveAccessor archiveAccessor;
private final Stack<Integer> requests = new Stack<>();
private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
private final LinkedHashMap<Integer, InputStream> streams = new LinkedHashMap<>();
@@ -65,8 +52,15 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
private volatile int size = STATE_WAIT;
private String error;
public ArchiveGalleryProvider(Context context, Uri uri) {
file = UniFile.fromMediaUri(context, uri);
public ArchiveGalleryProvider(Context context, Uri uri){
UriArchiveAccessor archiveAccessor1;
try {
archiveAccessor1 = new UriArchiveAccessor(context, uri);
} catch (FileNotFoundException e) {
archiveAccessor1 = null;
e.printStackTrace();
}
archiveAccessor = archiveAccessor1;
}
@Override
@@ -167,37 +161,14 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
private class ArchiveTask implements Runnable {
@Override
public void run() {
UniRandomAccessFile uraf = null;
try {
uraf = file.createRandomAccessFile("r");
} catch (IOException e) {
e.printStackTrace();
}
if (uraf == null) {
size = archiveAccessor.open();
if (size <= 0) {
size = STATE_ERROR;
error = GetText.getString(R.string.error_reading_failed);
notifyDataChanged();
return;
}
A7ZipArchive archive = null;
try {
archive = A7ZipArchive.create(uraf);
} catch (ArchiveException e) {
e.printStackTrace();
}
if (archive == null) {
size = STATE_ERROR;
error = GetText.getString(R.string.error_invalid_archive);
notifyDataChanged();
return;
}
List<A7ZipArchive.A7ZipArchiveEntry> entries = archive.getArchiveEntries();
Collections.sort(entries, naturalComparator);
// Update size and notify changed
size = entries.size();
notifyDataChanged();
while (!Thread.currentThread().isInterrupted()) {
@@ -217,13 +188,13 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
}
// Check index valid
if (index < 0 || index >= entries.size()) {
if (index < 0 || index >= size) {
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
notifyPageFailed(index, GetText.getString(R.string.error_out_of_range));
continue;
}
Pipe pipe = new Pipe(4 * 1024);
Pipe pipe = new Pipe(32 * 2048);
synchronized (streams) {
if (streams.get(index) != null) {
@@ -233,13 +204,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
streams.notify();
}
try {
entries.get(index).extract(pipe.getOutputStream());
} catch (ArchiveException e) {
e.printStackTrace();
} finally {
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
}
archiveAccessor.extractTargetIndexToOutputStream(index, pipe.getOutputStream());
}
}
}
@@ -282,4 +247,4 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
}
}
}
}
}
@@ -107,13 +107,6 @@ class Pipe {
public void write(@NonNull byte[] b, int off, int len) throws IOException {
synchronized (Pipe.this) {
while (len != 0) {
if (outClosed) {
throw new IOException("The OutputStream is closed");
}
if (inClosed) {
throw new IOException("The InputStream is closed");
}
if (head == tail && full) {
// The buffer is full, wait for InputStream read bytes
try {
@@ -238,13 +238,9 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
} else if (Intent.ACTION_VIEW.equals(mAction)) {
if (mUri != null) {
// Only support zip now
try {
grantUriPermission(BuildConfig.APPLICATION_ID, mUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (Exception e) {
// Some stupid file manager send us a uri and don't allow us to read it
// java.lang.SecurityException: UID 10671 does not have permission to
// content://com.UCMobile.fileProvider/external_files/BaiduNetdisk/getvoice [user 0]
Toast.makeText(this, R.string.error_reading_failed, Toast.LENGTH_SHORT).show();
}
mGalleryProvider = new ArchiveGalleryProvider(this, mUri);
@@ -709,15 +709,10 @@ public final class GalleryListScene extends BaseScene
if (null == context) {
return;
}
final CheckBoxDialogBuilder builder = new CheckBoxDialogBuilder(
context, getString(R.string.add_quick_search_tip), getString(R.string.get_it), false);
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
builder.setMessage(R.string.add_quick_search_tip);
builder.setTitle(R.string.readme);
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
if (builder.isChecked()) {
Settings.putQuickSearchTip(false);
}
showAddQuickSearchDialog(adapter, recyclerView, tip);
}).show();
builder.show();
}
@@ -823,11 +818,9 @@ public final class GalleryListScene extends BaseScene
toolbar.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if (id == R.id.action_add) {
if (Settings.getQuickSearchTip()) {
showQuickSearchTipDialog(qsDrawerAdapter, recyclerView, tip);
} else {
showAddQuickSearchDialog(qsDrawerAdapter, recyclerView, tip);
}
showAddQuickSearchDialog(qsDrawerAdapter, recyclerView, tip);
} else if (id == R.id.action_help) {
showQuickSearchTipDialog(qsDrawerAdapter, recyclerView, tip);
}
return true;
});
@@ -15,103 +15,72 @@
*/
package com.hippo.ehviewer.widget
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import android.view.LayoutInflater
import com.google.android.material.chip.ChipGroup
import com.hippo.widget.RadioGridGroup
import com.google.android.material.tabs.TabLayout
import androidx.recyclerview.widget.LinearLayoutManager
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import androidx.recyclerview.widget.DefaultItemAnimator
import com.hippo.ehviewer.R
import com.hippo.easyrecyclerview.MarginItemDecoration
import android.view.ViewGroup
import android.util.SparseArray
import android.os.Parcelable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import kotlin.Throws
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.ehviewer.client.data.ListUrlBuilder
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import androidx.annotation.IntDef
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.hippo.easyrecyclerview.EasyRecyclerView
import com.hippo.easyrecyclerview.MarginItemDecoration
import com.hippo.easyrecyclerview.SimpleHolder
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhConfig
import com.hippo.ehviewer.client.data.ListUrlBuilder
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.widget.RadioGridGroup
import com.hippo.yorozuya.NumberUtils
import com.hippo.yorozuya.ViewUtils
import java.lang.IndexOutOfBoundsException
class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, View.OnClickListener,
@SuppressLint("InflateParams")
class SearchLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : EasyRecyclerView(context, attrs), CompoundButton.OnCheckedChangeListener, View.OnClickListener,
ImageSearchLayout.Helper, OnTabSelectedListener {
private val mInflater : LayoutInflater
private val mSharePref : SharedPreferences
@SearchMode
private var mSearchMode = SEARCH_MODE_NORMAL
private var mEnableAdvance = false
private var mNormalView: View? = null
private var mNormalSearchMode: RadioGridGroup? = null
private var mNormalSearchModeHelp: ImageView? = null
private var mEnableAdvanceSwitch: Switch? = null
private var mAdvanceView: View? = null
private var mTableAdvanceSearch: AdvanceSearchTable? = null
private var mImageView: ImageSearchLayout? = null
private var mActionView: View? = null
private var mAction: TabLayout? = null
private var mLayoutManager: LinearLayoutManager? = null
private var mAdapter: SearchAdapter? = null
private var mCategoryGroup: ChipGroup
private var mNormalView: View
private var mNormalSearchMode: RadioGridGroup
private var mNormalSearchModeHelp: ImageView
private var mEnableAdvanceSwitch: SwitchMaterial
private var mAdvanceView: View
private var mTableAdvanceSearch: AdvanceSearchTable
private var mImageView: ImageSearchLayout
private var mActionView: View
private var mAction: TabLayout
private var mAdapter: SearchAdapter
private var mHelper: Helper? = null
private val mSharePref: SharedPreferences
private val mInflater: LayoutInflater
private val mCategoryTable : ArrayList<Pair<Int, Int>> = arrayListOf(
Pair(EhConfig.DOUJINSHI, R.string.doujinshi),
Pair(EhConfig.MANGA, R.string.manga),
Pair(EhConfig.ARTIST_CG, R.string.artist_cg),
Pair(EhConfig.GAME_CG, R.string.game_cg),
Pair(EhConfig.WESTERN, R.string.western),
Pair(EhConfig.NON_H, R.string.non_h),
Pair(EhConfig.IMAGE_SET, R.string.image_set),
Pair(EhConfig.COSPLAY, R.string.cosplay),
Pair(EhConfig.ASIAN_PORN, R.string.asian_porn),
Pair(EhConfig.MISC, R.string.misc)
)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init {
mSharePref = PreferenceManager.getDefaultSharedPreferences(context)
mInflater = LayoutInflater.from(context)
init(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle
) {
mSharePref = PreferenceManager.getDefaultSharedPreferences(context)
mInflater = LayoutInflater.from(context)
init(context)
}
@SuppressLint("InflateParams")
private fun init(context: Context) {
val resources = context.resources
mLayoutManager = SearchLayoutManager(context)
layoutManager = SearchLayoutManager(context)
mAdapter = SearchAdapter()
mAdapter!!.setHasStableIds(true)
layoutManager = mLayoutManager
mAdapter.setHasStableIds(true)
adapter = mAdapter
setHasFixedSize(true)
clipToPadding = false
(itemAnimator as DefaultItemAnimator?)!!.supportsChangeAnimations = false
(itemAnimator as DefaultItemAnimator?)?.supportsChangeAnimations = false
val interval = resources.getDimensionPixelOffset(R.dimen.search_layout_interval)
val paddingH = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_h)
val paddingV = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_v)
@@ -120,13 +89,10 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
)
addItemDecoration(decoration)
decoration.applyPaddings(this)
// Create normal view
val normalView = mInflater.inflate(R.layout.search_normal, null)
mNormalView = normalView
mNormalView = mInflater.inflate(R.layout.search_normal, null)
val mCategoryStored = mSharePref.getInt(SEARCH_CATEGORY_PREF, EhConfig.ALL_CATEGORY)
val mCategoryGroup = normalView.findViewById(R.id.search_category_chipgroup) as ChipGroup
mCategoryGroup = mNormalView.findViewById(R.id.search_category_chipgroup)
for (mPair in mCategoryTable) {
val mChip = IdentifiedChip(context)
mChip.isCheckable = true
@@ -142,46 +108,44 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
mSharePref.edit { putInt(SEARCH_CATEGORY_PREF, mCategory) }
}
mNormalSearchMode = normalView.findViewById(R.id.normal_search_mode)
mNormalSearchModeHelp = normalView.findViewById(R.id.normal_search_mode_help)
mEnableAdvanceSwitch = normalView.findViewById(R.id.search_enable_advance)
mNormalSearchModeHelp!!.setOnClickListener(this)
mEnableAdvanceSwitch!!.setOnCheckedChangeListener(this)
mEnableAdvanceSwitch!!.switchPadding = resources.getDimensionPixelSize(R.dimen.switch_padding)
mNormalSearchMode = mNormalView.findViewById(R.id.normal_search_mode)
mNormalSearchModeHelp = mNormalView.findViewById(R.id.normal_search_mode_help)
mEnableAdvanceSwitch = mNormalView.findViewById(R.id.search_enable_advance)
mNormalSearchModeHelp.setOnClickListener(this)
mEnableAdvanceSwitch.setOnCheckedChangeListener(this)
mEnableAdvanceSwitch.switchPadding =
resources.getDimensionPixelSize(R.dimen.switch_padding)
// Create advance view
mAdvanceView = mInflater.inflate(R.layout.search_advance, null)
mTableAdvanceSearch = mAdvanceView!!.findViewById(R.id.search_advance_search_table)
mTableAdvanceSearch = mAdvanceView.findViewById(R.id.search_advance_search_table)
// Create image view
mImageView = mInflater.inflate(R.layout.search_image, null) as ImageSearchLayout
mImageView!!.setHelper(this)
mImageView.setHelper(this)
// Create action view
mActionView = mInflater.inflate(R.layout.search_action, null)
mActionView!!.layoutParams = LayoutParams(
mActionView.layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
mAction = mActionView!!.findViewById(R.id.action)
mAction!!.addOnTabSelectedListener(this)
mAction = mActionView.findViewById(R.id.action)
mAction.addOnTabSelectedListener(this)
}
fun setHelper(helper: Helper?) {
mHelper = helper
}
fun scrollSearchContainerToTop() {
mLayoutManager!!.scrollToPositionWithOffset(0, 0)
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0)
}
fun setImageUri(imageUri: Uri?) {
mImageView!!.setImageUri(imageUri)
mImageView.setImageUri(imageUri)
}
fun setNormalSearchMode(id: Int) {
mNormalSearchMode!!.check(id)
mNormalSearchMode.check(id)
}
override fun onSelectImage() {
@@ -190,47 +154,15 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
}
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
super.dispatchSaveInstanceState(container)
mNormalView!!.saveHierarchyState(container)
mAdvanceView!!.saveHierarchyState(container)
mImageView!!.saveHierarchyState(container)
mActionView!!.saveHierarchyState(container)
}
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
super.dispatchRestoreInstanceState(container)
mNormalView!!.restoreHierarchyState(container)
mAdvanceView!!.restoreHierarchyState(container)
mImageView!!.restoreHierarchyState(container)
mActionView!!.restoreHierarchyState(container)
}
override fun onSaveInstanceState(): Parcelable {
val state = Bundle()
state.putParcelable(STATE_KEY_SUPER, super.onSaveInstanceState())
state.putInt(STATE_KEY_SEARCH_MODE, mSearchMode)
state.putBoolean(STATE_KEY_ENABLE_ADVANCE, mEnableAdvance)
return state
}
override fun onRestoreInstanceState(state: Parcelable) {
if (state is Bundle) {
super.onRestoreInstanceState(state.getParcelable(STATE_KEY_SUPER))
mSearchMode = state.getInt(STATE_KEY_SEARCH_MODE)
mEnableAdvance = state.getBoolean(STATE_KEY_ENABLE_ADVANCE)
}
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
if (buttonView === mEnableAdvanceSwitch) {
post {
mEnableAdvance = isChecked
if (mSearchMode == SEARCH_MODE_NORMAL) {
if (mEnableAdvance) {
mAdapter!!.notifyItemInserted(1)
mAdapter.notifyItemInserted(1)
} else {
mAdapter!!.notifyItemRemoved(1)
mAdapter.notifyItemRemoved(1)
}
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
@@ -246,7 +178,7 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
urlBuilder.reset()
when (mSearchMode) {
SEARCH_MODE_NORMAL -> {
when (mNormalSearchMode!!.checkedRadioButtonId) {
when (mNormalSearchMode.checkedRadioButtonId) {
R.id.search_subscription_search -> {
urlBuilder.mode = ListUrlBuilder.MODE_SUBSCRIPTION
}
@@ -262,17 +194,18 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
urlBuilder.keyword = query
urlBuilder.category = PreferenceManager.getDefaultSharedPreferences(context).getInt(
SEARCH_CATEGORY_PREF, 0)
SEARCH_CATEGORY_PREF, 0
)
if (mEnableAdvance) {
urlBuilder.advanceSearch = mTableAdvanceSearch!!.advanceSearch
urlBuilder.minRating = mTableAdvanceSearch!!.minRating
urlBuilder.pageFrom = mTableAdvanceSearch!!.pageFrom
urlBuilder.pageTo = mTableAdvanceSearch!!.pageTo
urlBuilder.advanceSearch = mTableAdvanceSearch.advanceSearch
urlBuilder.minRating = mTableAdvanceSearch.minRating
urlBuilder.pageFrom = mTableAdvanceSearch.pageFrom
urlBuilder.pageTo = mTableAdvanceSearch.pageTo
}
}
SEARCH_MODE_IMAGE -> {
urlBuilder.mode = ListUrlBuilder.MODE_IMAGE_SEARCH
mImageView!!.formatListUrlBuilder(urlBuilder)
mImageView.formatListUrlBuilder(urlBuilder)
}
}
}
@@ -290,11 +223,11 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
}
fun setSearchMode(@SearchMode mode: Int) {
val oldItemCount = mAdapter!!.itemCount
val oldItemCount = mAdapter.itemCount
mSearchMode = mode
val newItemCount = mAdapter!!.itemCount
mAdapter!!.notifyItemRangeRemoved(0, oldItemCount - 1)
mAdapter!!.notifyItemRangeInserted(0, newItemCount - 1)
val newItemCount = mAdapter.itemCount
mAdapter.notifyItemRangeRemoved(0, oldItemCount - 1)
mAdapter.notifyItemRangeInserted(0, newItemCount - 1)
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
}
@@ -370,7 +303,7 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
override fun onBindViewHolder(holder: SimpleHolder, position: Int) {
if (holder.itemViewType == ITEM_TYPE_ACTION) {
mAction!!.selectTab(mAction!!.getTabAt(mSearchMode))
mAction.selectTab(mAction.getTabAt(mSearchMode))
}
}
@@ -386,15 +319,12 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
inner class IdentifiedChip @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : Chip(context, attrs) {
var idt = 0
var idt = 0
}
companion object {
const val SEARCH_MODE_NORMAL = 0
const val SEARCH_MODE_IMAGE = 1
private const val STATE_KEY_SUPER = "super"
private const val STATE_KEY_SEARCH_MODE = "search_mode"
private const val STATE_KEY_ENABLE_ADVANCE = "enable_advance"
const val SEARCH_CATEGORY_PREF = "search_pref"
private const val ITEM_TYPE_NORMAL = 0
private const val ITEM_TYPE_NORMAL_ADVANCE = 1
@@ -403,6 +333,18 @@ class SearchLayout : EasyRecyclerView, CompoundButton.OnCheckedChangeListener, V
private val SEARCH_ITEM_COUNT_ARRAY = intArrayOf(
3, 2
)
private val mCategoryTable: ArrayList<Pair<Int, Int>> = arrayListOf(
Pair(EhConfig.DOUJINSHI, R.string.doujinshi),
Pair(EhConfig.MANGA, R.string.manga),
Pair(EhConfig.ARTIST_CG, R.string.artist_cg),
Pair(EhConfig.GAME_CG, R.string.game_cg),
Pair(EhConfig.WESTERN, R.string.western),
Pair(EhConfig.NON_H, R.string.non_h),
Pair(EhConfig.IMAGE_SET, R.string.image_set),
Pair(EhConfig.COSPLAY, R.string.cosplay),
Pair(EhConfig.ASIAN_PORN, R.string.asian_porn),
Pair(EhConfig.MISC, R.string.misc)
)
private val SEARCH_ITEM_TYPE = arrayOf(
intArrayOf(ITEM_TYPE_NORMAL, ITEM_TYPE_NORMAL_ADVANCE, ITEM_TYPE_ACTION), intArrayOf(
ITEM_TYPE_IMAGE, ITEM_TYPE_ACTION
+2 -5
View File
@@ -14,12 +14,10 @@
~ limitations under the License.
-->
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/Widget.Material3.CardView.Filled"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:cardBackgroundColor="?android:attr/windowBackground">
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
@@ -32,7 +30,6 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp"
android:saveEnabled="false" />
<FrameLayout
@@ -70,9 +70,9 @@
android:maxLines="5"
android:textSize="@dimen/text_little_large" />
<com.google.android.material.chip.Chip
<Button
android:id="@+id/uploader"
style="@style/Widget.Material3.Chip.Assist"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
@@ -81,17 +81,16 @@
android:singleLine="true"
android:textAllCaps="true" />
<com.google.android.material.chip.Chip
<Button
android:id="@+id/category"
style="@style/Widget.Material3.Chip.Assist"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/uploader"
android:layout_alignStart="@id/title"
android:layout_below="@id/title"
android:layout_toRightOf="@id/uploader"
android:ellipsize="end"
android:singleLine="true"
android:textAllCaps="true"
android:textStyle="bold" />
android:textAllCaps="true" />
<ImageView
android:id="@+id/other_actions"
@@ -111,39 +110,30 @@
</RelativeLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/action_card"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
app:cardUseCompatPadding="true">
<LinearLayout
android:id="@+id/action_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
android:orientation="horizontal">
<TextView
<Button
android:id="@+id/download"
style="@style/ButtonInCard"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?attr/dividerColor" />
<android.widget.Space
android:layout_width="8dp"
android:layout_height="wrap_content" />
<TextView
<Button
android:id="@+id/read"
style="@style/ButtonInCard"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -152,6 +142,5 @@
android:text="@string/read" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</FrameLayout>
+1 -1
View File
@@ -89,7 +89,7 @@
android:layout_height="wrap_content"
android:layout_weight="1" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/search_enable_advance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+3 -4
View File
@@ -14,20 +14,19 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/settings"
app:showAsAction="always" />
android:showAsAction="always" />
<item
android:id="@+id/action_default_download_label"
android:icon="@drawable/v_download_box_dark_x24"
android:title="@string/default_download_label"
app:showAsAction="ifRoom" />
android:showAsAction="ifRoom" />
</menu>
@@ -14,13 +14,18 @@
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_help"
android:icon="@drawable/v_help_circle_x24"
android:title="@string/add"
android:showAsAction="ifRoom" />
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/add"
app:showAsAction="always" />
android:showAsAction="always" />
</menu>
+4 -4
View File
@@ -17,14 +17,14 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="save_parse_error_body"
android:summary="@string/settings_advanced_save_parse_error_body_summary"
android:title="@string/settings_advanced_save_parse_error_body"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="save_crash_log"
android:summary="@string/settings_advanced_save_crash_log_summary"
@@ -72,7 +72,7 @@
android:title="@string/settings_advanced_proxy"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="built_in_hosts_2"
android:summary="@string/settings_advanced_built_in_hosts_summary"
@@ -86,7 +86,7 @@
android:title="@string/settings_advanced_custom_hosts_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="domain_fronting"
android:summary="@string/settings_advanced_domain_fronting_summary"
+2 -2
View File
@@ -22,7 +22,7 @@
android:title="@string/settings_download_download_location"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="media_scan"
android:title="@string/settings_download_media_scan"
@@ -66,7 +66,7 @@
app:entryValues="@array/image_resolution_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="download_origin_image"
android:summary="@string/settings_download_download_origin_image_summary"
+7 -7
View File
@@ -61,7 +61,7 @@
android:title="@string/dark_theme"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:key="black_dark_theme"
android:title="@string/black_dark_theme"
app:iconSpaceReserved="false" />
@@ -111,27 +111,27 @@
app:entryValues="@array/thumb_resolution_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="fix_thumb_url"
android:title="@string/settings_eh_fix_thumb_url"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_jpn_title"
android:summary="@string/settings_eh_show_jpn_title_summary"
android:title="@string/settings_eh_show_jpn_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_gallery_pages"
android:summary="@string/settings_eh_show_gallery_pages_summary"
android:title="@string/settings_eh_show_gallery_pages"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="show_tag_translations"
android:summary="@string/settings_eh_show_tag_translations_summary"
@@ -151,13 +151,13 @@
android:title="@string/settings_eh_filter"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="cellular_network_warning"
android:title="@string/settings_eh_metered_network_warning"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="request_news"
android:title="@string/settings_request_news_title"
+1 -1
View File
@@ -8,7 +8,7 @@
android:title="@string/settings_privacy_pattern_protection_title"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="enable_secure"
android:title="@string/settings_privacy_secure"
+9 -9
View File
@@ -62,56 +62,56 @@
app:entryValues="@array/read_theme_entry_values"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="keep_screen_on"
android:title="@string/settings_read_keep_screen_on"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_clock"
android:title="@string/settings_read_show_clock"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_progress"
android:title="@string/settings_read_show_progress"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_battery"
android:title="@string/settings_read_show_battery"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="gallery_show_page_interval"
android:title="@string/settings_read_show_page_interval"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="volume_page"
android:title="@string/settings_read_volume_page"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="volume_page"
android:key="reserve_volume_page"
android:title="@string/settings_read_reverse_volume"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="reading_fullscreen"
android:title="@string/settings_read_reading_fullscreen"
app:iconSpaceReserved="false" />
<SwitchPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="custom_screen_lightness"
android:title="@string/settings_read_custom_screen_lightness"
+6
View File
@@ -1,3 +1,9 @@
buildscript {
ext {
compose = '1.2.0-beta02'
}
}
plugins {
id 'com.android.application' version '7.2.0' apply false
id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
+2 -2
View File
@@ -24,6 +24,6 @@ task executeDaoGenerator(type:JavaExec) {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.greenrobot:greendao-generator:3.3.0'
implementation 'org.jboss.forge.roaster:roaster-api:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-api:2.25.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.25.0.Final'
}
+7
View File
@@ -21,3 +21,10 @@ android.useAndroidX=true
android.enableJetifier=true
android.enableAppCompileTimeRClass=true
android.nonTransitiveRClass=true
# signing environment variable
signStoreFile=keystore/androidkey.jks
signStorePassword=000000
signkeyAlias=key0
signkeyPassword=000000
+1 -6
View File
@@ -12,12 +12,6 @@ android {
targetSdkVersion 32
consumerProguardFiles 'proguard-rules.pro'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
}
}
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
@@ -25,6 +19,7 @@ android {
externalNativeBuild {
cmake {
version "3.23.1"
path 'src/main/jni/CMakeLists.txt'
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
-keep class com.hippo.a7zip.* { *; }
-keep class com.hippo.* { *; }
-keep class com.hippo.image.* { *; }
@@ -0,0 +1,84 @@
package com.hippo;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
public class UriArchiveAccessor implements Closeable {
public static int OPEN_OK = 1;
public static int OPEN_ERR = 2;
private final OsReadableFile osf;
public UriArchiveAccessor(Context ctx, Uri uri) throws FileNotFoundException {
osf = new OsReadableFile(ctx, uri);
}
public int open() {
return openArchive(osf);
}
public void extractTargetIndexToOutputStream(int index, OutputStream os) {
extracttoOutputStream(osf, index, os);
}
private native int openArchive(OsReadableFile osf);
private native int extracttoOutputStream(OsReadableFile osf, int index, OutputStream os);
@Override
public void close() throws IOException {
osf.close();
}
static class OsReadableFile {
ParcelFileDescriptor pfd;
FileDescriptor fd;
public OsReadableFile(Context ctx, Uri uri) throws FileNotFoundException {
pfd = ctx.getContentResolver().openFileDescriptor(uri, "r");
fd = pfd.getFileDescriptor();
}
public int read(byte[] b, int off, int len) throws IOException, ErrnoException {
return Os.read(fd, b, off, len);
}
public int read(byte[] b) throws IOException, ErrnoException {
return read(b, 0, b.length);
}
public long length() throws IOException, ErrnoException {
return Os.fstat(fd).st_size;
}
public long seek(long pos, int whence) throws IOException, ErrnoException {
try {
return Os.lseek(fd, pos, whence);
} catch (Exception e) {
e.printStackTrace();
return -30;
}
}
public void rewind() throws IOException, ErrnoException {
Os.lseek(fd, 0, OsConstants.SEEK_SET);
}
public void close() throws IOException {
pfd.close();
try {
Os.close(fd);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
}
}
}
@@ -1,28 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
public class ArchiveException extends Exception {
public ArchiveException(String message) {
super(message);
}
public ArchiveException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -1,76 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import androidx.annotation.NonNull;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class FileSeekableInputStream extends SeekableInputStream {
private final RandomAccessFile file;
public FileSeekableInputStream(String path) throws FileNotFoundException {
this(new File(path));
}
public FileSeekableInputStream(File file) throws FileNotFoundException {
this(new RandomAccessFile(file, "r"));
}
public FileSeekableInputStream(RandomAccessFile file) {
this.file = file;
}
@Override
public void seek(long pos) throws IOException {
file.seek(pos);
}
@Override
public long tell() throws IOException {
return file.getFilePointer();
}
@Override
public long size() throws IOException {
return file.length();
}
@Override
public int read() throws IOException {
return file.read();
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return file.read(b);
}
@Override
public int read(@NonNull byte[] b, int off, int len) throws IOException {
return file.read(b, off, len);
}
@Override
public void close() throws IOException {
file.close();
}
}
@@ -1,81 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import androidx.annotation.NonNull;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class FileSeekableOutputStream extends SeekableOutputStream {
private final RandomAccessFile file;
public FileSeekableOutputStream(String path) throws FileNotFoundException {
this(new File(path));
}
public FileSeekableOutputStream(File file) throws FileNotFoundException {
this(new RandomAccessFile(file, "rw"));
}
public FileSeekableOutputStream(RandomAccessFile file) {
this.file = file;
}
@Override
public void seek(long pos) throws IOException {
file.seek(pos);
}
@Override
public long tell() throws IOException {
return file.getFilePointer();
}
@Override
public long size() throws IOException {
return file.length();
}
@Override
public void truncate(long size) throws IOException {
file.setLength(size);
}
@Override
public void write(int b) throws IOException {
file.write(b);
}
@Override
public void write(@NonNull byte[] b) throws IOException {
file.write(b);
}
@Override
public void write(@NonNull byte[] b, int off, int len) throws IOException {
file.write(b, off, len);
}
@Override
public void close() throws IOException {
file.close();
}
}
@@ -1,456 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
public class InArchive implements Closeable {
private long nativePtr;
@Nullable
private final Charset charset;
@Nullable
private final String password;
private InArchive(long nativePtr, @Nullable Charset charset, @Nullable String password) {
this.nativePtr = nativePtr;
this.charset = charset;
this.password = password;
}
private void checkClosed() {
if (nativePtr == 0) {
throw new IllegalStateException("This InArchive is closed.");
}
}
// Sometimes p7zip returns string in the original charset, sometimes in utf-16.
// If it's in the original charset, each byte stores in each char,
// so every char in the string is smaller than 0xFF.
// But if every char in the string is smaller than 0xFF,
// it's hard to tell the string is in utf-16 or the original charset.
// TODO Let p7zip tell whether it have encoded the string.
private static String applyCharsetToString(String str, Charset charset) {
if (str == null || charset == null) {
return str;
}
int length = str.length();
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
if (c > 0xFF) {
// It's not a pure byte list, can't apply charset
return str;
}
}
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
bytes[i] = (byte) str.charAt(i);
}
return new String(bytes, charset);
}
private static String applyCharsetToPassword(String str, Charset charset) {
if (str == null || charset == null) {
return str;
}
byte[] bytes = str.getBytes(charset);
char[] chars = new char[bytes.length];
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) (bytes[i] & 0xFF);
}
return new String(chars);
}
/**
* Returns the format name of this archive.
* Empty string if can't get the name.
*/
@NonNull
public String getFormatName() {
checkClosed();
return nativeGetFormatName(nativePtr);
}
/**
* Returns the number of entries in this archive.
* {@code -1} if get error.
*/
public int getNumberOfEntries() {
checkClosed();
return nativeGetNumberOfEntries(nativePtr);
}
/**
* Returns the type of the property for the archive.
*
* @param propID the id of the property
* @return one of {@link PropType}
*/
public PropType getArchivePropertyType(PropID propID) {
int type = nativeGetArchivePropertyType(nativePtr, propID.ordinal());
if (type >= 0 && type < PropType.values().length) {
return PropType.values()[type];
} else {
return PropType.UNKNOWN;
}
}
/**
* Returns boolean property for the archive.
*
* @param propID the id of the property
* @return the boolean property, {@code false} if get error
*/
public boolean getArchiveBooleanProperty(PropID propID) {
checkClosed();
return nativeGetArchiveBooleanProperty(nativePtr, propID.ordinal());
}
/**
* Returns int property for the archive.
*
* @param propID the id of the property
* @return the int property, {@code 0} if get error
*/
public int getArchiveIntProperty(PropID propID) {
checkClosed();
return nativeGetArchiveIntProperty(nativePtr, propID.ordinal());
}
/**
* Returns long property for the archive.
*
* @param propID the id of the property
* @return the long property, {@code 0} if get error
*/
public long getArchiveLongProperty(PropID propID) {
checkClosed();
return nativeGetArchiveLongProperty(nativePtr, propID.ordinal());
}
/**
* Returns string property for the archive.
*
* @param propID the id of the property
* @return the string property, empty string if get error
*/
@NonNull
public String getArchiveStringProperty(PropID propID) {
return getArchiveStringProperty(propID, charset);
}
/**
* Returns string property for the archive.
*
* @param propID the id of the property
* @param charset the charset of the string, {@code null} to let p7zip handle it
* @return the string property, empty string if get error
*/
@NonNull
public String getArchiveStringProperty(PropID propID, @Nullable Charset charset) {
checkClosed();
String str = nativeGetArchiveStringProperty(nativePtr, propID.ordinal());
str = applyCharsetToString(str, charset);
return str != null ? str : "";
}
/**
* Returns the type of the property for the archive.
*
* @param propID the id of the property
* @return one of {@link PropType}
*/
public PropType getEntryPropertyType(int index, PropID propID) {
int type = nativeGetEntryPropertyType(nativePtr, index, propID.ordinal());
if (type >= 0 && type < PropType.values().length) {
return PropType.values()[type];
} else {
return PropType.UNKNOWN;
}
}
/**
* Returns boolean property for the entry.
*
* @param index the index of the entry
* @param propID the id of the property
* @return the boolean property, {@code false} if get error
*/
public boolean getEntryBooleanProperty(int index, PropID propID) {
checkClosed();
return nativeGetEntryBooleanProperty(nativePtr, index, propID.ordinal());
}
/**
* Returns int property for the entry.
*
* @param index the index of the entry
* @param propID the id of the property
* @return the int property, {@code 0} if get error
*/
public int getEntryIntProperty(int index, PropID propID) {
checkClosed();
return nativeGetEntryIntProperty(nativePtr, index, propID.ordinal());
}
/**
* Returns long property for the entry.
*
* @param index the index of the entry
* @param propID the id of the property
* @return the long property, {@code 0} if get error
*/
public long getEntryLongProperty(int index, PropID propID) {
checkClosed();
return nativeGetEntryLongProperty(nativePtr, index, propID.ordinal());
}
/**
* Returns string property for the entry.
*
* @param index the index of the entry
* @param propID the id of the property
* @return the string property, empty string if get error
*/
@NonNull
public String getEntryStringProperty(int index, PropID propID) {
return getEntryStringProperty(index, propID, charset);
}
/**
* Returns string property for the entry.
*
* @param index the index of the entry
* @param propID the id of the property
* @param charset the charset of the string, {@code null} to let p7zip handle it
* @return the string property, empty string if get error
*/
@NonNull
public String getEntryStringProperty(int index, PropID propID, @Nullable Charset charset) {
checkClosed();
String str = nativeGetEntryStringProperty(nativePtr, index, propID.ordinal());
str = applyCharsetToString(str, charset);
return str != null ? str : "";
}
/**
* Returns the path of the entry.
*
* @param index the index of the entry
* @return the path, empty string if get error
*/
@NonNull
public String getEntryPath(int index) {
return getEntryPath(index, charset);
}
/**
* Returns the path of the entry.
*
* @param index the index of the entry
* @param charset the charset of the string, {@code null} to let p7zip handle it
* @return the path, empty string if get error
*/
@NonNull
public String getEntryPath(int index, @Nullable Charset charset) {
return getEntryStringProperty(index, PropID.PATH, charset);
}
/**
* Returns the stream of the entry. Only a few archive formats support it.
*
* @param index the index of the entry
* @return the stream of the entry
* @throws ArchiveException if the archive format doesn't support the operation or get error
* @see #extractEntry(int, OutputStream)
* @see #extractEntry(int, String, OutputStream)
*/
@NonNull
public InputStream getEntryStream(int index) throws ArchiveException {
return nativeGetEntryStream(nativePtr, index);
}
/**
* Extracts the context of the entry into the output stream.
*
* @param index the index of the entry
* @param os the output steam to receive the content,
* it will be closed at the end of this method
* @throws ArchiveException if get error
* @see #getEntryStream(int)
*/
public void extractEntry(int index, @NonNull OutputStream os) throws ArchiveException {
extractEntry(index, password, os);
}
/**
* Extracts the context of the entry into the output stream.
*
* @param index the index of the entry
* @param password the password of the entry
* @param os the output steam to receive the content,
* it will be closed at the end of this method
* @throws ArchiveException if get error
*/
@SuppressWarnings("ThrowFromFinallyBlock")
public void extractEntry(int index, String password, @NonNull OutputStream os) throws ArchiveException {
try {
checkClosed();
nativeExtractEntry(nativePtr, index, password, os);
} finally {
try {
os.close();
} catch (IOException e) {
throw new ArchiveException("Catch IOException while closing the OutputStream", e);
}
}
}
@Override
public void close() {
if (nativePtr != 0) {
nativeClose(nativePtr);
nativePtr = 0;
}
}
/**
* Returns {@code true} if the archive is closed.
*/
public boolean isClosed() {
return nativePtr == 0;
}
@NonNull
public static InArchive open(File file) throws ArchiveException {
try {
return open(new FileSeekableInputStream(file), null, null, file.getName(), new OpenVolumeInDirCallback(file.getParentFile()));
} catch (FileNotFoundException e) {
throw new ArchiveException("Can't open the archive: " + file.getPath(), e);
}
}
@NonNull
public static InArchive open(SeekableInputStream stream) throws ArchiveException {
return open(stream, null, null, null, null);
}
/**
* Opens an archive to read from the specified stream.
* <p>
* {@code charset} is for password and string property.
* The charset of string property can reset in
* {@link InArchive#getArchiveStringProperty(PropID, Charset)}.
*/
@NonNull
public static InArchive open(
SeekableInputStream stream,
@Nullable Charset charset,
@Nullable String password,
@Nullable String filename,
@Nullable OpenVolumeCallback openVolumeCallback
) throws ArchiveException {
password = applyCharsetToPassword(password, charset);
long nativePtr = nativeOpen(stream, password, filename, openVolumeCallback);
if (nativePtr == 0) {
// It should not be 0
throw new ArchiveException("a7zip is buggy");
}
return new InArchive(nativePtr, charset, password);
}
@Keep
public interface OpenVolumeCallback {
@NonNull
SeekableInputStream openVolume(String filename) throws ArchiveException;
}
public static class OpenVolumeInDirCallback implements OpenVolumeCallback {
private final File dir;
public OpenVolumeInDirCallback(File dir) {
this.dir = dir;
}
@NonNull
@Override
public SeekableInputStream openVolume(String filename) throws ArchiveException {
try {
return new FileSeekableInputStream(new File(dir, filename));
} catch (FileNotFoundException e) {
throw new ArchiveException("Can't find the file<" + filename + "> in dir<" + dir.getPath() + ">", e);
}
}
}
private static native long nativeOpen(
SeekableInputStream stream,
String password,
String filename,
OpenVolumeCallback openVolumeCallback
) throws ArchiveException;
private static native String nativeGetFormatName(long nativePtr);
private static native int nativeGetNumberOfEntries(long nativePtr);
private static native int nativeGetArchivePropertyType(long nativePtr, int propID);
private static native boolean nativeGetArchiveBooleanProperty(long nativePtr, int propID);
private static native int nativeGetArchiveIntProperty(long nativePtr, int propID);
private static native long nativeGetArchiveLongProperty(long nativePtr, int propID);
@Nullable
private static native String nativeGetArchiveStringProperty(long nativePtr, int propID);
private static native int nativeGetEntryPropertyType(long nativePtr, int index, int propID);
private static native boolean nativeGetEntryBooleanProperty(long nativePtr, int index, int propID);
private static native int nativeGetEntryIntProperty(long nativePtr, int index, int propID);
private static native long nativeGetEntryLongProperty(long nativePtr, int index, int propID);
@Nullable
private static native String nativeGetEntryStringProperty(long nativePtr, int index, int propID);
@NonNull
private static native InputStream nativeGetEntryStream(long nativePtr, int index) throws ArchiveException;
private static native void nativeExtractEntry(long nativePtr, int index, String password, OutputStream os) throws ArchiveException;
private static native void nativeClose(long nativePtr);
}
@@ -1,66 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.io.InputStream;
class NativeInputStream extends InputStream {
private long nativePtr;
private final byte[] scratch = new byte[8];
private NativeInputStream(long nativePtr) {
this.nativePtr = nativePtr;
}
private void checkClosed() {
if (nativePtr == 0) {
throw new IllegalStateException("This JavaInputStream is closed.");
}
}
@Override
public int read() throws IOException {
return (read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
return nativeRead(nativePtr, b, off, len);
}
@Override
public void close() throws IOException {
if (nativePtr != 0) {
nativeClose(nativePtr);
nativePtr = 0;
}
}
private static native int nativeRead(long nativePtr, byte[] b, int off, int len) throws IOException;
private static native void nativeClose(long nativePtr) throws IOException;
}
@@ -1,89 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import androidx.annotation.NonNull;
import java.io.IOException;
class NativeSeekableInputStream extends SeekableInputStream {
private long nativePtr;
private final byte[] scratch = new byte[8];
private NativeSeekableInputStream(long nativePtr) {
this.nativePtr = nativePtr;
}
private void checkClosed() {
if (nativePtr == 0) {
throw new IllegalStateException("This JavaSeekableInputStream is closed.");
}
}
@Override
public void seek(long pos) throws IOException {
checkClosed();
nativeSeek(nativePtr, pos);
}
@Override
public long tell() throws IOException {
checkClosed();
return nativeTell(nativePtr);
}
@Override
public long size() throws IOException {
checkClosed();
return nativeSize(nativePtr);
}
@Override
public int read() throws IOException {
return (read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
return nativeRead(nativePtr, b, off, len);
}
@Override
public void close() throws IOException {
if (nativePtr != 0) {
nativeClose(nativePtr);
nativePtr = 0;
}
}
private static native void nativeSeek(long nativePtr, long pos) throws IOException;
private static native long nativeTell(long nativePtr) throws IOException;
private static native long nativeSize(long nativePtr) throws IOException;
private static native int nativeRead(long nativePtr, byte[] b, int off, int len) throws IOException;
private static native void nativeClose(long nativePtr) throws IOException;
}
@@ -1,31 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import androidx.annotation.Keep;
@Keep
public class PasswordException extends ArchiveException {
public PasswordException(String message) {
super(message);
}
public PasswordException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -1,116 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
public enum PropID {
NO_PROPERTY,
MAIN_SUBFILE,
HANDLER_ITEM_INDEX,
PATH,
NAME,
EXTENSION,
IS_DIR,
SIZE,
PACK_SIZE,
ATTRIB,
C_TIME,
A_TIME,
M_TIME,
SOLID,
COMMENTED,
ENCRYPTED,
SPLIT_BEFORE,
SPLIT_AFTER,
DICTIONARY_SIZE,
CRC,
TYPE,
IS_ANTI,
METHOD,
HOST_OS,
FILE_SYSTEM,
USER,
GROUP,
BLOCK,
COMMENT,
POSITION,
PREFIX,
NUM_SUB_DIRS,
NUM_SUB_FILES,
UNPACK_VER,
VOLUME,
IS_VOLUME,
OFFSET,
LINKS,
NUM_BLOCKS,
NUM_VOLUMES,
TIME_TYPE,
BIT64,
BIG_ENDIAN,
CPU,
PHY_SIZE,
HEADERS_SIZE,
CHECKSUM,
CHARACTS,
VA,
ID,
SHORT_NAME,
CREATOR_APP,
SECTOR_SIZE,
POSIX_ATTRIB,
SYM_LINK,
ERROR,
TOTAL_SIZE,
FREE_SPACE,
CLUSTER_SIZE,
VOLUME_NAME,
LOCAL_NAME,
PROVIDER,
NT_SECURE,
IS_ALT_STREAM,
IS_AUX,
IS_DELETED,
IS_TREE,
SHA1,
SHA256,
ERROR_TYPE,
NUM_ERRORS,
ERROR_FLAGS,
WARNING_FLAGS,
WARNING,
NUM_STREAMS,
NUM_ALT_STREAMS,
ALT_STREAMS_SIZE,
VIRTUAL_SIZE,
UNPACK_SIZE,
TOTAL_PHY_SIZE,
VOLUME_INDEX,
SUB_TYPE,
SHORT_COMMENT,
CODE_PAGE,
IS_NOT_ARC_TYPE,
PHY_SIZE_CANT_BE_DETECTED,
ZEROS_TAIL_IS_ALLOWED,
TAIL_SIZE,
EMBEDDED_STUB_SIZE,
NT_REPARSE,
HARD_LINK,
INODE,
STREAM_ID,
READ_ONLY,
OUT_NAME,
COPY_LINK,
}
@@ -1,28 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
public enum PropType {
UNKNOWN,
EMPTY,
BOOL,
INT,
LONG,
FLOAT,
DOUBLE,
STRING,
}
@@ -1,40 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import java.io.IOException;
import java.io.InputStream;
public abstract class SeekableInputStream extends InputStream {
/**
* Sets the position, measured from the beginning,
* at which the next read occurs. The offset may be
* set beyond the end of the file.
*/
public abstract void seek(long pos) throws IOException;
/**
* Returns current position, measured from the beginning.
*/
public abstract long tell() throws IOException;
/**
* Returns the size.
*/
public abstract long size() throws IOException;
}
@@ -1,45 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.a7zip;
import java.io.IOException;
import java.io.OutputStream;
public abstract class SeekableOutputStream extends OutputStream {
/**
* Sets the position, measured from the beginning,
* at which the next write occurs. The offset may be
* set beyond the end of the file.
*/
public abstract void seek(long pos) throws IOException;
/**
* Returns current position, measured from the beginning.
*/
public abstract long tell() throws IOException;
/**
* Returns the size.
*/
public abstract long size() throws IOException;
/**
* Sets the size.
*/
public abstract void truncate(long size) throws IOException;
}
+13 -12
View File
@@ -1,17 +1,18 @@
project(ehviewer)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Oz -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Oz -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti -fno-stack-protector")
add_subdirectory(a7zip)
set(BUILD_SHARED_LIBS ON)
add_subdirectory(liblzma)
include_directories(liblzma/src/liblzma/api)
add_subdirectory(image)
add_subdirectory(integrity)
include_directories(libarchive/contrib/android/include)
add_library(${PROJECT_NAME} SHARED main.c)
SET(HAVE_LIBLZMA 1)
SET(HAVE_LZMA_H 1)
SET(HAVE_LZMA_STREAM_ENCODER_MT 1)
SET(HAVE_LZMADEC_H 1)
SET(HAVE_LIBLZMADEC 1)
target_compile_options(${PROJECT_NAME} PUBLIC -Oz -fvisibility=hidden -fvisibility-inlines-hidden -fno-exceptions)
target_include_directories(${PROJECT_NAME} PUBLIC a7zip/a7zip image/image integrity)
target_link_libraries(${PROJECT_NAME} a7zip image genuine)
add_subdirectory(libarchive)
add_library(${PROJECT_NAME} SHARED main.c archive.c)
target_include_directories(${PROJECT_NAME} PUBLIC image/image libarchive/libarchive)
target_link_libraries(${PROJECT_NAME} image archive)
-24
View File
@@ -1,24 +0,0 @@
cmake_minimum_required(VERSION 3.4.1)
project(a7zip C CXX)
add_subdirectory(p7zip)
set(A_SEVEN_ZIP_SOURCES
a7zip/BlackHole.cpp
a7zip/InArchive.cpp
a7zip/JavaEnv.cpp
a7zip/JavaHelper.cpp
a7zip/JavaInArchive.cpp
a7zip/JavaInitA7Zip.cpp
a7zip/JavaInputStream.cpp
a7zip/JavaSeekableInputStream.cpp
a7zip/OpenVolumeCallback.cpp
a7zip/OutputStream.cpp
a7zip/SeekableInputStream.cpp
a7zip/SevenZip.cpp
)
add_library(a7zip STATIC ${A_SEVEN_ZIP_SOURCES})
target_link_libraries(a7zip PUBLIC p7zip log)
@@ -1,69 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BlackHole.h"
using namespace a7zip;
BlackHole::BlackHole(): pos(0), size(0) { }
HRESULT BlackHole::Write(const void*, UInt32 size, UInt32* processedSize) {
if (processedSize != nullptr) {
*processedSize = size;
}
this->pos += size;
if (this->size < this->pos) {
this->size = this->pos;
}
return S_OK;
}
HRESULT BlackHole::Seek(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) {
Int64 newPos = -1;
switch (seekOrigin) {
case STREAM_SEEK_SET:
newPos = offset;
break;
case STREAM_SEEK_CUR:
newPos = this->pos + offset;
break;
case STREAM_SEEK_END:
newPos = this->size + offset;
break;
default:
return E_INVALIDARG;
}
if (newPos < 0) {
return STG_E_INVALIDFUNCTION;
}
*newPosition = this->pos;
return S_OK;
}
HRESULT BlackHole::SetSize(UInt64 newSize) {
this->size = newSize;
if (this->pos > this->size) {
this->pos = this->size;
}
return S_OK;
}
@@ -1,47 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_BLACK_HOLE_H__
#define __A7ZIP_BLACK_HOLE_H__
#include <Common/MyCom.h>
#include <7zip/IStream.h>
#include "Log.h"
namespace a7zip {
class BlackHole :
public IOutStream,
public CMyUnknownImp
{
public:
BlackHole();
public:
MY_UNKNOWN_IMP2(ISequentialOutStream, IOutStream)
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
STDMETHOD(SetSize)(UInt64 newSize);
private:
UInt64 pos;
UInt64 size;
};
}
#endif //__A7ZIP_BLACK_HOLE_H__
@@ -1,360 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "InArchive.h"
#include <Windows/PropVariant.h>
#include <7zip/ICoder.h>
#include <7zip/IPassword.h>
#include "BlackHole.h"
#include "Log.h"
#include "Utils.h"
using namespace a7zip;
class ArchiveExtractCallback :
public IArchiveExtractCallback,
public ICryptoGetTextPassword,
public CMyUnknownImp
{
public:
ArchiveExtractCallback(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream);
~ArchiveExtractCallback();
public:
MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICryptoGetTextPassword)
STDMETHOD(SetTotal)(UInt64 total);
STDMETHOD(SetCompleted)(const UInt64 *completeValue);
STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode);
STDMETHOD(PrepareOperation)(Int32 askExtractMode);
STDMETHOD(SetOperationResult)(Int32 opRes);
STDMETHOD(CryptoGetTextPassword)(BSTR *password);
HRESULT GetBetterResult(HRESULT result);
private:
UInt32 index;
BSTR password;
CMyComPtr<ISequentialOutStream> out_stream;
bool has_asked_password;
};
ArchiveExtractCallback::ArchiveExtractCallback(
UInt32 index,
BSTR password,
CMyComPtr<ISequentialOutStream>& out_stream
) :
index(index),
password(::SysAllocString(password)),
out_stream(out_stream),
has_asked_password(false) {}
ArchiveExtractCallback::~ArchiveExtractCallback() {
::SysFreeString(password);
}
HRESULT ArchiveExtractCallback::SetTotal(UInt64 total) {
// Ignored
return S_OK;
}
HRESULT ArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) {
// Ignored
return S_OK;
}
HRESULT ArchiveExtractCallback::GetStream(
UInt32 index,
ISequentialOutStream** outStream,
Int32 askExtractMode
) {
// If it's not extract mode or the index is different, return a black hole to skip data
if (askExtractMode != NArchive::NExtract::NAskMode::kExtract || this->index != index) {
CMyComPtr<ISequentialOutStream> black_hole(new BlackHole());
*outStream = black_hole.Detach();
return S_OK;
}
if (out_stream == nullptr) {
return E_NO_OUT_STREAM;
}
CMyComPtr<ISequentialOutStream> steam_copy(out_stream);
*outStream = steam_copy.Detach();
return S_OK;
}
HRESULT ArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) {
// Always return S_OK
return S_OK;
}
HRESULT ArchiveExtractCallback::SetOperationResult(Int32 opRes) {
// Stop extracting action if operation result is not OK
switch (opRes) {
case NArchive::NExtract::NOperationResult::kOK:
return S_OK;
case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
return E_UNSUPPORTED_METHOD;
case NArchive::NExtract::NOperationResult::kDataError:
return E_DATA_ERROR;
case NArchive::NExtract::NOperationResult::kCRCError:
return E_CRC_ERROR;
case NArchive::NExtract::NOperationResult::kUnavailable:
return E_UNAVAILABLE;
case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
return E_UNEXPECTED_END;
case NArchive::NExtract::NOperationResult::kDataAfterEnd:
return E_DATA_AFTER_END;
case NArchive::NExtract::NOperationResult::kIsNotArc:
return E_IS_NOT_ARC;
case NArchive::NExtract::NOperationResult::kHeadersError:
return E_HEADERS_ERROR;
case NArchive::NExtract::NOperationResult::kWrongPassword:
return E_WRONG_PASSWORD;
default:
return E_UNKNOWN_ERROR;
}
}
HRESULT ArchiveExtractCallback::CryptoGetTextPassword(BSTR* password) {
has_asked_password = true;
*password = ::SysAllocString(this->password);
return this->password != nullptr ? S_OK : E_NO_PASSWORD;
}
HRESULT ArchiveExtractCallback::GetBetterResult(HRESULT result) {
if (result == S_OK) {
return S_OK;
} else if (has_asked_password) {
if (password != nullptr) {
return E_WRONG_PASSWORD;
} else {
return E_NO_PASSWORD;
}
} else {
return result;
}
}
InArchive::InArchive(
InArchive* parent,
CMyComPtr<IInArchive>& in_archive,
AString& format_name
) :
parent(parent),
in_archive(in_archive),
format_name(format_name) { }
InArchive::~InArchive() {
this->in_archive->Close();
if (parent != nullptr) {
delete parent;
parent = nullptr;
}
}
const AString& InArchive::GetFormatName() {
return this->format_name;
}
HRESULT InArchive::GetNumberOfEntries(UInt32& number) {
return this->in_archive->GetNumberOfItems(&number);
}
static PropType VarTypeToPropType(VARTYPE var_enum) {
// TODO VT_ERROR
switch (var_enum) {
case VT_EMPTY:
return PT_EMPTY;
case VT_BOOL:
return PT_BOOL;
case VT_I1:
case VT_I2:
case VT_I4:
case VT_INT:
case VT_UI1:
case VT_UI2:
case VT_UI4:
case VT_UINT:
return PT_INT;
case VT_I8:
case VT_UI8:
case VT_FILETIME:
return PT_LONG;
case VT_BSTR:
return PT_STRING;
default:
return PT_UNKNOWN;
}
}
#define GET_ARCHIVE_PROPERTY_START(METHOD_NAME, VALUE_TYPE) \
HRESULT InArchive::METHOD_NAME(PROPID prop_id, VALUE_TYPE value) { \
NWindows::NCOM::CPropVariant prop; \
RETURN_SAME_IF_NOT_ZERO(this->in_archive->GetArchiveProperty(prop_id, &prop));
#define GET_ARCHIVE_PROPERTY_END \
}
#define GET_ENTRY_PROPERTY_START(METHOD_NAME, VALUE_TYPE) \
HRESULT InArchive::METHOD_NAME(UInt32 index, PROPID prop_id, VALUE_TYPE value) { \
NWindows::NCOM::CPropVariant prop; \
RETURN_SAME_IF_NOT_ZERO(this->in_archive->GetProperty(index, prop_id, &prop));
#define GET_ENTRY_PROPERTY_END \
}
#define GET_PROPERTY_TYPE \
*value = VarTypeToPropType(prop.vt); \
return S_OK;
GET_ARCHIVE_PROPERTY_START(GetArchivePropertyType, PropType*)
GET_PROPERTY_TYPE
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryPropertyType, PropType*)
GET_PROPERTY_TYPE
GET_ENTRY_PROPERTY_END
#define GET_BOOLEAN_PROPERTY \
switch (prop.vt) { \
case VT_BOOL: \
*value = prop.boolVal != 0; \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveBooleanProperty, bool*)
GET_BOOLEAN_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryBooleanProperty, bool*)
GET_BOOLEAN_PROPERTY
GET_ENTRY_PROPERTY_END
#define GET_INT_PROPERTY \
switch (prop.vt) { \
case VT_I1: \
*value = prop.cVal; \
return S_OK; \
case VT_I2: \
*value = prop.iVal; \
return S_OK; \
case VT_I4: \
*value = prop.lVal; \
return S_OK; \
case VT_INT: \
*value = prop.intVal; \
return S_OK; \
case VT_UI1: \
*value = prop.bVal; \
return S_OK; \
case VT_UI2: \
*value = prop.uiVal; \
return S_OK; \
case VT_UI4: \
*value = prop.ulVal; \
return S_OK; \
case VT_UINT: \
*value = prop.uintVal; \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveIntProperty, Int32*)
GET_INT_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryIntProperty, Int32*)
GET_INT_PROPERTY
GET_ENTRY_PROPERTY_END
static const UInt64 FILE_TIME_OFFSET = (369 * 365 + 89) * 86400ULL * 10000000ULL;
static const UInt64 FILE_TIME_MULTIPLE = 10000ULL;
#define GET_LONG_PROPERTY \
switch (prop.vt) { \
case VT_I8: \
*value = prop.hVal.QuadPart; \
return S_OK; \
case VT_UI8: \
*value = prop.uhVal.QuadPart; \
return S_OK; \
case VT_FILETIME: \
*value = (*reinterpret_cast<UInt64*>(&prop.filetime) - FILE_TIME_OFFSET) / \
FILE_TIME_MULTIPLE; \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveLongProperty, Int64*)
GET_LONG_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryLongProperty, Int64*)
GET_LONG_PROPERTY
GET_ENTRY_PROPERTY_END
#define GET_STRING_PROPERTY \
switch (prop.vt) { \
case VT_BSTR: \
*value = ::SysAllocString(prop.bstrVal); \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveStringProperty, BSTR*)
GET_STRING_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryStringProperty, BSTR*)
GET_STRING_PROPERTY
GET_ENTRY_PROPERTY_END
HRESULT InArchive::GetEntryStream(UInt32 index, ISequentialInStream** stream) {
*stream = nullptr;
CMyComPtr<IInArchiveGetStream> in_archive_get_stream;
in_archive->QueryInterface(IID_IInArchiveGetStream, reinterpret_cast<void **>(&in_archive_get_stream));
if (in_archive_get_stream != nullptr) {
return in_archive_get_stream->GetStream(index, stream);
} else {
return E_NOTIMPL;
}
}
HRESULT InArchive::ExtractEntry(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream) {
CMyComPtr<ArchiveExtractCallback> callback(new ArchiveExtractCallback(index, password, out_stream));
HRESULT result = this->in_archive->Extract(&index, 1, false, callback);
return callback->GetBetterResult(result);
}
@@ -1,62 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_IN_ARCHIVE_H__
#define __A7ZIP_IN_ARCHIVE_H__
#include <include_windows/windows.h>
#include <Common/MyCom.h>
#include <Common/MyString.h>
#include <7zip/Archive/IArchive.h>
#include "PropType.h"
namespace a7zip {
class InArchive {
public:
InArchive(InArchive* parent, CMyComPtr<IInArchive>& in_archive, AString& format_name);
~InArchive();
public:
const AString& GetFormatName();
HRESULT GetNumberOfEntries(UInt32& number);
HRESULT GetArchivePropertyType(PROPID prop_id, PropType* prop_type);
HRESULT GetArchiveBooleanProperty(PROPID prop_id, bool *bool_prop);
HRESULT GetArchiveIntProperty(PROPID prop_id, Int32* int_prop);
HRESULT GetArchiveLongProperty(PROPID prop_id, Int64* long_prop);
HRESULT GetArchiveStringProperty(PROPID prop_id, BSTR* str_prop);
HRESULT GetEntryPropertyType(UInt32 index, PROPID prop_id, PropType* prop_type);
HRESULT GetEntryBooleanProperty(UInt32 index, PROPID prop_id, bool *bool_prop);
HRESULT GetEntryIntProperty(UInt32 index, PROPID prop_id, Int32* int_prop);
HRESULT GetEntryLongProperty(UInt32 index, PROPID prop_id, Int64* long_prop);
HRESULT GetEntryStringProperty(UInt32 index, PROPID prop_id, BSTR* str_prop);
HRESULT GetEntryStream(UInt32 index, ISequentialInStream** stream);
HRESULT ExtractEntry(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream);
private:
InArchive* parent;
CMyComPtr<IInArchive> in_archive;
AString format_name;
};
}
#endif //__A7ZIP_IN_ARCHIVE_H__
@@ -1,46 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaEnv.h"
using namespace a7zip;
JavaVM* JavaEnv::jvm = nullptr;
void JavaEnv::Initialize(JavaVM* jvm) {
JavaEnv::jvm = jvm;
}
JavaEnv::JavaEnv() {
env = nullptr;
if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_EDETACHED) {
should_detach = jvm->AttachCurrentThread(&env, nullptr) == JNI_OK;
} else {
should_detach = false;
}
}
JavaEnv::~JavaEnv() {
if (jvm != nullptr && should_detach) {
jvm->DetachCurrentThread();
env = nullptr;
should_detach = false;
}
}
bool JavaEnv::IsValid() {
return env != nullptr;
}
-46
View File
@@ -1,46 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_ENV_H__
#define __A7ZIP_JAVA_ENV_H__
#include <jni.h>
namespace a7zip {
class JavaEnv {
public:
JavaEnv();
~JavaEnv();
bool IsValid();
JNIEnv* operator->() const { return env; }
explicit operator JNIEnv*() const { return env; }
private:
JNIEnv* env;
bool should_detach;
public:
static void Initialize(JavaVM* jvm);
private:
static JavaVM* jvm;
};
}
#endif //__A7ZIP_JAVA_ENV_H__
@@ -1,121 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaHelper.h"
#include <jni.h>
#include <cstdio>
#include "Utils.h"
#define MAX_MSG_SIZE 1024
using namespace a7zip;
static jint ThrowExceptionInternal(
JNIEnv* env,
const char* exception_name,
const char* message,
va_list va_args
) {
char formatted_message[MAX_MSG_SIZE];
vsnprintf(formatted_message, MAX_MSG_SIZE, message, va_args);
jclass exception_class = env->FindClass(exception_name);
return env->ThrowNew(exception_class, formatted_message);
}
jint JavaHelper::ThrowException(
JNIEnv* env,
const char* exception_name,
const char* message,
...
) {
va_list va_args;
va_start(va_args, message);
jint result = ThrowExceptionInternal(env, exception_name, message, va_args);
va_end(va_args);
return result;
}
static const char* GetMessageForCode(HRESULT code) {
switch (code) {
case E_NOT_INITIALIZED:
return "The module is not initialized";
case S_OK:
case E_INTERNAL:
return "a7zip is buggy";
case E_CLASS_NOT_FOUND:
return "Can't find the class";
case E_METHOD_NOT_FOUND:
return "Can't find the method";
case E_JAVA_EXCEPTION:
return "Catch a java exception";
case E_FAILED_CONSTRUCT:
return "Failed to create new class";
case E_FAILED_REGISTER:
return "Failed to register methods";
case E_INCONSISTENT_PROP_TYPE:
return "Inconsistent property type";
case E_EMPTY_PROP:
return "Empty property";
case E_UNKNOWN_FORMAT:
return "Unknown archive format";
case E_UNSUPPORTED_EXTRACT_MODE:
return "Unsupported extract mode";
case E_NO_OUT_STREAM:
return "No out stream";
case E_UNSUPPORTED_METHOD:
return "Unsupported method";
case E_DATA_ERROR:
return "Data error";
case E_DATA_ERROR_ENCRYPTED:
return "Data Error in encrypted file. Wrong password?";
case E_CRC_ERROR:
return "CRC failed";
case E_CRC_ERROR_ENCRYPTED:
return "CRC Failed in encrypted file. Wrong password?";
case E_UNAVAILABLE:
return "Unavailable data";
case E_UNEXPECTED_END:
return "Unexpected end of data";
case E_DATA_AFTER_END:
return "There are some data after the end of the payload data";
case E_IS_NOT_ARC:
return "Is not archive";
case E_HEADERS_ERROR:
return "Headers Error";
case E_WRONG_PASSWORD:
return "Wrong password";
case E_NO_PASSWORD:
return "No password";
case E_NOTIMPL:
return "Not implemented";
case E_OUTOFMEMORY:
return "Out of memory";
case E_UNKNOWN_ERROR:
default:
return "Unknown error.";
}
}
jint JavaHelper::ThrowException(
JNIEnv* env,
const char* exception_name,
HRESULT code
) {
return ThrowException(env, exception_name, GetMessageForCode(code));
}
@@ -1,89 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_HELPER_H__
#define __A7ZIP_JAVA_HELPER_H__
#include <jni.h>
#include <Common/MyWindows.h>
#define CLASS_NAME_ILLEGAL_STATE_EXCEPTION "java/lang/IllegalStateException"
#define CLASS_NAME_IO_EXCEPTION "java/io/IOException"
#define CLASS_NAME_ARCHIVE_EXCEPTION "com/hippo/a7zip/ArchiveException"
#define CLASS_NAME_PASSWORD_EXCEPTION "com/hippo/a7zip/PasswordException"
#define THROW_EXCEPTION(ENV, EXCEPTION_NAME, ...) \
do { \
a7zip::JavaHelper::ThrowException((ENV), (EXCEPTION_NAME), __VA_ARGS__); \
return; \
} while (0)
#define THROW_EXCEPTION_RET(ENV, RET, EXCEPTION_NAME, ...) \
do { \
a7zip::JavaHelper::ThrowException((ENV), (EXCEPTION_NAME), __VA_ARGS__); \
return (RET); \
} while (0)
#define THROW_IO_EXCEPTION(ENV, CODE) \
THROW_EXCEPTION(ENV, CLASS_NAME_IO_EXCEPTION, CODE)
#define THROW_IO_EXCEPTION_RET(ENV, RET, CODE) \
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_IO_EXCEPTION, CODE)
#define THROW_ARCHIVE_EXCEPTION(ENV, CODE) \
do { \
if ((CODE) == E_NO_PASSWORD || (CODE) == E_WRONG_PASSWORD) { \
THROW_EXCEPTION(ENV, CLASS_NAME_PASSWORD_EXCEPTION, CODE); \
} else { \
THROW_EXCEPTION(ENV, CLASS_NAME_ARCHIVE_EXCEPTION, CODE); \
} \
} while (0)
#define THROW_ARCHIVE_EXCEPTION_RET(ENV, RET, CODE) \
do { \
if ((CODE) == E_NO_PASSWORD || (CODE) == E_WRONG_PASSWORD) { \
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_PASSWORD_EXCEPTION, CODE); \
} else { \
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_ARCHIVE_EXCEPTION, CODE); \
} \
} while (0)
#define CHECK_CLOSED(ENV, NATIVE_PTR) \
do { \
if ((NATIVE_PTR) == 0) { \
THROW_EXCEPTION(ENV, CLASS_NAME_ILLEGAL_STATE_EXCEPTION, "It's closed"); \
} \
} while (0)
#define CHECK_CLOSED_RET(ENV, RET, NATIVE_PTR) \
do { \
if ((NATIVE_PTR) == 0) { \
THROW_EXCEPTION_RET( \
ENV, RET, CLASS_NAME_ILLEGAL_STATE_EXCEPTION, "It's closed"); \
} \
} while (0)
namespace a7zip {
namespace JavaHelper {
jint ThrowException(JNIEnv* env, const char* exception_name, const char* message, ...);
jint ThrowException(JNIEnv* env, const char* exception_name, HRESULT code);
}
}
#endif //__A7ZIP_JAVA_HELPER_H__
@@ -1,397 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaInArchive.h"
#include <cstdio>
#include <type_traits>
#include <include_windows/windows.h>
#include <7zip/Archive/IArchive.h>
#include "OpenVolumeCallback.h"
#include "SeekableInputStream.h"
#include "JavaHelper.h"
#include "JavaSeekableInputStream.h"
#include "JavaInputStream.h"
#include "Log.h"
#include "OutputStream.h"
#include "SevenZip.h"
#include "Utils.h"
#ifdef LOG_TAG
# undef LOG_TAG
#endif //LOG_TAG
#define LOG_TAG "JavaArchive"
using namespace a7zip;
static void CopyJStringToBSTR(BSTR bstr, const jchar* jstr, int length) {
for (int i = 0; i < length; i++) {
*bstr++ = *jstr++;
}
*bstr = 0;
}
static BSTR JStringToBSTR(JNIEnv* env, jstring jstr) {
BSTR bstr = nullptr;
if (jstr != nullptr) {
jsize length = env->GetStringLength(jstr);
const jchar* jchars = env->GetStringChars(jstr, nullptr);
bstr = ::SysAllocStringLen(nullptr, static_cast<UINT>(length));
CopyJStringToBSTR(bstr, jchars, length);
env->ReleaseStringChars(jstr, jchars);
}
return bstr;
}
static jlong NativeOpen(
JNIEnv* env,
jclass,
jobject stream,
jstring password,
jstring filename,
jobject open_volume_callback
) {
CMyComPtr<IInStream> in_stream = nullptr;
HRESULT result = SeekableInputStream::Create(env, stream, in_stream);
if (result != S_OK || in_stream == nullptr) {
// Call java methods before throw exception
if (in_stream != nullptr) {
in_stream.Release();
}
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
}
BSTR bstr_password = nullptr;
BSTR bstr_filename = nullptr;
CMyComPtr<OpenVolumeCallback> open_volume_callback_wrapper = nullptr;
if (filename != nullptr && open_volume_callback != nullptr) {
result = OpenVolumeCallback::Create(env, open_volume_callback, open_volume_callback_wrapper);
if (result != S_OK) {
// Call java methods before throw exception
if (in_stream != nullptr) {
in_stream.Release();
}
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
}
bstr_filename = JStringToBSTR(env, filename);
}
bstr_password = JStringToBSTR(env, password);
InArchive* archive = nullptr;
result = SevenZip::OpenArchive(in_stream, bstr_password, bstr_filename, open_volume_callback_wrapper, &archive);
::SysFreeString(bstr_password);
::SysFreeString(bstr_filename);
if (result != S_OK || archive == nullptr) {
// Call java methods before throw exception
in_stream.Release();
delete archive;
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
}
return reinterpret_cast<jlong>(archive);
}
static jstring NativeGetFormatName(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, nullptr, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
return env->NewStringUTF(archive->GetFormatName());
}
static jint NativeGetNumberOfEntries(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
UInt32 number = 0;
HRESULT result = archive->GetNumberOfEntries(number);
return result == S_OK ? number : -1;
}
#define GET_ARCHIVE_PROPERTY_START(METHOD_NAME, RETURN_TYPE) \
static RETURN_TYPE METHOD_NAME(JNIEnv* env, jclass, jlong native_ptr, jint prop_id) { \
CHECK_CLOSED_RET(env, 0, native_ptr); \
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
#define GET_ARCHIVE_PROPERTY_END \
}
#define GET_ENTRY_PROPERTY_START(METHOD_NAME, RETURN_TYPE) \
static RETURN_TYPE METHOD_NAME(JNIEnv* env, jclass, jlong native_ptr, jint index, jint prop_id) {\
CHECK_CLOSED_RET(env, 0, native_ptr); \
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
#define GET_ENTRY_PROPERTY_END \
}
#define GET_PROPERTY_TYPE(GETTER) \
PropType prop_type; \
HRESULT result = (GETTER); \
return result == S_OK ? prop_type : PT_UNKNOWN;
GET_ARCHIVE_PROPERTY_START(NativeGetArchivePropertyType, jint)
GET_PROPERTY_TYPE(archive->GetArchivePropertyType(static_cast<PROPID>(prop_id), &prop_type))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryPropertyType, jint)
GET_PROPERTY_TYPE(archive->GetEntryPropertyType(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &prop_type))
GET_ENTRY_PROPERTY_END
#define GET_BOOL_TYPE(GETTER) \
bool bool_prop; \
HRESULT result = (GETTER); \
return result == S_OK ? bool_prop : false;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveBooleanProperty, jboolean)
GET_BOOL_TYPE(archive->GetArchiveBooleanProperty(static_cast<PROPID>(prop_id), &bool_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryBooleanProperty, jboolean)
GET_BOOL_TYPE(archive->GetEntryBooleanProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &bool_prop))
GET_ENTRY_PROPERTY_END
#define GET_INT_TYPE(GETTER) \
Int32 int_prop; \
HRESULT result = (GETTER); \
return result == S_OK ? int_prop : 0;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveIntProperty, jint)
GET_INT_TYPE(archive->GetArchiveIntProperty(static_cast<PROPID>(prop_id), &int_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryIntProperty, jint)
GET_INT_TYPE(archive->GetEntryIntProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &int_prop))
GET_ENTRY_PROPERTY_END
#define GET_LONG_TYPE(GETTER) \
Int64 long_prop; \
HRESULT result = (GETTER); \
return result == S_OK ? long_prop : 0;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveLongProperty, jlong)
GET_LONG_TYPE(archive->GetArchiveLongProperty(static_cast<PROPID>(prop_id), &long_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryLongProperty, jlong)
GET_LONG_TYPE(archive->GetEntryLongProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &long_prop))
GET_ENTRY_PROPERTY_END
static void shrink(BSTR bstr) {
jchar* jstr = reinterpret_cast<jchar*>(bstr);
UINT n = ::SysStringLen(bstr);
for (UINT i = 0; i < n; i++) {
jstr[i] = static_cast<jchar>(bstr[i]);
}
jstr[n] = 0;
}
#define GET_STRING_PROPERTY(GETTER) \
BSTR str_prop = nullptr; \
HRESULT result = (GETTER); \
if (result != S_OK || str_prop == nullptr) { \
if (str_prop != nullptr) ::SysFreeString(str_prop); \
return nullptr; \
} \
shrink(str_prop); \
jstring jstr = \
env->NewString(reinterpret_cast<const jchar*>(str_prop), ::SysStringLen(str_prop)); \
::SysFreeString(str_prop); \
return jstr;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveStringProperty, jstring)
GET_STRING_PROPERTY(archive->GetArchiveStringProperty(static_cast<PROPID>(prop_id), &str_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryStringProperty, jstring)
GET_STRING_PROPERTY(archive->GetEntryStringProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &str_prop))
GET_ENTRY_PROPERTY_END
static jobject NativeGetEntryStream(
JNIEnv* env,
jclass,
jlong native_ptr,
jint index
) {
CHECK_CLOSED_RET(env, nullptr, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
CMyComPtr<ISequentialInStream> sequential_in_stream = nullptr;
HRESULT result = archive->GetEntryStream(static_cast<UInt32>(index), &sequential_in_stream);
if (result != S_OK || sequential_in_stream == nullptr) {
if (sequential_in_stream != nullptr) {
// Release the stream manually before throw java exception
sequential_in_stream.Release();
}
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
}
// Try to get IInStream
CMyComPtr<IInStream> in_stream = nullptr;
sequential_in_stream.QueryInterface(IID_IInStream, &in_stream);
if (in_stream != nullptr) {
// It's an IInStream
jobject object = nullptr;
result = JavaSeekableInputStream::NewInstance(env, in_stream, &object);
if (object == nullptr) {
// Release the stream manually before throw java exception
in_stream->Release();
sequential_in_stream->Release();
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
}
return object;
} else {
// It's just an ISequentialInStream
jobject object = nullptr;
result = JavaInputStream::NewInstance(env, sequential_in_stream, &object);
if (object == nullptr) {
// Release the stream manually before throw java exception
sequential_in_stream->Release();
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
}
return object;
}
}
static void NativeExtractEntry(
JNIEnv* env,
jclass,
jlong native_ptr,
jint index,
jstring password,
jobject stream
) {
CHECK_CLOSED(env, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
CMyComPtr<ISequentialOutStream> out_stream = nullptr;
HRESULT result = OutputStream::Create(env, stream, out_stream);
if (result != S_OK || out_stream == nullptr) {
if (out_stream != nullptr) {
// Call java methods before throw exception
out_stream.Release();
} else {
// Let java code closes the OutStream
}
THROW_ARCHIVE_EXCEPTION(env, result);
}
const jchar* j_password = nullptr;
BSTR bstr_password = nullptr;
if (password != nullptr) {
jsize length = env->GetStringLength(password);
j_password = env->GetStringChars(password, nullptr);
bstr_password = ::SysAllocStringLen(nullptr, static_cast<UINT>(length));
CopyJStringToBSTR(bstr_password, j_password, length);
}
result = archive->ExtractEntry(static_cast<UInt32>(index), bstr_password, out_stream);
if (password != nullptr) {
::SysFreeString(bstr_password);
env->ReleaseStringChars(password, j_password);
}
if (result != S_OK) {
// Call java methods before throw exception
out_stream.Release();
THROW_ARCHIVE_EXCEPTION(env, result);
}
}
static void NativeClose(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED(env, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
delete archive;
}
static JNINativeMethod archive_methods[] = {
{ "nativeOpen",
"(Lcom/hippo/a7zip/SeekableInputStream;Ljava/lang/String;Ljava/lang/String;Lcom/hippo/a7zip/InArchive$OpenVolumeCallback;)J",
reinterpret_cast<void *>(NativeOpen) },
{ "nativeGetFormatName",
"(J)Ljava/lang/String;",
reinterpret_cast<void *>(NativeGetFormatName) },
{ "nativeGetNumberOfEntries",
"(J)I",
reinterpret_cast<void *>(NativeGetNumberOfEntries) },
{ "nativeGetArchivePropertyType",
"(JI)I",
reinterpret_cast<void *>(NativeGetArchivePropertyType) },
{ "nativeGetArchiveBooleanProperty",
"(JI)Z",
reinterpret_cast<void *>(NativeGetArchiveBooleanProperty) },
{ "nativeGetArchiveIntProperty",
"(JI)I",
reinterpret_cast<void *>(NativeGetArchiveIntProperty) },
{ "nativeGetArchiveLongProperty",
"(JI)J",
reinterpret_cast<void *>(NativeGetArchiveLongProperty) },
{ "nativeGetArchiveStringProperty",
"(JI)Ljava/lang/String;",
reinterpret_cast<void *>(NativeGetArchiveStringProperty) },
{ "nativeGetEntryPropertyType",
"(JII)I",
reinterpret_cast<void *>(NativeGetEntryPropertyType) },
{ "nativeGetEntryBooleanProperty",
"(JII)Z",
reinterpret_cast<void *>(NativeGetEntryBooleanProperty) },
{ "nativeGetEntryIntProperty",
"(JII)I",
reinterpret_cast<void *>(NativeGetEntryIntProperty) },
{ "nativeGetEntryLongProperty",
"(JII)J",
reinterpret_cast<void *>(NativeGetEntryLongProperty) },
{ "nativeGetEntryStringProperty",
"(JII)Ljava/lang/String;",
reinterpret_cast<void *>(NativeGetEntryStringProperty) },
{ "nativeGetEntryStream",
"(JI)Ljava/io/InputStream;",
reinterpret_cast<void *>(NativeGetEntryStream) },
{ "nativeExtractEntry",
"(JILjava/lang/String;Ljava/io/OutputStream;)V",
reinterpret_cast<void *>(NativeExtractEntry) },
{ "nativeClose",
"(J)V",
reinterpret_cast<void *>(NativeClose) }
};
HRESULT JavaInArchive::RegisterMethods(JNIEnv* env) {
jclass clazz = env->FindClass("com/hippo/a7zip/InArchive");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
jint result = env->RegisterNatives(clazz, archive_methods, std::extent<decltype(archive_methods)>::value);
if (result < 0) {
return E_FAILED_REGISTER;
}
return S_OK;
}
@@ -1,32 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_IN_ARCHIVE_H__
#define __A7ZIP_JAVA_IN_ARCHIVE_H__
#include <jni.h>
#include <Common/MyWindows.h>
namespace a7zip {
namespace JavaInArchive {
HRESULT RegisterMethods(JNIEnv* env);
}
}
#endif //__A7ZIP_JAVA_IN_ARCHIVE_H__
@@ -1,60 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include "SeekableInputStream.h"
#include "JavaEnv.h"
#include "JavaInArchive.h"
#include "JavaInitA7Zip.h"
#include "JavaSeekableInputStream.h"
#include "JavaInputStream.h"
#include "OpenVolumeCallback.h"
#include "OutputStream.h"
#include "SevenZip.h"
#include "Utils.h"
using namespace a7zip;
#ifdef __cplusplus
extern "C"
{
#endif
bool a7zip_onLoad(JavaVM *vm) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
return false;
}
JavaEnv::Initialize(vm);
RETURN_JNI_ERR_IF_NOT_ZERO(SeekableInputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(JavaSeekableInputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(OpenVolumeCallback::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(OutputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(SevenZip::Initialize());
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInArchive::RegisterMethods(static_cast<JNIEnv *>(env)));
RETURN_JNI_ERR_IF_NOT_ZERO(
JavaSeekableInputStream::RegisterMethods(static_cast<JNIEnv *>(env)));
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInputStream::RegisterMethods(static_cast<JNIEnv *>(env)));
return true;
}
#ifdef __cplusplus
}
#endif
@@ -1,11 +0,0 @@
#include <jni.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
bool a7zip_onLoad(JavaVM *vm);
#ifdef __cplusplus
}
#endif
@@ -1,126 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaInputStream.h"
#include <type_traits>
#include <Common/MyCom.h>
#include "JavaHelper.h"
#include "Utils.h"
using namespace a7zip;
static jint NativeRead(
JNIEnv* env,
jclass,
jlong native_ptr,
jbyteArray array,
jint offset,
jint length
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
ISequentialInStream* stream = reinterpret_cast<ISequentialInStream*>(native_ptr);
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
if (bytes == nullptr) THROW_IO_EXCEPTION_RET(env, 0, E_JAVA_EXCEPTION);
UInt32 processed_size;
HRESULT result = stream->Read(bytes + offset, static_cast<UInt32>(length), &processed_size);
env->ReleaseByteArrayElements(array, bytes, 0);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
if (length != 0 && processed_size == 0) {
// EOF
return -1;
}
return processed_size;
}
static void NativeClose(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED(env, native_ptr);
ISequentialInStream* stream = reinterpret_cast<ISequentialInStream*>(native_ptr);
stream->Release();
}
static bool initialized = false;
static jclass class_native_sequential_in_stream = nullptr;
static jmethodID constructor_native_sequential_in_stream = nullptr;
HRESULT JavaInputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
class_native_sequential_in_stream = env->FindClass("com/hippo/a7zip/NativeInputStream");
if (class_native_sequential_in_stream == nullptr) return E_CLASS_NOT_FOUND;
class_native_sequential_in_stream = static_cast<jclass>(env->NewGlobalRef(class_native_sequential_in_stream));
if (class_native_sequential_in_stream == nullptr) return E_OUTOFMEMORY;
constructor_native_sequential_in_stream = env->GetMethodID(class_native_sequential_in_stream, "<init>", "(J)V");
if (constructor_native_sequential_in_stream == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return JNI_OK;
}
static JNINativeMethod stream_methods[] = {
{ "nativeRead",
"(J[BII)I",
reinterpret_cast<void *>(NativeRead) },
{ "nativeClose",
"(J)V",
reinterpret_cast<void *>(NativeClose) }
};
HRESULT JavaInputStream::RegisterMethods(JNIEnv* env) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jint result = env->RegisterNatives(class_native_sequential_in_stream, stream_methods, std::extent<decltype(stream_methods)>::value);
if (result < 0) {
return E_FAILED_REGISTER;
}
return S_OK;
}
HRESULT JavaInputStream::NewInstance(JNIEnv* env, ISequentialInStream* stream, jobject* object) {
*object = nullptr;
if (!initialized) {
return E_NOT_INITIALIZED;
}
*object = env->NewObject(class_native_sequential_in_stream, constructor_native_sequential_in_stream, stream);
if (*object == nullptr) {
// Clear java exception and return
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// No java exception, but it's still an error
return E_UNKNOWN_ERROR;
}
// Ignore if any exception is pending
CLEAR_IF_EXCEPTION_PENDING(env);
return S_OK;
}
@@ -1,37 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
#define __A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
#include <jni.h>
#include <Common/MyWindows.h>
#include <7zip/IStream.h>
namespace a7zip {
namespace JavaInputStream {
HRESULT Initialize(JNIEnv* env);
HRESULT RegisterMethods(JNIEnv* env);
HRESULT NewInstance(JNIEnv* env, ISequentialInStream* stream, jobject* object);
}
}
#endif //__A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
@@ -1,202 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaSeekableInputStream.h"
#include <type_traits>
#include <Common/MyCom.h>
#include "JavaHelper.h"
#include "Utils.h"
using namespace a7zip;
static void NativeSeek(
JNIEnv* env,
jclass,
jlong native_ptr,
jlong pos
) {
CHECK_CLOSED(env, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
UInt64 newPosition; // Ignored
HRESULT result = stream->Seek(pos, STREAM_SEEK_SET, &newPosition);
if (result != S_OK) THROW_IO_EXCEPTION(env, result);
}
static jlong NativeTell(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
UInt64 newPosition;
HRESULT result = stream->Seek(0, STREAM_SEEK_CUR, &newPosition);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
return static_cast<jlong>(newPosition);
}
static jlong NativeSize(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
// Try to get IStreamGetSize
CMyComPtr<IStreamGetSize> stream_get_size;
stream->QueryInterface(IID_IStreamGetSize, reinterpret_cast<void **>(&stream_get_size));
if (stream_get_size != nullptr) {
// It's an IStreamGetSize
UInt64 size;
HRESULT result = stream_get_size->GetSize(&size);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
return static_cast<jlong>(size);
} else {
// It's not an IStreamGetSize
// Backup current position
UInt64 cur;
HRESULT result = stream->Seek(0, STREAM_SEEK_CUR, &cur);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
// Seek to the end of steam to get size
UInt64 size;
result = stream->Seek(0, STREAM_SEEK_END, &size);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
// Back to previous position
UInt64 ignored;
result = stream->Seek(cur, STREAM_SEEK_SET, &ignored);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
return static_cast<jlong>(size);
}
}
static jint NativeRead(
JNIEnv* env,
jclass,
jlong native_ptr,
jbyteArray array,
jint offset,
jint length
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
if (bytes == nullptr) THROW_IO_EXCEPTION_RET(env, 0, E_JAVA_EXCEPTION);
UInt32 processed_size;
HRESULT result = stream->Read(bytes + offset, static_cast<UInt32>(length), &processed_size);
env->ReleaseByteArrayElements(array, bytes, 0);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
if (length != 0 && processed_size == 0) {
// EOF
return -1;
}
return processed_size;
}
static void NativeClose(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED(env, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
stream->Release();
}
static bool initialized = false;
static jclass class_native_in_stream = nullptr;
static jmethodID constructor_native_in_stream = nullptr;
HRESULT JavaSeekableInputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
class_native_in_stream = env->FindClass("com/hippo/a7zip/NativeSeekableInputStream");
if (class_native_in_stream == nullptr) return E_CLASS_NOT_FOUND;
class_native_in_stream = static_cast<jclass>(env->NewGlobalRef(class_native_in_stream));
if (class_native_in_stream == nullptr) return E_OUTOFMEMORY;
constructor_native_in_stream = env->GetMethodID(class_native_in_stream, "<init>", "(J)V");
if (constructor_native_in_stream == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return JNI_OK;
}
static JNINativeMethod stream_methods[] = {
{ "nativeSeek",
"(JJ)V",
reinterpret_cast<void *>(NativeSeek) },
{ "nativeTell",
"(J)J",
reinterpret_cast<void *>(NativeTell) },
{ "nativeSize",
"(J)J",
reinterpret_cast<void *>(NativeSize) },
{ "nativeRead",
"(J[BII)I",
reinterpret_cast<void *>(NativeRead) },
{ "nativeClose",
"(J)V",
reinterpret_cast<void *>(NativeClose) }
};
HRESULT JavaSeekableInputStream::RegisterMethods(JNIEnv* env) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jint result = env->RegisterNatives(class_native_in_stream, stream_methods, std::extent<decltype(stream_methods)>::value);
if (result < 0) {
return E_FAILED_REGISTER;
}
return S_OK;
}
HRESULT JavaSeekableInputStream::NewInstance(JNIEnv* env, IInStream* stream, jobject* object) {
*object = nullptr;
if (!initialized) {
return E_NOT_INITIALIZED;
}
*object = env->NewObject(class_native_in_stream, constructor_native_in_stream, stream);
if (*object == nullptr) {
// Clear java exception and return
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// No java exception, but it's still an error
return E_UNKNOWN_ERROR;
}
// Ignore if any exception is pending
CLEAR_IF_EXCEPTION_PENDING(env);
return S_OK;
}
@@ -1,37 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_IN_STREAM_H__
#define __A7ZIP_JAVA_IN_STREAM_H__
#include <jni.h>
#include <Common/MyWindows.h>
#include <7zip/IStream.h>
namespace a7zip {
namespace JavaSeekableInputStream {
HRESULT Initialize(JNIEnv* env);
HRESULT RegisterMethods(JNIEnv* env);
HRESULT NewInstance(JNIEnv* env, IInStream* stream, jobject* object);
}
}
#endif //__A7ZIP_JAVA_IN_STREAM_H__
-14
View File
@@ -1,14 +0,0 @@
#ifndef __A7ZIP_LOG_H__
#define __A7ZIP_LOG_H__
#include <android/log.h>
#define LOG_TAG "a7zip"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
#endif //__A7ZIP_LOG_H__
@@ -1,111 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "OpenVolumeCallback.h"
#include <malloc.h>
#include <MyString.h>
#include "SeekableInputStream.h"
#include "JavaEnv.h"
#include "Utils.h"
#include "Log.h"
using namespace a7zip;
bool OpenVolumeCallback::initialized = false;
jmethodID OpenVolumeCallback::method_open_volume = nullptr;
OpenVolumeCallback::OpenVolumeCallback(jobject callback) : callback(callback) { }
OpenVolumeCallback::~OpenVolumeCallback() {
JavaEnv env;
if (!env.IsValid()) return;
env->DeleteGlobalRef(callback);
callback = nullptr;
}
HRESULT OpenVolumeCallback::OpenVolume(const wchar_t *name, CMyComPtr<IInStream>& in_stream) {
JavaEnv env;
// const wchar_t* to jstring
unsigned len = MyStringLen(name);
jchar *buffer = reinterpret_cast<jchar *>(malloc(len * sizeof(jchar)));
if (buffer == nullptr) {
return E_OUTOFMEMORY;
}
for (int i = 0; i < len; i++) {
buffer[i] = static_cast<jchar>(name[i]);
}
jstring j_name = env->NewString(buffer, len);
free(buffer);
if (j_name == nullptr) {
return E_OUTOFMEMORY;
}
// Wrap java stream
jobject stream = env->CallObjectMethod(callback, method_open_volume, j_name);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
RETURN_SAME_IF_NOT_ZERO(SeekableInputStream::Create(static_cast<JNIEnv*>(env), stream, in_stream));
return S_OK;
}
HRESULT OpenVolumeCallback::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
jclass clazz = env->FindClass("com/hippo/a7zip/InArchive$OpenVolumeCallback");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
method_open_volume = env->GetMethodID(clazz, "openVolume", "(Ljava/lang/String;)Lcom/hippo/a7zip/SeekableInputStream;");
if (method_open_volume == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return S_OK;
}
HRESULT OpenVolumeCallback::Create(
JNIEnv* env,
jobject callback,
OpenVolumeCallback** result
) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jobject g_callback = env->NewGlobalRef(callback);
if (g_callback == nullptr) {
return E_OUTOFMEMORY;
}
*result = new OpenVolumeCallback(g_callback);
return S_OK;
}
HRESULT OpenVolumeCallback::Create(
JNIEnv* env,
jobject callback,
CMyComPtr<OpenVolumeCallback>& result
) {
OpenVolumeCallback *callback_ptr = nullptr;
RETURN_SAME_IF_NOT_ZERO(Create(env, callback, &callback_ptr));
result = callback_ptr;
return S_OK;
}
@@ -1,54 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_OPEN_VOLUME_CALLBACK_H__
#define __A7ZIP_OPEN_VOLUME_CALLBACK_H__
#include <jni.h>
#include <Common/MyCom.h>
#include <7zip/IStream.h>
namespace a7zip {
class OpenVolumeCallback : public CMyUnknownImp
{
private:
OpenVolumeCallback(jobject callback);
public:
virtual ~OpenVolumeCallback();
public:
MY_ADDREF_RELEASE
HRESULT OpenVolume(const wchar_t *name, CMyComPtr<IInStream>& in_stream);
private:
jobject callback;
public:
static HRESULT Initialize(JNIEnv* env);
static HRESULT Create(JNIEnv* env, jobject callback, OpenVolumeCallback** result);
static HRESULT Create(JNIEnv* env, jobject callback, CMyComPtr<OpenVolumeCallback>& result);
private:
static bool initialized;
static jmethodID method_open_volume;
};
}
#endif //__A7ZIP_OPEN_VOLUME_CALLBACK_H__
@@ -1,127 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "OutputStream.h"
#include "JavaEnv.h"
#include "Utils.h"
#include "Log.h"
#define ARRAY_SIZE DEFAULT_BUFFER_SIZE
using namespace a7zip;
bool OutputStream::initialized = false;
jmethodID OutputStream::method_write = nullptr;
jmethodID OutputStream::method_close = nullptr;
OutputStream::OutputStream(
jobject stream,
jbyteArray array
) :
stream(stream),
array(array) { }
OutputStream::~OutputStream() {
JavaEnv env;
if (!env.IsValid()) return;
env->CallVoidMethod(stream, method_close);
CLEAR_IF_EXCEPTION_PENDING(env);
env->DeleteGlobalRef(stream);
env->DeleteGlobalRef(array);
stream = nullptr;
array = nullptr;
}
HRESULT OutputStream::Write(const void* data, UInt32 size, UInt32* processedSize) {
if (processedSize != nullptr) {
*processedSize = 0;
}
if (size == 0) {
return S_OK;
}
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
// Make size not bigger than ARRAY_SIZE
size = MIN(ARRAY_SIZE, size);
// Copy data from native buffer to java buffer
env->SetByteArrayRegion(array, 0, size, reinterpret_cast<const jbyte*>(data));
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// Write data to sink
env->CallVoidMethod(stream, method_write, array, 0, size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (processedSize != nullptr) {
*processedSize = size;
}
return S_OK;
}
HRESULT OutputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
jclass clazz = env->FindClass("java/io/OutputStream");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
method_write = env->GetMethodID(clazz, "write", "([BII)V");
if (method_write == nullptr) return E_METHOD_NOT_FOUND;
method_close = env->GetMethodID(clazz, "close", "()V");
if (method_close == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return S_OK;
}
HRESULT OutputStream::Create(
JNIEnv* env,
jobject stream,
CMyComPtr<ISequentialOutStream>& out_stream
) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jobject g_stream = env->NewGlobalRef(stream);
if (g_stream == nullptr) {
return E_OUTOFMEMORY;
}
jbyteArray array = env->NewByteArray(ARRAY_SIZE);
if (array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_FAILED_CONSTRUCT;
}
jbyteArray g_array = static_cast<jbyteArray>(env->NewGlobalRef(array));
if (g_array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_OUTOFMEMORY;
}
out_stream = new OutputStream(g_stream, g_array);
return S_OK;
}
@@ -1,57 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
#define __A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
#include <jni.h>
#include <Common/MyCom.h>
#include <7zip/IStream.h>
namespace a7zip {
class OutputStream :
public ISequentialOutStream,
public CMyUnknownImp
{
private:
OutputStream(jobject stream, jbyteArray array);
public:
virtual ~OutputStream();
public:
MY_UNKNOWN_IMP
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
private:
jobject stream;
jbyteArray array;
public:
static HRESULT Initialize(JNIEnv* env);
static HRESULT Create(JNIEnv* env, jobject stream, CMyComPtr<ISequentialOutStream>& out_stream);
private:
static bool initialized;
static jmethodID method_write;
static jmethodID method_close;
};
}
#endif //__A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
@@ -1,37 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_PROP_TYPE_H__
#define __A7ZIP_PROP_TYPE_H__
namespace a7zip {
enum PropTypeEnum {
PT_UNKNOWN,
PT_EMPTY,
PT_BOOL,
PT_INT,
PT_LONG,
PT_FLOAT,
PT_DOUBLE,
PT_STRING,
};
typedef unsigned short PropType;
}
#endif //__A7ZIP_PROP_TYPE_H__
@@ -1,196 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include "SeekableInputStream.h"
#include "JavaEnv.h"
#include "Utils.h"
#include "Log.h"
#define ARRAY_SIZE DEFAULT_BUFFER_SIZE
using namespace a7zip;
bool SeekableInputStream::initialized = false;
jmethodID SeekableInputStream::method_read = nullptr;
jmethodID SeekableInputStream::method_seek = nullptr;
jmethodID SeekableInputStream::method_tell = nullptr;
jmethodID SeekableInputStream::method_size = nullptr;
jmethodID SeekableInputStream::method_close = nullptr;
SeekableInputStream::SeekableInputStream(
jobject stream,
jbyteArray array
) :
stream(stream),
array(array) { }
SeekableInputStream::~SeekableInputStream() {
JavaEnv env;
if (!env.IsValid()) return;
env->CallVoidMethod(stream, method_close);
CLEAR_IF_EXCEPTION_PENDING(env);
env->DeleteGlobalRef(stream);
env->DeleteGlobalRef(array);
stream = nullptr;
array = nullptr;
}
HRESULT SeekableInputStream::Read(void* data, UInt32 size, UInt32* processedSize) {
if (processedSize != nullptr) {
*processedSize = 0;
}
if (size == 0) {
return S_OK;
}
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
// Make size not bigger than ARRAY_SIZE
size = MIN(ARRAY_SIZE, size);
jint read = env->CallIntMethod(stream, method_read, array, 0, size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// Check EOF
if (read <= 0) {
return S_OK;
}
env->GetByteArrayRegion(array, 0, read, static_cast<jbyte*>(data));
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (processedSize != nullptr) {
*processedSize = static_cast<UInt32>(read);
}
return S_OK;
}
HRESULT SeekableInputStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) {
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
jlong actual_offset;
switch (seekOrigin) {
case STREAM_SEEK_SET: {
actual_offset = static_cast<jlong>(offset);
break;
}
case STREAM_SEEK_CUR: {
jlong position = env->CallLongMethod(stream, method_tell);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
actual_offset = position + offset;
break;
}
case STREAM_SEEK_END: {
jlong size = env->CallLongMethod(stream, method_size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
actual_offset = size + offset;
break;
}
default: {
return E_INVALIDARG;
}
}
if (actual_offset < 0) {
return E_INVALIDARG;
}
env->CallVoidMethod(stream, method_seek, actual_offset);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (newPosition != nullptr) {
*newPosition = static_cast<UInt64>(actual_offset);
}
return S_OK;
}
HRESULT SeekableInputStream::GetSize(UInt64* size) {
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
jlong store_size = env->CallLongMethod(stream, method_size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (size != nullptr) {
*size = static_cast<UInt64>(store_size);
}
return S_OK;
}
HRESULT SeekableInputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
jclass clazz = env->FindClass("com/hippo/a7zip/SeekableInputStream");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
method_read = env->GetMethodID(clazz, "read", "([BII)I");
if (method_read == nullptr) return E_METHOD_NOT_FOUND;
method_seek = env->GetMethodID(clazz, "seek", "(J)V");
if (method_seek == nullptr) return E_METHOD_NOT_FOUND;
method_tell = env->GetMethodID(clazz, "tell", "()J");
if (method_tell == nullptr) return E_METHOD_NOT_FOUND;
method_size = env->GetMethodID(clazz, "size", "()J");
if (method_size == nullptr) return E_METHOD_NOT_FOUND;
method_close = env->GetMethodID(clazz, "close", "()V");
if (method_close == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return JNI_OK;
}
HRESULT SeekableInputStream::Create(
JNIEnv* env,
jobject stream,
CMyComPtr<IInStream>& in_stream
) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jobject g_stream = env->NewGlobalRef(stream);
if (g_stream == nullptr) {
return E_OUTOFMEMORY;
}
jbyteArray array = env->NewByteArray(ARRAY_SIZE);
if (array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_FAILED_CONSTRUCT;
}
jbyteArray g_array = static_cast<jbyteArray>(env->NewGlobalRef(array));
if (g_array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_OUTOFMEMORY;
}
in_stream = new SeekableInputStream(g_stream, g_array);
return S_OK;
}
@@ -1,65 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_IN_STREAM_H__
#define __A7ZIP_IN_STREAM_H__
#include <jni.h>
#include <7zip/IStream.h>
#include <Common/MyCom.h>
namespace a7zip {
class SeekableInputStream :
public IInStream,
public IStreamGetSize,
public CMyUnknownImp
{
private:
SeekableInputStream(jobject stream, jbyteArray array);
public:
virtual ~SeekableInputStream();
public:
MY_UNKNOWN_IMP2(IInStream, IStreamGetSize)
STDMETHOD(Read)(void* data, UInt32 size, UInt32* processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition);
STDMETHOD(GetSize)(UInt64* size);
private:
jobject stream;
jbyteArray array;
public:
static HRESULT Initialize(JNIEnv* env);
static HRESULT Create(JNIEnv* env, jobject stream, CMyComPtr<IInStream>& in_stream);
private:
static bool initialized;
static jmethodID method_read;
static jmethodID method_seek;
static jmethodID method_tell;
static jmethodID method_size;
static jmethodID method_close;
};
}
#endif //__A7ZIP_IN_STREAM_H__
@@ -1,567 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SevenZip.h"
#include <dlfcn.h>
#include <Windows/PropVariant.h>
#include <7zip/Archive/IArchive.h>
#include <7zip/IPassword.h>
#include "Log.h"
#include "Utils.h"
#include "OpenVolumeCallback.h"
#ifdef LOG_TAG
# undef LOG_TAG
# define LOG_TAG "P7Zip"
#endif
using namespace a7zip;
extern "C" HRESULT GetNumberOfMethods(UINT32 *numCodecs);
extern "C" HRESULT GetNumberOfFormats(UINT32 *numFormats);
extern "C" HRESULT GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT* value);
extern "C" HRESULT GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT* value);
extern "C" HRESULT CreateObject(const GUID* clsid, const GUID* iid, void** outObject);
typedef HRESULT (*GetPropertyFunc)(UInt32 index, PROPID propID, PROPVARIANT* value);
class Method {
public:
bool has_name;
AString name;
bool has_encoder;
GUID encoder;
bool has_decoder;
GUID decoder;
};
class Format {
public:
GUID class_id;
bool has_name;
AString name;
UInt32 signature_offset;
CObjectVector<CByteBuffer> signatures;
};
class CompressCodecsInfo :
public ICompressCodecsInfo,
public CMyUnknownImp {
public:
MY_UNKNOWN_IMP1(ICompressCodecsInfo)
STDMETHOD(GetNumMethods)(UInt32* numMethods);
STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT* value);
STDMETHOD(CreateDecoder)(UInt32 index, const GUID* interfaceID, void** coder);
STDMETHOD(CreateEncoder)(UInt32 index, const GUID* interfaceID, void** coder);
};
static bool initialized = false;
static void* handle = nullptr;
static CObjectVector<Method> methods;
static CObjectVector<Format> formats;
static CompressCodecsInfo compress_codecs_info;
HRESULT CompressCodecsInfo::GetNumMethods(UInt32 *numMethods) {
if (numMethods != nullptr) {
*numMethods = methods.Size();
}
return S_OK;
}
HRESULT CompressCodecsInfo::GetProperty(UInt32 index, PROPID propID, PROPVARIANT* value) {
Method& method = methods[index];
switch (propID) {
case NMethodPropID::kDecoderIsAssigned: {
NWindows::NCOM::CPropVariant propVariant;
propVariant = method.has_decoder;
propVariant.Detach(value);
return S_OK;
}
case NMethodPropID::kEncoderIsAssigned: {
NWindows::NCOM::CPropVariant propVariant;
propVariant = method.has_encoder;
propVariant.Detach(value);
return S_OK;
}
default: {
return GetMethodProperty(index, propID, value);
}
}
}
HRESULT CompressCodecsInfo::CreateDecoder(
UInt32 index,
const GUID* interfaceID,
void** coder
) {
Method& method = methods[index];
if (method.has_decoder) {
return CreateObject(&(method.decoder), interfaceID, coder);
} else {
return S_OK;
}
}
HRESULT CompressCodecsInfo::CreateEncoder(
UInt32 index,
const GUID* interfaceID,
void** coder
) {
Method& method = methods[index];
if (method.has_encoder) {
return CreateObject(&(method.encoder), interfaceID, coder);
} else {
return S_OK;
}
}
class ArchiveOpenCallback :
public IArchiveOpenCallback,
public ICryptoGetTextPassword,
public CMyUnknownImp {
public:
ArchiveOpenCallback(BSTR password) {
this->password = ::SysAllocString(password);
this->has_asked_password = false;
}
~ArchiveOpenCallback() {
::SysFreeString(password);
}
public:
MY_UNKNOWN_IMP2(IArchiveOpenCallback, ICryptoGetTextPassword)
INTERFACE_IArchiveOpenCallback({ return S_OK; });
STDMETHOD(CryptoGetTextPassword)(BSTR *password) {
has_asked_password = true;
*password = ::SysAllocString(this->password);
return this->password != nullptr ? S_OK : E_NO_PASSWORD;
}
HRESULT GetBetterResult(HRESULT result) {
if (result == S_OK) {
return S_OK;
} else if (has_asked_password) {
if (password != nullptr) {
return E_WRONG_PASSWORD;
} else {
return E_NO_PASSWORD;
}
} else {
return result;
}
}
private:
BSTR password;
bool has_asked_password;
};
class ArchiveOpenCallback2 :
public ArchiveOpenCallback,
public IArchiveOpenVolumeCallback {
public:
ArchiveOpenCallback2(
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& callback
):
ArchiveOpenCallback(password),
filename(filename),
callback(callback) { }
public:
MY_UNKNOWN_IMP3(
IArchiveOpenCallback,
IArchiveOpenVolumeCallback,
ICryptoGetTextPassword)
STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) {
NWindows::NCOM::CPropVariant prop;
if (propID == kpidName) {
prop = filename;
}
prop.Detach(value);
return S_OK;
}
STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) {
CMyComPtr<IInStream> stream = nullptr;
HRESULT result = callback->OpenVolume(name, stream);
*inStream = stream.Detach();
return result != S_OK ? S_FALSE : S_OK;
}
private:
UString filename;
CMyComPtr<OpenVolumeCallback> callback;
};
#define GET_PROP_METHOD(METHOD_NAME, PROP_TYPE, VALUE_TYPE, CONVERTER) \
HRESULT METHOD_NAME( \
GetPropertyFunc get_property, \
UInt32 index, \
PROPID prop_id, \
PROP_TYPE& value, \
bool& is_assigned \
) { \
NWindows::NCOM::CPropVariant prop; \
is_assigned = false; \
\
RETURN_SAME_IF_NOT_ZERO(get_property(index, prop_id, &prop)); \
\
if (prop.vt == VALUE_TYPE) { \
is_assigned = true; \
CONVERTER; \
} else if (prop.vt != VT_EMPTY) { \
return E_INCONSISTENT_PROP_TYPE; \
} \
\
return S_OK; \
}
GET_PROP_METHOD(GetAString, AString, VT_BSTR, value.SetFromWStr_if_Ascii(prop.bstrVal));
GET_PROP_METHOD(GetGUID, GUID, VT_BSTR, value = *reinterpret_cast<const GUID*>(prop.bstrVal));
GET_PROP_METHOD(GetCByteBuffer, CByteBuffer, VT_BSTR, value.CopyFrom((const Byte *)prop.bstrVal, ::SysStringByteLen(prop.bstrVal)));
GET_PROP_METHOD(GetUInt32, UInt32, VT_UI4, value = prop.ulVal);
#undef GET_PROP_METHOD
static HRESULT LoadMethods() {
UInt32 method_number = 0;
RETURN_SAME_IF_NOT_ZERO(GetNumberOfMethods(&method_number));
for(UInt32 i = 0; i < method_number; i++) {
Method& method = methods.AddNew();
# define GET_PROP(METHOD, PROP, VALUE, ASSIGNED) \
if (METHOD(GetMethodProperty, i, PROP, VALUE, ASSIGNED) != S_OK) { \
methods.DeleteBack(); \
continue; \
}
// It's ok to assume all characters in method name are in ascii charset
GET_PROP(GetAString, NMethodPropID::kName, method.name, method.has_name);
GET_PROP(GetGUID, NMethodPropID::kDecoder, method.decoder, method.has_decoder);
GET_PROP(GetGUID, NMethodPropID::kEncoder, method.encoder, method.has_encoder);
# undef GET_PROP
}
return S_OK;
}
static void AppendMultiSignature(
CObjectVector<CByteBuffer>& signatures,
const unsigned char* multi_signature,
size_t size
) {
while (size > 0) {
unsigned length = *multi_signature++;
size--;
if (length > size) {
return;
}
signatures.AddNew().CopyFrom(multi_signature, length);
multi_signature += length;
size -= length;
}
}
static HRESULT LoadFormats() {
UInt32 format_number = 0;
RETURN_SAME_IF_NOT_ZERO(GetNumberOfFormats(&format_number));
for(UInt32 i = 0; i < format_number; i++) {
Format& format = formats.AddNew();
# define GET_PROP(METHOD, PROP, VALUE, ASSIGNED) \
if (METHOD(GetHandlerProperty2, i, PROP, VALUE, ASSIGNED) != S_OK) { \
formats.DeleteBack(); \
continue; \
}
// ClassID is required
bool has_class_id;
GET_PROP(GetGUID, NArchive::NHandlerPropID::kClassID, format.class_id, has_class_id);
if (!has_class_id) {
formats.DeleteBack();
continue;
}
// It's ok to assume all characters in format name are in ascii charset
GET_PROP(GetAString, NArchive::NHandlerPropID::kName, format.name, format.has_name);
bool has_signature;
CByteBuffer signature;
GET_PROP(GetUInt32, NArchive::NHandlerPropID::kSignatureOffset, format.signature_offset, has_signature);
if (!has_signature) {
format.signature_offset = 0;
}
GET_PROP(GetCByteBuffer, NArchive::NHandlerPropID::kSignature, signature, has_signature);
if (has_signature) {
format.signatures.AddNew().CopyFrom(signature, signature.Size());
}
GET_PROP(GetCByteBuffer, NArchive::NHandlerPropID::kMultiSignature, signature, has_signature);
if (has_signature) {
AppendMultiSignature(format.signatures, signature, signature.Size());
}
# undef GET_PROP
}
return S_OK;
}
HRESULT SevenZip::Initialize() {
if (initialized) {
return S_OK;
}
RETURN_SAME_IF_NOT_ZERO(LoadMethods());
RETURN_SAME_IF_NOT_ZERO(LoadFormats());
initialized = true;
return S_OK;
}
static HRESULT ReadFully(CMyComPtr<IInStream>& stream, Byte* data, UInt32 size, UInt32* processedSize) {
UInt32 read = 0;
while (read < size) {
RETURN_SAME_IF_NOT_ZERO(stream->Read(data + read, size - read, processedSize));
if (*processedSize == 0) {
// EOF
break;
}
read += *processedSize;
}
*processedSize = read;
return S_OK;
}
static HRESULT OpenInArchive(
GUID& class_id,
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
CMyComPtr<IInArchive>& in_archive
) {
RETURN_SAME_IF_NOT_ZERO(CreateObject(&class_id, &IID_IInArchive, reinterpret_cast<void **>(&in_archive)));
UInt64 newPosition = 0;
HRESULT result = in_stream->Seek(0, STREAM_SEEK_SET, &newPosition);
if (result != S_OK) {
in_archive->Close();
in_archive = nullptr;
return result;
}
UInt64 maxCheckStartPosition = 1 << 22;
ArchiveOpenCallback* callback_ptr = nullptr;
if (filename != nullptr && open_volume_callback != nullptr) {
callback_ptr = new ArchiveOpenCallback2(password, filename, open_volume_callback);
} else {
callback_ptr = new ArchiveOpenCallback(password);
}
CMyComPtr<ArchiveOpenCallback> callback(callback_ptr);
result = in_archive->Open(in_stream, &maxCheckStartPosition, callback);
result = callback->GetBetterResult(result);
if (result != S_OK) {
in_archive->Close();
in_archive = nullptr;
return result;
}
return S_OK;
}
static HRESULT OpenInArchive(
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
CMyComPtr<IInArchive>& in_archive,
AString& format_name
) {
bool formats_checked[formats.Size()];
memset(formats_checked, 0, formats.Size() * sizeof(bool));
for (int i = 0; i < formats.Size(); i++) {
Format& format = formats[i];
// Skip format without signatures
if (format.signatures.Size() == 0) {
continue;
}
// Check each signature
for (int j = 0; j < format.signatures.Size(); j++) {
CByteBuffer& signature = format.signatures[j];
UInt32 processedSize;
CByteBuffer bytes(signature.Size());
UInt64 newPosition;
CONTINUE_IF_NOT_ZERO(in_stream->Seek(format.signature_offset, STREAM_SEEK_SET, &newPosition));
if (newPosition != format.signature_offset) continue;
CONTINUE_IF_NOT_ZERO(ReadFully(in_stream, bytes, static_cast<UInt32>(signature.Size()), &processedSize));
if (processedSize != signature.Size() || bytes != signature) continue;
// The signature matched, try to open it
HRESULT result = OpenInArchive(format.class_id, in_stream, password, filename, open_volume_callback, in_archive);
// Mark the format
formats_checked[i] = true;
if (result == S_OK) {
format_name = format.name;
return S_OK;
}
if (result == E_NO_PASSWORD || result == E_WRONG_PASSWORD) {
// It's a password error, the archive format is confirmed
return result;
}
// Can't open archive in this format
// Skip this format
break;
}
}
// Try other unchecked formats
for (int i = 0; i < formats.Size(); i++) {
if (!formats_checked[i]) {
Format& format = formats[i];
HRESULT result = OpenInArchive(format.class_id, in_stream, password, filename, open_volume_callback, in_archive);
if (result == S_OK) {
format_name = format.name;
return S_OK;
}
if (result == E_NO_PASSWORD || result == E_WRONG_PASSWORD) {
// It's a password error, the archive format is confirmed
return result;
}
}
}
return E_UNKNOWN_FORMAT;
}
HRESULT SevenZip::OpenArchive(
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
InArchive** archive
) {
HRESULT result = S_FALSE;
InArchive* previous_archive = nullptr;
CMyComPtr<IInStream> arg_in_stream = in_stream;
BSTR arg_password = password;
BSTR arg_filename = filename;
CMyComPtr<OpenVolumeCallback> arg_open_volume_callback = open_volume_callback;
while (true) {
CMyComPtr<IInArchive> in_archive = nullptr;
AString format_name;
result = OpenInArchive(
arg_in_stream,
arg_password,
arg_filename,
arg_open_volume_callback,
in_archive,
format_name
);
if (result != S_OK || in_archive == nullptr) {
if (in_archive != nullptr) {
in_archive->Close();
in_archive = nullptr;
}
if (result == S_OK) {
result = E_INTERNAL;
}
break;
}
previous_archive = new InArchive(previous_archive, in_archive, format_name);
// Break if it has no entry or more than one entry
UInt32 number = 0;
previous_archive->GetNumberOfEntries(number);
if (number != 1) {
break;
}
// Break if it's not IInArchiveGetStream
CMyComPtr<IInArchiveGetStream> archive_get_stream;
in_archive->QueryInterface(IID_IInArchiveGetStream, (void**)&archive_get_stream);
if (archive_get_stream == nullptr) {
break;
}
// Break if GetStream returns null
CMyComPtr<ISequentialInStream> sequential_in_stream;
archive_get_stream->GetStream(0, &sequential_in_stream);
if (sequential_in_stream == nullptr) {
break;
}
// Break if the stream is not IInStream
arg_in_stream = nullptr;
sequential_in_stream->QueryInterface(IID_IInStream, (void**)&arg_in_stream);
if (arg_in_stream == nullptr) {
break;
}
// Clear arguments for the first archive
arg_password = nullptr;
arg_filename = nullptr;
arg_open_volume_callback = nullptr;
}
*archive = previous_archive;
return *archive != nullptr ? S_OK : result;
}
@@ -1,47 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_P7ZIP_H__
#define __A7ZIP_P7ZIP_H__
#include <jni.h>
#include <include_windows/windows.h>
#include <Common/MyBuffer.h>
#include <Common/MyCom.h>
#include <Common/MyString.h>
#include <7zip/ICoder.h>
#include "InArchive.h"
#include "OpenVolumeCallback.h"
namespace a7zip {
namespace SevenZip {
HRESULT Initialize();
HRESULT OpenArchive(
CMyComPtr<IInStream>& stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& callback,
InArchive** archive
);
}
}
#endif //__A7ZIP_P7ZIP_H__
-95
View File
@@ -1,95 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_UTILS_H__
#define __A7ZIP_UTILS_H__
#define DEFAULT_BUFFER_SIZE (4 * 1024)
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define E_NOT_INITIALIZED ((HRESULT)0x82210000L)
#define E_INTERNAL ((HRESULT)0x82210001L)
#define E_UNKNOWN_ERROR ((HRESULT)0x82210002L)
#define E_CLASS_NOT_FOUND ((HRESULT)0x82220000L)
#define E_METHOD_NOT_FOUND ((HRESULT)0x82220001L)
#define E_JAVA_EXCEPTION ((HRESULT)0x82220002L)
#define E_FAILED_CONSTRUCT ((HRESULT)0x82220003L)
#define E_FAILED_REGISTER ((HRESULT)0x82220004L)
#define E_FAILED_UNREGISTER ((HRESULT)0x82220005L)
#define E_INCONSISTENT_PROP_TYPE ((HRESULT)0x82240000L)
#define E_EMPTY_PROP ((HRESULT)0x82240001L)
#define E_UNKNOWN_FORMAT ((HRESULT)0x82240002L)
#define E_UNSUPPORTED_EXTRACT_MODE ((HRESULT)0x82240003L)
#define E_NO_OUT_STREAM ((HRESULT)0x82240004L)
#define E_UNSUPPORTED_METHOD ((HRESULT)0x82250000L)
#define E_DATA_ERROR ((HRESULT)0x82250001L)
#define E_DATA_ERROR_ENCRYPTED ((HRESULT)0x82250002L)
#define E_CRC_ERROR ((HRESULT)0x82250003L)
#define E_CRC_ERROR_ENCRYPTED ((HRESULT)0x82250004L)
#define E_UNAVAILABLE ((HRESULT)0x82250005L)
#define E_UNEXPECTED_END ((HRESULT)0x82250006L)
#define E_DATA_AFTER_END ((HRESULT)0x82250007L)
#define E_IS_NOT_ARC ((HRESULT)0x82250008L)
#define E_HEADERS_ERROR ((HRESULT)0x82250009L)
#define E_WRONG_PASSWORD ((HRESULT)0x82250010L)
#define E_NO_PASSWORD ((HRESULT)0x82250011L)
#define CLEAR_IF_EXCEPTION_PENDING(ENV) \
do { \
if ((ENV)->ExceptionCheck()) { \
(ENV)->ExceptionDescribe(); \
(ENV)->ExceptionClear(); \
} \
} while (0)
#define RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(ENV) \
do { \
if ((ENV)->ExceptionCheck()) { \
(ENV)->ExceptionDescribe(); \
(ENV)->ExceptionClear(); \
return E_JAVA_EXCEPTION; \
} \
} while (0)
#define RETURN_JNI_ERR_IF_NOT_ZERO(VALUE) \
do { \
if ((VALUE) != 0) { \
return JNI_ERR; \
} \
} while (0)
#define RETURN_SAME_IF_NOT_ZERO(VALUE) \
do { \
auto __temp__ = (VALUE); \
if (__temp__ != 0) { \
return __temp__; \
} \
} while (0)
#define CONTINUE_IF_NOT_ZERO(VALUE) \
do { \
if ((VALUE) != 0) { \
continue; \
} \
} while (0)
#endif //__A7ZIP_UTILS_H__
@@ -1,240 +0,0 @@
cmake_minimum_required(VERSION 3.4.1)
project(p7zip C CXX)
set(P_SEVEN_ZIP_EXTRACT_LITE_SOURCES
p7zip/CPP/7zip/Archive/7z/7zDecode.cpp
p7zip/CPP/7zip/Archive/7z/7zExtract.cpp
p7zip/CPP/7zip/Archive/7z/7zHandler.cpp
p7zip/CPP/7zip/Archive/7z/7zHeader.cpp
p7zip/CPP/7zip/Archive/7z/7zIn.cpp
p7zip/CPP/7zip/Archive/7z/7zProperties.cpp
p7zip/CPP/7zip/Archive/7z/7zRegister.cpp
p7zip/CPP/7zip/Archive/Common/CoderMixer2.cpp
p7zip/CPP/7zip/Archive/Common/FindSignature.cpp
p7zip/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
p7zip/CPP/7zip/Archive/Common/ItemNameUtils.cpp
p7zip/CPP/7zip/Archive/Common/MultiStream.cpp
p7zip/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
p7zip/CPP/7zip/Archive/Rar/RarHandler.cpp
p7zip/CPP/7zip/Archive/Rar/Rar5Handler.cpp
p7zip/CPP/7zip/Archive/Zip/ZipHandler.cpp
p7zip/CPP/7zip/Archive/Zip/ZipIn.cpp
p7zip/CPP/7zip/Archive/Zip/ZipItem.cpp
p7zip/CPP/7zip/Archive/Zip/ZipRegister.cpp
p7zip/CPP/7zip/Archive/ArchiveExports.cpp
p7zip/CPP/7zip/Archive/DllExports2.cpp
p7zip/CPP/7zip/Archive/HandlerCont.cpp
p7zip/CPP/7zip/Archive/SplitHandler.cpp
p7zip/CPP/7zip/Archive/XzHandler.cpp
p7zip/CPP/7zip/Common/CWrappers.cpp
p7zip/CPP/7zip/Common/CreateCoder.cpp
p7zip/CPP/7zip/Common/FilterCoder.cpp
p7zip/CPP/7zip/Common/InBuffer.cpp
p7zip/CPP/7zip/Common/LimitedStreams.cpp
p7zip/CPP/7zip/Common/OutBuffer.cpp
p7zip/CPP/7zip/Common/ProgressUtils.cpp
p7zip/CPP/7zip/Common/PropId.cpp
p7zip/CPP/7zip/Common/StreamObjects.cpp
p7zip/CPP/7zip/Common/StreamUtils.cpp
p7zip/CPP/7zip/Compress/Bcj2Coder.cpp
p7zip/CPP/7zip/Compress/Bcj2Register.cpp
p7zip/CPP/7zip/Compress/BcjCoder.cpp
p7zip/CPP/7zip/Compress/BcjRegister.cpp
p7zip/CPP/7zip/Compress/BitlDecoder.cpp
p7zip/CPP/7zip/Compress/BranchMisc.cpp
p7zip/CPP/7zip/Compress/BranchRegister.cpp
p7zip/CPP/7zip/Compress/ByteSwap.cpp
p7zip/CPP/7zip/Compress/BZip2Crc.cpp
p7zip/CPP/7zip/Compress/BZip2Decoder.cpp
p7zip/CPP/7zip/Compress/BZip2Register.cpp
p7zip/CPP/7zip/Compress/CodecExports.cpp
p7zip/CPP/7zip/Compress/CopyCoder.cpp
p7zip/CPP/7zip/Compress/CopyRegister.cpp
p7zip/CPP/7zip/Compress/Deflate64Register.cpp
p7zip/CPP/7zip/Compress/DeflateDecoder.cpp
p7zip/CPP/7zip/Compress/DeflateRegister.cpp
p7zip/CPP/7zip/Compress/DeltaFilter.cpp
p7zip/CPP/7zip/Compress/ImplodeDecoder.cpp
p7zip/CPP/7zip/Compress/ImplodeHuffmanDecoder.cpp
p7zip/CPP/7zip/Compress/Lzma2Decoder.cpp
p7zip/CPP/7zip/Compress/Lzma2Register.cpp
p7zip/CPP/7zip/Compress/LzmaDecoder.cpp
p7zip/CPP/7zip/Compress/LzmaRegister.cpp
p7zip/CPP/7zip/Compress/LzOutWindow.cpp
p7zip/CPP/7zip/Compress/PpmdDecoder.cpp
p7zip/CPP/7zip/Compress/PpmdRegister.cpp
p7zip/CPP/7zip/Compress/PpmdZip.cpp
p7zip/CPP/7zip/Compress/Rar1Decoder.cpp
p7zip/CPP/7zip/Compress/Rar2Decoder.cpp
p7zip/CPP/7zip/Compress/Rar3Decoder.cpp
p7zip/CPP/7zip/Compress/Rar3Vm.cpp
p7zip/CPP/7zip/Compress/Rar5Decoder.cpp
p7zip/CPP/7zip/Compress/RarCodecsRegister.cpp
p7zip/CPP/7zip/Compress/ShrinkDecoder.cpp
p7zip/CPP/7zip/Crypto/7zAes.cpp
p7zip/CPP/7zip/Crypto/7zAesRegister.cpp
p7zip/CPP/7zip/Crypto/HmacSha1.cpp
p7zip/CPP/7zip/Crypto/HmacSha256.cpp
p7zip/CPP/7zip/Crypto/MyAes.cpp
p7zip/CPP/7zip/Crypto/MyAesReg.cpp
p7zip/CPP/7zip/Crypto/Pbkdf2HmacSha1.cpp
p7zip/CPP/7zip/Crypto/RandGen.cpp
p7zip/CPP/7zip/Crypto/Rar20Crypto.cpp
p7zip/CPP/7zip/Crypto/Rar5Aes.cpp
p7zip/CPP/7zip/Crypto/RarAes.cpp
p7zip/CPP/7zip/Crypto/WzAes.cpp
p7zip/CPP/7zip/Crypto/ZipCrypto.cpp
p7zip/CPP/7zip/Crypto/ZipStrong.cpp
p7zip/CPP/Common/CRC.cpp
p7zip/CPP/Common/CrcReg.cpp
p7zip/CPP/Common/IntToString.cpp
p7zip/CPP/Common/MyString.cpp
p7zip/CPP/Common/MyVector.cpp
p7zip/CPP/Common/MyWindows.cpp
p7zip/CPP/Common/Sha1Reg.cpp
p7zip/CPP/Common/Sha256Reg.cpp
p7zip/CPP/Common/StringConvert.cpp
p7zip/CPP/Common/StringToInt.cpp
p7zip/CPP/Common/UTFConvert.cpp
p7zip/CPP/Common/Wildcard.cpp
p7zip/CPP/Common/XzCrc64Reg.cpp
p7zip/CPP/Windows/PropVariant.cpp
p7zip/CPP/Windows/PropVariantUtils.cpp
p7zip/CPP/Windows/System.cpp
p7zip/CPP/Windows/TimeUtils.cpp
p7zip/CPP/myWindows/wine_date_and_time.cpp
p7zip/C/7zCrc.c
p7zip/C/7zCrcOpt.c
p7zip/C/7zStream.c
p7zip/C/Aes.c
p7zip/C/Alloc.c
p7zip/C/Bcj2.c
p7zip/C/Blake2s.c
p7zip/C/Bra.c
p7zip/C/Bra86.c
p7zip/C/BraIA64.c
p7zip/C/CpuArch.c
p7zip/C/Delta.c
p7zip/C/Lzma2Dec.c
p7zip/C/LzmaDec.c
p7zip/C/Ppmd7.c
p7zip/C/Ppmd7Dec.c
p7zip/C/Ppmd8.c
p7zip/C/Ppmd8Dec.c
p7zip/C/Sha1.c
p7zip/C/Sha256.c
p7zip/C/Xz.c
p7zip/C/XzCrc64.c
p7zip/C/XzCrc64Opt.c
p7zip/C/XzDec.c
p7zip/C/XzIn.c
)
set(P_SEVEN_ZIP_EXTRACT_SOURCES
p7zip/CPP/7zip/Archive/Cab/CabBlockInStream.cpp
p7zip/CPP/7zip/Archive/Cab/CabHandler.cpp
p7zip/CPP/7zip/Archive/Cab/CabHeader.cpp
p7zip/CPP/7zip/Archive/Cab/CabIn.cpp
p7zip/CPP/7zip/Archive/Cab/CabRegister.cpp
p7zip/CPP/7zip/Archive/Chm/ChmHandler.cpp
p7zip/CPP/7zip/Archive/Chm/ChmIn.cpp
p7zip/CPP/7zip/Archive/Common/DummyOutStream.cpp
p7zip/CPP/7zip/Archive/Common/OutStreamWithSha1.cpp
p7zip/CPP/7zip/Archive/Iso/IsoHandler.cpp
p7zip/CPP/7zip/Archive/Iso/IsoHeader.cpp
p7zip/CPP/7zip/Archive/Iso/IsoIn.cpp
p7zip/CPP/7zip/Archive/Iso/IsoRegister.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisDecode.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisHandler.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisIn.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisRegister.cpp
p7zip/CPP/7zip/Archive/Tar/TarHandler.cpp
p7zip/CPP/7zip/Archive/Tar/TarHeader.cpp
p7zip/CPP/7zip/Archive/Tar/TarIn.cpp
p7zip/CPP/7zip/Archive/Tar/TarRegister.cpp
p7zip/CPP/7zip/Archive/Udf/UdfHandler.cpp
p7zip/CPP/7zip/Archive/Udf/UdfIn.cpp
p7zip/CPP/7zip/Archive/Wim/WimHandler.cpp
p7zip/CPP/7zip/Archive/Wim/WimIn.cpp
p7zip/CPP/7zip/Archive/Wim/WimRegister.cpp
p7zip/CPP/7zip/Archive/ApmHandler.cpp
p7zip/CPP/7zip/Archive/ArHandler.cpp
p7zip/CPP/7zip/Archive/ArjHandler.cpp
p7zip/CPP/7zip/Archive/Bz2Handler.cpp
p7zip/CPP/7zip/Archive/ComHandler.cpp
p7zip/CPP/7zip/Archive/CpioHandler.cpp
p7zip/CPP/7zip/Archive/CramfsHandler.cpp
p7zip/CPP/7zip/Archive/DmgHandler.cpp
p7zip/CPP/7zip/Archive/ElfHandler.cpp
p7zip/CPP/7zip/Archive/ExtHandler.cpp
p7zip/CPP/7zip/Archive/FatHandler.cpp
p7zip/CPP/7zip/Archive/FlvHandler.cpp
p7zip/CPP/7zip/Archive/GptHandler.cpp
p7zip/CPP/7zip/Archive/GzHandler.cpp
p7zip/CPP/7zip/Archive/HfsHandler.cpp
p7zip/CPP/7zip/Archive/IhexHandler.cpp
p7zip/CPP/7zip/Archive/LzhHandler.cpp
p7zip/CPP/7zip/Archive/LzmaHandler.cpp
p7zip/CPP/7zip/Archive/MachoHandler.cpp
p7zip/CPP/7zip/Archive/MbrHandler.cpp
p7zip/CPP/7zip/Archive/MslzHandler.cpp
p7zip/CPP/7zip/Archive/MubHandler.cpp
p7zip/CPP/7zip/Archive/NtfsHandler.cpp
p7zip/CPP/7zip/Archive/PeHandler.cpp
p7zip/CPP/7zip/Archive/PpmdHandler.cpp
p7zip/CPP/7zip/Archive/QcowHandler.cpp
p7zip/CPP/7zip/Archive/RpmHandler.cpp
p7zip/CPP/7zip/Archive/SquashfsHandler.cpp
p7zip/CPP/7zip/Archive/SwfHandler.cpp
p7zip/CPP/7zip/Archive/UefiHandler.cpp
p7zip/CPP/7zip/Archive/VdiHandler.cpp
p7zip/CPP/7zip/Archive/VhdHandler.cpp
p7zip/CPP/7zip/Archive/VmdkHandler.cpp
p7zip/CPP/7zip/Archive/XarHandler.cpp
p7zip/CPP/7zip/Archive/ZHandler.cpp
p7zip/CPP/7zip/Compress/BZip2Decoder.cpp
p7zip/CPP/7zip/Compress/BZip2Register.cpp
p7zip/CPP/7zip/Compress/LzhDecoder.cpp
p7zip/CPP/7zip/Compress/LzmsDecoder.cpp
p7zip/CPP/7zip/Compress/LzxDecoder.cpp
p7zip/CPP/7zip/Compress/QuantumDecoder.cpp
p7zip/CPP/7zip/Compress/XpressDecoder.cpp
p7zip/CPP/7zip/Compress/ZDecoder.cpp
p7zip/CPP/7zip/Compress/ZlibDecoder.cpp
p7zip/CPP/Common/MyMap.cpp
p7zip/CPP/Common/MyXml.cpp
p7zip/C/HuffEnc.c
p7zip/C/Sort.c
)
set(P_SEVEN_ZIP_INCLUDES
p7zip/C
p7zip/CPP
p7zip/CPP/Common
p7zip/CPP/include_windows
p7zip/CPP/myWindows
p7zip/CPP/Windows
)
set(P_SEVEN_ZIP_EXTRACT_FLAGS
"-DEXTRACT_ONLY -D_7ZIP_ST"
)
set(P_SEVEN_ZIP_COMMON_FLAGS
"-DANDROID_NDK -DNDEBUG -D_REENTRANT -DENV_UNIX -DEXTERNAL_CODECS -DUNICODE -D_UNICODE -DUNIX_USE_WIN_FILE"
)
set(P_SEVEN_ZIP_SOURCES ${P_SEVEN_ZIP_EXTRACT_LITE_SOURCES})
set(P_SEVEN_ZIP_FLAGS "${P_SEVEN_ZIP_COMMON_FLAGS} ${P_SEVEN_ZIP_EXTRACT_FLAGS}")
set(P_SEVEN_ZIP_NAME "p7zip")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${P_SEVEN_ZIP_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${P_SEVEN_ZIP_FLAGS}")
add_library(p7zip SHARED ${P_SEVEN_ZIP_SOURCES})
target_include_directories(p7zip PUBLIC ${P_SEVEN_ZIP_INCLUDES})
set_target_properties(p7zip PROPERTIES OUTPUT_NAME ${P_SEVEN_ZIP_NAME})
target_compile_options(p7zip PRIVATE -fvisibility=default)
+170
View File
@@ -0,0 +1,170 @@
//
// Created by tarsin on 2022/5/21.
//
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <android/log.h>
#include <archive.h>
#include <archive_entry.h>
#define LOG_TAG "libarchive"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
#define BLOCK_SIZE 4096
#define READ_DATA_ERR 3
typedef struct archive_inst_sc {
struct archive *archive;
int entry_num;
JNIEnv *env;
jobject file;
jmethodID readID;
jmethodID seekID;
jmethodID skipID;
void* read_buffer;
void* output_buffer;
jbyteArray jbr;
} archive_inst;
static int filename_is_playable_file(const char* name)
{
const char* dotptr = strrchr(name, '.');
if (dotptr++) {
switch (*dotptr) {
case 'b':
return strcmp(dotptr, "bmp") == 0;
case 'j':
return (strcmp(dotptr, "jpg") == 0) || (strcmp(dotptr, "jpeg") == 0);
case 'p':
return strcmp(dotptr, "png") == 0;
case 'g':
return strcmp(dotptr, "gif") == 0;
case 'w':
return strcmp(dotptr, "webp") == 0;
default:
return 0;
}
}
return 0;
}
static void archive_list_all_entries(struct archive_inst_sc *inst)
{
struct archive *a = inst->archive;
struct archive_entry *entry;
int count = 0;
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
if(filename_is_playable_file(archive_entry_pathname(entry)))
count++;
}
inst->entry_num = count;
}
ssize_t ins_read(struct archive *a, void* client_data_ptr, const void **buff)
{
archive_inst *arc = client_data_ptr;
int32_t r = (*arc->env)->CallIntMethod(arc->env, arc->file, arc->readID, arc->jbr);
(*arc->env)->GetByteArrayRegion(arc->env, arc->jbr, 0, r, arc->read_buffer);
*buff = arc->read_buffer;
return r;
}
int ins_close(struct archive *a, void* client_data)
{
archive_inst *arc = client_data;
JNIEnv *env = arc->env;
jmethodID rewind = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, arc->file), "rewind", "()V");
(*arc->env)->CallVoidMethod(arc->env, arc->file, rewind);
return ARCHIVE_OK;
}
la_int64_t ins_seek(struct archive *a, void *client_data, la_int64_t offset, int whence)
{
archive_inst *arc = client_data;
jlong ret = (*arc->env)->CallLongMethod(arc->env, arc->file, arc->seekID, offset, whence);
return ret;
}
static archive_inst *archive_create_inst(JNIEnv *env, jobject file)
{
archive_inst *inst = malloc(sizeof(archive_inst));
inst->archive = archive_read_new();
archive_read_set_seek_callback(inst->archive, ins_seek);
archive_read_support_format_all(inst->archive);
archive_read_support_filter_all(inst->archive);
inst->env = env;
inst->file = file;
inst->readID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "read", "([B)I");
inst->seekID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "seek", "(JI)J");
inst->jbr = (*env)->NewByteArray(env, BLOCK_SIZE);
inst->read_buffer = malloc(BLOCK_SIZE);
inst->output_buffer = malloc(BLOCK_SIZE);
return inst;
}
static void archive_destroy_inst(archive_inst* inst)
{
JNIEnv *env = inst->env;
archive_read_close(inst->archive);
(*env)->DeleteLocalRef(env, inst->jbr);
free(inst->read_buffer);
free(inst->output_buffer);
free(inst);
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jobject osf) {
int r;
archive_inst *arc = archive_create_inst(env, osf);
r = archive_read_open(arc->archive, arc, NULL, ins_read, ins_close);
if (r) {
r = 0;
} else {
archive_list_all_entries(arc);
r = arc->entry_num;
}
archive_destroy_inst(arc);
return r;
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_extracttoOutputStream(JNIEnv *env, jobject thiz, jobject osf,
jint index, jobject os) {
int count = 0;
struct archive_entry *entry;
archive_inst *arc = archive_create_inst(env, osf);
int32_t size;
jmethodID writeID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, os), "write", "([B)V");
jmethodID flushID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, os), "flush", "()V");
archive_read_open(arc->archive, arc, NULL, ins_read, ins_close);
while (archive_read_next_header(arc->archive, &entry) == ARCHIVE_OK) {
if(!filename_is_playable_file(archive_entry_pathname(entry)))
continue;
if (count == index) {
for (;;) {
size = (int32_t)archive_read_data(arc->archive, arc->output_buffer, BLOCK_SIZE);
if (size < 0) {
return READ_DATA_ERR;
}
if (size == 0)
break;
(*arc->env)->SetByteArrayRegion(env, arc->jbr, 0, size, arc->output_buffer);
(*arc->env)->CallVoidMethod(env, os, writeID, arc->jbr);
(*arc->env)->CallVoidMethod(env, os, flushID);
}
break;
}
count++;
}
archive_destroy_inst(arc);
return 0;
}
-2
View File
@@ -14,8 +14,6 @@
cmake_minimum_required(VERSION 3.4.1)
include(AndroidNdkModules)
android_ndk_import_module_cpufeatures()
add_subdirectory(image)
add_subdirectory(stream)
add_subdirectory(libjpeg-turbo)
@@ -16,11 +16,11 @@ cmake_minimum_required(VERSION 3.4.1)
project(giflib C)
set(GIFLIB_SOURCES
giflib/dgif_lib.c
giflib/gif_hash.c
giflib/gifalloc.c
giflib/openbsd-reallocarray.c
)
giflib/dgif_lib.c
giflib/gif_hash.c
giflib/gifalloc.c
giflib/openbsd-reallocarray.c
)
set(GIFLIB_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/giflib)
add_library(giflib STATIC ${GIFLIB_SOURCES})
+20 -20
View File
@@ -16,28 +16,28 @@ cmake_minimum_required(VERSION 3.4.1)
project(image C)
set(IMAGE_SOURCES
image.c
image_utils.c
image_plain.c
image_jpeg.c
image_png.c
image_gif.c
filter/clahe.c
filter/gray.c
java_wrapper.c
)
image.c
image_utils.c
image_plain.c
image_jpeg.c
image_png.c
image_gif.c
filter/clahe.c
filter/gray.c
java_wrapper.c
)
set(IMAGE_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
)
${CMAKE_CURRENT_SOURCE_DIR}
)
set(IMAGE_LIBRARIES
log
jnigraphics
GLESv2
stream
libjpeg-turbo
libpng
giflib
)
log
jnigraphics
GLESv2
stream
libjpeg-turbo
libpng
giflib
)
add_library(image STATIC ${IMAGE_SOURCES})
target_include_directories(image PRIVATE ${IMAGE_INCLUDES})
@@ -16,194 +16,194 @@ cmake_minimum_required(VERSION 3.4.1)
project(libjpeg-turbo C)
set(LIBJPEG_TURBO_SOURCES
libjpeg-turbo/jaricom.c
libjpeg-turbo/jcomapi.c
libjpeg-turbo/jdapimin.c
libjpeg-turbo/jdapistd.c
libjpeg-turbo/jdarith.c
libjpeg-turbo/jdatasrc.c
libjpeg-turbo/jdatasrc-tj.c
libjpeg-turbo/jdcoefct.c
libjpeg-turbo/jdcolor.c
libjpeg-turbo/jddctmgr.c
libjpeg-turbo/jdhuff.c
libjpeg-turbo/jdinput.c
libjpeg-turbo/jdmainct.c
libjpeg-turbo/jdmarker.c
libjpeg-turbo/jdmaster.c
libjpeg-turbo/jdmerge.c
libjpeg-turbo/jdphuff.c
libjpeg-turbo/jdpostct.c
libjpeg-turbo/jdsample.c
libjpeg-turbo/jdtrans.c
libjpeg-turbo/jerror.c
libjpeg-turbo/jfdctflt.c
libjpeg-turbo/jfdctfst.c
libjpeg-turbo/jfdctint.c
libjpeg-turbo/jidctflt.c
libjpeg-turbo/jidctfst.c
libjpeg-turbo/jidctint.c
libjpeg-turbo/jidctred.c
libjpeg-turbo/jmemmgr.c
libjpeg-turbo/jmemnobs.c
libjpeg-turbo/jquant1.c
libjpeg-turbo/jquant2.c
libjpeg-turbo/jutils.c
)
libjpeg-turbo/jaricom.c
libjpeg-turbo/jcomapi.c
libjpeg-turbo/jdapimin.c
libjpeg-turbo/jdapistd.c
libjpeg-turbo/jdarith.c
libjpeg-turbo/jdatasrc.c
libjpeg-turbo/jdatasrc-tj.c
libjpeg-turbo/jdcoefct.c
libjpeg-turbo/jdcolor.c
libjpeg-turbo/jddctmgr.c
libjpeg-turbo/jdhuff.c
libjpeg-turbo/jdinput.c
libjpeg-turbo/jdmainct.c
libjpeg-turbo/jdmarker.c
libjpeg-turbo/jdmaster.c
libjpeg-turbo/jdmerge.c
libjpeg-turbo/jdphuff.c
libjpeg-turbo/jdpostct.c
libjpeg-turbo/jdsample.c
libjpeg-turbo/jdtrans.c
libjpeg-turbo/jerror.c
libjpeg-turbo/jfdctflt.c
libjpeg-turbo/jfdctfst.c
libjpeg-turbo/jfdctint.c
libjpeg-turbo/jidctflt.c
libjpeg-turbo/jidctfst.c
libjpeg-turbo/jidctint.c
libjpeg-turbo/jidctred.c
libjpeg-turbo/jmemmgr.c
libjpeg-turbo/jmemnobs.c
libjpeg-turbo/jquant1.c
libjpeg-turbo/jquant2.c
libjpeg-turbo/jutils.c
)
set(LIBJPEG_TURBO_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo
)
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo
)
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
enable_language(ASM)
enable_language(ASM)
add_definitions(-DNEON_INTRINSICS)
add_definitions(-DNEON_INTRINSICS)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch32/jsimd.c
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch32/jsimd.c
)
elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
enable_language(ASM)
enable_language(ASM)
add_definitions(-DNEON_INTRINSICS)
add_definitions(-DNEON_INTRINSICS)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch64/jsimd.c
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch64/jsimd.c
)
elseif (${ANDROID_ABI} STREQUAL "x86")
enable_language(ASM_NASM)
enable_language(ASM_NASM)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/i386/jsimd.c
libjpeg-turbo/simd/i386/jsimdcpu.asm
libjpeg-turbo/simd/i386/jfdctflt-3dn.asm
libjpeg-turbo/simd/i386/jidctflt-3dn.asm
libjpeg-turbo/simd/i386/jquant-3dn.asm
libjpeg-turbo/simd/i386/jccolor-mmx.asm
libjpeg-turbo/simd/i386/jcgray-mmx.asm
libjpeg-turbo/simd/i386/jcsample-mmx.asm
libjpeg-turbo/simd/i386/jdcolor-mmx.asm
libjpeg-turbo/simd/i386/jdmerge-mmx.asm
libjpeg-turbo/simd/i386/jdsample-mmx.asm
libjpeg-turbo/simd/i386/jfdctfst-mmx.asm
libjpeg-turbo/simd/i386/jfdctint-mmx.asm
libjpeg-turbo/simd/i386/jidctfst-mmx.asm
libjpeg-turbo/simd/i386/jidctint-mmx.asm
libjpeg-turbo/simd/i386/jidctred-mmx.asm
libjpeg-turbo/simd/i386/jquant-mmx.asm
libjpeg-turbo/simd/i386/jfdctflt-sse.asm
libjpeg-turbo/simd/i386/jidctflt-sse.asm
libjpeg-turbo/simd/i386/jquant-sse.asm
libjpeg-turbo/simd/i386/jccolor-sse2.asm
libjpeg-turbo/simd/i386/jcgray-sse2.asm
libjpeg-turbo/simd/i386/jchuff-sse2.asm
libjpeg-turbo/simd/i386/jcphuff-sse2.asm
libjpeg-turbo/simd/i386/jcsample-sse2.asm
libjpeg-turbo/simd/i386/jdcolor-sse2.asm
libjpeg-turbo/simd/i386/jdmerge-sse2.asm
libjpeg-turbo/simd/i386/jdsample-sse2.asm
libjpeg-turbo/simd/i386/jfdctfst-sse2.asm
libjpeg-turbo/simd/i386/jfdctint-sse2.asm
libjpeg-turbo/simd/i386/jidctflt-sse2.asm
libjpeg-turbo/simd/i386/jidctfst-sse2.asm
libjpeg-turbo/simd/i386/jidctint-sse2.asm
libjpeg-turbo/simd/i386/jidctred-sse2.asm
libjpeg-turbo/simd/i386/jquantf-sse2.asm
libjpeg-turbo/simd/i386/jquanti-sse2.asm
libjpeg-turbo/simd/i386/jccolor-avx2.asm
libjpeg-turbo/simd/i386/jcgray-avx2.asm
libjpeg-turbo/simd/i386/jcsample-avx2.asm
libjpeg-turbo/simd/i386/jdcolor-avx2.asm
libjpeg-turbo/simd/i386/jdmerge-avx2.asm
libjpeg-turbo/simd/i386/jdsample-avx2.asm
libjpeg-turbo/simd/i386/jfdctint-avx2.asm
libjpeg-turbo/simd/i386/jidctint-avx2.asm
libjpeg-turbo/simd/i386/jquanti-avx2.asm
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/i386/jsimd.c
libjpeg-turbo/simd/i386/jsimdcpu.asm
libjpeg-turbo/simd/i386/jfdctflt-3dn.asm
libjpeg-turbo/simd/i386/jidctflt-3dn.asm
libjpeg-turbo/simd/i386/jquant-3dn.asm
libjpeg-turbo/simd/i386/jccolor-mmx.asm
libjpeg-turbo/simd/i386/jcgray-mmx.asm
libjpeg-turbo/simd/i386/jcsample-mmx.asm
libjpeg-turbo/simd/i386/jdcolor-mmx.asm
libjpeg-turbo/simd/i386/jdmerge-mmx.asm
libjpeg-turbo/simd/i386/jdsample-mmx.asm
libjpeg-turbo/simd/i386/jfdctfst-mmx.asm
libjpeg-turbo/simd/i386/jfdctint-mmx.asm
libjpeg-turbo/simd/i386/jidctfst-mmx.asm
libjpeg-turbo/simd/i386/jidctint-mmx.asm
libjpeg-turbo/simd/i386/jidctred-mmx.asm
libjpeg-turbo/simd/i386/jquant-mmx.asm
libjpeg-turbo/simd/i386/jfdctflt-sse.asm
libjpeg-turbo/simd/i386/jidctflt-sse.asm
libjpeg-turbo/simd/i386/jquant-sse.asm
libjpeg-turbo/simd/i386/jccolor-sse2.asm
libjpeg-turbo/simd/i386/jcgray-sse2.asm
libjpeg-turbo/simd/i386/jchuff-sse2.asm
libjpeg-turbo/simd/i386/jcphuff-sse2.asm
libjpeg-turbo/simd/i386/jcsample-sse2.asm
libjpeg-turbo/simd/i386/jdcolor-sse2.asm
libjpeg-turbo/simd/i386/jdmerge-sse2.asm
libjpeg-turbo/simd/i386/jdsample-sse2.asm
libjpeg-turbo/simd/i386/jfdctfst-sse2.asm
libjpeg-turbo/simd/i386/jfdctint-sse2.asm
libjpeg-turbo/simd/i386/jidctflt-sse2.asm
libjpeg-turbo/simd/i386/jidctfst-sse2.asm
libjpeg-turbo/simd/i386/jidctint-sse2.asm
libjpeg-turbo/simd/i386/jidctred-sse2.asm
libjpeg-turbo/simd/i386/jquantf-sse2.asm
libjpeg-turbo/simd/i386/jquanti-sse2.asm
libjpeg-turbo/simd/i386/jccolor-avx2.asm
libjpeg-turbo/simd/i386/jcgray-avx2.asm
libjpeg-turbo/simd/i386/jcsample-avx2.asm
libjpeg-turbo/simd/i386/jdcolor-avx2.asm
libjpeg-turbo/simd/i386/jdmerge-avx2.asm
libjpeg-turbo/simd/i386/jdsample-avx2.asm
libjpeg-turbo/simd/i386/jfdctint-avx2.asm
libjpeg-turbo/simd/i386/jidctint-avx2.asm
libjpeg-turbo/simd/i386/jquanti-avx2.asm
)
set(CMAKE_ASM_NASM_FLAGS "-DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
set(CMAKE_ASM_NASM_FLAGS "-DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
elseif (${ANDROID_ABI} STREQUAL "x86_64")
enable_language(ASM_NASM)
enable_language(ASM_NASM)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/x86_64/jsimd.c
libjpeg-turbo/simd/x86_64/jsimdcpu.asm
libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm
libjpeg-turbo/simd/x86_64/jccolor-sse2.asm
libjpeg-turbo/simd/x86_64/jcgray-sse2.asm
libjpeg-turbo/simd/x86_64/jchuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcsample-sse2.asm
libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm
libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm
libjpeg-turbo/simd/x86_64/jdsample-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm
libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jidctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctred-sse2.asm
libjpeg-turbo/simd/x86_64/jquantf-sse2.asm
libjpeg-turbo/simd/x86_64/jquanti-sse2.asm
libjpeg-turbo/simd/x86_64/jccolor-avx2.asm
libjpeg-turbo/simd/x86_64/jcgray-avx2.asm
libjpeg-turbo/simd/x86_64/jcsample-avx2.asm
libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm
libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm
libjpeg-turbo/simd/x86_64/jdsample-avx2.asm
libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm
libjpeg-turbo/simd/x86_64/jidctint-avx2.asm
libjpeg-turbo/simd/x86_64/jquanti-avx2.asm
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/x86_64/jsimd.c
libjpeg-turbo/simd/x86_64/jsimdcpu.asm
libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm
libjpeg-turbo/simd/x86_64/jccolor-sse2.asm
libjpeg-turbo/simd/x86_64/jcgray-sse2.asm
libjpeg-turbo/simd/x86_64/jchuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcsample-sse2.asm
libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm
libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm
libjpeg-turbo/simd/x86_64/jdsample-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm
libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jidctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctred-sse2.asm
libjpeg-turbo/simd/x86_64/jquantf-sse2.asm
libjpeg-turbo/simd/x86_64/jquanti-sse2.asm
libjpeg-turbo/simd/x86_64/jccolor-avx2.asm
libjpeg-turbo/simd/x86_64/jcgray-avx2.asm
libjpeg-turbo/simd/x86_64/jcsample-avx2.asm
libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm
libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm
libjpeg-turbo/simd/x86_64/jdsample-avx2.asm
libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm
libjpeg-turbo/simd/x86_64/jidctint-avx2.asm
libjpeg-turbo/simd/x86_64/jquanti-avx2.asm
)
set(CMAKE_ASM_NASM_FLAGS "-D__x86_64__ -DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
set(CMAKE_ASM_NASM_FLAGS "-D__x86_64__ -DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
else ()
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/jsimd_none.c
)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/jsimd_none.c
)
endif ()
+61 -60
View File
@@ -16,77 +16,78 @@ cmake_minimum_required(VERSION 3.4.1)
project(libpng C)
set(LIBPNG_SOURCES
libpng/png.c
libpng/pngerror.c
libpng/pngget.c
libpng/pngmem.c
libpng/pngpread.c
libpng/pngread.c
libpng/pngrio.c
libpng/pngrtran.c
libpng/pngrutil.c
libpng/pngset.c
libpng/pngtrans.c
)
libpng/png.c
libpng/pngerror.c
libpng/pngget.c
libpng/pngmem.c
libpng/pngpread.c
libpng/pngread.c
libpng/pngrio.c
libpng/pngrtran.c
libpng/pngrutil.c
libpng/pngset.c
libpng/pngtrans.c
)
set(LIBPNG_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libpng
)
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libpng
)
set(LIBPNG_DEFINITIONS)
set(LIBPNG_LIBRARIES z)
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
enable_language(ASM)
enable_language(ASM)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon.S
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=2
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
cpufeatures
)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon.S
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=2
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
)
elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
enable_language(ASM)
enable_language(ASM)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon_intrinsics.c
libpng/arm/palette_neon_intrinsics.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=1
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
cpufeatures
)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon_intrinsics.c
libpng/arm/palette_neon_intrinsics.c
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=1
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
)
endif ()
@@ -1,12 +0,0 @@
project(genuine C)
add_library(genuine STATIC
am-proxy.c
apk-sign-v2.c
common.c
genuine.c
inline.c
openat.c
path.c
plt.c)
target_include_directories(genuine PUBLIC integrity)
-824
View File
@@ -1,824 +0,0 @@
//
// Created by Thom on 2019/3/15.
//
#include "common.h"
#include "am-proxy.h"
static inline void fill_android_app_ActivityManager(char v[]) {
// android/app/ActivityManager
static unsigned int m = 0;
if (m == 0) {
m = 23;
} else if (m == 29) {
m = 31;
}
v[0x0] = 'e';
v[0x1] = 'k';
v[0x2] = 'b';
v[0x3] = 'u';
v[0x4] = 'g';
v[0x5] = '`';
v[0x6] = 'n';
v[0x7] = '$';
v[0x8] = 'm';
v[0x9] = '}';
v[0xa] = '~';
v[0xb] = ' ';
v[0xc] = 'Q';
v[0xd] = 'r';
v[0xe] = 'f';
v[0xf] = 'z';
v[0x10] = 'b';
v[0x11] = '|';
v[0x12] = 'b';
v[0x13] = 'y';
v[0x14] = 'L';
v[0x15] = 'c';
v[0x16] = 'm';
v[0x17] = 'e';
v[0x18] = 'b';
v[0x19] = 'c';
v[0x1a] = 'u';
for (unsigned int i = 0; i < 0x1b; ++i) {
v[i] ^= ((i + 0x1b) % m);
}
v[0x1b] = '\0';
}
static inline void fill_getService(char v[]) {
// getService
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = 'd';
v[0x1] = 'a';
v[0x2] = 'q';
v[0x3] = 'U';
v[0x4] = 'e';
v[0x5] = 's';
v[0x6] = 't';
v[0x7] = 'j';
v[0x8] = 'g';
v[0x9] = '`';
for (unsigned int i = 0; i < 0xa; ++i) {
v[i] ^= ((i + 0xa) % m);
}
v[0xa] = '\0';
}
static inline void fill_getService_signature(char v[]) {
// ()Landroid/app/IActivityManager;
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'e';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'u';
v[0x7] = 'g';
v[0x8] = '`';
v[0x9] = 'n';
v[0xa] = '$';
v[0xb] = 'm';
v[0xc] = '}';
v[0xd] = '~';
v[0xe] = ' ';
v[0xf] = 'Y';
v[0x10] = 'P';
v[0x11] = 'q';
v[0x12] = 'g';
v[0x13] = '}';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 'c';
v[0x17] = 'a';
v[0x18] = 'T';
v[0x19] = '{';
v[0x1a] = 'u';
v[0x1b] = '}';
v[0x1c] = 'z';
v[0x1d] = '{';
v[0x1e] = 'r';
v[0x1f] = ':';
for (unsigned int i = 0; i < 0x20; ++i) {
v[i] ^= ((i + 0x20) % m);
}
v[0x20] = '\0';
}
static inline void fill_android_app_ActivityManagerNative(char v[]) {
// android/app/ActivityManagerNative
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'k';
v[0x9] = '{';
v[0xa] = '|';
v[0xb] = '"';
v[0xc] = 'O';
v[0xd] = 'l';
v[0xe] = 'd';
v[0xf] = 'x';
v[0x10] = 'd';
v[0x11] = 'z';
v[0x12] = '`';
v[0x13] = 'l';
v[0x14] = '[';
v[0x15] = 'v';
v[0x16] = 'v';
v[0x17] = 'x';
v[0x18] = '}';
v[0x19] = '~';
v[0x1a] = 'n';
v[0x1b] = 'S';
v[0x1c] = '\x7f';
v[0x1d] = 't';
v[0x1e] = 'h';
v[0x1f] = 't';
v[0x20] = 'f';
for (unsigned int i = 0; i < 0x21; ++i) {
v[i] ^= ((i + 0x21) % m);
}
v[0x21] = '\0';
}
static inline void fill_getDefault(char v[]) {
// getDefault
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = 'd';
v[0x1] = 'a';
v[0x2] = 'q';
v[0x3] = 'B';
v[0x4] = 'e';
v[0x5] = 'g';
v[0x6] = 'c';
v[0x7] = 'v';
v[0x8] = 'h';
v[0x9] = 'q';
for (unsigned int i = 0; i < 0xa; ++i) {
v[i] ^= ((i + 0xa) % m);
}
v[0xa] = '\0';
}
static inline void fill_getDefault_signature(char v[]) {
// ()Landroid/app/IActivityManager;
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'e';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'u';
v[0x7] = 'g';
v[0x8] = '`';
v[0x9] = 'n';
v[0xa] = '$';
v[0xb] = 'm';
v[0xc] = '}';
v[0xd] = '~';
v[0xe] = ' ';
v[0xf] = 'Y';
v[0x10] = 'P';
v[0x11] = 'q';
v[0x12] = 'g';
v[0x13] = '}';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 'c';
v[0x17] = 'a';
v[0x18] = 'T';
v[0x19] = '{';
v[0x1a] = 'u';
v[0x1b] = '}';
v[0x1c] = 'z';
v[0x1d] = '{';
v[0x1e] = 'r';
v[0x1f] = ':';
for (unsigned int i = 0; i < 0x20; ++i) {
v[i] ^= ((i + 0x20) % m);
}
v[0x20] = '\0';
}
static inline void fill_android_os_BinderProxy(char v[]) {
// android/os/BinderProxy
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 'b';
v[0x1] = 'j';
v[0x2] = 'a';
v[0x3] = 't';
v[0x4] = 'h';
v[0x5] = 'a';
v[0x6] = 'm';
v[0x7] = '%';
v[0x8] = 'd';
v[0x9] = '\x7f';
v[0xa] = '"';
v[0xb] = 'L';
v[0xc] = 'f';
v[0xd] = '~';
v[0xe] = 'u';
v[0xf] = 'w';
v[0x10] = 'r';
v[0x11] = 'Q';
v[0x12] = 'p';
v[0x13] = 'l';
v[0x14] = '|';
v[0x15] = '|';
for (unsigned int i = 0; i < 0x16; ++i) {
v[i] ^= ((i + 0x16) % m);
}
v[0x16] = '\0';
}
static inline void fill_android_os_IInterface(char v[]) {
// android/os/IInterface
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'e';
v[0x9] = 'x';
v[0xa] = '#';
v[0xb] = 'D';
v[0xc] = 'G';
v[0xd] = 'a';
v[0xe] = 'd';
v[0xf] = 't';
v[0x10] = '`';
v[0x11] = 'f';
v[0x12] = '`';
v[0x13] = 'a';
v[0x14] = 'f';
for (unsigned int i = 0; i < 0x15; ++i) {
v[i] ^= ((i + 0x15) % m);
}
v[0x15] = '\0';
}
static inline void fill_asBinder(char v[]) {
// asBinder
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = '`';
v[0x1] = 'q';
v[0x2] = 'A';
v[0x3] = 'm';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'e';
v[0x7] = 's';
for (unsigned int i = 0; i < 0x8; ++i) {
v[i] ^= ((i + 0x8) % m);
}
v[0x8] = '\0';
}
static inline void fill_asBinder_signature(char v[]) {
// ()Landroid/os/IBinder;
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = '+';
v[0x1] = '-';
v[0x2] = 'I';
v[0x3] = 'g';
v[0x4] = 'i';
v[0x5] = 'l';
v[0x6] = '{';
v[0x7] = 'e';
v[0x8] = 'b';
v[0x9] = 'h';
v[0xa] = '"';
v[0xb] = 'a';
v[0xc] = '|';
v[0xd] = '?';
v[0xe] = 'X';
v[0xf] = 'P';
v[0x10] = 'i';
v[0x11] = 'o';
v[0x12] = 'f';
v[0x13] = 'f';
v[0x14] = 'v';
v[0x15] = '>';
for (unsigned int i = 0; i < 0x16; ++i) {
v[i] ^= ((i + 0x16) % m);
}
v[0x16] = '\0';
}
static inline void fill_android_os_ServiceManager(char v[]) {
// android/os/ServiceManager
static unsigned int m = 0;
if (m == 0) {
m = 23;
} else if (m == 29) {
m = 31;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'e';
v[0x9] = 'x';
v[0xa] = '#';
v[0xb] = '^';
v[0xc] = 'k';
v[0xd] = '}';
v[0xe] = 'f';
v[0xf] = 'x';
v[0x10] = 'q';
v[0x11] = 'v';
v[0x12] = 'Y';
v[0x13] = 't';
v[0x14] = 'x';
v[0x15] = 'a';
v[0x16] = 'f';
v[0x17] = 'g';
v[0x18] = 'q';
for (unsigned int i = 0; i < 0x19; ++i) {
v[i] ^= ((i + 0x19) % m);
}
v[0x19] = '\0';
}
static inline void fill_getService_signature_IBinder(char v[]) {
// (Ljava/lang/String;)Landroid/os/IBinder;
static unsigned int m = 0;
if (m == 0) {
m = 37;
} else if (m == 41) {
m = 43;
}
v[0x0] = '+';
v[0x1] = 'H';
v[0x2] = 'o';
v[0x3] = 'g';
v[0x4] = 'q';
v[0x5] = 'i';
v[0x6] = '&';
v[0x7] = 'f';
v[0x8] = 'j';
v[0x9] = 'b';
v[0xa] = 'j';
v[0xb] = '!';
v[0xc] = '\\';
v[0xd] = 'd';
v[0xe] = 'c';
v[0xf] = '{';
v[0x10] = '}';
v[0x11] = 's';
v[0x12] = '.';
v[0x13] = '?';
v[0x14] = '[';
v[0x15] = 'y';
v[0x16] = 'w';
v[0x17] = '~';
v[0x18] = 'i';
v[0x19] = 's';
v[0x1a] = 't';
v[0x1b] = 'z';
v[0x1c] = '0';
v[0x1d] = 'O';
v[0x1e] = 'R';
v[0x1f] = '\r';
v[0x20] = 'j';
v[0x21] = 'f';
v[0x22] = 'i';
v[0x23] = 'o';
v[0x24] = 'f';
v[0x25] = 'f';
v[0x26] = 'v';
v[0x27] = '>';
for (unsigned int i = 0; i < 0x28; ++i) {
v[i] ^= ((i + 0x28) % m);
}
v[0x28] = '\0';
}
static inline void fill_activity(char v[]) {
// activity
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = '`';
v[0x1] = 'a';
v[0x2] = 'w';
v[0x3] = 'm';
v[0x4] = 's';
v[0x5] = 'o';
v[0x6] = 't';
v[0x7] = 'x';
for (unsigned int i = 0; i < 0x8; ++i) {
v[i] ^= ((i + 0x8) % m);
}
v[0x8] = '\0';
}
static inline void fill_getName(char v[]) {
// getName
static unsigned int m = 0;
if (m == 0) {
m = 5;
} else if (m == 7) {
m = 11;
}
v[0x0] = 'e';
v[0x1] = 'f';
v[0x2] = 'p';
v[0x3] = 'N';
v[0x4] = '`';
v[0x5] = 'o';
v[0x6] = 'f';
for (unsigned int i = 0; i < 0x7; ++i) {
v[i] ^= ((i + 0x7) % m);
}
v[0x7] = '\0';
}
static inline void fill_getName_signature(char v[]) {
// ()Ljava/lang/String;
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'n';
v[0x4] = 'd';
v[0x5] = 'p';
v[0x6] = 'f';
v[0x7] = '\'';
v[0x8] = 'e';
v[0x9] = 'k';
v[0xa] = 'e';
v[0xb] = 'k';
v[0xc] = '"';
v[0xd] = ']';
v[0xe] = '{';
v[0xf] = 'b';
v[0x10] = 'x';
v[0x11] = '|';
v[0x12] = 'g';
v[0x13] = ':';
for (unsigned int i = 0; i < 0x14; ++i) {
v[i] ^= ((i + 0x14) % m);
}
v[0x14] = '\0';
}
static inline void fill_activity_manager_service_is_s(char v[]) {
// activity manager service is %s
static unsigned int m = 0;
if (m == 0) {
m = 29;
} else if (m == 31) {
m = 37;
}
v[0x0] = '`';
v[0x1] = 'a';
v[0x2] = 'w';
v[0x3] = 'm';
v[0x4] = 's';
v[0x5] = 'o';
v[0x6] = 's';
v[0x7] = 'q';
v[0x8] = ')';
v[0x9] = 'g';
v[0xa] = 'j';
v[0xb] = 'b';
v[0xc] = 'l';
v[0xd] = 'i';
v[0xe] = 'j';
v[0xf] = 'b';
v[0x10] = '1';
v[0x11] = 'a';
v[0x12] = 'v';
v[0x13] = 'f';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 't';
v[0x17] = '}';
v[0x18] = '9';
v[0x19] = 's';
v[0x1a] = 'h';
v[0x1b] = '<';
v[0x1c] = '%';
v[0x1d] = 'r';
for (unsigned int i = 0; i < 0x1e; ++i) {
v[i] ^= ((i + 0x1e) % m);
}
v[0x1e] = '\0';
}
static inline void showError(JNIEnv *env, jclass object) {
char v1[0x8], v2[0x1f];
if (object == NULL) {
fill_activity_manager_service_is_s(v2); // 0x1f
LOGE(v2, NULL);
} else {
fill_getName(v1); // 0x8
fill_getName_signature(v2); // 0x15
jclass clazz = (*env)->GetObjectClass(env, object);
jmethodID method = (*env)->GetMethodID(env, clazz, v1, v2);
jstring string = (*env)->CallObjectMethod(env, object, method);
const char *name = (*env)->GetStringUTFChars(env, string, NULL);
fill_activity_manager_service_is_s(v2); // 0x1f
LOGE(v2, name);
(*env)->ReleaseStringUTFChars(env, string, name);
(*env)->DeleteLocalRef(env, string);
(*env)->DeleteLocalRef(env, clazz);
}
}
bool isAmProxy(JNIEnv *env, int sdk) {
char v1[0xb], v2[0x29];
bool proxy = false;
jmethodID method;
jclass classActivityManager;
if (sdk >= 26) {
fill_android_app_ActivityManager(v2); // 0x1b + 1
classActivityManager = (*env)->FindClass(env, v2);
debug(env, "ActivityManager: %s", classActivityManager);
fill_getService(v1); // 0xa + 1
fill_getService_signature(v2); // 0x20 + 1
method = (*env)->GetStaticMethodID(env, classActivityManager, v1, v2);
#ifdef DEBUG
LOGI("ActivityManager.getService: %p", method);
#endif
if (method == NULL) {
#ifdef DEBUG
LOGW("cannot find ActivityManager.getService");
#endif
(*env)->ExceptionClear(env);
goto clean3;
}
} else {
fill_android_app_ActivityManagerNative(v2); // 0x21 + 1
classActivityManager = (*env)->FindClass(env, v2);
debug(env, "ActivityManagerNative: %s", classActivityManager);
fill_getDefault(v1); // 0xa + 1
fill_getDefault_signature(v2); // 0x20 + 1
method = (*env)->GetStaticMethodID(env, classActivityManager, v1, v2);
#ifdef DEBUG
LOGI("ActivityManagerNative.getDefault: %p", method);
#endif
}
jobject activityManager = (*env)->CallStaticObjectMethod(env, classActivityManager, method);
if (activityManager == NULL) {
#ifdef DEBUG
LOGW("activity manager is null");
#endif
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
goto clean3;
}
debug(env, "activity manager: %s", activityManager);
jclass binderClass = NULL;
jobject binder = NULL;
fill_android_os_BinderProxy(v2); // 0x16 + 1
jclass classBinderProxy = (*env)->FindClass(env, v2);
if (classBinderProxy == NULL) {
#ifdef DEBUG
LOGW("cannot find BinderProxy");
#endif
(*env)->ExceptionClear(env);
} else {
debug(env, "BinderProxy: %s", classBinderProxy);
}
fill_android_os_IInterface(v2); // 0x15 + 1
jclass classIInterface = (*env)->FindClass(env, v2);
debug(env, "IInterface: %s", classIInterface);
if (!(*env)->IsInstanceOf(env, activityManager, classIInterface)) {
#ifdef DEBUG
LOGW("activity manager is not IInterface");
#endif
goto clean2;
}
fill_asBinder(v1); // 0x8 + 1
fill_asBinder_signature(v2); // 0x16 + 1
method = (*env)->GetMethodID(env, classIInterface, v1, v2);
#ifdef DEBUG
LOGI("IInterface.asBinder: %p", method);
#endif
binder = (*env)->CallObjectMethod(env, activityManager, method);
if (binder == NULL) {
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
showError(env, NULL);
proxy = true;
goto clean2;
}
debug(env, "binder: %s", binder);
binderClass = (*env)->GetObjectClass(env, binder);
debug(env, "binder class: %s", binderClass);
if (classBinderProxy != NULL && !(*env)->IsSameObject(env, binderClass, classBinderProxy)) {
showError(env, binderClass);
proxy = true;
goto clean2;
}
fill_android_os_ServiceManager(v2); // 0x19 + 1
jclass classServiceManager = (*env)->FindClass(env, v2);
if (classServiceManager == NULL) {
#ifdef DEBUG
LOGW("cannot find ServiceManager");
#endif
(*env)->ExceptionClear(env);
goto clean2;
}
debug(env, "ServiceManager: %s", classServiceManager);
fill_getService(v1); // 0xa + 1
fill_getService_signature_IBinder(v2); // 0x28 + 1
method = (*env)->GetStaticMethodID(env, classServiceManager, v1, v2);
#ifdef DEBUG
LOGI("ServiceManager.getService: %p", method);
#endif
if (method == NULL) {
(*env)->ExceptionClear(env);
goto clean1;
}
fill_activity(v2); // 0x8 + 1
jstring stringActivity = (*env)->NewStringUTF(env, v2);
jobject service = (*env)->CallStaticObjectMethod(env, classServiceManager, method,
stringActivity);
jclass serviceClass = NULL;
if (service == NULL) {
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
showError(env, NULL);
proxy = true;
goto clean;
}
debug(env, "service: %s", service);
serviceClass = (*env)->GetObjectClass(env, service);
debug(env, "service class: %s", serviceClass);
if (classBinderProxy != NULL && !(*env)->IsSameObject(env, serviceClass, classBinderProxy)) {
showError(env, serviceClass);
proxy = true;
goto clean;
}
bool equals = (*env)->IsSameObject(env, binder, service);
#ifdef DEBUG
LOGI("binder: %p, service: %p, equals: %d", binder, service, equals);
#endif
proxy = !equals;
clean:
if (serviceClass != NULL) {
(*env)->DeleteLocalRef(env, serviceClass);
}
if (service != NULL) {
(*env)->DeleteLocalRef(env, service);
}
(*env)->DeleteLocalRef(env, stringActivity);
clean1:
(*env)->DeleteLocalRef(env, classServiceManager);
clean2:
if (binderClass != NULL) {
(*env)->DeleteLocalRef(env, binderClass);
}
if (binder != NULL) {
(*env)->DeleteLocalRef(env, binder);
}
if (classBinderProxy != NULL) {
(*env)->DeleteLocalRef(env, classBinderProxy);
}
(*env)->DeleteLocalRef(env, classIInterface);
(*env)->DeleteLocalRef(env, activityManager);
clean3:
(*env)->DeleteLocalRef(env, classActivityManager);
return proxy;
}
-21
View File
@@ -1,21 +0,0 @@
//
// Created by Thom on 2019/3/15.
//
#ifndef BREVENT_AM_PROXY_H
#define BREVENT_AM_PROXY_H
#include <jni.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool isAmProxy(JNIEnv *env, int sdk);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_AM_PROXY_H
-169
View File
@@ -1,169 +0,0 @@
//
// Created by Thom on 2019/3/8.
//
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#ifdef MAIN
#include <stdio.h>
#else
#include "common.h"
#include "openat.h"
#endif
static bool isApkSigBlock42(const char *buffer) {
// APK Sig Block 42
return *buffer == 'A'
&& *++buffer == 'P'
&& *++buffer == 'K'
&& *++buffer == ' '
&& *++buffer == 'S'
&& *++buffer == 'i'
&& *++buffer == 'g'
&& *++buffer == ' '
&& *++buffer == 'B'
&& *++buffer == 'l'
&& *++buffer == 'o'
&& *++buffer == 'c'
&& *++buffer == 'k'
&& *++buffer == ' '
&& *++buffer == '4'
&& *++buffer == '2';
}
int checkSignature(const char *path) {
unsigned char buffer[0x11] = {0};
uint32_t size4;
uint64_t size8, size_of_block;
#ifdef DEBUG
LOGI("check signature for %s", path);
#endif
int sign = -1;
int fd = (int) openAt(AT_FDCWD, path, O_RDONLY);
#ifdef DEBUG_OPENAT
LOGI("openat %s returns %d", path, fd);
#endif
if (fd < 0) {
return sign;
}
sign = 1;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (int i = 0;; ++i) {
unsigned short n;
lseek(fd, -i - 2, SEEK_END);
read(fd, &n, 2);
if (n == i) {
lseek(fd, -22, SEEK_CUR);
read(fd, &size4, 4);
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
#ifdef MAIN
if (i > 0) {
printf("warning: comment length is %d\n", i);
}
#endif
break;
}
}
if (i == 0xffff) {
#ifdef MAIN
printf("error: cannot find eocd\n");
#endif
goto clean;
}
}
lseek(fd, 12, SEEK_CUR);
// offset
read(fd, &size4, 0x4);
lseek(fd, (off_t) (size4 - 0x18), SEEK_SET);
read(fd, &size8, 0x8);
read(fd, buffer, 0x10);
if (!isApkSigBlock42((char *) buffer)) {
goto clean;
}
lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET);
read(fd, &size_of_block, 0x8);
if (size_of_block != size8) {
goto clean;
}
for (;;) {
uint32_t id;
uint32_t offset;
read(fd, &size8, 0x8); // sequence length
if (size8 == size_of_block) {
break;
}
read(fd, &id, 0x4); // id
offset = 4;
#ifdef MAIN
printf("id: 0x%08x\n", id);
#endif
if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) {
read(fd, &size4, 0x4); // signer-sequence length
read(fd, &size4, 0x4); // signer length
read(fd, &size4, 0x4); // signed data length
offset += 0x4 * 3;
read(fd, &size4, 0x4); // digests-sequence length
lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests
offset += 0x4 + size4;
read(fd, &size4, 0x4); // certificates length
read(fd, &size4, 0x4); // certificate length
offset += 0x4 * 2;
#ifdef MAIN
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
printf(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
#else
#if defined(GENUINE_SIZE) && defined(GENUINE_HASH)
if (size4 == GENUINE_SIZE) {
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
if ((((unsigned) hash) ^ 0x14131211u) == GENUINE_HASH) {
sign = 0;
break;
}
}
#else
sign = 0;
break;
#endif
#endif
}
lseek(fd, (off_t) (size8 - offset), SEEK_CUR);
}
clean:
close(fd);
return sign;
}
#ifdef MAIN
int main(int argc, char **argv) {
if (argc > 1) {
checkSignature(argv[1]);
}
}
#endif
@@ -1,18 +0,0 @@
//
// Created by Thom on 2019/3/8.
//
#ifndef BREVENT_APK_SIGN_V2_H
#define BREVENT_APK_SIGN_V2_H
#ifdef __cplusplus
extern "C" {
#endif
int checkSignature(const char *path);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_APK_SIGN_V2_H
-233
View File
@@ -1,233 +0,0 @@
//
// Created by Thom on 2019/3/20.
//
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <sys/system_properties.h>
#include "common.h"
#include "plt.h"
static volatile int mGenuine;
static bool onCheckTrue(JNIEnv *env __unused) {
#ifdef DEBUG_NATIVE
has_native_libs();
#endif
#ifdef DEBUG_GENUINE_MOCK
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFalse(JNIEnv *env __unused) {
#if defined(GENUINE_FALSE_CRASH)
return false;
#elif defined(GENUINE_FALSE_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFake(JNIEnv *env __unused) {
#if defined(GENUINE_FAKE_CRASH)
return false;
#elif defined(GENUINE_FAKE_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckOverlay(JNIEnv *env __unused) {
#if defined(GENUINE_OVERLAY_CRASH)
return false;
#elif defined(GENUINE_OVERLAY_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckOdex(JNIEnv *env __unused) {
#if defined(GENUINE_ODEX_CRASH)
return false;
#elif defined(GENUINE_ODEX_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckDex(JNIEnv *env __unused) {
#if defined(GENUINE_DEX_CRASH)
return false;
#elif defined(GENUINE_DEX_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckProxy(JNIEnv *env __unused) {
#if defined(GENUINE_PROXY_CRASH)
return false;
#elif defined(GENUINE_PROXY_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckError(JNIEnv *env __unused) {
#if defined(GENUINE_ERROR_CRASH)
return false;
#elif defined(GENUINE_ERROR_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFatal(JNIEnv *env __unused) {
#if defined(GENUINE_FATAL_CRASH)
return false;
#elif defined(GENUINE_FATAL_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckNoapk(JNIEnv *env __unused) {
#if defined(GENUINE_NOAPK_CRASH)
return false;
#elif defined(GENUINE_NOAPK_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
bool setGenuine(JNIEnv *env, int genuine) {
mGenuine = genuine;
switch (genuine) {
case CHECK_TRUE:
return onCheckTrue(env);
case CHECK_FALSE:
return onCheckFalse(env);
case CHECK_FAKE:
return onCheckFake(env);
case CHECK_OVERLAY:
return onCheckOverlay(env);
case CHECK_ODEX:
return onCheckOdex(env);
case CHECK_DEX:
return onCheckDex(env);
case CHECK_PROXY:
return onCheckProxy(env);
case CHECK_ERROR:
return onCheckError(env);
case CHECK_FATAL:
return onCheckFatal(env);
case CHECK_NOAPK:
return onCheckNoapk(env);
default:
return true;
}
}
int getGenuine() {
return mGenuine;
}
char *getGenuinePackageName() {
#ifdef GET_GENUINE_PACKAGE_NAME
return GET_GENUINE_PACKAGE_NAME();
#elif defined(GENUINE_NAME)
static unsigned int m = 0;
if (m == 0) {
m = 20;
} else if (m == 23) {
m = 29;
}
char name[] = GENUINE_NAME;
unsigned int length = sizeof(name) - 1;
for (unsigned int i = 0; i < length; ++i) {
name[i] ^= ((i + length) % m);
}
name[length] = '\0';
return strdup(name);
#else
return NULL;
#endif
}
__attribute__((__format__ (__printf__, 2, 0)))
void genuine_log_print(int prio, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
__android_log_vprint(prio, TAG, fmt, ap);
va_end(ap);
}
static inline void fill_ro_build_version_sdk(char v[]) {
// ro.build.version.sdk
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 's';
v[0x1] = 'm';
v[0x2] = '-';
v[0x3] = 'f';
v[0x4] = 'p';
v[0x5] = 'o';
v[0x6] = 'k';
v[0x7] = 'l';
v[0x8] = '\'';
v[0x9] = '|';
v[0xa] = 'n';
v[0xb] = '~';
v[0xc] = '~';
v[0xd] = 'g';
v[0xe] = '`';
v[0xf] = '~';
v[0x10] = '?';
v[0x11] = 'a';
v[0x12] = 'd';
v[0x13] = 'j';
for (unsigned int i = 0; i < 0x14; ++i) {
v[i] ^= ((i + 0x14) % m);
}
v[0x14] = '\0';
}
int getSdk() {
static int sdk = 0;
if (sdk == 0) {
char v1[0x20];
char prop[PROP_VALUE_MAX] = {0};
fill_ro_build_version_sdk(v1);
__system_property_get(v1, prop);
sdk = (int) strtol(prop, NULL, 10);
}
return sdk;
}
#ifdef DEBUG
void debug(JNIEnv *env, const char *format, jobject object) {
if (object == NULL) {
LOGI(format, NULL);
} else {
jclass objectClass = (*env)->FindClass(env, "java/lang/Object");
jmethodID toString = (*env)->GetMethodID(env, objectClass, "toString",
"()Ljava/lang/String;");
jstring string = (jstring) (*env)->CallObjectMethod(env, object, toString);
const char *value = (*env)->GetStringUTFChars(env, string, NULL);
LOGI(format, value);
(*env)->ReleaseStringUTFChars(env, string, value);
(*env)->DeleteLocalRef(env, string);
(*env)->DeleteLocalRef(env, objectClass);
}
}
#endif
-73
View File
@@ -1,73 +0,0 @@
//
// Created by Thom on 2019/3/20.
//
#ifndef BREVENT_COMMON_H
#define BREVENT_COMMON_H
#include <jni.h>
#include <stdbool.h>
#include <android/log.h>
#if __has_include("genuine.h")
#include "genuine.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
enum {
CHECK_TRUE,
CHECK_FALSE,
CHECK_FAKE,
CHECK_OVERLAY,
CHECK_ODEX,
CHECK_DEX,
CHECK_PROXY,
CHECK_ERROR,
CHECK_FATAL,
CHECK_NOAPK,
};
#ifndef TAG
#define TAG "Genuine"
#endif
#ifndef LOGI
#define LOGI(...) (genuine_log_print(ANDROID_LOG_INFO, __VA_ARGS__))
#endif
#ifndef LOGW
#define LOGW(...) (genuine_log_print(ANDROID_LOG_WARN, __VA_ARGS__))
#endif
#ifndef LOGE
#define LOGE(...) (genuine_log_print(ANDROID_LOG_ERROR, __VA_ARGS__))
#endif
void genuine_log_print(int prio, const char *fmt, ...);
char *getGenuinePackageName();
bool setGenuine(JNIEnv *env, int genuine);
int getGenuine();
int getSdk();
bool has_native_libs();
#ifdef DEBUG
void debug(JNIEnv *env, const char *format, jobject object);
#else
#define debug(...) do {} while(0);
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_COMMON_H
File diff suppressed because it is too large. Load diff
-43
View File
@@ -1,43 +0,0 @@
#include <stdbool.h>
#define GENUINE_NAME {0x62, 0x63, 0x23, 0x69, 0x66, 0x64, 0x79, 0x67, 0x71, 0x2e, 0x6f, 0x67, 0x68, 0x6b, 0x6c, 0x68, 0x71, 0x6d, 0x7b, 0x7e, 0x6e, 0x7e, 0x23, 0x6b, 0x67, 0x66, 0x78, 0x77, 0x64, 0x65, 0x73, 0x0}
#define GENUINE_SIZE 0x02d1
#define GENUINE_HASH 0x9a99ba0c
/* genuine false handler */
#define GENUINE_FALSE_CRASH
// #define GENUINE_FALSE_NATIVE
/* genuine fake handler */
#define GENUINE_FAKE_CRASH
// #define GENUINE_FAKE_NATIVE
/* genuine overlay handler */
// #define GENUINE_OVERLAY_CRASH
// #define GENUINE_OVERLAY_NATIVE
/* genuine odex handler */
// #define GENUINE_ODEX_CRASH
// #define GENUINE_ODEX_NATIVE
/* genuine dex handler */
#define GENUINE_DEX_CRASH
// #define GENUINE_DEX_NATIVE
/* genuine proxy handler */
#define GENUINE_PROXY_CRASH
// #define GENUINE_PROXY_NATIVE
/* genuine error handler */
#define GENUINE_ERROR_CRASH
// #define GENUINE_ERROR_NATIVE
/* genuine fatal handler */
#define GENUINE_FATAL_CRASH
// #define GENUINE_FATAL_NATIVE
/* genuine noapk handler */
#define GENUINE_NOAPK_CRASH
// #define GENUINE_NOAPK_NATIVE
bool checkGenuine(JNIEnv *env);
-333
View File
@@ -1,333 +0,0 @@
//
// Created by Thom on 2019/3/18.
//
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "inline.h"
#include "common.h"
#ifdef DEBUG_HOOK_SELF
#include <syscall.h>
#include <unistd.h>
#include <sys/stat.h>
#include "whale/whale.h"
static long faccessat_replace(int dirfd, const char *pathname, int mode, int flags) {
return syscall(__NR_faccessat, dirfd, pathname, mode, flags);
}
static long fchownat_replace(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) {
return syscall(__NR_fchownat, dirfd, pathname, owner, group, flags);
}
static int futimens_replace(int fd, const struct timespec times[2]) {
return utimensat(fd, NULL, times, 0);
}
#if defined(__arm__) || defined(__aarch64__)
void check_inline_hook_hookzz() {
void *backup;
zz_disable_arm_arm64_b_branch();
ZzReplace(&fchownat, &fchownat_replace, &backup);
LOGI("hookzz fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
ZzReplace(&faccessat, &faccessat_replace, &backup);
LOGI("hookzz faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
ZzReplace(&futimens, &futimens_replace, &backup);
LOGI("hookzz futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
void check_inline_hook_hookzz_b() {
void *backup;
zz_enable_arm_arm64_b_branch();
ZzReplace(&fchownat, &fchownat_replace, &backup);
LOGI("hookzz-b fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
ZzReplace(&faccessat, &faccessat_replace, &backup);
LOGI("hookzz-b faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
ZzReplace(&futimens, &futimens_replace, &backup);
LOGI("hookzz-b futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#endif
void check_inline_hook_whale() {
WInlineHookFunction(&fchownat, &fchownat_replace, NULL);
LOGI("whale fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
WInlineHookFunction(&faccessat, &faccessat_replace, NULL);
LOGI("whale faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
WInlineHookFunction(&futimens, &futimens_replace, NULL);
LOGI("whale futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#if defined(__arm__) || defined(DEBUG_HOOK_IDE)
#include "substrate/CydiaSubstrate.h"
void check_inline_hook_substrate() {
MSHookFunction(&fchownat, &fchownat_replace, NULL);
LOGI("substrate fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
MSHookFunction(&faccessat, &faccessat_replace, NULL);
LOGI("substrate faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
MSHookFunction(&futimens, &futimens_replace, NULL);
LOGI("substrate futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#endif
#endif
bool isInlineHooked(void *symbol) {
if (symbol == NULL) {
return false;
}
#if defined(__arm__) || defined(DEBUG_HOOK_IDE)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/ldr-literal-load-register-literal
// A1, !(P == 0 && W == 1), we don't check P and W
// cond 010P U0W1 1111 _Rt_ xxxx xxxx xxxx
#define IS_LDR_PC_A1(x) (((x) & 0xfe5ff000u) == 0xe41ff000u)
// T2
// 1111 1000 U101 1111 | _Rt_ xxxx xxxx xxxx
#define IS_LDR_PC_T2(x) (((x) & 0xf000ff7fu) == 0xf000f85fu)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/b-branch
// A1
// cond 100 xxxx xxxx xxxx xxxx xxxx xxxx
#define IS_B_A1(x) (((x) & 0xff000000u) == 0xea000000u)
// T2
// 1110 0xxx xxxx xxxx
#define IS_B_T2(x) (((x) & 0xf800u) == 0xe000u)
// T4
// 1111 0Sxx xxxx xxxx | 10J1 Jxxx xxxx xxxx
// -- imm10 --- | --- imm11 ---
#define IS_B_T4(x) (((x) & 0xd000f800u) == 0x9000f000u)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/nop-no-operation
// T1, hint should be 0000, we don't check
// 1011 1111 hint 0000
#define IS_NOP_T1(x) (((x) & 0xff0fu) == 0xbf00u)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/mov-movs-register-move-register
// cydia use `mov r8, r8` for Nop
// T1, Mmmm is Rm, Dddd is Rd
// 0100 0110 DMmm mddd
#define _IS_MOV_T1(x) (((x) & 0xff00u) == 0x4600u)
#define _RM_MOV_T1(x) ((((x) & 0x78u) >> 3u))
#define _RD_MOV_T1(x) ((((x) & 0x80u) >> 4u) | ((x) & 7u))
#define IS_MOV_T1_RR(x) (_IS_MOV_T1(x) && _RM_MOV_T1(x) == _RD_MOV_T1(x))
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/bx-branch-and-exchange
// cydia use `bx`
// T1
// 0100 0111 0Rmm m000
#define IS_BX_T1(x) (((x) & 0xff87u) == 0x4700u)
#define RM_BX_T1(x) (((x) & 0x0078u) >> 3u)
#define IS_BX_PC_T1(x) ((x) == 0x4778u)
uintptr_t address = (uintptr_t) symbol;
if ((address & 1U) == 0) {
uint32_t *value32 = (uint32_t *) address;
if (IS_LDR_PC_A1(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm ldr pc) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
if (IS_B_A1(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm b) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
#ifdef DEBUG
LOGI("(arm) symbol: %p, value: %08x", symbol, *value32);
#endif
} else {
address = address & ~1U;
uint16_t *value16 = (uint16_t *) address;
uint32_t *value32 = (uint32_t *) address;
if (IS_LDR_PC_T2(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_B_T4(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb b) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_B_T2(*value16)) {
#ifdef DEBUG_HOOK
LOGW("(thumb b) symbol: %p, address: %p, value: %04x",
symbol, address, *value16);
#endif
return true;
}
if (IS_NOP_T1(*value16) || IS_MOV_T1_RR(*value16)) {
#ifdef DEBUG_HOOK
LOGW("(thumb nop) symbol: %p, address: %p, value: %04x",
symbol, address, *value16);
#endif
address += 2;
value16 = (uint16_t *) address;
value32 = (uint32_t *) address;
}
if (IS_LDR_PC_T2(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_BX_PC_T1(*value16) && IS_LDR_PC_A1(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(thumb bx + arm ldr pc) symbol: %p, address: %p, value: %08x %08x",
symbol, address, *value32, *(value32 + 1));
#endif
return true;
}
#ifdef DEBUG
LOGI("(thumb) symbol: %p, address: %p, value: %08x %08x",
symbol, address, *value32, *(value32 + 1));
#endif
}
#endif
#if defined(__aarch64__) || defined(DEBUG_HOOK_IDE)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch
// 0001 01xx xxxx xxxx xxxx xxxx xxxx xxxx
// ------------ imm26 -------------
#define IS_B(x) (((x) & 0xfc000000u) == 0x14000000u)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/ldr-literal-load-register-literal
// 0101 1000 xxxx xxxx xxxx xxxx xxxR tttt
// -------- imm19 --------
#define IS_LDR_X(x) (((x) & 0xff000000u) == 0x58000000u)
#define X_LDR(x) ((x) & 0x1fu)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/adrp-form-pc-relative-address-to-4kb-page
// 1xx1 0000 xxxx xxxx xxxx xxxx xxxR dddd
// lo -------- immhi --------
#define IS_ADRP_X(x) (((x) & 0x9f000000u) == 0x90000000u)
#define X_ADRP(x) ((x) & 0x1fu)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/br-branch-to-register
// 1101 0110 0001 1111 0000 00Rn nnn0 0000
#define IS_BR_X(x) (((x) & 0xfffffc0f) == 0xd61f0000u)
#define X_BR(x) (((x) & 0x3e0u) >> 0x5u)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/movz-move-wide-with-zero
// 1op1 0010 1hwx xxxx xxxx xxxx xxxR dddd
// ------ imm16 -------
// for op, 00 -> MOVN, 10 -> MOVZ, 11 -> MOVK
#define IS_MOV_X(x) (((x) & 0x9f800000u) == 0x92800000u)
#define X_MOV(x) ((x) & 0x1fu)
uint32_t *value32 = symbol;
if (IS_B(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm64 b) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
if (IS_LDR_X(*value32) && IS_BR_X(*(value32 + 1))) {
uint32_t x = X_LDR(*value32);
if (x == X_BR(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(arm64 ldr+br x%d) symbol: %p, value: %08x %08x",
x, symbol, *value32, *(value32 + 1));
#endif
return true;
}
}
if (IS_ADRP_X(*value32) && IS_BR_X(*(value32 + 1))) {
uint32_t x = X_ADRP(*value32);
if (x == X_BR(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(arm64 adrp+br x%d) symbol: %p, value: %08x %08x",
x, symbol, *value32, *(value32 + 1));
#endif
return true;
}
}
if (IS_MOV_X(*value32)) {
uint32_t x = X_MOV(*value32);
for (int i = 1; i <= 4; ++i) {
if (IS_BR_X(*(value32 + i))) {
if (x != X_BR(*(value32 + i))) {
break;
}
#ifdef DEBUG_HOOK
for (int k = 0; k < i; ++k) {
LOGW("(arm64 mov x%d) symbol: %p, value: %08x",
x, symbol + sizeof(uint32_t) * k, *(value32 + k));
}
LOGW("(arm64 br x%d) symbol: %p, value: %08x",
x, symbol + sizeof(uint32_t) * i, *(value32 + i));
#endif
return true;
} else if (IS_MOV_X(*(value32 + i))) {
if (x != X_MOV(*(value32 + i))) {
break;
}
}
}
}
#ifdef DEBUG
LOGI("(arm64) symbol: %p, value: %08x %08x", symbol, *value32, *(value32 + 1));
#endif
#endif
return false;
}
#ifdef DEBUG_HOOK_IO
bool setRead(void *symbol) {
uintptr_t address = (uintptr_t) symbol;
uintptr_t page_size = (uintptr_t) getpagesize();
uintptr_t base = address & ~(page_size - 1);
// inline check read at most 20 bytes
uintptr_t end = (address + 20 + page_size - 1) & -page_size;
#ifdef DEBUG
LOGI("set r+x from %p to %p", base, end);
#endif
if (mprotect((void *) base, end - base, PROT_READ | PROT_EXEC)) {
#ifdef DEBUG
LOGW("cannot mprotect: %s", strerror(errno));
#endif
return false;
} else {
return true;
}
}
#endif
-45
View File
@@ -1,45 +0,0 @@
//
// Created by Thom on 2019/3/18.
//
#ifndef BREVENT_INLINE_H
#define BREVENT_INLINE_H
#include <stdbool.h>
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DEBUG_HOOK_IO
bool setRead(void *symbol);
#endif
bool isInlineHooked(void *symbol);
#ifdef DEBUG_HOOK_SELF
#if defined(__arm__) || defined(__aarch64__)
#include "hookzz/hookzz.h"
void check_inline_hook_hookzz();
void check_inline_hook_hookzz_b();
#endif
void check_inline_hook_whale();
#if defined(__arm__)
void check_inline_hook_substrate();
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_INLINE_H
-56
View File
@@ -1,56 +0,0 @@
//
// Created by Thom on 2019/3/30.
//
#include <unistd.h>
#include <sys/syscall.h>
#include "openat.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) {
#if defined(__arm__)
intptr_t r;
__asm__ volatile(
#ifndef OPTIMIZE_ASM
"mov r0, %1\n\t"
"mov r1, %2\n\t"
"mov r2, %3\n\t"
#endif
"mov ip, r7\n\t"
".cfi_register r7, ip\n\t"
"ldr r7, =" STR(__NR_openat) "\n\t"
"svc #0\n\t"
"mov r7, ip\n\t"
".cfi_restore r7\n\t"
#ifndef OPTIMIZE_ASM
"mov %0, r0\n\t"
#endif
: "=r" (r)
: "r" (fd), "r" (path), "r" (flag));
return r;
#elif defined(__aarch64__)
intptr_t r;
__asm__ volatile(
#ifndef OPTIMIZE_ASM
"mov x0, %1\n\t"
"mov x1, %2\n\t"
"mov x2, %3\n\t"
#endif
"mov x8, " STR(__NR_openat) "\n\t"
"svc #0\n\t"
#ifndef OPTIMIZE_ASM
"mov %0, x0\n\t"
#endif
: "=r" (r)
: "r" (fd), "r" (path), "r" (flag));
return r;
#else
return (intptr_t) syscall(__NR_openat, fd, path, flag);
#endif
}
-28
View File
@@ -1,28 +0,0 @@
//
// Created by Thom on 2019/3/30.
//
#ifndef BREVENT_OPENAT_H
#define BREVENT_OPENAT_H
#include <stdint.h>
#include <fcntl.h>
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __ANDROID__
intptr_t openAt(intptr_t fd, const char *path, intptr_t flag);
#else
#define openAt openat
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_OPENAT_H
-95
View File
@@ -1,95 +0,0 @@
//
// Created by Thom on 2019-05-03.
//
#include "path.h"
#include <stdio.h>
static inline bool isSystem(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 's'
&& *++str == 'y'
&& *++str == 's'
&& *++str == 't'
&& *++str == 'e'
&& *++str == 'm'
&& *++str == '/';
}
static inline bool isVendor(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'v'
&& *++str == 'e'
&& *++str == 'n'
&& *++str == 'd'
&& *++str == 'o'
&& *++str == 'r'
&& *++str == '/';
}
static inline bool isOem(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'o'
&& *++str == 'e'
&& *++str == 'm'
&& *++str == '/';
}
bool isThirdParty(const char *str) {
if (isSystem(str) || isVendor(str) || isOem(str)) {
return false;
} else {
return true;
}
}
bool isDataApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'd'
&& *++str == 'a'
&& *++str == 't'
&& *++str == 'a'
&& *++str == '/'
&& *++str == 'a'
&& *++str == 'p'
&& *++str == 'p'
&& *++str == '/';
}
bool isAsecApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'm'
&& *++str == 'n'
&& *++str == 't'
&& *++str == '/'
&& *++str == 'a'
&& *++str == 's'
&& *++str == 'e'
&& *++str == 'c'
&& *++str == '/';
}
bool isExpandApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'm'
&& *++str == 'n'
&& *++str == 't'
&& *++str == '/'
&& *++str == 'e'
&& *++str == 'x'
&& *++str == 'p'
&& *++str == 'a'
&& *++str == 'n'
&& *++str == 'd'
&& *++str == '/';
}
bool isExternalSdApp(const char *str) {
return isAsecApp(str) || isExpandApp(str);
}
-16
View File
@@ -1,16 +0,0 @@
//
// Created by Thom on 2019-05-03.
//
#ifndef BREVENT_PATH_H
#define BREVENT_PATH_H
#include <stdbool.h>
bool isThirdParty(const char *str);
bool isDataApp(const char *str);
bool isExternalSdApp(const char *str);
#endif //BREVENT_PATH_H
-355
View File
@@ -1,355 +0,0 @@
//
// Created by Thom on 2019/2/16.
//
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "plt.h"
#include "common.h"
#include "path.h"
/*
* reference: https://android.googlesource.com/platform/bionic/+/master/linker/linker_soinfo.cpp
*/
static uint32_t gnu_hash(const uint8_t *name) {
uint32_t h = 5381;
while (*name) {
h += (h << 5) + *name++;
}
return h;
}
static uint32_t elf_hash(const uint8_t *name) {
uint32_t h = 0, g;
while (*name) {
h = (h << 4) + *name++;
g = h & 0xf0000000;
h ^= g;
h ^= g >> 24;
}
return h;
}
static ElfW(Dyn) *find_dyn_by_tag(ElfW(Dyn) *dyn, ElfW(Sxword) tag) {
while (dyn->d_tag != DT_NULL) {
if (dyn->d_tag == tag) {
return dyn;
}
++dyn;
}
return NULL;
}
static inline bool is_global(ElfW(Sym) *sym) {
unsigned char stb = ELF_ST_BIND(sym->st_info);
if (stb == STB_GLOBAL || stb == STB_WEAK) {
return sym->st_shndx != SHN_UNDEF;
} else {
return false;
}
}
static ElfW(Addr) *
find_symbol(struct dl_phdr_info *info, ElfW(Dyn) *base_addr, const char *symbol) {
ElfW(Dyn) *dyn;
dyn = find_dyn_by_tag(base_addr, DT_SYMTAB);
ElfW(Sym) *dynsym = (ElfW(Sym) *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_STRTAB);
char *dynstr = (char *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_GNU_HASH);
if (dyn != NULL) {
ElfW(Word) *dt_gnu_hash = (ElfW(Word) *) (info->dlpi_addr + dyn->d_un.d_ptr);
size_t gnu_nbucket_ = dt_gnu_hash[0];
uint32_t gnu_maskwords_ = dt_gnu_hash[2];
uint32_t gnu_shift2_ = dt_gnu_hash[3];
ElfW(Addr) *gnu_bloom_filter_ = (ElfW(Addr) *) (dt_gnu_hash + 4);
uint32_t *gnu_bucket_ = (uint32_t *) (gnu_bloom_filter_ + gnu_maskwords_);
uint32_t *gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - dt_gnu_hash[1];
--gnu_maskwords_;
uint32_t hash = gnu_hash((uint8_t *) symbol);
uint32_t h2 = hash >> gnu_shift2_;
uint32_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8;
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
if ((1 & (bloom_word >> (hash % bloom_mask_bits)) &
(bloom_word >> (h2 % bloom_mask_bits))) == 0) {
return NULL;
}
uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
if (n == 0) {
return NULL;
}
do {
ElfW(Sym) *sym = dynsym + n;
if (((gnu_chain_[n] ^ hash) >> 1) == 0
&& is_global(sym)
&& strcmp(dynstr + sym->st_name, symbol) == 0) {
ElfW(Addr) *symbol_sym = (ElfW(Addr) *) (info->dlpi_addr + sym->st_value);
#ifdef DEBUG_PLT
LOGI("found %s(gnu+%u) in %s, %p", symbol, n, info->dlpi_name, symbol_sym);
#endif
return symbol_sym;
}
} while ((gnu_chain_[n++] & 1) == 0);
return NULL;
}
dyn = find_dyn_by_tag(base_addr, DT_HASH);
if (dyn != NULL) {
ElfW(Word) *dt_hash = (ElfW(Word) *) (info->dlpi_addr + dyn->d_un.d_ptr);
size_t nbucket_ = dt_hash[0];
uint32_t *bucket_ = dt_hash + 2;
uint32_t *chain_ = bucket_ + nbucket_;
uint32_t hash = elf_hash((uint8_t *) (symbol));
for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
ElfW(Sym) *sym = dynsym + n;
if (is_global(sym) &&
strcmp(dynstr + sym->st_name, symbol) == 0) {
ElfW(Addr) *symbol_sym = (ElfW(Addr) *) (info->dlpi_addr + sym->st_value);
#ifdef DEBUG_PLT
LOGI("found %s(elf+%u) in %s, %p", symbol, n, info->dlpi_name, symbol_sym);
#endif
return symbol_sym;
}
}
return NULL;
}
return NULL;
}
#if defined(__LP64__)
#define Elf_Rela ElfW(Rela)
#define ELF_R_SYM ELF64_R_SYM
#else
#define Elf_Rela ElfW(Rel)
#define ELF_R_SYM ELF32_R_SYM
#endif
#ifdef DEBUG_PLT
#if defined(__x86_64__)
#define R_JUMP_SLOT R_X86_64_JUMP_SLOT
#define ELF_R_TYPE ELF64_R_TYPE
#elif defined(__i386__)
#define R_JUMP_SLOT R_386_JMP_SLOT
#define ELF_R_TYPE ELF32_R_TYPE
#elif defined(__arm__)
#define R_JUMP_SLOT R_ARM_JUMP_SLOT
#define ELF_R_TYPE ELF32_R_TYPE
#elif defined(__aarch64__)
#define R_JUMP_SLOT R_AARCH64_JUMP_SLOT
#define ELF_R_TYPE ELF64_R_TYPE
#else
#error unsupported OS
#endif
#endif
static ElfW(Addr) *find_plt(struct dl_phdr_info *info, ElfW(Dyn) *base_addr, const char *symbol) {
ElfW(Dyn) *dyn = find_dyn_by_tag(base_addr, DT_JMPREL);
if (dyn == NULL) {
return NULL;
}
Elf_Rela *dynplt = (Elf_Rela *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_SYMTAB);
ElfW(Sym) *dynsym = (ElfW(Sym) *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_STRTAB);
char *dynstr = (char *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_PLTRELSZ);
if (dyn == NULL) {
return NULL;
}
size_t count = dyn->d_un.d_val / sizeof(Elf_Rela);
for (size_t i = 0; i < count; ++i) {
Elf_Rela *plt = dynplt + i;
#ifdef DEBUG_PLT
if (ELF_R_TYPE(plt->r_info) != R_JUMP_SLOT) {
LOGW("invalid type for plt+%zu in %s", i, info->dlpi_name);
continue;
}
#endif
size_t idx = ELF_R_SYM(plt->r_info);
idx = dynsym[idx].st_name;
if (strcmp(dynstr + idx, symbol) == 0) {
ElfW(Addr) *symbol_plt = (ElfW(Addr) *) (info->dlpi_addr + plt->r_offset);
#ifdef DEBUG_PLT
ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol_plt;
LOGI("found %s(plt+%zu) in %s, %p -> %p", symbol, i, info->dlpi_name, symbol_plt,
symbol_plt_value);
#endif
return symbol_plt;
}
}
return NULL;
}
static inline bool isso(const char *str) {
if (str == NULL) {
return false;
}
const char *dot = strrchr(str, '.');
return dot != NULL
&& *++dot == 's'
&& *++dot == 'o'
&& (*++dot == '\0' || *dot == '\r' || *dot == '\n');
}
static inline bool should_check_plt(Symbol *symbol, struct dl_phdr_info *info) {
const char *path = info->dlpi_name;
if (symbol->check & PLT_CHECK_PLT_ALL) {
return true;
} else if (symbol->check & PLT_CHECK_PLT_APP) {
return *path != '/' || isThirdParty(path);
} else {
return false;
}
}
static int callback(struct dl_phdr_info *info, __unused size_t size, void *data) {
if (!isso(info->dlpi_name)) {
#ifdef DEBUG_PLT
LOGW("ignore non-so: %s", info->dlpi_name);
#endif
return 0;
}
Symbol *symbol = (Symbol *) data;
#if 0
LOGI("Name: \"%s\" (%d segments)", info->dlpi_name, info->dlpi_phnum);
#endif
++symbol->total;
for (ElfW(Half) phdr_idx = 0; phdr_idx < info->dlpi_phnum; ++phdr_idx) {
ElfW(Phdr) phdr = info->dlpi_phdr[phdr_idx];
if (phdr.p_type != PT_DYNAMIC) {
continue;
}
ElfW(Dyn) *base_addr = (ElfW(Dyn) *) (info->dlpi_addr + phdr.p_vaddr);
ElfW(Addr) *addr;
addr = should_check_plt(symbol, info) ? find_plt(info, base_addr, symbol->symbol_name) : NULL;
if (addr != NULL) {
if (symbol->symbol_plt != NULL) {
ElfW(Addr) *addr_value = (ElfW(Addr) *) *addr;
ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol->symbol_plt;
if (addr_value != symbol_plt_value) {
#ifdef DEBUG_PLT
LOGW("%s, plt %p -> %p != %p", symbol->symbol_name, addr, addr_value,
symbol_plt_value);
#endif
return 1;
}
}
symbol->symbol_plt = addr;
if (symbol->check & PLT_CHECK_NAME) {
if (symbol->size == 0) {
symbol->size = 1;
symbol->names = calloc(1, sizeof(char *));
} else {
++symbol->size;
symbol->names = realloc(symbol->names, symbol->size * sizeof(char *));
}
#ifdef DEBUG_PLT
LOGI("[%d]: %s", symbol->size - 1, info->dlpi_name);
#endif
symbol->names[symbol->size - 1] = strdup(info->dlpi_name);
}
}
addr = find_symbol(info, base_addr, symbol->symbol_name);
if (addr != NULL) {
symbol->symbol_sym = addr;
if (symbol->check == PLT_CHECK_SYM_ONE) {
return PLT_CHECK_SYM_ONE;
}
}
if (symbol->symbol_plt != NULL && symbol->symbol_sym != NULL) {
ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol->symbol_plt;
// stop if unmatch
if (symbol_plt_value != symbol->symbol_sym) {
#ifdef DEBUG_PLT
LOGW("%s, plt: %p -> %p != %p", symbol->symbol_name, symbol->symbol_plt,
symbol_plt_value, symbol->symbol_sym);
#endif
return 1;
}
}
}
return 0;
}
void *plt_dlsym(const char *name, size_t *total) {
Symbol symbol;
memset(&symbol, 0, sizeof(Symbol));
if (total == NULL) {
symbol.check = PLT_CHECK_SYM_ONE;
}
symbol.symbol_name = name;
dl_iterate_phdr_symbol(&symbol);
if (total != NULL) {
*total = symbol.total;
}
return symbol.symbol_sym;
}
bool isPltHooked(const char *name, bool all) {
Symbol symbol;
memset(&symbol, 0, sizeof(Symbol));
symbol.check = all ? PLT_CHECK_PLT_ALL : PLT_CHECK_PLT_APP;
symbol.symbol_name = name;
return dl_iterate_phdr_symbol(&symbol) ? true : false;
}
/**
* symbol->check PLT_CHECK_PLT | PLT_CHECK_NAME
* @param symbol
* @return
*/
int dl_iterate_phdr_symbol(Symbol *symbol) {
int result;
#ifdef DEBUG_PLT
LOGI("start dl_iterate_phdr: %s", symbol->symbol_name);
#endif
#if __ANDROID_API__ >= 21 || !defined(__arm__)
result = dl_iterate_phdr(callback, symbol);
#else
int (*dl_iterate_phdr)(int (*)(struct dl_phdr_info *, size_t, void *), void *);
dl_iterate_phdr = dlsym(RTLD_NEXT, "dl_iterate_phdr");
if (dl_iterate_phdr != NULL) {
result = dl_iterate_phdr(callback, symbol);
} else {
result = 0;
void *handle = dlopen("libdl.so", RTLD_NOW);
dl_iterate_phdr = dlsym(handle, "dl_iterate_phdr");
if (dl_iterate_phdr != NULL) {
result = dl_iterate_phdr(callback, symbol);
} else {
LOGW("cannot dlsym dl_iterate_phdr");
}
dlclose(handle);
}
#endif
#ifdef DEBUG_PLT
LOGI("complete dl_iterate_phdr: %s", symbol->symbol_name);
#endif
return result;
}
-42
View File
@@ -1,42 +0,0 @@
//
// Created by Thom on 2019/2/16.
//
#ifndef BREVENT_PLT_H
#define BREVENT_PLT_H
#include <elf.h>
#include <link.h>
#include <android/log.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PLT_CHECK_PLT_APP ((unsigned short) 0x1u)
#define PLT_CHECK_PLT_ALL ((unsigned short) 0x2u)
#define PLT_CHECK_NAME ((unsigned short) 0x4u)
#define PLT_CHECK_SYM_ONE ((unsigned short) 0x8u)
typedef struct Symbol {
unsigned short check;
unsigned short size;
size_t total;
ElfW(Addr) *symbol_plt;
ElfW(Addr) *symbol_sym;
const char *symbol_name;
char **names;
} Symbol;
int dl_iterate_phdr_symbol(Symbol *symbol);
void *plt_dlsym(const char *name, size_t *total);
bool isPltHooked(const char *name, bool all);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_PLT_H
+1 -5
View File
@@ -1,12 +1,11 @@
#include <jni.h>
#include "genuine.h"
#include "java_wrapper.h"
#include "JavaInitA7Zip.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**) (&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
@@ -14,9 +13,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
if (!image_onLoad(vm)) {
return JNI_ERR;
}
if (!a7zip_onLoad(vm)) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}