Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e63738542 | ||
|
|
d6823c9c8e | ||
|
|
6bc3866660 | ||
|
|
f41f4dd8ba | ||
|
|
cde39812ce | ||
|
|
03c2abc901 | ||
|
|
7646334525 | ||
|
|
f8d1cd3fab | ||
|
|
4ea94be64a | ||
|
|
dc0470e75a | ||
|
|
5d49ecdd7d | ||
|
|
ef2441116d | ||
|
|
207909c7be | ||
|
|
ddb3dd2d95 | ||
|
|
8bb980fe97 | ||
|
|
3c3a201ed8 | ||
|
|
81bb37e929 | ||
|
|
ae34690eb2 | ||
|
|
285b0f5a69 | ||
|
|
a5327fce07 | ||
|
|
a4b750f9cd | ||
|
|
943344d4bb | ||
|
|
79f1b92960 | ||
|
|
575ae38a28 | ||
|
|
f90e2a5c80 | ||
|
|
4d9b2877fe | ||
|
|
4a955947a2 | ||
|
|
7307c136ee | ||
|
|
657e1b1d0f |
No files matched your search
@@ -36,7 +36,6 @@ jobs:
|
||||
uses: softprops/action-gh-release@v0.1.14
|
||||
with:
|
||||
body: Bump Version
|
||||
prerelease: true
|
||||
files: |
|
||||
app/build/outputs/apk/release/app-arm64-v8a-release.apk
|
||||
app/build/outputs/apk/release/app-x86_64-release.apk
|
||||
+6
-3
@@ -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
|
||||
@@ -1,8 +1,27 @@
|
||||
# EhViewer
|
||||
<h1 align="center">
|
||||
<img src="art/launcher_icon-web.png" width="200">
|
||||
<br>EhViewer<br>
|
||||
</h1>
|
||||
|
||||
[](https://www.codefactor.io/repository/github/asuka-mio/ehviewer)
|
||||

|
||||
<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 及其动态取色系统
|
||||
@@ -25,6 +44,16 @@ Only Support Android S and above!
|
||||

|
||||
|
||||
|
||||
# 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
|
||||
|
||||
本项目受到了诸多开源项目的帮助
|
||||
|
||||
+2
-2
@@ -38,8 +38,8 @@ android {
|
||||
applicationId "io.github.nekoinverter.ehviewer"
|
||||
minSdkVersion 31
|
||||
targetSdkVersion 32
|
||||
versionCode 114519
|
||||
versionName "1.7.26.10"
|
||||
versionCode 114521
|
||||
versionName "1.7.27.1"
|
||||
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -110,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"
|
||||
@@ -151,6 +142,5 @@
|
||||
android:text="@string/read" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -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'
|
||||
}
|
||||
Vendored
+1
-1
@@ -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;
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
project(ehviewer)
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -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 -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__
|
||||
@@ -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__
|
||||
@@ -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)
|
||||
Submodule native/src/main/jni/a7zip/p7zip/p7zip deleted from 6efdeb2e53.
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Submodule
+1
Submodule native/src/main/jni/libarchive added at 588598490a.
Submodule
+1
Submodule native/src/main/jni/liblzma added at 2327a461e1.
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user