Compare commits

...
59 Commits
Author SHA1 Message Date
tarsin 57f21c8d39 Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-24 18:19:11 +08:00
tarsin 07f27a8438 screenshot update 2022-05-24 18:16:56 +08:00
tarsin 4f4dc55b4e itemcard layout tweaks 2022-05-24 18:15:33 +08:00
tarsin 8e9cda8e93 update screenshot 2022-05-24 17:37:46 +08:00
tarsin d5cfabd73e Only check this when longpressed on chip 2022-05-24 17:12:05 +08:00
tarsin 4f1b371e75 Drop fuse-archive
* This glue can't work with latest libfuse and require c++ STL for no reason, drop it and try find better impl
2022-05-24 16:59:07 +08:00
tarsin 4d8b50912d Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-24 16:52:50 +08:00
tarsin 660c717cfb Make elf32 release 2022-05-24 16:51:37 +08:00
tarsin 70d3d9a5b2 Update jconfigint.h 2022-05-24 16:35:37 +08:00
tarsin 464da25ca1 Update android.yml 2022-05-24 16:08:58 +08:00
tarsin c2795120e1 Update CMakeLists.txt 2022-05-24 16:00:51 +08:00
tarsin b10fb46f1f Update build.gradle 2022-05-24 15:48:38 +08:00
tarsin e39897176a Update build.gradle 2022-05-24 15:47:45 +08:00
tarsin 6e7e6fab74 Import fuse-archive 0.1.11
* From https://github.com/google/fuse-archive
2022-05-24 11:33:41 +08:00
tarsin 94009b6829 Build libfuse 2022-05-24 11:09:19 +08:00
tarsin 289217fefc Add libfuse 3.11.0 2022-05-24 10:02:26 +08:00
tarsin 97feab1203 Rename native libs, drop a rikka util 2022-05-24 09:23:05 +08:00
tarsin a9a3b20eb3 Make all lib shared 2022-05-23 21:22:39 +08:00
tarsin 7af1e168a4 Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 20:46:46 +08:00
tarsin c8bf34e0d7 Switch to md3 switch 2022-05-23 20:46:09 +08:00
tarsin 86d2282cd6 native: Reformat code 2022-05-23 20:04:04 +08:00
tarsin cfdb23d6dc app: Reformat code 2022-05-23 19:59:40 +08:00
tarsin 1874ddd15b Add nativelib version check 2022-05-23 19:58:16 +08:00
tarsin a004c308cc Rename .java to .kt 2022-05-23 19:58:16 +08:00
tarsin 863337c789 Support fskip for better decompress perf
* Actually the whole impl can't be more sucks, will import algorithm to manage buffer and read strategy further
2022-05-23 17:34:59 +08:00
tarsin 64d91eed33 Upgrade libjpeg-turbo to 2.1.3 2022-05-23 14:55:15 +08:00
tarsin d0c3a3e14d Refactor cmake 2022-05-23 11:36:51 +08:00
tarsin 464636d87d Add zstd support 2022-05-23 11:28:28 +08:00
tarsin 0b0a1f456f Reduce supported ext
* hippo's legacy glgallery only support these four extensions, further support need we migrate to coil and drop glgallery, in progress
2022-05-23 08:40:15 +08:00
tarsin 302030ca65 Update README.md 2022-05-23 08:28:38 +08:00
tarsin 3e63738542 Make a release
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 08:21:14 +08:00
tarsin d6823c9c8e Checkout to our newly patched libarchive 2022-05-23 08:04:59 +08:00
tarsin 6bc3866660 Build lzma
FIXME: Don't hack!!
https://github.com/asuka-mio/libarchive/commit/588598490ad0a80e44554ed5e608a9f9878220c0
2022-05-23 08:02:20 +08:00
tarsin f41f4dd8ba Support 7z read 2022-05-23 07:31:58 +08:00
tarsin cde39812ce Update proguard-rules.pro 2022-05-23 01:12:58 +08:00
tarsin 03c2abc901 Merge pull request #34 from txyyh/feature/Button
Use Material3 Button instead
2022-05-23 00:59:14 +08:00
tarsin 7646334525 Don't obfuscate native code 2022-05-23 00:59:00 +08:00
txyyh f8d1cd3fab Use Material3 Button instead 2022-05-23 00:47:28 +08:00
tarsin 4ea94be64a Update releases.yml 2022-05-23 00:44:56 +08:00
tarsin dc0470e75a Bump Version
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-23 00:34:33 +08:00
tarsin 5d49ecdd7d Add lzma xz v5.2.5 2022-05-23 00:29:01 +08:00
tarsin ef2441116d Use more friendly suffix recogniser and support all image file 2022-05-22 23:17:35 +08:00
tarsin 207909c7be Initial libarchive simple support 2022-05-22 22:27:31 +08:00
tarsin ddb3dd2d95 Merge pull request #33 from txyyh/feature/buildguide
Add build guide
2022-05-22 02:17:38 +08:00
txyyh 8bb980fe97 Add build guide 2022-05-22 01:55:38 +08:00
tarsin 3c3a201ed8 Merge pull request #30 from txyyh/feature/README
Rewrite README header
2022-05-22 01:31:13 +08:00
tarsin 81bb37e929 Merge pull request #31 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-api-2.x
Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final
2022-05-22 01:30:54 +08:00
Renovate Bot ae34690eb2 Update dependency org.jboss.forge.roaster:roaster-api to v2.25.0.Final 2022-05-21 17:30:16 +00:00
tarsin 285b0f5a69 Merge pull request #32 from asuka-mio/renovate/org.jboss.forge.roaster-roaster-jdt-2.x
Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final
2022-05-22 01:29:43 +08:00
Renovate Bot a5327fce07 Update dependency org.jboss.forge.roaster:roaster-jdt to v2.25.0.Final 2022-05-21 15:11:11 +00:00
txyyh a4b750f9cd Add description title 2022-05-21 21:58:53 +08:00
txyyh 943344d4bb Rewrite shields 2022-05-21 21:58:50 +08:00
txyyh 79f1b92960 Rewrite icon header 2022-05-21 21:26:48 +08:00
tarsin 575ae38a28 Re-checkout 2022-05-21 20:46:25 +08:00
tarsin f90e2a5c80 Checkout to our libarchive 2022-05-21 20:34:06 +08:00
tarsin 4d9b2877fe Build libarchive 2022-05-21 20:08:03 +08:00
tarsin 4a955947a2 Add libarchive 2022-05-21 19:41:09 +08:00
tarsin 7307c136ee Drop useless native part 2022-05-21 17:24:03 +08:00
tarsin 657e1b1d0f Drop a7zip for libarchive integrate 2022-05-21 17:04:27 +08:00
111 changed files with 861 additions and 8540 deletions

No files matched your search

+17 -5
View File
@@ -13,8 +13,8 @@ jobs:
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
@@ -32,11 +32,23 @@ jobs:
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
name: arm64-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
path: app/build/outputs/apk/release/app-x86_64-release.apk
- name: Update ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
- name: Update x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
+4 -3
View File
@@ -16,8 +16,8 @@ jobs:
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
@@ -36,7 +36,8 @@ 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
app/build/outputs/apk/release/app-x86-release.apk
app/build/outputs/apk/release/app-armeabi-v7a-release.apk
+13 -4
View File
@@ -1,12 +1,21 @@
[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
url = https://github.com/asuka-mio/libjpeg-turbo
[submodule "native/src/main/jni/image/giflib/giflib"]
path = native/src/main/jni/image/giflib/giflib
url = https://gitlab.com/NekoInverter/giflib.git
[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
[submodule "native/src/main/jni/libzstd"]
path = native/src/main/jni/libzstd
url = https://github.com/facebook/zstd
[submodule "native/src/main/jni/libfuse/libfuse"]
path = native/src/main/jni/libfuse/libfuse
url = https://github.com/libfuse/libfuse/
+34 -3
View File
@@ -1,8 +1,27 @@
# EhViewer
<h1 align="center">
<img src="art/launcher_icon-web.png" width="200">
<br>EhViewer<br>
</h1>
[![CodeFactor](https://www.codefactor.io/repository/github/asuka-mio/ehviewer/badge)](https://www.codefactor.io/repository/github/asuka-mio/ehviewer)
![Icon](art/launcher_icon-web.png)
<p align="center">
<a href="https://github.com/asuka-mio/Ehviewer/actions">
<img src="https://img.shields.io/github/workflow/status/asuka-mio/Ehviewer/EhViewer" alt="Github Actions">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/asuka-mio/Ehviewer" alt="LICENSE">
</a>
<a href="https://www.codefactor.io/repository/github/asuka-mio/ehviewer">
<img src="https://www.codefactor.io/repository/github/asuka-mio/ehviewer/badge" alt="CodeFactor">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/releases">
<img src="https://img.shields.io/github/v/release/asuka-mio/ehviewer?include_prereleases" alt="Release">
</a>
<a href="https://github.com/asuka-mio/Ehviewer/issues">
<img src="https://img.shields.io/github/issues/asuka-mio/ehviewer" alt="Issues">
</a>
</p>
# Description
这是一个 E-Hentai Android 平台的浏览器
已针对性适配 Material Design 3 及其动态取色系统
@@ -25,6 +44,16 @@ Only Support Android S and above!
![screenshot-01](art/screenshot-01.png)
# Build
本项目使用了 CMake 3.23.1 作为构建工具链之一,而当前版本的 Android Studio SDK Tool 无法满足本项目需求。
因此,在构建本项目前需要提前配置好 CMake >=3.23.1。
This Project use CMake 3.23.1, with is highter than Android Studio bundle CMake.
So you need to pre-install CMake>=3.23.1 before build.
# Thanks
本项目受到了诸多开源项目的帮助
@@ -45,6 +74,8 @@ Here is the libraries
- [ShowcaseView](https://github.com/amlcurran/ShowcaseView)
- [Slabo](https://github.com/TiroTypeworks/Slabo)
- [TagSoup](http://home.ccil.org/~cowan/tagsoup/)
- [libarchive](http://www.libarchive.org/)
- [XZ Utils](https://tukaani.org/xz/)
# License
+9 -10
View File
@@ -17,17 +17,17 @@ android {
abi {
enable = true
reset()
include 'arm64-v8a', 'x86_64'
include 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
}
}
signingConfigs {
sign {
storeFile file(signStoreFile)
storePassword signStorePassword
keyAlias signkeyAlias
keyPassword signkeyPassword
}
sign {
storeFile file(signStoreFile)
storePassword signStorePassword
keyAlias signkeyAlias
keyPassword signkeyPassword
}
}
buildFeatures {
@@ -38,8 +38,8 @@ android {
applicationId "io.github.nekoinverter.ehviewer"
minSdkVersion 31
targetSdkVersion 32
versionCode 114519
versionName "1.7.26.10"
versionCode 114524
versionName "1.7.27.4"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -133,7 +133,6 @@ dependencies {
implementation 'dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0'
implementation 'dev.rikka.rikkax.material:material:2.4.0'
implementation 'dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1'
implementation 'dev.rikka.rikkax.widget:borderview:1.1.0'
implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.3'
implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1'
implementation 'org.greenrobot:greendao:3.3.0'
@@ -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<>();
@@ -66,7 +53,14 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
private String error;
public ArchiveGalleryProvider(Context context, Uri uri) {
file = UniFile.fromMediaUri(context, 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(4096);
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 {
@@ -1,58 +0,0 @@
/*
* Copyright 2016 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.preference;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.AttributeSet;
import androidx.preference.Preference;
import com.hippo.ehviewer.R;
public class VersionPreference extends Preference {
public VersionPreference(Context context) {
super(context);
init(context);
}
public VersionPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public VersionPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setTitle(R.string.settings_about_version);
String version;
try {
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
version = pInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
version = context.getString(R.string.error_unknown);
}
setSummary(version);
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2016 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.preference
import android.content.Context
import android.content.pm.PackageManager
import android.util.AttributeSet
import androidx.appcompat.app.AlertDialog
import com.hippo.Native
import com.hippo.ehviewer.R
import com.hippo.preference.MessagePreference
class VersionPreference @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : MessagePreference(context, attrs) {
init {
setTitle(R.string.settings_about_version)
val version: String = try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
pInfo.versionName
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
context.getString(R.string.error_unknown)
}
summary = version
}
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setTitle(R.string.show_library_version)
builder.setMessage(Native.getlibarchiveVersion() + "\nlzma(xz-utils) " + Native.getliblzmaVersion() + "\nlibzstd " + Native.getlibzstdVersion() + "\nzlib " + Native.getzlibVersion() + "\nlibjpeg-turbo " + Native.getlibjpeg_turboVersion())
}
}
@@ -66,6 +66,7 @@ import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.switchmaterial.SwitchMaterial;
import com.hippo.ehviewer.AppConfig;
import com.hippo.ehviewer.BuildConfig;
import com.hippo.ehviewer.R;
@@ -238,28 +239,15 @@ 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);
}
}
} ActivityResultLauncher<String> requestStoragePermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result && mSavingPage != -1) {
saveImage(mSavingPage);
} else {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
}
mSavingPage = -1;
});
}
private void onInit() {
Intent intent = getIntent();
@@ -273,7 +261,16 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
mGalleryInfo = intent.getParcelableExtra(KEY_GALLERY_INFO);
mPage = intent.getIntExtra(KEY_PAGE, -1);
buildProvider();
}
} ActivityResultLauncher<String> requestStoragePermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result && mSavingPage != -1) {
saveImage(mSavingPage);
} else {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
}
mSavingPage = -1;
});
private void onRestore(@NonNull Bundle savedInstanceState) {
mAction = savedInstanceState.getString(KEY_ACTION);
@@ -1051,15 +1048,15 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
private final Spinner mScaleMode;
private final Spinner mStartPosition;
private final Spinner mReadTheme;
private final Switch mKeepScreenOn;
private final Switch mShowClock;
private final Switch mShowProgress;
private final Switch mShowBattery;
private final Switch mShowPageInterval;
private final Switch mVolumePage;
private final Switch mReverseVolumePage;
private final Switch mReadingFullscreen;
private final Switch mCustomScreenLightness;
private final SwitchMaterial mKeepScreenOn;
private final SwitchMaterial mShowClock;
private final SwitchMaterial mShowProgress;
private final SwitchMaterial mShowBattery;
private final SwitchMaterial mShowPageInterval;
private final SwitchMaterial mVolumePage;
private final SwitchMaterial mReverseVolumePage;
private final SwitchMaterial mReadingFullscreen;
private final SwitchMaterial mCustomScreenLightness;
private final SeekBar mScreenLightness;
@SuppressLint("InflateParams")
@@ -69,7 +69,6 @@ import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter;
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange;
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager;
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableItemViewHolder;
import com.hippo.app.CheckBoxDialogBuilder;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.drawable.AddDeleteDrawable;
import com.hippo.drawable.DrawerArrowDrawable;
@@ -141,7 +141,7 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, os);
mImagePath = temp.getPath();
mPreview.setImageBitmap(bitmap);
((View)mPreview.getParent()).setVisibility(VISIBLE);
((View) mPreview.getParent()).setVisibility(VISIBLE);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
@@ -166,7 +166,7 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic
}
mImagePath = imagePath;
mPreview.setImageBitmap(bitmap);
((View)mPreview.getParent()).setVisibility(VISIBLE);
((View) mPreview.getParent()).setVisibility(VISIBLE);
} catch (FileNotFoundException e) {
// Ignore
} finally {
@@ -99,6 +99,11 @@ class SearchLayout @JvmOverloads constructor(
mChip.setText(mPair.second)
mChip.idt = mPair.first
mChip.isChecked = NumberUtils.int2boolean(mPair.first and mCategoryStored)
mChip.setOnLongClickListener {
mCategoryGroup.clearCheck()
mChip.isChecked = true
true
}
mCategoryGroup.addView(mChip)
}
mCategoryGroup.setOnCheckedStateChangeListener { _, checkedIds ->
@@ -42,7 +42,11 @@ public class BatteryView extends AppCompatTextView {
private BatteryDrawable mDrawable;
private boolean mAttached = false;
private boolean mIsChargerWorking = false; private final Runnable mCharger = new Runnable() {
private boolean mIsChargerWorking = false;
public BatteryView(Context context) {
super(context);
init();
} private final Runnable mCharger = new Runnable() {
private int level = 0;
@@ -56,10 +60,6 @@ public class BatteryView extends AppCompatTextView {
getHandler().postDelayed(mCharger, 200);
}
};
public BatteryView(Context context) {
super(context);
init();
}
public BatteryView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -85,6 +85,22 @@ public class BatteryView extends AppCompatTextView {
int height = (int) getTextSize();
mDrawable.setBounds(0, 0, (int) (height / 0.618f), height);
setCompoundDrawables(mDrawable, null, null, null);
}
@Override
public void setTextColor(int color) {
if (mCurrentColor == color) {
return;
}
mCurrentColor = color;
super.setTextColor(color);
}
private void startCharger() {
if (!mIsChargerWorking) {
getHandler().post(mCharger);
mIsChargerWorking = true;
}
} private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -116,22 +132,6 @@ public class BatteryView extends AppCompatTextView {
}
};
@Override
public void setTextColor(int color) {
if (mCurrentColor == color) {
return;
}
mCurrentColor = color;
super.setTextColor(color);
}
private void startCharger() {
if (!mIsChargerWorking) {
getHandler().post(mCharger);
mIsChargerWorking = true;
}
}
private void stopCharger() {
if (mIsChargerWorking) {
getHandler().removeCallbacks(mCharger);
@@ -65,7 +65,10 @@ public class TextClock extends AppCompatTextView {
}
onTimeChanged();
}
}; private final Runnable mTicker = new Runnable() {
};
public TextClock(Context context) {
this(context, null);
} private final Runnable mTicker = new Runnable() {
@Override
public void run() {
onTimeChanged();
@@ -76,24 +79,10 @@ public class TextClock extends AppCompatTextView {
getHandler().postAtTime(mTicker, next);
}
};
public TextClock(Context context) {
this(context, null);
}
public TextClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
}
public TextClock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@@ -109,7 +98,19 @@ public class TextClock extends AppCompatTextView {
createTime(mTimeZone);
// Wait until onAttachedToWindow() to handle the ticker
chooseFormat(false);
}
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
private void createTime(String timeZone) {
if (timeZone != null) {
+24 -37
View File
@@ -14,7 +14,7 @@
~ limitations under the License.
-->
<rikka.widget.borderview.BorderNestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -37,8 +37,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_screen_rotation"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_screen_rotation" />
<com.hippo.widget.CuteSpinner
android:id="@+id/screen_rotation"
@@ -60,8 +59,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_reading_direction"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_reading_direction" />
<com.hippo.widget.CuteSpinner
android:id="@+id/reading_direction"
@@ -83,8 +81,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_page_scaling"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_page_scaling" />
<com.hippo.widget.CuteSpinner
android:id="@+id/page_scaling"
@@ -106,8 +103,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/settings_read_start_position"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_start_position" />
<com.hippo.widget.CuteSpinner
android:id="@+id/start_position"
@@ -129,8 +125,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/read_theme"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/read_theme" />
<com.hippo.widget.CuteSpinner
android:id="@+id/read_theme"
@@ -142,68 +137,60 @@
</LinearLayout>
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/keep_screen_on"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_keep_screen_on"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_keep_screen_on" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/show_clock"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_clock"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_clock" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/show_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_progress"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_progress" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/show_battery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_battery"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_battery" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/show_page_interval"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_show_page_interval"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_show_page_interval" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/volume_page"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_volume_page"
android:textColor="?android:attr/textColorPrimary" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/reverse_volume_page"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_reverse_volume"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_reverse_volume" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/reading_fullscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_reading_fullscreen"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_reading_fullscreen" />
<Switch
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/custom_screen_lightness"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_read_custom_screen_lightness"
android:textColor="?android:attr/textColorPrimary" />
android:text="@string/settings_read_custom_screen_lightness" />
<SeekBar
android:id="@+id/screen_lightness"
@@ -212,4 +199,4 @@
android:max="200" />
</LinearLayout>
</rikka.widget.borderview.BorderNestedScrollView>
</androidx.core.widget.NestedScrollView>
@@ -110,47 +110,37 @@
</RelativeLayout>
<com.google.android.material.card.MaterialCardView
<LinearLayout
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">
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
<Button
android:id="@+id/download"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_weight="1"
android:clickable="true"
android:focusable="true" />
<TextView
android:id="@+id/download"
style="@style/ButtonInCard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true" />
<android.widget.Space
android:layout_width="8dp"
android:layout_height="wrap_content" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?attr/dividerColor" />
<Button
android:id="@+id/read"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:text="@string/read" />
<TextView
android:id="@+id/read"
style="@style/ButtonInCard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:text="@string/read" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</LinearLayout>
</FrameLayout>
@@ -19,8 +19,7 @@
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:background="?selectableItemBackground"
android:layout_margin="4dp"
android:checkable="true"
android:clickable="true"
android:focusable="true">
@@ -61,8 +60,8 @@
style="@style/CardMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_alignStart="@id/title"
android:layout_above="@id/rating"
android:layout_marginTop="2dp" />
<TextView
@@ -86,7 +85,7 @@
android:layout_height="wrap_content"
android:layout_above="@id/category"
android:layout_alignStart="@id/title"
android:layout_marginBottom="4dp" />
android:layout_marginBottom="2dp" />
<TextView
android:id="@+id/posted"
+1 -1
View File
@@ -24,7 +24,7 @@
android:id="@+id/search_category_chipgroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/search_item_interval"/>
android:layout_marginBottom="@dimen/search_item_interval" />
<com.hippo.widget.RadioGridGroup
android:id="@+id/normal_search_mode"
@@ -20,7 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="gone" >
android:visibility="gone">
<com.hippo.widget.FixedAspectImageView
+4 -4
View File
@@ -20,13 +20,13 @@
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/settings"
android:showAsAction="always" />
android:showAsAction="always"
android:title="@string/settings" />
<item
android:id="@+id/action_default_download_label"
android:icon="@drawable/v_download_box_dark_x24"
android:title="@string/default_download_label"
android:showAsAction="ifRoom" />
android:showAsAction="ifRoom"
android:title="@string/default_download_label" />
</menu>
@@ -19,13 +19,13 @@
<item
android:id="@+id/action_help"
android:icon="@drawable/v_help_circle_x24"
android:title="@string/add"
android:showAsAction="ifRoom" />
android:showAsAction="ifRoom"
android:title="@string/add" />
<item
android:id="@+id/action_add"
android:icon="@drawable/v_plus_dark_x24"
android:title="@string/add"
android:showAsAction="always" />
android:showAsAction="always"
android:title="@string/add" />
</menu>
@@ -759,4 +759,6 @@
<string name="app_link_not_verified_message">对于 Android 12 及更新的版本,您需要手动添加链接到已验证链接才能在 EhViewer 中打开 E-Hentai 链接。</string>
<string name="app_link_not_verified_title">应用链接未验证</string>
<string name="open_by_default">默认打开</string>
<string name="show_library_version">依赖库之版本</string>
</resources>
+2
View File
@@ -823,4 +823,6 @@
<string name="app_link_not_verified_message">For Android 12 and newer, you need to manually add link to verified links in order to open E-Hentai links in EhViewer.</string>
<string name="app_link_not_verified_title">App links not verified</string>
<string name="open_by_default">Open by default</string>
<string name="show_library_version">Library Version</string>
</resources>
+2 -21
View File
@@ -28,38 +28,19 @@
<item name="android:minHeight">48dp</item>
</style>
<style name="TextAppearance.CardTitle">
<item name="android:textSize">@dimen/text_little_small</item>
</style>
<style name="CardTitle">
<item name="android:textAppearance">@style/TextAppearance.CardTitle</item>
<item name="android:textAppearance">@style/TextAppearance.Material3.TitleSmall</item>
<item name="android:ellipsize">end</item>
<item name="android:maxLines">2</item>
<item name="android:lineSpacingExtra">0sp</item>
</style>
<style name="TextAppearance.CardMessage">
<item name="android:textSize">@dimen/text_small</item>
</style>
<style name="CardMessage">
<item name="android:textAppearance">@style/TextAppearance.CardMessage</item>
<item name="android:textAppearance">@style/TextAppearance.Material3.BodyMedium</item>
<item name="android:ellipsize">end</item>
<item name="android:singleLine">true</item>
<item name="android:maxLines">1</item>
</style>
<!-- CategoryText -->
<style name="CategoryText">
<item name="android:textAppearance">@style/TextAppearance.Material3.ActionBar.Subtitle
</item>
<item name="android:textStyle">bold</item>
<item name="android:gravity">center</item>
<item name="android:minHeight">@dimen/category_table_item_height</item>
<item name="android:foreground">@drawable/check_text_view_foreground</item>
</style>
<style name="TextAppearance.CategoryTitle" parent="TextAppearance.Material3.TitleMedium">
<item name="android:textStyle">bold</item>
</style>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 KiB

After

Width:  |  Height:  |  Size: 1003 KiB

+2 -2
View File
@@ -24,6 +24,6 @@ task executeDaoGenerator(type:JavaExec) {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.greenrobot:greendao-generator:3.3.0'
implementation 'org.jboss.forge.roaster:roaster-api:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.24.0.Final'
implementation 'org.jboss.forge.roaster:roaster-api:2.25.0.Final'
implementation 'org.jboss.forge.roaster:roaster-jdt:2.25.0.Final'
}
+7 -1
View File
@@ -12,8 +12,14 @@ android {
targetSdkVersion 32
consumerProguardFiles 'proguard-rules.pro'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=none'
}
}
ndk {
abiFilters 'arm64-v8a', 'x86_64'
abiFilters 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
-keep class com.hippo.a7zip.* { *; }
-keep class com.hippo.* { *; }
-keep class com.hippo.image.* { *; }
@@ -5,4 +5,14 @@ public class Native {
public static void initialize() {
System.loadLibrary("ehviewer");
}
public static native String getlibarchiveVersion();
public static native String getliblzmaVersion();
public static native String getlibzstdVersion();
public static native String getlibjpeg_turboVersion();
public static native String getzlibVersion();
}
@@ -0,0 +1,105 @@
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 long skip(long n) throws IOException, ErrnoException {
long pos;
long len;
long newpos;
if (n <= 0) {
return 0;
}
pos = Os.lseek(fd, 0L, OsConstants.SEEK_CUR);
len = length();
newpos = pos + n;
if (newpos > len) {
newpos = len;
}
seek(newpos, OsConstants.SEEK_SET);
/* return the actual number of bytes skipped */
return newpos - pos;
}
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;
}
+78 -80
View File
@@ -55,12 +55,10 @@ public final class Image {
public static final int FORMAT_GIF = 3;
private static final AtomicInteger sImageCount = new AtomicInteger();
private long mNativePtr;
private final int mFormat;
private final int mWidth;
private final int mHeight;
private long mNativePtr;
private Throwable mRecycleTracker;
private Image(long nativePtr, int format, int width, int height) {
@@ -72,6 +70,83 @@ public final class Image {
sImageCount.getAndIncrement();
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decode(InputStream is, boolean partially) {
if (!(is instanceof BufferedInputStream)) {
is = new BufferedInputStream(is);
}
return nativeDecode(is, partially);
}
/**
* Create a plain image from Bitmap
*/
@Nullable
public static Image create(Bitmap bitmap) {
return nativeCreate(bitmap);
}
/**
* Return all un-recycled {@code Image} instance count.
* It is useful for debug.
*/
public static int getImageCount() {
return sImageCount.get();
}
/**
* Return all supported image formats, exclude {@link #FORMAT_PLAIN}
*/
public static int[] getSupportedImageFormats() {
return nativeGetSupportedImageFormats();
}
/**
* Return decoder description of the image format,
* {@code null} for invalid image format.
*/
public static String getDecoderDescription(int format) {
return nativeGetDecoderDescription(format);
}
private static native Image nativeDecode(InputStream is, boolean partially);
private static native Image nativeCreate(Bitmap bitmap);
private static native int nativeGetByteCount(long nativePtr, int format);
private static native boolean nativeComplete(long nativePtr, int format);
private static native boolean nativeIsCompleted(long nativePtr, int format);
private static native void nativeRender(long nativePtr, int format,
int srcX, int srcY, Bitmap dst, int dstX, int dstY,
int width, int height, boolean fillBlank, int defaultColor);
private static native void nativeTexImage(long nativePtr, int format,
boolean init, int offsetX, int offsetY, int width, int height);
private static native void nativeAdvance(long nativePtr, int format);
private static native int nativeGetDelay(long nativePtr, int format);
private static native int nativeFrameCount(long nativePtr, int format);
private static native boolean nativeIsOpaque(long nativePtr, int format);
private static native boolean nativeIsGray(long nativePtr, int format, int error);
private static native void nativeClahe(long nativePtr, int format, boolean toGray);
private static native void nativeRecycle(long nativePtr, int format);
private static native int[] nativeGetSupportedImageFormats();
private static native String nativeGetDecoderDescription(int format);
/**
* Return the format of the image
*/
@@ -218,81 +293,4 @@ public final class Image {
public boolean isRecycled() {
return mNativePtr == 0;
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decode(InputStream is, boolean partially) {
if (!(is instanceof BufferedInputStream)) {
is = new BufferedInputStream(is);
}
return nativeDecode(is, partially);
}
/**
* Create a plain image from Bitmap
*/
@Nullable
public static Image create(Bitmap bitmap) {
return nativeCreate(bitmap);
}
/**
* Return all un-recycled {@code Image} instance count.
* It is useful for debug.
*/
public static int getImageCount() {
return sImageCount.get();
}
/**
* Return all supported image formats, exclude {@link #FORMAT_PLAIN}
*/
public static int[] getSupportedImageFormats() {
return nativeGetSupportedImageFormats();
}
/**
* Return decoder description of the image format,
* {@code null} for invalid image format.
*/
public static String getDecoderDescription(int format) {
return nativeGetDecoderDescription(format);
}
private static native Image nativeDecode(InputStream is, boolean partially);
private static native Image nativeCreate(Bitmap bitmap);
private static native int nativeGetByteCount(long nativePtr, int format);
private static native boolean nativeComplete(long nativePtr, int format);
private static native boolean nativeIsCompleted(long nativePtr, int format);
private static native void nativeRender(long nativePtr, int format,
int srcX, int srcY, Bitmap dst, int dstX, int dstY,
int width, int height, boolean fillBlank, int defaultColor);
private static native void nativeTexImage(long nativePtr, int format,
boolean init, int offsetX, int offsetY, int width, int height);
private static native void nativeAdvance(long nativePtr, int format);
private static native int nativeGetDelay(long nativePtr, int format);
private static native int nativeFrameCount(long nativePtr, int format);
private static native boolean nativeIsOpaque(long nativePtr, int format);
private static native boolean nativeIsGray(long nativePtr, int format, int error);
private static native void nativeClahe(long nativePtr, int format, boolean toGray);
private static native void nativeRecycle(long nativePtr, int format);
private static native int[] nativeGetSupportedImageFormats();
private static native String nativeGetDecoderDescription(int format);
}
@@ -40,19 +40,18 @@ import java.util.Set;
public final class ImageBitmap implements Animatable, Runnable {
private static final Handler HANDLER = new Handler(Looper.getMainLooper());
@Nullable
private Image mImage;
@NonNull
private final Bitmap mBitmap;
private final int mFormat;
private final boolean mIsOpaque;
private final int mByteCount;
private final int mFrameCount;
private final Set<WeakReference<Callback>> mCallbackSet = new LinkedHashSet<>();
@Nullable
private Image mImage;
private int mReferences;
private int mAnimationReferences;
private boolean mRunning;
private final Set<WeakReference<Callback>> mCallbackSet = new LinkedHashSet<>();
private ImageBitmap(@NonNull Image image) throws OutOfMemoryError {
int width = image.getWidth();
+34 -10
View File
@@ -1,17 +1,41 @@
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")
# Build liblzma as shared lib
set(BUILD_SHARED_LIBS ON)
add_subdirectory(liblzma)
add_subdirectory(a7zip)
# Build libzstd
add_subdirectory(libzstd/build/cmake)
# Configure libarchive for android platform
include_directories(libarchive/contrib/android/include)
# Configure lzma support for libarchive
include_directories(liblzma/src/liblzma/api)
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)
# Configure zstd support for libarchive
include_directories(libzstd/lib)
SET(HAVE_LIBZSTD 1)
SET(HAVE_LIBZSTD_COMPRESSOR 1)
SET(HAVE_ZSTD_H 1)
# Build libarchive
add_subdirectory(libarchive)
# Build hippo's image lib
add_subdirectory(image)
add_subdirectory(integrity)
add_library(${PROJECT_NAME} SHARED main.c)
# Build libfuse
add_subdirectory(libfuse)
include_directories(libfuse/libfuse/include)
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)
# Build and link our app's native lib
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 fuse)
-24
View File
@@ -1,24 +0,0 @@
cmake_minimum_required(VERSION 3.4.1)
project(a7zip C CXX)
add_subdirectory(p7zip)
set(A_SEVEN_ZIP_SOURCES
a7zip/BlackHole.cpp
a7zip/InArchive.cpp
a7zip/JavaEnv.cpp
a7zip/JavaHelper.cpp
a7zip/JavaInArchive.cpp
a7zip/JavaInitA7Zip.cpp
a7zip/JavaInputStream.cpp
a7zip/JavaSeekableInputStream.cpp
a7zip/OpenVolumeCallback.cpp
a7zip/OutputStream.cpp
a7zip/SeekableInputStream.cpp
a7zip/SevenZip.cpp
)
add_library(a7zip STATIC ${A_SEVEN_ZIP_SOURCES})
target_link_libraries(a7zip PUBLIC p7zip log)
@@ -1,69 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BlackHole.h"
using namespace a7zip;
BlackHole::BlackHole(): pos(0), size(0) { }
HRESULT BlackHole::Write(const void*, UInt32 size, UInt32* processedSize) {
if (processedSize != nullptr) {
*processedSize = size;
}
this->pos += size;
if (this->size < this->pos) {
this->size = this->pos;
}
return S_OK;
}
HRESULT BlackHole::Seek(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) {
Int64 newPos = -1;
switch (seekOrigin) {
case STREAM_SEEK_SET:
newPos = offset;
break;
case STREAM_SEEK_CUR:
newPos = this->pos + offset;
break;
case STREAM_SEEK_END:
newPos = this->size + offset;
break;
default:
return E_INVALIDARG;
}
if (newPos < 0) {
return STG_E_INVALIDFUNCTION;
}
*newPosition = this->pos;
return S_OK;
}
HRESULT BlackHole::SetSize(UInt64 newSize) {
this->size = newSize;
if (this->pos > this->size) {
this->pos = this->size;
}
return S_OK;
}
@@ -1,47 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_BLACK_HOLE_H__
#define __A7ZIP_BLACK_HOLE_H__
#include <Common/MyCom.h>
#include <7zip/IStream.h>
#include "Log.h"
namespace a7zip {
class BlackHole :
public IOutStream,
public CMyUnknownImp
{
public:
BlackHole();
public:
MY_UNKNOWN_IMP2(ISequentialOutStream, IOutStream)
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
STDMETHOD(SetSize)(UInt64 newSize);
private:
UInt64 pos;
UInt64 size;
};
}
#endif //__A7ZIP_BLACK_HOLE_H__
@@ -1,360 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "InArchive.h"
#include <Windows/PropVariant.h>
#include <7zip/ICoder.h>
#include <7zip/IPassword.h>
#include "BlackHole.h"
#include "Log.h"
#include "Utils.h"
using namespace a7zip;
class ArchiveExtractCallback :
public IArchiveExtractCallback,
public ICryptoGetTextPassword,
public CMyUnknownImp
{
public:
ArchiveExtractCallback(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream);
~ArchiveExtractCallback();
public:
MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICryptoGetTextPassword)
STDMETHOD(SetTotal)(UInt64 total);
STDMETHOD(SetCompleted)(const UInt64 *completeValue);
STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode);
STDMETHOD(PrepareOperation)(Int32 askExtractMode);
STDMETHOD(SetOperationResult)(Int32 opRes);
STDMETHOD(CryptoGetTextPassword)(BSTR *password);
HRESULT GetBetterResult(HRESULT result);
private:
UInt32 index;
BSTR password;
CMyComPtr<ISequentialOutStream> out_stream;
bool has_asked_password;
};
ArchiveExtractCallback::ArchiveExtractCallback(
UInt32 index,
BSTR password,
CMyComPtr<ISequentialOutStream>& out_stream
) :
index(index),
password(::SysAllocString(password)),
out_stream(out_stream),
has_asked_password(false) {}
ArchiveExtractCallback::~ArchiveExtractCallback() {
::SysFreeString(password);
}
HRESULT ArchiveExtractCallback::SetTotal(UInt64 total) {
// Ignored
return S_OK;
}
HRESULT ArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) {
// Ignored
return S_OK;
}
HRESULT ArchiveExtractCallback::GetStream(
UInt32 index,
ISequentialOutStream** outStream,
Int32 askExtractMode
) {
// If it's not extract mode or the index is different, return a black hole to skip data
if (askExtractMode != NArchive::NExtract::NAskMode::kExtract || this->index != index) {
CMyComPtr<ISequentialOutStream> black_hole(new BlackHole());
*outStream = black_hole.Detach();
return S_OK;
}
if (out_stream == nullptr) {
return E_NO_OUT_STREAM;
}
CMyComPtr<ISequentialOutStream> steam_copy(out_stream);
*outStream = steam_copy.Detach();
return S_OK;
}
HRESULT ArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) {
// Always return S_OK
return S_OK;
}
HRESULT ArchiveExtractCallback::SetOperationResult(Int32 opRes) {
// Stop extracting action if operation result is not OK
switch (opRes) {
case NArchive::NExtract::NOperationResult::kOK:
return S_OK;
case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
return E_UNSUPPORTED_METHOD;
case NArchive::NExtract::NOperationResult::kDataError:
return E_DATA_ERROR;
case NArchive::NExtract::NOperationResult::kCRCError:
return E_CRC_ERROR;
case NArchive::NExtract::NOperationResult::kUnavailable:
return E_UNAVAILABLE;
case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
return E_UNEXPECTED_END;
case NArchive::NExtract::NOperationResult::kDataAfterEnd:
return E_DATA_AFTER_END;
case NArchive::NExtract::NOperationResult::kIsNotArc:
return E_IS_NOT_ARC;
case NArchive::NExtract::NOperationResult::kHeadersError:
return E_HEADERS_ERROR;
case NArchive::NExtract::NOperationResult::kWrongPassword:
return E_WRONG_PASSWORD;
default:
return E_UNKNOWN_ERROR;
}
}
HRESULT ArchiveExtractCallback::CryptoGetTextPassword(BSTR* password) {
has_asked_password = true;
*password = ::SysAllocString(this->password);
return this->password != nullptr ? S_OK : E_NO_PASSWORD;
}
HRESULT ArchiveExtractCallback::GetBetterResult(HRESULT result) {
if (result == S_OK) {
return S_OK;
} else if (has_asked_password) {
if (password != nullptr) {
return E_WRONG_PASSWORD;
} else {
return E_NO_PASSWORD;
}
} else {
return result;
}
}
InArchive::InArchive(
InArchive* parent,
CMyComPtr<IInArchive>& in_archive,
AString& format_name
) :
parent(parent),
in_archive(in_archive),
format_name(format_name) { }
InArchive::~InArchive() {
this->in_archive->Close();
if (parent != nullptr) {
delete parent;
parent = nullptr;
}
}
const AString& InArchive::GetFormatName() {
return this->format_name;
}
HRESULT InArchive::GetNumberOfEntries(UInt32& number) {
return this->in_archive->GetNumberOfItems(&number);
}
static PropType VarTypeToPropType(VARTYPE var_enum) {
// TODO VT_ERROR
switch (var_enum) {
case VT_EMPTY:
return PT_EMPTY;
case VT_BOOL:
return PT_BOOL;
case VT_I1:
case VT_I2:
case VT_I4:
case VT_INT:
case VT_UI1:
case VT_UI2:
case VT_UI4:
case VT_UINT:
return PT_INT;
case VT_I8:
case VT_UI8:
case VT_FILETIME:
return PT_LONG;
case VT_BSTR:
return PT_STRING;
default:
return PT_UNKNOWN;
}
}
#define GET_ARCHIVE_PROPERTY_START(METHOD_NAME, VALUE_TYPE) \
HRESULT InArchive::METHOD_NAME(PROPID prop_id, VALUE_TYPE value) { \
NWindows::NCOM::CPropVariant prop; \
RETURN_SAME_IF_NOT_ZERO(this->in_archive->GetArchiveProperty(prop_id, &prop));
#define GET_ARCHIVE_PROPERTY_END \
}
#define GET_ENTRY_PROPERTY_START(METHOD_NAME, VALUE_TYPE) \
HRESULT InArchive::METHOD_NAME(UInt32 index, PROPID prop_id, VALUE_TYPE value) { \
NWindows::NCOM::CPropVariant prop; \
RETURN_SAME_IF_NOT_ZERO(this->in_archive->GetProperty(index, prop_id, &prop));
#define GET_ENTRY_PROPERTY_END \
}
#define GET_PROPERTY_TYPE \
*value = VarTypeToPropType(prop.vt); \
return S_OK;
GET_ARCHIVE_PROPERTY_START(GetArchivePropertyType, PropType*)
GET_PROPERTY_TYPE
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryPropertyType, PropType*)
GET_PROPERTY_TYPE
GET_ENTRY_PROPERTY_END
#define GET_BOOLEAN_PROPERTY \
switch (prop.vt) { \
case VT_BOOL: \
*value = prop.boolVal != 0; \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveBooleanProperty, bool*)
GET_BOOLEAN_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryBooleanProperty, bool*)
GET_BOOLEAN_PROPERTY
GET_ENTRY_PROPERTY_END
#define GET_INT_PROPERTY \
switch (prop.vt) { \
case VT_I1: \
*value = prop.cVal; \
return S_OK; \
case VT_I2: \
*value = prop.iVal; \
return S_OK; \
case VT_I4: \
*value = prop.lVal; \
return S_OK; \
case VT_INT: \
*value = prop.intVal; \
return S_OK; \
case VT_UI1: \
*value = prop.bVal; \
return S_OK; \
case VT_UI2: \
*value = prop.uiVal; \
return S_OK; \
case VT_UI4: \
*value = prop.ulVal; \
return S_OK; \
case VT_UINT: \
*value = prop.uintVal; \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveIntProperty, Int32*)
GET_INT_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryIntProperty, Int32*)
GET_INT_PROPERTY
GET_ENTRY_PROPERTY_END
static const UInt64 FILE_TIME_OFFSET = (369 * 365 + 89) * 86400ULL * 10000000ULL;
static const UInt64 FILE_TIME_MULTIPLE = 10000ULL;
#define GET_LONG_PROPERTY \
switch (prop.vt) { \
case VT_I8: \
*value = prop.hVal.QuadPart; \
return S_OK; \
case VT_UI8: \
*value = prop.uhVal.QuadPart; \
return S_OK; \
case VT_FILETIME: \
*value = (*reinterpret_cast<UInt64*>(&prop.filetime) - FILE_TIME_OFFSET) / \
FILE_TIME_MULTIPLE; \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveLongProperty, Int64*)
GET_LONG_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryLongProperty, Int64*)
GET_LONG_PROPERTY
GET_ENTRY_PROPERTY_END
#define GET_STRING_PROPERTY \
switch (prop.vt) { \
case VT_BSTR: \
*value = ::SysAllocString(prop.bstrVal); \
return S_OK; \
case VT_EMPTY: \
return E_EMPTY_PROP; \
default: \
return E_INCONSISTENT_PROP_TYPE; \
}
GET_ARCHIVE_PROPERTY_START(GetArchiveStringProperty, BSTR*)
GET_STRING_PROPERTY
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(GetEntryStringProperty, BSTR*)
GET_STRING_PROPERTY
GET_ENTRY_PROPERTY_END
HRESULT InArchive::GetEntryStream(UInt32 index, ISequentialInStream** stream) {
*stream = nullptr;
CMyComPtr<IInArchiveGetStream> in_archive_get_stream;
in_archive->QueryInterface(IID_IInArchiveGetStream, reinterpret_cast<void **>(&in_archive_get_stream));
if (in_archive_get_stream != nullptr) {
return in_archive_get_stream->GetStream(index, stream);
} else {
return E_NOTIMPL;
}
}
HRESULT InArchive::ExtractEntry(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream) {
CMyComPtr<ArchiveExtractCallback> callback(new ArchiveExtractCallback(index, password, out_stream));
HRESULT result = this->in_archive->Extract(&index, 1, false, callback);
return callback->GetBetterResult(result);
}
@@ -1,62 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_IN_ARCHIVE_H__
#define __A7ZIP_IN_ARCHIVE_H__
#include <include_windows/windows.h>
#include <Common/MyCom.h>
#include <Common/MyString.h>
#include <7zip/Archive/IArchive.h>
#include "PropType.h"
namespace a7zip {
class InArchive {
public:
InArchive(InArchive* parent, CMyComPtr<IInArchive>& in_archive, AString& format_name);
~InArchive();
public:
const AString& GetFormatName();
HRESULT GetNumberOfEntries(UInt32& number);
HRESULT GetArchivePropertyType(PROPID prop_id, PropType* prop_type);
HRESULT GetArchiveBooleanProperty(PROPID prop_id, bool *bool_prop);
HRESULT GetArchiveIntProperty(PROPID prop_id, Int32* int_prop);
HRESULT GetArchiveLongProperty(PROPID prop_id, Int64* long_prop);
HRESULT GetArchiveStringProperty(PROPID prop_id, BSTR* str_prop);
HRESULT GetEntryPropertyType(UInt32 index, PROPID prop_id, PropType* prop_type);
HRESULT GetEntryBooleanProperty(UInt32 index, PROPID prop_id, bool *bool_prop);
HRESULT GetEntryIntProperty(UInt32 index, PROPID prop_id, Int32* int_prop);
HRESULT GetEntryLongProperty(UInt32 index, PROPID prop_id, Int64* long_prop);
HRESULT GetEntryStringProperty(UInt32 index, PROPID prop_id, BSTR* str_prop);
HRESULT GetEntryStream(UInt32 index, ISequentialInStream** stream);
HRESULT ExtractEntry(UInt32 index, BSTR password, CMyComPtr<ISequentialOutStream>& out_stream);
private:
InArchive* parent;
CMyComPtr<IInArchive> in_archive;
AString format_name;
};
}
#endif //__A7ZIP_IN_ARCHIVE_H__
@@ -1,46 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaEnv.h"
using namespace a7zip;
JavaVM* JavaEnv::jvm = nullptr;
void JavaEnv::Initialize(JavaVM* jvm) {
JavaEnv::jvm = jvm;
}
JavaEnv::JavaEnv() {
env = nullptr;
if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_EDETACHED) {
should_detach = jvm->AttachCurrentThread(&env, nullptr) == JNI_OK;
} else {
should_detach = false;
}
}
JavaEnv::~JavaEnv() {
if (jvm != nullptr && should_detach) {
jvm->DetachCurrentThread();
env = nullptr;
should_detach = false;
}
}
bool JavaEnv::IsValid() {
return env != nullptr;
}
-46
View File
@@ -1,46 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_ENV_H__
#define __A7ZIP_JAVA_ENV_H__
#include <jni.h>
namespace a7zip {
class JavaEnv {
public:
JavaEnv();
~JavaEnv();
bool IsValid();
JNIEnv* operator->() const { return env; }
explicit operator JNIEnv*() const { return env; }
private:
JNIEnv* env;
bool should_detach;
public:
static void Initialize(JavaVM* jvm);
private:
static JavaVM* jvm;
};
}
#endif //__A7ZIP_JAVA_ENV_H__
@@ -1,121 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaHelper.h"
#include <jni.h>
#include <cstdio>
#include "Utils.h"
#define MAX_MSG_SIZE 1024
using namespace a7zip;
static jint ThrowExceptionInternal(
JNIEnv* env,
const char* exception_name,
const char* message,
va_list va_args
) {
char formatted_message[MAX_MSG_SIZE];
vsnprintf(formatted_message, MAX_MSG_SIZE, message, va_args);
jclass exception_class = env->FindClass(exception_name);
return env->ThrowNew(exception_class, formatted_message);
}
jint JavaHelper::ThrowException(
JNIEnv* env,
const char* exception_name,
const char* message,
...
) {
va_list va_args;
va_start(va_args, message);
jint result = ThrowExceptionInternal(env, exception_name, message, va_args);
va_end(va_args);
return result;
}
static const char* GetMessageForCode(HRESULT code) {
switch (code) {
case E_NOT_INITIALIZED:
return "The module is not initialized";
case S_OK:
case E_INTERNAL:
return "a7zip is buggy";
case E_CLASS_NOT_FOUND:
return "Can't find the class";
case E_METHOD_NOT_FOUND:
return "Can't find the method";
case E_JAVA_EXCEPTION:
return "Catch a java exception";
case E_FAILED_CONSTRUCT:
return "Failed to create new class";
case E_FAILED_REGISTER:
return "Failed to register methods";
case E_INCONSISTENT_PROP_TYPE:
return "Inconsistent property type";
case E_EMPTY_PROP:
return "Empty property";
case E_UNKNOWN_FORMAT:
return "Unknown archive format";
case E_UNSUPPORTED_EXTRACT_MODE:
return "Unsupported extract mode";
case E_NO_OUT_STREAM:
return "No out stream";
case E_UNSUPPORTED_METHOD:
return "Unsupported method";
case E_DATA_ERROR:
return "Data error";
case E_DATA_ERROR_ENCRYPTED:
return "Data Error in encrypted file. Wrong password?";
case E_CRC_ERROR:
return "CRC failed";
case E_CRC_ERROR_ENCRYPTED:
return "CRC Failed in encrypted file. Wrong password?";
case E_UNAVAILABLE:
return "Unavailable data";
case E_UNEXPECTED_END:
return "Unexpected end of data";
case E_DATA_AFTER_END:
return "There are some data after the end of the payload data";
case E_IS_NOT_ARC:
return "Is not archive";
case E_HEADERS_ERROR:
return "Headers Error";
case E_WRONG_PASSWORD:
return "Wrong password";
case E_NO_PASSWORD:
return "No password";
case E_NOTIMPL:
return "Not implemented";
case E_OUTOFMEMORY:
return "Out of memory";
case E_UNKNOWN_ERROR:
default:
return "Unknown error.";
}
}
jint JavaHelper::ThrowException(
JNIEnv* env,
const char* exception_name,
HRESULT code
) {
return ThrowException(env, exception_name, GetMessageForCode(code));
}
@@ -1,89 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_HELPER_H__
#define __A7ZIP_JAVA_HELPER_H__
#include <jni.h>
#include <Common/MyWindows.h>
#define CLASS_NAME_ILLEGAL_STATE_EXCEPTION "java/lang/IllegalStateException"
#define CLASS_NAME_IO_EXCEPTION "java/io/IOException"
#define CLASS_NAME_ARCHIVE_EXCEPTION "com/hippo/a7zip/ArchiveException"
#define CLASS_NAME_PASSWORD_EXCEPTION "com/hippo/a7zip/PasswordException"
#define THROW_EXCEPTION(ENV, EXCEPTION_NAME, ...) \
do { \
a7zip::JavaHelper::ThrowException((ENV), (EXCEPTION_NAME), __VA_ARGS__); \
return; \
} while (0)
#define THROW_EXCEPTION_RET(ENV, RET, EXCEPTION_NAME, ...) \
do { \
a7zip::JavaHelper::ThrowException((ENV), (EXCEPTION_NAME), __VA_ARGS__); \
return (RET); \
} while (0)
#define THROW_IO_EXCEPTION(ENV, CODE) \
THROW_EXCEPTION(ENV, CLASS_NAME_IO_EXCEPTION, CODE)
#define THROW_IO_EXCEPTION_RET(ENV, RET, CODE) \
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_IO_EXCEPTION, CODE)
#define THROW_ARCHIVE_EXCEPTION(ENV, CODE) \
do { \
if ((CODE) == E_NO_PASSWORD || (CODE) == E_WRONG_PASSWORD) { \
THROW_EXCEPTION(ENV, CLASS_NAME_PASSWORD_EXCEPTION, CODE); \
} else { \
THROW_EXCEPTION(ENV, CLASS_NAME_ARCHIVE_EXCEPTION, CODE); \
} \
} while (0)
#define THROW_ARCHIVE_EXCEPTION_RET(ENV, RET, CODE) \
do { \
if ((CODE) == E_NO_PASSWORD || (CODE) == E_WRONG_PASSWORD) { \
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_PASSWORD_EXCEPTION, CODE); \
} else { \
THROW_EXCEPTION_RET(ENV, RET, CLASS_NAME_ARCHIVE_EXCEPTION, CODE); \
} \
} while (0)
#define CHECK_CLOSED(ENV, NATIVE_PTR) \
do { \
if ((NATIVE_PTR) == 0) { \
THROW_EXCEPTION(ENV, CLASS_NAME_ILLEGAL_STATE_EXCEPTION, "It's closed"); \
} \
} while (0)
#define CHECK_CLOSED_RET(ENV, RET, NATIVE_PTR) \
do { \
if ((NATIVE_PTR) == 0) { \
THROW_EXCEPTION_RET( \
ENV, RET, CLASS_NAME_ILLEGAL_STATE_EXCEPTION, "It's closed"); \
} \
} while (0)
namespace a7zip {
namespace JavaHelper {
jint ThrowException(JNIEnv* env, const char* exception_name, const char* message, ...);
jint ThrowException(JNIEnv* env, const char* exception_name, HRESULT code);
}
}
#endif //__A7ZIP_JAVA_HELPER_H__
@@ -1,397 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaInArchive.h"
#include <cstdio>
#include <type_traits>
#include <include_windows/windows.h>
#include <7zip/Archive/IArchive.h>
#include "OpenVolumeCallback.h"
#include "SeekableInputStream.h"
#include "JavaHelper.h"
#include "JavaSeekableInputStream.h"
#include "JavaInputStream.h"
#include "Log.h"
#include "OutputStream.h"
#include "SevenZip.h"
#include "Utils.h"
#ifdef LOG_TAG
# undef LOG_TAG
#endif //LOG_TAG
#define LOG_TAG "JavaArchive"
using namespace a7zip;
static void CopyJStringToBSTR(BSTR bstr, const jchar* jstr, int length) {
for (int i = 0; i < length; i++) {
*bstr++ = *jstr++;
}
*bstr = 0;
}
static BSTR JStringToBSTR(JNIEnv* env, jstring jstr) {
BSTR bstr = nullptr;
if (jstr != nullptr) {
jsize length = env->GetStringLength(jstr);
const jchar* jchars = env->GetStringChars(jstr, nullptr);
bstr = ::SysAllocStringLen(nullptr, static_cast<UINT>(length));
CopyJStringToBSTR(bstr, jchars, length);
env->ReleaseStringChars(jstr, jchars);
}
return bstr;
}
static jlong NativeOpen(
JNIEnv* env,
jclass,
jobject stream,
jstring password,
jstring filename,
jobject open_volume_callback
) {
CMyComPtr<IInStream> in_stream = nullptr;
HRESULT result = SeekableInputStream::Create(env, stream, in_stream);
if (result != S_OK || in_stream == nullptr) {
// Call java methods before throw exception
if (in_stream != nullptr) {
in_stream.Release();
}
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
}
BSTR bstr_password = nullptr;
BSTR bstr_filename = nullptr;
CMyComPtr<OpenVolumeCallback> open_volume_callback_wrapper = nullptr;
if (filename != nullptr && open_volume_callback != nullptr) {
result = OpenVolumeCallback::Create(env, open_volume_callback, open_volume_callback_wrapper);
if (result != S_OK) {
// Call java methods before throw exception
if (in_stream != nullptr) {
in_stream.Release();
}
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
}
bstr_filename = JStringToBSTR(env, filename);
}
bstr_password = JStringToBSTR(env, password);
InArchive* archive = nullptr;
result = SevenZip::OpenArchive(in_stream, bstr_password, bstr_filename, open_volume_callback_wrapper, &archive);
::SysFreeString(bstr_password);
::SysFreeString(bstr_filename);
if (result != S_OK || archive == nullptr) {
// Call java methods before throw exception
in_stream.Release();
delete archive;
THROW_ARCHIVE_EXCEPTION_RET(env, 0, result);
}
return reinterpret_cast<jlong>(archive);
}
static jstring NativeGetFormatName(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, nullptr, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
return env->NewStringUTF(archive->GetFormatName());
}
static jint NativeGetNumberOfEntries(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
UInt32 number = 0;
HRESULT result = archive->GetNumberOfEntries(number);
return result == S_OK ? number : -1;
}
#define GET_ARCHIVE_PROPERTY_START(METHOD_NAME, RETURN_TYPE) \
static RETURN_TYPE METHOD_NAME(JNIEnv* env, jclass, jlong native_ptr, jint prop_id) { \
CHECK_CLOSED_RET(env, 0, native_ptr); \
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
#define GET_ARCHIVE_PROPERTY_END \
}
#define GET_ENTRY_PROPERTY_START(METHOD_NAME, RETURN_TYPE) \
static RETURN_TYPE METHOD_NAME(JNIEnv* env, jclass, jlong native_ptr, jint index, jint prop_id) {\
CHECK_CLOSED_RET(env, 0, native_ptr); \
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
#define GET_ENTRY_PROPERTY_END \
}
#define GET_PROPERTY_TYPE(GETTER) \
PropType prop_type; \
HRESULT result = (GETTER); \
return result == S_OK ? prop_type : PT_UNKNOWN;
GET_ARCHIVE_PROPERTY_START(NativeGetArchivePropertyType, jint)
GET_PROPERTY_TYPE(archive->GetArchivePropertyType(static_cast<PROPID>(prop_id), &prop_type))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryPropertyType, jint)
GET_PROPERTY_TYPE(archive->GetEntryPropertyType(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &prop_type))
GET_ENTRY_PROPERTY_END
#define GET_BOOL_TYPE(GETTER) \
bool bool_prop; \
HRESULT result = (GETTER); \
return result == S_OK ? bool_prop : false;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveBooleanProperty, jboolean)
GET_BOOL_TYPE(archive->GetArchiveBooleanProperty(static_cast<PROPID>(prop_id), &bool_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryBooleanProperty, jboolean)
GET_BOOL_TYPE(archive->GetEntryBooleanProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &bool_prop))
GET_ENTRY_PROPERTY_END
#define GET_INT_TYPE(GETTER) \
Int32 int_prop; \
HRESULT result = (GETTER); \
return result == S_OK ? int_prop : 0;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveIntProperty, jint)
GET_INT_TYPE(archive->GetArchiveIntProperty(static_cast<PROPID>(prop_id), &int_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryIntProperty, jint)
GET_INT_TYPE(archive->GetEntryIntProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &int_prop))
GET_ENTRY_PROPERTY_END
#define GET_LONG_TYPE(GETTER) \
Int64 long_prop; \
HRESULT result = (GETTER); \
return result == S_OK ? long_prop : 0;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveLongProperty, jlong)
GET_LONG_TYPE(archive->GetArchiveLongProperty(static_cast<PROPID>(prop_id), &long_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryLongProperty, jlong)
GET_LONG_TYPE(archive->GetEntryLongProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &long_prop))
GET_ENTRY_PROPERTY_END
static void shrink(BSTR bstr) {
jchar* jstr = reinterpret_cast<jchar*>(bstr);
UINT n = ::SysStringLen(bstr);
for (UINT i = 0; i < n; i++) {
jstr[i] = static_cast<jchar>(bstr[i]);
}
jstr[n] = 0;
}
#define GET_STRING_PROPERTY(GETTER) \
BSTR str_prop = nullptr; \
HRESULT result = (GETTER); \
if (result != S_OK || str_prop == nullptr) { \
if (str_prop != nullptr) ::SysFreeString(str_prop); \
return nullptr; \
} \
shrink(str_prop); \
jstring jstr = \
env->NewString(reinterpret_cast<const jchar*>(str_prop), ::SysStringLen(str_prop)); \
::SysFreeString(str_prop); \
return jstr;
GET_ARCHIVE_PROPERTY_START(NativeGetArchiveStringProperty, jstring)
GET_STRING_PROPERTY(archive->GetArchiveStringProperty(static_cast<PROPID>(prop_id), &str_prop))
GET_ARCHIVE_PROPERTY_END
GET_ENTRY_PROPERTY_START(NativeGetEntryStringProperty, jstring)
GET_STRING_PROPERTY(archive->GetEntryStringProperty(static_cast<UInt32>(index), static_cast<PROPID>(prop_id), &str_prop))
GET_ENTRY_PROPERTY_END
static jobject NativeGetEntryStream(
JNIEnv* env,
jclass,
jlong native_ptr,
jint index
) {
CHECK_CLOSED_RET(env, nullptr, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
CMyComPtr<ISequentialInStream> sequential_in_stream = nullptr;
HRESULT result = archive->GetEntryStream(static_cast<UInt32>(index), &sequential_in_stream);
if (result != S_OK || sequential_in_stream == nullptr) {
if (sequential_in_stream != nullptr) {
// Release the stream manually before throw java exception
sequential_in_stream.Release();
}
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
}
// Try to get IInStream
CMyComPtr<IInStream> in_stream = nullptr;
sequential_in_stream.QueryInterface(IID_IInStream, &in_stream);
if (in_stream != nullptr) {
// It's an IInStream
jobject object = nullptr;
result = JavaSeekableInputStream::NewInstance(env, in_stream, &object);
if (object == nullptr) {
// Release the stream manually before throw java exception
in_stream->Release();
sequential_in_stream->Release();
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
}
return object;
} else {
// It's just an ISequentialInStream
jobject object = nullptr;
result = JavaInputStream::NewInstance(env, sequential_in_stream, &object);
if (object == nullptr) {
// Release the stream manually before throw java exception
sequential_in_stream->Release();
THROW_ARCHIVE_EXCEPTION_RET(env, nullptr, result);
}
return object;
}
}
static void NativeExtractEntry(
JNIEnv* env,
jclass,
jlong native_ptr,
jint index,
jstring password,
jobject stream
) {
CHECK_CLOSED(env, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
CMyComPtr<ISequentialOutStream> out_stream = nullptr;
HRESULT result = OutputStream::Create(env, stream, out_stream);
if (result != S_OK || out_stream == nullptr) {
if (out_stream != nullptr) {
// Call java methods before throw exception
out_stream.Release();
} else {
// Let java code closes the OutStream
}
THROW_ARCHIVE_EXCEPTION(env, result);
}
const jchar* j_password = nullptr;
BSTR bstr_password = nullptr;
if (password != nullptr) {
jsize length = env->GetStringLength(password);
j_password = env->GetStringChars(password, nullptr);
bstr_password = ::SysAllocStringLen(nullptr, static_cast<UINT>(length));
CopyJStringToBSTR(bstr_password, j_password, length);
}
result = archive->ExtractEntry(static_cast<UInt32>(index), bstr_password, out_stream);
if (password != nullptr) {
::SysFreeString(bstr_password);
env->ReleaseStringChars(password, j_password);
}
if (result != S_OK) {
// Call java methods before throw exception
out_stream.Release();
THROW_ARCHIVE_EXCEPTION(env, result);
}
}
static void NativeClose(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED(env, native_ptr);
InArchive* archive = reinterpret_cast<InArchive*>(native_ptr);
delete archive;
}
static JNINativeMethod archive_methods[] = {
{ "nativeOpen",
"(Lcom/hippo/a7zip/SeekableInputStream;Ljava/lang/String;Ljava/lang/String;Lcom/hippo/a7zip/InArchive$OpenVolumeCallback;)J",
reinterpret_cast<void *>(NativeOpen) },
{ "nativeGetFormatName",
"(J)Ljava/lang/String;",
reinterpret_cast<void *>(NativeGetFormatName) },
{ "nativeGetNumberOfEntries",
"(J)I",
reinterpret_cast<void *>(NativeGetNumberOfEntries) },
{ "nativeGetArchivePropertyType",
"(JI)I",
reinterpret_cast<void *>(NativeGetArchivePropertyType) },
{ "nativeGetArchiveBooleanProperty",
"(JI)Z",
reinterpret_cast<void *>(NativeGetArchiveBooleanProperty) },
{ "nativeGetArchiveIntProperty",
"(JI)I",
reinterpret_cast<void *>(NativeGetArchiveIntProperty) },
{ "nativeGetArchiveLongProperty",
"(JI)J",
reinterpret_cast<void *>(NativeGetArchiveLongProperty) },
{ "nativeGetArchiveStringProperty",
"(JI)Ljava/lang/String;",
reinterpret_cast<void *>(NativeGetArchiveStringProperty) },
{ "nativeGetEntryPropertyType",
"(JII)I",
reinterpret_cast<void *>(NativeGetEntryPropertyType) },
{ "nativeGetEntryBooleanProperty",
"(JII)Z",
reinterpret_cast<void *>(NativeGetEntryBooleanProperty) },
{ "nativeGetEntryIntProperty",
"(JII)I",
reinterpret_cast<void *>(NativeGetEntryIntProperty) },
{ "nativeGetEntryLongProperty",
"(JII)J",
reinterpret_cast<void *>(NativeGetEntryLongProperty) },
{ "nativeGetEntryStringProperty",
"(JII)Ljava/lang/String;",
reinterpret_cast<void *>(NativeGetEntryStringProperty) },
{ "nativeGetEntryStream",
"(JI)Ljava/io/InputStream;",
reinterpret_cast<void *>(NativeGetEntryStream) },
{ "nativeExtractEntry",
"(JILjava/lang/String;Ljava/io/OutputStream;)V",
reinterpret_cast<void *>(NativeExtractEntry) },
{ "nativeClose",
"(J)V",
reinterpret_cast<void *>(NativeClose) }
};
HRESULT JavaInArchive::RegisterMethods(JNIEnv* env) {
jclass clazz = env->FindClass("com/hippo/a7zip/InArchive");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
jint result = env->RegisterNatives(clazz, archive_methods, std::extent<decltype(archive_methods)>::value);
if (result < 0) {
return E_FAILED_REGISTER;
}
return S_OK;
}
@@ -1,32 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_IN_ARCHIVE_H__
#define __A7ZIP_JAVA_IN_ARCHIVE_H__
#include <jni.h>
#include <Common/MyWindows.h>
namespace a7zip {
namespace JavaInArchive {
HRESULT RegisterMethods(JNIEnv* env);
}
}
#endif //__A7ZIP_JAVA_IN_ARCHIVE_H__
@@ -1,60 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include "SeekableInputStream.h"
#include "JavaEnv.h"
#include "JavaInArchive.h"
#include "JavaInitA7Zip.h"
#include "JavaSeekableInputStream.h"
#include "JavaInputStream.h"
#include "OpenVolumeCallback.h"
#include "OutputStream.h"
#include "SevenZip.h"
#include "Utils.h"
using namespace a7zip;
#ifdef __cplusplus
extern "C"
{
#endif
bool a7zip_onLoad(JavaVM *vm) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
return false;
}
JavaEnv::Initialize(vm);
RETURN_JNI_ERR_IF_NOT_ZERO(SeekableInputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(JavaSeekableInputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(OpenVolumeCallback::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(OutputStream::Initialize(env));
RETURN_JNI_ERR_IF_NOT_ZERO(SevenZip::Initialize());
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInArchive::RegisterMethods(static_cast<JNIEnv *>(env)));
RETURN_JNI_ERR_IF_NOT_ZERO(
JavaSeekableInputStream::RegisterMethods(static_cast<JNIEnv *>(env)));
RETURN_JNI_ERR_IF_NOT_ZERO(JavaInputStream::RegisterMethods(static_cast<JNIEnv *>(env)));
return true;
}
#ifdef __cplusplus
}
#endif
@@ -1,11 +0,0 @@
#include <jni.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
bool a7zip_onLoad(JavaVM *vm);
#ifdef __cplusplus
}
#endif
@@ -1,126 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaInputStream.h"
#include <type_traits>
#include <Common/MyCom.h>
#include "JavaHelper.h"
#include "Utils.h"
using namespace a7zip;
static jint NativeRead(
JNIEnv* env,
jclass,
jlong native_ptr,
jbyteArray array,
jint offset,
jint length
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
ISequentialInStream* stream = reinterpret_cast<ISequentialInStream*>(native_ptr);
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
if (bytes == nullptr) THROW_IO_EXCEPTION_RET(env, 0, E_JAVA_EXCEPTION);
UInt32 processed_size;
HRESULT result = stream->Read(bytes + offset, static_cast<UInt32>(length), &processed_size);
env->ReleaseByteArrayElements(array, bytes, 0);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
if (length != 0 && processed_size == 0) {
// EOF
return -1;
}
return processed_size;
}
static void NativeClose(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED(env, native_ptr);
ISequentialInStream* stream = reinterpret_cast<ISequentialInStream*>(native_ptr);
stream->Release();
}
static bool initialized = false;
static jclass class_native_sequential_in_stream = nullptr;
static jmethodID constructor_native_sequential_in_stream = nullptr;
HRESULT JavaInputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
class_native_sequential_in_stream = env->FindClass("com/hippo/a7zip/NativeInputStream");
if (class_native_sequential_in_stream == nullptr) return E_CLASS_NOT_FOUND;
class_native_sequential_in_stream = static_cast<jclass>(env->NewGlobalRef(class_native_sequential_in_stream));
if (class_native_sequential_in_stream == nullptr) return E_OUTOFMEMORY;
constructor_native_sequential_in_stream = env->GetMethodID(class_native_sequential_in_stream, "<init>", "(J)V");
if (constructor_native_sequential_in_stream == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return JNI_OK;
}
static JNINativeMethod stream_methods[] = {
{ "nativeRead",
"(J[BII)I",
reinterpret_cast<void *>(NativeRead) },
{ "nativeClose",
"(J)V",
reinterpret_cast<void *>(NativeClose) }
};
HRESULT JavaInputStream::RegisterMethods(JNIEnv* env) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jint result = env->RegisterNatives(class_native_sequential_in_stream, stream_methods, std::extent<decltype(stream_methods)>::value);
if (result < 0) {
return E_FAILED_REGISTER;
}
return S_OK;
}
HRESULT JavaInputStream::NewInstance(JNIEnv* env, ISequentialInStream* stream, jobject* object) {
*object = nullptr;
if (!initialized) {
return E_NOT_INITIALIZED;
}
*object = env->NewObject(class_native_sequential_in_stream, constructor_native_sequential_in_stream, stream);
if (*object == nullptr) {
// Clear java exception and return
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// No java exception, but it's still an error
return E_UNKNOWN_ERROR;
}
// Ignore if any exception is pending
CLEAR_IF_EXCEPTION_PENDING(env);
return S_OK;
}
@@ -1,37 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
#define __A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
#include <jni.h>
#include <Common/MyWindows.h>
#include <7zip/IStream.h>
namespace a7zip {
namespace JavaInputStream {
HRESULT Initialize(JNIEnv* env);
HRESULT RegisterMethods(JNIEnv* env);
HRESULT NewInstance(JNIEnv* env, ISequentialInStream* stream, jobject* object);
}
}
#endif //__A7ZIP_JAVA_SEQUENTIAL_IN_STREAM_H__
@@ -1,202 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaSeekableInputStream.h"
#include <type_traits>
#include <Common/MyCom.h>
#include "JavaHelper.h"
#include "Utils.h"
using namespace a7zip;
static void NativeSeek(
JNIEnv* env,
jclass,
jlong native_ptr,
jlong pos
) {
CHECK_CLOSED(env, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
UInt64 newPosition; // Ignored
HRESULT result = stream->Seek(pos, STREAM_SEEK_SET, &newPosition);
if (result != S_OK) THROW_IO_EXCEPTION(env, result);
}
static jlong NativeTell(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
UInt64 newPosition;
HRESULT result = stream->Seek(0, STREAM_SEEK_CUR, &newPosition);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
return static_cast<jlong>(newPosition);
}
static jlong NativeSize(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
// Try to get IStreamGetSize
CMyComPtr<IStreamGetSize> stream_get_size;
stream->QueryInterface(IID_IStreamGetSize, reinterpret_cast<void **>(&stream_get_size));
if (stream_get_size != nullptr) {
// It's an IStreamGetSize
UInt64 size;
HRESULT result = stream_get_size->GetSize(&size);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
return static_cast<jlong>(size);
} else {
// It's not an IStreamGetSize
// Backup current position
UInt64 cur;
HRESULT result = stream->Seek(0, STREAM_SEEK_CUR, &cur);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
// Seek to the end of steam to get size
UInt64 size;
result = stream->Seek(0, STREAM_SEEK_END, &size);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
// Back to previous position
UInt64 ignored;
result = stream->Seek(cur, STREAM_SEEK_SET, &ignored);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
return static_cast<jlong>(size);
}
}
static jint NativeRead(
JNIEnv* env,
jclass,
jlong native_ptr,
jbyteArray array,
jint offset,
jint length
) {
CHECK_CLOSED_RET(env, 0, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
if (bytes == nullptr) THROW_IO_EXCEPTION_RET(env, 0, E_JAVA_EXCEPTION);
UInt32 processed_size;
HRESULT result = stream->Read(bytes + offset, static_cast<UInt32>(length), &processed_size);
env->ReleaseByteArrayElements(array, bytes, 0);
if (result != S_OK) THROW_IO_EXCEPTION_RET(env, 0, result);
if (length != 0 && processed_size == 0) {
// EOF
return -1;
}
return processed_size;
}
static void NativeClose(
JNIEnv* env,
jclass,
jlong native_ptr
) {
CHECK_CLOSED(env, native_ptr);
IInStream* stream = reinterpret_cast<IInStream*>(native_ptr);
stream->Release();
}
static bool initialized = false;
static jclass class_native_in_stream = nullptr;
static jmethodID constructor_native_in_stream = nullptr;
HRESULT JavaSeekableInputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
class_native_in_stream = env->FindClass("com/hippo/a7zip/NativeSeekableInputStream");
if (class_native_in_stream == nullptr) return E_CLASS_NOT_FOUND;
class_native_in_stream = static_cast<jclass>(env->NewGlobalRef(class_native_in_stream));
if (class_native_in_stream == nullptr) return E_OUTOFMEMORY;
constructor_native_in_stream = env->GetMethodID(class_native_in_stream, "<init>", "(J)V");
if (constructor_native_in_stream == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return JNI_OK;
}
static JNINativeMethod stream_methods[] = {
{ "nativeSeek",
"(JJ)V",
reinterpret_cast<void *>(NativeSeek) },
{ "nativeTell",
"(J)J",
reinterpret_cast<void *>(NativeTell) },
{ "nativeSize",
"(J)J",
reinterpret_cast<void *>(NativeSize) },
{ "nativeRead",
"(J[BII)I",
reinterpret_cast<void *>(NativeRead) },
{ "nativeClose",
"(J)V",
reinterpret_cast<void *>(NativeClose) }
};
HRESULT JavaSeekableInputStream::RegisterMethods(JNIEnv* env) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jint result = env->RegisterNatives(class_native_in_stream, stream_methods, std::extent<decltype(stream_methods)>::value);
if (result < 0) {
return E_FAILED_REGISTER;
}
return S_OK;
}
HRESULT JavaSeekableInputStream::NewInstance(JNIEnv* env, IInStream* stream, jobject* object) {
*object = nullptr;
if (!initialized) {
return E_NOT_INITIALIZED;
}
*object = env->NewObject(class_native_in_stream, constructor_native_in_stream, stream);
if (*object == nullptr) {
// Clear java exception and return
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// No java exception, but it's still an error
return E_UNKNOWN_ERROR;
}
// Ignore if any exception is pending
CLEAR_IF_EXCEPTION_PENDING(env);
return S_OK;
}
@@ -1,37 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_JAVA_IN_STREAM_H__
#define __A7ZIP_JAVA_IN_STREAM_H__
#include <jni.h>
#include <Common/MyWindows.h>
#include <7zip/IStream.h>
namespace a7zip {
namespace JavaSeekableInputStream {
HRESULT Initialize(JNIEnv* env);
HRESULT RegisterMethods(JNIEnv* env);
HRESULT NewInstance(JNIEnv* env, IInStream* stream, jobject* object);
}
}
#endif //__A7ZIP_JAVA_IN_STREAM_H__
-14
View File
@@ -1,14 +0,0 @@
#ifndef __A7ZIP_LOG_H__
#define __A7ZIP_LOG_H__
#include <android/log.h>
#define LOG_TAG "a7zip"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
#endif //__A7ZIP_LOG_H__
@@ -1,111 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "OpenVolumeCallback.h"
#include <malloc.h>
#include <MyString.h>
#include "SeekableInputStream.h"
#include "JavaEnv.h"
#include "Utils.h"
#include "Log.h"
using namespace a7zip;
bool OpenVolumeCallback::initialized = false;
jmethodID OpenVolumeCallback::method_open_volume = nullptr;
OpenVolumeCallback::OpenVolumeCallback(jobject callback) : callback(callback) { }
OpenVolumeCallback::~OpenVolumeCallback() {
JavaEnv env;
if (!env.IsValid()) return;
env->DeleteGlobalRef(callback);
callback = nullptr;
}
HRESULT OpenVolumeCallback::OpenVolume(const wchar_t *name, CMyComPtr<IInStream>& in_stream) {
JavaEnv env;
// const wchar_t* to jstring
unsigned len = MyStringLen(name);
jchar *buffer = reinterpret_cast<jchar *>(malloc(len * sizeof(jchar)));
if (buffer == nullptr) {
return E_OUTOFMEMORY;
}
for (int i = 0; i < len; i++) {
buffer[i] = static_cast<jchar>(name[i]);
}
jstring j_name = env->NewString(buffer, len);
free(buffer);
if (j_name == nullptr) {
return E_OUTOFMEMORY;
}
// Wrap java stream
jobject stream = env->CallObjectMethod(callback, method_open_volume, j_name);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
RETURN_SAME_IF_NOT_ZERO(SeekableInputStream::Create(static_cast<JNIEnv*>(env), stream, in_stream));
return S_OK;
}
HRESULT OpenVolumeCallback::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
jclass clazz = env->FindClass("com/hippo/a7zip/InArchive$OpenVolumeCallback");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
method_open_volume = env->GetMethodID(clazz, "openVolume", "(Ljava/lang/String;)Lcom/hippo/a7zip/SeekableInputStream;");
if (method_open_volume == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return S_OK;
}
HRESULT OpenVolumeCallback::Create(
JNIEnv* env,
jobject callback,
OpenVolumeCallback** result
) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jobject g_callback = env->NewGlobalRef(callback);
if (g_callback == nullptr) {
return E_OUTOFMEMORY;
}
*result = new OpenVolumeCallback(g_callback);
return S_OK;
}
HRESULT OpenVolumeCallback::Create(
JNIEnv* env,
jobject callback,
CMyComPtr<OpenVolumeCallback>& result
) {
OpenVolumeCallback *callback_ptr = nullptr;
RETURN_SAME_IF_NOT_ZERO(Create(env, callback, &callback_ptr));
result = callback_ptr;
return S_OK;
}
@@ -1,54 +0,0 @@
/*
* Copyright 2020 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_OPEN_VOLUME_CALLBACK_H__
#define __A7ZIP_OPEN_VOLUME_CALLBACK_H__
#include <jni.h>
#include <Common/MyCom.h>
#include <7zip/IStream.h>
namespace a7zip {
class OpenVolumeCallback : public CMyUnknownImp
{
private:
OpenVolumeCallback(jobject callback);
public:
virtual ~OpenVolumeCallback();
public:
MY_ADDREF_RELEASE
HRESULT OpenVolume(const wchar_t *name, CMyComPtr<IInStream>& in_stream);
private:
jobject callback;
public:
static HRESULT Initialize(JNIEnv* env);
static HRESULT Create(JNIEnv* env, jobject callback, OpenVolumeCallback** result);
static HRESULT Create(JNIEnv* env, jobject callback, CMyComPtr<OpenVolumeCallback>& result);
private:
static bool initialized;
static jmethodID method_open_volume;
};
}
#endif //__A7ZIP_OPEN_VOLUME_CALLBACK_H__
@@ -1,127 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "OutputStream.h"
#include "JavaEnv.h"
#include "Utils.h"
#include "Log.h"
#define ARRAY_SIZE DEFAULT_BUFFER_SIZE
using namespace a7zip;
bool OutputStream::initialized = false;
jmethodID OutputStream::method_write = nullptr;
jmethodID OutputStream::method_close = nullptr;
OutputStream::OutputStream(
jobject stream,
jbyteArray array
) :
stream(stream),
array(array) { }
OutputStream::~OutputStream() {
JavaEnv env;
if (!env.IsValid()) return;
env->CallVoidMethod(stream, method_close);
CLEAR_IF_EXCEPTION_PENDING(env);
env->DeleteGlobalRef(stream);
env->DeleteGlobalRef(array);
stream = nullptr;
array = nullptr;
}
HRESULT OutputStream::Write(const void* data, UInt32 size, UInt32* processedSize) {
if (processedSize != nullptr) {
*processedSize = 0;
}
if (size == 0) {
return S_OK;
}
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
// Make size not bigger than ARRAY_SIZE
size = MIN(ARRAY_SIZE, size);
// Copy data from native buffer to java buffer
env->SetByteArrayRegion(array, 0, size, reinterpret_cast<const jbyte*>(data));
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// Write data to sink
env->CallVoidMethod(stream, method_write, array, 0, size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (processedSize != nullptr) {
*processedSize = size;
}
return S_OK;
}
HRESULT OutputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
jclass clazz = env->FindClass("java/io/OutputStream");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
method_write = env->GetMethodID(clazz, "write", "([BII)V");
if (method_write == nullptr) return E_METHOD_NOT_FOUND;
method_close = env->GetMethodID(clazz, "close", "()V");
if (method_close == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return S_OK;
}
HRESULT OutputStream::Create(
JNIEnv* env,
jobject stream,
CMyComPtr<ISequentialOutStream>& out_stream
) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jobject g_stream = env->NewGlobalRef(stream);
if (g_stream == nullptr) {
return E_OUTOFMEMORY;
}
jbyteArray array = env->NewByteArray(ARRAY_SIZE);
if (array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_FAILED_CONSTRUCT;
}
jbyteArray g_array = static_cast<jbyteArray>(env->NewGlobalRef(array));
if (g_array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_OUTOFMEMORY;
}
out_stream = new OutputStream(g_stream, g_array);
return S_OK;
}
@@ -1,57 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
#define __A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
#include <jni.h>
#include <Common/MyCom.h>
#include <7zip/IStream.h>
namespace a7zip {
class OutputStream :
public ISequentialOutStream,
public CMyUnknownImp
{
private:
OutputStream(jobject stream, jbyteArray array);
public:
virtual ~OutputStream();
public:
MY_UNKNOWN_IMP
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
private:
jobject stream;
jbyteArray array;
public:
static HRESULT Initialize(JNIEnv* env);
static HRESULT Create(JNIEnv* env, jobject stream, CMyComPtr<ISequentialOutStream>& out_stream);
private:
static bool initialized;
static jmethodID method_write;
static jmethodID method_close;
};
}
#endif //__A7ZIP_OUTPUT_STREAM_OUT_STREAM_H__
@@ -1,37 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_PROP_TYPE_H__
#define __A7ZIP_PROP_TYPE_H__
namespace a7zip {
enum PropTypeEnum {
PT_UNKNOWN,
PT_EMPTY,
PT_BOOL,
PT_INT,
PT_LONG,
PT_FLOAT,
PT_DOUBLE,
PT_STRING,
};
typedef unsigned short PropType;
}
#endif //__A7ZIP_PROP_TYPE_H__
@@ -1,196 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include "SeekableInputStream.h"
#include "JavaEnv.h"
#include "Utils.h"
#include "Log.h"
#define ARRAY_SIZE DEFAULT_BUFFER_SIZE
using namespace a7zip;
bool SeekableInputStream::initialized = false;
jmethodID SeekableInputStream::method_read = nullptr;
jmethodID SeekableInputStream::method_seek = nullptr;
jmethodID SeekableInputStream::method_tell = nullptr;
jmethodID SeekableInputStream::method_size = nullptr;
jmethodID SeekableInputStream::method_close = nullptr;
SeekableInputStream::SeekableInputStream(
jobject stream,
jbyteArray array
) :
stream(stream),
array(array) { }
SeekableInputStream::~SeekableInputStream() {
JavaEnv env;
if (!env.IsValid()) return;
env->CallVoidMethod(stream, method_close);
CLEAR_IF_EXCEPTION_PENDING(env);
env->DeleteGlobalRef(stream);
env->DeleteGlobalRef(array);
stream = nullptr;
array = nullptr;
}
HRESULT SeekableInputStream::Read(void* data, UInt32 size, UInt32* processedSize) {
if (processedSize != nullptr) {
*processedSize = 0;
}
if (size == 0) {
return S_OK;
}
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
// Make size not bigger than ARRAY_SIZE
size = MIN(ARRAY_SIZE, size);
jint read = env->CallIntMethod(stream, method_read, array, 0, size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
// Check EOF
if (read <= 0) {
return S_OK;
}
env->GetByteArrayRegion(array, 0, read, static_cast<jbyte*>(data));
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (processedSize != nullptr) {
*processedSize = static_cast<UInt32>(read);
}
return S_OK;
}
HRESULT SeekableInputStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64* newPosition) {
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
jlong actual_offset;
switch (seekOrigin) {
case STREAM_SEEK_SET: {
actual_offset = static_cast<jlong>(offset);
break;
}
case STREAM_SEEK_CUR: {
jlong position = env->CallLongMethod(stream, method_tell);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
actual_offset = position + offset;
break;
}
case STREAM_SEEK_END: {
jlong size = env->CallLongMethod(stream, method_size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
actual_offset = size + offset;
break;
}
default: {
return E_INVALIDARG;
}
}
if (actual_offset < 0) {
return E_INVALIDARG;
}
env->CallVoidMethod(stream, method_seek, actual_offset);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (newPosition != nullptr) {
*newPosition = static_cast<UInt64>(actual_offset);
}
return S_OK;
}
HRESULT SeekableInputStream::GetSize(UInt64* size) {
JavaEnv env;
if (!env.IsValid()) return E_JAVA_EXCEPTION;
jlong store_size = env->CallLongMethod(stream, method_size);
RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(env);
if (size != nullptr) {
*size = static_cast<UInt64>(store_size);
}
return S_OK;
}
HRESULT SeekableInputStream::Initialize(JNIEnv* env) {
if (initialized) {
return S_OK;
}
jclass clazz = env->FindClass("com/hippo/a7zip/SeekableInputStream");
if (clazz == nullptr) return E_CLASS_NOT_FOUND;
method_read = env->GetMethodID(clazz, "read", "([BII)I");
if (method_read == nullptr) return E_METHOD_NOT_FOUND;
method_seek = env->GetMethodID(clazz, "seek", "(J)V");
if (method_seek == nullptr) return E_METHOD_NOT_FOUND;
method_tell = env->GetMethodID(clazz, "tell", "()J");
if (method_tell == nullptr) return E_METHOD_NOT_FOUND;
method_size = env->GetMethodID(clazz, "size", "()J");
if (method_size == nullptr) return E_METHOD_NOT_FOUND;
method_close = env->GetMethodID(clazz, "close", "()V");
if (method_close == nullptr) return E_METHOD_NOT_FOUND;
initialized = true;
return JNI_OK;
}
HRESULT SeekableInputStream::Create(
JNIEnv* env,
jobject stream,
CMyComPtr<IInStream>& in_stream
) {
if (!initialized) {
return E_NOT_INITIALIZED;
}
jobject g_stream = env->NewGlobalRef(stream);
if (g_stream == nullptr) {
return E_OUTOFMEMORY;
}
jbyteArray array = env->NewByteArray(ARRAY_SIZE);
if (array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_FAILED_CONSTRUCT;
}
jbyteArray g_array = static_cast<jbyteArray>(env->NewGlobalRef(array));
if (g_array == nullptr) {
env->DeleteGlobalRef(g_stream);
return E_OUTOFMEMORY;
}
in_stream = new SeekableInputStream(g_stream, g_array);
return S_OK;
}
@@ -1,65 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_IN_STREAM_H__
#define __A7ZIP_IN_STREAM_H__
#include <jni.h>
#include <7zip/IStream.h>
#include <Common/MyCom.h>
namespace a7zip {
class SeekableInputStream :
public IInStream,
public IStreamGetSize,
public CMyUnknownImp
{
private:
SeekableInputStream(jobject stream, jbyteArray array);
public:
virtual ~SeekableInputStream();
public:
MY_UNKNOWN_IMP2(IInStream, IStreamGetSize)
STDMETHOD(Read)(void* data, UInt32 size, UInt32* processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition);
STDMETHOD(GetSize)(UInt64* size);
private:
jobject stream;
jbyteArray array;
public:
static HRESULT Initialize(JNIEnv* env);
static HRESULT Create(JNIEnv* env, jobject stream, CMyComPtr<IInStream>& in_stream);
private:
static bool initialized;
static jmethodID method_read;
static jmethodID method_seek;
static jmethodID method_tell;
static jmethodID method_size;
static jmethodID method_close;
};
}
#endif //__A7ZIP_IN_STREAM_H__
@@ -1,567 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SevenZip.h"
#include <dlfcn.h>
#include <Windows/PropVariant.h>
#include <7zip/Archive/IArchive.h>
#include <7zip/IPassword.h>
#include "Log.h"
#include "Utils.h"
#include "OpenVolumeCallback.h"
#ifdef LOG_TAG
# undef LOG_TAG
# define LOG_TAG "P7Zip"
#endif
using namespace a7zip;
extern "C" HRESULT GetNumberOfMethods(UINT32 *numCodecs);
extern "C" HRESULT GetNumberOfFormats(UINT32 *numFormats);
extern "C" HRESULT GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT* value);
extern "C" HRESULT GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT* value);
extern "C" HRESULT CreateObject(const GUID* clsid, const GUID* iid, void** outObject);
typedef HRESULT (*GetPropertyFunc)(UInt32 index, PROPID propID, PROPVARIANT* value);
class Method {
public:
bool has_name;
AString name;
bool has_encoder;
GUID encoder;
bool has_decoder;
GUID decoder;
};
class Format {
public:
GUID class_id;
bool has_name;
AString name;
UInt32 signature_offset;
CObjectVector<CByteBuffer> signatures;
};
class CompressCodecsInfo :
public ICompressCodecsInfo,
public CMyUnknownImp {
public:
MY_UNKNOWN_IMP1(ICompressCodecsInfo)
STDMETHOD(GetNumMethods)(UInt32* numMethods);
STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT* value);
STDMETHOD(CreateDecoder)(UInt32 index, const GUID* interfaceID, void** coder);
STDMETHOD(CreateEncoder)(UInt32 index, const GUID* interfaceID, void** coder);
};
static bool initialized = false;
static void* handle = nullptr;
static CObjectVector<Method> methods;
static CObjectVector<Format> formats;
static CompressCodecsInfo compress_codecs_info;
HRESULT CompressCodecsInfo::GetNumMethods(UInt32 *numMethods) {
if (numMethods != nullptr) {
*numMethods = methods.Size();
}
return S_OK;
}
HRESULT CompressCodecsInfo::GetProperty(UInt32 index, PROPID propID, PROPVARIANT* value) {
Method& method = methods[index];
switch (propID) {
case NMethodPropID::kDecoderIsAssigned: {
NWindows::NCOM::CPropVariant propVariant;
propVariant = method.has_decoder;
propVariant.Detach(value);
return S_OK;
}
case NMethodPropID::kEncoderIsAssigned: {
NWindows::NCOM::CPropVariant propVariant;
propVariant = method.has_encoder;
propVariant.Detach(value);
return S_OK;
}
default: {
return GetMethodProperty(index, propID, value);
}
}
}
HRESULT CompressCodecsInfo::CreateDecoder(
UInt32 index,
const GUID* interfaceID,
void** coder
) {
Method& method = methods[index];
if (method.has_decoder) {
return CreateObject(&(method.decoder), interfaceID, coder);
} else {
return S_OK;
}
}
HRESULT CompressCodecsInfo::CreateEncoder(
UInt32 index,
const GUID* interfaceID,
void** coder
) {
Method& method = methods[index];
if (method.has_encoder) {
return CreateObject(&(method.encoder), interfaceID, coder);
} else {
return S_OK;
}
}
class ArchiveOpenCallback :
public IArchiveOpenCallback,
public ICryptoGetTextPassword,
public CMyUnknownImp {
public:
ArchiveOpenCallback(BSTR password) {
this->password = ::SysAllocString(password);
this->has_asked_password = false;
}
~ArchiveOpenCallback() {
::SysFreeString(password);
}
public:
MY_UNKNOWN_IMP2(IArchiveOpenCallback, ICryptoGetTextPassword)
INTERFACE_IArchiveOpenCallback({ return S_OK; });
STDMETHOD(CryptoGetTextPassword)(BSTR *password) {
has_asked_password = true;
*password = ::SysAllocString(this->password);
return this->password != nullptr ? S_OK : E_NO_PASSWORD;
}
HRESULT GetBetterResult(HRESULT result) {
if (result == S_OK) {
return S_OK;
} else if (has_asked_password) {
if (password != nullptr) {
return E_WRONG_PASSWORD;
} else {
return E_NO_PASSWORD;
}
} else {
return result;
}
}
private:
BSTR password;
bool has_asked_password;
};
class ArchiveOpenCallback2 :
public ArchiveOpenCallback,
public IArchiveOpenVolumeCallback {
public:
ArchiveOpenCallback2(
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& callback
):
ArchiveOpenCallback(password),
filename(filename),
callback(callback) { }
public:
MY_UNKNOWN_IMP3(
IArchiveOpenCallback,
IArchiveOpenVolumeCallback,
ICryptoGetTextPassword)
STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) {
NWindows::NCOM::CPropVariant prop;
if (propID == kpidName) {
prop = filename;
}
prop.Detach(value);
return S_OK;
}
STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) {
CMyComPtr<IInStream> stream = nullptr;
HRESULT result = callback->OpenVolume(name, stream);
*inStream = stream.Detach();
return result != S_OK ? S_FALSE : S_OK;
}
private:
UString filename;
CMyComPtr<OpenVolumeCallback> callback;
};
#define GET_PROP_METHOD(METHOD_NAME, PROP_TYPE, VALUE_TYPE, CONVERTER) \
HRESULT METHOD_NAME( \
GetPropertyFunc get_property, \
UInt32 index, \
PROPID prop_id, \
PROP_TYPE& value, \
bool& is_assigned \
) { \
NWindows::NCOM::CPropVariant prop; \
is_assigned = false; \
\
RETURN_SAME_IF_NOT_ZERO(get_property(index, prop_id, &prop)); \
\
if (prop.vt == VALUE_TYPE) { \
is_assigned = true; \
CONVERTER; \
} else if (prop.vt != VT_EMPTY) { \
return E_INCONSISTENT_PROP_TYPE; \
} \
\
return S_OK; \
}
GET_PROP_METHOD(GetAString, AString, VT_BSTR, value.SetFromWStr_if_Ascii(prop.bstrVal));
GET_PROP_METHOD(GetGUID, GUID, VT_BSTR, value = *reinterpret_cast<const GUID*>(prop.bstrVal));
GET_PROP_METHOD(GetCByteBuffer, CByteBuffer, VT_BSTR, value.CopyFrom((const Byte *)prop.bstrVal, ::SysStringByteLen(prop.bstrVal)));
GET_PROP_METHOD(GetUInt32, UInt32, VT_UI4, value = prop.ulVal);
#undef GET_PROP_METHOD
static HRESULT LoadMethods() {
UInt32 method_number = 0;
RETURN_SAME_IF_NOT_ZERO(GetNumberOfMethods(&method_number));
for(UInt32 i = 0; i < method_number; i++) {
Method& method = methods.AddNew();
# define GET_PROP(METHOD, PROP, VALUE, ASSIGNED) \
if (METHOD(GetMethodProperty, i, PROP, VALUE, ASSIGNED) != S_OK) { \
methods.DeleteBack(); \
continue; \
}
// It's ok to assume all characters in method name are in ascii charset
GET_PROP(GetAString, NMethodPropID::kName, method.name, method.has_name);
GET_PROP(GetGUID, NMethodPropID::kDecoder, method.decoder, method.has_decoder);
GET_PROP(GetGUID, NMethodPropID::kEncoder, method.encoder, method.has_encoder);
# undef GET_PROP
}
return S_OK;
}
static void AppendMultiSignature(
CObjectVector<CByteBuffer>& signatures,
const unsigned char* multi_signature,
size_t size
) {
while (size > 0) {
unsigned length = *multi_signature++;
size--;
if (length > size) {
return;
}
signatures.AddNew().CopyFrom(multi_signature, length);
multi_signature += length;
size -= length;
}
}
static HRESULT LoadFormats() {
UInt32 format_number = 0;
RETURN_SAME_IF_NOT_ZERO(GetNumberOfFormats(&format_number));
for(UInt32 i = 0; i < format_number; i++) {
Format& format = formats.AddNew();
# define GET_PROP(METHOD, PROP, VALUE, ASSIGNED) \
if (METHOD(GetHandlerProperty2, i, PROP, VALUE, ASSIGNED) != S_OK) { \
formats.DeleteBack(); \
continue; \
}
// ClassID is required
bool has_class_id;
GET_PROP(GetGUID, NArchive::NHandlerPropID::kClassID, format.class_id, has_class_id);
if (!has_class_id) {
formats.DeleteBack();
continue;
}
// It's ok to assume all characters in format name are in ascii charset
GET_PROP(GetAString, NArchive::NHandlerPropID::kName, format.name, format.has_name);
bool has_signature;
CByteBuffer signature;
GET_PROP(GetUInt32, NArchive::NHandlerPropID::kSignatureOffset, format.signature_offset, has_signature);
if (!has_signature) {
format.signature_offset = 0;
}
GET_PROP(GetCByteBuffer, NArchive::NHandlerPropID::kSignature, signature, has_signature);
if (has_signature) {
format.signatures.AddNew().CopyFrom(signature, signature.Size());
}
GET_PROP(GetCByteBuffer, NArchive::NHandlerPropID::kMultiSignature, signature, has_signature);
if (has_signature) {
AppendMultiSignature(format.signatures, signature, signature.Size());
}
# undef GET_PROP
}
return S_OK;
}
HRESULT SevenZip::Initialize() {
if (initialized) {
return S_OK;
}
RETURN_SAME_IF_NOT_ZERO(LoadMethods());
RETURN_SAME_IF_NOT_ZERO(LoadFormats());
initialized = true;
return S_OK;
}
static HRESULT ReadFully(CMyComPtr<IInStream>& stream, Byte* data, UInt32 size, UInt32* processedSize) {
UInt32 read = 0;
while (read < size) {
RETURN_SAME_IF_NOT_ZERO(stream->Read(data + read, size - read, processedSize));
if (*processedSize == 0) {
// EOF
break;
}
read += *processedSize;
}
*processedSize = read;
return S_OK;
}
static HRESULT OpenInArchive(
GUID& class_id,
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
CMyComPtr<IInArchive>& in_archive
) {
RETURN_SAME_IF_NOT_ZERO(CreateObject(&class_id, &IID_IInArchive, reinterpret_cast<void **>(&in_archive)));
UInt64 newPosition = 0;
HRESULT result = in_stream->Seek(0, STREAM_SEEK_SET, &newPosition);
if (result != S_OK) {
in_archive->Close();
in_archive = nullptr;
return result;
}
UInt64 maxCheckStartPosition = 1 << 22;
ArchiveOpenCallback* callback_ptr = nullptr;
if (filename != nullptr && open_volume_callback != nullptr) {
callback_ptr = new ArchiveOpenCallback2(password, filename, open_volume_callback);
} else {
callback_ptr = new ArchiveOpenCallback(password);
}
CMyComPtr<ArchiveOpenCallback> callback(callback_ptr);
result = in_archive->Open(in_stream, &maxCheckStartPosition, callback);
result = callback->GetBetterResult(result);
if (result != S_OK) {
in_archive->Close();
in_archive = nullptr;
return result;
}
return S_OK;
}
static HRESULT OpenInArchive(
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
CMyComPtr<IInArchive>& in_archive,
AString& format_name
) {
bool formats_checked[formats.Size()];
memset(formats_checked, 0, formats.Size() * sizeof(bool));
for (int i = 0; i < formats.Size(); i++) {
Format& format = formats[i];
// Skip format without signatures
if (format.signatures.Size() == 0) {
continue;
}
// Check each signature
for (int j = 0; j < format.signatures.Size(); j++) {
CByteBuffer& signature = format.signatures[j];
UInt32 processedSize;
CByteBuffer bytes(signature.Size());
UInt64 newPosition;
CONTINUE_IF_NOT_ZERO(in_stream->Seek(format.signature_offset, STREAM_SEEK_SET, &newPosition));
if (newPosition != format.signature_offset) continue;
CONTINUE_IF_NOT_ZERO(ReadFully(in_stream, bytes, static_cast<UInt32>(signature.Size()), &processedSize));
if (processedSize != signature.Size() || bytes != signature) continue;
// The signature matched, try to open it
HRESULT result = OpenInArchive(format.class_id, in_stream, password, filename, open_volume_callback, in_archive);
// Mark the format
formats_checked[i] = true;
if (result == S_OK) {
format_name = format.name;
return S_OK;
}
if (result == E_NO_PASSWORD || result == E_WRONG_PASSWORD) {
// It's a password error, the archive format is confirmed
return result;
}
// Can't open archive in this format
// Skip this format
break;
}
}
// Try other unchecked formats
for (int i = 0; i < formats.Size(); i++) {
if (!formats_checked[i]) {
Format& format = formats[i];
HRESULT result = OpenInArchive(format.class_id, in_stream, password, filename, open_volume_callback, in_archive);
if (result == S_OK) {
format_name = format.name;
return S_OK;
}
if (result == E_NO_PASSWORD || result == E_WRONG_PASSWORD) {
// It's a password error, the archive format is confirmed
return result;
}
}
}
return E_UNKNOWN_FORMAT;
}
HRESULT SevenZip::OpenArchive(
CMyComPtr<IInStream>& in_stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& open_volume_callback,
InArchive** archive
) {
HRESULT result = S_FALSE;
InArchive* previous_archive = nullptr;
CMyComPtr<IInStream> arg_in_stream = in_stream;
BSTR arg_password = password;
BSTR arg_filename = filename;
CMyComPtr<OpenVolumeCallback> arg_open_volume_callback = open_volume_callback;
while (true) {
CMyComPtr<IInArchive> in_archive = nullptr;
AString format_name;
result = OpenInArchive(
arg_in_stream,
arg_password,
arg_filename,
arg_open_volume_callback,
in_archive,
format_name
);
if (result != S_OK || in_archive == nullptr) {
if (in_archive != nullptr) {
in_archive->Close();
in_archive = nullptr;
}
if (result == S_OK) {
result = E_INTERNAL;
}
break;
}
previous_archive = new InArchive(previous_archive, in_archive, format_name);
// Break if it has no entry or more than one entry
UInt32 number = 0;
previous_archive->GetNumberOfEntries(number);
if (number != 1) {
break;
}
// Break if it's not IInArchiveGetStream
CMyComPtr<IInArchiveGetStream> archive_get_stream;
in_archive->QueryInterface(IID_IInArchiveGetStream, (void**)&archive_get_stream);
if (archive_get_stream == nullptr) {
break;
}
// Break if GetStream returns null
CMyComPtr<ISequentialInStream> sequential_in_stream;
archive_get_stream->GetStream(0, &sequential_in_stream);
if (sequential_in_stream == nullptr) {
break;
}
// Break if the stream is not IInStream
arg_in_stream = nullptr;
sequential_in_stream->QueryInterface(IID_IInStream, (void**)&arg_in_stream);
if (arg_in_stream == nullptr) {
break;
}
// Clear arguments for the first archive
arg_password = nullptr;
arg_filename = nullptr;
arg_open_volume_callback = nullptr;
}
*archive = previous_archive;
return *archive != nullptr ? S_OK : result;
}
@@ -1,47 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_P7ZIP_H__
#define __A7ZIP_P7ZIP_H__
#include <jni.h>
#include <include_windows/windows.h>
#include <Common/MyBuffer.h>
#include <Common/MyCom.h>
#include <Common/MyString.h>
#include <7zip/ICoder.h>
#include "InArchive.h"
#include "OpenVolumeCallback.h"
namespace a7zip {
namespace SevenZip {
HRESULT Initialize();
HRESULT OpenArchive(
CMyComPtr<IInStream>& stream,
BSTR password,
BSTR filename,
CMyComPtr<OpenVolumeCallback>& callback,
InArchive** archive
);
}
}
#endif //__A7ZIP_P7ZIP_H__
-95
View File
@@ -1,95 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __A7ZIP_UTILS_H__
#define __A7ZIP_UTILS_H__
#define DEFAULT_BUFFER_SIZE (4 * 1024)
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define E_NOT_INITIALIZED ((HRESULT)0x82210000L)
#define E_INTERNAL ((HRESULT)0x82210001L)
#define E_UNKNOWN_ERROR ((HRESULT)0x82210002L)
#define E_CLASS_NOT_FOUND ((HRESULT)0x82220000L)
#define E_METHOD_NOT_FOUND ((HRESULT)0x82220001L)
#define E_JAVA_EXCEPTION ((HRESULT)0x82220002L)
#define E_FAILED_CONSTRUCT ((HRESULT)0x82220003L)
#define E_FAILED_REGISTER ((HRESULT)0x82220004L)
#define E_FAILED_UNREGISTER ((HRESULT)0x82220005L)
#define E_INCONSISTENT_PROP_TYPE ((HRESULT)0x82240000L)
#define E_EMPTY_PROP ((HRESULT)0x82240001L)
#define E_UNKNOWN_FORMAT ((HRESULT)0x82240002L)
#define E_UNSUPPORTED_EXTRACT_MODE ((HRESULT)0x82240003L)
#define E_NO_OUT_STREAM ((HRESULT)0x82240004L)
#define E_UNSUPPORTED_METHOD ((HRESULT)0x82250000L)
#define E_DATA_ERROR ((HRESULT)0x82250001L)
#define E_DATA_ERROR_ENCRYPTED ((HRESULT)0x82250002L)
#define E_CRC_ERROR ((HRESULT)0x82250003L)
#define E_CRC_ERROR_ENCRYPTED ((HRESULT)0x82250004L)
#define E_UNAVAILABLE ((HRESULT)0x82250005L)
#define E_UNEXPECTED_END ((HRESULT)0x82250006L)
#define E_DATA_AFTER_END ((HRESULT)0x82250007L)
#define E_IS_NOT_ARC ((HRESULT)0x82250008L)
#define E_HEADERS_ERROR ((HRESULT)0x82250009L)
#define E_WRONG_PASSWORD ((HRESULT)0x82250010L)
#define E_NO_PASSWORD ((HRESULT)0x82250011L)
#define CLEAR_IF_EXCEPTION_PENDING(ENV) \
do { \
if ((ENV)->ExceptionCheck()) { \
(ENV)->ExceptionDescribe(); \
(ENV)->ExceptionClear(); \
} \
} while (0)
#define RETURN_E_JAVA_EXCEPTION_IF_EXCEPTION_PENDING(ENV) \
do { \
if ((ENV)->ExceptionCheck()) { \
(ENV)->ExceptionDescribe(); \
(ENV)->ExceptionClear(); \
return E_JAVA_EXCEPTION; \
} \
} while (0)
#define RETURN_JNI_ERR_IF_NOT_ZERO(VALUE) \
do { \
if ((VALUE) != 0) { \
return JNI_ERR; \
} \
} while (0)
#define RETURN_SAME_IF_NOT_ZERO(VALUE) \
do { \
auto __temp__ = (VALUE); \
if (__temp__ != 0) { \
return __temp__; \
} \
} while (0)
#define CONTINUE_IF_NOT_ZERO(VALUE) \
do { \
if ((VALUE) != 0) { \
continue; \
} \
} while (0)
#endif //__A7ZIP_UTILS_H__
@@ -1,240 +0,0 @@
cmake_minimum_required(VERSION 3.4.1)
project(p7zip C CXX)
set(P_SEVEN_ZIP_EXTRACT_LITE_SOURCES
p7zip/CPP/7zip/Archive/7z/7zDecode.cpp
p7zip/CPP/7zip/Archive/7z/7zExtract.cpp
p7zip/CPP/7zip/Archive/7z/7zHandler.cpp
p7zip/CPP/7zip/Archive/7z/7zHeader.cpp
p7zip/CPP/7zip/Archive/7z/7zIn.cpp
p7zip/CPP/7zip/Archive/7z/7zProperties.cpp
p7zip/CPP/7zip/Archive/7z/7zRegister.cpp
p7zip/CPP/7zip/Archive/Common/CoderMixer2.cpp
p7zip/CPP/7zip/Archive/Common/FindSignature.cpp
p7zip/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
p7zip/CPP/7zip/Archive/Common/ItemNameUtils.cpp
p7zip/CPP/7zip/Archive/Common/MultiStream.cpp
p7zip/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
p7zip/CPP/7zip/Archive/Rar/RarHandler.cpp
p7zip/CPP/7zip/Archive/Rar/Rar5Handler.cpp
p7zip/CPP/7zip/Archive/Zip/ZipHandler.cpp
p7zip/CPP/7zip/Archive/Zip/ZipIn.cpp
p7zip/CPP/7zip/Archive/Zip/ZipItem.cpp
p7zip/CPP/7zip/Archive/Zip/ZipRegister.cpp
p7zip/CPP/7zip/Archive/ArchiveExports.cpp
p7zip/CPP/7zip/Archive/DllExports2.cpp
p7zip/CPP/7zip/Archive/HandlerCont.cpp
p7zip/CPP/7zip/Archive/SplitHandler.cpp
p7zip/CPP/7zip/Archive/XzHandler.cpp
p7zip/CPP/7zip/Common/CWrappers.cpp
p7zip/CPP/7zip/Common/CreateCoder.cpp
p7zip/CPP/7zip/Common/FilterCoder.cpp
p7zip/CPP/7zip/Common/InBuffer.cpp
p7zip/CPP/7zip/Common/LimitedStreams.cpp
p7zip/CPP/7zip/Common/OutBuffer.cpp
p7zip/CPP/7zip/Common/ProgressUtils.cpp
p7zip/CPP/7zip/Common/PropId.cpp
p7zip/CPP/7zip/Common/StreamObjects.cpp
p7zip/CPP/7zip/Common/StreamUtils.cpp
p7zip/CPP/7zip/Compress/Bcj2Coder.cpp
p7zip/CPP/7zip/Compress/Bcj2Register.cpp
p7zip/CPP/7zip/Compress/BcjCoder.cpp
p7zip/CPP/7zip/Compress/BcjRegister.cpp
p7zip/CPP/7zip/Compress/BitlDecoder.cpp
p7zip/CPP/7zip/Compress/BranchMisc.cpp
p7zip/CPP/7zip/Compress/BranchRegister.cpp
p7zip/CPP/7zip/Compress/ByteSwap.cpp
p7zip/CPP/7zip/Compress/BZip2Crc.cpp
p7zip/CPP/7zip/Compress/BZip2Decoder.cpp
p7zip/CPP/7zip/Compress/BZip2Register.cpp
p7zip/CPP/7zip/Compress/CodecExports.cpp
p7zip/CPP/7zip/Compress/CopyCoder.cpp
p7zip/CPP/7zip/Compress/CopyRegister.cpp
p7zip/CPP/7zip/Compress/Deflate64Register.cpp
p7zip/CPP/7zip/Compress/DeflateDecoder.cpp
p7zip/CPP/7zip/Compress/DeflateRegister.cpp
p7zip/CPP/7zip/Compress/DeltaFilter.cpp
p7zip/CPP/7zip/Compress/ImplodeDecoder.cpp
p7zip/CPP/7zip/Compress/ImplodeHuffmanDecoder.cpp
p7zip/CPP/7zip/Compress/Lzma2Decoder.cpp
p7zip/CPP/7zip/Compress/Lzma2Register.cpp
p7zip/CPP/7zip/Compress/LzmaDecoder.cpp
p7zip/CPP/7zip/Compress/LzmaRegister.cpp
p7zip/CPP/7zip/Compress/LzOutWindow.cpp
p7zip/CPP/7zip/Compress/PpmdDecoder.cpp
p7zip/CPP/7zip/Compress/PpmdRegister.cpp
p7zip/CPP/7zip/Compress/PpmdZip.cpp
p7zip/CPP/7zip/Compress/Rar1Decoder.cpp
p7zip/CPP/7zip/Compress/Rar2Decoder.cpp
p7zip/CPP/7zip/Compress/Rar3Decoder.cpp
p7zip/CPP/7zip/Compress/Rar3Vm.cpp
p7zip/CPP/7zip/Compress/Rar5Decoder.cpp
p7zip/CPP/7zip/Compress/RarCodecsRegister.cpp
p7zip/CPP/7zip/Compress/ShrinkDecoder.cpp
p7zip/CPP/7zip/Crypto/7zAes.cpp
p7zip/CPP/7zip/Crypto/7zAesRegister.cpp
p7zip/CPP/7zip/Crypto/HmacSha1.cpp
p7zip/CPP/7zip/Crypto/HmacSha256.cpp
p7zip/CPP/7zip/Crypto/MyAes.cpp
p7zip/CPP/7zip/Crypto/MyAesReg.cpp
p7zip/CPP/7zip/Crypto/Pbkdf2HmacSha1.cpp
p7zip/CPP/7zip/Crypto/RandGen.cpp
p7zip/CPP/7zip/Crypto/Rar20Crypto.cpp
p7zip/CPP/7zip/Crypto/Rar5Aes.cpp
p7zip/CPP/7zip/Crypto/RarAes.cpp
p7zip/CPP/7zip/Crypto/WzAes.cpp
p7zip/CPP/7zip/Crypto/ZipCrypto.cpp
p7zip/CPP/7zip/Crypto/ZipStrong.cpp
p7zip/CPP/Common/CRC.cpp
p7zip/CPP/Common/CrcReg.cpp
p7zip/CPP/Common/IntToString.cpp
p7zip/CPP/Common/MyString.cpp
p7zip/CPP/Common/MyVector.cpp
p7zip/CPP/Common/MyWindows.cpp
p7zip/CPP/Common/Sha1Reg.cpp
p7zip/CPP/Common/Sha256Reg.cpp
p7zip/CPP/Common/StringConvert.cpp
p7zip/CPP/Common/StringToInt.cpp
p7zip/CPP/Common/UTFConvert.cpp
p7zip/CPP/Common/Wildcard.cpp
p7zip/CPP/Common/XzCrc64Reg.cpp
p7zip/CPP/Windows/PropVariant.cpp
p7zip/CPP/Windows/PropVariantUtils.cpp
p7zip/CPP/Windows/System.cpp
p7zip/CPP/Windows/TimeUtils.cpp
p7zip/CPP/myWindows/wine_date_and_time.cpp
p7zip/C/7zCrc.c
p7zip/C/7zCrcOpt.c
p7zip/C/7zStream.c
p7zip/C/Aes.c
p7zip/C/Alloc.c
p7zip/C/Bcj2.c
p7zip/C/Blake2s.c
p7zip/C/Bra.c
p7zip/C/Bra86.c
p7zip/C/BraIA64.c
p7zip/C/CpuArch.c
p7zip/C/Delta.c
p7zip/C/Lzma2Dec.c
p7zip/C/LzmaDec.c
p7zip/C/Ppmd7.c
p7zip/C/Ppmd7Dec.c
p7zip/C/Ppmd8.c
p7zip/C/Ppmd8Dec.c
p7zip/C/Sha1.c
p7zip/C/Sha256.c
p7zip/C/Xz.c
p7zip/C/XzCrc64.c
p7zip/C/XzCrc64Opt.c
p7zip/C/XzDec.c
p7zip/C/XzIn.c
)
set(P_SEVEN_ZIP_EXTRACT_SOURCES
p7zip/CPP/7zip/Archive/Cab/CabBlockInStream.cpp
p7zip/CPP/7zip/Archive/Cab/CabHandler.cpp
p7zip/CPP/7zip/Archive/Cab/CabHeader.cpp
p7zip/CPP/7zip/Archive/Cab/CabIn.cpp
p7zip/CPP/7zip/Archive/Cab/CabRegister.cpp
p7zip/CPP/7zip/Archive/Chm/ChmHandler.cpp
p7zip/CPP/7zip/Archive/Chm/ChmIn.cpp
p7zip/CPP/7zip/Archive/Common/DummyOutStream.cpp
p7zip/CPP/7zip/Archive/Common/OutStreamWithSha1.cpp
p7zip/CPP/7zip/Archive/Iso/IsoHandler.cpp
p7zip/CPP/7zip/Archive/Iso/IsoHeader.cpp
p7zip/CPP/7zip/Archive/Iso/IsoIn.cpp
p7zip/CPP/7zip/Archive/Iso/IsoRegister.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisDecode.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisHandler.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisIn.cpp
p7zip/CPP/7zip/Archive/Nsis/NsisRegister.cpp
p7zip/CPP/7zip/Archive/Tar/TarHandler.cpp
p7zip/CPP/7zip/Archive/Tar/TarHeader.cpp
p7zip/CPP/7zip/Archive/Tar/TarIn.cpp
p7zip/CPP/7zip/Archive/Tar/TarRegister.cpp
p7zip/CPP/7zip/Archive/Udf/UdfHandler.cpp
p7zip/CPP/7zip/Archive/Udf/UdfIn.cpp
p7zip/CPP/7zip/Archive/Wim/WimHandler.cpp
p7zip/CPP/7zip/Archive/Wim/WimIn.cpp
p7zip/CPP/7zip/Archive/Wim/WimRegister.cpp
p7zip/CPP/7zip/Archive/ApmHandler.cpp
p7zip/CPP/7zip/Archive/ArHandler.cpp
p7zip/CPP/7zip/Archive/ArjHandler.cpp
p7zip/CPP/7zip/Archive/Bz2Handler.cpp
p7zip/CPP/7zip/Archive/ComHandler.cpp
p7zip/CPP/7zip/Archive/CpioHandler.cpp
p7zip/CPP/7zip/Archive/CramfsHandler.cpp
p7zip/CPP/7zip/Archive/DmgHandler.cpp
p7zip/CPP/7zip/Archive/ElfHandler.cpp
p7zip/CPP/7zip/Archive/ExtHandler.cpp
p7zip/CPP/7zip/Archive/FatHandler.cpp
p7zip/CPP/7zip/Archive/FlvHandler.cpp
p7zip/CPP/7zip/Archive/GptHandler.cpp
p7zip/CPP/7zip/Archive/GzHandler.cpp
p7zip/CPP/7zip/Archive/HfsHandler.cpp
p7zip/CPP/7zip/Archive/IhexHandler.cpp
p7zip/CPP/7zip/Archive/LzhHandler.cpp
p7zip/CPP/7zip/Archive/LzmaHandler.cpp
p7zip/CPP/7zip/Archive/MachoHandler.cpp
p7zip/CPP/7zip/Archive/MbrHandler.cpp
p7zip/CPP/7zip/Archive/MslzHandler.cpp
p7zip/CPP/7zip/Archive/MubHandler.cpp
p7zip/CPP/7zip/Archive/NtfsHandler.cpp
p7zip/CPP/7zip/Archive/PeHandler.cpp
p7zip/CPP/7zip/Archive/PpmdHandler.cpp
p7zip/CPP/7zip/Archive/QcowHandler.cpp
p7zip/CPP/7zip/Archive/RpmHandler.cpp
p7zip/CPP/7zip/Archive/SquashfsHandler.cpp
p7zip/CPP/7zip/Archive/SwfHandler.cpp
p7zip/CPP/7zip/Archive/UefiHandler.cpp
p7zip/CPP/7zip/Archive/VdiHandler.cpp
p7zip/CPP/7zip/Archive/VhdHandler.cpp
p7zip/CPP/7zip/Archive/VmdkHandler.cpp
p7zip/CPP/7zip/Archive/XarHandler.cpp
p7zip/CPP/7zip/Archive/ZHandler.cpp
p7zip/CPP/7zip/Compress/BZip2Decoder.cpp
p7zip/CPP/7zip/Compress/BZip2Register.cpp
p7zip/CPP/7zip/Compress/LzhDecoder.cpp
p7zip/CPP/7zip/Compress/LzmsDecoder.cpp
p7zip/CPP/7zip/Compress/LzxDecoder.cpp
p7zip/CPP/7zip/Compress/QuantumDecoder.cpp
p7zip/CPP/7zip/Compress/XpressDecoder.cpp
p7zip/CPP/7zip/Compress/ZDecoder.cpp
p7zip/CPP/7zip/Compress/ZlibDecoder.cpp
p7zip/CPP/Common/MyMap.cpp
p7zip/CPP/Common/MyXml.cpp
p7zip/C/HuffEnc.c
p7zip/C/Sort.c
)
set(P_SEVEN_ZIP_INCLUDES
p7zip/C
p7zip/CPP
p7zip/CPP/Common
p7zip/CPP/include_windows
p7zip/CPP/myWindows
p7zip/CPP/Windows
)
set(P_SEVEN_ZIP_EXTRACT_FLAGS
"-DEXTRACT_ONLY -D_7ZIP_ST"
)
set(P_SEVEN_ZIP_COMMON_FLAGS
"-DANDROID_NDK -DNDEBUG -D_REENTRANT -DENV_UNIX -DEXTERNAL_CODECS -DUNICODE -D_UNICODE -DUNIX_USE_WIN_FILE"
)
set(P_SEVEN_ZIP_SOURCES ${P_SEVEN_ZIP_EXTRACT_LITE_SOURCES})
set(P_SEVEN_ZIP_FLAGS "${P_SEVEN_ZIP_COMMON_FLAGS} ${P_SEVEN_ZIP_EXTRACT_FLAGS}")
set(P_SEVEN_ZIP_NAME "p7zip")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${P_SEVEN_ZIP_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${P_SEVEN_ZIP_FLAGS}")
add_library(p7zip SHARED ${P_SEVEN_ZIP_SOURCES})
target_include_directories(p7zip PUBLIC ${P_SEVEN_ZIP_INCLUDES})
set_target_properties(p7zip PROPERTIES OUTPUT_NAME ${P_SEVEN_ZIP_NAME})
target_compile_options(p7zip PRIVATE -fvisibility=default)
+171
View File
@@ -0,0 +1,171 @@
//
// 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 'j':
return (strcmp(dotptr, "jpg") == 0) || (strcmp(dotptr, "jpeg") == 0);
case 'p':
return strcmp(dotptr, "png") == 0;
case 'g':
return strcmp(dotptr, "gif") == 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++;
archive_read_data_skip(a);
}
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;
}
la_int64_t ins_skip(struct archive *a, void *client_data, la_int64_t request) {
archive_inst *arc = client_data;
jlong ret = (*arc->env)->CallLongMethod(arc->env, arc->file, arc->skipID, request);
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_set_skip_callback(inst->archive, ins_skip);
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->skipID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "skip", "(J)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;
} else {
archive_read_data_skip(arc->archive);
}
count++;
}
archive_destroy_inst(arc);
return 0;
}
@@ -13,7 +13,7 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(giflib C)
project(gif C)
set(GIFLIB_SOURCES
giflib/dgif_lib.c
@@ -23,5 +23,5 @@ set(GIFLIB_SOURCES
)
set(GIFLIB_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/giflib)
add_library(giflib STATIC ${GIFLIB_SOURCES})
target_include_directories(giflib PUBLIC ${GIFLIB_INCLUDES})
add_library(gif SHARED ${GIFLIB_SOURCES})
target_include_directories(gif PUBLIC ${GIFLIB_INCLUDES})
@@ -34,11 +34,11 @@ set(IMAGE_LIBRARIES
jnigraphics
GLESv2
stream
libjpeg-turbo
libpng
giflib
jpeg
png
gif
)
add_library(image STATIC ${IMAGE_SOURCES})
add_library(image SHARED ${IMAGE_SOURCES})
target_include_directories(image PRIVATE ${IMAGE_INCLUDES})
target_link_libraries(image PRIVATE ${IMAGE_LIBRARIES})
@@ -13,7 +13,7 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(libjpeg-turbo C)
project(jpeg C)
set(LIBJPEG_TURBO_SOURCES
libjpeg-turbo/jaricom.c
@@ -207,5 +207,5 @@ else ()
endif ()
add_library(libjpeg-turbo STATIC ${LIBJPEG_TURBO_SOURCES})
target_include_directories(libjpeg-turbo PUBLIC ${LIBJPEG_TURBO_INCLUDES})
add_library(jpeg SHARED ${LIBJPEG_TURBO_SOURCES})
target_include_directories(jpeg PUBLIC ${LIBJPEG_TURBO_INCLUDES})
@@ -4,22 +4,22 @@
#define JPEG_LIB_VERSION 62
/* libjpeg-turbo version */
#define LIBJPEG_TURBO_VERSION 2.0.0
#define LIBJPEG_TURBO_VERSION 2.1.3
/* libjpeg-turbo version in integer form */
#define LIBJPEG_TURBO_VERSION_NUMBER 2000000
#define LIBJPEG_TURBO_VERSION_NUMBER 2001003
/* Support arithmetic encoding */
#define C_ARITH_CODING_SUPPORTED
#define C_ARITH_CODING_SUPPORTED 1
/* Support arithmetic decoding */
#define D_ARITH_CODING_SUPPORTED
#define D_ARITH_CODING_SUPPORTED 1
/* Support in-memory source/destination managers */
#define MEM_SRCDST_SUPPORTED
#define MEM_SRCDST_SUPPORTED 1
/* Use accelerated SIMD routines. */
#define WITH_SIMD
#define WITH_SIMD 1
/*
* Define BITS_IN_JSAMPLE as either
@@ -32,42 +32,6 @@
#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */
/* Define to 1 if you have the <locale.h> header file. */
#define HAVE_LOCALE_H
/* Define to 1 if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H
/* Define if you need to include <sys/types.h> to get size_t. */
#define NEED_SYS_TYPES_H
/* Define if you have BSD-like bzero and bcopy in <strings.h> rather than
memset/memcpy in <string.h>. */
/* #undef NEED_BSD_STRINGS */
/* Define to 1 if the system has the type `unsigned char'. */
#define HAVE_UNSIGNED_CHAR
/* Define to 1 if the system has the type `unsigned short'. */
#define HAVE_UNSIGNED_SHORT
/* Compiler does not support pointers to undefined structures. */
/* #undef INCOMPLETE_TYPES_BROKEN */
/* Define if your (broken) compiler shifts signed values as if they were
unsigned. */
/* #undef RIGHT_SHIFT_IS_UNSIGNED */
/* Define to 1 if type `char' is unsigned and you are not using gcc. */
#ifndef __CHAR_UNSIGNED__
/* #undef __CHAR_UNSIGNED__ */
#endif
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
@@ -1,5 +1,5 @@
/* libjpeg-turbo build number */
#define BUILD "20180730"
#define BUILD "20220523"
/* Compiler's inline keyword */
#undef inline
@@ -7,11 +7,14 @@
/* How to obtain function inlining. */
#define INLINE __inline__ __attribute__((always_inline))
/* How to obtain thread-local storage */
#define THREAD_LOCAL __thread
/* Define to the full name of this package. */
#define PACKAGE_NAME "libjpeg-turbo"
/* Version number of package */
#define VERSION "2.0.0"
#define VERSION "2.1.3"
/* The size of `size_t', as computed by sizeof. */
#define SIZEOF_SIZE_T __SIZEOF_SIZE_T__
@@ -0,0 +1,54 @@
/*
* jversion.h
*
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding.
* libjpeg-turbo Modifications:
* Copyright (C) 2010, 2012-2022, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
* This file contains software version identification.
*/
#if JPEG_LIB_VERSION >= 80
#define JVERSION "8d 15-Jan-2012"
#elif JPEG_LIB_VERSION >= 70
#define JVERSION "7 27-Jun-2009"
#else
#define JVERSION "6b 27-Mar-1998"
#endif
/*
* NOTE: It is our convention to place the authors in the following order:
* - libjpeg-turbo authors (2009-) in descending order of the date of their
* most recent contribution to the project, then in ascending order of the
* date of their first contribution to the project, then in alphabetical
* order
* - Upstream authors in descending order of the date of the first inclusion of
* their code
*/
#define JCOPYRIGHT \
"Copyright (C) 2009-2022 D. R. Commander\n" \
"Copyright (C) 2015, 2020 Google, Inc.\n" \
"Copyright (C) 2019-2020 Arm Limited\n" \
"Copyright (C) 2015-2016, 2018 Matthieu Darbois\n" \
"Copyright (C) 2011-2016 Siarhei Siamashka\n" \
"Copyright (C) 2015 Intel Corporation\n" \
"Copyright (C) 2013-2014 Linaro Limited\n" \
"Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \
"Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \
"Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \
"Copyright (C) 1999-2006 MIYASAKA Masaru\n" \
"Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding"
#define JCOPYRIGHT_SHORT \
"Copyright (C) 1991-2022 The libjpeg-turbo Project and many others"
@@ -13,7 +13,7 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(libpng C)
project(png C)
set(LIBPNG_SOURCES
libpng/png.c
@@ -91,7 +91,7 @@ elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
endif ()
add_library(libpng STATIC ${LIBPNG_SOURCES})
target_include_directories(libpng PUBLIC ${LIBPNG_INCLUDES})
target_compile_definitions(libpng PRIVATE ${LIBPNG_DEFINITIONS})
target_link_libraries(libpng PRIVATE ${LIBPNG_LIBRARIES})
add_library(png SHARED ${LIBPNG_SOURCES})
target_include_directories(png PUBLIC ${LIBPNG_INCLUDES})
target_compile_definitions(png PRIVATE ${LIBPNG_DEFINITIONS})
target_link_libraries(png PRIVATE ${LIBPNG_LIBRARIES})
@@ -24,5 +24,6 @@ set(STREAM_SOURCES
)
set(STREAM_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
add_library(stream STATIC ${STREAM_SOURCES})
add_library(stream SHARED ${STREAM_SOURCES})
target_include_directories(stream PUBLIC ${STREAM_INCLUDES})
target_link_libraries(stream PRIVATE log)
@@ -1,12 +0,0 @@
project(genuine C)
add_library(genuine STATIC
am-proxy.c
apk-sign-v2.c
common.c
genuine.c
inline.c
openat.c
path.c
plt.c)
target_include_directories(genuine PUBLIC integrity)
-824
View File
@@ -1,824 +0,0 @@
//
// Created by Thom on 2019/3/15.
//
#include "common.h"
#include "am-proxy.h"
static inline void fill_android_app_ActivityManager(char v[]) {
// android/app/ActivityManager
static unsigned int m = 0;
if (m == 0) {
m = 23;
} else if (m == 29) {
m = 31;
}
v[0x0] = 'e';
v[0x1] = 'k';
v[0x2] = 'b';
v[0x3] = 'u';
v[0x4] = 'g';
v[0x5] = '`';
v[0x6] = 'n';
v[0x7] = '$';
v[0x8] = 'm';
v[0x9] = '}';
v[0xa] = '~';
v[0xb] = ' ';
v[0xc] = 'Q';
v[0xd] = 'r';
v[0xe] = 'f';
v[0xf] = 'z';
v[0x10] = 'b';
v[0x11] = '|';
v[0x12] = 'b';
v[0x13] = 'y';
v[0x14] = 'L';
v[0x15] = 'c';
v[0x16] = 'm';
v[0x17] = 'e';
v[0x18] = 'b';
v[0x19] = 'c';
v[0x1a] = 'u';
for (unsigned int i = 0; i < 0x1b; ++i) {
v[i] ^= ((i + 0x1b) % m);
}
v[0x1b] = '\0';
}
static inline void fill_getService(char v[]) {
// getService
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = 'd';
v[0x1] = 'a';
v[0x2] = 'q';
v[0x3] = 'U';
v[0x4] = 'e';
v[0x5] = 's';
v[0x6] = 't';
v[0x7] = 'j';
v[0x8] = 'g';
v[0x9] = '`';
for (unsigned int i = 0; i < 0xa; ++i) {
v[i] ^= ((i + 0xa) % m);
}
v[0xa] = '\0';
}
static inline void fill_getService_signature(char v[]) {
// ()Landroid/app/IActivityManager;
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'e';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'u';
v[0x7] = 'g';
v[0x8] = '`';
v[0x9] = 'n';
v[0xa] = '$';
v[0xb] = 'm';
v[0xc] = '}';
v[0xd] = '~';
v[0xe] = ' ';
v[0xf] = 'Y';
v[0x10] = 'P';
v[0x11] = 'q';
v[0x12] = 'g';
v[0x13] = '}';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 'c';
v[0x17] = 'a';
v[0x18] = 'T';
v[0x19] = '{';
v[0x1a] = 'u';
v[0x1b] = '}';
v[0x1c] = 'z';
v[0x1d] = '{';
v[0x1e] = 'r';
v[0x1f] = ':';
for (unsigned int i = 0; i < 0x20; ++i) {
v[i] ^= ((i + 0x20) % m);
}
v[0x20] = '\0';
}
static inline void fill_android_app_ActivityManagerNative(char v[]) {
// android/app/ActivityManagerNative
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'k';
v[0x9] = '{';
v[0xa] = '|';
v[0xb] = '"';
v[0xc] = 'O';
v[0xd] = 'l';
v[0xe] = 'd';
v[0xf] = 'x';
v[0x10] = 'd';
v[0x11] = 'z';
v[0x12] = '`';
v[0x13] = 'l';
v[0x14] = '[';
v[0x15] = 'v';
v[0x16] = 'v';
v[0x17] = 'x';
v[0x18] = '}';
v[0x19] = '~';
v[0x1a] = 'n';
v[0x1b] = 'S';
v[0x1c] = '\x7f';
v[0x1d] = 't';
v[0x1e] = 'h';
v[0x1f] = 't';
v[0x20] = 'f';
for (unsigned int i = 0; i < 0x21; ++i) {
v[i] ^= ((i + 0x21) % m);
}
v[0x21] = '\0';
}
static inline void fill_getDefault(char v[]) {
// getDefault
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = 'd';
v[0x1] = 'a';
v[0x2] = 'q';
v[0x3] = 'B';
v[0x4] = 'e';
v[0x5] = 'g';
v[0x6] = 'c';
v[0x7] = 'v';
v[0x8] = 'h';
v[0x9] = 'q';
for (unsigned int i = 0; i < 0xa; ++i) {
v[i] ^= ((i + 0xa) % m);
}
v[0xa] = '\0';
}
static inline void fill_getDefault_signature(char v[]) {
// ()Landroid/app/IActivityManager;
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'e';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'u';
v[0x7] = 'g';
v[0x8] = '`';
v[0x9] = 'n';
v[0xa] = '$';
v[0xb] = 'm';
v[0xc] = '}';
v[0xd] = '~';
v[0xe] = ' ';
v[0xf] = 'Y';
v[0x10] = 'P';
v[0x11] = 'q';
v[0x12] = 'g';
v[0x13] = '}';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 'c';
v[0x17] = 'a';
v[0x18] = 'T';
v[0x19] = '{';
v[0x1a] = 'u';
v[0x1b] = '}';
v[0x1c] = 'z';
v[0x1d] = '{';
v[0x1e] = 'r';
v[0x1f] = ':';
for (unsigned int i = 0; i < 0x20; ++i) {
v[i] ^= ((i + 0x20) % m);
}
v[0x20] = '\0';
}
static inline void fill_android_os_BinderProxy(char v[]) {
// android/os/BinderProxy
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 'b';
v[0x1] = 'j';
v[0x2] = 'a';
v[0x3] = 't';
v[0x4] = 'h';
v[0x5] = 'a';
v[0x6] = 'm';
v[0x7] = '%';
v[0x8] = 'd';
v[0x9] = '\x7f';
v[0xa] = '"';
v[0xb] = 'L';
v[0xc] = 'f';
v[0xd] = '~';
v[0xe] = 'u';
v[0xf] = 'w';
v[0x10] = 'r';
v[0x11] = 'Q';
v[0x12] = 'p';
v[0x13] = 'l';
v[0x14] = '|';
v[0x15] = '|';
for (unsigned int i = 0; i < 0x16; ++i) {
v[i] ^= ((i + 0x16) % m);
}
v[0x16] = '\0';
}
static inline void fill_android_os_IInterface(char v[]) {
// android/os/IInterface
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'e';
v[0x9] = 'x';
v[0xa] = '#';
v[0xb] = 'D';
v[0xc] = 'G';
v[0xd] = 'a';
v[0xe] = 'd';
v[0xf] = 't';
v[0x10] = '`';
v[0x11] = 'f';
v[0x12] = '`';
v[0x13] = 'a';
v[0x14] = 'f';
for (unsigned int i = 0; i < 0x15; ++i) {
v[i] ^= ((i + 0x15) % m);
}
v[0x15] = '\0';
}
static inline void fill_asBinder(char v[]) {
// asBinder
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = '`';
v[0x1] = 'q';
v[0x2] = 'A';
v[0x3] = 'm';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'e';
v[0x7] = 's';
for (unsigned int i = 0; i < 0x8; ++i) {
v[i] ^= ((i + 0x8) % m);
}
v[0x8] = '\0';
}
static inline void fill_asBinder_signature(char v[]) {
// ()Landroid/os/IBinder;
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = '+';
v[0x1] = '-';
v[0x2] = 'I';
v[0x3] = 'g';
v[0x4] = 'i';
v[0x5] = 'l';
v[0x6] = '{';
v[0x7] = 'e';
v[0x8] = 'b';
v[0x9] = 'h';
v[0xa] = '"';
v[0xb] = 'a';
v[0xc] = '|';
v[0xd] = '?';
v[0xe] = 'X';
v[0xf] = 'P';
v[0x10] = 'i';
v[0x11] = 'o';
v[0x12] = 'f';
v[0x13] = 'f';
v[0x14] = 'v';
v[0x15] = '>';
for (unsigned int i = 0; i < 0x16; ++i) {
v[i] ^= ((i + 0x16) % m);
}
v[0x16] = '\0';
}
static inline void fill_android_os_ServiceManager(char v[]) {
// android/os/ServiceManager
static unsigned int m = 0;
if (m == 0) {
m = 23;
} else if (m == 29) {
m = 31;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'e';
v[0x9] = 'x';
v[0xa] = '#';
v[0xb] = '^';
v[0xc] = 'k';
v[0xd] = '}';
v[0xe] = 'f';
v[0xf] = 'x';
v[0x10] = 'q';
v[0x11] = 'v';
v[0x12] = 'Y';
v[0x13] = 't';
v[0x14] = 'x';
v[0x15] = 'a';
v[0x16] = 'f';
v[0x17] = 'g';
v[0x18] = 'q';
for (unsigned int i = 0; i < 0x19; ++i) {
v[i] ^= ((i + 0x19) % m);
}
v[0x19] = '\0';
}
static inline void fill_getService_signature_IBinder(char v[]) {
// (Ljava/lang/String;)Landroid/os/IBinder;
static unsigned int m = 0;
if (m == 0) {
m = 37;
} else if (m == 41) {
m = 43;
}
v[0x0] = '+';
v[0x1] = 'H';
v[0x2] = 'o';
v[0x3] = 'g';
v[0x4] = 'q';
v[0x5] = 'i';
v[0x6] = '&';
v[0x7] = 'f';
v[0x8] = 'j';
v[0x9] = 'b';
v[0xa] = 'j';
v[0xb] = '!';
v[0xc] = '\\';
v[0xd] = 'd';
v[0xe] = 'c';
v[0xf] = '{';
v[0x10] = '}';
v[0x11] = 's';
v[0x12] = '.';
v[0x13] = '?';
v[0x14] = '[';
v[0x15] = 'y';
v[0x16] = 'w';
v[0x17] = '~';
v[0x18] = 'i';
v[0x19] = 's';
v[0x1a] = 't';
v[0x1b] = 'z';
v[0x1c] = '0';
v[0x1d] = 'O';
v[0x1e] = 'R';
v[0x1f] = '\r';
v[0x20] = 'j';
v[0x21] = 'f';
v[0x22] = 'i';
v[0x23] = 'o';
v[0x24] = 'f';
v[0x25] = 'f';
v[0x26] = 'v';
v[0x27] = '>';
for (unsigned int i = 0; i < 0x28; ++i) {
v[i] ^= ((i + 0x28) % m);
}
v[0x28] = '\0';
}
static inline void fill_activity(char v[]) {
// activity
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = '`';
v[0x1] = 'a';
v[0x2] = 'w';
v[0x3] = 'm';
v[0x4] = 's';
v[0x5] = 'o';
v[0x6] = 't';
v[0x7] = 'x';
for (unsigned int i = 0; i < 0x8; ++i) {
v[i] ^= ((i + 0x8) % m);
}
v[0x8] = '\0';
}
static inline void fill_getName(char v[]) {
// getName
static unsigned int m = 0;
if (m == 0) {
m = 5;
} else if (m == 7) {
m = 11;
}
v[0x0] = 'e';
v[0x1] = 'f';
v[0x2] = 'p';
v[0x3] = 'N';
v[0x4] = '`';
v[0x5] = 'o';
v[0x6] = 'f';
for (unsigned int i = 0; i < 0x7; ++i) {
v[i] ^= ((i + 0x7) % m);
}
v[0x7] = '\0';
}
static inline void fill_getName_signature(char v[]) {
// ()Ljava/lang/String;
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'n';
v[0x4] = 'd';
v[0x5] = 'p';
v[0x6] = 'f';
v[0x7] = '\'';
v[0x8] = 'e';
v[0x9] = 'k';
v[0xa] = 'e';
v[0xb] = 'k';
v[0xc] = '"';
v[0xd] = ']';
v[0xe] = '{';
v[0xf] = 'b';
v[0x10] = 'x';
v[0x11] = '|';
v[0x12] = 'g';
v[0x13] = ':';
for (unsigned int i = 0; i < 0x14; ++i) {
v[i] ^= ((i + 0x14) % m);
}
v[0x14] = '\0';
}
static inline void fill_activity_manager_service_is_s(char v[]) {
// activity manager service is %s
static unsigned int m = 0;
if (m == 0) {
m = 29;
} else if (m == 31) {
m = 37;
}
v[0x0] = '`';
v[0x1] = 'a';
v[0x2] = 'w';
v[0x3] = 'm';
v[0x4] = 's';
v[0x5] = 'o';
v[0x6] = 's';
v[0x7] = 'q';
v[0x8] = ')';
v[0x9] = 'g';
v[0xa] = 'j';
v[0xb] = 'b';
v[0xc] = 'l';
v[0xd] = 'i';
v[0xe] = 'j';
v[0xf] = 'b';
v[0x10] = '1';
v[0x11] = 'a';
v[0x12] = 'v';
v[0x13] = 'f';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 't';
v[0x17] = '}';
v[0x18] = '9';
v[0x19] = 's';
v[0x1a] = 'h';
v[0x1b] = '<';
v[0x1c] = '%';
v[0x1d] = 'r';
for (unsigned int i = 0; i < 0x1e; ++i) {
v[i] ^= ((i + 0x1e) % m);
}
v[0x1e] = '\0';
}
static inline void showError(JNIEnv *env, jclass object) {
char v1[0x8], v2[0x1f];
if (object == NULL) {
fill_activity_manager_service_is_s(v2); // 0x1f
LOGE(v2, NULL);
} else {
fill_getName(v1); // 0x8
fill_getName_signature(v2); // 0x15
jclass clazz = (*env)->GetObjectClass(env, object);
jmethodID method = (*env)->GetMethodID(env, clazz, v1, v2);
jstring string = (*env)->CallObjectMethod(env, object, method);
const char *name = (*env)->GetStringUTFChars(env, string, NULL);
fill_activity_manager_service_is_s(v2); // 0x1f
LOGE(v2, name);
(*env)->ReleaseStringUTFChars(env, string, name);
(*env)->DeleteLocalRef(env, string);
(*env)->DeleteLocalRef(env, clazz);
}
}
bool isAmProxy(JNIEnv *env, int sdk) {
char v1[0xb], v2[0x29];
bool proxy = false;
jmethodID method;
jclass classActivityManager;
if (sdk >= 26) {
fill_android_app_ActivityManager(v2); // 0x1b + 1
classActivityManager = (*env)->FindClass(env, v2);
debug(env, "ActivityManager: %s", classActivityManager);
fill_getService(v1); // 0xa + 1
fill_getService_signature(v2); // 0x20 + 1
method = (*env)->GetStaticMethodID(env, classActivityManager, v1, v2);
#ifdef DEBUG
LOGI("ActivityManager.getService: %p", method);
#endif
if (method == NULL) {
#ifdef DEBUG
LOGW("cannot find ActivityManager.getService");
#endif
(*env)->ExceptionClear(env);
goto clean3;
}
} else {
fill_android_app_ActivityManagerNative(v2); // 0x21 + 1
classActivityManager = (*env)->FindClass(env, v2);
debug(env, "ActivityManagerNative: %s", classActivityManager);
fill_getDefault(v1); // 0xa + 1
fill_getDefault_signature(v2); // 0x20 + 1
method = (*env)->GetStaticMethodID(env, classActivityManager, v1, v2);
#ifdef DEBUG
LOGI("ActivityManagerNative.getDefault: %p", method);
#endif
}
jobject activityManager = (*env)->CallStaticObjectMethod(env, classActivityManager, method);
if (activityManager == NULL) {
#ifdef DEBUG
LOGW("activity manager is null");
#endif
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
goto clean3;
}
debug(env, "activity manager: %s", activityManager);
jclass binderClass = NULL;
jobject binder = NULL;
fill_android_os_BinderProxy(v2); // 0x16 + 1
jclass classBinderProxy = (*env)->FindClass(env, v2);
if (classBinderProxy == NULL) {
#ifdef DEBUG
LOGW("cannot find BinderProxy");
#endif
(*env)->ExceptionClear(env);
} else {
debug(env, "BinderProxy: %s", classBinderProxy);
}
fill_android_os_IInterface(v2); // 0x15 + 1
jclass classIInterface = (*env)->FindClass(env, v2);
debug(env, "IInterface: %s", classIInterface);
if (!(*env)->IsInstanceOf(env, activityManager, classIInterface)) {
#ifdef DEBUG
LOGW("activity manager is not IInterface");
#endif
goto clean2;
}
fill_asBinder(v1); // 0x8 + 1
fill_asBinder_signature(v2); // 0x16 + 1
method = (*env)->GetMethodID(env, classIInterface, v1, v2);
#ifdef DEBUG
LOGI("IInterface.asBinder: %p", method);
#endif
binder = (*env)->CallObjectMethod(env, activityManager, method);
if (binder == NULL) {
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
showError(env, NULL);
proxy = true;
goto clean2;
}
debug(env, "binder: %s", binder);
binderClass = (*env)->GetObjectClass(env, binder);
debug(env, "binder class: %s", binderClass);
if (classBinderProxy != NULL && !(*env)->IsSameObject(env, binderClass, classBinderProxy)) {
showError(env, binderClass);
proxy = true;
goto clean2;
}
fill_android_os_ServiceManager(v2); // 0x19 + 1
jclass classServiceManager = (*env)->FindClass(env, v2);
if (classServiceManager == NULL) {
#ifdef DEBUG
LOGW("cannot find ServiceManager");
#endif
(*env)->ExceptionClear(env);
goto clean2;
}
debug(env, "ServiceManager: %s", classServiceManager);
fill_getService(v1); // 0xa + 1
fill_getService_signature_IBinder(v2); // 0x28 + 1
method = (*env)->GetStaticMethodID(env, classServiceManager, v1, v2);
#ifdef DEBUG
LOGI("ServiceManager.getService: %p", method);
#endif
if (method == NULL) {
(*env)->ExceptionClear(env);
goto clean1;
}
fill_activity(v2); // 0x8 + 1
jstring stringActivity = (*env)->NewStringUTF(env, v2);
jobject service = (*env)->CallStaticObjectMethod(env, classServiceManager, method,
stringActivity);
jclass serviceClass = NULL;
if (service == NULL) {
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
showError(env, NULL);
proxy = true;
goto clean;
}
debug(env, "service: %s", service);
serviceClass = (*env)->GetObjectClass(env, service);
debug(env, "service class: %s", serviceClass);
if (classBinderProxy != NULL && !(*env)->IsSameObject(env, serviceClass, classBinderProxy)) {
showError(env, serviceClass);
proxy = true;
goto clean;
}
bool equals = (*env)->IsSameObject(env, binder, service);
#ifdef DEBUG
LOGI("binder: %p, service: %p, equals: %d", binder, service, equals);
#endif
proxy = !equals;
clean:
if (serviceClass != NULL) {
(*env)->DeleteLocalRef(env, serviceClass);
}
if (service != NULL) {
(*env)->DeleteLocalRef(env, service);
}
(*env)->DeleteLocalRef(env, stringActivity);
clean1:
(*env)->DeleteLocalRef(env, classServiceManager);
clean2:
if (binderClass != NULL) {
(*env)->DeleteLocalRef(env, binderClass);
}
if (binder != NULL) {
(*env)->DeleteLocalRef(env, binder);
}
if (classBinderProxy != NULL) {
(*env)->DeleteLocalRef(env, classBinderProxy);
}
(*env)->DeleteLocalRef(env, classIInterface);
(*env)->DeleteLocalRef(env, activityManager);
clean3:
(*env)->DeleteLocalRef(env, classActivityManager);
return proxy;
}
-21
View File
@@ -1,21 +0,0 @@
//
// Created by Thom on 2019/3/15.
//
#ifndef BREVENT_AM_PROXY_H
#define BREVENT_AM_PROXY_H
#include <jni.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool isAmProxy(JNIEnv *env, int sdk);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_AM_PROXY_H
-169
View File
@@ -1,169 +0,0 @@
//
// Created by Thom on 2019/3/8.
//
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#ifdef MAIN
#include <stdio.h>
#else
#include "common.h"
#include "openat.h"
#endif
static bool isApkSigBlock42(const char *buffer) {
// APK Sig Block 42
return *buffer == 'A'
&& *++buffer == 'P'
&& *++buffer == 'K'
&& *++buffer == ' '
&& *++buffer == 'S'
&& *++buffer == 'i'
&& *++buffer == 'g'
&& *++buffer == ' '
&& *++buffer == 'B'
&& *++buffer == 'l'
&& *++buffer == 'o'
&& *++buffer == 'c'
&& *++buffer == 'k'
&& *++buffer == ' '
&& *++buffer == '4'
&& *++buffer == '2';
}
int checkSignature(const char *path) {
unsigned char buffer[0x11] = {0};
uint32_t size4;
uint64_t size8, size_of_block;
#ifdef DEBUG
LOGI("check signature for %s", path);
#endif
int sign = -1;
int fd = (int) openAt(AT_FDCWD, path, O_RDONLY);
#ifdef DEBUG_OPENAT
LOGI("openat %s returns %d", path, fd);
#endif
if (fd < 0) {
return sign;
}
sign = 1;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (int i = 0;; ++i) {
unsigned short n;
lseek(fd, -i - 2, SEEK_END);
read(fd, &n, 2);
if (n == i) {
lseek(fd, -22, SEEK_CUR);
read(fd, &size4, 4);
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
#ifdef MAIN
if (i > 0) {
printf("warning: comment length is %d\n", i);
}
#endif
break;
}
}
if (i == 0xffff) {
#ifdef MAIN
printf("error: cannot find eocd\n");
#endif
goto clean;
}
}
lseek(fd, 12, SEEK_CUR);
// offset
read(fd, &size4, 0x4);
lseek(fd, (off_t) (size4 - 0x18), SEEK_SET);
read(fd, &size8, 0x8);
read(fd, buffer, 0x10);
if (!isApkSigBlock42((char *) buffer)) {
goto clean;
}
lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET);
read(fd, &size_of_block, 0x8);
if (size_of_block != size8) {
goto clean;
}
for (;;) {
uint32_t id;
uint32_t offset;
read(fd, &size8, 0x8); // sequence length
if (size8 == size_of_block) {
break;
}
read(fd, &id, 0x4); // id
offset = 4;
#ifdef MAIN
printf("id: 0x%08x\n", id);
#endif
if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) {
read(fd, &size4, 0x4); // signer-sequence length
read(fd, &size4, 0x4); // signer length
read(fd, &size4, 0x4); // signed data length
offset += 0x4 * 3;
read(fd, &size4, 0x4); // digests-sequence length
lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests
offset += 0x4 + size4;
read(fd, &size4, 0x4); // certificates length
read(fd, &size4, 0x4); // certificate length
offset += 0x4 * 2;
#ifdef MAIN
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
printf(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
#else
#if defined(GENUINE_SIZE) && defined(GENUINE_HASH)
if (size4 == GENUINE_SIZE) {
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
if ((((unsigned) hash) ^ 0x14131211u) == GENUINE_HASH) {
sign = 0;
break;
}
}
#else
sign = 0;
break;
#endif
#endif
}
lseek(fd, (off_t) (size8 - offset), SEEK_CUR);
}
clean:
close(fd);
return sign;
}
#ifdef MAIN
int main(int argc, char **argv) {
if (argc > 1) {
checkSignature(argv[1]);
}
}
#endif
@@ -1,18 +0,0 @@
//
// Created by Thom on 2019/3/8.
//
#ifndef BREVENT_APK_SIGN_V2_H
#define BREVENT_APK_SIGN_V2_H
#ifdef __cplusplus
extern "C" {
#endif
int checkSignature(const char *path);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_APK_SIGN_V2_H
-233
View File
@@ -1,233 +0,0 @@
//
// Created by Thom on 2019/3/20.
//
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <sys/system_properties.h>
#include "common.h"
#include "plt.h"
static volatile int mGenuine;
static bool onCheckTrue(JNIEnv *env __unused) {
#ifdef DEBUG_NATIVE
has_native_libs();
#endif
#ifdef DEBUG_GENUINE_MOCK
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFalse(JNIEnv *env __unused) {
#if defined(GENUINE_FALSE_CRASH)
return false;
#elif defined(GENUINE_FALSE_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFake(JNIEnv *env __unused) {
#if defined(GENUINE_FAKE_CRASH)
return false;
#elif defined(GENUINE_FAKE_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckOverlay(JNIEnv *env __unused) {
#if defined(GENUINE_OVERLAY_CRASH)
return false;
#elif defined(GENUINE_OVERLAY_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckOdex(JNIEnv *env __unused) {
#if defined(GENUINE_ODEX_CRASH)
return false;
#elif defined(GENUINE_ODEX_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckDex(JNIEnv *env __unused) {
#if defined(GENUINE_DEX_CRASH)
return false;
#elif defined(GENUINE_DEX_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckProxy(JNIEnv *env __unused) {
#if defined(GENUINE_PROXY_CRASH)
return false;
#elif defined(GENUINE_PROXY_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckError(JNIEnv *env __unused) {
#if defined(GENUINE_ERROR_CRASH)
return false;
#elif defined(GENUINE_ERROR_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFatal(JNIEnv *env __unused) {
#if defined(GENUINE_FATAL_CRASH)
return false;
#elif defined(GENUINE_FATAL_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckNoapk(JNIEnv *env __unused) {
#if defined(GENUINE_NOAPK_CRASH)
return false;
#elif defined(GENUINE_NOAPK_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
bool setGenuine(JNIEnv *env, int genuine) {
mGenuine = genuine;
switch (genuine) {
case CHECK_TRUE:
return onCheckTrue(env);
case CHECK_FALSE:
return onCheckFalse(env);
case CHECK_FAKE:
return onCheckFake(env);
case CHECK_OVERLAY:
return onCheckOverlay(env);
case CHECK_ODEX:
return onCheckOdex(env);
case CHECK_DEX:
return onCheckDex(env);
case CHECK_PROXY:
return onCheckProxy(env);
case CHECK_ERROR:
return onCheckError(env);
case CHECK_FATAL:
return onCheckFatal(env);
case CHECK_NOAPK:
return onCheckNoapk(env);
default:
return true;
}
}
int getGenuine() {
return mGenuine;
}
char *getGenuinePackageName() {
#ifdef GET_GENUINE_PACKAGE_NAME
return GET_GENUINE_PACKAGE_NAME();
#elif defined(GENUINE_NAME)
static unsigned int m = 0;
if (m == 0) {
m = 20;
} else if (m == 23) {
m = 29;
}
char name[] = GENUINE_NAME;
unsigned int length = sizeof(name) - 1;
for (unsigned int i = 0; i < length; ++i) {
name[i] ^= ((i + length) % m);
}
name[length] = '\0';
return strdup(name);
#else
return NULL;
#endif
}
__attribute__((__format__ (__printf__, 2, 0)))
void genuine_log_print(int prio, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
__android_log_vprint(prio, TAG, fmt, ap);
va_end(ap);
}
static inline void fill_ro_build_version_sdk(char v[]) {
// ro.build.version.sdk
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 's';
v[0x1] = 'm';
v[0x2] = '-';
v[0x3] = 'f';
v[0x4] = 'p';
v[0x5] = 'o';
v[0x6] = 'k';
v[0x7] = 'l';
v[0x8] = '\'';
v[0x9] = '|';
v[0xa] = 'n';
v[0xb] = '~';
v[0xc] = '~';
v[0xd] = 'g';
v[0xe] = '`';
v[0xf] = '~';
v[0x10] = '?';
v[0x11] = 'a';
v[0x12] = 'd';
v[0x13] = 'j';
for (unsigned int i = 0; i < 0x14; ++i) {
v[i] ^= ((i + 0x14) % m);
}
v[0x14] = '\0';
}
int getSdk() {
static int sdk = 0;
if (sdk == 0) {
char v1[0x20];
char prop[PROP_VALUE_MAX] = {0};
fill_ro_build_version_sdk(v1);
__system_property_get(v1, prop);
sdk = (int) strtol(prop, NULL, 10);
}
return sdk;
}
#ifdef DEBUG
void debug(JNIEnv *env, const char *format, jobject object) {
if (object == NULL) {
LOGI(format, NULL);
} else {
jclass objectClass = (*env)->FindClass(env, "java/lang/Object");
jmethodID toString = (*env)->GetMethodID(env, objectClass, "toString",
"()Ljava/lang/String;");
jstring string = (jstring) (*env)->CallObjectMethod(env, object, toString);
const char *value = (*env)->GetStringUTFChars(env, string, NULL);
LOGI(format, value);
(*env)->ReleaseStringUTFChars(env, string, value);
(*env)->DeleteLocalRef(env, string);
(*env)->DeleteLocalRef(env, objectClass);
}
}
#endif
-73
View File
@@ -1,73 +0,0 @@
//
// Created by Thom on 2019/3/20.
//
#ifndef BREVENT_COMMON_H
#define BREVENT_COMMON_H
#include <jni.h>
#include <stdbool.h>
#include <android/log.h>
#if __has_include("genuine.h")
#include "genuine.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
enum {
CHECK_TRUE,
CHECK_FALSE,
CHECK_FAKE,
CHECK_OVERLAY,
CHECK_ODEX,
CHECK_DEX,
CHECK_PROXY,
CHECK_ERROR,
CHECK_FATAL,
CHECK_NOAPK,
};
#ifndef TAG
#define TAG "Genuine"
#endif
#ifndef LOGI
#define LOGI(...) (genuine_log_print(ANDROID_LOG_INFO, __VA_ARGS__))
#endif
#ifndef LOGW
#define LOGW(...) (genuine_log_print(ANDROID_LOG_WARN, __VA_ARGS__))
#endif
#ifndef LOGE
#define LOGE(...) (genuine_log_print(ANDROID_LOG_ERROR, __VA_ARGS__))
#endif
void genuine_log_print(int prio, const char *fmt, ...);
char *getGenuinePackageName();
bool setGenuine(JNIEnv *env, int genuine);
int getGenuine();
int getSdk();
bool has_native_libs();
#ifdef DEBUG
void debug(JNIEnv *env, const char *format, jobject object);
#else
#define debug(...) do {} while(0);
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_COMMON_H
File diff suppressed because it is too large. Load diff
-43
View File
@@ -1,43 +0,0 @@
#include <stdbool.h>
#define GENUINE_NAME {0x62, 0x63, 0x23, 0x69, 0x66, 0x64, 0x79, 0x67, 0x71, 0x2e, 0x6f, 0x67, 0x68, 0x6b, 0x6c, 0x68, 0x71, 0x6d, 0x7b, 0x7e, 0x6e, 0x7e, 0x23, 0x6b, 0x67, 0x66, 0x78, 0x77, 0x64, 0x65, 0x73, 0x0}
#define GENUINE_SIZE 0x02d1
#define GENUINE_HASH 0x9a99ba0c
/* genuine false handler */
#define GENUINE_FALSE_CRASH
// #define GENUINE_FALSE_NATIVE
/* genuine fake handler */
#define GENUINE_FAKE_CRASH
// #define GENUINE_FAKE_NATIVE
/* genuine overlay handler */
// #define GENUINE_OVERLAY_CRASH
// #define GENUINE_OVERLAY_NATIVE
/* genuine odex handler */
// #define GENUINE_ODEX_CRASH
// #define GENUINE_ODEX_NATIVE
/* genuine dex handler */
#define GENUINE_DEX_CRASH
// #define GENUINE_DEX_NATIVE
/* genuine proxy handler */
#define GENUINE_PROXY_CRASH
// #define GENUINE_PROXY_NATIVE
/* genuine error handler */
#define GENUINE_ERROR_CRASH
// #define GENUINE_ERROR_NATIVE
/* genuine fatal handler */
#define GENUINE_FATAL_CRASH
// #define GENUINE_FATAL_NATIVE
/* genuine noapk handler */
#define GENUINE_NOAPK_CRASH
// #define GENUINE_NOAPK_NATIVE
bool checkGenuine(JNIEnv *env);
-333
View File
@@ -1,333 +0,0 @@
//
// Created by Thom on 2019/3/18.
//
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "inline.h"
#include "common.h"
#ifdef DEBUG_HOOK_SELF
#include <syscall.h>
#include <unistd.h>
#include <sys/stat.h>
#include "whale/whale.h"
static long faccessat_replace(int dirfd, const char *pathname, int mode, int flags) {
return syscall(__NR_faccessat, dirfd, pathname, mode, flags);
}
static long fchownat_replace(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) {
return syscall(__NR_fchownat, dirfd, pathname, owner, group, flags);
}
static int futimens_replace(int fd, const struct timespec times[2]) {
return utimensat(fd, NULL, times, 0);
}
#if defined(__arm__) || defined(__aarch64__)
void check_inline_hook_hookzz() {
void *backup;
zz_disable_arm_arm64_b_branch();
ZzReplace(&fchownat, &fchownat_replace, &backup);
LOGI("hookzz fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
ZzReplace(&faccessat, &faccessat_replace, &backup);
LOGI("hookzz faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
ZzReplace(&futimens, &futimens_replace, &backup);
LOGI("hookzz futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
void check_inline_hook_hookzz_b() {
void *backup;
zz_enable_arm_arm64_b_branch();
ZzReplace(&fchownat, &fchownat_replace, &backup);
LOGI("hookzz-b fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
ZzReplace(&faccessat, &faccessat_replace, &backup);
LOGI("hookzz-b faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
ZzReplace(&futimens, &futimens_replace, &backup);
LOGI("hookzz-b futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#endif
void check_inline_hook_whale() {
WInlineHookFunction(&fchownat, &fchownat_replace, NULL);
LOGI("whale fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
WInlineHookFunction(&faccessat, &faccessat_replace, NULL);
LOGI("whale faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
WInlineHookFunction(&futimens, &futimens_replace, NULL);
LOGI("whale futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#if defined(__arm__) || defined(DEBUG_HOOK_IDE)
#include "substrate/CydiaSubstrate.h"
void check_inline_hook_substrate() {
MSHookFunction(&fchownat, &fchownat_replace, NULL);
LOGI("substrate fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
MSHookFunction(&faccessat, &faccessat_replace, NULL);
LOGI("substrate faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
MSHookFunction(&futimens, &futimens_replace, NULL);
LOGI("substrate futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#endif
#endif
bool isInlineHooked(void *symbol) {
if (symbol == NULL) {
return false;
}
#if defined(__arm__) || defined(DEBUG_HOOK_IDE)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/ldr-literal-load-register-literal
// A1, !(P == 0 && W == 1), we don't check P and W
// cond 010P U0W1 1111 _Rt_ xxxx xxxx xxxx
#define IS_LDR_PC_A1(x) (((x) & 0xfe5ff000u) == 0xe41ff000u)
// T2
// 1111 1000 U101 1111 | _Rt_ xxxx xxxx xxxx
#define IS_LDR_PC_T2(x) (((x) & 0xf000ff7fu) == 0xf000f85fu)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/b-branch
// A1
// cond 100 xxxx xxxx xxxx xxxx xxxx xxxx
#define IS_B_A1(x) (((x) & 0xff000000u) == 0xea000000u)
// T2
// 1110 0xxx xxxx xxxx
#define IS_B_T2(x) (((x) & 0xf800u) == 0xe000u)
// T4
// 1111 0Sxx xxxx xxxx | 10J1 Jxxx xxxx xxxx
// -- imm10 --- | --- imm11 ---
#define IS_B_T4(x) (((x) & 0xd000f800u) == 0x9000f000u)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/nop-no-operation
// T1, hint should be 0000, we don't check
// 1011 1111 hint 0000
#define IS_NOP_T1(x) (((x) & 0xff0fu) == 0xbf00u)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/mov-movs-register-move-register
// cydia use `mov r8, r8` for Nop
// T1, Mmmm is Rm, Dddd is Rd
// 0100 0110 DMmm mddd
#define _IS_MOV_T1(x) (((x) & 0xff00u) == 0x4600u)
#define _RM_MOV_T1(x) ((((x) & 0x78u) >> 3u))
#define _RD_MOV_T1(x) ((((x) & 0x80u) >> 4u) | ((x) & 7u))
#define IS_MOV_T1_RR(x) (_IS_MOV_T1(x) && _RM_MOV_T1(x) == _RD_MOV_T1(x))
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/bx-branch-and-exchange
// cydia use `bx`
// T1
// 0100 0111 0Rmm m000
#define IS_BX_T1(x) (((x) & 0xff87u) == 0x4700u)
#define RM_BX_T1(x) (((x) & 0x0078u) >> 3u)
#define IS_BX_PC_T1(x) ((x) == 0x4778u)
uintptr_t address = (uintptr_t) symbol;
if ((address & 1U) == 0) {
uint32_t *value32 = (uint32_t *) address;
if (IS_LDR_PC_A1(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm ldr pc) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
if (IS_B_A1(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm b) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
#ifdef DEBUG
LOGI("(arm) symbol: %p, value: %08x", symbol, *value32);
#endif
} else {
address = address & ~1U;
uint16_t *value16 = (uint16_t *) address;
uint32_t *value32 = (uint32_t *) address;
if (IS_LDR_PC_T2(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_B_T4(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb b) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_B_T2(*value16)) {
#ifdef DEBUG_HOOK
LOGW("(thumb b) symbol: %p, address: %p, value: %04x",
symbol, address, *value16);
#endif
return true;
}
if (IS_NOP_T1(*value16) || IS_MOV_T1_RR(*value16)) {
#ifdef DEBUG_HOOK
LOGW("(thumb nop) symbol: %p, address: %p, value: %04x",
symbol, address, *value16);
#endif
address += 2;
value16 = (uint16_t *) address;
value32 = (uint32_t *) address;
}
if (IS_LDR_PC_T2(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_BX_PC_T1(*value16) && IS_LDR_PC_A1(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(thumb bx + arm ldr pc) symbol: %p, address: %p, value: %08x %08x",
symbol, address, *value32, *(value32 + 1));
#endif
return true;
}
#ifdef DEBUG
LOGI("(thumb) symbol: %p, address: %p, value: %08x %08x",
symbol, address, *value32, *(value32 + 1));
#endif
}
#endif
#if defined(__aarch64__) || defined(DEBUG_HOOK_IDE)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch
// 0001 01xx xxxx xxxx xxxx xxxx xxxx xxxx
// ------------ imm26 -------------
#define IS_B(x) (((x) & 0xfc000000u) == 0x14000000u)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/ldr-literal-load-register-literal
// 0101 1000 xxxx xxxx xxxx xxxx xxxR tttt
// -------- imm19 --------
#define IS_LDR_X(x) (((x) & 0xff000000u) == 0x58000000u)
#define X_LDR(x) ((x) & 0x1fu)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/adrp-form-pc-relative-address-to-4kb-page
// 1xx1 0000 xxxx xxxx xxxx xxxx xxxR dddd
// lo -------- immhi --------
#define IS_ADRP_X(x) (((x) & 0x9f000000u) == 0x90000000u)
#define X_ADRP(x) ((x) & 0x1fu)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/br-branch-to-register
// 1101 0110 0001 1111 0000 00Rn nnn0 0000
#define IS_BR_X(x) (((x) & 0xfffffc0f) == 0xd61f0000u)
#define X_BR(x) (((x) & 0x3e0u) >> 0x5u)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/movz-move-wide-with-zero
// 1op1 0010 1hwx xxxx xxxx xxxx xxxR dddd
// ------ imm16 -------
// for op, 00 -> MOVN, 10 -> MOVZ, 11 -> MOVK
#define IS_MOV_X(x) (((x) & 0x9f800000u) == 0x92800000u)
#define X_MOV(x) ((x) & 0x1fu)
uint32_t *value32 = symbol;
if (IS_B(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm64 b) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
if (IS_LDR_X(*value32) && IS_BR_X(*(value32 + 1))) {
uint32_t x = X_LDR(*value32);
if (x == X_BR(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(arm64 ldr+br x%d) symbol: %p, value: %08x %08x",
x, symbol, *value32, *(value32 + 1));
#endif
return true;
}
}
if (IS_ADRP_X(*value32) && IS_BR_X(*(value32 + 1))) {
uint32_t x = X_ADRP(*value32);
if (x == X_BR(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(arm64 adrp+br x%d) symbol: %p, value: %08x %08x",
x, symbol, *value32, *(value32 + 1));
#endif
return true;
}
}
if (IS_MOV_X(*value32)) {
uint32_t x = X_MOV(*value32);
for (int i = 1; i <= 4; ++i) {
if (IS_BR_X(*(value32 + i))) {
if (x != X_BR(*(value32 + i))) {
break;
}
#ifdef DEBUG_HOOK
for (int k = 0; k < i; ++k) {
LOGW("(arm64 mov x%d) symbol: %p, value: %08x",
x, symbol + sizeof(uint32_t) * k, *(value32 + k));
}
LOGW("(arm64 br x%d) symbol: %p, value: %08x",
x, symbol + sizeof(uint32_t) * i, *(value32 + i));
#endif
return true;
} else if (IS_MOV_X(*(value32 + i))) {
if (x != X_MOV(*(value32 + i))) {
break;
}
}
}
}
#ifdef DEBUG
LOGI("(arm64) symbol: %p, value: %08x %08x", symbol, *value32, *(value32 + 1));
#endif
#endif
return false;
}
#ifdef DEBUG_HOOK_IO
bool setRead(void *symbol) {
uintptr_t address = (uintptr_t) symbol;
uintptr_t page_size = (uintptr_t) getpagesize();
uintptr_t base = address & ~(page_size - 1);
// inline check read at most 20 bytes
uintptr_t end = (address + 20 + page_size - 1) & -page_size;
#ifdef DEBUG
LOGI("set r+x from %p to %p", base, end);
#endif
if (mprotect((void *) base, end - base, PROT_READ | PROT_EXEC)) {
#ifdef DEBUG
LOGW("cannot mprotect: %s", strerror(errno));
#endif
return false;
} else {
return true;
}
}
#endif
-45
View File
@@ -1,45 +0,0 @@
//
// Created by Thom on 2019/3/18.
//
#ifndef BREVENT_INLINE_H
#define BREVENT_INLINE_H
#include <stdbool.h>
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DEBUG_HOOK_IO
bool setRead(void *symbol);
#endif
bool isInlineHooked(void *symbol);
#ifdef DEBUG_HOOK_SELF
#if defined(__arm__) || defined(__aarch64__)
#include "hookzz/hookzz.h"
void check_inline_hook_hookzz();
void check_inline_hook_hookzz_b();
#endif
void check_inline_hook_whale();
#if defined(__arm__)
void check_inline_hook_substrate();
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_INLINE_H
-56
View File
@@ -1,56 +0,0 @@
//
// Created by Thom on 2019/3/30.
//
#include <unistd.h>
#include <sys/syscall.h>
#include "openat.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) {
#if defined(__arm__)
intptr_t r;
__asm__ volatile(
#ifndef OPTIMIZE_ASM
"mov r0, %1\n\t"
"mov r1, %2\n\t"
"mov r2, %3\n\t"
#endif
"mov ip, r7\n\t"
".cfi_register r7, ip\n\t"
"ldr r7, =" STR(__NR_openat) "\n\t"
"svc #0\n\t"
"mov r7, ip\n\t"
".cfi_restore r7\n\t"
#ifndef OPTIMIZE_ASM
"mov %0, r0\n\t"
#endif
: "=r" (r)
: "r" (fd), "r" (path), "r" (flag));
return r;
#elif defined(__aarch64__)
intptr_t r;
__asm__ volatile(
#ifndef OPTIMIZE_ASM
"mov x0, %1\n\t"
"mov x1, %2\n\t"
"mov x2, %3\n\t"
#endif
"mov x8, " STR(__NR_openat) "\n\t"
"svc #0\n\t"
#ifndef OPTIMIZE_ASM
"mov %0, x0\n\t"
#endif
: "=r" (r)
: "r" (fd), "r" (path), "r" (flag));
return r;
#else
return (intptr_t) syscall(__NR_openat, fd, path, flag);
#endif
}
-28
View File
@@ -1,28 +0,0 @@
//
// Created by Thom on 2019/3/30.
//
#ifndef BREVENT_OPENAT_H
#define BREVENT_OPENAT_H
#include <stdint.h>
#include <fcntl.h>
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __ANDROID__
intptr_t openAt(intptr_t fd, const char *path, intptr_t flag);
#else
#define openAt openat
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_OPENAT_H
-95
View File
@@ -1,95 +0,0 @@
//
// Created by Thom on 2019-05-03.
//
#include "path.h"
#include <stdio.h>
static inline bool isSystem(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 's'
&& *++str == 'y'
&& *++str == 's'
&& *++str == 't'
&& *++str == 'e'
&& *++str == 'm'
&& *++str == '/';
}
static inline bool isVendor(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'v'
&& *++str == 'e'
&& *++str == 'n'
&& *++str == 'd'
&& *++str == 'o'
&& *++str == 'r'
&& *++str == '/';
}
static inline bool isOem(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'o'
&& *++str == 'e'
&& *++str == 'm'
&& *++str == '/';
}
bool isThirdParty(const char *str) {
if (isSystem(str) || isVendor(str) || isOem(str)) {
return false;
} else {
return true;
}
}
bool isDataApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'd'
&& *++str == 'a'
&& *++str == 't'
&& *++str == 'a'
&& *++str == '/'
&& *++str == 'a'
&& *++str == 'p'
&& *++str == 'p'
&& *++str == '/';
}
bool isAsecApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'm'
&& *++str == 'n'
&& *++str == 't'
&& *++str == '/'
&& *++str == 'a'
&& *++str == 's'
&& *++str == 'e'
&& *++str == 'c'
&& *++str == '/';
}
bool isExpandApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'm'
&& *++str == 'n'
&& *++str == 't'
&& *++str == '/'
&& *++str == 'e'
&& *++str == 'x'
&& *++str == 'p'
&& *++str == 'a'
&& *++str == 'n'
&& *++str == 'd'
&& *++str == '/';
}
bool isExternalSdApp(const char *str) {
return isAsecApp(str) || isExpandApp(str);
}
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff. Show more