summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2014-10-16 23:34:20 +0400
committerAndrew Dolgov <[email protected]>2014-10-16 23:34:20 +0400
commit124f869faae3a0f75a3825e6a8e195c17f3c626a (patch)
tree4a6f120195b60673994cfd78b6d0002bd1020bea
parentbc10c74406c0e8a16c40dbfcd30a138d441dd859 (diff)
initial for idea
-rw-r--r--.gitignore11
-rw-r--r--.idea/.name1
-rw-r--r--.idea/compiler.xml23
-rw-r--r--.idea/copyright/profiles_settings.xml3
-rw-r--r--.idea/encodings.xml5
-rw-r--r--.idea/gradle.xml18
-rw-r--r--.idea/misc.xml23
-rw-r--r--.idea/modules.xml10
-rw-r--r--.idea/scopes/scope_settings.xml5
-rw-r--r--.idea/vcs.xml7
-rw-r--r--Tiny-Comics-Reader.iml19
-rw-r--r--build.gradle15
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 49896 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--gradlew164
-rw-r--r--gradlew.bat90
-rw-r--r--org.fox.ttcomics/build.gradle25
-rw-r--r--org.fox.ttcomics/org.fox.ttcomics.iml88
-rw-r--r--org.fox.ttcomics/src/main/AndroidManifest.xml54
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java583
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/MVTest.java56
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/UnrarCallback.java21
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/Volume.java44
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/VolumeManager.java28
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/crc/RarCRC.java145
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/crypt/Rijndael.java27
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/exception/RarException.java73
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolume.java65
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolumeManager.java54
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/io/IReadOnlyAccess.java61
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/io/InputStreamReadOnlyAccessFile.java59
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/io/RandomAccessStream.java203
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/io/Raw.java305
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessByteArray.java90
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessFile.java55
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessInputStream.java81
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/AVHeader.java66
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BaseBlock.java179
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BlockHeader.java70
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/CommentHeader.java70
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EAHeader.java90
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EndArcHeader.java63
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileHeader.java421
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileNameDecoder.java76
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/HostSystem.java72
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MacInfoHeader.java81
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MainHeader.java141
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MarkHeader.java84
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/NewSubHeaderType.java89
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/ProtectHeader.java71
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SignHeader.java60
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeader.java70
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeaderType.java77
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnixOwnersHeader.java93
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnrarHeadertype.java164
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ComprDataIO.java362
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack.java1051
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack15.java620
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack20.java597
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/UnpackFilter.java94
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/AudioVariables.java144
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/BitDecode.java35
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/CodeType.java29
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Compress.java45
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Decode.java81
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/DistDecode.java37
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/FilterType.java30
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LitDecode.java36
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LowDistDecode.java37
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/MultDecode.java37
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/RepDecode.java36
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/AnalyzeHeapDump.java94
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/BlockTypes.java58
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/FreqData.java87
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/ModelPPM.java696
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/PPMContext.java477
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/Pointer.java59
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RangeCoder.java177
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarMemBlock.java139
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarNode.java69
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SEE2Context.java102
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/State.java119
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/StateRef.java92
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SubAllocator.java427
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/BitInput.java108
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/RarVM.java1221
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCmdFlags.java81
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCommands.java226
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMFlags.java79
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMOpType.java66
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedCommand.java57
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedOperand.java58
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedProgram.java127
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilterSignature.java64
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilters.java79
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedByte.java68
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedInteger.java29
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedLong.java29
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedShort.java29
-rw-r--r--org.fox.ttcomics/src/main/java/com/github/junrar/util/VolumeHelper.java101
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouch.java268
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java509
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Cubic.java20
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Easing.java10
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/FastBitmapDrawable.java84
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/IBitmapDrawable.java14
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/utils/IDisposable.java6
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java69
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java50
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java12
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java329
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java529
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java166
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/CommonActivity.java653
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java68
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/DirectoryPicker.java169
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/MainActivity.java253
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java124
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/SyncClient.java159
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewComicActivity.java391
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java38
-rw-r--r--org.fox.ttcomics/src/main/res/anim/appear.xml9
-rw-r--r--org.fox.ttcomics/src/main/res/anim/layout_comics.xml5
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/badimage.pngbin0 -> 14350 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_good.pngbin0 -> 457 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_overflow.pngbin0 -> 225 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/ic_launcher.pngbin0 -> 7867 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/ic_refresh_light.pngbin0 -> 3138 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/ic_search_light.pngbin0 -> 1764 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/ic_settings.pngbin0 -> 1540 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-hdpi/ic_share_light.pngbin0 -> 1606 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_good.pngbin0 -> 585 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_overflow.pngbin0 -> 280 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_launcher.pngbin0 -> 11597 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_refresh_light.pngbin0 -> 3219 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_search_light.pngbin0 -> 2127 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_settings.pngbin0 -> 1641 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_share_light.pngbin0 -> 1780 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 13687 bytes
-rw-r--r--org.fox.ttcomics/src/main/res/drawable/comic_tile.xml26
-rw-r--r--org.fox.ttcomics/src/main/res/drawable/comic_tile_folder.xml26
-rw-r--r--org.fox.ttcomics/src/main/res/drawable/s_badimage.svg88
-rw-r--r--org.fox.ttcomics/src/main/res/drawable/s_launcher.svg204
-rw-r--r--org.fox.ttcomics/src/main/res/layout-land/fragment_comics_list.xml29
-rw-r--r--org.fox.ttcomics/src/main/res/layout-sw600dp/activity_main.xml22
-rw-r--r--org.fox.ttcomics/src/main/res/layout-sw600dp/activity_view_comic.xml23
-rw-r--r--org.fox.ttcomics/src/main/res/layout-sw600dp/comics_grid_row.xml73
-rw-r--r--org.fox.ttcomics/src/main/res/layout-sw600dp/fragment_comics_list.xml35
-rw-r--r--org.fox.ttcomics/src/main/res/layout/activity_main.xml15
-rw-r--r--org.fox.ttcomics/src/main/res/layout/activity_view_comic.xml16
-rw-r--r--org.fox.ttcomics/src/main/res/layout/chooser_list.xml33
-rw-r--r--org.fox.ttcomics/src/main/res/layout/comics_grid_row.xml73
-rw-r--r--org.fox.ttcomics/src/main/res/layout/comics_list_row.xml73
-rw-r--r--org.fox.ttcomics/src/main/res/layout/dialog_location.xml14
-rw-r--r--org.fox.ttcomics/src/main/res/layout/dialog_location_compat.xml23
-rw-r--r--org.fox.ttcomics/src/main/res/layout/fragment_comic.xml21
-rw-r--r--org.fox.ttcomics/src/main/res/layout/fragment_comics_list.xml36
-rw-r--r--org.fox.ttcomics/src/main/res/layout/fragment_comics_pager.xml9
-rw-r--r--org.fox.ttcomics/src/main/res/layout/list_item.xml7
-rw-r--r--org.fox.ttcomics/src/main/res/menu/activity_main.xml19
-rw-r--r--org.fox.ttcomics/src/main/res/menu/activity_view_comic.xml26
-rw-r--r--org.fox.ttcomics/src/main/res/menu/comic_archive_context.xml8
-rw-r--r--org.fox.ttcomics/src/main/res/values-large/dimens.xml7
-rw-r--r--org.fox.ttcomics/src/main/res/values-v19/style.xml31
-rw-r--r--org.fox.ttcomics/src/main/res/values/attrs.xml5
-rw-r--r--org.fox.ttcomics/src/main/res/values/dimens.xml7
-rw-r--r--org.fox.ttcomics/src/main/res/values/strings.xml67
-rw-r--r--org.fox.ttcomics/src/main/res/values/style.xml15
-rw-r--r--org.fox.ttcomics/src/main/res/xml/preferences.xml63
-rw-r--r--settings.gradle1
170 files changed, 17873 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..906aa82
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+bin/
+gen/
+.gradle/
+build/
+local.properties
+import-summary.txt
+Thumbs.db
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/datasources.xml
+.idea/dataSources.ids
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..0e0a20e
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Tiny-Comics-Reader \ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..28bb10a
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ </component>
+</project>
+
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..c7d1c5a
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+<component name="CopyrightManager">
+ <settings default="" />
+</component> \ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..7c62b52
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
+</project>
+
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..8842032
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="distributionType" value="DEFAULT_WRAPPED" />
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/org.fox.ttcomics" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..38cc86c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/build/classes" />
+ </component>
+ <component name="masterDetails">
+ <states>
+ <state key="ProjectJDKs.UI">
+ <settings>
+ <last-edited>Android API 19 Platform</last-edited>
+ <splitter-proportions>
+ <option name="proportions">
+ <list>
+ <option value="0.2" />
+ </list>
+ </option>
+ </splitter-proportions>
+ </settings>
+ </state>
+ </states>
+ </component>
+</project>
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..93796ff
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/Tiny-Comics-Reader.iml" filepath="$PROJECT_DIR$/Tiny-Comics-Reader.iml" />
+ <module fileurl="file://$PROJECT_DIR$/org.fox.ttcomics/org.fox.ttcomics.iml" filepath="$PROJECT_DIR$/org.fox.ttcomics/org.fox.ttcomics.iml" />
+ </modules>
+ </component>
+</project>
+
diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
new file mode 100644
index 0000000..0d5175c
--- /dev/null
+++ b/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+<component name="DependencyValidationManager">
+ <state>
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+ </state>
+</component> \ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..ab55cf1
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project>
+
diff --git a/Tiny-Comics-Reader.iml b/Tiny-Comics-Reader.iml
new file mode 100644
index 0000000..8d49284
--- /dev/null
+++ b/Tiny-Comics-Reader.iml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="java-gradle" name="Java-Gradle">
+ <configuration>
+ <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..3ef601d
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,15 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.12.2'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1e61d1f
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/org.fox.ttcomics/build.gradle b/org.fox.ttcomics/build.gradle
new file mode 100644
index 0000000..ad3ab44
--- /dev/null
+++ b/org.fox.ttcomics/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion "20.0.0"
+
+ defaultConfig {
+ applicationId "org.fox.ttcomics"
+ minSdkVersion 8
+ targetSdkVersion 19
+ }
+
+ buildTypes {
+ release {
+ runProguard false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
+
+dependencies {
+ compile 'com.readystatesoftware.systembartint:systembartint:1.0.3'
+ compile 'com.android.support:support-v4:19.1.0'
+ compile 'com.android.support:appcompat-v7:18.0.0'
+}
diff --git a/org.fox.ttcomics/org.fox.ttcomics.iml b/org.fox.ttcomics/org.fox.ttcomics.iml
new file mode 100644
index 0000000..333a3cd
--- /dev/null
+++ b/org.fox.ttcomics/org.fox.ttcomics.iml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="Tiny-Comics-Reader" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="android-gradle" name="Android-Gradle">
+ <configuration>
+ <option name="GRADLE_PROJECT_PATH" value=":org.fox.ttcomics" />
+ </configuration>
+ </facet>
+ <facet type="android" name="Android">
+ <configuration>
+ <option name="SELECTED_BUILD_VARIANT" value="debug" />
+ <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+ <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugJava" />
+ <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
+ <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
+ <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
+ <option name="ALLOW_USER_CONFIGURATION" value="false" />
+ <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+ <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+ <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
+ <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+ <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
+ </content>
+ <orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" exported="" name="systembartint-1.0.3" level="project" />
+ <orderEntry type="library" exported="" name="support-v4-19.1.0" level="project" />
+ <orderEntry type="library" exported="" name="appcompat-v7-18.0.0" level="project" />
+ </component>
+</module>
+
diff --git a/org.fox.ttcomics/src/main/AndroidManifest.xml b/org.fox.ttcomics/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..bb68e84
--- /dev/null
+++ b/org.fox.ttcomics/src/main/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.fox.ttcomics"
+ android:versionCode="47"
+ android:versionName="1.13" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="19" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:hardwareAccelerated="true"
+ android:largeHeap="true"
+ android:allowBackup="true">
+ <activity
+ android:theme="@style/AppTheme"
+ android:name=".MainActivity"
+ android:label="@string/title_activity_main" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".PreferencesActivity"
+ android:label="@string/title_activity_preferences">
+ </activity>
+
+ <activity
+ android:theme="@style/ViewLightTheme"
+ android:name=".ViewComicActivity"
+ android:label="@string/title_activity_main">
+ </activity>
+
+ <activity
+ android:name=".CommonActivity"
+ android:label="@string/title_activity_main">
+ </activity>
+
+ <activity
+ android:name=".DirectoryPicker"
+ android:label="@string/title_activity_main">
+ </activity>
+
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java b/org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java
new file mode 100644
index 0000000..23ca39b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 22.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression
+ * algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.github.junrar.exception.RarException;
+import com.github.junrar.exception.RarException.RarExceptionType;
+import com.github.junrar.impl.FileVolumeManager;
+import com.github.junrar.io.IReadOnlyAccess;
+import com.github.junrar.rarfile.AVHeader;
+import com.github.junrar.rarfile.BaseBlock;
+import com.github.junrar.rarfile.BlockHeader;
+import com.github.junrar.rarfile.CommentHeader;
+import com.github.junrar.rarfile.EAHeader;
+import com.github.junrar.rarfile.EndArcHeader;
+import com.github.junrar.rarfile.FileHeader;
+import com.github.junrar.rarfile.MacInfoHeader;
+import com.github.junrar.rarfile.MainHeader;
+import com.github.junrar.rarfile.MarkHeader;
+import com.github.junrar.rarfile.ProtectHeader;
+import com.github.junrar.rarfile.SignHeader;
+import com.github.junrar.rarfile.SubBlockHeader;
+import com.github.junrar.rarfile.UnixOwnersHeader;
+import com.github.junrar.rarfile.UnrarHeadertype;
+import com.github.junrar.unpack.ComprDataIO;
+import com.github.junrar.unpack.Unpack;
+
+
+/**
+ * The Main Rar Class; represents a rar Archive
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class Archive implements Closeable {
+ private static Logger logger = Logger.getLogger(Archive.class.getName());
+
+ private IReadOnlyAccess rof;
+
+ private final UnrarCallback unrarCallback;
+
+ private final ComprDataIO dataIO;
+
+ private final List<BaseBlock> headers = new ArrayList<BaseBlock>();
+
+ private MarkHeader markHead = null;
+
+ private MainHeader newMhd = null;
+
+ private Unpack unpack;
+
+ private int currentHeaderIndex;
+
+ /** Size of packed data in current file. */
+ private long totalPackedSize = 0L;
+
+ /** Number of bytes of compressed data read from current file. */
+ private long totalPackedRead = 0L;
+
+ private VolumeManager volumeManager;
+ private Volume volume;
+
+ public Archive(VolumeManager volumeManager) throws RarException,
+ IOException {
+ this(volumeManager, null);
+ }
+
+ /**
+ * create a new archive object using the given {@link VolumeManager}
+ *
+ * @param volumeManager
+ * the the {@link VolumeManager} that will provide volume stream
+ * data
+ * @throws RarException
+ */
+ public Archive(VolumeManager volumeManager, UnrarCallback unrarCallback)
+ throws RarException, IOException {
+ this.volumeManager = volumeManager;
+ this.unrarCallback = unrarCallback;
+
+ setVolume(this.volumeManager.nextArchive(this, null));
+ dataIO = new ComprDataIO(this);
+ }
+
+ public Archive(File firstVolume) throws RarException, IOException {
+ this(new FileVolumeManager(firstVolume), null);
+ }
+
+ public Archive(File firstVolume, UnrarCallback unrarCallback)
+ throws RarException, IOException {
+ this(new FileVolumeManager(firstVolume), unrarCallback);
+ }
+
+ // public File getFile() {
+ // return file;
+ // }
+ //
+ // void setFile(File file) throws IOException {
+ // this.file = file;
+ // setFile(new ReadOnlyAccessFile(file), file.length());
+ // }
+
+ private void setFile(IReadOnlyAccess file, long length) throws IOException {
+ totalPackedSize = 0L;
+ totalPackedRead = 0L;
+ close();
+ rof = file;
+ try {
+ readHeaders(length);
+ } catch (Exception e) {
+ logger.log(Level.WARNING,
+ "exception in archive constructor maybe file is encrypted "
+ + "or currupt", e);
+ // ignore exceptions to allow exraction of working files in
+ // corrupt archive
+ }
+ // Calculate size of packed data
+ for (BaseBlock block : headers) {
+ if (block.getHeaderType() == UnrarHeadertype.FileHeader) {
+ totalPackedSize += ((FileHeader) block).getFullPackSize();
+ }
+ }
+ if (unrarCallback != null) {
+ unrarCallback.volumeProgressChanged(totalPackedRead,
+ totalPackedSize);
+ }
+ }
+
+ public void bytesReadRead(int count) {
+ if (count > 0) {
+ totalPackedRead += count;
+ if (unrarCallback != null) {
+ unrarCallback.volumeProgressChanged(totalPackedRead,
+ totalPackedSize);
+ }
+ }
+ }
+
+ public IReadOnlyAccess getRof() {
+ return rof;
+ }
+
+ /**
+ * @return returns all file headers of the archive
+ */
+ public List<FileHeader> getFileHeaders() {
+ List<FileHeader> list = new ArrayList<FileHeader>();
+ for (BaseBlock block : headers) {
+ if (block.getHeaderType().equals(UnrarHeadertype.FileHeader)) {
+ list.add((FileHeader) block);
+ }
+ }
+ return list;
+ }
+
+ public FileHeader nextFileHeader() {
+ int n = headers.size();
+ while (currentHeaderIndex < n) {
+ BaseBlock block = headers.get(currentHeaderIndex++);
+ if (block.getHeaderType() == UnrarHeadertype.FileHeader) {
+ return (FileHeader) block;
+ }
+ }
+ return null;
+ }
+
+ public UnrarCallback getUnrarCallback() {
+ return unrarCallback;
+ }
+
+ /**
+ *
+ * @return whether the archive is encrypted
+ */
+ public boolean isEncrypted() {
+ if (newMhd != null) {
+ return newMhd.isEncrypted();
+ } else {
+ throw new NullPointerException("mainheader is null");
+ }
+ }
+
+ /**
+ * Read the headers of the archive
+ *
+ * @param fileLength
+ * Length of file.
+ * @throws RarException
+ */
+ private void readHeaders(long fileLength) throws IOException, RarException {
+ markHead = null;
+ newMhd = null;
+ headers.clear();
+ currentHeaderIndex = 0;
+ int toRead = 0;
+
+ while (true) {
+ int size = 0;
+ long newpos = 0;
+ byte[] baseBlockBuffer = new byte[BaseBlock.BaseBlockSize];
+
+ long position = rof.getPosition();
+
+ // Weird, but is trying to read beyond the end of the file
+ if (position >= fileLength) {
+ break;
+ }
+
+ // logger.info("\n--------reading header--------");
+ size = rof.readFully(baseBlockBuffer, BaseBlock.BaseBlockSize);
+ if (size == 0) {
+ break;
+ }
+ BaseBlock block = new BaseBlock(baseBlockBuffer);
+
+ block.setPositionInFile(position);
+
+ switch (block.getHeaderType()) {
+
+ case MarkHeader:
+ markHead = new MarkHeader(block);
+ if (!markHead.isSignature()) {
+ throw new RarException(
+ RarException.RarExceptionType.badRarArchive);
+ }
+ headers.add(markHead);
+ // markHead.print();
+ break;
+
+ case MainHeader:
+ toRead = block.hasEncryptVersion() ? MainHeader.mainHeaderSizeWithEnc
+ : MainHeader.mainHeaderSize;
+ byte[] mainbuff = new byte[toRead];
+ rof.readFully(mainbuff, toRead);
+ MainHeader mainhead = new MainHeader(block, mainbuff);
+ headers.add(mainhead);
+ this.newMhd = mainhead;
+ if (newMhd.isEncrypted()) {
+ throw new RarException(
+ RarExceptionType.rarEncryptedException);
+ }
+ // mainhead.print();
+ break;
+
+ case SignHeader:
+ toRead = SignHeader.signHeaderSize;
+ byte[] signBuff = new byte[toRead];
+ rof.readFully(signBuff, toRead);
+ SignHeader signHead = new SignHeader(block, signBuff);
+ headers.add(signHead);
+ // logger.info("HeaderType: SignHeader");
+
+ break;
+
+ case AvHeader:
+ toRead = AVHeader.avHeaderSize;
+ byte[] avBuff = new byte[toRead];
+ rof.readFully(avBuff, toRead);
+ AVHeader avHead = new AVHeader(block, avBuff);
+ headers.add(avHead);
+ // logger.info("headertype: AVHeader");
+ break;
+
+ case CommHeader:
+ toRead = CommentHeader.commentHeaderSize;
+ byte[] commBuff = new byte[toRead];
+ rof.readFully(commBuff, toRead);
+ CommentHeader commHead = new CommentHeader(block, commBuff);
+ headers.add(commHead);
+ // logger.info("method: "+commHead.getUnpMethod()+"; 0x"+
+ // Integer.toHexString(commHead.getUnpMethod()));
+ newpos = commHead.getPositionInFile()
+ + commHead.getHeaderSize();
+ rof.setPosition(newpos);
+
+ break;
+ case EndArcHeader:
+
+ toRead = 0;
+ if (block.hasArchiveDataCRC()) {
+ toRead += EndArcHeader.endArcArchiveDataCrcSize;
+ }
+ if (block.hasVolumeNumber()) {
+ toRead += EndArcHeader.endArcVolumeNumberSize;
+ }
+ EndArcHeader endArcHead;
+ if (toRead > 0) {
+ byte[] endArchBuff = new byte[toRead];
+ rof.readFully(endArchBuff, toRead);
+ endArcHead = new EndArcHeader(block, endArchBuff);
+ // logger.info("HeaderType: endarch\ndatacrc:"+
+ // endArcHead.getArchiveDataCRC());
+ } else {
+ // logger.info("HeaderType: endarch - no Data");
+ endArcHead = new EndArcHeader(block, null);
+ }
+ headers.add(endArcHead);
+ // logger.info("\n--------end header--------");
+ return;
+
+ default:
+ byte[] blockHeaderBuffer = new byte[BlockHeader.blockHeaderSize];
+ rof.readFully(blockHeaderBuffer, BlockHeader.blockHeaderSize);
+ BlockHeader blockHead = new BlockHeader(block,
+ blockHeaderBuffer);
+
+ switch (blockHead.getHeaderType()) {
+ case NewSubHeader:
+ case FileHeader:
+ toRead = blockHead.getHeaderSize()
+ - BlockHeader.BaseBlockSize
+ - BlockHeader.blockHeaderSize;
+ byte[] fileHeaderBuffer = new byte[toRead];
+ rof.readFully(fileHeaderBuffer, toRead);
+
+ FileHeader fh = new FileHeader(blockHead, fileHeaderBuffer);
+ headers.add(fh);
+ newpos = fh.getPositionInFile() + fh.getHeaderSize()
+ + fh.getFullPackSize();
+ rof.setPosition(newpos);
+ break;
+
+ case ProtectHeader:
+ toRead = blockHead.getHeaderSize()
+ - BlockHeader.BaseBlockSize
+ - BlockHeader.blockHeaderSize;
+ byte[] protectHeaderBuffer = new byte[toRead];
+ rof.readFully(protectHeaderBuffer, toRead);
+ ProtectHeader ph = new ProtectHeader(blockHead,
+ protectHeaderBuffer);
+
+ newpos = ph.getPositionInFile() + ph.getHeaderSize()
+ + ph.getDataSize();
+ rof.setPosition(newpos);
+ break;
+
+ case SubHeader: {
+ byte[] subHeadbuffer = new byte[SubBlockHeader.SubBlockHeaderSize];
+ rof.readFully(subHeadbuffer,
+ SubBlockHeader.SubBlockHeaderSize);
+ SubBlockHeader subHead = new SubBlockHeader(blockHead,
+ subHeadbuffer);
+ subHead.print();
+ switch (subHead.getSubType()) {
+ case MAC_HEAD: {
+ byte[] macHeaderbuffer = new byte[MacInfoHeader.MacInfoHeaderSize];
+ rof.readFully(macHeaderbuffer,
+ MacInfoHeader.MacInfoHeaderSize);
+ MacInfoHeader macHeader = new MacInfoHeader(subHead,
+ macHeaderbuffer);
+ macHeader.print();
+ headers.add(macHeader);
+
+ break;
+ }
+ // TODO implement other subheaders
+ case BEEA_HEAD:
+ break;
+ case EA_HEAD: {
+ byte[] eaHeaderBuffer = new byte[EAHeader.EAHeaderSize];
+ rof.readFully(eaHeaderBuffer, EAHeader.EAHeaderSize);
+ EAHeader eaHeader = new EAHeader(subHead,
+ eaHeaderBuffer);
+ eaHeader.print();
+ headers.add(eaHeader);
+
+ break;
+ }
+ case NTACL_HEAD:
+ break;
+ case STREAM_HEAD:
+ break;
+ case UO_HEAD:
+ toRead = subHead.getHeaderSize();
+ toRead -= BaseBlock.BaseBlockSize;
+ toRead -= BlockHeader.blockHeaderSize;
+ toRead -= SubBlockHeader.SubBlockHeaderSize;
+ byte[] uoHeaderBuffer = new byte[toRead];
+ rof.readFully(uoHeaderBuffer, toRead);
+ UnixOwnersHeader uoHeader = new UnixOwnersHeader(
+ subHead, uoHeaderBuffer);
+ uoHeader.print();
+ headers.add(uoHeader);
+ break;
+ default:
+ break;
+ }
+
+ break;
+ }
+ default:
+ logger.warning("Unknown Header");
+ throw new RarException(RarExceptionType.notRarArchive);
+
+ }
+ }
+ // logger.info("\n--------end header--------");
+ }
+ }
+
+ /**
+ * Extract the file specified by the given header and write it to the
+ * supplied output stream
+ *
+ * @param header
+ * the header to be extracted
+ * @param os
+ * the outputstream
+ * @throws RarException
+ */
+ public void extractFile(FileHeader hd, OutputStream os) throws RarException {
+ if (!headers.contains(hd)) {
+ throw new RarException(RarExceptionType.headerNotInArchive);
+ }
+ try {
+ doExtractFile(hd, os);
+ } catch (Exception e) {
+ if (e instanceof RarException) {
+ throw (RarException) e;
+ } else {
+ throw new RarException(e);
+ }
+ }
+ }
+
+ /**
+ * Returns an {@link InputStream} that will allow to read the file and
+ * stream it. Please note that this method will create a new Thread and an a
+ * pair of Pipe streams.
+ *
+ * @param header
+ * the header to be extracted
+ * @throws RarException
+ * @throws IOException
+ * if any IO error occur
+ */
+ public InputStream getInputStream(final FileHeader hd) throws RarException,
+ IOException {
+ final PipedInputStream in = new PipedInputStream(32 * 1024);
+ final PipedOutputStream out = new PipedOutputStream(in);
+
+ // creates a new thread that will write data to the pipe. Data will be
+ // available in another InputStream, connected to the OutputStream.
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ extractFile(hd, out);
+ } catch (RarException e) {
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }).start();
+
+ return in;
+ }
+
+ private void doExtractFile(FileHeader hd, OutputStream os)
+ throws RarException, IOException {
+ dataIO.init(os);
+ dataIO.init(hd);
+ dataIO.setUnpFileCRC(this.isOldFormat() ? 0 : 0xffFFffFF);
+ if (unpack == null) {
+ unpack = new Unpack(dataIO);
+ }
+ if (!hd.isSolid()) {
+ unpack.init(null);
+ }
+ unpack.setDestSize(hd.getFullUnpackSize());
+ try {
+ unpack.doUnpack(hd.getUnpVersion(), hd.isSolid());
+ // Verify file CRC
+ hd = dataIO.getSubHeader();
+ long actualCRC = hd.isSplitAfter() ? ~dataIO.getPackedCRC()
+ : ~dataIO.getUnpFileCRC();
+ int expectedCRC = hd.getFileCRC();
+ if (actualCRC != expectedCRC) {
+ throw new RarException(RarExceptionType.crcError);
+ }
+ // if (!hd.isSplitAfter()) {
+ // // Verify file CRC
+ // if(~dataIO.getUnpFileCRC() != hd.getFileCRC()){
+ // throw new RarException(RarExceptionType.crcError);
+ // }
+ // }
+ } catch (Exception e) {
+ unpack.cleanUp();
+ if (e instanceof RarException) {
+ // throw new RarException((RarException)e);
+ throw (RarException) e;
+ } else {
+ throw new RarException(e);
+ }
+ }
+ }
+
+ /**
+ * @return returns the main header of this archive
+ */
+ public MainHeader getMainHeader() {
+ return newMhd;
+ }
+
+ /**
+ * @return whether the archive is old format
+ */
+ public boolean isOldFormat() {
+ return markHead.isOldFormat();
+ }
+
+ /** Close the underlying compressed file. */
+ public void close() throws IOException {
+ if (rof != null) {
+ rof.close();
+ rof = null;
+ }
+ if (unpack != null) {
+ unpack.cleanUp();
+ }
+ }
+
+ /**
+ * @return the volumeManager
+ */
+ public VolumeManager getVolumeManager() {
+ return volumeManager;
+ }
+
+ /**
+ * @param volumeManager
+ * the volumeManager to set
+ */
+ public void setVolumeManager(VolumeManager volumeManager) {
+ this.volumeManager = volumeManager;
+ }
+
+ /**
+ * @return the volume
+ */
+ public Volume getVolume() {
+ return volume;
+ }
+
+ /**
+ * @param volume
+ * the volume to set
+ * @throws IOException
+ */
+ public void setVolume(Volume volume) throws IOException {
+ this.volume = volume;
+ setFile(volume.getReadOnlyAccess(), volume.getLength());
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/MVTest.java b/org.fox.ttcomics/src/main/java/com/github/junrar/MVTest.java
new file mode 100644
index 0000000..36c33c8
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/MVTest.java
@@ -0,0 +1,56 @@
+package com.github.junrar;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import com.github.junrar.exception.RarException;
+import com.github.junrar.impl.FileVolumeManager;
+import com.github.junrar.rarfile.FileHeader;
+
+
+public class MVTest {
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ String filename = "/home/rogiel/fs/home/ae721273-eade-45e7-8112-d14115ebae56/Village People - Y.M.C.A.mp3.part1.rar";
+ File f = new File(filename);
+ Archive a = null;
+ try {
+ a = new Archive(new FileVolumeManager(f));
+ } catch (RarException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ if (a != null) {
+ a.getMainHeader().print();
+ FileHeader fh = a.nextFileHeader();
+ while (fh != null) {
+ try {
+ File out = new File("/home/rogiel/fs/test/"
+ + fh.getFileNameString().trim());
+ System.out.println(out.getAbsolutePath());
+ FileOutputStream os = new FileOutputStream(out);
+ a.extractFile(fh, os);
+ os.close();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (RarException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ fh = a.nextFileHeader();
+ }
+ }
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/UnrarCallback.java b/org.fox.ttcomics/src/main/java/com/github/junrar/UnrarCallback.java
new file mode 100644
index 0000000..06bc088
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/UnrarCallback.java
@@ -0,0 +1,21 @@
+package com.github.junrar;
+
+
+/**
+ *
+ * @author alban
+ */
+public interface UnrarCallback {
+
+ /**
+ * Return <tt>true</tt> if the next volume is ready to be processed,
+ * <tt>false</tt> otherwise.
+ */
+ boolean isNextVolumeReady(Volume nextVolume);
+
+ /**
+ * This method is invoked each time the progress of the current
+ * volume changes.
+ */
+ void volumeProgressChanged(long current, long total);
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/Volume.java b/org.fox.ttcomics/src/main/java/com/github/junrar/Volume.java
new file mode 100644
index 0000000..edc17ae
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/Volume.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of seedbox <github.com/seedbox>.
+ *
+ * seedbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * seedbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with seedbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.github.junrar;
+
+import java.io.IOException;
+
+import com.github.junrar.io.IReadOnlyAccess;
+
+
+/**
+ * @author <a href="http://www.rogiel.com">Rogiel</a>
+ *
+ */
+public interface Volume {
+ /**
+ * @return the access
+ * @throws IOException
+ */
+ IReadOnlyAccess getReadOnlyAccess() throws IOException;
+
+ /**
+ * @return the data length
+ */
+ long getLength();
+
+ /**
+ * @return the archive this volume belongs to
+ */
+ Archive getArchive();
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/VolumeManager.java b/org.fox.ttcomics/src/main/java/com/github/junrar/VolumeManager.java
new file mode 100644
index 0000000..3e96530
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/VolumeManager.java
@@ -0,0 +1,28 @@
+/*
+ * This file is part of seedbox <github.com/seedbox>.
+ *
+ * seedbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * seedbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with seedbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.github.junrar;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="http://www.rogiel.com">Rogiel</a>
+ *
+ */
+public interface VolumeManager {
+ public Volume nextArchive(Archive archive, Volume lastVolume)
+ throws IOException;
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/crc/RarCRC.java b/org.fox.ttcomics/src/main/java/com/github/junrar/crc/RarCRC.java
new file mode 100644
index 0000000..8a778ed
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/crc/RarCRC.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 29.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.crc;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class RarCRC {
+
+ private final static int crcTab[];
+ static {
+ crcTab = new int[256];
+ for (int i = 0; i < 256; i++) {
+ int c = i;
+ for (int j = 0; j < 8; j++){
+ if ((c & 1) !=0) {
+ c >>>= 1;
+ c ^= 0xEDB88320;
+ }
+ else{
+ c >>>= 1;
+ }
+ }
+ crcTab[i] = c;
+ }
+ }
+
+ private RarCRC() {
+ }
+
+ public static int checkCrc(int startCrc, byte[] data, int offset,
+ int count) {
+ int size = Math.min(data.length-offset,count);
+ // #if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) &&
+ // defined(ALLOW_NOT_ALIGNED_INT)
+ /*
+ for (int i = 0; (0 < size) && i < data.length - 8
+ && ((data[i + 8] & 7) != 0); i++) {
+ startCrc = crcTab[(short) (startCrc ^ data[i]) & 0x00FF] ^ (startCrc >>> 8);
+ size--;
+ }
+
+ for (int i = 0; size >= 8; i += 8) {
+ startCrc ^= data[i + 0] << 24;
+ startCrc ^= data[i + 1] << 16;
+ startCrc ^= data[i + 2] << 8;
+ startCrc ^= data[i + 3];
+
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+
+ startCrc ^= data[i + 4] << 24;
+ startCrc ^= data[i + 5] << 16;
+ startCrc ^= data[i + 6] << 8;
+ startCrc ^= data[i + 7];
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+ startCrc = crcTab[(short) startCrc & 0x00FF] ^ (startCrc >>> 8);
+ size -= 8;
+ }*/
+
+ for (int i = 0; i < size; i++)
+ {
+/*
+ // (byte)(StartCRC^Data[I])
+ int pos = 0; // pos=0x00000000
+ pos |= startCrc; // pos=ffffffff
+
+ pos ^= data[i]; // data[0]=0x73=115dec --> pos=140
+ System.out.println(Integer.toHexString(pos));
+
+ // Only last 8 bit because CRCtab has length 256
+ pos = pos & 0x000000FF;
+ System.out.println("pos:"+pos);
+ //startCrc >>>= 8;
+
+
+ //StartCRC>>8
+ int temp =0;
+ temp|=startCrc;
+ temp >>>= 8;
+ System.out.println("temp:"+Integer.toHexString(temp));
+
+
+ startCrc = (crcTab[pos]^temp);
+ System.out.println("--"+Integer.toHexString(startCrc));*/
+
+ startCrc=(crcTab[((int)((int)startCrc ^
+ (int)data[offset+i]))&0xff]^(startCrc>>>8));
+
+ //System.out.println(Integer.toHexString(startCrc));
+
+ // Original code:
+ //StartCRC=CRCTab[(byte)(StartCRC^Data[I])]^(StartCRC>>8);
+ }
+ return (startCrc);
+ }
+
+ public static short checkOldCrc(short startCrc, byte[] data, int count) {
+ int n = Math.min(data.length, count);
+ for (int i = 0; i < n; i++) {
+ startCrc = (short) ((short) (startCrc + (short) (data[i]&0x00ff)) & -1);
+ startCrc = (short) (((startCrc << 1) | (startCrc >>> 15)) & -1);
+ }
+ return (startCrc);
+ }
+
+// public static void main(String[] args)
+// {
+// RarCRC rc = new RarCRC();
+// //byte[] data = { 0x72, 0x21, 0x1A, 0x07, 0x00};
+//
+// byte[] data = {0x73 ,0x00 ,0x00 ,0x0D ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00};
+//
+// int crc = 0x90CF;
+//
+//
+// int result = rc.checkCrc(0xFFFFffff, data,0,data.length);
+// System.out.println("3: "+Integer.toHexString(~result&0xffff));
+//
+// }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/crypt/Rijndael.java b/org.fox.ttcomics/src/main/java/com/github/junrar/crypt/Rijndael.java
new file mode 100644
index 0000000..a1cf7c7
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/crypt/Rijndael.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.crypt;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class Rijndael {
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/exception/RarException.java b/org.fox.ttcomics/src/main/java/com/github/junrar/exception/RarException.java
new file mode 100644
index 0000000..a0b969f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/exception/RarException.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 30.07.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.exception;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class RarException extends Exception
+{
+ private static final long serialVersionUID = 1L;
+ private RarExceptionType type;
+
+ public RarException(Exception e){
+ super(RarExceptionType.unkownError.name(),e);
+ this.type = RarExceptionType.unkownError;
+ }
+
+ public RarException(RarException e)
+ {
+
+ super(e.getMessage(),e);
+ this.type = e.getType();
+ }
+
+ public RarException(RarExceptionType type){
+ super(type.name());
+ this.type = type;
+ }
+
+
+
+ public enum RarExceptionType{
+ notImplementedYet,
+ crcError,
+ notRarArchive,
+ badRarArchive,
+ unkownError,
+ headerNotInArchive,
+ wrongHeaderType,
+ ioError,
+ rarEncryptedException ;
+ }
+
+
+
+ public RarExceptionType getType()
+ {
+ return type;
+ }
+
+ public void setType(RarExceptionType type)
+ {
+ this.type = type;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolume.java b/org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolume.java
new file mode 100644
index 0000000..eba4da9
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolume.java
@@ -0,0 +1,65 @@
+/*
+ * This file is part of seedbox <github.com/seedbox>.
+ *
+ * seedbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * seedbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with seedbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.github.junrar.impl;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.github.junrar.Archive;
+import com.github.junrar.Volume;
+import com.github.junrar.io.IReadOnlyAccess;
+import com.github.junrar.io.ReadOnlyAccessFile;
+
+
+/**
+ * @author <a href="http://www.rogiel.com">Rogiel</a>
+ *
+ */
+public class FileVolume implements Volume {
+ private final Archive archive;
+ private final File file;
+
+ /**
+ * @param file
+ */
+ public FileVolume(Archive archive, File file) {
+ this.archive = archive;
+ this.file = file;
+ }
+
+ @Override
+ public IReadOnlyAccess getReadOnlyAccess() throws IOException {
+ return new ReadOnlyAccessFile(file);
+ }
+
+ @Override
+ public long getLength() {
+ return file.length();
+ }
+
+ @Override
+ public Archive getArchive() {
+ return archive;
+ }
+
+ /**
+ * @return the file
+ */
+ public File getFile() {
+ return file;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolumeManager.java b/org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolumeManager.java
new file mode 100644
index 0000000..3552248
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/impl/FileVolumeManager.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of seedbox <github.com/seedbox>.
+ *
+ * seedbox is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * seedbox is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with seedbox. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.github.junrar.impl;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.github.junrar.Archive;
+import com.github.junrar.Volume;
+import com.github.junrar.VolumeManager;
+import com.github.junrar.util.VolumeHelper;
+
+
+/**
+ * @author <a href="http://www.rogiel.com">Rogiel</a>
+ *
+ */
+public class FileVolumeManager implements VolumeManager {
+ private final File firstVolume;
+
+ public FileVolumeManager(File firstVolume) {
+ this.firstVolume = firstVolume;
+ }
+
+ @Override
+ public Volume nextArchive(Archive archive, Volume last)
+ throws IOException {
+ if (last == null)
+ return new FileVolume(archive, firstVolume);
+
+ FileVolume lastFileVolume = (FileVolume) last;
+ boolean oldNumbering = !archive.getMainHeader().isNewNumbering()
+ || archive.isOldFormat();
+ String nextName = VolumeHelper.nextVolumeName(lastFileVolume.getFile()
+ .getAbsolutePath(), oldNumbering);
+ File nextVolume = new File(nextName);
+
+ return new FileVolume(archive, nextVolume);
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/io/IReadOnlyAccess.java b/org.fox.ttcomics/src/main/java/com/github/junrar/io/IReadOnlyAccess.java
new file mode 100644
index 0000000..e0cde6e
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/io/IReadOnlyAccess.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 23.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.io;
+
+import java.io.IOException;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public interface IReadOnlyAccess {
+
+ /**
+ * @return the current position in the file
+ */
+ public long getPosition() throws IOException;
+
+ /**
+ * @param pos the position in the file
+ * @return success ? true : false
+ */
+ public void setPosition(long pos) throws IOException;
+
+ /** Read a single byte of data. */
+ public int read() throws IOException;
+
+ /**
+ * Read up to <tt>count</tt> bytes to the specified buffer.
+ */
+ public int read(byte[] buffer, int off, int count) throws IOException;
+
+ /**
+ * Read exactly <tt>count</tt> bytes to the specified buffer.
+ *
+ * @param buffer where to store the read data
+ * @param count how many bytes to read
+ * @return bytes read || -1 if IO problem
+ */
+ public int readFully(byte[] buffer, int count) throws IOException;
+
+ /** Close this file. */
+ public void close() throws IOException;
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/io/InputStreamReadOnlyAccessFile.java b/org.fox.ttcomics/src/main/java/com/github/junrar/io/InputStreamReadOnlyAccessFile.java
new file mode 100644
index 0000000..3c7eedd
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/io/InputStreamReadOnlyAccessFile.java
@@ -0,0 +1,59 @@
+package com.github.junrar.io;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+
+
+/**
+ * InputStream based implementation of the <code>IReadOnlyAccess</code> interface.
+ *
+ * @see http://rsbweb.nih.gov/ij/
+ * @author martinr
+ */
+public class InputStreamReadOnlyAccessFile implements IReadOnlyAccess {
+ private RandomAccessStream is;
+
+ /**
+ * Create new instance.
+ *
+ * @param is The input stream to wrap.
+ */
+ public InputStreamReadOnlyAccessFile(final InputStream is) {
+ this.is = new RandomAccessStream(new BufferedInputStream(is));
+ }
+
+ @Override
+ public long getPosition() throws IOException {
+ return is.getLongFilePointer();
+ }
+
+ @Override
+ public void setPosition(long pos) throws IOException {
+ is.seek(pos);
+ }
+
+ @Override
+ public int read() throws IOException {
+ return is.read();
+ }
+
+ @Override
+ public int read(byte[] buffer, int off, int count) throws IOException {
+ return is.read(buffer, off, count);
+ }
+
+ @Override
+ public int readFully(byte[] buffer, int count) throws IOException {
+ is.readFully(buffer, count);
+ return count;
+ }
+
+ @Override
+ public void close() throws IOException {
+ is.close();
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/io/RandomAccessStream.java b/org.fox.ttcomics/src/main/java/com/github/junrar/io/RandomAccessStream.java
new file mode 100644
index 0000000..49442bf
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/io/RandomAccessStream.java
@@ -0,0 +1,203 @@
+/*
+ * public domain as of http://rsbweb.nih.gov/ij/disclaimer.html
+ */
+package com.github.junrar.io;
+
+import java.io.*;
+import java.util.Vector;
+
+/**
+ * This is a class that uses a memory cache to allow seeking within an
+ * InputStream. Based on the JAI MemoryCacheSeekableStream class. Can also be
+ * constructed from a RandomAccessFile, which uses less memory since the memory
+ * cache is not required.
+ */
+@SuppressWarnings("rawtypes")
+public final class RandomAccessStream extends InputStream {
+
+ private static final int BLOCK_SIZE = 512;
+ private static final int BLOCK_MASK = 511;
+ private static final int BLOCK_SHIFT = 9;
+
+ private InputStream src;
+ private RandomAccessFile ras;
+ private long pointer;
+ private Vector data;
+ private int length;
+ private boolean foundEOS;
+
+ /**
+ * Constructs a RandomAccessStream from an InputStream. Seeking backwards is
+ * supported using a memory cache.
+ */
+ public RandomAccessStream(InputStream inputstream) {
+ pointer = 0L;
+ data = new Vector();
+ length = 0;
+ foundEOS = false;
+ src = inputstream;
+ }
+
+ /** Constructs a RandomAccessStream from an RandomAccessFile. */
+ public RandomAccessStream(RandomAccessFile ras) {
+ this.ras = ras;
+ }
+
+ public int getFilePointer() throws IOException {
+ if (ras != null)
+ return (int) ras.getFilePointer();
+ else
+ return (int) pointer;
+ }
+
+ public long getLongFilePointer() throws IOException {
+ if (ras != null)
+ return ras.getFilePointer();
+ else
+ return pointer;
+ }
+
+ public int read() throws IOException {
+ if (ras != null)
+ return ras.read();
+ long l = pointer + 1L;
+ long l1 = readUntil(l);
+ if (l1 >= l) {
+ byte abyte0[] = (byte[]) data
+ .elementAt((int) (pointer >> BLOCK_SHIFT));
+ return abyte0[(int) (pointer++ & BLOCK_MASK)] & 0xff;
+ } else
+ return -1;
+ }
+
+ public int read(byte[] bytes, int off, int len) throws IOException {
+ if (bytes == null)
+ throw new NullPointerException();
+ if (ras != null)
+ return ras.read(bytes, off, len);
+ if (off < 0 || len < 0 || off + len > bytes.length)
+ throw new IndexOutOfBoundsException();
+ if (len == 0)
+ return 0;
+ long l = readUntil(pointer + len);
+ if (l <= pointer)
+ return -1;
+ else {
+ byte abyte1[] = (byte[]) data
+ .elementAt((int) (pointer >> BLOCK_SHIFT));
+ int k = Math.min(len, BLOCK_SIZE - (int) (pointer & BLOCK_MASK));
+ System.arraycopy(abyte1, (int) (pointer & BLOCK_MASK), bytes, off,
+ k);
+ pointer += k;
+ return k;
+ }
+ }
+
+ public final void readFully(byte[] bytes) throws IOException {
+ readFully(bytes, bytes.length);
+ }
+
+ public final void readFully(byte[] bytes, int len) throws IOException {
+ int read = 0;
+ do {
+ int l = read(bytes, read, len - read);
+ if (l < 0)
+ break;
+ read += l;
+ } while (read < len);
+ }
+
+ @SuppressWarnings("unchecked")
+ private long readUntil(long l) throws IOException {
+ if (l < length)
+ return l;
+ if (foundEOS)
+ return length;
+ int i = (int) (l >> BLOCK_SHIFT);
+ int j = length >> BLOCK_SHIFT;
+ for (int k = j; k <= i; k++) {
+ byte abyte0[] = new byte[BLOCK_SIZE];
+ data.addElement(abyte0);
+ int i1 = BLOCK_SIZE;
+ int j1 = 0;
+ while (i1 > 0) {
+ int k1 = src.read(abyte0, j1, i1);
+ if (k1 == -1) {
+ foundEOS = true;
+ return length;
+ }
+ j1 += k1;
+ i1 -= k1;
+ length += k1;
+ }
+
+ }
+
+ return length;
+ }
+
+ public void seek(long loc) throws IOException {
+ if (ras != null) {
+ ras.seek(loc);
+ return;
+ }
+ if (loc < 0L)
+ pointer = 0L;
+ else
+ pointer = loc;
+ }
+
+ public void seek(int loc) throws IOException {
+ long lloc = ((long) loc) & 0xffffffffL;
+ if (ras != null) {
+ ras.seek(lloc);
+ return;
+ }
+ if (lloc < 0L)
+ pointer = 0L;
+ else
+ pointer = lloc;
+ }
+
+ public final int readInt() throws IOException {
+ int i = read();
+ int j = read();
+ int k = read();
+ int l = read();
+ if ((i | j | k | l) < 0)
+ throw new EOFException();
+ else
+ return (i << 24) + (j << 16) + (k << 8) + l;
+ }
+
+ public final long readLong() throws IOException {
+ return ((long) readInt() << 32) + ((long) readInt() & 0xffffffffL);
+ }
+
+ public final double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ public final short readShort() throws IOException {
+ int i = read();
+ int j = read();
+ if ((i | j) < 0)
+ throw new EOFException();
+ else
+ return (short) ((i << 8) + j);
+ }
+
+ public final float readFloat() throws IOException {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ public void close() throws IOException {
+ if (ras != null)
+ ras.close();
+ else {
+ data.removeAllElements();
+ src.close();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/io/Raw.java b/org.fox.ttcomics/src/main/java/com/github/junrar/io/Raw.java
new file mode 100644
index 0000000..87affda
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/io/Raw.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 18.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.io;
+
+/**
+ * Read / write numbers to a byte[] regarding the endianness of the array
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class Raw {
+ /**
+ * Read a short value from the byte array at the given position (Big Endian)
+ *
+ * @param array
+ * the array to read from
+ * @param pos
+ * the position
+ * @return the value
+ */
+ public static final short readShortBigEndian(byte[] array, int pos) {
+ short temp = 0;
+ temp |= array[pos] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 1] & 0xff;
+ return temp;
+ }
+
+ /**
+ * Read a int value from the byte array at the given position (Big Endian)
+ *
+ * @param array
+ * the array to read from
+ * @param pos
+ * the offset
+ * @return the value
+ */
+ public static final int readIntBigEndian(byte[] array, int pos) {
+ int temp = 0;
+ temp |= array[pos] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 1] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 2] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 3] & 0xff;
+ return temp;
+ }
+
+ /**
+ * Read a long value from the byte array at the given position (Big Endian)
+ *
+ * @param array
+ * the array to read from
+ * @param pos
+ * the offset
+ * @return the value
+ */
+ public static final long readLongBigEndian(byte[] array, int pos) {
+ int temp = 0;
+ temp |= array[pos] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 1] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 2] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 3] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 4] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 5] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 6] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 7] & 0xff;
+ return temp;
+ }
+
+ /**
+ * Read a short value from the byte array at the given position (little
+ * Endian)
+ *
+ * @param array
+ * the array to read from
+ * @param pos
+ * the offset
+ * @return the value
+ */
+ public static final short readShortLittleEndian(byte[] array, int pos) {
+ short result = 0;
+ result += array[pos + 1] & 0xff;
+ result <<= 8;
+ result += array[pos] & 0xff;
+ return result;
+ }
+
+ /**
+ * Read an int value from the byte array at the given position (little
+ * Endian)
+ *
+ * @param array
+ * the array to read from
+ * @param pos
+ * the offset
+ * @return the value
+ */
+ public static final int readIntLittleEndian(byte[] array, int pos) {
+ return ((array[pos + 3] & 0xff) << 24)
+ | ((array[pos + 2] & 0xff) << 16)
+ | ((array[pos + 1] & 0xff) << 8) | ((array[pos] & 0xff));
+ }
+
+ /**
+ * Read an long value(unsigned int) from the byte array at the given
+ * position (little Endian)
+ *
+ * @param array
+ * @param pos
+ * @return
+ */
+ public static final long readIntLittleEndianAsLong(byte[] array, int pos) {
+ return (((long) array[pos + 3] & 0xff) << 24)
+ | (((long) array[pos + 2] & 0xff) << 16)
+ | (((long) array[pos + 1] & 0xff) << 8)
+ | (((long) array[pos] & 0xff));
+ }
+
+ /**
+ * Read a long value from the byte array at the given position (little
+ * Endian)
+ *
+ * @param array
+ * the array to read from
+ * @param pos
+ * the offset
+ * @return the value
+ */
+ public static final long readLongLittleEndian(byte[] array, int pos) {
+ int temp = 0;
+ temp |= array[pos + 7] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 6] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 5] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 4] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 3] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 2] & 0xff;
+ temp <<= 8;
+ temp |= array[pos + 1] & 0xff;
+ temp <<= 8;
+ temp |= array[pos];
+ return temp;
+ }
+
+ /**
+ * Write a short value into the byte array at the given position (Big
+ * endian)
+ *
+ * @param array
+ * the array
+ * @param pos
+ * the offset
+ * @param value
+ * the value to write
+ */
+ public static final void writeShortBigEndian(byte[] array, int pos,
+ short value) {
+ array[pos] = (byte) (value >>> 8);
+ array[pos + 1] = (byte) (value & 0xFF);
+
+ }
+
+ /**
+ * Write an int value into the byte array at the given position (Big endian)
+ *
+ * @param array
+ * the array
+ * @param pos
+ * the offset
+ * @param value
+ * the value to write
+ */
+ public static final void writeIntBigEndian(byte[] array, int pos, int value) {
+ array[pos] = (byte) ((value >>> 24) & 0xff);
+ array[pos + 1] = (byte) ((value >>> 16) & 0xff);
+ array[pos + 2] = (byte) ((value >>> 8) & 0xff);
+ array[pos + 3] = (byte) ((value) & 0xff);
+
+ }
+
+ /**
+ * Write a long value into the byte array at the given position (Big endian)
+ *
+ * @param array
+ * the array
+ * @param pos
+ * the offset
+ * @param value
+ * the value to write
+ */
+ public static final void writeLongBigEndian(byte[] array, int pos,
+ long value) {
+ array[pos] = (byte) (value >>> 56);
+ array[pos + 1] = (byte) (value >>> 48);
+ array[pos + 2] = (byte) (value >>> 40);
+ array[pos + 3] = (byte) (value >>> 32);
+ array[pos + 4] = (byte) (value >>> 24);
+ array[pos + 5] = (byte) (value >>> 16);
+ array[pos + 6] = (byte) (value >>> 8);
+ array[pos + 7] = (byte) (value & 0xFF);
+
+ }
+
+ /**
+ * Write a short value into the byte array at the given position (little
+ * endian)
+ *
+ * @param array
+ * the array
+ * @param pos
+ * the offset
+ * @param value
+ * the value to write
+ */
+ public static final void writeShortLittleEndian(byte[] array, int pos,
+ short value) {
+ array[pos + 1] = (byte) (value >>> 8);
+ array[pos] = (byte) (value & 0xFF);
+
+ }
+
+ /**
+ * Increment a short value at the specified position by the specified amount
+ * (little endian).
+ */
+ public static final void incShortLittleEndian(byte[] array, int pos, int dv) {
+ int c = ((array[pos] & 0xff) + (dv & 0xff)) >>> 8;
+ array[pos] += dv & 0xff;
+ if ((c > 0) || ((dv & 0xff00) != 0)) {
+ array[pos + 1] += ((dv >>> 8) & 0xff) + c;
+ }
+ }
+
+ /**
+ * Write an int value into the byte array at the given position (little
+ * endian)
+ *
+ * @param array
+ * the array
+ * @param pos
+ * the offset
+ * @param value
+ * the value to write
+ */
+ public static final void writeIntLittleEndian(byte[] array, int pos,
+ int value) {
+ array[pos + 3] = (byte) (value >>> 24);
+ array[pos + 2] = (byte) (value >>> 16);
+ array[pos + 1] = (byte) (value >>> 8);
+ array[pos] = (byte) (value & 0xFF);
+
+ }
+
+ /**
+ * Write a long value into the byte array at the given position (little
+ * endian)
+ *
+ * @param array
+ * the array
+ * @param pos
+ * the offset
+ * @param value
+ * the value to write
+ */
+ public static final void writeLongLittleEndian(byte[] array, int pos,
+ long value) {
+ array[pos + 7] = (byte) (value >>> 56);
+ array[pos + 6] = (byte) (value >>> 48);
+ array[pos + 5] = (byte) (value >>> 40);
+ array[pos + 4] = (byte) (value >>> 32);
+ array[pos + 3] = (byte) (value >>> 24);
+ array[pos + 2] = (byte) (value >>> 16);
+ array[pos + 1] = (byte) (value >>> 8);
+ array[pos] = (byte) (value & 0xFF);
+
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessByteArray.java b/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessByteArray.java
new file mode 100644
index 0000000..bbdb687
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessByteArray.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 30.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * A File like access to a byte array.
+ * (seek and read certain number of bytes)
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class ReadOnlyAccessByteArray implements IReadOnlyAccess{
+
+ private int positionInFile;
+ private byte[] file;
+
+ /**
+ * Initialize with byte[ ]
+ * @param file the file given as byte array
+ */
+ public ReadOnlyAccessByteArray(byte[] file){
+ if(file == null){
+ throw new NullPointerException("file must not be null!!");
+ }
+ this.file = file;
+ this.positionInFile = 0;
+ }
+
+ public long getPosition() throws IOException {
+ return positionInFile;
+ }
+
+ public void setPosition(long pos) throws IOException {
+ if (pos < file.length && pos >= 0){
+ this.positionInFile = (int)pos;
+ }
+ else{
+ throw new EOFException();
+ }
+ }
+
+ /** Read a single byte of data. */
+ public int read() throws IOException {
+ return file[positionInFile++];
+ }
+
+ /**
+ * Read up to <tt>count</tt> bytes to the specified buffer.
+ */
+ public int read(byte[] buffer, int off, int count) throws IOException {
+ int read = Math.min(count, file.length-positionInFile);
+ System.arraycopy(file, positionInFile, buffer, off, read);
+ positionInFile += read;
+ return read;
+ }
+
+ public int readFully(byte[] buffer, int count) throws IOException {
+ if(buffer == null ){
+ throw new NullPointerException("buffer must not be null");
+ }
+ if(count == 0){
+ throw new IllegalArgumentException("cannot read 0 bytes ;-)");
+ }
+ int read = Math.min(count, file.length-(int)positionInFile-1);
+ System.arraycopy(file, (int)positionInFile, buffer, 0, read );
+ positionInFile+=read;
+ return read;
+ }
+
+ public void close() throws IOException {
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessFile.java b/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessFile.java
new file mode 100644
index 0000000..77ad35a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessFile.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 23.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class ReadOnlyAccessFile extends RandomAccessFile
+ implements IReadOnlyAccess{
+
+ /**
+ * @param file the file
+ * @throws FileNotFoundException
+ */
+ public ReadOnlyAccessFile(File file) throws FileNotFoundException {
+ super(file, "r");
+ }
+
+ public int readFully(byte[] buffer, int count) throws IOException {
+ assert (count > 0) : count;
+ this.readFully(buffer, 0, count);
+ return count;
+ }
+
+ public long getPosition() throws IOException {
+ return this.getFilePointer();
+ }
+
+ public void setPosition(long pos) throws IOException {
+ this.seek(pos);
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessInputStream.java b/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessInputStream.java
new file mode 100644
index 0000000..80c022e
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/io/ReadOnlyAccessInputStream.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 26.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression
+ * algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class ReadOnlyAccessInputStream extends InputStream {
+ private IReadOnlyAccess file;
+
+ private long curPos;
+ private final long startPos;
+ private final long endPos;
+
+ public ReadOnlyAccessInputStream(IReadOnlyAccess file, long startPos,
+ long endPos) throws IOException {
+ super();
+ this.file = file;
+ this.startPos = startPos;
+ curPos = startPos;
+ this.endPos = endPos;
+ file.setPosition(curPos);
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (curPos == endPos) {
+ return -1;
+ }
+ else {
+ int b = file.read();
+ curPos++;
+ return b;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (len == 0) {
+ return 0;
+ }
+ if (curPos == endPos) {
+ return -1;
+ }
+ int bytesRead = file.read(b, off,
+ (int)Math.min(len, endPos - curPos));
+ curPos += bytesRead;
+ return bytesRead;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+//
+// public void close() throws IOException {
+// file.close();
+// }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/AVHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/AVHeader.java
new file mode 100644
index 0000000..66e9dbb
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/AVHeader.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 24.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+/**
+ * extended version info header
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class AVHeader extends BaseBlock {
+
+ public static final int avHeaderSize = 7;
+
+ private byte unpackVersion;
+ private byte method;
+ private byte avVersion;
+ private int avInfoCRC;
+
+ public AVHeader(BaseBlock bb, byte[] avHeader){
+ super(bb);
+
+ int pos =0;
+ unpackVersion |= avHeader[pos]&0xff;
+ pos++;
+ method |= avHeader[pos]&0xff;
+ pos++;
+ avVersion |= avHeader[pos]&0xff;
+ pos++;
+ avInfoCRC = Raw.readIntLittleEndian(avHeader, pos);
+ }
+
+ public int getAvInfoCRC() {
+ return avInfoCRC;
+ }
+
+ public byte getAvVersion() {
+ return avVersion;
+ }
+
+ public byte getMethod() {
+ return method;
+ }
+
+ public byte getUnpackVersion() {
+ return unpackVersion;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BaseBlock.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BaseBlock.java
new file mode 100644
index 0000000..68c2d94
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BaseBlock.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 22.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+
+/**
+ * Base class of all rar headers
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class BaseBlock{
+
+ //Log //logger = LogFactory.getLog(BaseBlock.class.getName());
+
+ public static final short BaseBlockSize = 7;
+
+ //TODO move somewhere else
+
+ public static final short MHD_VOLUME = 0x0001;
+ public static final short MHD_COMMENT = 0x0002;
+ public static final short MHD_LOCK = 0x0004;
+ public static final short MHD_SOLID = 0x0008;
+ public static final short MHD_PACK_COMMENT = 0x0010;
+ public static final short MHD_NEWNUMBERING = 0x0010;
+ public static final short MHD_AV = 0x0020;
+ public static final short MHD_PROTECT = 0x0040;
+ public static final short MHD_PASSWORD = 0x0080;
+ public static final short MHD_FIRSTVOLUME = 0x0100;
+ public static final short MHD_ENCRYPTVER = 0x0200;
+
+
+ public static final short LHD_SPLIT_BEFORE = 0x0001;
+ public static final short LHD_SPLIT_AFTER = 0x0002;
+ public static final short LHD_PASSWORD = 0x0004;
+ public static final short LHD_COMMENT = 0x0008;
+ public static final short LHD_SOLID = 0x0010;
+
+ public static final short LHD_WINDOWMASK = 0x00e0;
+ public static final short LHD_WINDOW64 = 0x0000;
+ public static final short LHD_WINDOW128 = 0x0020;
+ public static final short LHD_WINDOW256 = 0x0040;
+ public static final short LHD_WINDOW512 = 0x0060;
+ public static final short LHD_WINDOW1024 = 0x0080;
+ public static final short LHD_WINDOW2048 = 0x00a0;
+ public static final short LHD_WINDOW4096 = 0x00c0;
+ public static final short LHD_DIRECTORY = 0x00e0;
+
+ public static final short LHD_LARGE = 0x0100;
+ public static final short LHD_UNICODE = 0x0200;
+ public static final short LHD_SALT = 0x0400;
+ public static final short LHD_VERSION = 0x0800;
+ public static final short LHD_EXTTIME = 0x1000;
+ public static final short LHD_EXTFLAGS = 0x2000;
+
+ public static final short SKIP_IF_UNKNOWN = 0x4000;
+ public static final short LONG_BLOCK = -0x8000;
+
+ public static final short EARC_NEXT_VOLUME = 0x0001;
+ public static final short EARC_DATACRC = 0x0002;
+ public static final short EARC_REVSPACE = 0x0004;
+ public static final short EARC_VOLNUMBER = 0x0008;
+
+
+ protected long positionInFile;
+
+ protected short headCRC = 0;
+ protected byte headerType = 0;
+ protected short flags = 0;
+ protected short headerSize = 0 ;
+
+ /**
+ *
+ */
+ public BaseBlock(){
+
+ }
+
+ public BaseBlock(BaseBlock bb){
+ this.flags = bb.getFlags();
+ this.headCRC = bb.getHeadCRC();
+ this.headerType = bb.getHeaderType().getHeaderByte();
+ this.headerSize = bb.getHeaderSize();
+ this.positionInFile = bb.getPositionInFile();
+ }
+ public BaseBlock(byte[] baseBlockHeader){
+
+ int pos = 0;
+ this.headCRC = Raw.readShortLittleEndian(baseBlockHeader, pos);
+ pos+=2;
+ this.headerType |= baseBlockHeader[pos]&0xff;
+ pos++;
+ this.flags = Raw.readShortLittleEndian(baseBlockHeader, pos);
+ pos+=2;
+ this.headerSize = Raw.readShortLittleEndian(baseBlockHeader, pos);
+ }
+
+
+ public boolean hasArchiveDataCRC(){
+ return (this.flags & EARC_DATACRC)!=0;
+ }
+
+ public boolean hasVolumeNumber(){
+ return (this.flags & EARC_VOLNUMBER)!=0;
+ }
+
+ public boolean hasEncryptVersion(){
+ return (flags & MHD_ENCRYPTVER)!=0;
+ }
+
+ /**
+ * @return is it a sub block
+ */
+ public boolean isSubBlock()
+ {
+ if (UnrarHeadertype.SubHeader.equals(headerType)){
+ return(true);
+ }
+ if (UnrarHeadertype.NewSubHeader.equals(headerType) && (flags & LHD_SOLID)!=0)
+ {
+ return(true);
+ }
+ return(false);
+
+ }
+
+ public long getPositionInFile() {
+ return positionInFile;
+ }
+
+ public short getFlags() {
+ return flags;
+ }
+
+ public short getHeadCRC() {
+ return headCRC;
+ }
+
+ public short getHeaderSize() {
+ return headerSize;
+ }
+
+ public UnrarHeadertype getHeaderType() {
+ return UnrarHeadertype.findType(headerType);
+ }
+
+ public void setPositionInFile(long positionInFile) {
+ this.positionInFile = positionInFile;
+ }
+
+ public void print(){
+ StringBuilder str =new StringBuilder();
+ str.append("HeaderType: " + getHeaderType());
+ str.append("\nHeadCRC: "+Integer.toHexString(getHeadCRC()));
+ str.append("\nFlags: "+Integer.toHexString(getFlags()));
+ str.append("\nHeaderSize: "+getHeaderSize());
+ str.append("\nPosition in file: "+getPositionInFile());
+ //logger.info(str.toString());
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BlockHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BlockHeader.java
new file mode 100644
index 0000000..78fe955
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/BlockHeader.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 22.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+/**
+ * Base class of headers that contain data
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class BlockHeader extends BaseBlock{
+ public static final short blockHeaderSize = 4;
+
+ //private Log //logger = LogFactory.getLog(BlockHeader.class.getName());
+
+ private int dataSize;
+ private int packSize;
+
+ public BlockHeader(){
+
+ }
+
+ public BlockHeader(BlockHeader bh){
+ super(bh);
+ this.packSize = bh.getDataSize();
+ this.dataSize = packSize;
+ this.positionInFile = bh.getPositionInFile();
+ }
+
+ public BlockHeader(BaseBlock bb, byte[] blockHeader)
+ {
+ super(bb);
+
+ this.packSize = Raw.readIntLittleEndian(blockHeader, 0);
+ this.dataSize = this.packSize;
+ }
+
+ public int getDataSize() {
+ return dataSize;
+ }
+
+ public int getPackSize() {
+ return packSize;
+ }
+
+ public void print(){
+ super.print();
+ String s = "DataSize: "+getDataSize()+" packSize: "+getPackSize();
+ //logger.info(s);
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/CommentHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/CommentHeader.java
new file mode 100644
index 0000000..2664330
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/CommentHeader.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 23.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+/**
+ * Comment header
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class CommentHeader extends BaseBlock {
+
+ public static final short commentHeaderSize = 6;
+
+ private short unpSize;
+ private byte unpVersion;
+ private byte unpMethod;
+ private short commCRC;
+
+
+ public CommentHeader(BaseBlock bb, byte[] commentHeader){
+ super(bb);
+
+ int pos =0;
+ unpSize = Raw.readShortLittleEndian(commentHeader, pos);
+ pos += 2;
+ unpVersion |= commentHeader[pos]&0xff;
+ pos++;
+
+ unpMethod |= commentHeader[pos]&0xff;
+ pos++;
+ commCRC =Raw.readShortLittleEndian(commentHeader, pos);
+
+ }
+
+ public short getCommCRC() {
+ return commCRC;
+ }
+
+ public byte getUnpMethod() {
+ return unpMethod;
+ }
+
+ public short getUnpSize() {
+ return unpSize;
+ }
+
+ public byte getUnpVersion() {
+ return unpVersion;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EAHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EAHeader.java
new file mode 100644
index 0000000..762e644
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EAHeader.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 27.11.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+/**
+ * extended archive CRC header
+ *
+ */
+public class EAHeader
+extends SubBlockHeader
+{
+ //private Log //logger = LogFactory.getLog(getClass());
+
+ public static final short EAHeaderSize = 10;
+
+ private int unpSize;
+ private byte unpVer;
+ private byte method;
+ private int EACRC;
+
+ public EAHeader(SubBlockHeader sb, byte[] eahead)
+ {
+ super(sb);
+ int pos = 0;
+ unpSize = Raw.readIntLittleEndian(eahead, pos);
+ pos+=4;
+ unpVer |= eahead[pos]&0xff;
+ pos++;
+ method |= eahead[pos]&0xff;
+ pos++;
+ EACRC = Raw.readIntLittleEndian(eahead, pos);
+ }
+
+ /**
+ * @return the eACRC
+ */
+ public int getEACRC() {
+ return EACRC;
+ }
+
+ /**
+ * @return the method
+ */
+ public byte getMethod() {
+ return method;
+ }
+
+ /**
+ * @return the unpSize
+ */
+ public int getUnpSize() {
+ return unpSize;
+ }
+
+ /**
+ * @return the unpVer
+ */
+ public byte getUnpVer() {
+ return unpVer;
+ }
+
+ public void print()
+ {
+ super.print();
+ //logger.info("unpSize: "+unpSize);
+ //logger.info("unpVersion: " + unpVer);
+ //logger.info("method: "+method);
+ //logger.info("EACRC:" + EACRC);
+ }
+}
+
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EndArcHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EndArcHeader.java
new file mode 100644
index 0000000..5a9be5e
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/EndArcHeader.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 24.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+/**
+ *
+ * the optional End header
+ *
+ */
+public class EndArcHeader extends BaseBlock{
+
+ private static final short EARC_NEXT_VOLUME = 0x0001;
+ private static final short EARC_DATACRC = 0x0002;
+ private static final short EARC_REVSPACE = 0x0004;
+ private static final short EARC_VOLNUMBER = 0x0008;
+
+ private static final short endArcHeaderSize = 6;
+ public static final short endArcArchiveDataCrcSize = 4;
+ public static final short endArcVolumeNumberSize = 2;
+
+ private int archiveDataCRC;
+ private short volumeNumber;
+
+
+ public EndArcHeader(BaseBlock bb, byte[] endArcHeader){
+ super(bb);
+
+ int pos = 0;
+ if(hasArchiveDataCRC()){
+ archiveDataCRC =Raw.readIntLittleEndian(endArcHeader, pos);
+ pos+=4;
+ }
+ if(hasVolumeNumber()){
+ volumeNumber = Raw.readShortLittleEndian(endArcHeader, pos);
+ }
+ }
+
+ public int getArchiveDataCRC() {
+ return archiveDataCRC;
+ }
+
+ public short getVolumeNumber() {
+ return volumeNumber;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileHeader.java
new file mode 100644
index 0000000..84dce5e
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileHeader.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 22.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.commons.logging.Log;
+
+import com.github.junrar.io.Raw;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class FileHeader extends BlockHeader {
+
+ //private final Log //logger = LogFactory.getLog(FileHeader.class.getName());
+
+ private static final byte SALT_SIZE = 8;
+
+ private static final byte NEWLHD_SIZE = 32;
+
+ private long unpSize;
+
+ private final HostSystem hostOS;
+
+ private final int fileCRC;
+
+ private final int fileTime;
+
+ private byte unpVersion;
+
+ private byte unpMethod;
+
+ private short nameSize;
+
+ private int highPackSize;
+
+ private int highUnpackSize;
+
+ private final byte[] fileNameBytes;
+
+ private String fileName;
+ private String fileNameW;
+
+ private byte[] subData;
+
+ private final byte[] salt = new byte[SALT_SIZE];
+
+ private Date mTime;
+
+ private Date cTime;
+
+ private Date aTime;
+
+ private Date arcTime;
+
+ private long fullPackSize;
+
+ private long fullUnpackSize;
+
+ private int fileAttr;
+
+ private int subFlags; // same as fileAttr (in header)
+
+ private int recoverySectors = -1;
+
+ public FileHeader(BlockHeader bh, byte[] fileHeader) {
+ super(bh);
+
+ int position = 0;
+ unpSize = Raw.readIntLittleEndianAsLong(fileHeader, position);
+ position += 4;
+ hostOS = HostSystem.findHostSystem(fileHeader[4]);
+ position++;
+
+ fileCRC = Raw.readIntLittleEndian(fileHeader, position);
+ position += 4;
+
+ fileTime = Raw.readIntLittleEndian(fileHeader, position);
+ position += 4;
+
+ unpVersion |= fileHeader[13] & 0xff;
+ position++;
+ unpMethod |= fileHeader[14] & 0xff;
+ position++;
+ nameSize = Raw.readShortLittleEndian(fileHeader, position);
+ position += 2;
+
+ fileAttr = Raw.readIntLittleEndian(fileHeader, position);
+ position += 4;
+ if (isLargeBlock()) {
+ highPackSize = Raw.readIntLittleEndian(fileHeader, position);
+ position += 4;
+
+ highUnpackSize = Raw.readIntLittleEndian(fileHeader, position);
+ position += 4;
+ } else {
+ highPackSize = 0;
+ highUnpackSize = 0;
+ if (unpSize == 0xffffffff) {
+
+ unpSize = 0xffffffff;
+ highUnpackSize = Integer.MAX_VALUE;
+ }
+
+ }
+ fullPackSize |= highPackSize;
+ fullPackSize <<= 32;
+ fullPackSize |= getPackSize();
+
+ fullUnpackSize |= highUnpackSize;
+ fullUnpackSize <<= 32;
+ fullUnpackSize += unpSize;
+
+ nameSize = nameSize > 4 * 1024 ? 4 * 1024 : nameSize;
+
+ fileNameBytes = new byte[nameSize];
+ for (int i = 0; i < nameSize; i++) {
+ fileNameBytes[i] = fileHeader[position];
+ position++;
+ }
+
+ if (isFileHeader()) {
+ if (isUnicode()) {
+ int length = 0;
+ fileName = "";
+ fileNameW = "";
+ while (length < fileNameBytes.length
+ && fileNameBytes[length] != 0) {
+ length++;
+ }
+ byte[] name = new byte[length];
+ System.arraycopy(fileNameBytes, 0, name, 0, name.length);
+ fileName = new String(name);
+ if (length != nameSize) {
+ length++;
+ fileNameW = FileNameDecoder.decode(fileNameBytes, length);
+ }
+ } else {
+ fileName = new String(fileNameBytes);
+ fileNameW = "";
+ }
+ }
+
+ if (UnrarHeadertype.NewSubHeader.equals(headerType)) {
+ int datasize = headerSize - NEWLHD_SIZE - nameSize;
+ if (hasSalt()) {
+ datasize -= SALT_SIZE;
+ }
+ if (datasize > 0) {
+ subData = new byte[datasize];
+ for (int i = 0; i < datasize; i++) {
+ subData[i] = (fileHeader[position]);
+ position++;
+ }
+ }
+
+ if (NewSubHeaderType.SUBHEAD_TYPE_RR.byteEquals(fileNameBytes)) {
+ recoverySectors = subData[8] + (subData[9] << 8)
+ + (subData[10] << 16) + (subData[11] << 24);
+ }
+ }
+
+ if (hasSalt()) {
+ for (int i = 0; i < SALT_SIZE; i++) {
+ salt[i] = fileHeader[position];
+ position++;
+ }
+ }
+ mTime = getDateDos(fileTime);
+ // TODO rartime -> extended
+
+ }
+
+ @Override
+ public void print() {
+ super.print();
+ StringBuilder str = new StringBuilder();
+ str.append("unpSize: " + getUnpSize());
+ str.append("\nHostOS: " + hostOS.name());
+ str.append("\nMDate: " + mTime);
+ str.append("\nFileName: " + getFileNameString());
+ str.append("\nunpMethod: " + Integer.toHexString(getUnpMethod()));
+ str.append("\nunpVersion: " + Integer.toHexString(getUnpVersion()));
+ str.append("\nfullpackedsize: " + getFullPackSize());
+ str.append("\nfullunpackedsize: " + getFullUnpackSize());
+ str.append("\nisEncrypted: " + isEncrypted());
+ str.append("\nisfileHeader: " + isFileHeader());
+ str.append("\nisSolid: " + isSolid());
+ str.append("\nisSplitafter: " + isSplitAfter());
+ str.append("\nisSplitBefore:" + isSplitBefore());
+ str.append("\nunpSize: " + getUnpSize());
+ str.append("\ndataSize: " + getDataSize());
+ str.append("\nisUnicode: " + isUnicode());
+ str.append("\nhasVolumeNumber: " + hasVolumeNumber());
+ str.append("\nhasArchiveDataCRC: " + hasArchiveDataCRC());
+ str.append("\nhasSalt: " + hasSalt());
+ str.append("\nhasEncryptVersions: " + hasEncryptVersion());
+ str.append("\nisSubBlock: " + isSubBlock());
+ //logger.info(str.toString());
+ }
+
+ private Date getDateDos(int time) {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.YEAR, (time >>> 25) + 1980);
+ cal.set(Calendar.MONTH, ((time >>> 21) & 0x0f) - 1);
+ cal.set(Calendar.DAY_OF_MONTH, (time >>> 16) & 0x1f);
+ cal.set(Calendar.HOUR_OF_DAY, (time >>> 11) & 0x1f);
+ cal.set(Calendar.MINUTE, (time >>> 5) & 0x3f);
+ cal.set(Calendar.SECOND, (time & 0x1f) * 2);
+ return cal.getTime();
+ }
+
+ public Date getArcTime() {
+ return arcTime;
+ }
+
+ public void setArcTime(Date arcTime) {
+ this.arcTime = arcTime;
+ }
+
+ public Date getATime() {
+ return aTime;
+ }
+
+ public void setATime(Date time) {
+ aTime = time;
+ }
+
+ public Date getCTime() {
+ return cTime;
+ }
+
+ public void setCTime(Date time) {
+ cTime = time;
+ }
+
+ public int getFileAttr() {
+ return fileAttr;
+ }
+
+ public void setFileAttr(int fileAttr) {
+ this.fileAttr = fileAttr;
+ }
+
+ public int getFileCRC() {
+ return fileCRC;
+ }
+
+ public byte[] getFileNameByteArray() {
+ return fileNameBytes;
+ }
+
+ public String getFileNameString() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getFileNameW() {
+ return fileNameW;
+ }
+
+ public void setFileNameW(String fileNameW) {
+ this.fileNameW = fileNameW;
+ }
+
+ public int getHighPackSize() {
+ return highPackSize;
+ }
+
+ public int getHighUnpackSize() {
+ return highUnpackSize;
+ }
+
+ public HostSystem getHostOS() {
+ return hostOS;
+ }
+
+ public Date getMTime() {
+ return mTime;
+ }
+
+ public void setMTime(Date time) {
+ mTime = time;
+ }
+
+ public short getNameSize() {
+ return nameSize;
+ }
+
+ public int getRecoverySectors() {
+ return recoverySectors;
+ }
+
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public byte[] getSubData() {
+ return subData;
+ }
+
+ public int getSubFlags() {
+ return subFlags;
+ }
+
+ public byte getUnpMethod() {
+ return unpMethod;
+ }
+
+ public long getUnpSize() {
+ return unpSize;
+ }
+
+ public byte getUnpVersion() {
+ return unpVersion;
+ }
+
+ public long getFullPackSize() {
+ return fullPackSize;
+ }
+
+ public long getFullUnpackSize() {
+ return fullUnpackSize;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+
+ /**
+ * the file will be continued in the next archive part
+ *
+ * @return
+ */
+ public boolean isSplitAfter() {
+ return (this.flags & BlockHeader.LHD_SPLIT_AFTER) != 0;
+ }
+
+ /**
+ * the file is continued in this archive
+ *
+ * @return
+ */
+ public boolean isSplitBefore() {
+ return (this.flags & LHD_SPLIT_BEFORE) != 0;
+ }
+
+ /**
+ * this file is compressed as solid (all files handeled as one)
+ *
+ * @return
+ */
+ public boolean isSolid() {
+ return (this.flags & LHD_SOLID) != 0;
+ }
+
+ /**
+ * the file is encrypted
+ *
+ * @return
+ */
+ public boolean isEncrypted() {
+ return (this.flags & BlockHeader.LHD_PASSWORD) != 0;
+ }
+
+ /**
+ * the filename is also present in unicode
+ *
+ * @return
+ */
+ public boolean isUnicode() {
+ return (flags & LHD_UNICODE) != 0;
+ }
+
+ public boolean isFileHeader() {
+ return UnrarHeadertype.FileHeader.equals(headerType);
+ }
+
+ public boolean hasSalt() {
+ return (flags & LHD_SALT) != 0;
+ }
+
+ public boolean isLargeBlock() {
+ return (flags & LHD_LARGE) != 0;
+ }
+
+ /**
+ * whether this fileheader represents a directory
+ *
+ * @return
+ */
+ public boolean isDirectory() {
+ return (flags & LHD_WINDOWMASK) == LHD_DIRECTORY;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileNameDecoder.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileNameDecoder.java
new file mode 100644
index 0000000..aee1179
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/FileNameDecoder.java
@@ -0,0 +1,76 @@
+/*
+ *
+ * Original author: alpha_lam
+ * Creation date: ?
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+public class FileNameDecoder {
+ public static int getChar(byte [] name,int pos){
+ return name[pos]&0xff;
+ }
+
+ public static String decode(byte [] name,int encPos){
+ int decPos = 0;
+ int flags = 0;
+ int flagBits = 0;
+
+ int low = 0;
+ int high = 0;
+ int highByte = getChar(name,encPos++);
+ StringBuffer buf = new StringBuffer();
+ while(encPos < name.length){
+ if(flagBits == 0){
+ flags = getChar(name,encPos++);
+ flagBits = 8;
+ }
+ switch(flags >> 6){
+ case 0:
+ buf.append((char)(getChar(name,encPos++)));
+ ++decPos;
+ break;
+ case 1:
+ buf.append((char)(getChar(name,encPos++)+(highByte<<8)));
+ ++decPos;
+ break;
+ case 2:
+ low = getChar(name,encPos);
+ high = getChar(name,encPos+1);
+ buf.append((char)((high << 8) + low));
+ ++decPos;
+ encPos += 2;
+ break;
+ case 3:
+ int length = getChar(name,encPos++);
+ if((length&0x80)!=0){
+ int correction = getChar(name,encPos++);
+ for(length=(length&0x7f)+2;length>0&&decPos<name.length;length--,decPos++){
+ low = (getChar(name,decPos) + correction)&0xff;
+ buf.append((char)((highByte << 8) + low));
+ }
+ }else{
+ for(length+=2;length>0&&decPos<name.length;length--,decPos++){
+ buf.append((char)(getChar(name,decPos)));
+ }
+ }
+ break;
+ }
+ flags = (flags << 2) & 0xff;
+ flagBits -= 2;
+ }
+ return buf.toString();
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/HostSystem.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/HostSystem.java
new file mode 100644
index 0000000..7970fef
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/HostSystem.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 22.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum HostSystem {
+ msdos ((byte)0),
+ os2 ((byte)1),
+ win32 ((byte)2),
+ unix ((byte)3),
+ macos ((byte)4),
+ beos ((byte)5);
+
+ private byte hostByte;
+
+ public static HostSystem findHostSystem(byte hostByte){
+ if(HostSystem.msdos.equals(hostByte)){
+ return HostSystem.msdos;
+ }
+ if(HostSystem.os2.equals(hostByte)){
+ return HostSystem.os2;
+ }
+ if(HostSystem.win32.equals(hostByte)){
+ return HostSystem.win32;
+ }
+ if(HostSystem.unix.equals(hostByte)){
+ return HostSystem.unix;
+ }
+ if(HostSystem.macos.equals(hostByte)){
+ return HostSystem.macos;
+ }
+ if(HostSystem.beos.equals(hostByte)){
+ return HostSystem.beos;
+ }
+ return null;
+ }
+
+
+ private HostSystem(byte hostByte){
+ this.hostByte = hostByte;
+ }
+
+ public boolean equals(byte hostByte){
+ return this.hostByte == hostByte;
+ }
+
+ public byte getHostByte(){
+ return hostByte;
+ }
+ //???? public static final byte max = 6;
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MacInfoHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MacInfoHeader.java
new file mode 100644
index 0000000..e94521d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MacInfoHeader.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 26.11.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+/**
+ * Mac File attribute header
+ *
+ */
+public class MacInfoHeader
+extends SubBlockHeader
+{
+ //private Log //logger = LogFactory.getLog(getClass());
+
+ public static final short MacInfoHeaderSize = 8;
+
+ private int fileType;
+ private int fileCreator;
+
+ public MacInfoHeader(SubBlockHeader sb, byte[] macHeader)
+ {
+ super(sb);
+ int pos = 0;
+ fileType = Raw.readIntLittleEndian(macHeader, pos);
+ pos+=4;
+ fileCreator = Raw.readIntLittleEndian(macHeader, pos);
+ }
+
+ /**
+ * @return the fileCreator
+ */
+ public int getFileCreator() {
+ return fileCreator;
+ }
+
+ /**
+ * @param fileCreator the fileCreator to set
+ */
+ public void setFileCreator(int fileCreator) {
+ this.fileCreator = fileCreator;
+ }
+
+ /**
+ * @return the fileType
+ */
+ public int getFileType() {
+ return fileType;
+ }
+
+ /**
+ * @param fileType the fileType to set
+ */
+ public void setFileType(int fileType) {
+ this.fileType = fileType;
+ }
+
+ public void print(){
+ super.print();
+ //logger.info("filetype: "+fileType);
+ //logger.info("creator :"+fileCreator);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MainHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MainHeader.java
new file mode 100644
index 0000000..8c3e89f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MainHeader.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 22.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+/**
+ * The main header of an rar archive. holds information concerning the whole archive (solid, encrypted etc).
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class MainHeader extends BaseBlock {
+ //private Log //logger = LogFactory.getLog(MainHeader.class.getName());
+ public static final short mainHeaderSizeWithEnc = 7;
+ public static final short mainHeaderSize = 6;
+ private short highPosAv;
+ private int posAv;
+ private byte encryptVersion;
+
+ public MainHeader(BaseBlock bb, byte[] mainHeader) {
+ super(bb);
+ int pos = 0;
+ highPosAv = Raw.readShortLittleEndian(mainHeader, pos);
+ pos += 2;
+ posAv = Raw.readIntLittleEndian(mainHeader, pos);
+ pos+=4;
+
+ if(hasEncryptVersion()){
+ encryptVersion |= mainHeader[pos]&0xff;
+ }
+ }
+
+ /**
+ * old cmt block is present
+ * @return true if has cmt block
+ */
+ public boolean hasArchCmt(){
+ return (this.flags & BaseBlock.MHD_COMMENT)!=0;
+ }
+ /**
+ * the version the the encryption
+ * @return
+ */
+ public byte getEncryptVersion() {
+ return encryptVersion;
+ }
+
+ public short getHighPosAv() {
+ return highPosAv;
+ }
+
+ public int getPosAv() {
+ return posAv;
+ }
+
+ /**
+ * returns whether the archive is encrypted
+ * @return
+ */
+ public boolean isEncrypted(){
+ return (this.flags & BaseBlock.MHD_PASSWORD)!=0;
+ }
+
+ /**
+ * return whether the archive is a multivolume archive
+ * @return
+ */
+ public boolean isMultiVolume(){
+ return (this.flags & BaseBlock.MHD_VOLUME)!=0;
+ }
+
+ /**
+ * if the archive is a multivolume archive this method returns whether this instance is the first part of the multivolume archive
+ * @return
+ */
+ public boolean isFirstVolume(){
+ return (this.flags & BaseBlock.MHD_FIRSTVOLUME)!=0;
+ }
+
+ public void print(){
+ super.print();
+ StringBuilder str=new StringBuilder();
+ str.append("posav: "+getPosAv());
+ str.append("\nhighposav: "+getHighPosAv());
+ str.append("\nhasencversion: "+hasEncryptVersion()+(hasEncryptVersion()?getEncryptVersion():""));
+ str.append("\nhasarchcmt: "+hasArchCmt());
+ str.append("\nisEncrypted: "+isEncrypted());
+ str.append("\nisMultivolume: "+isMultiVolume());
+ str.append("\nisFirstvolume: "+isFirstVolume());
+ str.append("\nisSolid: "+isSolid());
+ str.append("\nisLocked: "+isLocked());
+ str.append("\nisProtected: "+isProtected());
+ str.append("\nisAV: "+isAV());
+ //logger.info(str.toString());
+ }
+
+ /**
+ * returns whether this archive is solid. in this case you can only extract all file at once
+ * @return
+ */
+ public boolean isSolid(){
+ return (this.flags&MHD_SOLID)!=0;
+ }
+
+ public boolean isLocked(){
+ return (this.flags&MHD_LOCK)!=0;
+ }
+
+ public boolean isProtected(){
+ return (this.flags&MHD_PROTECT)!=0;
+ }
+
+ public boolean isAV(){
+ return (this.flags&MHD_AV)!=0;
+ }
+ /**
+ * the numbering format a multivolume archive
+ * @return
+ */
+ public boolean isNewNumbering(){
+ return (this.flags&MHD_NEWNUMBERING)!=0;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MarkHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MarkHeader.java
new file mode 100644
index 0000000..1673306
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/MarkHeader.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 24.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+/**
+ * the header to recognize a file to be a rar archive
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class MarkHeader extends BaseBlock {
+
+ //private Log //logger = LogFactory.getLog(MarkHeader.class.getName());
+ private boolean oldFormat = false;
+
+ public MarkHeader(BaseBlock bb){
+ super(bb);
+ }
+ public boolean isValid(){
+ if(!(getHeadCRC() == 0x6152)){
+ return false;
+ }
+ if(!(getHeaderType() == UnrarHeadertype.MarkHeader)){
+ return false;
+ }
+ if(!(getFlags() == 0x1a21)){
+ return false;
+ }
+ if(!(getHeaderSize() == BaseBlockSize)){
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isSignature() {
+ boolean valid=false;
+ byte[] d = new byte[BaseBlock.BaseBlockSize];
+ Raw.writeShortLittleEndian(d, 0, headCRC);
+ d[2] = headerType;
+ Raw.writeShortLittleEndian(d, 3, flags);
+ Raw.writeShortLittleEndian(d, 5, headerSize);
+
+ if (d[0] == 0x52) {
+ if (d[1]==0x45 && d[2]==0x7e && d[3]==0x5e) {
+ oldFormat=true;
+ valid=true;
+ }
+ else if (d[1]==0x61 && d[2]==0x72 && d[3]==0x21 && d[4]==0x1a &&
+ d[5]==0x07 && d[6]==0x00) {
+ oldFormat=false;
+ valid=true;
+ }
+ }
+ return valid;
+ }
+
+ public boolean isOldFormat() {
+ return oldFormat;
+ }
+
+ public void print(){
+ super.print();
+ //logger.info("valid: "+isValid());
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/NewSubHeaderType.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/NewSubHeaderType.java
new file mode 100644
index 0000000..e0096f7
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/NewSubHeaderType.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 24.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import java.util.Arrays;
+
+/**
+ * subheaders new version of the info headers
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class NewSubHeaderType {
+
+ /**
+ * comment subheader
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_CMT = new NewSubHeaderType(new byte[]{'C','M','T'});
+ /**
+ *
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_ACL = new NewSubHeaderType(new byte[]{'A','C','L'});
+ /**
+ *
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_STREAM = new NewSubHeaderType(new byte[]{'S','T','M'});
+ /**
+ *
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_UOWNER = new NewSubHeaderType(new byte[]{'U','O','W'});
+ /**
+ *
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_AV = new NewSubHeaderType(new byte[]{'A','V'});
+ /**
+ * recovery record subheader
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_RR = new NewSubHeaderType(new byte[]{'R','R'});
+ /**
+ *
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_OS2EA = new NewSubHeaderType(new byte[]{'E','A','2'});
+ /**
+ *
+ */
+ public static final NewSubHeaderType SUBHEAD_TYPE_BEOSEA = new NewSubHeaderType(new byte[]{'E','A','B','E'});
+
+ private byte[] headerTypes;
+
+ /**
+ * Private constructor
+ * @param headerTypes
+ */
+ private NewSubHeaderType(byte[] headerTypes)
+ {
+ this.headerTypes = headerTypes;
+ }
+
+ /**
+ * @param toCompare
+ * @return Returns true if the given byte array matches to the internal byte array of this header.
+ */
+ public boolean byteEquals(byte[] toCompare)
+ {
+ return Arrays.equals(this.headerTypes, toCompare);
+ }
+
+ @Override
+ public String toString()
+ {
+ return new String(this.headerTypes);
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/ProtectHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/ProtectHeader.java
new file mode 100644
index 0000000..348b25b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/ProtectHeader.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 24.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+/**
+ * recovery header
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class ProtectHeader extends BlockHeader {
+
+ /**
+ * the header size
+ */
+ public static final int protectHeaderSize = 8;
+
+ private byte version;
+ private short recSectors;
+ private int totalBlocks;
+ private byte mark;
+
+
+ public ProtectHeader(BlockHeader bh, byte[] protectHeader){
+ super(bh);
+
+ int pos = 0;
+ version |= protectHeader[pos]&0xff;
+
+ recSectors = Raw.readShortLittleEndian(protectHeader, pos);
+ pos += 2;
+ totalBlocks = Raw.readIntLittleEndian(protectHeader, pos);
+ pos += 4;
+ mark |= protectHeader[pos]&0xff;
+ }
+
+
+ public byte getMark() {
+ return mark;
+ }
+
+ public short getRecSectors() {
+ return recSectors;
+ }
+
+ public int getTotalBlocks() {
+ return totalBlocks;
+ }
+
+ public byte getVersion() {
+ return version;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SignHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SignHeader.java
new file mode 100644
index 0000000..e78d4a3
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SignHeader.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 24.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+/**
+ * sign header
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class SignHeader extends BaseBlock {
+
+ public static final short signHeaderSize = 8;
+
+ private int creationTime=0;
+ private short arcNameSize=0;
+ private short userNameSize=0;
+
+
+ public SignHeader(BaseBlock bb, byte[] signHeader){
+ super(bb);
+
+ int pos = 0;
+ creationTime = Raw.readIntLittleEndian(signHeader, pos);
+ pos +=4;
+ arcNameSize = Raw.readShortLittleEndian(signHeader, pos);
+ pos+=2;
+ userNameSize = Raw.readShortLittleEndian(signHeader, pos);
+ }
+
+ public short getArcNameSize() {
+ return arcNameSize;
+ }
+
+ public int getCreationTime() {
+ return creationTime;
+ }
+
+ public short getUserNameSize() {
+ return userNameSize;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeader.java
new file mode 100644
index 0000000..d0e0eed
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeader.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 21.11.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+public class SubBlockHeader
+extends BlockHeader
+{
+ //private Log //logger = LogFactory.getLog(getClass());
+
+ public static final short SubBlockHeaderSize = 3;
+
+ private short subType;
+ private byte level;
+
+ public SubBlockHeader(SubBlockHeader sb)
+ {
+ super(sb);
+ subType = sb.getSubType().getSubblocktype();
+ level = sb.getLevel();
+ }
+
+ public SubBlockHeader(BlockHeader bh, byte[] subblock)
+ {
+ super(bh);
+ int position = 0;
+ subType = Raw.readShortLittleEndian(subblock, position);
+ position +=2;
+ level |= subblock[position]&0xff;
+ }
+
+ /**
+ * @return
+ */
+ public byte getLevel() {
+ return level;
+ }
+
+ /**
+ * @return
+ */
+ public SubBlockHeaderType getSubType() {
+ return SubBlockHeaderType.findSubblockHeaderType(subType);
+ }
+
+ public void print()
+ {
+ super.print();
+ //logger.info("subtype: "+getSubType());
+ //logger.info("level: "+level);
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeaderType.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeaderType.java
new file mode 100644
index 0000000..000f24d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/SubBlockHeaderType.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 20.11.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+
+package com.github.junrar.rarfile;
+
+public enum SubBlockHeaderType
+{
+ EA_HEAD ((short)0x100),
+ UO_HEAD ((short)0x101),
+ MAC_HEAD ((short)0x102),
+ BEEA_HEAD ((short)0x103),
+ NTACL_HEAD ((short)0x104),
+ STREAM_HEAD ((short)0x105);
+
+ private short subblocktype;
+
+ private SubBlockHeaderType(short subblocktype)
+ {
+ this.subblocktype = subblocktype;
+ }
+
+ /**
+ * Return true if the given value is equal to the enum's value
+ * @param subblocktype
+ * @return true if the given value is equal to the enum's value
+ */
+ public boolean equals(short subblocktype)
+ {
+ return this.subblocktype == subblocktype;
+ }
+
+ /**
+ * find the header type for the given short value
+ * @param SubType the short value
+ * @return the correspo nding enum or null
+ */
+ public static SubBlockHeaderType findSubblockHeaderType(short subType)
+ {
+ if(EA_HEAD.equals(subType)){
+ return EA_HEAD;
+ }else if(UO_HEAD.equals(subType)){
+ return UO_HEAD;
+ }else if(MAC_HEAD.equals(subType)){
+ return MAC_HEAD;
+ }else if(BEEA_HEAD.equals(subType)){
+ return BEEA_HEAD;
+ }else if(NTACL_HEAD.equals(subType)){
+ return NTACL_HEAD;
+ }else if(STREAM_HEAD.equals(subType)){
+ return STREAM_HEAD;
+ }
+ return null;
+ }
+
+ /**
+ * @return the short representation of this enum
+ */
+ public short getSubblocktype() {
+ return subblocktype;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnixOwnersHeader.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnixOwnersHeader.java
new file mode 100644
index 0000000..192ba7a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnixOwnersHeader.java
@@ -0,0 +1,93 @@
+package com.github.junrar.rarfile;
+
+import com.github.junrar.io.Raw;
+
+
+public class UnixOwnersHeader
+extends SubBlockHeader
+{
+ //private Log //logger = LogFactory.getLog(UnixOwnersHeader.class);
+ private int ownerNameSize;
+ private int groupNameSize;
+ private String owner;
+ private String group;
+
+ public UnixOwnersHeader(SubBlockHeader sb, byte[] uoHeader) {
+ super(sb);
+ int pos = 0;
+ ownerNameSize = Raw.readShortLittleEndian(uoHeader, pos)&0xFFFF;
+ pos+=2;
+ groupNameSize = Raw.readShortLittleEndian(uoHeader, pos)&0xFFFF;
+ pos+=2;
+ if(pos+ownerNameSize<uoHeader.length){
+ byte[] ownerBuffer = new byte[ownerNameSize];
+ System.arraycopy(uoHeader, pos, ownerBuffer, 0, ownerNameSize);
+ owner = new String(ownerBuffer);
+ }
+ pos+=ownerNameSize;
+ if(pos+groupNameSize<uoHeader.length){
+ byte[] groupBuffer = new byte[groupNameSize];
+ System.arraycopy(uoHeader, pos, groupBuffer, 0, groupNameSize);
+ group = new String(groupBuffer);
+ }
+ }
+ /**
+ * @return the group
+ */
+ public String getGroup() {
+ return group;
+ }
+ /**
+ * @param group the group to set
+ */
+ public void setGroup(String group) {
+ this.group = group;
+ }
+ /**
+ * @return the groupNameSize
+ */
+ public int getGroupNameSize() {
+ return groupNameSize;
+ }
+ /**
+ * @param groupNameSize the groupNameSize to set
+ */
+ public void setGroupNameSize(int groupNameSize) {
+ this.groupNameSize = groupNameSize;
+ }
+ /**
+ * @return the owner
+ */
+ public String getOwner() {
+ return owner;
+ }
+ /**
+ * @param owner the owner to set
+ */
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+ /**
+ * @return the ownerNameSize
+ */
+ public int getOwnerNameSize() {
+ return ownerNameSize;
+ }
+ /**
+ * @param ownerNameSize the ownerNameSize to set
+ */
+ public void setOwnerNameSize(int ownerNameSize) {
+ this.ownerNameSize = ownerNameSize;
+ }
+
+ /* (non-Javadoc)
+ * @see de.innosystec.unrar.rarfile.SubBlockHeader#print()
+ */
+ public void print(){
+ super.print();
+ //logger.info("ownerNameSize: "+ownerNameSize);
+ //logger.info("owner: "+owner);
+ //logger.info("groupNameSize: "+groupNameSize);
+ //logger.info("group: "+group);
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnrarHeadertype.java b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnrarHeadertype.java
new file mode 100644
index 0000000..9214759
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/rarfile/UnrarHeadertype.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 22.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.rarfile;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum UnrarHeadertype {
+
+
+ /**
+ *
+ */
+ MainHeader ((byte)0x73),
+
+ /**
+ *
+ */
+ MarkHeader ((byte)0x72),
+
+ /**
+ *
+ */
+ FileHeader ((byte) 0x74),
+
+ /**
+ *
+ */
+ CommHeader ((byte) 0x75),
+
+ /**
+ *
+ */
+ AvHeader ((byte) 0x76),
+
+ /**
+ *
+ */
+ SubHeader ((byte) 0x77),
+
+ /**
+ *
+ */
+ ProtectHeader ((byte) 0x78),
+
+ /**
+ *
+ */
+ SignHeader ((byte) 0x79),
+
+ /**
+ *
+ */
+ NewSubHeader ((byte) 0x7a),
+
+ /**
+ *
+ */
+ EndArcHeader ((byte) 0x7b);
+
+ /**
+ * Returns the enum according to the given byte or null
+ * @param headerType the headerbyte
+ * @return the enum or null
+ */
+ public static UnrarHeadertype findType(byte headerType)
+ {
+ if(UnrarHeadertype.MarkHeader.equals(headerType)){
+ return UnrarHeadertype.MarkHeader;
+ }
+ if(UnrarHeadertype.MainHeader.equals(headerType)){
+ return UnrarHeadertype.MainHeader;
+ }
+ if(UnrarHeadertype.FileHeader.equals(headerType)){
+ return UnrarHeadertype.FileHeader;
+ }
+ if(UnrarHeadertype.EndArcHeader.equals(headerType)){
+ return UnrarHeadertype.EndArcHeader;
+ }
+ if(UnrarHeadertype.NewSubHeader.equals(headerType)){
+ return UnrarHeadertype.NewSubHeader;
+ }
+ if(UnrarHeadertype.SubHeader.equals(headerType)){
+ return UnrarHeadertype.SubHeader;
+ }
+ if(UnrarHeadertype.SignHeader.equals(headerType)){
+ return UnrarHeadertype.SignHeader;
+ }
+ if(UnrarHeadertype.ProtectHeader.equals(headerType)){
+ return UnrarHeadertype.ProtectHeader;
+ }
+ if(UnrarHeadertype.MarkHeader.equals(headerType)){
+ return UnrarHeadertype.MarkHeader;
+ }
+ if(UnrarHeadertype.MainHeader.equals(headerType)){
+ return UnrarHeadertype.MainHeader;
+ }
+ if(UnrarHeadertype.FileHeader.equals(headerType)){
+ return UnrarHeadertype.FileHeader;
+ }
+ if(UnrarHeadertype.EndArcHeader.equals(headerType)){
+ return UnrarHeadertype.EndArcHeader;
+ }
+ if(UnrarHeadertype.CommHeader.equals(headerType)){
+ return UnrarHeadertype.CommHeader;
+ }
+ if(UnrarHeadertype.AvHeader.equals(headerType)){
+ return UnrarHeadertype.AvHeader;
+ }
+ return null;
+ }
+
+
+
+ private byte headerByte;
+
+ private UnrarHeadertype(byte headerByte)
+ {
+ this.headerByte = headerByte;
+ }
+
+
+ /**
+ * Return true if the given byte is equal to the enum's byte
+ * @param header
+ * @return true if the given byte is equal to the enum's byte
+ */
+ public boolean equals(byte header)
+ {
+ return headerByte == header;
+ }
+
+
+ /**
+ * the header byte of this enum
+ * @return the header byte of this enum
+ */
+ public byte getHeaderByte() {
+ return headerByte;
+ }
+
+
+
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ComprDataIO.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ComprDataIO.java
new file mode 100644
index 0000000..8791b09
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ComprDataIO.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.github.junrar.Archive;
+import com.github.junrar.UnrarCallback;
+import com.github.junrar.Volume;
+import com.github.junrar.crc.RarCRC;
+import com.github.junrar.exception.RarException;
+import com.github.junrar.exception.RarException.RarExceptionType;
+import com.github.junrar.io.ReadOnlyAccessInputStream;
+import com.github.junrar.rarfile.FileHeader;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class ComprDataIO {
+
+ private final Archive archive;
+
+ private long unpPackedSize;
+
+ private boolean testMode;
+
+ private boolean skipUnpCRC;
+
+ private InputStream inputStream;
+
+ private OutputStream outputStream;
+
+ private FileHeader subHead;
+
+ // cryptData Crypt;
+ // cryptData Decrypt;
+ private boolean packVolume;
+
+ private boolean unpVolume;
+
+ private boolean nextVolumeMissing;
+
+ private long totalPackRead;
+
+ private long unpArcSize;
+
+ private long curPackRead, curPackWrite, curUnpRead, curUnpWrite;
+
+ private long processedArcSize, totalArcSize;
+
+ private long packFileCRC, unpFileCRC, packedCRC;
+
+ private int encryption;
+
+ private int decryption;
+
+ private int lastPercent;
+
+ private char currentCommand;
+
+ public ComprDataIO(Archive arc) {
+ this.archive = arc;
+ }
+
+ public void init(OutputStream outputStream) {
+ this.outputStream = outputStream;
+ unpPackedSize = 0;
+ testMode = false;
+ skipUnpCRC = false;
+ packVolume = false;
+ unpVolume = false;
+ nextVolumeMissing = false;
+ // command = null;
+ encryption = 0;
+ decryption = 0;
+ totalPackRead = 0;
+ curPackRead = curPackWrite = curUnpRead = curUnpWrite = 0;
+ packFileCRC = unpFileCRC = packedCRC = 0xffffffff;
+ lastPercent = -1;
+ subHead = null;
+
+ currentCommand = 0;
+ processedArcSize = totalArcSize = 0;
+ }
+
+ public void init(FileHeader hd) throws IOException {
+ long startPos = hd.getPositionInFile() + hd.getHeaderSize();
+ unpPackedSize = hd.getFullPackSize();
+ inputStream = new ReadOnlyAccessInputStream(archive.getRof(), startPos,
+ startPos + unpPackedSize);
+ subHead = hd;
+ curUnpRead = 0;
+ curPackWrite = 0;
+ packedCRC = 0xFFffFFff;
+ }
+
+ public int unpRead(byte[] addr, int offset, int count) throws IOException,
+ RarException {
+ int retCode = 0, totalRead = 0;
+ while (count > 0) {
+ int readSize = (count > unpPackedSize) ? (int) unpPackedSize
+ : count;
+ retCode = inputStream.read(addr, offset, readSize);
+ if (retCode < 0) {
+ throw new EOFException();
+ }
+ if (subHead.isSplitAfter()) {
+ packedCRC = RarCRC.checkCrc((int) packedCRC, addr, offset,
+ retCode);
+ }
+
+ curUnpRead += retCode;
+ totalRead += retCode;
+ offset += retCode;
+ count -= retCode;
+ unpPackedSize -= retCode;
+ archive.bytesReadRead(retCode);
+ if (unpPackedSize == 0 && subHead.isSplitAfter()) {
+ Volume nextVolume = archive.getVolumeManager().nextArchive(
+ archive, archive.getVolume());
+ if (nextVolume == null) {
+ nextVolumeMissing = true;
+ return -1;
+ }
+
+ FileHeader hd = this.getSubHeader();
+ if (hd.getUnpVersion() >= 20 && hd.getFileCRC() != 0xffffffff
+ && this.getPackedCRC() != ~hd.getFileCRC()) {
+ throw new RarException(RarExceptionType.crcError);
+ }
+ UnrarCallback callback = archive.getUnrarCallback();
+ if ((callback != null)
+ && !callback.isNextVolumeReady(nextVolume)) {
+ return -1;
+ }
+ archive.setVolume(nextVolume);
+ hd = archive.nextFileHeader();
+ if (hd == null) {
+ return -1;
+ }
+ this.init(hd);
+ } else {
+ break;
+ }
+ }
+
+ if (retCode != -1) {
+ retCode = totalRead;
+ }
+ return retCode;
+
+ }
+
+ public void unpWrite(byte[] addr, int offset, int count) throws IOException {
+ if (!testMode) {
+ // DestFile->Write(Addr,Count);
+ outputStream.write(addr, offset, count);
+ }
+
+ curUnpWrite += count;
+
+ if (!skipUnpCRC) {
+ if (archive.isOldFormat()) {
+ unpFileCRC = RarCRC
+ .checkOldCrc((short) unpFileCRC, addr, count);
+ } else {
+ unpFileCRC = RarCRC.checkCrc((int) unpFileCRC, addr, offset,
+ count);
+ }
+ }
+ // if (!skipArcCRC) {
+ // archive.updateDataCRC(Addr, offset, ReadSize);
+ // }
+ }
+
+ public void setPackedSizeToRead(long size) {
+ unpPackedSize = size;
+ }
+
+ public void setTestMode(boolean mode) {
+ testMode = mode;
+ }
+
+ public void setSkipUnpCRC(boolean skip) {
+ skipUnpCRC = skip;
+ }
+
+ public void setSubHeader(FileHeader hd) {
+ subHead = hd;
+
+ }
+
+ public long getCurPackRead() {
+ return curPackRead;
+ }
+
+ public void setCurPackRead(long curPackRead) {
+ this.curPackRead = curPackRead;
+ }
+
+ public long getCurPackWrite() {
+ return curPackWrite;
+ }
+
+ public void setCurPackWrite(long curPackWrite) {
+ this.curPackWrite = curPackWrite;
+ }
+
+ public long getCurUnpRead() {
+ return curUnpRead;
+ }
+
+ public void setCurUnpRead(long curUnpRead) {
+ this.curUnpRead = curUnpRead;
+ }
+
+ public long getCurUnpWrite() {
+ return curUnpWrite;
+ }
+
+ public void setCurUnpWrite(long curUnpWrite) {
+ this.curUnpWrite = curUnpWrite;
+ }
+
+ public int getDecryption() {
+ return decryption;
+ }
+
+ public void setDecryption(int decryption) {
+ this.decryption = decryption;
+ }
+
+ public int getEncryption() {
+ return encryption;
+ }
+
+ public void setEncryption(int encryption) {
+ this.encryption = encryption;
+ }
+
+ public boolean isNextVolumeMissing() {
+ return nextVolumeMissing;
+ }
+
+ public void setNextVolumeMissing(boolean nextVolumeMissing) {
+ this.nextVolumeMissing = nextVolumeMissing;
+ }
+
+ public long getPackedCRC() {
+ return packedCRC;
+ }
+
+ public void setPackedCRC(long packedCRC) {
+ this.packedCRC = packedCRC;
+ }
+
+ public long getPackFileCRC() {
+ return packFileCRC;
+ }
+
+ public void setPackFileCRC(long packFileCRC) {
+ this.packFileCRC = packFileCRC;
+ }
+
+ public boolean isPackVolume() {
+ return packVolume;
+ }
+
+ public void setPackVolume(boolean packVolume) {
+ this.packVolume = packVolume;
+ }
+
+ public long getProcessedArcSize() {
+ return processedArcSize;
+ }
+
+ public void setProcessedArcSize(long processedArcSize) {
+ this.processedArcSize = processedArcSize;
+ }
+
+ public long getTotalArcSize() {
+ return totalArcSize;
+ }
+
+ public void setTotalArcSize(long totalArcSize) {
+ this.totalArcSize = totalArcSize;
+ }
+
+ public long getTotalPackRead() {
+ return totalPackRead;
+ }
+
+ public void setTotalPackRead(long totalPackRead) {
+ this.totalPackRead = totalPackRead;
+ }
+
+ public long getUnpArcSize() {
+ return unpArcSize;
+ }
+
+ public void setUnpArcSize(long unpArcSize) {
+ this.unpArcSize = unpArcSize;
+ }
+
+ public long getUnpFileCRC() {
+ return unpFileCRC;
+ }
+
+ public void setUnpFileCRC(long unpFileCRC) {
+ this.unpFileCRC = unpFileCRC;
+ }
+
+ public boolean isUnpVolume() {
+ return unpVolume;
+ }
+
+ public void setUnpVolume(boolean unpVolume) {
+ this.unpVolume = unpVolume;
+ }
+
+ public FileHeader getSubHeader() {
+ return subHead;
+ }
+
+ // public void setEncryption(int method, char[] Password, byte[] Salt,
+ // boolean encrypt, boolean handsOffHash)
+ // {
+ //
+ // }
+ //
+ // public void setAV15Encryption()
+ // {
+ //
+ // }
+ //
+ // public void setCmt13Encryption()
+ // {
+ //
+ // }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack.java
new file mode 100644
index 0000000..fdf5eb4
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack.java
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+
+import com.github.junrar.exception.RarException;
+import com.github.junrar.unpack.decode.Compress;
+import com.github.junrar.unpack.ppm.BlockTypes;
+import com.github.junrar.unpack.ppm.ModelPPM;
+import com.github.junrar.unpack.ppm.SubAllocator;
+import com.github.junrar.unpack.vm.BitInput;
+import com.github.junrar.unpack.vm.RarVM;
+import com.github.junrar.unpack.vm.VMPreparedProgram;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public final class Unpack extends Unpack20 {
+
+ private final ModelPPM ppm = new ModelPPM();
+
+ private int ppmEscChar;
+
+ private RarVM rarVM = new RarVM();
+
+ /* Filters code, one entry per filter */
+ private List<UnpackFilter> filters = new ArrayList<UnpackFilter>();
+
+ /* Filters stack, several entrances of same filter are possible */
+ private List<UnpackFilter> prgStack = new ArrayList<UnpackFilter>();
+
+ /*
+ * lengths of preceding blocks, one length per filter. Used to reduce size
+ * required to write block length if lengths are repeating
+ */
+ private List<Integer> oldFilterLengths = new ArrayList<Integer>();
+
+ private int lastFilter;
+
+ private boolean tablesRead;
+
+ private byte[] unpOldTable = new byte[Compress.HUFF_TABLE_SIZE];
+
+ private BlockTypes unpBlockType;
+
+ private boolean externalWindow;
+
+ private long writtenFileSize;
+
+ private boolean fileExtracted;
+
+ private boolean ppmError;
+
+ private int prevLowDist;
+
+ private int lowDistRepCount;
+
+ public static int[] DBitLengthCounts = { 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 14, 0, 12 };
+
+ public Unpack(ComprDataIO DataIO) {
+ unpIO = DataIO;
+ window = null;
+ externalWindow = false;
+ suspended = false;
+ unpAllBuf = false;
+ unpSomeRead = false;
+ }
+
+ public void init(byte[] window) {
+ if (window == null) {
+ this.window = new byte[Compress.MAXWINSIZE];
+ } else {
+ this.window = window;
+ externalWindow = true;
+ }
+ inAddr = 0;
+ unpInitData(false);
+ }
+
+ public void doUnpack(int method, boolean solid) throws IOException,
+ RarException {
+ if (unpIO.getSubHeader().getUnpMethod() == 0x30) {
+ unstoreFile();
+ }
+ switch (method) {
+ case 15: // rar 1.5 compression
+ unpack15(solid);
+ break;
+ case 20: // rar 2.x compression
+ case 26: // files larger than 2GB
+ unpack20(solid);
+ break;
+ case 29: // rar 3.x compression
+ case 36: // alternative hash
+ unpack29(solid);
+ break;
+ }
+ }
+
+ private void unstoreFile() throws IOException, RarException {
+ byte[] buffer = new byte[0x10000];
+ while (true) {
+ int code = unpIO.unpRead(buffer, 0, (int) Math.min(buffer.length,
+ destUnpSize));
+ if (code == 0 || code == -1)
+ break;
+ code = code < destUnpSize ? code : (int) destUnpSize;
+ unpIO.unpWrite(buffer, 0, code);
+ if (destUnpSize >= 0)
+ destUnpSize -= code;
+ }
+
+ }
+
+ private void unpack29(boolean solid) throws IOException, RarException {
+
+ int[] DDecode = new int[Compress.DC];
+ byte[] DBits = new byte[Compress.DC];
+
+ int Bits;
+
+ if (DDecode[1] == 0) {
+ int Dist = 0, BitLength = 0, Slot = 0;
+ for (int I = 0; I < DBitLengthCounts.length; I++, BitLength++) {
+ int count = DBitLengthCounts[I];
+ for (int J = 0; J < count; J++, Slot++, Dist += (1 << BitLength)) {
+ DDecode[Slot] = Dist;
+ DBits[Slot] = (byte) BitLength;
+ }
+ }
+ }
+
+ fileExtracted = true;
+
+ if (!suspended) {
+ unpInitData(solid);
+ if (!unpReadBuf()) {
+ return;
+ }
+ if ((!solid || !tablesRead) && !readTables()) {
+ return;
+ }
+ }
+
+ if (ppmError) {
+ return;
+ }
+
+ while (true) {
+ unpPtr &= Compress.MAXWINMASK;
+
+ if (inAddr > readBorder) {
+ if (!unpReadBuf()) {
+ break;
+ }
+ }
+ // System.out.println(((wrPtr - unpPtr) &
+ // Compress.MAXWINMASK)+":"+wrPtr+":"+unpPtr);
+ if (((wrPtr - unpPtr) & Compress.MAXWINMASK) < 260
+ && wrPtr != unpPtr) {
+
+ UnpWriteBuf();
+ if (writtenFileSize > destUnpSize) {
+ return;
+ }
+ if (suspended) {
+ fileExtracted = false;
+ return;
+ }
+ }
+ if (unpBlockType == BlockTypes.BLOCK_PPM) {
+ int Ch = ppm.decodeChar();
+ if (Ch == -1) {
+ ppmError = true;
+ break;
+ }
+ if (Ch == ppmEscChar) {
+ int NextCh = ppm.decodeChar();
+ if (NextCh == 0) {
+ if (!readTables()) {
+ break;
+ }
+ continue;
+ }
+ if (NextCh == 2 || NextCh == -1) {
+ break;
+ }
+ if (NextCh == 3) {
+ if (!readVMCodePPM()) {
+ break;
+ }
+ continue;
+ }
+ if (NextCh == 4) {
+ int Distance = 0, Length = 0;
+ boolean failed = false;
+ for (int I = 0; I < 4 && !failed; I++) {
+ int ch = ppm.decodeChar();
+ if (ch == -1) {
+ failed = true;
+ } else {
+ if (I == 3) {
+ // Bug fixed
+ Length = ch & 0xff;
+ } else {
+ // Bug fixed
+ Distance = (Distance << 8) + (ch & 0xff);
+ }
+ }
+ }
+ if (failed) {
+ break;
+ }
+ copyString(Length + 32, Distance + 2);
+ continue;
+ }
+ if (NextCh == 5) {
+ int Length = ppm.decodeChar();
+ if (Length == -1) {
+ break;
+ }
+ copyString(Length + 4, 1);
+ continue;
+ }
+ }
+ window[unpPtr++] = (byte) Ch;
+ continue;
+ }
+
+ int Number = decodeNumber(LD);
+ if (Number < 256) {
+ window[unpPtr++] = (byte) Number;
+ continue;
+ }
+ if (Number >= 271) {
+ int Length = LDecode[Number -= 271] + 3;
+ if ((Bits = LBits[Number]) > 0) {
+ Length += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+
+ int DistNumber = decodeNumber(DD);
+ int Distance = DDecode[DistNumber] + 1;
+ if ((Bits = DBits[DistNumber]) > 0) {
+ if (DistNumber > 9) {
+ if (Bits > 4) {
+ Distance += ((getbits() >>> (20 - Bits)) << 4);
+ addbits(Bits - 4);
+ }
+ if (lowDistRepCount > 0) {
+ lowDistRepCount--;
+ Distance += prevLowDist;
+ } else {
+ int LowDist = decodeNumber(LDD);
+ if (LowDist == 16) {
+ lowDistRepCount = Compress.LOW_DIST_REP_COUNT - 1;
+ Distance += prevLowDist;
+ } else {
+ Distance += LowDist;
+ prevLowDist = LowDist;
+ }
+ }
+ } else {
+ Distance += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+ }
+
+ if (Distance >= 0x2000) {
+ Length++;
+ if (Distance >= 0x40000L) {
+ Length++;
+ }
+ }
+
+ insertOldDist(Distance);
+ insertLastMatch(Length, Distance);
+
+ copyString(Length, Distance);
+ continue;
+ }
+ if (Number == 256) {
+ if (!readEndOfBlock()) {
+ break;
+ }
+ continue;
+ }
+ if (Number == 257) {
+ if (!readVMCode()) {
+ break;
+ }
+ continue;
+ }
+ if (Number == 258) {
+ if (lastLength != 0) {
+ copyString(lastLength, lastDist);
+ }
+ continue;
+ }
+ if (Number < 263) {
+ int DistNum = Number - 259;
+ int Distance = oldDist[DistNum];
+ for (int I = DistNum; I > 0; I--) {
+ oldDist[I] = oldDist[I - 1];
+ }
+ oldDist[0] = Distance;
+
+ int LengthNumber = decodeNumber(RD);
+ int Length = LDecode[LengthNumber] + 2;
+ if ((Bits = LBits[LengthNumber]) > 0) {
+ Length += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+ insertLastMatch(Length, Distance);
+ copyString(Length, Distance);
+ continue;
+ }
+ if (Number < 272) {
+ int Distance = SDDecode[Number -= 263] + 1;
+ if ((Bits = SDBits[Number]) > 0) {
+ Distance += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+ insertOldDist(Distance);
+ insertLastMatch(2, Distance);
+ copyString(2, Distance);
+ continue;
+ }
+ }
+ UnpWriteBuf();
+
+ }
+
+ private void UnpWriteBuf() throws IOException {
+ int WrittenBorder = wrPtr;
+ int WriteSize = (unpPtr - WrittenBorder) & Compress.MAXWINMASK;
+ for (int I = 0; I < prgStack.size(); I++) {
+ UnpackFilter flt = prgStack.get(I);
+ if (flt == null) {
+ continue;
+ }
+ if (flt.isNextWindow()) {
+ flt.setNextWindow(false);// ->NextWindow=false;
+ continue;
+ }
+ int BlockStart = flt.getBlockStart();// ->BlockStart;
+ int BlockLength = flt.getBlockLength();// ->BlockLength;
+ if (((BlockStart - WrittenBorder) & Compress.MAXWINMASK) < WriteSize) {
+ if (WrittenBorder != BlockStart) {
+ UnpWriteArea(WrittenBorder, BlockStart);
+ WrittenBorder = BlockStart;
+ WriteSize = (unpPtr - WrittenBorder) & Compress.MAXWINMASK;
+ }
+ if (BlockLength <= WriteSize) {
+ int BlockEnd = (BlockStart + BlockLength)
+ & Compress.MAXWINMASK;
+ if (BlockStart < BlockEnd || BlockEnd == 0) {
+ // VM.SetMemory(0,Window+BlockStart,BlockLength);
+ rarVM.setMemory(0, window, BlockStart, BlockLength);
+ } else {
+ int FirstPartLength = Compress.MAXWINSIZE - BlockStart;
+ // VM.SetMemory(0,Window+BlockStart,FirstPartLength);
+ rarVM.setMemory(0, window, BlockStart, FirstPartLength);
+ // VM.SetMemory(FirstPartLength,Window,BlockEnd);
+ rarVM.setMemory(FirstPartLength, window, 0, BlockEnd);
+
+ }
+
+ VMPreparedProgram ParentPrg = filters.get(
+ flt.getParentFilter()).getPrg();
+ VMPreparedProgram Prg = flt.getPrg();
+
+ if (ParentPrg.getGlobalData().size() > RarVM.VM_FIXEDGLOBALSIZE) {
+ // copy global data from previous script execution if
+ // any
+ // Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size());
+ // memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
+ Prg.getGlobalData().setSize(
+ ParentPrg.getGlobalData().size());
+ for (int i = 0; i < ParentPrg.getGlobalData().size()
+ - RarVM.VM_FIXEDGLOBALSIZE; i++) {
+ Prg.getGlobalData().set(
+ RarVM.VM_FIXEDGLOBALSIZE + i,
+ ParentPrg.getGlobalData().get(
+ RarVM.VM_FIXEDGLOBALSIZE + i));
+ }
+ }
+
+ ExecuteCode(Prg);
+
+ if (Prg.getGlobalData().size() > RarVM.VM_FIXEDGLOBALSIZE) {
+ // save global data for next script execution
+ if (ParentPrg.getGlobalData().size() < Prg
+ .getGlobalData().size()) {
+ ParentPrg.getGlobalData().setSize(
+ Prg.getGlobalData().size());// ->GlobalData.Alloc(Prg->GlobalData.Size());
+ }
+ // memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
+ for (int i = 0; i < Prg.getGlobalData().size()
+ - RarVM.VM_FIXEDGLOBALSIZE; i++) {
+ ParentPrg.getGlobalData().set(
+ RarVM.VM_FIXEDGLOBALSIZE + i,
+ Prg.getGlobalData().get(
+ RarVM.VM_FIXEDGLOBALSIZE + i));
+ }
+ } else {
+ ParentPrg.getGlobalData().clear();
+ }
+
+ int FilteredDataOffset = Prg.getFilteredDataOffset();
+ int FilteredDataSize = Prg.getFilteredDataSize();
+ byte[] FilteredData = new byte[FilteredDataSize];
+
+ for (int i = 0; i < FilteredDataSize; i++) {
+ FilteredData[i] = rarVM.getMem()[FilteredDataOffset + i];// Prg.getGlobalData().get(FilteredDataOffset
+ // +
+ // i);
+ }
+
+ prgStack.set(I, null);
+ while (I + 1 < prgStack.size()) {
+ UnpackFilter NextFilter = prgStack.get(I + 1);
+ if (NextFilter == null
+ || NextFilter.getBlockStart() != BlockStart
+ || NextFilter.getBlockLength() != FilteredDataSize
+ || NextFilter.isNextWindow()) {
+ break;
+ }
+ // apply several filters to same data block
+
+ rarVM.setMemory(0, FilteredData, 0, FilteredDataSize);// .SetMemory(0,FilteredData,FilteredDataSize);
+
+ VMPreparedProgram pPrg = filters.get(
+ NextFilter.getParentFilter()).getPrg();
+ VMPreparedProgram NextPrg = NextFilter.getPrg();
+
+ if (pPrg.getGlobalData().size() > RarVM.VM_FIXEDGLOBALSIZE) {
+ // copy global data from previous script execution
+ // if any
+ // NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size());
+ NextPrg.getGlobalData().setSize(
+ pPrg.getGlobalData().size());
+ // memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
+ for (int i = 0; i < pPrg.getGlobalData().size()
+ - RarVM.VM_FIXEDGLOBALSIZE; i++) {
+ NextPrg.getGlobalData().set(
+ RarVM.VM_FIXEDGLOBALSIZE + i,
+ pPrg.getGlobalData().get(
+ RarVM.VM_FIXEDGLOBALSIZE + i));
+ }
+ }
+
+ ExecuteCode(NextPrg);
+
+ if (NextPrg.getGlobalData().size() > RarVM.VM_FIXEDGLOBALSIZE) {
+ // save global data for next script execution
+ if (pPrg.getGlobalData().size() < NextPrg
+ .getGlobalData().size()) {
+ pPrg.getGlobalData().setSize(
+ NextPrg.getGlobalData().size());
+ }
+ // memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
+ for (int i = 0; i < NextPrg.getGlobalData().size()
+ - RarVM.VM_FIXEDGLOBALSIZE; i++) {
+ pPrg.getGlobalData().set(
+ RarVM.VM_FIXEDGLOBALSIZE + i,
+ NextPrg.getGlobalData().get(
+ RarVM.VM_FIXEDGLOBALSIZE + i));
+ }
+ } else {
+ pPrg.getGlobalData().clear();
+ }
+ FilteredDataOffset = NextPrg.getFilteredDataOffset();
+ FilteredDataSize = NextPrg.getFilteredDataSize();
+
+ FilteredData = new byte[FilteredDataSize];
+ for (int i = 0; i < FilteredDataSize; i++) {
+ FilteredData[i] = NextPrg.getGlobalData().get(
+ FilteredDataOffset + i);
+ }
+
+ I++;
+ prgStack.set(I, null);
+ }
+ unpIO.unpWrite(FilteredData, 0, FilteredDataSize);
+ unpSomeRead = true;
+ writtenFileSize += FilteredDataSize;
+ WrittenBorder = BlockEnd;
+ WriteSize = (unpPtr - WrittenBorder) & Compress.MAXWINMASK;
+ } else {
+ for (int J = I; J < prgStack.size(); J++) {
+ UnpackFilter filt = prgStack.get(J);
+ if (filt != null && filt.isNextWindow()) {
+ filt.setNextWindow(false);
+ }
+ }
+ wrPtr = WrittenBorder;
+ return;
+ }
+ }
+ }
+
+ UnpWriteArea(WrittenBorder, unpPtr);
+ wrPtr = unpPtr;
+
+ }
+
+ private void UnpWriteArea(int startPtr, int endPtr) throws IOException {
+ if (endPtr != startPtr) {
+ unpSomeRead = true;
+ }
+ if (endPtr < startPtr) {
+ UnpWriteData(window, startPtr, -startPtr & Compress.MAXWINMASK);
+ UnpWriteData(window, 0, endPtr);
+ unpAllBuf = true;
+ } else {
+ UnpWriteData(window, startPtr, endPtr - startPtr);
+ }
+ }
+
+ private void UnpWriteData(byte[] data, int offset, int size)
+ throws IOException {
+ if (writtenFileSize >= destUnpSize) {
+ return;
+ }
+ int writeSize = size;
+ long leftToWrite = destUnpSize - writtenFileSize;
+ if (writeSize > leftToWrite) {
+ writeSize = (int) leftToWrite;
+ }
+ unpIO.unpWrite(data, offset, writeSize);
+
+ writtenFileSize += size;
+
+ }
+
+ private void insertOldDist(int distance) {
+ oldDist[3] = oldDist[2];
+ oldDist[2] = oldDist[1];
+ oldDist[1] = oldDist[0];
+ oldDist[0] = distance;
+ }
+
+ private void insertLastMatch(int length, int distance) {
+ lastDist = distance;
+ lastLength = length;
+ }
+
+ private void copyString(int length, int distance) {
+ // System.out.println("copyString(" + length + ", " + distance + ")");
+
+ int destPtr = unpPtr - distance;
+ // System.out.println(unpPtr+":"+distance);
+ if (destPtr >= 0 && destPtr < Compress.MAXWINSIZE - 260
+ && unpPtr < Compress.MAXWINSIZE - 260) {
+
+ window[unpPtr++] = window[destPtr++];
+
+ while (--length > 0)
+
+ window[unpPtr++] = window[destPtr++];
+ } else
+ while (length-- != 0) {
+ window[unpPtr] = window[destPtr++ & Compress.MAXWINMASK];
+ unpPtr = (unpPtr + 1) & Compress.MAXWINMASK;
+ }
+ }
+
+ protected void unpInitData(boolean solid) {
+ if (!solid) {
+ tablesRead = false;
+ Arrays.fill(oldDist, 0); // memset(oldDist,0,sizeof(OldDist));
+
+ oldDistPtr = 0;
+ lastDist = 0;
+ lastLength = 0;
+
+ Arrays.fill(unpOldTable, (byte) 0);// memset(UnpOldTable,0,sizeof(UnpOldTable));
+
+ unpPtr = 0;
+ wrPtr = 0;
+ ppmEscChar = 2;
+
+ initFilters();
+ }
+ InitBitInput();
+ ppmError = false;
+ writtenFileSize = 0;
+ readTop = 0;
+ readBorder = 0;
+ unpInitData20(solid);
+ }
+
+ private void initFilters() {
+ oldFilterLengths.clear();
+ lastFilter = 0;
+
+ filters.clear();
+
+ prgStack.clear();
+ }
+
+ private boolean readEndOfBlock() throws IOException, RarException {
+ int BitField = getbits();
+ boolean NewTable, NewFile = false;
+ if ((BitField & 0x8000) != 0) {
+ NewTable = true;
+ addbits(1);
+ } else {
+ NewFile = true;
+ NewTable = (BitField & 0x4000) != 0 ? true : false;
+ addbits(2);
+ }
+ tablesRead = !NewTable;
+ return !(NewFile || NewTable && !readTables());
+ }
+
+ private boolean readTables() throws IOException, RarException {
+ byte[] bitLength = new byte[Compress.BC];
+
+ byte[] table = new byte[Compress.HUFF_TABLE_SIZE];
+ if (inAddr > readTop - 25) {
+ if (!unpReadBuf()) {
+ return (false);
+ }
+ }
+ faddbits((8 - inBit) & 7);
+ long bitField = fgetbits() & 0xffFFffFF;
+ if ((bitField & 0x8000) != 0) {
+ unpBlockType = BlockTypes.BLOCK_PPM;
+ return (ppm.decodeInit(this, ppmEscChar));
+ }
+ unpBlockType = BlockTypes.BLOCK_LZ;
+
+ prevLowDist = 0;
+ lowDistRepCount = 0;
+
+ if ((bitField & 0x4000) == 0) {
+ Arrays.fill(unpOldTable, (byte) 0);// memset(UnpOldTable,0,sizeof(UnpOldTable));
+ }
+ faddbits(2);
+
+ for (int i = 0; i < Compress.BC; i++) {
+ int length = (fgetbits() >>> 12) & 0xFF;
+ faddbits(4);
+ if (length == 15) {
+ int zeroCount = (fgetbits() >>> 12) & 0xFF;
+ faddbits(4);
+ if (zeroCount == 0) {
+ bitLength[i] = 15;
+ } else {
+ zeroCount += 2;
+ while (zeroCount-- > 0 && i < bitLength.length) {
+ bitLength[i++] = 0;
+ }
+ i--;
+ }
+ } else {
+ bitLength[i] = (byte) length;
+ }
+ }
+
+ makeDecodeTables(bitLength, 0, BD, Compress.BC);
+
+ int TableSize = Compress.HUFF_TABLE_SIZE;
+
+ for (int i = 0; i < TableSize;) {
+ if (inAddr > readTop - 5) {
+ if (!unpReadBuf()) {
+ return (false);
+ }
+ }
+ int Number = decodeNumber(BD);
+ if (Number < 16) {
+ table[i] = (byte) ((Number + unpOldTable[i]) & 0xf);
+ i++;
+ } else if (Number < 18) {
+ int N;
+ if (Number == 16) {
+ N = (fgetbits() >>> 13) + 3;
+ faddbits(3);
+ } else {
+ N = (fgetbits() >>> 9) + 11;
+ faddbits(7);
+ }
+ while (N-- > 0 && i < TableSize) {
+ table[i] = table[i - 1];
+ i++;
+ }
+ } else {
+ int N;
+ if (Number == 18) {
+ N = (fgetbits() >>> 13) + 3;
+ faddbits(3);
+ } else {
+ N = (fgetbits() >>> 9) + 11;
+ faddbits(7);
+ }
+ while (N-- > 0 && i < TableSize) {
+ table[i++] = 0;
+ }
+ }
+ }
+ tablesRead = true;
+ if (inAddr > readTop) {
+ return (false);
+ }
+ makeDecodeTables(table, 0, LD, Compress.NC);
+ makeDecodeTables(table, Compress.NC, DD, Compress.DC);
+ makeDecodeTables(table, Compress.NC + Compress.DC, LDD, Compress.LDC);
+ makeDecodeTables(table, Compress.NC + Compress.DC + Compress.LDC, RD,
+ Compress.RC);
+
+ // memcpy(unpOldTable,table,sizeof(unpOldTable));
+ for (int i = 0; i < unpOldTable.length; i++) {
+ unpOldTable[i] = table[i];
+ }
+ return (true);
+
+ }
+
+ private boolean readVMCode() throws IOException, RarException {
+ int FirstByte = getbits() >> 8;
+ addbits(8);
+ int Length = (FirstByte & 7) + 1;
+ if (Length == 7) {
+ Length = (getbits() >> 8) + 7;
+ addbits(8);
+ } else if (Length == 8) {
+ Length = getbits();
+ addbits(16);
+ }
+ List<Byte> vmCode = new ArrayList<Byte>();
+ for (int I = 0; I < Length; I++) {
+ if (inAddr >= readTop - 1 && !unpReadBuf() && I < Length - 1) {
+ return (false);
+ }
+ vmCode.add(Byte.valueOf((byte) (getbits() >> 8)));
+ addbits(8);
+ }
+ return (addVMCode(FirstByte, vmCode, Length));
+ }
+
+ private boolean readVMCodePPM() throws IOException, RarException {
+ int FirstByte = ppm.decodeChar();
+ if ((int) FirstByte == -1) {
+ return (false);
+ }
+ int Length = (FirstByte & 7) + 1;
+ if (Length == 7) {
+ int B1 = ppm.decodeChar();
+ if (B1 == -1) {
+ return (false);
+ }
+ Length = B1 + 7;
+ } else if (Length == 8) {
+ int B1 = ppm.decodeChar();
+ if (B1 == -1) {
+ return (false);
+ }
+ int B2 = ppm.decodeChar();
+ if (B2 == -1) {
+ return (false);
+ }
+ Length = B1 * 256 + B2;
+ }
+ List<Byte> vmCode = new ArrayList<Byte>();
+ for (int I = 0; I < Length; I++) {
+ int Ch = ppm.decodeChar();
+ if (Ch == -1) {
+ return (false);
+ }
+ vmCode.add(Byte.valueOf((byte) Ch));// VMCode[I]=Ch;
+ }
+ return (addVMCode(FirstByte, vmCode, Length));
+ }
+
+ private boolean addVMCode(int firstByte, List<Byte> vmCode, int length) {
+ BitInput Inp = new BitInput();
+ Inp.InitBitInput();
+ // memcpy(Inp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize));
+ for (int i = 0; i < Math.min(BitInput.MAX_SIZE, vmCode.size()); i++) {
+ Inp.getInBuf()[i] = vmCode.get(i);
+ }
+ rarVM.init();
+
+ int FiltPos;
+ if ((firstByte & 0x80) != 0) {
+ FiltPos = RarVM.ReadData(Inp);
+ if (FiltPos == 0) {
+ initFilters();
+ } else {
+ FiltPos--;
+ }
+ } else
+ FiltPos = lastFilter; // use the same filter as last time
+
+ if (FiltPos > filters.size() || FiltPos > oldFilterLengths.size()) {
+ return (false);
+ }
+ lastFilter = FiltPos;
+ boolean NewFilter = (FiltPos == filters.size());
+
+ UnpackFilter StackFilter = new UnpackFilter(); // new filter for
+ // PrgStack
+
+ UnpackFilter Filter;
+ if (NewFilter) // new filter code, never used before since VM reset
+ {
+ // too many different filters, corrupt archive
+ if (FiltPos > 1024) {
+ return (false);
+ }
+
+ // Filters[Filters.Size()-1]=Filter=new UnpackFilter;
+ Filter = new UnpackFilter();
+ filters.add(Filter);
+ StackFilter.setParentFilter(filters.size() - 1);
+ oldFilterLengths.add(0);
+ Filter.setExecCount(0);
+ } else // filter was used in the past
+ {
+ Filter = filters.get(FiltPos);
+ StackFilter.setParentFilter(FiltPos);
+ Filter.setExecCount(Filter.getExecCount() + 1);// ->ExecCount++;
+ }
+
+ prgStack.add(StackFilter);
+ StackFilter.setExecCount(Filter.getExecCount());// ->ExecCount;
+
+ int BlockStart = RarVM.ReadData(Inp);
+ if ((firstByte & 0x40) != 0) {
+ BlockStart += 258;
+ }
+ StackFilter.setBlockStart((BlockStart + unpPtr) & Compress.MAXWINMASK);
+ if ((firstByte & 0x20) != 0) {
+ StackFilter.setBlockLength(RarVM.ReadData(Inp));
+ } else {
+ StackFilter
+ .setBlockLength(FiltPos < oldFilterLengths.size() ? oldFilterLengths
+ .get(FiltPos)
+ : 0);
+ }
+ StackFilter.setNextWindow((wrPtr != unpPtr)
+ && ((wrPtr - unpPtr) & Compress.MAXWINMASK) <= BlockStart);
+
+ // DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x
+ // BlockStart=%08x",UnpPtr,WrPtr,BlockStart);
+
+ oldFilterLengths.set(FiltPos, StackFilter.getBlockLength());
+
+ // memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR));
+ Arrays.fill(StackFilter.getPrg().getInitR(), 0);
+ StackFilter.getPrg().getInitR()[3] = RarVM.VM_GLOBALMEMADDR;// StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR;
+ StackFilter.getPrg().getInitR()[4] = StackFilter.getBlockLength();// StackFilter->Prg.InitR[4]=StackFilter->BlockLength;
+ StackFilter.getPrg().getInitR()[5] = StackFilter.getExecCount();// StackFilter->Prg.InitR[5]=StackFilter->ExecCount;
+
+ if ((firstByte & 0x10) != 0) // set registers to optional parameters
+ // if any
+ {
+ int InitMask = Inp.fgetbits() >>> 9;
+ Inp.faddbits(7);
+ for (int I = 0; I < 7; I++) {
+ if ((InitMask & (1 << I)) != 0) {
+ // StackFilter->Prg.InitR[I]=RarVM::ReadData(Inp);
+ StackFilter.getPrg().getInitR()[I] = RarVM.ReadData(Inp);
+ }
+ }
+ }
+
+ if (NewFilter) {
+ int VMCodeSize = RarVM.ReadData(Inp);
+ if (VMCodeSize >= 0x10000 || VMCodeSize == 0) {
+ return (false);
+ }
+ byte[] VMCode = new byte[VMCodeSize];
+ for (int I = 0; I < VMCodeSize; I++) {
+ if (Inp.Overflow(3)) {
+ return (false);
+ }
+ VMCode[I] = (byte) (Inp.fgetbits() >> 8);
+ Inp.faddbits(8);
+ }
+ // VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg);
+ rarVM.prepare(VMCode, VMCodeSize, Filter.getPrg());
+ }
+ StackFilter.getPrg().setAltCmd(Filter.getPrg().getCmd());// StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0];
+ StackFilter.getPrg().setCmdCount(Filter.getPrg().getCmdCount());// StackFilter->Prg.CmdCount=Filter->Prg.CmdCount;
+
+ int StaticDataSize = Filter.getPrg().getStaticData().size();
+ if (StaticDataSize > 0 && StaticDataSize < RarVM.VM_GLOBALMEMSIZE) {
+ // read statically defined data contained in DB commands
+ // StackFilter->Prg.StaticData.Add(StaticDataSize);
+ StackFilter.getPrg().setStaticData(Filter.getPrg().getStaticData());
+ // memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize);
+ }
+
+ if (StackFilter.getPrg().getGlobalData().size() < RarVM.VM_FIXEDGLOBALSIZE) {
+ // StackFilter->Prg.GlobalData.Reset();
+ // StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE);
+ StackFilter.getPrg().getGlobalData().clear();
+ StackFilter.getPrg().getGlobalData().setSize(
+ RarVM.VM_FIXEDGLOBALSIZE);
+ }
+
+ // byte *GlobalData=&StackFilter->Prg.GlobalData[0];
+ Vector<Byte> globalData = StackFilter.getPrg().getGlobalData();
+ for (int I = 0; I < 7; I++) {
+ rarVM.setLowEndianValue(globalData, I * 4, StackFilter.getPrg()
+ .getInitR()[I]);
+ }
+
+ // VM.SetLowEndianValue((uint
+ // *)&GlobalData[0x1c],StackFilter->BlockLength);
+ rarVM.setLowEndianValue(globalData, 0x1c, StackFilter.getBlockLength());
+ // VM.SetLowEndianValue((uint *)&GlobalData[0x20],0);
+ rarVM.setLowEndianValue(globalData, 0x20, 0);
+ rarVM.setLowEndianValue(globalData, 0x24, 0);
+ rarVM.setLowEndianValue(globalData, 0x28, 0);
+
+ // VM.SetLowEndianValue((uint
+ // *)&GlobalData[0x2c],StackFilter->ExecCount);
+ rarVM.setLowEndianValue(globalData, 0x2c, StackFilter.getExecCount());
+ // memset(&GlobalData[0x30],0,16);
+ for (int i = 0; i < 16; i++) {
+ globalData.set(0x30 + i, Byte.valueOf((byte) (0)));
+ }
+ if ((firstByte & 8) != 0) // put data block passed as parameter if any
+ {
+ if (Inp.Overflow(3)) {
+ return (false);
+ }
+ int DataSize = RarVM.ReadData(Inp);
+ if (DataSize > RarVM.VM_GLOBALMEMSIZE - RarVM.VM_FIXEDGLOBALSIZE) {
+ return (false);
+ }
+ int CurSize = StackFilter.getPrg().getGlobalData().size();
+ if (CurSize < DataSize + RarVM.VM_FIXEDGLOBALSIZE) {
+ // StackFilter->Prg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize);
+ StackFilter.getPrg().getGlobalData().setSize(
+ DataSize + RarVM.VM_FIXEDGLOBALSIZE - CurSize);
+ }
+ int offset = RarVM.VM_FIXEDGLOBALSIZE;
+ globalData = StackFilter.getPrg().getGlobalData();
+ for (int I = 0; I < DataSize; I++) {
+ if (Inp.Overflow(3)) {
+ return (false);
+ }
+ globalData.set(offset + I, Byte
+ .valueOf((byte) (Inp.fgetbits() >>> 8)));
+ Inp.faddbits(8);
+ }
+ }
+ return (true);
+ }
+
+ private void ExecuteCode(VMPreparedProgram Prg) {
+ if (Prg.getGlobalData().size() > 0) {
+ // Prg->InitR[6]=int64to32(WrittenFileSize);
+ Prg.getInitR()[6] = (int) (writtenFileSize);
+ // rarVM.SetLowEndianValue((uint
+ // *)&Prg->GlobalData[0x24],int64to32(WrittenFileSize));
+ rarVM.setLowEndianValue(Prg.getGlobalData(), 0x24,
+ (int) writtenFileSize);
+ // rarVM.SetLowEndianValue((uint
+ // *)&Prg->GlobalData[0x28],int64to32(WrittenFileSize>>32));
+ rarVM.setLowEndianValue(Prg.getGlobalData(), 0x28,
+ (int) (writtenFileSize >>> 32));
+ rarVM.execute(Prg);
+ }
+ }
+
+ // Duplicate method
+ // private boolean ReadEndOfBlock() throws IOException, RarException
+ // {
+ // int BitField = getbits();
+ // boolean NewTable, NewFile = false;
+ // if ((BitField & 0x8000) != 0) {
+ // NewTable = true;
+ // addbits(1);
+ // } else {
+ // NewFile = true;
+ // NewTable = (BitField & 0x4000) != 0;
+ // addbits(2);
+ // }
+ // tablesRead = !NewTable;
+ // return !(NewFile || NewTable && !readTables());
+ // }
+
+ public boolean isFileExtracted() {
+ return fileExtracted;
+ }
+
+ public void setDestSize(long destSize) {
+ this.destUnpSize = destSize;
+ this.fileExtracted = false;
+ }
+
+ public void setSuspended(boolean suspended) {
+ this.suspended = suspended;
+ }
+
+ public int getChar() throws IOException, RarException {
+ if (inAddr > BitInput.MAX_SIZE - 30) {
+ unpReadBuf();
+ }
+ return (inBuf[inAddr++] & 0xff);
+ }
+
+ public int getPpmEscChar() {
+ return ppmEscChar;
+ }
+
+ public void setPpmEscChar(int ppmEscChar) {
+ this.ppmEscChar = ppmEscChar;
+ }
+
+ public void cleanUp() {
+ if (ppm != null) {
+ SubAllocator allocator = ppm.getSubAlloc();
+ if (allocator != null) {
+ allocator.stopSubAllocator();
+ }
+ }
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack15.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack15.java
new file mode 100644
index 0000000..d365cc6
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack15.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 21.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import com.github.junrar.exception.RarException;
+import com.github.junrar.unpack.decode.Compress;
+import com.github.junrar.unpack.vm.BitInput;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public abstract class Unpack15 extends BitInput
+{
+
+ protected int readBorder;
+
+ protected boolean suspended;
+
+ protected boolean unpAllBuf;
+
+ protected ComprDataIO unpIO;
+
+ protected boolean unpSomeRead;
+
+ protected int readTop;
+
+ protected long destUnpSize;
+
+ protected byte[] window;
+
+ protected int[] oldDist = new int[4];
+
+ protected int unpPtr, wrPtr;
+
+ protected int oldDistPtr;
+
+ protected int[] ChSet = new int[256], ChSetA = new int[256],
+ ChSetB = new int[256], ChSetC = new int[256];
+
+ protected int[] Place = new int[256], PlaceA = new int[256],
+ PlaceB = new int[256], PlaceC = new int[256];
+
+ protected int[] NToPl = new int[256], NToPlB = new int[256],
+ NToPlC = new int[256];
+
+ protected int FlagBuf, AvrPlc, AvrPlcB, AvrLn1, AvrLn2, AvrLn3;
+
+ protected int Buf60, NumHuf, StMode, LCount, FlagsCnt;
+
+ protected int Nhfb, Nlzb, MaxDist3;
+
+ protected int lastDist, lastLength;
+
+ private static final int STARTL1 = 2;
+
+ private static int DecL1[] = { 0x8000, 0xa000, 0xc000, 0xd000, 0xe000,
+ 0xea00, 0xee00, 0xf000, 0xf200, 0xf200, 0xffff };
+
+ private static int PosL1[] = { 0, 0, 0, 2, 3, 5, 7, 11, 16, 20, 24, 32, 32 };
+
+ private static final int STARTL2 = 3;
+
+ private static int DecL2[] = { 0xa000, 0xc000, 0xd000, 0xe000, 0xea00,
+ 0xee00, 0xf000, 0xf200, 0xf240, 0xffff };
+
+ private static int PosL2[] = { 0, 0, 0, 0, 5, 7, 9, 13, 18, 22, 26, 34, 36 };
+
+ private static final int STARTHF0 = 4;
+
+ private static int DecHf0[] = { 0x8000, 0xc000, 0xe000, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xffff };
+
+ private static int PosHf0[] = { 0, 0, 0, 0, 0, 8, 16, 24, 33, 33, 33, 33,
+ 33 };
+
+ private static final int STARTHF1 = 5;
+
+ private static int DecHf1[] = { 0x2000, 0xc000, 0xe000, 0xf000, 0xf200,
+ 0xf200, 0xf7e0, 0xffff };
+
+ private static int PosHf1[] = { 0, 0, 0, 0, 0, 0, 4, 44, 60, 76, 80, 80,
+ 127 };
+
+ private static final int STARTHF2 = 5;
+
+ private static int DecHf2[] = { 0x1000, 0x2400, 0x8000, 0xc000, 0xfa00,
+ 0xffff, 0xffff, 0xffff };
+
+ private static int PosHf2[] = { 0, 0, 0, 0, 0, 0, 2, 7, 53, 117, 233, 0, 0 };
+
+ private static final int STARTHF3 = 6;
+
+ private static int DecHf3[] = { 0x800, 0x2400, 0xee00, 0xfe80, 0xffff,
+ 0xffff, 0xffff };
+
+ private static int PosHf3[] = { 0, 0, 0, 0, 0, 0, 0, 2, 16, 218, 251, 0, 0 };
+
+ private static final int STARTHF4 = 8;
+
+ private static int DecHf4[] = { 0xff00, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff };
+
+ private static int PosHf4[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0 };
+
+ static int ShortLen1[] = { 1, 3, 4, 4, 5, 6, 7, 8, 8, 4, 4, 5, 6, 6, 4, 0 };
+
+ static int ShortXor1[] = { 0, 0xa0, 0xd0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe,
+ 0xff, 0xc0, 0x80, 0x90, 0x98, 0x9c, 0xb0 };
+
+ static int ShortLen2[] = { 2, 3, 3, 3, 4, 4, 5, 6, 6, 4, 4, 5, 6, 6, 4, 0 };
+
+ static int ShortXor2[] = { 0, 0x40, 0x60, 0xa0, 0xd0, 0xe0, 0xf0, 0xf8,
+ 0xfc, 0xc0, 0x80, 0x90, 0x98, 0x9c, 0xb0 };
+
+ protected abstract void unpInitData(boolean solid);
+
+ protected void unpack15(boolean solid) throws IOException, RarException
+ {
+ if (suspended) {
+ unpPtr = wrPtr;
+ } else {
+ unpInitData(solid);
+ oldUnpInitData(solid);
+ unpReadBuf();
+ if (!solid) {
+ initHuff();
+ unpPtr = 0;
+ } else {
+ unpPtr = wrPtr;
+ }
+ --destUnpSize;
+ }
+ if (destUnpSize >= 0) {
+ getFlagsBuf();
+ FlagsCnt = 8;
+ }
+
+ while (destUnpSize >= 0) {
+ unpPtr &= Compress.MAXWINMASK;
+
+ if (inAddr > readTop - 30 && !unpReadBuf()) {
+ break;
+ }
+ if (((wrPtr - unpPtr) & Compress.MAXWINMASK) < 270
+ && wrPtr != unpPtr) {
+ oldUnpWriteBuf();
+ if (suspended) {
+ return;
+ }
+ }
+ if (StMode != 0) {
+ huffDecode();
+ continue;
+ }
+
+ if (--FlagsCnt < 0) {
+ getFlagsBuf();
+ FlagsCnt = 7;
+ }
+
+ if ((FlagBuf & 0x80) != 0) {
+ FlagBuf <<= 1;
+ if (Nlzb > Nhfb) {
+ longLZ();
+ } else {
+ huffDecode();
+ }
+ } else {
+ FlagBuf <<= 1;
+ if (--FlagsCnt < 0) {
+ getFlagsBuf();
+ FlagsCnt = 7;
+ }
+ if ((FlagBuf & 0x80) != 0) {
+ FlagBuf <<= 1;
+ if (Nlzb > Nhfb) {
+ huffDecode();
+ } else {
+ longLZ();
+ }
+ } else {
+ FlagBuf <<= 1;
+ shortLZ();
+ }
+ }
+ }
+ oldUnpWriteBuf();
+ }
+
+
+
+ protected boolean unpReadBuf() throws IOException, RarException
+ {
+ int dataSize=readTop-inAddr;
+ if (dataSize<0){
+ return(false);
+ }
+ if (inAddr>BitInput.MAX_SIZE/2) {
+ if (dataSize>0){
+ //memmove(InBuf,InBuf+InAddr,DataSize);
+// for (int i = 0; i < dataSize; i++) {
+// inBuf[i] = inBuf[inAddr + i];
+// }
+ System.arraycopy(inBuf, inAddr, inBuf, 0, dataSize);
+ }
+ inAddr=0;
+ readTop=dataSize;
+ }
+ else{
+ dataSize=readTop;
+ }
+ //int readCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf);
+ int readCode=unpIO.unpRead(inBuf, dataSize, (BitInput.MAX_SIZE-dataSize)&~0xf);
+ if (readCode>0){
+ readTop+=readCode;
+ }
+ readBorder=readTop-30;
+ return(readCode!=-1);
+ }
+
+ private int getShortLen1(int pos)
+ {
+ return pos == 1 ? Buf60 + 3 : ShortLen1[pos];
+ }
+
+ private int getShortLen2(int pos)
+ {
+ return pos == 3 ? Buf60 + 3 : ShortLen2[pos];
+ }
+
+ protected void shortLZ()
+ {
+ int Length, SaveLength;
+ int LastDistance;
+ int Distance;
+ int DistancePlace;
+ NumHuf = 0;
+
+ int BitField = fgetbits();
+ if (LCount == 2) {
+ faddbits(1);
+ if (BitField >= 0x8000) {
+ oldCopyString(lastDist, lastLength);
+ return;
+ }
+ BitField <<= 1;
+ LCount = 0;
+ }
+ BitField >>>= 8;
+ if (AvrLn1 < 37) {
+ for (Length = 0;; Length++) {
+ if (((BitField ^ ShortXor1[Length]) & (~(0xff >>> getShortLen1(Length)))) == 0) {
+ break;
+ }
+ }
+ faddbits(getShortLen1(Length));
+ } else {
+ for (Length = 0;; Length++) {
+ if (((BitField ^ ShortXor2[Length]) & (~(0xff >> getShortLen2(Length)))) == 0) {
+ break;
+ }
+ }
+ faddbits(getShortLen2(Length));
+ }
+
+ if (Length >= 9) {
+ if (Length == 9) {
+ LCount++;
+ oldCopyString(lastDist, lastLength);
+ return;
+ }
+ if (Length == 14) {
+ LCount = 0;
+ Length = decodeNum(fgetbits(), STARTL2, DecL2, PosL2) + 5;
+ Distance = (fgetbits() >> 1) | 0x8000;
+ faddbits(15);
+ lastLength = Length;
+ lastDist = Distance;
+ oldCopyString(Distance, Length);
+ return;
+ }
+
+ LCount = 0;
+ SaveLength = Length;
+ Distance = oldDist[(oldDistPtr - (Length - 9)) & 3];
+ Length = decodeNum(fgetbits(), STARTL1, DecL1, PosL1) + 2;
+ if (Length == 0x101 && SaveLength == 10) {
+ Buf60 ^= 1;
+ return;
+ }
+ if (Distance > 256)
+ Length++;
+ if (Distance >= MaxDist3)
+ Length++;
+
+ oldDist[oldDistPtr++] = Distance;
+ oldDistPtr = oldDistPtr & 3;
+ lastLength = Length;
+ lastDist = Distance;
+ oldCopyString(Distance, Length);
+ return;
+ }
+
+ LCount = 0;
+ AvrLn1 += Length;
+ AvrLn1 -= AvrLn1 >> 4;
+
+ DistancePlace = decodeNum(fgetbits(), STARTHF2, DecHf2, PosHf2) & 0xff;
+ Distance = ChSetA[DistancePlace];
+ if (--DistancePlace != -1) {
+ PlaceA[Distance]--;
+ LastDistance = ChSetA[DistancePlace];
+ PlaceA[LastDistance]++;
+ ChSetA[DistancePlace + 1] = LastDistance;
+ ChSetA[DistancePlace] = Distance;
+ }
+ Length += 2;
+ oldDist[oldDistPtr++] = ++Distance;
+ oldDistPtr = oldDistPtr & 3;
+ lastLength = Length;
+ lastDist = Distance;
+ oldCopyString(Distance, Length);
+ }
+
+ protected void longLZ()
+ {
+ int Length;
+ int Distance;
+ int DistancePlace, NewDistancePlace;
+ int OldAvr2, OldAvr3;
+
+ NumHuf = 0;
+ Nlzb += 16;
+ if (Nlzb > 0xff) {
+ Nlzb = 0x90;
+ Nhfb >>>= 1;
+ }
+ OldAvr2 = AvrLn2;
+
+ int BitField = fgetbits();
+ if (AvrLn2 >= 122) {
+ Length = decodeNum(BitField, STARTL2, DecL2, PosL2);
+ } else {
+ if (AvrLn2 >= 64) {
+ Length = decodeNum(BitField, STARTL1, DecL1, PosL1);
+ } else {
+ if (BitField < 0x100) {
+ Length = BitField;
+ faddbits(16);
+ } else {
+ for (Length = 0; ((BitField << Length) & 0x8000) == 0; Length++) {
+ ;
+ }
+ faddbits(Length + 1);
+ }
+ }
+ }
+ AvrLn2 += Length;
+ AvrLn2 -= AvrLn2 >>> 5;
+
+ BitField = fgetbits();
+ if (AvrPlcB > 0x28ff) {
+ DistancePlace = decodeNum(BitField, STARTHF2, DecHf2, PosHf2);
+ } else {
+ if (AvrPlcB > 0x6ff) {
+ DistancePlace = decodeNum(BitField, STARTHF1, DecHf1, PosHf1);
+ } else {
+ DistancePlace = decodeNum(BitField, STARTHF0, DecHf0, PosHf0);
+ }
+ }
+ AvrPlcB += DistancePlace;
+ AvrPlcB -= AvrPlcB >> 8;
+ while (true) {
+ Distance = ChSetB[DistancePlace & 0xff];
+ NewDistancePlace = NToPlB[Distance++ & 0xff]++;
+ if ((Distance & 0xff) == 0) {
+ corrHuff(ChSetB, NToPlB);
+ } else {
+ break;
+ }
+ }
+
+ ChSetB[DistancePlace] = ChSetB[NewDistancePlace];
+ ChSetB[NewDistancePlace] = Distance;
+
+ Distance = ((Distance & 0xff00) | (fgetbits() >>> 8)) >>> 1;
+ faddbits(7);
+
+ OldAvr3 = AvrLn3;
+ if (Length != 1 && Length != 4) {
+ if (Length == 0 && Distance <= MaxDist3) {
+ AvrLn3++;
+ AvrLn3 -= AvrLn3 >> 8;
+ } else {
+ if (AvrLn3 > 0) {
+ AvrLn3--;
+ }
+ }
+ }
+ Length += 3;
+ if (Distance >= MaxDist3) {
+ Length++;
+ }
+ if (Distance <= 256) {
+ Length += 8;
+ }
+ if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) {
+ MaxDist3 = 0x7f00;
+ } else {
+ MaxDist3 = 0x2001;
+ }
+ oldDist[oldDistPtr++] = Distance;
+ oldDistPtr = oldDistPtr & 3;
+ lastLength = Length;
+ lastDist = Distance;
+ oldCopyString(Distance, Length);
+ }
+
+ protected void huffDecode()
+ {
+ int CurByte, NewBytePlace;
+ int Length;
+ int Distance;
+ int BytePlace;
+
+ int BitField = fgetbits();
+
+ if (AvrPlc > 0x75ff) {
+ BytePlace = decodeNum(BitField, STARTHF4, DecHf4, PosHf4);
+ } else {
+ if (AvrPlc > 0x5dff) {
+ BytePlace = decodeNum(BitField, STARTHF3, DecHf3, PosHf3);
+ } else {
+ if (AvrPlc > 0x35ff) {
+ BytePlace = decodeNum(BitField, STARTHF2, DecHf2, PosHf2);
+ } else {
+ if (AvrPlc > 0x0dff) {
+ BytePlace = decodeNum(BitField, STARTHF1, DecHf1,
+ PosHf1);
+ } else {
+ BytePlace = decodeNum(BitField, STARTHF0, DecHf0,
+ PosHf0);
+ }
+ }
+ }
+ }
+ BytePlace &= 0xff;
+ if (StMode != 0) {
+ if (BytePlace == 0 && BitField > 0xfff) {
+ BytePlace = 0x100;
+ }
+ if (--BytePlace == -1) {
+ BitField = fgetbits();
+ faddbits(1);
+ if ((BitField & 0x8000) != 0) {
+ NumHuf = StMode = 0;
+ return;
+ } else {
+ Length = (BitField & 0x4000) != 0 ? 4 : 3;
+ faddbits(1);
+ Distance = decodeNum(fgetbits(), STARTHF2, DecHf2, PosHf2);
+ Distance = (Distance << 5) | (fgetbits() >>> 11);
+ faddbits(5);
+ oldCopyString(Distance, Length);
+ return;
+ }
+ }
+ } else {
+ if (NumHuf++ >= 16 && FlagsCnt == 0) {
+ StMode = 1;
+ }
+ }
+ AvrPlc += BytePlace;
+ AvrPlc -= AvrPlc >>> 8;
+ Nhfb += 16;
+ if (Nhfb > 0xff) {
+ Nhfb = 0x90;
+ Nlzb >>>= 1;
+ }
+
+ window[unpPtr++] = (byte) (ChSet[BytePlace] >>> 8);
+ --destUnpSize;
+
+ while (true) {
+ CurByte = ChSet[BytePlace];
+ NewBytePlace = NToPl[CurByte++ & 0xff]++;
+ if ((CurByte & 0xff) > 0xa1) {
+ corrHuff(ChSet, NToPl);
+ } else {
+ break;
+ }
+ }
+
+ ChSet[BytePlace] = ChSet[NewBytePlace];
+ ChSet[NewBytePlace] = CurByte;
+ }
+
+ protected void getFlagsBuf()
+ {
+ int Flags, NewFlagsPlace;
+ int FlagsPlace = decodeNum(fgetbits(), STARTHF2, DecHf2, PosHf2);
+
+ while (true) {
+ Flags = ChSetC[FlagsPlace];
+ FlagBuf = Flags >>> 8;
+ NewFlagsPlace = NToPlC[Flags++ & 0xff]++;
+ if ((Flags & 0xff) != 0) {
+ break;
+ }
+ corrHuff(ChSetC, NToPlC);
+ }
+
+ ChSetC[FlagsPlace] = ChSetC[NewFlagsPlace];
+ ChSetC[NewFlagsPlace] = Flags;
+ }
+
+ protected void oldUnpInitData(boolean Solid)
+ {
+ if (!Solid ) {
+ AvrPlcB = AvrLn1 = AvrLn2 = AvrLn3 = NumHuf = Buf60 = 0;
+ AvrPlc = 0x3500;
+ MaxDist3 = 0x2001;
+ Nhfb = Nlzb = 0x80;
+ }
+ FlagsCnt = 0;
+ FlagBuf = 0;
+ StMode = 0;
+ LCount = 0;
+ readTop = 0;
+ }
+
+ protected void initHuff()
+ {
+ for (int I = 0; I < 256; I++) {
+ Place[I] = PlaceA[I] = PlaceB[I] = I;
+ PlaceC[I] = (~I + 1) & 0xff;
+ ChSet[I] = ChSetB[I] = I << 8;
+ ChSetA[I] = I;
+ ChSetC[I] = ((~I + 1) & 0xff) << 8;
+ }
+
+ Arrays.fill(NToPl, 0);// memset(NToPl,0,sizeof(NToPl));
+ Arrays.fill(NToPlB, 0); // memset(NToPlB,0,sizeof(NToPlB));
+ Arrays.fill(NToPlC, 0); // memset(NToPlC,0,sizeof(NToPlC));
+ corrHuff(ChSetB, NToPlB);
+ }
+
+ protected void corrHuff(int[] CharSet, int[] NumToPlace)
+ {
+ int I, J, pos = 0;
+ for (I = 7; I >= 0; I--) {
+ for (J = 0; J < 32; J++, pos++) {
+ CharSet[pos] = ((CharSet[pos] & ~0xff) | I);// *CharSet=(*CharSet
+ // & ~0xff) | I;
+ }
+ }
+ Arrays.fill(NumToPlace, 0);// memset(NumToPlace,0,sizeof(NToPl));
+ for (I = 6; I >= 0; I--) {
+ NumToPlace[I] = (7 - I) * 32;
+ }
+ }
+
+ protected void oldCopyString(int Distance, int Length)
+ {
+ destUnpSize -= Length;
+ while ((Length--) != 0) {
+ window[unpPtr] = window[(unpPtr - Distance) & Compress.MAXWINMASK];
+ unpPtr = (unpPtr + 1) & Compress.MAXWINMASK;
+ }
+ }
+
+ protected int decodeNum(int Num, int StartPos, int[] DecTab, int[] PosTab)
+ {
+ int I;
+ for (Num &= 0xfff0, I = 0; DecTab[I] <= Num; I++) {
+ StartPos++;
+ }
+ faddbits(StartPos);
+ return (((Num - (I != 0 ? DecTab[I - 1] : 0)) >>> (16 - StartPos)) + PosTab[StartPos]);
+ }
+
+ protected void oldUnpWriteBuf() throws IOException
+ {
+ if (unpPtr != wrPtr) {
+ unpSomeRead = true;
+ }
+ if (unpPtr < wrPtr) {
+ unpIO.unpWrite(window, wrPtr, -wrPtr & Compress.MAXWINMASK);
+ unpIO.unpWrite(window, 0, unpPtr);
+ unpAllBuf = true;
+ } else {
+ unpIO.unpWrite(window, wrPtr, unpPtr - wrPtr);
+ }
+ wrPtr = unpPtr;
+ }
+
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack20.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack20.java
new file mode 100644
index 0000000..a9138cc
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/Unpack20.java
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 21.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import com.github.junrar.exception.RarException;
+import com.github.junrar.unpack.decode.AudioVariables;
+import com.github.junrar.unpack.decode.BitDecode;
+import com.github.junrar.unpack.decode.Compress;
+import com.github.junrar.unpack.decode.Decode;
+import com.github.junrar.unpack.decode.DistDecode;
+import com.github.junrar.unpack.decode.LitDecode;
+import com.github.junrar.unpack.decode.LowDistDecode;
+import com.github.junrar.unpack.decode.MultDecode;
+import com.github.junrar.unpack.decode.RepDecode;
+
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public abstract class Unpack20 extends Unpack15
+{
+
+ protected MultDecode[] MD = new MultDecode[4];
+
+ protected byte[] UnpOldTable20 = new byte[Compress.MC20 * 4];
+
+ protected int UnpAudioBlock, UnpChannels, UnpCurChannel, UnpChannelDelta;
+
+ protected AudioVariables[] AudV = new AudioVariables[4];
+
+ protected LitDecode LD = new LitDecode();
+
+ protected DistDecode DD = new DistDecode();
+
+ protected LowDistDecode LDD = new LowDistDecode();
+
+ protected RepDecode RD = new RepDecode();
+
+ protected BitDecode BD = new BitDecode();
+
+ public static final int[] LDecode = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12,
+ 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192,
+ 224 };
+
+ public static final byte[] LBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2,
+ 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
+
+ public static final int[] DDecode = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32,
+ 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072,
+ 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304,
+ 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824,
+ 655360, 720896, 786432, 851968, 917504, 983040 };
+
+ public static final int[] DBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5,
+ 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
+ 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 };
+
+ public static final int[] SDDecode = { 0, 4, 8, 16, 32, 64, 128, 192 };
+
+ public static final int[] SDBits = { 2, 2, 3, 4, 5, 6, 6, 6 };
+
+ protected void unpack20(boolean solid) throws IOException, RarException
+ {
+
+ int Bits;
+
+ if (suspended) {
+ unpPtr = wrPtr;
+ } else {
+ unpInitData(solid);
+ if (!unpReadBuf()) {
+ return;
+ }
+ if (!solid) {
+ if (!ReadTables20()) {
+ return;
+ }
+ }
+ --destUnpSize;
+ }
+
+ while (destUnpSize >= 0) {
+ unpPtr &= Compress.MAXWINMASK;
+
+ if (inAddr > readTop - 30)
+ if (!unpReadBuf())
+ break;
+ if (((wrPtr - unpPtr) & Compress.MAXWINMASK) < 270
+ && wrPtr != unpPtr) {
+ oldUnpWriteBuf();
+ if (suspended)
+ return;
+ }
+ if (UnpAudioBlock != 0) {
+ int AudioNumber = decodeNumber(MD[UnpCurChannel]);
+
+ if (AudioNumber == 256) {
+ if (!ReadTables20())
+ break;
+ continue;
+ }
+ window[unpPtr++] = DecodeAudio(AudioNumber);
+ if (++UnpCurChannel == UnpChannels)
+ UnpCurChannel = 0;
+ --destUnpSize;
+ continue;
+ }
+
+ int Number = decodeNumber(LD);
+ if (Number < 256) {
+ window[unpPtr++] = (byte) Number;
+ --destUnpSize;
+ continue;
+ }
+ if (Number > 269) {
+ int Length = LDecode[Number -= 270] + 3;
+ if ((Bits = LBits[Number]) > 0) {
+ Length += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+
+ int DistNumber = decodeNumber(DD);
+ int Distance = DDecode[DistNumber] + 1;
+ if ((Bits = DBits[DistNumber]) > 0) {
+ Distance += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+
+ if (Distance >= 0x2000) {
+ Length++;
+ if (Distance >= 0x40000L)
+ Length++;
+ }
+
+ CopyString20(Length, Distance);
+ continue;
+ }
+ if (Number == 269) {
+ if (!ReadTables20())
+ break;
+ continue;
+ }
+ if (Number == 256) {
+ CopyString20(lastLength, lastDist);
+ continue;
+ }
+ if (Number < 261) {
+ int Distance = oldDist[(oldDistPtr - (Number - 256)) & 3];
+ int LengthNumber = decodeNumber(RD);
+ int Length = LDecode[LengthNumber] + 2;
+ if ((Bits = LBits[LengthNumber]) > 0) {
+ Length += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+ if (Distance >= 0x101) {
+ Length++;
+ if (Distance >= 0x2000) {
+ Length++;
+ if (Distance >= 0x40000)
+ Length++;
+ }
+ }
+ CopyString20(Length, Distance);
+ continue;
+ }
+ if (Number < 270) {
+ int Distance = SDDecode[Number -= 261] + 1;
+ if ((Bits = SDBits[Number]) > 0) {
+ Distance += getbits() >>> (16 - Bits);
+ addbits(Bits);
+ }
+ CopyString20(2, Distance);
+ continue;
+ }
+ }
+ ReadLastTables();
+ oldUnpWriteBuf();
+
+ }
+
+ protected void CopyString20(int Length, int Distance)
+ {
+ lastDist = oldDist[oldDistPtr++ & 3] = Distance;
+ lastLength = Length;
+ destUnpSize -= Length;
+
+ int DestPtr = unpPtr - Distance;
+ if (DestPtr < Compress.MAXWINSIZE - 300
+ && unpPtr < Compress.MAXWINSIZE - 300) {
+ window[unpPtr++] = window[DestPtr++];
+ window[unpPtr++] = window[DestPtr++];
+ while (Length > 2) {
+ Length--;
+ window[unpPtr++] = window[DestPtr++];
+ }
+ } else {
+ while ((Length--) != 0) {
+ window[unpPtr] = window[DestPtr++ & Compress.MAXWINMASK];
+ unpPtr = (unpPtr + 1) & Compress.MAXWINMASK;
+ }
+ }
+ }
+
+ protected void makeDecodeTables(byte[] lenTab, int offset, Decode dec,
+ int size)
+ {
+ int[] lenCount = new int[16];
+ int[] tmpPos = new int[16];
+ int i;
+ long M, N;
+
+ Arrays.fill(lenCount, 0);// memset(LenCount,0,sizeof(LenCount));
+
+ Arrays.fill(dec.getDecodeNum(), 0);// memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum));
+
+ for (i = 0; i < size; i++) {
+ lenCount[(int) (lenTab[offset + i] & 0xF)]++;
+ }
+ lenCount[0] = 0;
+ for (tmpPos[0] = 0, dec.getDecodePos()[0] = 0, dec.getDecodeLen()[0] = 0, N = 0, i = 1; i < 16; i++) {
+ N = 2 * (N + lenCount[i]);
+ M = N << (15 - i);
+ if (M > 0xFFFF) {
+ M = 0xFFFF;
+ }
+ dec.getDecodeLen()[i] = (int) M;
+ tmpPos[i] = dec.getDecodePos()[i] = dec.getDecodePos()[i - 1]
+ + lenCount[i - 1];
+ }
+
+ for (i = 0; i < size; i++) {
+ if (lenTab[offset + i] != 0) {
+ dec.getDecodeNum()[tmpPos[lenTab[offset + i] & 0xF]++] = i;
+ }
+ }
+ dec.setMaxNum(size);
+ }
+
+ protected int decodeNumber(Decode dec)
+ {
+ int bits;
+ long bitField = getbits() & 0xfffe;
+// if (bitField < dec.getDecodeLen()[8]) {
+// if (bitField < dec.getDecodeLen()[4]) {
+// if (bitField < dec.getDecodeLen()[2]) {
+// if (bitField < dec.getDecodeLen()[1]) {
+// bits = 1;
+// } else {
+// bits = 2;
+// }
+// } else {
+// if (bitField < dec.getDecodeLen()[3]) {
+// bits = 3;
+// } else {
+// bits = 4;
+// }
+// }
+// } else {
+// if (bitField < dec.getDecodeLen()[6]) {
+// if (bitField < dec.getDecodeLen()[5])
+// bits = 5;
+// else
+// bits = 6;
+// } else {
+// if (bitField < dec.getDecodeLen()[7]) {
+// bits = 7;
+// } else {
+// bits = 8;
+// }
+// }
+// }
+// } else {
+// if (bitField < dec.getDecodeLen()[12]) {
+// if (bitField < dec.getDecodeLen()[10])
+// if (bitField < dec.getDecodeLen()[9])
+// bits = 9;
+// else
+// bits = 10;
+// else if (bitField < dec.getDecodeLen()[11])
+// bits = 11;
+// else
+// bits = 12;
+// } else {
+// if (bitField < dec.getDecodeLen()[14]) {
+// if (bitField < dec.getDecodeLen()[13]) {
+// bits = 13;
+// } else {
+// bits = 14;
+// }
+// } else {
+// bits = 15;
+// }
+// }
+// }
+// addbits(bits);
+// int N = dec.getDecodePos()[bits]
+// + (((int) bitField - dec.getDecodeLen()[bits - 1]) >>> (16 - bits));
+// if (N >= dec.getMaxNum()) {
+// N = 0;
+// }
+// return (dec.getDecodeNum()[N]);
+ int[] decodeLen = dec.getDecodeLen();
+ if (bitField < decodeLen[8]) {
+ if (bitField < decodeLen[4]) {
+ if (bitField < decodeLen[2]) {
+ if (bitField < decodeLen[1]) {
+ bits = 1;
+ } else {
+ bits = 2;
+ }
+ } else {
+ if (bitField < decodeLen[3]) {
+ bits = 3;
+ } else {
+ bits = 4;
+ }
+ }
+ } else {
+ if (bitField < decodeLen[6]) {
+ if (bitField < decodeLen[5])
+ bits = 5;
+ else
+ bits = 6;
+ } else {
+ if (bitField < decodeLen[7]) {
+ bits = 7;
+ } else {
+ bits = 8;
+ }
+ }
+ }
+ } else {
+ if (bitField < decodeLen[12]) {
+ if (bitField < decodeLen[10])
+ if (bitField < decodeLen[9])
+ bits = 9;
+ else
+ bits = 10;
+ else if (bitField < decodeLen[11])
+ bits = 11;
+ else
+ bits = 12;
+ } else {
+ if (bitField < decodeLen[14]) {
+ if (bitField < decodeLen[13]) {
+ bits = 13;
+ } else {
+ bits = 14;
+ }
+ } else {
+ bits = 15;
+ }
+ }
+ }
+ addbits(bits);
+ int N = dec.getDecodePos()[bits]
+ + (((int) bitField - decodeLen[bits - 1]) >>> (16 - bits));
+ if (N >= dec.getMaxNum()) {
+ N = 0;
+ }
+ return (dec.getDecodeNum()[N]);
+ }
+
+ protected boolean ReadTables20() throws IOException, RarException
+ {
+ byte[] BitLength = new byte[Compress.BC20];
+ byte[] Table = new byte[Compress.MC20 * 4];
+ int TableSize, N, I;
+ if (inAddr > readTop - 25) {
+ if (!unpReadBuf()) {
+ return (false);
+ }
+ }
+ int BitField = getbits();
+ UnpAudioBlock = (BitField & 0x8000);
+
+ if (0 == (BitField & 0x4000)) {
+ // memset(UnpOldTable20,0,sizeof(UnpOldTable20));
+ Arrays.fill(UnpOldTable20, (byte) 0);
+ }
+ addbits(2);
+
+ if (UnpAudioBlock != 0) {
+ UnpChannels = ((BitField >>> 12) & 3) + 1;
+ if (UnpCurChannel >= UnpChannels) {
+ UnpCurChannel = 0;
+ }
+ addbits(2);
+ TableSize = Compress.MC20 * UnpChannels;
+ } else {
+ TableSize = Compress.NC20 + Compress.DC20 + Compress.RC20;
+ }
+ for (I = 0; I < Compress.BC20; I++) {
+ BitLength[I] = (byte) (getbits() >>> 12);
+ addbits(4);
+ }
+ makeDecodeTables(BitLength, 0, BD, Compress.BC20);
+ I = 0;
+ while (I < TableSize) {
+ if (inAddr > readTop - 5) {
+ if (!unpReadBuf()) {
+ return (false);
+ }
+ }
+ int Number = decodeNumber(BD);
+ if (Number < 16) {
+ Table[I] = (byte) ((Number + UnpOldTable20[I]) & 0xf);
+ I++;
+ } else if (Number == 16) {
+ N = (getbits() >>> 14) + 3;
+ addbits(2);
+ while (N-- > 0 && I < TableSize) {
+ Table[I] = Table[I - 1];
+ I++;
+ }
+ } else {
+ if (Number == 17) {
+ N = (getbits() >>> 13) + 3;
+ addbits(3);
+ } else {
+ N = (getbits() >>> 9) + 11;
+ addbits(7);
+ }
+ while (N-- > 0 && I < TableSize)
+ Table[I++] = 0;
+ }
+ }
+ if (inAddr > readTop) {
+ return (true);
+ }
+ if (UnpAudioBlock != 0)
+ for (I = 0; I < UnpChannels; I++)
+ makeDecodeTables(Table, I * Compress.MC20, MD[I], Compress.MC20);
+ else {
+ makeDecodeTables(Table, 0, LD, Compress.NC20);
+ makeDecodeTables(Table, Compress.NC20, DD, Compress.DC20);
+ makeDecodeTables(Table, Compress.NC20 + Compress.DC20, RD,
+ Compress.RC20);
+ }
+ // memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20));
+ for (int i = 0; i < UnpOldTable20.length; i++) {
+ UnpOldTable20[i] = Table[i];
+ }
+ return (true);
+ }
+
+ protected void unpInitData20(boolean Solid)
+ {
+ if (!Solid) {
+ UnpChannelDelta = UnpCurChannel = 0;
+ UnpChannels = 1;
+ // memset(AudV,0,sizeof(AudV));
+ Arrays.fill(AudV, new AudioVariables());
+ // memset(UnpOldTable20,0,sizeof(UnpOldTable20));
+ Arrays.fill(UnpOldTable20, (byte) 0);
+ }
+ }
+
+ protected void ReadLastTables() throws IOException, RarException
+ {
+ if (readTop >= inAddr + 5) {
+ if (UnpAudioBlock != 0) {
+ if (decodeNumber(MD[UnpCurChannel]) == 256) {
+ ReadTables20();
+ }
+ } else {
+ if (decodeNumber(LD) == 269) {
+ ReadTables20();
+ }
+ }
+ }
+ }
+
+ protected byte DecodeAudio(int Delta)
+ {
+ AudioVariables v = AudV[UnpCurChannel];
+ v.setByteCount(v.getByteCount() + 1);
+ v.setD4(v.getD3());
+ v.setD3(v.getD2());// ->D3=V->D2;
+ v.setD2(v.getLastDelta() - v.getD1());// ->D2=V->LastDelta-V->D1;
+ v.setD1(v.getLastDelta());// V->D1=V->LastDelta;
+ // int PCh=8*V->LastChar+V->K1*V->D1 +V->K2*V->D2 +V->K3*V->D3
+ // +V->K4*V->D4+ V->K5*UnpChannelDelta;
+ int PCh = 8 * v.getLastChar() + v.getK1() * v.getD1();
+ PCh += v.getK2() * v.getD2() + v.getK3() * v.getD3();
+ PCh += v.getK4() * v.getD4() + v.getK5() * UnpChannelDelta;
+ PCh = (PCh >>> 3) & 0xFF;
+
+ int Ch = PCh - Delta;
+
+ int D = ((byte) Delta) << 3;
+
+ v.getDif()[0] += Math.abs(D);// V->Dif[0]+=abs(D);
+ v.getDif()[1] += Math.abs(D - v.getD1());// V->Dif[1]+=abs(D-V->D1);
+ v.getDif()[2] += Math.abs(D + v.getD1());// V->Dif[2]+=abs(D+V->D1);
+ v.getDif()[3] += Math.abs(D - v.getD2());// V->Dif[3]+=abs(D-V->D2);
+ v.getDif()[4] += Math.abs(D + v.getD2());// V->Dif[4]+=abs(D+V->D2);
+ v.getDif()[5] += Math.abs(D - v.getD3());// V->Dif[5]+=abs(D-V->D3);
+ v.getDif()[6] += Math.abs(D + v.getD3());// V->Dif[6]+=abs(D+V->D3);
+ v.getDif()[7] += Math.abs(D - v.getD4());// V->Dif[7]+=abs(D-V->D4);
+ v.getDif()[8] += Math.abs(D + v.getD4());// V->Dif[8]+=abs(D+V->D4);
+ v.getDif()[9] += Math.abs(D - UnpChannelDelta);// V->Dif[9]+=abs(D-UnpChannelDelta);
+ v.getDif()[10] += Math.abs(D + UnpChannelDelta);// V->Dif[10]+=abs(D+UnpChannelDelta);
+
+ v.setLastDelta((byte) (Ch - v.getLastChar()));
+ UnpChannelDelta = v.getLastDelta();
+ v.setLastChar(Ch);// V->LastChar=Ch;
+
+ if ((v.getByteCount() & 0x1F) == 0) {
+ int MinDif = v.getDif()[0], NumMinDif = 0;
+ v.getDif()[0] = 0;// ->Dif[0]=0;
+ for (int I = 1; I < v.getDif().length; I++) {
+ if (v.getDif()[I] < MinDif) {
+ MinDif = v.getDif()[I];
+ NumMinDif = I;
+ }
+ v.getDif()[I] = 0;
+ }
+ switch (NumMinDif) {
+ case 1:
+ if (v.getK1() >= -16) {
+ v.setK1(v.getK1() - 1);// V->K1--;
+ }
+ break;
+ case 2:
+ if (v.getK1() < 16) {
+ v.setK1(v.getK1() + 1);// V->K1++;
+ }
+ break;
+ case 3:
+ if (v.getK2() >= -16) {
+ v.setK2(v.getK2() - 1);// V->K2--;
+ }
+ break;
+ case 4:
+ if (v.getK2() < 16) {
+ v.setK2(v.getK2() + 1);// V->K2++;
+ }
+ break;
+ case 5:
+ if (v.getK3() >= -16) {
+ v.setK3(v.getK3() - 1);
+ }
+ break;
+ case 6:
+ if (v.getK3() < 16) {
+ v.setK3(v.getK3() + 1);
+ }
+ break;
+ case 7:
+ if (v.getK4() >= -16) {
+ v.setK4(v.getK4() - 1);
+ }
+ break;
+ case 8:
+ if (v.getK4() < 16) {
+ v.setK4(v.getK4() + 1);
+ }
+ break;
+ case 9:
+ if (v.getK5() >= -16) {
+ v.setK5(v.getK5() - 1);
+ }
+ break;
+ case 10:
+ if (v.getK5() < 16) {
+ v.setK5(v.getK5() + 1);
+ }
+ break;
+ }
+ }
+ return ((byte) Ch);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/UnpackFilter.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/UnpackFilter.java
new file mode 100644
index 0000000..29ed99a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/UnpackFilter.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack;
+
+import com.github.junrar.unpack.vm.VMPreparedProgram;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class UnpackFilter {
+
+ private int BlockStart;
+
+ private int BlockLength;
+
+ private int ExecCount;
+
+ private boolean NextWindow;
+
+ // position of parent filter in Filters array used as prototype for filter
+ // in PrgStack array. Not defined for filters in Filters array.
+ private int ParentFilter;
+
+ private VMPreparedProgram Prg = new VMPreparedProgram();
+
+ public int getBlockLength() {
+ return BlockLength;
+ }
+
+ public void setBlockLength(int blockLength) {
+ BlockLength = blockLength;
+ }
+
+ public int getBlockStart() {
+ return BlockStart;
+ }
+
+ public void setBlockStart(int blockStart) {
+ BlockStart = blockStart;
+ }
+
+ public int getExecCount() {
+ return ExecCount;
+ }
+
+ public void setExecCount(int execCount) {
+ ExecCount = execCount;
+ }
+
+ public boolean isNextWindow() {
+ return NextWindow;
+ }
+
+ public void setNextWindow(boolean nextWindow) {
+ NextWindow = nextWindow;
+ }
+
+ public int getParentFilter() {
+ return ParentFilter;
+ }
+
+ public void setParentFilter(int parentFilter) {
+ ParentFilter = parentFilter;
+ }
+
+ public VMPreparedProgram getPrg() {
+ return Prg;
+ }
+
+ public void setPrg(VMPreparedProgram prg) {
+ Prg = prg;
+ }
+
+
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/AudioVariables.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/AudioVariables.java
new file mode 100644
index 0000000..7310663
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/AudioVariables.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class AudioVariables {
+ int k1, k2, k3, k4, k5;
+
+ int d1, d2, d3, d4;
+
+ int lastDelta;
+
+ int dif[] = new int[11];
+
+ int byteCount;
+
+ int lastChar;
+
+ public int getByteCount() {
+ return byteCount;
+ }
+
+ public void setByteCount(int byteCount) {
+ this.byteCount = byteCount;
+ }
+
+ public int getD1() {
+ return d1;
+ }
+
+ public void setD1(int d1) {
+ this.d1 = d1;
+ }
+
+ public int getD2() {
+ return d2;
+ }
+
+ public void setD2(int d2) {
+ this.d2 = d2;
+ }
+
+ public int getD3() {
+ return d3;
+ }
+
+ public void setD3(int d3) {
+ this.d3 = d3;
+ }
+
+ public int getD4() {
+ return d4;
+ }
+
+ public void setD4(int d4) {
+ this.d4 = d4;
+ }
+
+ public int[] getDif() {
+ return dif;
+ }
+
+ public void setDif(int[] dif) {
+ this.dif = dif;
+ }
+
+ public int getK1() {
+ return k1;
+ }
+
+ public void setK1(int k1) {
+ this.k1 = k1;
+ }
+
+ public int getK2() {
+ return k2;
+ }
+
+ public void setK2(int k2) {
+ this.k2 = k2;
+ }
+
+ public int getK3() {
+ return k3;
+ }
+
+ public void setK3(int k3) {
+ this.k3 = k3;
+ }
+
+ public int getK4() {
+ return k4;
+ }
+
+ public void setK4(int k4) {
+ this.k4 = k4;
+ }
+
+ public int getK5() {
+ return k5;
+ }
+
+ public void setK5(int k5) {
+ this.k5 = k5;
+ }
+
+ public int getLastChar() {
+ return lastChar;
+ }
+
+ public void setLastChar(int lastChar) {
+ this.lastChar = lastChar;
+ }
+
+ public int getLastDelta() {
+ return lastDelta;
+ }
+
+ public void setLastDelta(int lastDelta) {
+ this.lastDelta = lastDelta;
+ }
+
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/BitDecode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/BitDecode.java
new file mode 100644
index 0000000..453d90a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/BitDecode.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class BitDecode extends Decode
+{
+ /**
+ *
+ */
+ public BitDecode()
+ {
+ decodeNum = new int[Compress.BC];
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/CodeType.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/CodeType.java
new file mode 100644
index 0000000..047dc0c
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/CodeType.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum CodeType {
+ CODE_HUFFMAN,CODE_LZ,CODE_LZ2,CODE_REPEATLZ,CODE_CACHELZ,
+ CODE_STARTFILE,CODE_ENDFILE,CODE_VM,CODE_VMDATA;
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Compress.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Compress.java
new file mode 100644
index 0000000..12bf342
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Compress.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class Compress {
+ public static final int CODEBUFSIZE = 0x4000;
+ public static final int MAXWINSIZE = 0x400000;
+ public static final int MAXWINMASK = (MAXWINSIZE-1);
+
+ public static final int LOW_DIST_REP_COUNT = 16;
+
+ public static final int NC = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
+ public static final int DC = 60;
+ public static final int LDC = 17;
+ public static final int RC = 28;
+ public static final int HUFF_TABLE_SIZE = (NC+DC+RC+LDC);
+ public static final int BC = 20;
+
+ public static final int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */
+ public static final int DC20 = 48;
+ public static final int RC20 = 28;
+ public static final int BC20 = 19;
+ public static final int MC20 = 257;
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Decode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Decode.java
new file mode 100644
index 0000000..2d1b9b6
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/Decode.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * Used to store information for lz decoding
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class Decode
+{
+ private int maxNum;
+
+ private final int[] decodeLen = new int[16];
+
+ private final int[] decodePos = new int[16];
+
+ protected int[] decodeNum = new int[2];
+
+ /**
+ * returns the decode Length array
+ * @return decodeLength
+ */
+ public int[] getDecodeLen()
+ {
+ return decodeLen;
+ }
+
+ /**
+ * returns the decode num array
+ * @return decodeNum
+ */
+ public int[] getDecodeNum()
+ {
+ return decodeNum;
+ }
+
+ /**
+ * returns the decodePos array
+ * @return decodePos
+ */
+ public int[] getDecodePos()
+ {
+ return decodePos;
+ }
+
+ /**
+ * returns the max num
+ * @return maxNum
+ */
+ public int getMaxNum()
+ {
+ return maxNum;
+ }
+
+ /**
+ * sets the max num
+ * @param maxNum to be set to maxNum
+ */
+ public void setMaxNum(int maxNum)
+ {
+ this.maxNum = maxNum;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/DistDecode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/DistDecode.java
new file mode 100644
index 0000000..d52d767
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/DistDecode.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class DistDecode extends Decode
+{
+
+ /**
+ *
+ */
+ public DistDecode()
+ {
+ decodeNum = new int[Compress.DC];
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/FilterType.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/FilterType.java
new file mode 100644
index 0000000..d0cbfcc
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/FilterType.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum FilterType {
+ FILTER_NONE, FILTER_PPM /*dummy*/, FILTER_E8, FILTER_E8E9,
+ FILTER_UPCASETOLOW, FILTER_AUDIO, FILTER_RGB, FILTER_DELTA,
+ FILTER_ITANIUM, FILTER_E8E9V2;
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LitDecode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LitDecode.java
new file mode 100644
index 0000000..563566f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LitDecode.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class LitDecode extends Decode
+{
+ /**
+ *
+ */
+ public LitDecode()
+ {
+ decodeNum = new int[Compress.NC];
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LowDistDecode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LowDistDecode.java
new file mode 100644
index 0000000..837eb77
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/LowDistDecode.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class LowDistDecode extends Decode
+{
+
+ /**
+ *
+ */
+ public LowDistDecode()
+ {
+ decodeNum = new int[Compress.LDC];
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/MultDecode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/MultDecode.java
new file mode 100644
index 0000000..95db9cf
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/MultDecode.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class MultDecode extends Decode
+{
+
+ /**
+ *
+ */
+ public MultDecode()
+ {
+ decodeNum = new int[Compress.MC20];
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/RepDecode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/RepDecode.java
new file mode 100644
index 0000000..02f7ef4
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/decode/RepDecode.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.decode;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class RepDecode extends Decode
+{
+ /**
+ *
+ */
+ public RepDecode()
+ {
+ decodeNum = new int[Compress.RC];
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/AnalyzeHeapDump.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/AnalyzeHeapDump.java
new file mode 100644
index 0000000..634fc92
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/AnalyzeHeapDump.java
@@ -0,0 +1,94 @@
+package com.github.junrar.unpack.ppm;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * For debugging purposes only.
+ *
+ * @author alban
+ */
+public class AnalyzeHeapDump {
+
+ /** Creates a new instance of AnalyzeHeapDump */
+ public AnalyzeHeapDump() {
+ }
+
+ public static void main(String[] argv) {
+ File cfile = new File("P:\\test\\heapdumpc");
+ File jfile = new File("P:\\test\\heapdumpj");
+ if (!cfile.exists()) {
+ System.err.println("File not found: " + cfile.getAbsolutePath());
+ return;
+ }
+ if (!jfile.exists()) {
+ System.err.println("File not found: " + jfile.getAbsolutePath());
+ return;
+ }
+ long clen = cfile.length();
+ long jlen = jfile.length();
+ if (clen != jlen) {
+ System.out.println("File size mismatch");
+ System.out.println("clen = " + clen);
+ System.out.println("jlen = " + jlen);
+ }
+ // Do byte comparison
+ long len = Math.min(clen, jlen);
+ InputStream cin = null;
+ InputStream jin = null;
+ int bufferLen = 256*1024;
+ try {
+ cin = new BufferedInputStream(
+ new FileInputStream(cfile), bufferLen);
+ jin = new BufferedInputStream(
+ new FileInputStream(jfile), bufferLen);
+ boolean matching = true;
+ boolean mismatchFound = false;
+ long startOff = 0L;
+ long off = 0L;
+ while (off < len) {
+ if (cin.read() != jin.read()) {
+ if (matching) {
+ startOff = off;
+ matching = false;
+ mismatchFound = true;
+ }
+ }
+ else { // match
+ if (!matching) {
+ printMismatch(startOff, off);
+ matching = true;
+ }
+ }
+ off++;
+ }
+ if (!matching) {
+ printMismatch(startOff, off);
+ }
+ if (!mismatchFound) {
+ System.out.println("Files are identical");
+ }
+ System.out.println("Done");
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ finally {
+ try {
+ cin.close();
+ jin.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static void printMismatch(long startOff, long bytesRead) {
+ System.out.println("Mismatch: off=" + startOff +
+ "(0x" + Long.toHexString(startOff) +
+ "), len=" + (bytesRead - startOff));
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/BlockTypes.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/BlockTypes.java
new file mode 100644
index 0000000..ffe7c61
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/BlockTypes.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum BlockTypes
+{
+ BLOCK_LZ(0), BLOCK_PPM(1);
+
+ private int blockType;
+
+ private BlockTypes(int blockType)
+ {
+ this.blockType = blockType;
+ }
+
+ public int getBlockType()
+ {
+ return blockType;
+ }
+
+ public boolean equals(int blockType)
+ {
+ return this.blockType == blockType;
+ }
+
+ public static BlockTypes findBlockType(int blockType)
+ {
+ if (BLOCK_LZ.equals(blockType)) {
+ return BLOCK_LZ;
+ }
+ if (BLOCK_PPM.equals(blockType)) {
+ return BLOCK_PPM;
+ }
+ return null;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/FreqData.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/FreqData.java
new file mode 100644
index 0000000..4308a85
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/FreqData.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 04.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import com.github.junrar.io.Raw;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class FreqData extends Pointer{
+
+ public static final int size = 6;
+
+// struct FreqData
+// {
+// ushort SummFreq;
+// STATE _PACK_ATTR * Stats;
+// };
+
+ public FreqData(byte[]mem){
+ super(mem);
+ }
+
+ public FreqData init(byte[] mem) {
+ this.mem = mem;
+ pos = 0;
+ return this;
+ }
+
+ public int getSummFreq() {
+ return Raw.readShortLittleEndian(mem, pos)&0xffff;
+ }
+
+ public void setSummFreq(int summFreq) {
+ Raw.writeShortLittleEndian(mem, pos, (short)summFreq);
+ }
+
+ public void incSummFreq(int dSummFreq) {
+ Raw.incShortLittleEndian(mem, pos, dSummFreq);
+ }
+
+ public int getStats() {
+ return Raw.readIntLittleEndian(mem, pos+2);
+ }
+
+ public void setStats(State state) {
+ setStats(state.getAddress());
+ }
+
+ public void setStats(int state) {
+ Raw.writeIntLittleEndian(mem, pos+2, state);
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("FreqData[");
+ buffer.append("\n pos=");
+ buffer.append(pos);
+ buffer.append("\n size=");
+ buffer.append(size);
+ buffer.append("\n summFreq=");
+ buffer.append(getSummFreq());
+ buffer.append("\n stats=");
+ buffer.append(getStats());
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/ModelPPM.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/ModelPPM.java
new file mode 100644
index 0000000..25e20bb
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/ModelPPM.java
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import com.github.junrar.exception.RarException;
+import com.github.junrar.unpack.Unpack;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class ModelPPM
+{
+ public static final int MAX_O = 64; /* maximum allowed model order */
+
+ public static final int INT_BITS = 7;
+
+ public static final int PERIOD_BITS = 7;
+
+ public static final int TOT_BITS = INT_BITS + PERIOD_BITS;
+
+ public static final int INTERVAL = 1 << INT_BITS;
+
+ public static final int BIN_SCALE = 1 << TOT_BITS;
+
+ public static final int MAX_FREQ = 124;
+
+ private SEE2Context[][] SEE2Cont = new SEE2Context[25][16];
+
+ private SEE2Context dummySEE2Cont;
+
+ private PPMContext minContext, medContext, maxContext;
+
+ private State foundState; // found next state transition
+
+ private int numMasked, initEsc, orderFall, maxOrder, runLength, initRL;
+
+ private int[] charMask = new int[256];
+
+ private int[] NS2Indx = new int[256];
+
+ private int[] NS2BSIndx = new int[256];
+
+ private int[] HB2Flag = new int[256];
+
+ // byte EscCount, PrevSuccess, HiBitsFlag;
+ private int escCount, prevSuccess, hiBitsFlag;
+
+ private int[][] binSumm = new int[128][64]; // binary SEE-contexts
+
+ private RangeCoder coder = new RangeCoder();
+
+ private SubAllocator subAlloc = new SubAllocator();
+
+ private static int InitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3,
+ 0x64A1, 0x5ABC, 0x6632, 0x6051 };
+
+ // Temp fields
+ private final State tempState1 = new State(null);
+ private final State tempState2 = new State(null);
+ private final State tempState3 = new State(null);
+ private final State tempState4 = new State(null);
+ private final StateRef tempStateRef1 = new StateRef();
+ private final StateRef tempStateRef2 = new StateRef();
+ private final PPMContext tempPPMContext1 = new PPMContext(null);
+ private final PPMContext tempPPMContext2 = new PPMContext(null);
+ private final PPMContext tempPPMContext3 = new PPMContext(null);
+ private final PPMContext tempPPMContext4 = new PPMContext(null);
+ private final int[] ps = new int[MAX_O];
+
+ public ModelPPM()
+ {
+ minContext = null;
+ maxContext = null;
+ medContext = null;
+ }
+
+ public SubAllocator getSubAlloc()
+ {
+ return subAlloc;
+ }
+
+ private void restartModelRare()
+ {
+ Arrays.fill(charMask, 0);
+ subAlloc.initSubAllocator();
+ initRL = -(maxOrder < 12 ? maxOrder : 12) - 1;
+ int addr = subAlloc.allocContext();
+ minContext.setAddress(addr);
+ maxContext.setAddress(addr);
+ minContext.setSuffix(0);
+ orderFall = maxOrder;
+ minContext.setNumStats(256);
+ minContext.getFreqData().setSummFreq(minContext.getNumStats()+1);
+
+ addr = subAlloc.allocUnits(256 / 2);
+ foundState.setAddress(addr);
+ minContext.getFreqData().setStats(addr);
+
+ State state = new State(subAlloc.getHeap());
+ addr = minContext.getFreqData().getStats();
+ runLength = initRL;
+ prevSuccess = 0;
+ for (int i = 0; i < 256; i++) {
+ state.setAddress(addr + i * State.size);
+ state.setSymbol(i);
+ state.setFreq(1);
+ state.setSuccessor(0);
+ }
+
+ for (int i = 0; i < 128; i++) {
+ for (int k = 0; k < 8; k++) {
+ for (int m = 0; m < 64; m += 8) {
+ binSumm[i][k + m] = BIN_SCALE - InitBinEsc[k] / (i + 2);
+ }
+ }
+ }
+ for (int i = 0; i < 25; i++) {
+ for (int k = 0; k < 16; k++) {
+ SEE2Cont[i][k].init(5 * i + 10);
+ }
+ }
+ }
+
+ private void startModelRare(int MaxOrder)
+ {
+ int i, k, m, Step;
+ escCount = 1;
+ this.maxOrder = MaxOrder;
+ restartModelRare();
+ // Bug Fixed
+ NS2BSIndx[0] = 0;
+ NS2BSIndx[1] = 2;
+ for (int j = 0; j < 9; j++) {
+ NS2BSIndx[2 + j] = 4;
+ }
+ for (int j = 0; j < 256 - 11; j++) {
+ NS2BSIndx[11 + j] = 6;
+ }
+ for (i = 0; i < 3; i++) {
+ NS2Indx[i] = i;
+ }
+ for (m = i, k = 1, Step = 1; i < 256; i++) {
+ NS2Indx[i] = m;
+ if ((--k) == 0) {
+ k = ++Step;
+ m++;
+ }
+ }
+ for (int j = 0; j < 0x40; j++) {
+ HB2Flag[j] = 0;
+ }
+ for (int j = 0; j < 0x100 - 0x40; j++) {
+ HB2Flag[0x40 + j] = 0x08;
+ }
+ dummySEE2Cont.setShift(PERIOD_BITS);
+
+ }
+
+ private void clearMask()
+ {
+ escCount = 1;
+ Arrays.fill(charMask, 0);
+ }
+
+ public boolean decodeInit(Unpack unpackRead, int escChar/* ref */)
+ throws IOException, RarException
+ {
+
+ int MaxOrder = unpackRead.getChar() & 0xff;
+ boolean reset = ((MaxOrder & 0x20) != 0);
+
+ int MaxMB = 0;
+ if (reset) {
+ MaxMB = unpackRead.getChar();
+ } else {
+ if (subAlloc.GetAllocatedMemory() == 0) {
+ return (false);
+ }
+ }
+ if ((MaxOrder & 0x40) != 0) {
+ escChar = unpackRead.getChar();
+ unpackRead.setPpmEscChar(escChar);
+ }
+ coder.initDecoder(unpackRead);
+ if (reset) {
+ MaxOrder = (MaxOrder & 0x1f) + 1;
+ if (MaxOrder > 16) {
+ MaxOrder = 16 + (MaxOrder - 16) * 3;
+ }
+ if (MaxOrder == 1) {
+ subAlloc.stopSubAllocator();
+ return (false);
+ }
+ subAlloc.startSubAllocator(MaxMB + 1);
+ minContext = new PPMContext(getHeap());
+ medContext = new PPMContext(getHeap());
+ maxContext = new PPMContext(getHeap());
+ foundState = new State(getHeap());
+ dummySEE2Cont = new SEE2Context();
+ for (int i = 0; i < 25; i++) {
+ for (int j = 0; j < 16; j++) {
+ SEE2Cont[i][j] = new SEE2Context();
+ }
+ }
+ startModelRare(MaxOrder);
+ }
+ return (minContext.getAddress() != 0);
+ }
+
+ public int decodeChar() throws IOException, RarException
+ {
+ // Debug
+ //subAlloc.dumpHeap();
+
+ if (minContext.getAddress() <= subAlloc.getPText()
+ || minContext.getAddress() > subAlloc.getHeapEnd()) {
+ return (-1);
+ }
+
+ if (minContext.getNumStats() != 1) {
+ if (minContext.getFreqData().getStats() <= subAlloc.getPText()
+ || minContext.getFreqData().getStats() > subAlloc.getHeapEnd()) {
+ return (-1);
+ }
+ if (!minContext.decodeSymbol1(this)) {
+ return (-1);
+ }
+ } else {
+ minContext.decodeBinSymbol(this);
+ }
+ coder.decode();
+ while (foundState.getAddress() == 0) {
+ coder.ariDecNormalize();
+ do {
+ orderFall++;
+ minContext.setAddress(minContext.getSuffix());// =MinContext->Suffix;
+ if (minContext.getAddress() <= subAlloc.getPText()
+ || minContext.getAddress() > subAlloc.getHeapEnd()) {
+ return (-1);
+ }
+ } while (minContext.getNumStats() == numMasked);
+ if (!minContext.decodeSymbol2(this)) {
+ return (-1);
+ }
+ coder.decode();
+ }
+ int Symbol = foundState.getSymbol();
+ if ((orderFall == 0) && foundState.getSuccessor() > subAlloc.getPText()) {
+ // MinContext=MaxContext=FoundState->Successor;
+ int addr = foundState.getSuccessor();
+ minContext.setAddress(addr);
+ maxContext.setAddress(addr);
+ } else {
+ updateModel();
+ //this.foundState.setAddress(foundState.getAddress());//TODO just 4 debugging
+ if (escCount == 0) {
+ clearMask();
+ }
+ }
+ coder.ariDecNormalize();// ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead);
+ return (Symbol);
+ }
+
+ public SEE2Context[][] getSEE2Cont()
+ {
+ return SEE2Cont;
+ }
+
+ public SEE2Context getDummySEE2Cont()
+ {
+ return dummySEE2Cont;
+ }
+
+ public int getInitRL()
+ {
+ return initRL;
+ }
+
+ public void setEscCount(int escCount)
+ {
+ this.escCount = escCount&0xff;
+ }
+
+ public int getEscCount()
+ {
+ return escCount;
+ }
+
+ public void incEscCount(int dEscCount) {
+ setEscCount(getEscCount() + dEscCount);
+ }
+
+ public int[] getCharMask()
+ {
+ return charMask;
+ }
+
+ public int getNumMasked()
+ {
+ return numMasked;
+ }
+
+ public void setNumMasked(int numMasked)
+ {
+ this.numMasked = numMasked;
+ }
+
+ public void setPrevSuccess(int prevSuccess)
+ {
+ this.prevSuccess = prevSuccess&0xff;
+ }
+
+ public int getInitEsc()
+ {
+ return initEsc;
+ }
+
+ public void setInitEsc(int initEsc)
+ {
+ this.initEsc = initEsc;
+ }
+
+ public void setRunLength(int runLength)
+ {
+ this.runLength = runLength;
+ }
+
+ public int getRunLength()
+ {
+ return runLength;
+ }
+
+ public void incRunLength(int dRunLength) {
+ setRunLength(getRunLength() + dRunLength);
+ }
+
+ public int getPrevSuccess()
+ {
+ return prevSuccess;
+ }
+
+ public int getHiBitsFlag()
+ {
+ return hiBitsFlag;
+ }
+
+ public void setHiBitsFlag(int hiBitsFlag)
+ {
+ this.hiBitsFlag = hiBitsFlag&0xff;
+ }
+
+ public int[][] getBinSumm()
+ {
+ return binSumm;
+ }
+
+ public RangeCoder getCoder()
+ {
+ return coder;
+ }
+
+ public int[] getHB2Flag()
+ {
+ return HB2Flag;
+ }
+
+ public int[] getNS2BSIndx()
+ {
+ return NS2BSIndx;
+ }
+
+ public int[] getNS2Indx()
+ {
+ return NS2Indx;
+ }
+
+ public State getFoundState()
+ {
+ return foundState;
+ }
+
+ public byte[] getHeap()
+ {
+ return subAlloc.getHeap();
+ }
+
+ public int getOrderFall()
+ {
+ return orderFall;
+ }
+
+ private int /* ppmcontext ptr */createSuccessors(boolean Skip,
+ State p1 /* state ptr */) {
+ //State upState = tempState1.init(null);
+ StateRef upState = tempStateRef2;
+ State tempState = tempState1.init(getHeap());
+
+ // PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor;
+ PPMContext pc = tempPPMContext1.init(getHeap());
+ pc.setAddress(minContext.getAddress());
+ PPMContext upBranch = tempPPMContext2.init(getHeap());
+ upBranch.setAddress(foundState.getSuccessor());
+
+ // STATE * p, * ps[MAX_O], ** pps=ps;
+ State p = tempState2.init(getHeap());
+ int pps = 0;
+
+ boolean noLoop = false;
+
+ if (!Skip) {
+ ps[pps++] = foundState.getAddress();// *pps++ = FoundState;
+ if (pc.getSuffix() == 0) {
+ noLoop = true;
+ }
+ }
+ if (!noLoop) {
+ boolean loopEntry = false;
+ if (p1.getAddress() != 0) {
+ p.setAddress(p1.getAddress());
+ pc.setAddress(pc.getSuffix());// =pc->Suffix;
+ loopEntry = true;
+ }
+ do {
+ if (!loopEntry) {
+ pc.setAddress(pc.getSuffix());// pc=pc->Suffix;
+ if (pc.getNumStats() != 1) {
+ p.setAddress(pc.getFreqData().getStats());// p=pc->U.Stats
+ if (p.getSymbol() != foundState.getSymbol()) {
+ do {
+ p.incAddress();
+ } while (p.getSymbol() != foundState.getSymbol());
+ }
+ } else {
+ p.setAddress(pc.getOneState().getAddress());// p=&(pc->OneState);
+ }
+ }// LOOP_ENTRY:
+ loopEntry = false;
+ if (p.getSuccessor() != upBranch.getAddress()) {
+ pc.setAddress(p.getSuccessor());// =p->Successor;
+ break;
+ }
+ ps[pps++] = p.getAddress();
+ } while (pc.getSuffix() != 0);
+
+ } // NO_LOOP:
+ if (pps == 0) {
+ return pc.getAddress();
+ }
+ upState.setSymbol(getHeap()[upBranch.getAddress()]);// UpState.Symbol=*(byte*)
+ // UpBranch;
+ // UpState.Successor=(PPM_CONTEXT*) (((byte*) UpBranch)+1);
+ upState.setSuccessor(upBranch.getAddress() + 1); //TODO check if +1 necessary
+ if (pc.getNumStats() != 1) {
+ if (pc.getAddress() <= subAlloc.getPText()) {
+ return (0);
+ }
+ p.setAddress(pc.getFreqData().getStats());
+ if (p.getSymbol() != upState.getSymbol()) {
+ do {
+ p.incAddress();
+ } while (p.getSymbol() != upState.getSymbol());
+ }
+ int cf = p.getFreq() - 1;
+ int s0 = pc.getFreqData().getSummFreq() - pc.getNumStats() - cf;
+ // UpState.Freq=1+((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0)));
+ upState.setFreq(1 + ((2 * cf <= s0) ? (5 * cf > s0 ? 1 : 0) :
+ ((2 * cf + 3 * s0 - 1) / (2 * s0))));
+ } else {
+ upState.setFreq(pc.getOneState().getFreq());// UpState.Freq=pc->OneState.Freq;
+ }
+ do {
+ // pc = pc->createChild(this,*--pps,UpState);
+ tempState.setAddress(ps[--pps]);
+ pc.setAddress(pc.createChild(this, tempState, upState));
+ if (pc.getAddress() == 0) {
+ return 0;
+ }
+ } while (pps != 0);
+ return pc.getAddress();
+ }
+
+ private void updateModelRestart()
+ {
+ restartModelRare();
+ escCount = 0;
+ }
+
+ private void updateModel()
+ {
+ //System.out.println("ModelPPM.updateModel()");
+ // STATE fs = *FoundState, *p = NULL;
+ StateRef fs = tempStateRef1;
+ fs.setValues(foundState);
+ State p = tempState3.init(getHeap());
+ State tempState = tempState4.init(getHeap());
+
+ PPMContext pc = tempPPMContext3.init(getHeap());
+ PPMContext successor = tempPPMContext4.init(getHeap());
+
+ int ns1, ns, cf, sf, s0;
+ pc.setAddress(minContext.getSuffix());
+ if (fs.getFreq() < MAX_FREQ / 4 && pc.getAddress() != 0) {
+ if (pc.getNumStats() != 1) {
+ p.setAddress(pc.getFreqData().getStats());
+ if (p.getSymbol() != fs.getSymbol()) {
+ do {
+ p.incAddress();
+ } while (p.getSymbol() != fs.getSymbol());
+ tempState.setAddress(p.getAddress() - State.size);
+ if (p.getFreq() >= tempState.getFreq()) {
+ State.ppmdSwap(p, tempState);
+ p.decAddress();
+ }
+ }
+ if (p.getFreq() < MAX_FREQ - 9) {
+ p.incFreq(2);
+ pc.getFreqData().incSummFreq(2);
+ }
+ } else {
+ p.setAddress(pc.getOneState().getAddress());
+ if (p.getFreq() < 32) {
+ p.incFreq(1);
+ }
+ }
+ }
+ if (orderFall == 0) {
+ foundState.setSuccessor(createSuccessors(true, p));
+ minContext.setAddress(foundState.getSuccessor());
+ maxContext.setAddress(foundState.getSuccessor());
+ if (minContext.getAddress() == 0) {
+ updateModelRestart();
+ return;
+ }
+ return;
+ }
+ subAlloc.getHeap()[subAlloc.getPText()] = (byte)fs.getSymbol();
+ subAlloc.incPText();
+ successor.setAddress(subAlloc.getPText());
+ if (subAlloc.getPText() >= subAlloc.getFakeUnitsStart()) {
+ updateModelRestart();
+ return;
+ }
+// // Debug
+// subAlloc.dumpHeap();
+ if (fs.getSuccessor() != 0) {
+ if (fs.getSuccessor() <= subAlloc.getPText()) {
+ fs.setSuccessor(createSuccessors(false, p));
+ if (fs.getSuccessor() == 0) {
+ updateModelRestart();
+ return;
+ }
+ }
+ if (--orderFall == 0) {
+ successor.setAddress(fs.getSuccessor());
+ if (maxContext.getAddress() != minContext.getAddress()) {
+ subAlloc.decPText(1);
+ }
+ }
+ }
+ else {
+ foundState.setSuccessor(successor.getAddress());
+ fs.setSuccessor(minContext);
+ }
+// // Debug
+// subAlloc.dumpHeap();
+ ns = minContext.getNumStats();
+ s0 = minContext.getFreqData().getSummFreq() - (ns) - (fs.getFreq() - 1);
+ for (pc.setAddress(maxContext.getAddress());
+ pc.getAddress() != minContext.getAddress();
+ pc.setAddress(pc.getSuffix())) {
+ if ((ns1 = pc.getNumStats()) != 1) {
+ if ((ns1 & 1) == 0) {
+ //System.out.println(ns1);
+ pc.getFreqData().setStats(
+ subAlloc.expandUnits(pc.getFreqData().getStats(),
+ ns1 >>> 1));
+ if (pc.getFreqData().getStats() == 0) {
+ updateModelRestart();
+ return;
+ }
+ }
+ // bug fixed
+// int sum = ((2 * ns1 < ns) ? 1 : 0) +
+// 2 * ((4 * ((ns1 <= ns) ? 1 : 0)) & ((pc.getFreqData()
+// .getSummFreq() <= 8 * ns1) ? 1 : 0));
+ int sum = ((2 * ns1 < ns) ? 1 : 0) + 2 * (
+ ((4 * ns1 <= ns) ? 1 : 0) &
+ ((pc.getFreqData().getSummFreq() <= 8 * ns1) ? 1 : 0)
+ );
+ pc.getFreqData().incSummFreq(sum);
+ }
+ else {
+ p.setAddress(subAlloc.allocUnits(1));
+ if (p.getAddress() == 0) {
+ updateModelRestart();
+ return;
+ }
+ p.setValues(pc.getOneState());
+ pc.getFreqData().setStats(p);
+ if (p.getFreq() < MAX_FREQ / 4 - 1) {
+ p.incFreq(p.getFreq());
+ }
+ else {
+ p.setFreq(MAX_FREQ - 4);
+ }
+ pc.getFreqData().setSummFreq(
+ (p.getFreq() + initEsc + (ns > 3 ? 1 : 0)));
+ }
+ cf = 2 * fs.getFreq() * (pc.getFreqData().getSummFreq() + 6);
+ sf = s0 + pc.getFreqData().getSummFreq();
+ if (cf < 6 * sf) {
+ cf = 1 + (cf > sf ? 1 : 0) + (cf >= 4 * sf ? 1 : 0);
+ pc.getFreqData().incSummFreq(3);
+ }
+ else {
+ cf = 4 + (cf >= 9 * sf ? 1 : 0) + (cf >= 12 * sf ? 1 : 0) +
+ (cf >= 15 * sf ? 1 : 0);
+ pc.getFreqData().incSummFreq(cf);
+ }
+ p.setAddress(pc.getFreqData().getStats() + ns1*State.size);
+ p.setSuccessor(successor);
+ p.setSymbol(fs.getSymbol());
+ p.setFreq(cf);
+ pc.setNumStats(++ns1);
+ }
+
+ int address = fs.getSuccessor();
+ maxContext.setAddress(address);
+ minContext.setAddress(address);
+ //TODO-----debug
+// int pos = minContext.getFreqData().getStats();
+// State a = new State(getHeap());
+// a.setAddress(pos);
+// pos+=State.size;
+// a.setAddress(pos);
+ //--dbg end
+ return;
+ }
+
+ // Debug
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("ModelPPM[");
+ buffer.append("\n numMasked=");
+ buffer.append(numMasked);
+ buffer.append("\n initEsc=");
+ buffer.append(initEsc);
+ buffer.append("\n orderFall=");
+ buffer.append(orderFall);
+ buffer.append("\n maxOrder=");
+ buffer.append(maxOrder);
+ buffer.append("\n runLength=");
+ buffer.append(runLength);
+ buffer.append("\n initRL=");
+ buffer.append(initRL);
+ buffer.append("\n escCount=");
+ buffer.append(escCount);
+ buffer.append("\n prevSuccess=");
+ buffer.append(prevSuccess);
+ buffer.append("\n foundState=");
+ buffer.append(foundState);
+ buffer.append("\n coder=");
+ buffer.append(coder);
+ buffer.append("\n subAlloc=");
+ buffer.append(subAlloc);
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+
+ // Debug
+// public void dumpHeap() {
+// subAlloc.dumpHeap();
+// }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/PPMContext.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/PPMContext.java
new file mode 100644
index 0000000..f6e5d67
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/PPMContext.java
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import com.github.junrar.io.Raw;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class PPMContext extends Pointer
+{
+
+ private static final int unionSize = Math.max(FreqData.size, State.size);
+
+ public static final int size = 2 + unionSize + 4; // 12
+
+ // ushort NumStats;
+ private int numStats; // determines if feqData or onstate is used
+
+ // (1==onestate)
+
+ private final FreqData freqData; // -\
+
+ // |-> union
+ private final State oneState; // -/
+
+ private int suffix; // pointer ppmcontext
+
+ public final static int[] ExpEscape =
+ { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+
+ // Temp fields
+ private final State tempState1 = new State(null);
+ private final State tempState2 = new State(null);
+ private final State tempState3 = new State(null);
+ private final State tempState4 = new State(null);
+ private final State tempState5 = new State(null);
+ private PPMContext tempPPMContext = null;
+ private final int[] ps = new int[256];
+
+ public PPMContext(byte[] mem)
+ {
+ super(mem);
+ oneState = new State(mem);
+ freqData = new FreqData(mem);
+ }
+
+ public PPMContext init(byte[] mem) {
+ this.mem = mem;
+ pos = 0;
+ oneState.init(mem);
+ freqData.init(mem);
+ return this;
+ }
+
+ public FreqData getFreqData()
+ {
+ return freqData;
+ }
+
+ public void setFreqData(FreqData freqData)
+ {
+ this.freqData.setSummFreq(freqData.getSummFreq());
+ this.freqData.setStats(freqData.getStats());
+ }
+
+ public final int getNumStats()
+ {
+ if (mem!=null){
+ numStats = Raw.readShortLittleEndian(mem, pos)&0xffff;
+ }
+ return numStats;
+ }
+
+ public final void setNumStats(int numStats)
+ {
+ this.numStats = numStats&0xffff;
+ if (mem != null) {
+ Raw.writeShortLittleEndian(mem, pos, (short)numStats);
+ }
+ }
+
+ public State getOneState()
+ {
+ return oneState;
+ }
+
+ public void setOneState(StateRef oneState)
+ {
+ this.oneState.setValues(oneState);
+ }
+
+ public int getSuffix()
+ {
+ if(mem!=null){
+ suffix = Raw.readIntLittleEndian(mem, pos+8);
+ }
+ return suffix;
+ }
+
+ public void setSuffix(PPMContext suffix)
+ {
+ setSuffix(suffix.getAddress());
+ }
+
+ public void setSuffix(int suffix)
+ {
+ this.suffix = suffix;
+ if (mem != null) {
+ Raw.writeIntLittleEndian(mem, pos + 8, suffix);
+ }
+ }
+
+ @Override
+ public void setAddress(int pos)
+ {
+ super.setAddress(pos);
+ oneState.setAddress(pos+2);
+ freqData.setAddress(pos+2);
+ }
+
+ private PPMContext getTempPPMContext(byte[] mem) {
+ if (tempPPMContext == null) {
+ tempPPMContext = new PPMContext(null);
+ }
+ return tempPPMContext.init(mem);
+ }
+
+ public int createChild(ModelPPM model, State pStats/* ptr */,
+ StateRef firstState /* ref */)
+ {
+ PPMContext pc = getTempPPMContext(model.getSubAlloc().getHeap());
+ pc.setAddress(model.getSubAlloc().allocContext());
+ if (pc != null) {
+ pc.setNumStats(1);
+ pc.setOneState(firstState);
+ pc.setSuffix(this);
+ pStats.setSuccessor(pc);
+ }
+ return pc.getAddress();
+ }
+
+ public void rescale(ModelPPM model)
+ {
+ int OldNS = getNumStats(), i = getNumStats() - 1, Adder, EscFreq;
+ // STATE* p1, * p;
+ State p1 = new State(model.getHeap());
+ State p = new State(model.getHeap());
+ State temp = new State(model.getHeap());
+
+ for (p.setAddress(model.getFoundState().getAddress());
+ p.getAddress() != freqData.getStats();
+ p.decAddress()) {
+ temp.setAddress(p.getAddress() - State.size);
+ State.ppmdSwap(p, temp);
+ }
+ temp.setAddress(freqData.getStats());
+ temp.incFreq(4);
+ freqData.incSummFreq(4);
+ EscFreq = freqData.getSummFreq() - p.getFreq();
+ Adder = (model.getOrderFall() != 0) ? 1 : 0;
+ p.setFreq((p.getFreq() + Adder) >>> 1);
+ freqData.setSummFreq(p.getFreq());
+ do {
+ p.incAddress();
+ EscFreq -= p.getFreq();
+ p.setFreq((p.getFreq() + Adder) >>> 1);
+ freqData.incSummFreq(p.getFreq());
+ temp.setAddress(p.getAddress() - State.size);
+ if (p.getFreq() > temp.getFreq()) {
+ p1.setAddress(p.getAddress());
+ StateRef tmp = new StateRef();
+ tmp.setValues(p1);
+ State temp2 = new State(model.getHeap());
+ State temp3 = new State(model.getHeap());
+ do {
+ // p1[0]=p1[-1];
+ temp2.setAddress(p1.getAddress() - State.size);
+ p1.setValues(temp2);
+ p1.decAddress();
+ temp3.setAddress(p1.getAddress() - State.size);
+ } while (p1.getAddress() != freqData.getStats() && tmp.getFreq() > temp3.getFreq());
+ p1.setValues(tmp);
+ }
+ } while (--i != 0);
+ if (p.getFreq() == 0) {
+ do {
+ i++;
+ p.decAddress();
+ } while (p.getFreq() == 0);
+ EscFreq += i;
+ setNumStats(getNumStats() - i);
+ if (getNumStats() == 1) {
+ StateRef tmp = new StateRef();
+ temp.setAddress(freqData.getStats());
+ tmp.setValues(temp);
+ // STATE tmp=*U.Stats;
+ do {
+ // tmp.Freq-=(tmp.Freq >> 1)
+ tmp.decFreq(tmp.getFreq() >>> 1);
+ EscFreq >>>= 1;
+ } while (EscFreq > 1);
+ model.getSubAlloc().freeUnits(freqData.getStats(),(OldNS + 1) >>> 1);
+ oneState.setValues(tmp);
+ model.getFoundState().setAddress(oneState.getAddress());
+ return;
+ }
+ }
+ EscFreq -= EscFreq >>> 1;
+ freqData.incSummFreq(EscFreq);
+ int n0 = (OldNS + 1) >>> 1, n1 = (getNumStats() + 1) >>> 1;
+ if (n0 != n1) {
+ freqData.setStats(model.getSubAlloc().shrinkUnits(freqData.getStats(), n0, n1));
+ }
+ model.getFoundState().setAddress(freqData.getStats());
+ }
+
+ private int getArrayIndex(ModelPPM Model, State rs)
+ {
+ PPMContext tempSuffix = getTempPPMContext(Model.getSubAlloc().getHeap());
+ tempSuffix.setAddress(getSuffix());
+ int ret = 0;
+ ret += Model.getPrevSuccess();
+ ret += Model.getNS2BSIndx()[tempSuffix.getNumStats() - 1];
+ ret += Model.getHiBitsFlag() + 2* Model.getHB2Flag()[rs.getSymbol()];
+ ret += ((Model.getRunLength() >>> 26) & 0x20);
+ return ret;
+ }
+
+ public int getMean(int summ, int shift, int round)
+ {
+ return ( (summ + (1 << (shift - round) ) ) >>> (shift) );
+ }
+
+ public void decodeBinSymbol(ModelPPM model)
+ {
+ State rs = tempState1.init(model.getHeap());
+ rs.setAddress(oneState.getAddress());// State&
+ model.setHiBitsFlag(model.getHB2Flag()[model.getFoundState().getSymbol()]);
+ int off1 = rs.getFreq() - 1;
+ int off2 = getArrayIndex(model, rs);
+ int bs = model.getBinSumm()[off1][off2];
+ if (model.getCoder().getCurrentShiftCount(ModelPPM.TOT_BITS) < bs) {
+ model.getFoundState().setAddress(rs.getAddress());
+ rs.incFreq((rs.getFreq() < 128) ? 1 : 0);
+ model.getCoder().getSubRange().setLowCount(0);
+ model.getCoder().getSubRange().setHighCount(bs);
+ bs = ((bs + ModelPPM.INTERVAL - getMean(bs, ModelPPM.PERIOD_BITS, 2)) & 0xffff);
+ model.getBinSumm()[off1][off2] = bs;
+ model.setPrevSuccess(1);
+ model.incRunLength(1);
+ } else {
+ model.getCoder().getSubRange().setLowCount(bs);
+ bs = (bs - getMean(bs, ModelPPM.PERIOD_BITS, 2)) & 0xFFFF;
+ model.getBinSumm()[off1][off2] = bs;
+ model.getCoder().getSubRange().setHighCount(ModelPPM.BIN_SCALE);
+ model.setInitEsc(ExpEscape[bs >>> 10]);
+ model.setNumMasked(1);
+ model.getCharMask()[rs.getSymbol()] = model.getEscCount();
+ model.setPrevSuccess(0);
+ model.getFoundState().setAddress(0);
+ }
+ //int a = 0;//TODO just 4 debugging
+ }
+
+// public static void ppmdSwap(ModelPPM model, StatePtr state1, StatePtr state2)
+// {
+// byte[] bytes = model.getSubAlloc().getHeap();
+// int p1 = state1.getAddress();
+// int p2 = state2.getAddress();
+//
+// for (int i = 0; i < StatePtr.size; i++) {
+// byte temp = bytes[p1+i];
+// bytes[p1+i] = bytes[p2+i];
+// bytes[p2+i] = temp;
+// }
+// state1.setAddress(p1);
+// state2.setAddress(p2);
+// }
+
+ public void update1(ModelPPM model, int p/* ptr */)
+ {
+ model.getFoundState().setAddress(p);
+ model.getFoundState().incFreq(4);
+ freqData.incSummFreq(4);
+ State p0 = tempState3.init(model.getHeap());
+ State p1 = tempState4.init(model.getHeap());
+ p0.setAddress(p);
+ p1.setAddress(p - State.size);
+ if (p0.getFreq() > p1.getFreq()) {
+ State.ppmdSwap(p0, p1);
+ model.getFoundState().setAddress(p1.getAddress());
+ if (p1.getFreq() > ModelPPM.MAX_FREQ)
+ rescale(model);
+ }
+ }
+
+ public boolean decodeSymbol2(ModelPPM model)
+ {
+ long count;
+ int hiCnt, i = getNumStats() - model.getNumMasked();
+ SEE2Context psee2c = makeEscFreq2(model, i);
+ RangeCoder coder = model.getCoder();
+ // STATE* ps[256], ** pps=ps, * p=U.Stats-1;
+ State p = tempState1.init(model.getHeap());
+ State temp = tempState2.init(model.getHeap());
+ p.setAddress(freqData.getStats() - State.size);
+ int pps = 0;
+ hiCnt = 0;
+
+ do {
+ do {
+ p.incAddress();// p++;
+ } while (model.getCharMask()[p.getSymbol()] == model.getEscCount());
+ hiCnt += p.getFreq();
+ ps[pps++] = p.getAddress();
+ } while (--i != 0);
+ coder.getSubRange().incScale(hiCnt);
+ count = coder.getCurrentCount();
+ if (count >= coder.getSubRange().getScale()) {
+ return false;
+ }
+ pps = 0;
+ p.setAddress(ps[pps]);
+ if (count < hiCnt) {
+ hiCnt = 0;
+ while ((hiCnt += p.getFreq()) <= count) {
+ p.setAddress(ps[++pps]);// p=*++pps;
+ }
+ coder.getSubRange().setHighCount(hiCnt);
+ coder.getSubRange().setLowCount(hiCnt - p.getFreq());
+ psee2c.update();
+ update2(model, p.getAddress());
+ } else {
+ coder.getSubRange().setLowCount(hiCnt);
+ coder.getSubRange().setHighCount(coder.getSubRange().getScale());
+ i = getNumStats() - model.getNumMasked();// ->NumMasked;
+ pps--;
+ do {
+ temp.setAddress(ps[++pps]);// (*++pps)
+ model.getCharMask()[temp.getSymbol()] = model.getEscCount();
+ } while (--i != 0);
+ psee2c.incSumm((int)coder.getSubRange().getScale());
+ model.setNumMasked(getNumStats());
+ }
+ return (true);
+ }
+
+ public void update2(ModelPPM model, int p/* state ptr */)
+ {
+ State temp = tempState5.init(model.getHeap());
+ temp.setAddress(p);
+ model.getFoundState().setAddress(p);
+ model.getFoundState().incFreq(4);
+ freqData.incSummFreq(4);
+ if (temp.getFreq() > ModelPPM.MAX_FREQ) {
+ rescale(model);
+ }
+ model.incEscCount(1);
+ model.setRunLength(model.getInitRL());
+ }
+
+ private SEE2Context makeEscFreq2(ModelPPM model, int Diff)
+ {
+ SEE2Context psee2c;
+ int numStats = getNumStats();
+ if (numStats != 256) {
+ PPMContext suff = getTempPPMContext(model.getHeap());
+ suff.setAddress(getSuffix());
+ int idx1 = model.getNS2Indx()[Diff - 1];
+ int idx2 = 0;
+ idx2 += (Diff < suff.getNumStats() - numStats) ? 1 : 0;
+ idx2 += 2 * ((freqData.getSummFreq() < 11 * numStats) ? 1 : 0);
+ idx2 += 4 * ((model.getNumMasked() > Diff) ? 1 : 0);
+ idx2 += model.getHiBitsFlag();
+ psee2c = model.getSEE2Cont()[idx1][idx2];
+ model.getCoder().getSubRange().setScale(psee2c.getMean());
+ } else {
+ psee2c = model.getDummySEE2Cont();
+ model.getCoder().getSubRange().setScale(1);
+ }
+ return psee2c;
+ }
+
+ public boolean decodeSymbol1(ModelPPM model)
+ {
+
+ RangeCoder coder = model.getCoder();
+ coder.getSubRange().setScale(freqData.getSummFreq());
+ State p = new State(model.getHeap());
+ p.setAddress(freqData.getStats());
+ int i, HiCnt;
+ long count = coder.getCurrentCount();
+ if (count >= coder.getSubRange().getScale()) {
+ return false;
+ }
+ if (count < (HiCnt = p.getFreq())) {
+ coder.getSubRange().setHighCount(HiCnt);
+ model.setPrevSuccess((2 * HiCnt > coder.getSubRange().getScale()) ? 1 : 0);
+ model.incRunLength(model.getPrevSuccess());
+ HiCnt += 4;
+ model.getFoundState().setAddress(p.getAddress());
+ model.getFoundState().setFreq(HiCnt);
+ freqData.incSummFreq(4);
+ if (HiCnt > ModelPPM.MAX_FREQ) {
+ rescale(model);
+ }
+ coder.getSubRange().setLowCount(0);
+ return true;
+ } else {
+ if (model.getFoundState().getAddress() == 0) {
+ return (false);
+ }
+ }
+ model.setPrevSuccess(0);
+ int numStats = getNumStats();
+ i = numStats - 1;
+ while ((HiCnt += p.incAddress().getFreq()) <= count)
+ {
+ if (--i == 0) {
+ model.setHiBitsFlag(model.getHB2Flag()[model.getFoundState().getSymbol()]);
+ coder.getSubRange().setLowCount(HiCnt);
+ model.getCharMask()[p.getSymbol()] = model.getEscCount();
+ model.setNumMasked(numStats);
+ i = numStats - 1;
+ model.getFoundState().setAddress(0);
+ do {
+ model.getCharMask()[p.decAddress().getSymbol()] = model.getEscCount();
+ } while (--i != 0);
+ coder.getSubRange().setHighCount(coder.getSubRange().getScale());
+ return (true);
+ }
+ }
+ coder.getSubRange().setLowCount(HiCnt-p.getFreq());
+ coder.getSubRange().setHighCount(HiCnt);
+ update1(model, p.getAddress());
+ return (true);
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("PPMContext[");
+ buffer.append("\n pos=");
+ buffer.append(pos);
+ buffer.append("\n size=");
+ buffer.append(size);
+ buffer.append("\n numStats=");
+ buffer.append(getNumStats());
+ buffer.append("\n Suffix=");
+ buffer.append(getSuffix());
+ buffer.append("\n freqData=");
+ buffer.append(freqData);
+ buffer.append("\n oneState=");
+ buffer.append(oneState);
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/Pointer.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/Pointer.java
new file mode 100644
index 0000000..c206b9d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/Pointer.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 14.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+/**
+ * Simulates Pointers on a single mem block as a byte[]
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public abstract class Pointer
+{
+ protected byte[] mem;
+ protected int pos;
+
+ /**
+ * Initialize the object with the array (may be null)
+ * @param mem the byte array
+ */
+ public Pointer(byte[] mem){
+ this.mem = mem;
+ }
+ /**
+ * returns the position of this object in the byte[]
+ * @return the address of this object
+ */
+ public int getAddress(){
+ assert (mem != null);
+ return pos;
+ }
+
+ /**
+ * needs to set the fields of this object to the values in the byte[]
+ * at the given position.
+ * be aware of the byte order
+ * @param pos the position this object should point to
+ * @return true if the address could be set
+ */
+ public void setAddress(int pos) {
+ assert (mem != null);
+ assert (pos >= 0) && (pos < mem.length) : pos;
+ this.pos = pos;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RangeCoder.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RangeCoder.java
new file mode 100644
index 0000000..734813f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RangeCoder.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import java.io.IOException;
+
+import com.github.junrar.exception.RarException;
+import com.github.junrar.unpack.Unpack;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class RangeCoder
+{
+ public static final int TOP = 1 << 24;
+
+ public static final int BOT = 1 << 15;
+
+ private static final long uintMask = 0xFFFFffffL;
+
+ // uint low, code, range;
+ private long low, code, range;
+
+ private final SubRange subRange = new SubRange();
+
+ private Unpack unpackRead;
+
+ public SubRange getSubRange()
+ {
+ return subRange;
+ }
+
+ public void initDecoder(Unpack unpackRead) throws IOException, RarException
+ {
+ this.unpackRead = unpackRead;
+
+ low = code = 0L;
+ range = 0xFFFFffffL;
+ for (int i = 0; i < 4; i++) {
+ code = ((code << 8) | getChar())&uintMask;
+ }
+ }
+
+ public int getCurrentCount()
+ {
+ range = (range / subRange.getScale())&uintMask;
+ return (int)((code - low) / (range));
+ }
+
+ public long getCurrentShiftCount(int SHIFT)
+ {
+ range = range >>>SHIFT;
+ return ((code - low) / (range))&uintMask;
+ }
+
+ public void decode()
+ {
+ low = (low + (range * subRange.getLowCount()))&uintMask;
+ range = (range * (subRange.getHighCount() - subRange.getLowCount()))&uintMask;
+ }
+
+ private int getChar() throws IOException, RarException
+ {
+ return (unpackRead.getChar());
+ }
+
+ public void ariDecNormalize() throws IOException, RarException
+ {
+// while ((low ^ (low + range)) < TOP || range < BOT && ((range = -low & (BOT - 1)) != 0 ? true : true))
+// {
+// code = ((code << 8) | unpackRead.getChar()&0xff)&uintMask;
+// range = (range << 8)&uintMask;
+// low = (low << 8)&uintMask;
+// }
+
+ // Rewrote for clarity
+ boolean c2 = false;
+ while ((low ^ (low + range)) < TOP || (c2 = range < BOT)) {
+ if (c2) {
+ range = (-low & (BOT - 1))&uintMask;
+ c2 = false;
+ }
+ code = ((code << 8) | getChar())&uintMask;
+ range = (range << 8)&uintMask;
+ low = (low << 8)&uintMask;
+ }
+ }
+
+ // Debug
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("RangeCoder[");
+ buffer.append("\n low=");
+ buffer.append(low);
+ buffer.append("\n code=");
+ buffer.append(code);
+ buffer.append("\n range=");
+ buffer.append(range);
+ buffer.append("\n subrange=");
+ buffer.append(subRange);
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+ public static class SubRange
+ {
+ // uint LowCount, HighCount, scale;
+ private long lowCount, highCount, scale;
+
+ public long getHighCount()
+ {
+ return highCount;
+ }
+
+ public void setHighCount(long highCount)
+ {
+ this.highCount = highCount&uintMask;
+ }
+
+ public long getLowCount()
+ {
+ return lowCount&uintMask;
+ }
+
+ public void setLowCount(long lowCount)
+ {
+ this.lowCount = lowCount&uintMask;
+ }
+
+ public long getScale()
+ {
+ return scale;
+ }
+
+ public void setScale(long scale)
+ {
+ this.scale = scale&uintMask;
+ }
+
+ public void incScale(int dScale) {
+ setScale(getScale() + dScale);
+ }
+
+ // Debug
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("SubRange[");
+ buffer.append("\n lowCount=");
+ buffer.append(lowCount);
+ buffer.append("\n highCount=");
+ buffer.append(highCount);
+ buffer.append("\n scale=");
+ buffer.append(scale);
+ buffer.append("]");
+ return buffer.toString();
+ }
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarMemBlock.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarMemBlock.java
new file mode 100644
index 0000000..1b5e7cc
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarMemBlock.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 05.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import com.github.junrar.io.Raw;
+
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class RarMemBlock extends Pointer
+{
+
+ public static final int size = 12;
+
+ private int stamp, NU;
+
+ private int next, prev; // Pointer RarMemBlock
+
+ public RarMemBlock(byte[] mem)
+ {
+ super(mem);
+ }
+
+ public void insertAt(RarMemBlock p)
+ {
+ RarMemBlock temp = new RarMemBlock(mem);
+ setPrev(p.getAddress());
+ temp.setAddress(getPrev());
+ setNext(temp.getNext());// prev.getNext();
+ temp.setNext(this);// prev.setNext(this);
+ temp.setAddress(getNext());
+ temp.setPrev(this);// next.setPrev(this);
+ }
+
+ public void remove()
+ {
+ RarMemBlock temp = new RarMemBlock(mem);
+ temp.setAddress(getPrev());
+ temp.setNext(getNext());// prev.setNext(next);
+ temp.setAddress(getNext());
+ temp.setPrev(getPrev());// next.setPrev(prev);
+// next = -1;
+// prev = -1;
+ }
+
+ public int getNext()
+ {
+ if(mem!=null){
+ next = Raw.readIntLittleEndian(mem, pos+4);
+ }
+ return next;
+ }
+
+ public void setNext(RarMemBlock next)
+ {
+ setNext(next.getAddress());
+ }
+
+ public void setNext(int next)
+ {
+ this.next = next;
+ if (mem != null) {
+ Raw.writeIntLittleEndian(mem, pos + 4, next);
+ }
+ }
+
+ public int getNU()
+ {
+ if(mem!=null){
+ NU = Raw.readShortLittleEndian(mem, pos+2)&0xffff;
+ }
+ return NU;
+ }
+
+ public void setNU(int nu)
+ {
+ NU = nu&0xffff;
+ if (mem != null) {
+ Raw.writeShortLittleEndian(mem, pos + 2, (short)nu);
+ }
+ }
+
+ public int getPrev()
+ {
+ if(mem!=null){
+ prev = Raw.readIntLittleEndian(mem, pos+8);
+ }
+ return prev;
+ }
+
+ public void setPrev(RarMemBlock prev)
+ {
+ setPrev(prev.getAddress());
+ }
+
+ public void setPrev(int prev)
+ {
+ this.prev = prev;
+ if (mem != null) {
+ Raw.writeIntLittleEndian(mem, pos + 8, prev);
+ }
+ }
+
+ public int getStamp()
+ {
+ if(mem!=null){
+ stamp = Raw.readShortLittleEndian(mem, pos)&0xffff;
+ }
+ return stamp;
+ }
+
+ public void setStamp(int stamp)
+ {
+ this.stamp = stamp;
+ if (mem != null) {
+ Raw.writeShortLittleEndian(mem, pos, (short)stamp);
+ }
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarNode.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarNode.java
new file mode 100644
index 0000000..a9353cb
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/RarNode.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 05.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import com.github.junrar.io.Raw;
+
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class RarNode extends Pointer{
+ private int next; //rarnode pointer
+
+ public static final int size = 4;
+
+ public RarNode(byte[] mem){
+ super(mem);
+ }
+
+ public int getNext() {
+ if(mem!=null){
+ next = Raw.readIntLittleEndian(mem, pos);
+ }
+ return next;
+ }
+
+ public void setNext(RarNode next) {
+ setNext(next.getAddress());
+ }
+
+ public void setNext(int next) {
+ this.next = next;
+ if(mem!=null){
+ Raw.writeIntLittleEndian(mem, pos, next);
+ }
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("State[");
+ buffer.append("\n pos=");
+ buffer.append(pos);
+ buffer.append("\n size=");
+ buffer.append(size);
+ buffer.append("\n next=");
+ buffer.append(getNext());
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SEE2Context.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SEE2Context.java
new file mode 100644
index 0000000..1f42419
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SEE2Context.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class SEE2Context {
+ public static final int size = 4;
+
+ // ushort Summ;
+ private int summ;
+
+ // byte Shift;
+ private int shift;
+
+ // byte Count;
+ private int count;
+
+ public void init(int initVal) {
+ shift = (ModelPPM.PERIOD_BITS - 4)&0xff;
+ summ = (initVal << shift)&0xffff;
+ count = 4;
+ }
+
+ public int getMean() {
+ int retVal = summ >>> shift;
+ summ -= retVal;
+ return retVal + ((retVal == 0) ? 1 : 0);
+ }
+
+ public void update() {
+ if (shift < ModelPPM.PERIOD_BITS && --count == 0) {
+ summ += summ;
+ count = (3 << shift++);
+ }
+ summ &= 0xffff;
+ count &= 0xff;
+ shift &= 0xff;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count&0xff;
+ }
+
+ public int getShift() {
+ return shift;
+ }
+
+ public void setShift(int shift) {
+ this.shift = shift&0xff;
+ }
+
+ public int getSumm() {
+ return summ;
+ }
+
+ public void setSumm(int summ) {
+ this.summ = summ&0xffff;
+ }
+
+ public void incSumm(int dSumm) {
+ setSumm(getSumm() + dSumm);
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("SEE2Context[");
+ buffer.append("\n size=");
+ buffer.append(size);
+ buffer.append("\n summ=");
+ buffer.append(summ);
+ buffer.append("\n shift=");
+ buffer.append(shift);
+ buffer.append("\n count=");
+ buffer.append(count);
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/State.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/State.java
new file mode 100644
index 0000000..812fb40
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/State.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import com.github.junrar.io.Raw;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class State extends Pointer {
+
+ public static final int size = 6;
+
+ public State(byte[] mem) {
+ super(mem);
+ }
+
+ public State init(byte[] mem) {
+ this.mem = mem;
+ pos = 0;
+ return this;
+ }
+
+ public int getSymbol() {
+ return mem[pos]&0xff;
+ }
+
+ public void setSymbol(int symbol) {
+ mem[pos] = (byte)symbol;
+ }
+
+ public int getFreq() {
+ return mem[pos+1]&0xff;
+ }
+
+ public void setFreq(int freq) {
+ mem[pos + 1] = (byte)freq;
+ }
+
+ public void incFreq(int dFreq) {
+ mem[pos + 1] += dFreq;
+ }
+
+ public int getSuccessor() {
+ return Raw.readIntLittleEndian(mem, pos+2);
+ }
+
+ public void setSuccessor(PPMContext successor) {
+ setSuccessor(successor.getAddress());
+ }
+
+ public void setSuccessor(int successor) {
+ Raw.writeIntLittleEndian(mem, pos + 2, successor);
+ }
+
+ public void setValues(StateRef state){
+ setSymbol(state.getSymbol());
+ setFreq(state.getFreq());
+ setSuccessor(state.getSuccessor());
+ }
+
+ public void setValues(State ptr){
+ System.arraycopy(ptr.mem, ptr.pos, mem, pos, size);
+ }
+
+ public State decAddress(){
+ setAddress(pos-size);
+ return this;
+ }
+
+ public State incAddress(){
+ setAddress(pos+size);
+ return this;
+ }
+
+ public static void ppmdSwap(State ptr1, State ptr2) {
+ byte[] mem1=ptr1.mem, mem2=ptr2.mem;
+ for (int i=0, pos1=ptr1.pos, pos2=ptr2.pos; i < size; i++, pos1++, pos2++) {
+ byte temp = mem1[pos1];
+ mem1[pos1] = mem2[pos2];
+ mem2[pos2] = temp;
+ }
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("State[");
+ buffer.append("\n pos=");
+ buffer.append(pos);
+ buffer.append("\n size=");
+ buffer.append(size);
+ buffer.append("\n symbol=");
+ buffer.append(getSymbol());
+ buffer.append("\n freq=");
+ buffer.append(getFreq());
+ buffer.append("\n successor=");
+ buffer.append(getSuccessor());
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/StateRef.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/StateRef.java
new file mode 100644
index 0000000..39195e0
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/StateRef.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class StateRef {
+
+ private int symbol;
+
+ private int freq;
+
+ private int successor; // pointer ppmcontext
+
+ public StateRef() {
+ }
+
+ public int getSymbol() {
+ return symbol;
+ }
+
+ public void setSymbol(int symbol) {
+ this.symbol = symbol&0xff;
+ }
+
+ public int getFreq() {
+ return freq;
+ }
+
+ public void setFreq(int freq) {
+ this.freq = freq&0xff;
+ }
+
+ public void incFreq(int dFreq) {
+ freq = (freq + dFreq)&0xff;
+ }
+
+ public void decFreq(int dFreq) {
+ freq = (freq - dFreq)&0xff;
+ }
+
+ public void setValues(State statePtr){
+ setFreq(statePtr.getFreq());
+ setSuccessor(statePtr.getSuccessor());
+ setSymbol(statePtr.getSymbol());
+ }
+
+ public int getSuccessor() {
+ return successor;
+ }
+
+ public void setSuccessor(PPMContext successor) {
+ setSuccessor(successor.getAddress());
+ }
+
+ public void setSuccessor(int successor) {
+ this.successor = successor;
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("State[");
+ buffer.append("\n symbol=");
+ buffer.append(getSymbol());
+ buffer.append("\n freq=");
+ buffer.append(getFreq());
+ buffer.append("\n successor=");
+ buffer.append(getSuccessor());
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SubAllocator.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SubAllocator.java
new file mode 100644
index 0000000..8844736
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/ppm/SubAllocator.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.ppm;
+
+import java.util.Arrays;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class SubAllocator {
+ public static final int N1 = 4, N2 = 4, N3 = 4, N4 = (128 + 3 - 1 * N1 - 2
+ * N2 - 3 * N3) / 4;
+
+ public static final int N_INDEXES = N1 + N2 + N3 + N4;
+
+ public static final int UNIT_SIZE = Math.max(PPMContext.size,
+ RarMemBlock.size);
+
+ public static final int FIXED_UNIT_SIZE = 12;
+
+ private int subAllocatorSize;
+
+ // byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount;
+ private int[] indx2Units = new int[N_INDEXES];
+ private int[] units2Indx = new int[128];
+ private int glueCount;
+
+ // byte *HeapStart,*LoUnit, *HiUnit;
+ private int heapStart, loUnit, hiUnit;
+
+ private final RarNode[] freeList = new RarNode[N_INDEXES];
+
+ // byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart;
+ private int pText, unitsStart, heapEnd, fakeUnitsStart;
+
+ private byte[] heap;
+
+ private int freeListPos;
+
+ private int tempMemBlockPos;
+
+ // Temp fields
+ private RarNode tempRarNode = null;
+ private RarMemBlock tempRarMemBlock1 = null;
+ private RarMemBlock tempRarMemBlock2 = null;
+ private RarMemBlock tempRarMemBlock3 = null;
+
+ public SubAllocator() {
+ clean();
+ }
+
+ public void clean() {
+ subAllocatorSize = 0;
+ }
+
+ private void insertNode(int p/* rarnode ptr */, int indx) {
+ RarNode temp = tempRarNode;
+ temp.setAddress(p);
+ temp.setNext(freeList[indx].getNext());
+ freeList[indx].setNext(temp);
+ }
+
+ public void incPText() {
+ pText++;
+ }
+
+ private int removeNode(int indx) {
+ int retVal = freeList[indx].getNext();
+ RarNode temp = tempRarNode;
+ temp.setAddress(retVal);
+ freeList[indx].setNext(temp.getNext());
+ return retVal;
+ }
+
+ private int U2B(int NU) {
+ return /* 8*NU+4*NU */UNIT_SIZE * NU;
+ }
+
+ /* memblockptr */
+ private int MBPtr(int BasePtr, int Items) {
+ return (BasePtr + U2B(Items));
+ }
+
+ private void splitBlock(int pv/* ptr */, int oldIndx, int newIndx) {
+ int i, uDiff = indx2Units[oldIndx] - indx2Units[newIndx];
+ int p = pv + U2B(indx2Units[newIndx]);
+ if (indx2Units[i = units2Indx[uDiff - 1]] != uDiff) {
+ insertNode(p, --i);
+ p += U2B(i = indx2Units[i]);
+ uDiff -= i;
+ }
+ insertNode(p, units2Indx[uDiff - 1]);
+ }
+
+ public void stopSubAllocator() {
+ if (subAllocatorSize != 0) {
+ subAllocatorSize = 0;
+ heap = null;
+ heapStart = 1;
+ // rarfree(HeapStart);
+ // Free temp fields
+ tempRarNode = null;
+ tempRarMemBlock1 = null;
+ tempRarMemBlock2 = null;
+ tempRarMemBlock3 = null;
+ }
+ }
+
+ public int GetAllocatedMemory() {
+ return subAllocatorSize;
+ };
+
+ public boolean startSubAllocator(int SASize) {
+ int t = SASize << 20;
+ if (subAllocatorSize == t) {
+ return true;
+ }
+ stopSubAllocator();
+ int allocSize = t / FIXED_UNIT_SIZE * UNIT_SIZE + UNIT_SIZE;
+
+ // adding space for freelist (needed for poiters)
+ // 1+ for null pointer
+ int realAllocSize = 1 + allocSize + 4 * N_INDEXES;
+ // adding space for an additional memblock
+ tempMemBlockPos = realAllocSize;
+ realAllocSize += RarMemBlock.size;
+
+ heap = new byte[realAllocSize];
+ heapStart = 1;
+ heapEnd = heapStart + allocSize - UNIT_SIZE;
+ subAllocatorSize = t;
+ // Bug fixed
+ freeListPos = heapStart + allocSize;
+ assert (realAllocSize - tempMemBlockPos == RarMemBlock.size) : realAllocSize
+ + " " + tempMemBlockPos + " " + RarMemBlock.size;
+
+ // Init freeList
+ for (int i = 0, pos = freeListPos; i < freeList.length; i++, pos += RarNode.size) {
+ freeList[i] = new RarNode(heap);
+ freeList[i].setAddress(pos);
+ }
+
+ // Init temp fields
+ tempRarNode = new RarNode(heap);
+ tempRarMemBlock1 = new RarMemBlock(heap);
+ tempRarMemBlock2 = new RarMemBlock(heap);
+ tempRarMemBlock3 = new RarMemBlock(heap);
+
+ return true;
+ }
+
+ private void glueFreeBlocks() {
+ RarMemBlock s0 = tempRarMemBlock1;
+ s0.setAddress(tempMemBlockPos);
+ RarMemBlock p = tempRarMemBlock2;
+ RarMemBlock p1 = tempRarMemBlock3;
+ int i, k, sz;
+ if (loUnit != hiUnit) {
+ heap[loUnit] = 0;
+ }
+ for (i = 0, s0.setPrev(s0), s0.setNext(s0); i < N_INDEXES; i++) {
+ while (freeList[i].getNext() != 0) {
+ p.setAddress(removeNode(i));// =(RAR_MEM_BLK*)RemoveNode(i);
+ p.insertAt(s0);// p->insertAt(&s0);
+ p.setStamp(0xFFFF);// p->Stamp=0xFFFF;
+ p.setNU(indx2Units[i]);// p->NU=Indx2Units[i];
+ }
+ }
+ for (p.setAddress(s0.getNext()); p.getAddress() != s0.getAddress(); p
+ .setAddress(p.getNext())) {
+ // while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU
+ // < 0x10000)
+ // Bug fixed
+ p1.setAddress(MBPtr(p.getAddress(), p.getNU()));
+ while (p1.getStamp() == 0xFFFF && p.getNU() + p1.getNU() < 0x10000) {
+ p1.remove();
+ p.setNU(p.getNU() + p1.getNU());// ->NU += p1->NU;
+ p1.setAddress(MBPtr(p.getAddress(), p.getNU()));
+ }
+ }
+ // while ((p=s0.next) != &s0)
+ // Bug fixed
+ p.setAddress(s0.getNext());
+ while (p.getAddress() != s0.getAddress()) {
+ for (p.remove(), sz = p.getNU(); sz > 128; sz -= 128, p
+ .setAddress(MBPtr(p.getAddress(), 128))) {
+ insertNode(p.getAddress(), N_INDEXES - 1);
+ }
+ if (indx2Units[i = units2Indx[sz - 1]] != sz) {
+ k = sz - indx2Units[--i];
+ insertNode(MBPtr(p.getAddress(), sz - k), k - 1);
+ }
+ insertNode(p.getAddress(), i);
+ p.setAddress(s0.getNext());
+ }
+ }
+
+ private int allocUnitsRare(int indx) {
+ if (glueCount == 0) {
+ glueCount = 255;
+ glueFreeBlocks();
+ if (freeList[indx].getNext() != 0) {
+ return removeNode(indx);
+ }
+ }
+ int i = indx;
+ do {
+ if (++i == N_INDEXES) {
+ glueCount--;
+ i = U2B(indx2Units[indx]);
+ int j = FIXED_UNIT_SIZE * indx2Units[indx];
+ if (fakeUnitsStart - pText > j) {
+ fakeUnitsStart -= j;
+ unitsStart -= i;
+ return unitsStart;
+ }
+ return (0);
+ }
+ } while (freeList[i].getNext() == 0);
+ int retVal = removeNode(i);
+ splitBlock(retVal, i, indx);
+ return retVal;
+ }
+
+ public int allocUnits(int NU) {
+ int indx = units2Indx[NU - 1];
+ if (freeList[indx].getNext() != 0) {
+ return removeNode(indx);
+ }
+ int retVal = loUnit;
+ loUnit += U2B(indx2Units[indx]);
+ if (loUnit <= hiUnit) {
+ return retVal;
+ }
+ loUnit -= U2B(indx2Units[indx]);
+ return allocUnitsRare(indx);
+ }
+
+ public int allocContext() {
+ if (hiUnit != loUnit)
+ return (hiUnit -= UNIT_SIZE);
+ if (freeList[0].getNext() != 0) {
+ return removeNode(0);
+ }
+ return allocUnitsRare(0);
+ }
+
+ public int expandUnits(int oldPtr, int OldNU) {
+ int i0 = units2Indx[OldNU - 1];
+ int i1 = units2Indx[OldNU - 1 + 1];
+ if (i0 == i1) {
+ return oldPtr;
+ }
+ int ptr = allocUnits(OldNU + 1);
+ if (ptr != 0) {
+ // memcpy(ptr,OldPtr,U2B(OldNU));
+ System.arraycopy(heap, oldPtr, heap, ptr, U2B(OldNU));
+ insertNode(oldPtr, i0);
+ }
+ return ptr;
+ }
+
+ public int shrinkUnits(int oldPtr, int oldNU, int newNU) {
+ // System.out.println("SubAllocator.shrinkUnits(" + OldPtr + ", " +
+ // OldNU + ", " + NewNU + ")");
+ int i0 = units2Indx[oldNU - 1];
+ int i1 = units2Indx[newNU - 1];
+ if (i0 == i1) {
+ return oldPtr;
+ }
+ if (freeList[i1].getNext() != 0) {
+ int ptr = removeNode(i1);
+ // memcpy(ptr,OldPtr,U2B(NewNU));
+ // for (int i = 0; i < U2B(NewNU); i++) {
+ // heap[ptr + i] = heap[OldPtr + i];
+ // }
+ System.arraycopy(heap, oldPtr, heap, ptr, U2B(newNU));
+ insertNode(oldPtr, i0);
+ return ptr;
+ } else {
+ splitBlock(oldPtr, i0, i1);
+ return oldPtr;
+ }
+ }
+
+ public void freeUnits(int ptr, int OldNU) {
+ insertNode(ptr, units2Indx[OldNU - 1]);
+ }
+
+ public int getFakeUnitsStart() {
+ return fakeUnitsStart;
+ }
+
+ public void setFakeUnitsStart(int fakeUnitsStart) {
+ this.fakeUnitsStart = fakeUnitsStart;
+ }
+
+ public int getHeapEnd() {
+ return heapEnd;
+ }
+
+ public int getPText() {
+ return pText;
+ }
+
+ public void setPText(int text) {
+ pText = text;
+ }
+
+ public void decPText(int dPText) {
+ setPText(getPText() - dPText);
+ }
+
+ public int getUnitsStart() {
+ return unitsStart;
+ }
+
+ public void setUnitsStart(int unitsStart) {
+ this.unitsStart = unitsStart;
+ }
+
+ public void initSubAllocator() {
+ int i, k;
+ Arrays
+ .fill(heap, freeListPos, freeListPos + sizeOfFreeList(),
+ (byte) 0);
+
+ pText = heapStart;
+
+ int size2 = FIXED_UNIT_SIZE
+ * (subAllocatorSize / 8 / FIXED_UNIT_SIZE * 7);
+ int realSize2 = size2 / FIXED_UNIT_SIZE * UNIT_SIZE;
+ int size1 = subAllocatorSize - size2;
+ int realSize1 = size1 / FIXED_UNIT_SIZE * UNIT_SIZE + size1
+ % FIXED_UNIT_SIZE;
+ hiUnit = heapStart + subAllocatorSize;
+ loUnit = unitsStart = heapStart + realSize1;
+ fakeUnitsStart = heapStart + size1;
+ hiUnit = loUnit + realSize2;
+
+ for (i = 0, k = 1; i < N1; i++, k += 1) {
+ indx2Units[i] = k & 0xff;
+ }
+ for (k++; i < N1 + N2; i++, k += 2) {
+ indx2Units[i] = k & 0xff;
+ }
+ for (k++; i < N1 + N2 + N3; i++, k += 3) {
+ indx2Units[i] = k & 0xff;
+ }
+ for (k++; i < (N1 + N2 + N3 + N4); i++, k += 4) {
+ indx2Units[i] = k & 0xff;
+ }
+
+ for (glueCount = 0, k = 0, i = 0; k < 128; k++) {
+ i += ((indx2Units[i] < (k + 1)) ? 1 : 0);
+ units2Indx[k] = i & 0xff;
+ }
+
+ }
+
+ private int sizeOfFreeList() {
+ return freeList.length * RarNode.size;
+ }
+
+ public byte[] getHeap() {
+ return heap;
+ }
+
+ // Debug
+ // public void dumpHeap() {
+ // File file = new File("P:\\test\\heapdumpj");
+ // OutputStream out = null;
+ // try {
+ // out = new FileOutputStream(file);
+ // out.write(heap, heapStart, heapEnd - heapStart);
+ // out.flush();
+ // System.out.println("Heap dumped to " + file.getAbsolutePath());
+ // }
+ // catch (IOException e) {
+ // e.printStackTrace();
+ // }
+ // finally {
+ // FileUtil.close(out);
+ // }
+ // }
+
+ // Debug
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("SubAllocator[");
+ buffer.append("\n subAllocatorSize=");
+ buffer.append(subAllocatorSize);
+ buffer.append("\n glueCount=");
+ buffer.append(glueCount);
+ buffer.append("\n heapStart=");
+ buffer.append(heapStart);
+ buffer.append("\n loUnit=");
+ buffer.append(loUnit);
+ buffer.append("\n hiUnit=");
+ buffer.append(hiUnit);
+ buffer.append("\n pText=");
+ buffer.append(pText);
+ buffer.append("\n unitsStart=");
+ buffer.append(unitsStart);
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/BitInput.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/BitInput.java
new file mode 100644
index 0000000..603cb77
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/BitInput.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class BitInput {
+ /**
+ * the max size of the input
+ */
+ public static final int MAX_SIZE = 0x8000;
+ protected int inAddr;
+ protected int inBit;
+ protected byte[] inBuf;
+
+ /**
+ *
+ */
+ public void InitBitInput()
+ {
+ inAddr=0;
+ inBit=0;
+ }
+ /**
+ * @param Bits
+ */
+ public void addbits(int Bits)
+ {
+ Bits+=inBit;
+ inAddr+=Bits>>3;
+ inBit=Bits&7;
+ }
+
+ /**
+ * @return the bits (unsigned short)
+ */
+ public int getbits()
+ {
+// int BitField=0;
+// BitField|=(int)(inBuf[inAddr] << 16)&0xFF0000;
+// BitField|=(int)(inBuf[inAddr+1] << 8)&0xff00;
+// BitField|=(int)(inBuf[inAddr+2])&0xFF;
+// BitField >>>= (8-inBit);
+// return (BitField & 0xffff);
+ return (((((inBuf[inAddr] & 0xff) << 16) +
+ ((inBuf[inAddr+1] & 0xff) << 8) +
+ ((inBuf[inAddr+2] & 0xff))) >>> (8-inBit)) & 0xffff);
+ }
+
+ /**
+ *
+ */
+ public BitInput()
+ {
+ inBuf=new byte[MAX_SIZE];
+ }
+
+ /**
+ * @param Bits add the bits
+ */
+ public void faddbits(int Bits)
+ {
+ addbits(Bits);
+ }
+
+
+ /**
+ * @return get the bits
+ */
+ public int fgetbits()
+ {
+ return(getbits());
+ }
+
+ /**
+ * Indicates an Overfow
+ * @param IncPtr how many bytes to inc
+ * @return true if an Oververflow would occur
+ */
+ public boolean Overflow(int IncPtr) {
+ return(inAddr+IncPtr>=MAX_SIZE);
+ }
+ public byte[] getInBuf()
+ {
+ return inBuf;
+ }
+
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/RarVM.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/RarVM.java
new file mode 100644
index 0000000..e6e6a01
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/RarVM.java
@@ -0,0 +1,1221 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+import java.util.List;
+import java.util.Vector;
+
+import com.github.junrar.crc.RarCRC;
+import com.github.junrar.io.Raw;
+
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class RarVM extends BitInput {
+
+ public static final int VM_MEMSIZE = 0x40000;
+
+ public static final int VM_MEMMASK = (VM_MEMSIZE - 1);
+
+ public static final int VM_GLOBALMEMADDR = 0x3C000;
+
+ public static final int VM_GLOBALMEMSIZE = 0x2000;
+
+ public static final int VM_FIXEDGLOBALSIZE = 64;
+
+ private static final int regCount = 8;
+
+ private static final long UINT_MASK = 0xffffFFFF;//((long)2*(long)Integer.MAX_VALUE);
+
+ private byte[] mem;
+
+ private int[] R = new int[regCount];
+
+ private int flags;
+
+ private int maxOpCount = 25000000;
+
+ private int codeSize;
+
+ private int IP;
+
+ public RarVM() {
+ mem = null;
+ }
+
+ public void init() {
+ if (mem == null) {
+ mem = new byte[VM_MEMSIZE + 4];
+ }
+ }
+
+ private boolean isVMMem(byte[] mem) {
+ return this.mem == mem;
+ }
+
+ private int getValue(boolean byteMode, byte[] mem, int offset) {
+ if (byteMode) {
+ if (isVMMem(mem)) {
+ return (mem[offset]);
+ } else {
+ return (mem[offset] & 0xff);
+ }
+ } else {
+ if (isVMMem(mem)) {
+ //little
+ return Raw.readIntLittleEndian(mem, offset);
+ } else
+ //big endian
+ return Raw.readIntBigEndian(mem, offset);
+ }
+ }
+
+ private void setValue(boolean byteMode, byte[] mem, int offset, int value) {
+ if (byteMode) {
+ if (isVMMem(mem)) {
+ mem[offset] = (byte) value;
+ } else {
+ mem[offset] = (byte) ((mem[offset] & 0x00) | (byte) (value & 0xff));
+ }
+ } else {
+ if (isVMMem(mem)) {
+ Raw.writeIntLittleEndian(mem, offset, value);
+// mem[offset + 0] = (byte) value;
+// mem[offset + 1] = (byte) (value >>> 8);
+// mem[offset + 2] = (byte) (value >>> 16);
+// mem[offset + 3] = (byte) (value >>> 24);
+ } else {
+ Raw.writeIntBigEndian(mem, offset, value);
+// mem[offset + 3] = (byte) value;
+// mem[offset + 2] = (byte) (value >>> 8);
+// mem[offset + 1] = (byte) (value >>> 16);
+// mem[offset + 0] = (byte) (value >>> 24);
+ }
+
+ }
+ // #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint
+ // *)Addr,Value)
+ }
+
+ public void setLowEndianValue(byte[] mem, int offset, int value) {
+ Raw.writeIntLittleEndian(mem, offset, value);
+// mem[offset + 0] = (byte) (value&0xff);
+// mem[offset + 1] = (byte) ((value >>> 8)&0xff);
+// mem[offset + 2] = (byte) ((value >>> 16)&0xff);
+// mem[offset + 3] = (byte) ((value >>> 24)&0xff);
+ }
+ public void setLowEndianValue(Vector<Byte> mem, int offset, int value) {
+ mem.set(offset + 0, Byte.valueOf((byte) (value&0xff))) ;
+ mem.set(offset + 1, Byte.valueOf((byte) ((value >>> 8)&0xff)));
+ mem.set(offset + 2, Byte.valueOf((byte) ((value >>> 16)&0xff) ));
+ mem.set(offset + 3, Byte.valueOf((byte) ((value >>> 24)&0xff))) ;
+ }
+ private int getOperand(VMPreparedOperand cmdOp) {
+ int ret = 0;
+ if (cmdOp.getType() == VMOpType.VM_OPREGMEM) {
+ int pos = (cmdOp.getOffset() + cmdOp.getBase()) & VM_MEMMASK;
+ ret = Raw.readIntLittleEndian(mem, pos);
+ } else {
+ int pos = cmdOp.getOffset();
+ ret = Raw.readIntLittleEndian(mem, pos);
+ }
+ return ret;
+ }
+
+ public void execute(VMPreparedProgram prg) {
+ for (int i = 0; i < prg.getInitR().length; i++) // memcpy(R,Prg->InitR,sizeof(Prg->InitR));
+ {
+ R[i] = prg.getInitR()[i];
+ }
+
+ long globalSize = Math
+ .min(prg.getGlobalData().size(), VM_GLOBALMEMSIZE) & 0xffFFffFF;
+ if (globalSize != 0) {
+ for (int i = 0; i < globalSize; i++) // memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize);
+ {
+ mem[VM_GLOBALMEMADDR + i] = prg.getGlobalData().get(i);
+ }
+
+ }
+ long staticSize = Math.min(prg.getStaticData().size(), VM_GLOBALMEMSIZE
+ - globalSize) & 0xffFFffFF;
+ if (staticSize != 0) {
+ for (int i = 0; i < staticSize; i++) // memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize);
+ {
+ mem[VM_GLOBALMEMADDR + (int) globalSize + i] = prg
+ .getStaticData().get(i);
+ }
+
+ }
+ R[7] = VM_MEMSIZE;
+ flags = 0;
+
+ List<VMPreparedCommand> preparedCode = prg.getAltCmd().size() != 0 ? prg
+ .getAltCmd()
+ : prg.getCmd();
+
+ if (!ExecuteCode(preparedCode, prg.getCmdCount())) {
+ preparedCode.get(0).setOpCode(VMCommands.VM_RET);
+ }
+ int newBlockPos = getValue(false, mem, VM_GLOBALMEMADDR + 0x20)
+ & VM_MEMMASK;
+ int newBlockSize = getValue(false, mem, VM_GLOBALMEMADDR + 0x1c)
+ & VM_MEMMASK;
+ if ((newBlockPos + newBlockSize) >= VM_MEMSIZE) {
+ newBlockPos = 0;
+ newBlockSize = 0;
+ }
+
+ prg.setFilteredDataOffset(newBlockPos);
+ prg.setFilteredDataSize(newBlockSize);
+
+ prg.getGlobalData().clear();
+
+ int dataSize = Math.min(getValue(false, mem, VM_GLOBALMEMADDR + 0x30),
+ VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE);
+ if (dataSize != 0) {
+ prg.getGlobalData().setSize(dataSize + VM_FIXEDGLOBALSIZE);
+ // ->GlobalData.Add(dataSize+VM_FIXEDGLOBALSIZE);
+
+ for (int i = 0; i < dataSize + VM_FIXEDGLOBALSIZE; i++) // memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE);
+ {
+ prg.getGlobalData().set(i, mem[VM_GLOBALMEMADDR + i]);
+ }
+ }
+ }
+
+ public byte[] getMem()
+ {
+ return mem;
+ }
+
+ private boolean setIP(int ip) {
+ if ((ip) >= codeSize) {
+ return (true);
+ }
+
+ if (--maxOpCount <= 0) {
+ return (false);
+ }
+
+ IP = ip;
+ return true;
+ }
+
+ private boolean ExecuteCode(List<VMPreparedCommand> preparedCode,
+ int cmdCount) {
+
+ maxOpCount = 25000000;
+ this.codeSize = cmdCount;
+ this.IP = 0;
+
+ while (true) {
+ VMPreparedCommand cmd = preparedCode.get(IP);
+ int op1 = getOperand(cmd.getOp1());
+ int op2 = getOperand(cmd.getOp2());
+ switch (cmd.getOpCode()) {
+ case VM_MOV:
+ setValue(cmd.isByteMode(), mem, op1, getValue(cmd.isByteMode(),
+ mem, op2)); // SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2));
+ break;
+ case VM_MOVB:
+ setValue(true, mem, op1, getValue(true, mem, op2));
+ break;
+ case VM_MOVD:
+ setValue(false, mem, op1, getValue(false, mem, op2));
+ break;
+
+ case VM_CMP: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int result = value1 - getValue(cmd.isByteMode(), mem, op2);
+
+ if (result == 0) {
+ flags = VMFlags.VM_FZ.getFlag();
+ } else {
+ flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
+ .getFlag());
+ }
+ }
+ break;
+
+ case VM_CMPB: {
+ int value1 = getValue(true, mem, op1);
+ int result = value1 - getValue(true, mem, op2);
+ if (result == 0) {
+ flags = VMFlags.VM_FZ.getFlag();
+ } else {
+ flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
+ .getFlag());
+ }
+ }
+ break;
+ case VM_CMPD: {
+ int value1 = getValue(false, mem, op1);
+ int result = value1 - getValue(false, mem, op2);
+ if (result == 0) {
+ flags = VMFlags.VM_FZ.getFlag();
+ } else {
+ flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
+ .getFlag());
+ }
+ }
+ break;
+
+ case VM_ADD: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int result = (int) ((((long) value1 + (long) getValue(cmd
+ .isByteMode(), mem, op2))) & 0xffffffff);
+ if (cmd.isByteMode()) {
+ result &= 0xff;
+ flags = (result < value1) ? 1
+ : 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
+ : ((result & 0x80) != 0) ? VMFlags.VM_FS
+ .getFlag() : 0);
+ // Flags=(Result<Value1)|(Result==0 ? VM_FZ:((Result&0x80) ?
+ // VM_FS:0));
+ } else
+ flags = (result < value1) ? 1
+ : 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
+ : (result & VMFlags.VM_FS.getFlag()));
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+
+ case VM_ADDB:
+ setValue(true, mem, op1,
+ (int) ((long) getValue(true, mem, op1) & 0xFFffFFff
+ + (long) getValue(true, mem, op2) & 0xFFffFFff));
+ break;
+ case VM_ADDD:
+ setValue(
+ false,
+ mem,
+ op1,
+ (int) ((long) getValue(false, mem, op1) & 0xFFffFFff
+ + (long) getValue(false, mem, op2) & 0xFFffFFff));
+ break;
+
+ case VM_SUB: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int result = (int) ((long) value1 & 0xffFFffFF
+ - (long) getValue(cmd.isByteMode(), mem, op2) & 0xFFffFFff);
+ flags = (result == 0) ? VMFlags.VM_FZ.getFlag()
+ : (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
+ .getFlag());
+ setValue(cmd.isByteMode(), mem, op1, result);// (Cmd->ByteMode,Op1,Result);
+ }
+ break;
+
+ case VM_SUBB:
+ setValue(true, mem, op1,
+ (int) ((long) getValue(true, mem, op1) & 0xFFffFFff
+ - (long) getValue(true, mem, op2) & 0xFFffFFff));
+ break;
+ case VM_SUBD:
+ setValue(
+ false,
+ mem,
+ op1,
+ (int) ((long) getValue(false, mem, op1) & 0xFFffFFff
+ - (long) getValue(false, mem, op2) & 0xFFffFFff));
+ break;
+
+ case VM_JZ:
+ if ((flags & VMFlags.VM_FZ.getFlag()) != 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_JNZ:
+ if ((flags & VMFlags.VM_FZ.getFlag()) == 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_INC: {
+ int result = (int) ((long) getValue(cmd.isByteMode(), mem, op1) & 0xFFffFFff + 1);
+ if (cmd.isByteMode()) {
+ result &= 0xff;
+ }
+
+ setValue(cmd.isByteMode(), mem, op1, result);
+ flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
+ & VMFlags.VM_FS.getFlag();
+ }
+ break;
+
+ case VM_INCB:
+ setValue(
+ true,
+ mem,
+ op1,
+ (int) ((long) getValue(true, mem, op1) & 0xFFffFFff + 1));
+ break;
+ case VM_INCD:
+ setValue(false, mem, op1, (int) ((long) getValue(false, mem,
+ op1) & 0xFFffFFff + 1));
+ break;
+
+ case VM_DEC: {
+ int result = (int) ((long) getValue(cmd.isByteMode(), mem, op1) & 0xFFffFFff - 1);
+ setValue(cmd.isByteMode(), mem, op1, result);
+ flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
+ & VMFlags.VM_FS.getFlag();
+ }
+ break;
+
+ case VM_DECB:
+ setValue(
+ true,
+ mem,
+ op1,
+ (int) ((long) getValue(true, mem, op1) & 0xFFffFFff - 1));
+ break;
+ case VM_DECD:
+ setValue(false, mem, op1, (int) ((long) getValue(false, mem,
+ op1) & 0xFFffFFff - 1));
+ break;
+
+ case VM_JMP:
+ setIP(getValue(false, mem, op1));
+ continue;
+ case VM_XOR: {
+ int result = getValue(cmd.isByteMode(), mem, op1)
+ ^ getValue(cmd.isByteMode(), mem, op2);
+ flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
+ & VMFlags.VM_FS.getFlag();
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_AND: {
+ int result = getValue(cmd.isByteMode(), mem, op1)
+ & getValue(cmd.isByteMode(), mem, op2);
+ flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
+ & VMFlags.VM_FS.getFlag();
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_OR: {
+ int result = getValue(cmd.isByteMode(), mem, op1)
+ | getValue(cmd.isByteMode(), mem, op2);
+ flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
+ & VMFlags.VM_FS.getFlag();
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_TEST: {
+ int result = getValue(cmd.isByteMode(), mem, op1)
+ & getValue(cmd.isByteMode(), mem, op2);
+ flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
+ & VMFlags.VM_FS.getFlag();
+ }
+ break;
+ case VM_JS:
+ if ((flags & VMFlags.VM_FS.getFlag()) != 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_JNS:
+ if ((flags & VMFlags.VM_FS.getFlag()) == 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_JB:
+ if ((flags & VMFlags.VM_FC.getFlag()) != 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_JBE:
+ if ((flags & (VMFlags.VM_FC.getFlag() | VMFlags.VM_FZ.getFlag())) != 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_JA:
+ if ((flags & (VMFlags.VM_FC.getFlag() | VMFlags.VM_FZ.getFlag())) == 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_JAE:
+ if ((flags & VMFlags.VM_FC.getFlag()) == 0) {
+ setIP(getValue(false, mem, op1));
+ continue;
+ }
+ break;
+ case VM_PUSH:
+ R[7] -= 4;
+ setValue(false, mem, R[7] & VM_MEMMASK, getValue(false, mem,
+ op1));
+ break;
+ case VM_POP:
+ setValue(false, mem, op1, getValue(false, mem, R[7]
+ & VM_MEMMASK));
+ R[7] += 4;
+ break;
+ case VM_CALL:
+ R[7] -= 4;
+ setValue(false, mem, R[7] & VM_MEMMASK, IP + 1);
+ setIP(getValue(false, mem, op1));
+ continue;
+ case VM_NOT:
+ setValue(cmd.isByteMode(), mem, op1, ~getValue(
+ cmd.isByteMode(), mem, op1));
+ break;
+ case VM_SHL: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int value2 = getValue(cmd.isByteMode(), mem, op2);
+ int result = value1 << value2;
+ flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
+ : (result & VMFlags.VM_FS.getFlag()))
+ | (((value1 << (value2 - 1)) & 0x80000000) != 0 ? VMFlags.VM_FC
+ .getFlag()
+ : 0);
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_SHR: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int value2 = getValue(cmd.isByteMode(), mem, op2);
+ int result = value1 >>> value2;
+ flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
+ : (result & VMFlags.VM_FS.getFlag()))
+ | ((value1 >>> (value2 - 1)) & VMFlags.VM_FC.getFlag());
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_SAR: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int value2 = getValue(cmd.isByteMode(), mem, op2);
+ int result = ((int) value1) >> value2;
+ flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
+ : (result & VMFlags.VM_FS.getFlag()))
+ | ((value1 >> (value2 - 1)) & VMFlags.VM_FC.getFlag());
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_NEG: {
+ int result = -getValue(cmd.isByteMode(), mem, op1);
+ flags = result == 0 ? VMFlags.VM_FZ.getFlag() : VMFlags.VM_FC
+ .getFlag()
+ | (result & VMFlags.VM_FS.getFlag());
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+
+ case VM_NEGB:
+ setValue(true, mem, op1, -getValue(true, mem, op1));
+ break;
+ case VM_NEGD:
+ setValue(false, mem, op1, -getValue(false, mem, op1));
+ break;
+ case VM_PUSHA: {
+ for (int i = 0, SP = R[7] - 4; i < regCount; i++, SP -= 4) {
+ setValue(false, mem, SP & VM_MEMMASK, R[i]);
+ }
+ R[7] -= regCount * 4;
+ }
+ break;
+ case VM_POPA: {
+ for (int i = 0, SP = R[7]; i < regCount; i++, SP += 4)
+ R[7 - i] = getValue(false, mem, SP & VM_MEMMASK);
+ }
+ break;
+ case VM_PUSHF:
+ R[7] -= 4;
+ setValue(false, mem, R[7] & VM_MEMMASK, flags);
+ break;
+ case VM_POPF:
+ flags = getValue(false, mem, R[7] & VM_MEMMASK);
+ R[7] += 4;
+ break;
+ case VM_MOVZX:
+ setValue(false, mem, op1, getValue(true, mem, op2));
+ break;
+ case VM_MOVSX:
+ setValue(false, mem, op1, (byte) getValue(true, mem, op2));
+ break;
+ case VM_XCHG: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ setValue(cmd.isByteMode(), mem, op1, getValue(cmd.isByteMode(),
+ mem, op2));
+ setValue(cmd.isByteMode(), mem, op2, value1);
+ }
+ break;
+ case VM_MUL: {
+ int result = (int) (((long) getValue(cmd.isByteMode(), mem, op1)
+ & 0xFFffFFff
+ * (long) getValue(cmd.isByteMode(), mem, op2) & 0xFFffFFff) & 0xFFffFFff);
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_DIV: {
+ int divider = getValue(cmd.isByteMode(), mem, op2);
+ if (divider != 0) {
+ int result = getValue(cmd.isByteMode(), mem, op1) / divider;
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ }
+ break;
+ case VM_ADC: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int FC = (flags & VMFlags.VM_FC.getFlag());
+ int result = (int) ((long) value1 & 0xFFffFFff
+ + (long) getValue(cmd.isByteMode(), mem, op2)
+ & 0xFFffFFff + (long) FC & 0xFFffFFff);
+ if (cmd.isByteMode()) {
+ result &= 0xff;
+ }
+
+ flags = (result < value1 || result == value1 && FC != 0) ? 1
+ : 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
+ : (result & VMFlags.VM_FS.getFlag()));
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+ case VM_SBB: {
+ int value1 = getValue(cmd.isByteMode(), mem, op1);
+ int FC = (flags & VMFlags.VM_FC.getFlag());
+ int result = (int) ((long) value1 & 0xFFffFFff
+ - (long) getValue(cmd.isByteMode(), mem, op2)
+ & 0xFFffFFff - (long) FC & 0xFFffFFff);
+ if (cmd.isByteMode()) {
+ result &= 0xff;
+ }
+ flags = (result > value1 || result == value1 && FC != 0) ? 1
+ : 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
+ : (result & VMFlags.VM_FS.getFlag()));
+ setValue(cmd.isByteMode(), mem, op1, result);
+ }
+ break;
+
+ case VM_RET:
+ if (R[7] >= VM_MEMSIZE) {
+ return (true);
+ }
+ setIP(getValue(false, mem, R[7] & VM_MEMMASK));
+ R[7] += 4;
+ continue;
+
+ case VM_STANDARD:
+ ExecuteStandardFilter(VMStandardFilters.findFilter(cmd.getOp1()
+ .getData()));
+ break;
+ case VM_PRINT:
+ break;
+ }
+ IP++;
+ --maxOpCount;
+ }
+ }
+
+ public void prepare(byte[] code, int codeSize, VMPreparedProgram prg) {
+ InitBitInput();
+ int cpLength = Math.min(MAX_SIZE, codeSize);
+ for (int i = 0; i < cpLength; i++) // memcpy(inBuf,Code,Min(CodeSize,BitInput::MAX_SIZE));
+ {
+ inBuf[i] |= code[i];
+ }
+
+ byte xorSum = 0;
+ for (int i = 1; i < codeSize; i++) {
+ xorSum ^= code[i];
+ }
+
+ faddbits(8);
+
+ prg.setCmdCount(0);
+ if (xorSum == code[0]) {
+ VMStandardFilters filterType = IsStandardFilter(code, codeSize);
+ if (filterType != VMStandardFilters.VMSF_NONE) {
+
+ VMPreparedCommand curCmd = new VMPreparedCommand();
+ curCmd.setOpCode(VMCommands.VM_STANDARD);
+ curCmd.getOp1().setData(filterType.getFilter());
+ curCmd.getOp1().setType(VMOpType.VM_OPNONE);
+ curCmd.getOp2().setType(VMOpType.VM_OPNONE);
+ codeSize = 0;
+ prg.getCmd().add(curCmd);
+ prg.setCmdCount(prg.getCmdCount()+1);
+ // TODO
+ // curCmd->Op1.Data=FilterType;
+ // >>>>>> CurCmd->Op1.Addr=&CurCmd->Op1.Data; <<<<<<<<<< not set
+ // do i need to ?
+ // >>>>>> CurCmd->Op2.Addr=&CurCmd->Op2.Data; <<<<<<<<<< "
+ // CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE;
+ // CodeSize=0;
+ }
+ int dataFlag = fgetbits();
+ faddbits(1);
+
+ // Read static data contained in DB operators. This data cannot be
+ // changed,
+ // it is a part of VM code, not a filter parameter.
+
+ if ((dataFlag & 0x8000) != 0) {
+ long dataSize = (long) ((long) ReadData(this) & 0xffFFffFF + 1);
+ for (int i = 0; inAddr < codeSize && i < dataSize; i++) {
+ prg.getStaticData().add(
+ Byte.valueOf((byte) (fgetbits() >> 8)));
+ faddbits(8);
+ }
+ }
+
+ while (inAddr < codeSize) {
+ VMPreparedCommand curCmd = new VMPreparedCommand();
+ int data = fgetbits();
+ if ((data & 0x8000) == 0) {
+ curCmd.setOpCode(VMCommands.findVMCommand((data >> 12)));
+ faddbits(4);
+ } else {
+ curCmd.setOpCode(VMCommands
+ .findVMCommand((data >> 10) - 24));
+ faddbits(6);
+ }
+ if ((VMCmdFlags.VM_CmdFlags[curCmd.getOpCode().getVMCommand()] & VMCmdFlags.VMCF_BYTEMODE) != 0) {
+ curCmd.setByteMode((fgetbits() >> 15) == 1 ? true : false);
+ faddbits(1);
+ } else {
+ curCmd.setByteMode(false);
+ }
+ curCmd.getOp1().setType(VMOpType.VM_OPNONE);
+ curCmd.getOp2().setType(VMOpType.VM_OPNONE);
+
+ int opNum = (VMCmdFlags.VM_CmdFlags[curCmd.getOpCode()
+ .getVMCommand()] & VMCmdFlags.VMCF_OPMASK);
+ // TODO >>> CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; <<<???
+ if (opNum > 0) {
+ decodeArg(curCmd.getOp1(), curCmd.isByteMode());
+ if (opNum == 2)
+ decodeArg(curCmd.getOp2(), curCmd.isByteMode());
+ else {
+ if (curCmd.getOp1().getType() == VMOpType.VM_OPINT
+ && (VMCmdFlags.VM_CmdFlags[curCmd.getOpCode()
+ .getVMCommand()] & (VMCmdFlags.VMCF_JUMP | VMCmdFlags.VMCF_PROC)) != 0) {
+ int distance = curCmd.getOp1().getData();
+ if (distance >= 256)
+ distance -= 256;
+ else {
+ if (distance >= 136) {
+ distance -= 264;
+ } else {
+ if (distance >= 16) {
+ distance -= 8;
+ } else {
+ if (distance >= 8) {
+ distance -= 16;
+ }
+ }
+ }
+ distance += prg.getCmdCount();
+ }
+ curCmd.getOp1().setData(distance);
+ }
+ }
+ }
+ prg.setCmdCount(prg.getCmdCount() + 1);
+ prg.getCmd().add(curCmd);
+ }
+ }
+ VMPreparedCommand curCmd = new VMPreparedCommand();
+ curCmd.setOpCode(VMCommands.VM_RET);
+ // TODO CurCmd->Op1.Addr=&CurCmd->Op1.Data;
+ // CurCmd->Op2.Addr=&CurCmd->Op2.Data;
+ curCmd.getOp1().setType(VMOpType.VM_OPNONE);
+ curCmd.getOp2().setType(VMOpType.VM_OPNONE);
+
+ // for (int i=0;i<prg.getCmdCount();i++)
+ // {
+ // VM_PreparedCommand *Cmd=&Prg->Cmd[I];
+ // if (Cmd->Op1.Addr==NULL)
+ // Cmd->Op1.Addr=&Cmd->Op1.Data;
+ // if (Cmd->Op2.Addr==NULL)
+ // Cmd->Op2.Addr=&Cmd->Op2.Data;
+ // }
+
+ prg.getCmd().add(curCmd);
+ prg.setCmdCount(prg.getCmdCount()+1);
+ // #ifdef VM_OPTIMIZE
+ if (codeSize != 0) {
+ optimize(prg);
+ }
+ }
+
+ private void decodeArg(VMPreparedOperand op, boolean byteMode) {
+ int data = fgetbits();
+ if ((data & 0x8000) != 0) {
+ op.setType(VMOpType.VM_OPREG);
+ op.setData((data >> 12) & 7);
+ op.setOffset(op.getData());
+ faddbits(4);
+ } else {
+ if ((data & 0xc000) == 0) {
+ op.setType(VMOpType.VM_OPINT);
+ if (byteMode) {
+ op.setData((data >> 6) & 0xff);
+ faddbits(10);
+ } else {
+ faddbits(2);
+ op.setData(ReadData(this));
+ }
+ } else {
+ op.setType(VMOpType.VM_OPREGMEM);
+ if ((data & 0x2000) == 0) {
+ op.setData((data >> 10) & 7);
+ op.setOffset(op.getData());
+ op.setBase(0);
+ faddbits(6);
+ } else {
+ if ((data & 0x1000) == 0) {
+ op.setData((data >> 9) & 7);
+ op.setOffset(op.getData());
+ faddbits(7);
+ } else {
+ op.setData(0);
+ faddbits(4);
+ }
+ op.setBase(ReadData(this));
+ }
+ }
+ }
+
+ }
+
+ private void optimize(VMPreparedProgram prg) {
+ List<VMPreparedCommand> commands = prg.getCmd();
+
+ for (VMPreparedCommand cmd : commands) {
+ switch (cmd.getOpCode()) {
+ case VM_MOV:
+ cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_MOVB
+ : VMCommands.VM_MOVD);
+ continue;
+ case VM_CMP:
+ cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_CMPB
+ : VMCommands.VM_CMPD);
+ continue;
+ }
+ if ((VMCmdFlags.VM_CmdFlags[cmd.getOpCode().getVMCommand()] & VMCmdFlags.VMCF_CHFLAGS) == 0) {
+ continue;
+ }
+ boolean flagsRequired = false;
+
+ for (int i = commands.indexOf(cmd) + 1; i < commands.size(); i++) {
+ int flags = VMCmdFlags.VM_CmdFlags[commands.get(i).getOpCode()
+ .getVMCommand()];
+ if ((flags & (VMCmdFlags.VMCF_JUMP | VMCmdFlags.VMCF_PROC | VMCmdFlags.VMCF_USEFLAGS)) != 0) {
+ flagsRequired = true;
+ break;
+ }
+ if ((flags & VMCmdFlags.VMCF_CHFLAGS) != 0) {
+ break;
+ }
+ }
+ if (flagsRequired) {
+ continue;
+ }
+ switch (cmd.getOpCode()) {
+ case VM_ADD:
+ cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_ADDB
+ : VMCommands.VM_ADDD);
+ continue;
+ case VM_SUB:
+ cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_SUBB
+ : VMCommands.VM_SUBD);
+ continue;
+ case VM_INC:
+ cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_INCB
+ : VMCommands.VM_INCD);
+ continue;
+ case VM_DEC:
+ cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_DECB
+ : VMCommands.VM_DECD);
+ continue;
+ case VM_NEG:
+ cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_NEGB
+ : VMCommands.VM_NEGD);
+ continue;
+ }
+ }
+
+ }
+
+ public static int ReadData(BitInput rarVM) {
+ int data = rarVM.fgetbits();
+ switch (data & 0xc000) {
+ case 0:
+ rarVM.faddbits(6);
+ return ((data >> 10) & 0xf);
+ case 0x4000:
+ if ((data & 0x3c00) == 0) {
+ data = 0xffffff00 | ((data >> 2) & 0xff);
+ rarVM.faddbits(14);
+ } else {
+ data = (data >> 6) & 0xff;
+ rarVM.faddbits(10);
+ }
+ return (data);
+ case 0x8000:
+ rarVM.faddbits(2);
+ data = rarVM.fgetbits();
+ rarVM.faddbits(16);
+ return (data);
+ default:
+ rarVM.faddbits(2);
+ data = (rarVM.fgetbits() << 16);
+ rarVM.faddbits(16);
+ data |= rarVM.fgetbits();
+ rarVM.faddbits(16);
+ return (data);
+ }
+ }
+
+ private VMStandardFilters IsStandardFilter(byte[] code, int codeSize) {
+ VMStandardFilterSignature stdList[]={
+ new VMStandardFilterSignature(53, 0xad576887, VMStandardFilters.VMSF_E8),
+ new VMStandardFilterSignature(57, 0x3cd7e57e, VMStandardFilters.VMSF_E8E9),
+ new VMStandardFilterSignature(120, 0x3769893f, VMStandardFilters.VMSF_ITANIUM),
+ new VMStandardFilterSignature(29, 0x0e06077d, VMStandardFilters.VMSF_DELTA),
+ new VMStandardFilterSignature(149, 0x1c2c5dc8, VMStandardFilters.VMSF_RGB),
+ new VMStandardFilterSignature(216, 0xbc85e701, VMStandardFilters.VMSF_AUDIO),
+ new VMStandardFilterSignature(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE)
+ };
+ int CodeCRC = RarCRC.checkCrc(0xffffffff,code,0,code.length)^0xffffffff;
+ for (int i=0;i<stdList.length;i++){
+ if (stdList[i].getCRC()==CodeCRC && stdList[i].getLength()==code.length){
+ return(stdList[i].getType());
+ }
+
+ }
+ return(VMStandardFilters.VMSF_NONE);
+ }
+
+ private void ExecuteStandardFilter(VMStandardFilters filterType) {
+ switch(filterType)
+ {
+ case VMSF_E8:
+ case VMSF_E8E9:
+ {
+ int dataSize=R[4];
+ long fileOffset=R[6]&0xFFffFFff;
+
+ if (dataSize>=VM_GLOBALMEMADDR){
+ break;
+ }
+ int fileSize=0x1000000;
+ byte cmpByte2=(byte) ((filterType==VMStandardFilters.VMSF_E8E9) ? 0xe9:0xe8);
+ for (int curPos=0;curPos<dataSize-4;)
+ {
+ byte curByte=mem[curPos++];
+ if (curByte==0xe8 || curByte==cmpByte2)
+ {
+// #ifdef PRESENT_INT32
+// sint32 Offset=CurPos+FileOffset;
+// sint32 Addr=GET_VALUE(false,Data);
+// if (Addr<0)
+// {
+// if (Addr+Offset>=0)
+// SET_VALUE(false,Data,Addr+FileSize);
+// }
+// else
+// if (Addr<FileSize)
+// SET_VALUE(false,Data,Addr-Offset);
+// #else
+ long offset=curPos+fileOffset;
+ long Addr=getValue(false,mem,curPos);
+ if ((Addr & 0x80000000)!=0)
+ {
+ if (((Addr+offset) & 0x80000000)==0)
+ setValue(false,mem,curPos,(int)Addr+fileSize);
+ }
+ else {
+ if (((Addr-fileSize) & 0x80000000)!=0){
+ setValue(false,mem,curPos,(int)(Addr-offset));
+ }
+ }
+// #endif
+ curPos+=4;
+ }
+ }
+ }
+ break;
+ case VMSF_ITANIUM:
+ {
+
+ int dataSize=R[4];
+ long fileOffset=R[6]&0xFFffFFff;
+
+ if (dataSize>=VM_GLOBALMEMADDR){
+ break;
+ }
+ int curPos=0;
+ final byte Masks[]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0};
+ fileOffset>>>=4;
+
+ while (curPos<dataSize-21)
+ {
+ int Byte=(mem[curPos]&0x1f)-0x10;
+ if (Byte>=0)
+ {
+
+ byte cmdMask=Masks[Byte];
+ if (cmdMask!=0)
+ for (int i=0;i<=2;i++)
+ if ((cmdMask & (1<<i))!=0)
+ {
+ int startPos=i*41+5;
+ int opType=filterItanium_GetBits(curPos,startPos+37,4);
+ if (opType==5)
+ {
+ int offset=filterItanium_GetBits(curPos,startPos+13,20);
+ filterItanium_SetBits(curPos,(int)(offset-fileOffset)&0xfffff,startPos+13,20);
+ }
+ }
+ }
+ curPos+=16;
+ fileOffset++;
+ }
+ }
+ break;
+ case VMSF_DELTA:
+ {
+ int dataSize=R[4]&0xFFffFFff;
+ int channels=R[0]&0xFFffFFff;
+ int srcPos=0;
+ int border=(dataSize*2) &0xFFffFFff;
+ setValue(false,mem,VM_GLOBALMEMADDR+0x20,(int)dataSize);
+ if (dataSize>=VM_GLOBALMEMADDR/2){
+ break;
+ }
+// bytes from same channels are grouped to continual data blocks,
+// so we need to place them back to their interleaving positions
+
+ for (int curChannel=0;curChannel<channels;curChannel++)
+ {
+ byte PrevByte=0;
+ for (int destPos=dataSize+curChannel;destPos<border;destPos+=channels){
+ mem[destPos]=(PrevByte-=mem[srcPos++]);
+ }
+
+ }
+ }
+ break;
+ case VMSF_RGB:
+ {
+ // byte *SrcData=Mem,*DestData=SrcData+DataSize;
+ int dataSize=R[4],width=R[0]-3,posR=R[1];
+ int channels=3;
+ int srcPos = 0;
+ int destDataPos = dataSize;
+ setValue(false,mem,VM_GLOBALMEMADDR+0x20,dataSize);
+ if (dataSize>=VM_GLOBALMEMADDR/2 || posR<0){
+ break;
+ }
+ for (int curChannel=0;curChannel<channels;curChannel++)
+ {
+ long prevByte=0;
+
+ for (int i=curChannel;i<dataSize;i+=channels)
+ {
+ long predicted;
+ int upperPos=i-width;
+ if (upperPos>=3)
+ {
+ int upperDataPos=destDataPos+upperPos;
+ int upperByte=mem[(int)upperDataPos]&0xff;
+ int upperLeftByte=mem[upperDataPos-3]&0xff;
+ predicted=prevByte+upperByte-upperLeftByte;
+ int pa=Math.abs((int)(predicted-prevByte));
+ int pb=Math.abs((int)(predicted-upperByte));
+ int pc=Math.abs((int)(predicted-upperLeftByte));
+ if (pa<=pb && pa<=pc){
+ predicted=prevByte;
+ }
+ else{
+ if (pb<=pc){
+ predicted=upperByte;
+ }
+ else{
+ predicted=upperLeftByte;
+ }
+ }
+ }
+ else{
+ predicted=prevByte;
+ }
+
+ prevByte=(predicted-mem[srcPos++]&0xff)&0xff;
+ mem[destDataPos+i]=(byte)(prevByte&0xff);
+
+ }
+ }
+ for (int i=posR,border=dataSize-2;i<border;i+=3)
+ {
+ byte G=mem[destDataPos+i+1];
+ mem[destDataPos+i]+=G;
+ mem[destDataPos+i+2]+=G;
+ }
+ }
+ break;
+ case VMSF_AUDIO:
+ {
+ int dataSize=R[4],channels=R[0];
+ int srcPos = 0;
+ int destDataPos = dataSize;
+ //byte *SrcData=Mem,*DestData=SrcData+DataSize;
+ setValue(false,mem,VM_GLOBALMEMADDR+0x20,dataSize);
+ if (dataSize>=VM_GLOBALMEMADDR/2){
+ break;
+ }
+ for (int curChannel=0;curChannel<channels;curChannel++)
+ {
+ long prevByte=0;
+ long prevDelta=0;
+ long Dif[] = new long[7];
+ int D1=0,D2=0,D3;
+ int K1=0,K2=0,K3=0;
+
+ for (int i=curChannel,byteCount=0;i<dataSize;i+=channels,byteCount++)
+ {
+ D3=D2;
+ D2=(int)prevDelta-D1;
+ D1=(int)prevDelta;
+
+ long predicted=8*prevByte+K1*D1+K2*D2+K3*D3;
+ predicted=(predicted>>>3) & 0xff;
+
+ long curByte=mem[srcPos++]&0xff;
+
+ predicted = (predicted - curByte)&UINT_MASK;
+ mem[destDataPos+i]=(byte)predicted;
+ prevDelta=(byte)(predicted-prevByte);
+ prevByte=predicted;
+
+ int D=((byte)curByte)<<3;
+
+ Dif[0]+=Math.abs(D);
+ Dif[1]+=Math.abs(D-D1);
+ Dif[2]+=Math.abs(D+D1);
+ Dif[3]+=Math.abs(D-D2);
+ Dif[4]+=Math.abs(D+D2);
+ Dif[5]+=Math.abs(D-D3);
+ Dif[6]+=Math.abs(D+D3);
+
+ if ((byteCount & 0x1f)==0)
+ {
+ long minDif=Dif[0], numMinDif=0;
+ Dif[0]=0;
+ for (int j=1;j<Dif.length;j++)
+ {
+ if (Dif[j]<minDif)
+ {
+ minDif=Dif[j];
+ numMinDif=j;
+ }
+ Dif[j]=0;
+ }
+ switch((int)numMinDif)
+ {
+ case 1: if (K1>=-16) K1--; break;
+ case 2: if (K1 < 16) K1++; break;
+ case 3: if (K2>=-16) K2--; break;
+ case 4: if (K2 < 16) K2++; break;
+ case 5: if (K3>=-16) K3--; break;
+ case 6: if (K3 < 16) K3++; break;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case VMSF_UPCASE:
+ {
+ int dataSize=R[4],srcPos=0,destPos=dataSize;
+ if (dataSize>=VM_GLOBALMEMADDR/2){
+ break;
+ }
+ while (srcPos<dataSize)
+ {
+ byte curByte=mem[srcPos++];
+ if (curByte==2 && (curByte=mem[srcPos++])!=2){
+ curByte-=32;
+ }
+ mem[destPos++]=curByte;
+ }
+ setValue(false,mem,VM_GLOBALMEMADDR+0x1c,destPos-dataSize);
+ setValue(false,mem,VM_GLOBALMEMADDR+0x20,dataSize);
+ }
+ break;
+ }
+
+ }
+
+ private void filterItanium_SetBits(int curPos, int bitField, int bitPos, int bitCount) {
+ int inAddr=bitPos/8;
+ int inBit=bitPos&7;
+ int andMask=0xffffffff>>>(32-bitCount);
+ andMask=~(andMask<<inBit);
+
+ bitField<<=inBit;
+
+ for (int i=0;i<4;i++)
+ {
+ mem[curPos+inAddr+i]&=andMask;
+ mem[curPos+inAddr+i]|=bitField;
+ andMask=(andMask>>>8)|0xff000000;
+ bitField>>>=8;
+ }
+
+ }
+
+ private int filterItanium_GetBits(int curPos, int bitPos, int bitCount) {
+ int inAddr=bitPos/8;
+ int inBit=bitPos&7;
+ int bitField=(int)(mem[curPos+inAddr++]&0xff);
+ bitField|=(int) ((mem[curPos+inAddr++]&0xff) << 8);
+ bitField|=(int) ((mem[curPos+inAddr++]&0xff) << 16);
+ bitField|=(int) ((mem[curPos+inAddr]&0xff) << 24);
+ bitField >>>= inBit;
+ return(bitField & (0xffffffff>>>(32-bitCount)));
+ }
+
+
+ public void setMemory(int pos,byte[] data,int offset,int dataSize)
+ {
+ if (pos<VM_MEMSIZE){ //&& data!=Mem+Pos)
+ //memmove(Mem+Pos,Data,Min(DataSize,VM_MEMSIZE-Pos));
+ for (int i = 0; i < Math.min(data.length-offset,dataSize); i++) {
+ if((VM_MEMSIZE-pos)<i){
+ break;
+ }
+ mem[pos+i] = data[offset+i];
+ }
+ }
+ }
+
+
+}
+
+// \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCmdFlags.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCmdFlags.java
new file mode 100644
index 0000000..2ccfa46
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCmdFlags.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 01.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class VMCmdFlags {
+ public static final byte VMCF_OP0 = 0;
+ public static final byte VMCF_OP1 = 1;
+ public static final byte VMCF_OP2 = 2;
+ public static final byte VMCF_OPMASK = 3;
+ public static final byte VMCF_BYTEMODE = 4;
+ public static final byte VMCF_JUMP = 8;
+ public static final byte VMCF_PROC = 16;
+ public static final byte VMCF_USEFLAGS = 32;
+ public static final byte VMCF_CHFLAGS = 64;
+
+ public static byte VM_CmdFlags[]=
+ {
+ /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_JMP */ VMCF_OP1 | VMCF_JUMP ,
+ /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_PUSH */ VMCF_OP1 ,
+ /* VM_POP */ VMCF_OP1 ,
+ /* VM_CALL */ VMCF_OP1 | VMCF_PROC ,
+ /* VM_RET */ VMCF_OP0 | VMCF_PROC ,
+ /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE ,
+ /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_PUSHA */ VMCF_OP0 ,
+ /* VM_POPA */ VMCF_OP0 ,
+ /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS ,
+ /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS ,
+ /* VM_MOVZX */ VMCF_OP2 ,
+ /* VM_MOVSX */ VMCF_OP2 ,
+ /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS ,
+ /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS ,
+ /* VM_PRINT */ VMCF_OP0
+ };
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCommands.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCommands.java
new file mode 100644
index 0000000..e52911a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMCommands.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum VMCommands {
+ VM_MOV(0), VM_CMP(1), VM_ADD(2), VM_SUB(3), VM_JZ(4), VM_JNZ(5), VM_INC(6), VM_DEC(
+ 7), VM_JMP(8), VM_XOR(9), VM_AND(10), VM_OR(11), VM_TEST(12), VM_JS(
+ 13), VM_JNS(14), VM_JB(15), VM_JBE(16), VM_JA(17), VM_JAE(18), VM_PUSH(
+ 19), VM_POP(20), VM_CALL(21), VM_RET(22), VM_NOT(23), VM_SHL(24), VM_SHR(
+ 25), VM_SAR(26), VM_NEG(27), VM_PUSHA(28), VM_POPA(29), VM_PUSHF(30), VM_POPF(
+ 31), VM_MOVZX(32), VM_MOVSX(33), VM_XCHG(34), VM_MUL(35), VM_DIV(36), VM_ADC(
+ 37), VM_SBB(38), VM_PRINT(39),
+
+ // #ifdef VM_OPTIMIZE
+ VM_MOVB(40), VM_MOVD(41), VM_CMPB(42), VM_CMPD(43),
+
+ VM_ADDB(44), VM_ADDD(45), VM_SUBB(46), VM_SUBD(47), VM_INCB(48), VM_INCD(49), VM_DECB(
+ 50), VM_DECD(51), VM_NEGB(52), VM_NEGD(53),
+ // #endif*/
+
+ VM_STANDARD(54);
+
+ private int vmCommand;
+
+ private VMCommands(int vmCommand) {
+ this.vmCommand = vmCommand;
+ }
+
+ public int getVMCommand() {
+ return vmCommand;
+ }
+
+ public boolean equals(int vmCommand) {
+ return this.vmCommand == vmCommand;
+ }
+
+ public static VMCommands findVMCommand(int vmCommand) {
+ if (VM_MOV.equals(vmCommand)) {
+ return VM_MOV;
+ }
+ if (VM_CMP.equals(vmCommand)) {
+ return VM_CMP;
+ }
+ if (VM_ADD.equals(vmCommand)) {
+ return VM_ADD;
+ }
+ if (VM_SUB.equals(vmCommand)) {
+ return VM_SUB;
+ }
+ if (VM_JZ.equals(vmCommand)) {
+ return VM_JZ;
+ }
+ if (VM_JNZ.equals(vmCommand)) {
+ return VM_JNZ;
+ }
+ if (VM_INC.equals(vmCommand)) {
+ return VM_INC;
+ }
+ if (VM_DEC.equals(vmCommand)) {
+ return VM_DEC;
+ }
+ if (VM_JMP.equals(vmCommand)) {
+ return VM_JMP;
+ }
+ if (VM_XOR.equals(vmCommand)) {
+ return VM_XOR;
+ }
+ if (VM_AND.equals(vmCommand)) {
+ return VM_AND;
+ }
+ if (VM_OR.equals(vmCommand)) {
+ return VM_OR;
+ }
+ if (VM_TEST.equals(vmCommand)) {
+ return VM_TEST;
+ }
+ if (VM_JS.equals(vmCommand)) {
+ return VM_JS;
+ }
+ if (VM_JNS.equals(vmCommand)) {
+ return VM_JNS;
+ }
+ if (VM_JB.equals(vmCommand)) {
+ return VM_JB;
+ }
+ if (VM_JBE.equals(vmCommand)) {
+ return VM_JBE;
+ }
+ if (VM_JA.equals(vmCommand)) {
+ return VM_JA;
+ }
+ if (VM_JAE.equals(vmCommand)) {
+ return VM_JAE;
+ }
+ if (VM_PUSH.equals(vmCommand)) {
+ return VM_PUSH;
+ }
+ if (VM_POP.equals(vmCommand)) {
+ return VM_POP;
+ }
+ if (VM_CALL.equals(vmCommand)) {
+ return VM_CALL;
+ }
+ if (VM_RET.equals(vmCommand)) {
+ return VM_RET;
+ }
+ if (VM_NOT.equals(vmCommand)) {
+ return VM_NOT;
+ }
+ if (VM_SHL.equals(vmCommand)) {
+ return VM_SHL;
+ }
+ if (VM_SHR.equals(vmCommand)) {
+ return VM_SHR;
+ }
+ if (VM_SAR.equals(vmCommand)) {
+ return VM_SAR;
+ }
+ if (VM_NEG.equals(vmCommand)) {
+ return VM_NEG;
+ }
+ if (VM_PUSHA.equals(vmCommand)) {
+ return VM_PUSHA;
+ }
+ if (VM_POPA.equals(vmCommand)) {
+ return VM_POPA;
+ }
+ if (VM_PUSHF.equals(vmCommand)) {
+ return VM_PUSHF;
+ }
+ if (VM_POPF.equals(vmCommand)) {
+ return VM_POPF;
+ }
+ if (VM_MOVZX.equals(vmCommand)) {
+ return VM_MOVZX;
+ }
+ if (VM_MOVSX.equals(vmCommand)) {
+ return VM_MOVSX;
+ }
+ if (VM_XCHG.equals(vmCommand)) {
+ return VM_XCHG;
+ }
+ if (VM_MUL.equals(vmCommand)) {
+ return VM_MUL;
+ }
+ if (VM_DIV.equals(vmCommand)) {
+ return VM_DIV;
+ }
+ if (VM_ADC.equals(vmCommand)) {
+ return VM_ADC;
+ }
+ if (VM_SBB.equals(vmCommand)) {
+ return VM_SBB;
+ }
+ if (VM_PRINT.equals(vmCommand)) {
+ return VM_PRINT;
+ }
+ if (VM_MOVB.equals(vmCommand)) {
+ return VM_MOVB;
+ }
+ if (VM_MOVD.equals(vmCommand)) {
+ return VM_MOVD;
+ }
+ if (VM_CMPB.equals(vmCommand)) {
+ return VM_CMPB;
+ }
+ if (VM_CMPD.equals(vmCommand)) {
+ return VM_CMPD;
+ }
+ if (VM_ADDB.equals(vmCommand)) {
+ return VM_ADDB;
+ }
+ if (VM_ADDD.equals(vmCommand)) {
+ return VM_ADDD;
+ }
+ if (VM_SUBB.equals(vmCommand)) {
+ return VM_SUBB;
+ }
+ if (VM_SUBD.equals(vmCommand)) {
+ return VM_SUBD;
+ }
+ if (VM_INCB.equals(vmCommand)) {
+ return VM_INCB;
+ }
+ if (VM_INCD.equals(vmCommand)) {
+ return VM_INCD;
+ }
+ if (VM_DECB.equals(vmCommand)) {
+ return VM_DECB;
+ }
+ if (VM_DECD.equals(vmCommand)) {
+ return VM_DECD;
+ }
+ if (VM_NEGB.equals(vmCommand)) {
+ return VM_NEGB;
+ }
+ if (VM_NEGD.equals(vmCommand)) {
+ return VM_NEGD;
+ }
+ if (VM_STANDARD.equals(vmCommand)) {
+ return VM_STANDARD;
+ }
+ return null;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMFlags.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMFlags.java
new file mode 100644
index 0000000..d2b997b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMFlags.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum VMFlags {
+ /**
+ *
+ */
+ VM_FC (1),
+ /**
+ *
+ */
+ VM_FZ (2),
+ /**
+ *
+ */
+ VM_FS (0x80000000);
+
+ private int flag;
+
+ private VMFlags(int flag){
+ this.flag = flag;
+ }
+
+ /**
+ * Returns the VMFlags Type of the given int or null
+ * @param flag as int
+ * @return VMFlag of the int value
+ */
+ public static VMFlags findFlag(int flag){
+ if(VM_FC.equals(flag)){
+ return VM_FC;
+ }
+ if(VM_FS.equals(flag)){
+ return VM_FS;
+ }
+ if(VM_FZ.equals(flag)){
+ return VM_FZ;
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the flag provided as int is equal to the enum
+ * @param flag
+ * @return returns true if the flag is equal to the enum
+ */
+ public boolean equals(int flag){
+ return this.flag == flag;
+ }
+ /**
+ * @return the flag as int
+ */
+ public int getFlag() {
+ return flag;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMOpType.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMOpType.java
new file mode 100644
index 0000000..1b8bc97
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMOpType.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum VMOpType {
+ VM_OPREG (0),
+ VM_OPINT (1),
+ VM_OPREGMEM (2),
+ VM_OPNONE (3);
+
+ private int opType;
+
+ private VMOpType(int opType){
+ this.opType=opType;
+ }
+
+ public int getOpType() {
+ return opType;
+ }
+
+
+ public boolean equals(int opType){
+ return this.opType == opType;
+ }
+ public static VMOpType findOpType(int opType){
+
+ if (VM_OPREG.equals(opType)) {
+ return VM_OPREG;
+ }
+
+
+ if (VM_OPINT.equals(opType)) {
+ return VM_OPINT;
+ }
+
+ if (VM_OPREGMEM.equals(opType)) {
+ return VM_OPREGMEM;
+ }
+
+ if (VM_OPNONE.equals(opType)) {
+ return VM_OPNONE;
+ }
+ return null;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedCommand.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedCommand.java
new file mode 100644
index 0000000..74cc86d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedCommand.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class VMPreparedCommand {
+ private VMCommands OpCode;
+ private boolean ByteMode;
+ private VMPreparedOperand Op1 = new VMPreparedOperand();
+ private VMPreparedOperand Op2 = new VMPreparedOperand();
+
+ public boolean isByteMode() {
+ return ByteMode;
+ }
+ public void setByteMode(boolean byteMode) {
+ ByteMode = byteMode;
+ }
+ public VMPreparedOperand getOp1() {
+ return Op1;
+ }
+ public void setOp1(VMPreparedOperand op1) {
+ Op1 = op1;
+ }
+ public VMPreparedOperand getOp2() {
+ return Op2;
+ }
+ public void setOp2(VMPreparedOperand op2) {
+ Op2 = op2;
+ }
+ public VMCommands getOpCode() {
+ return OpCode;
+ }
+ public void setOpCode(VMCommands opCode) {
+ OpCode = opCode;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedOperand.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedOperand.java
new file mode 100644
index 0000000..f6aeff4
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedOperand.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class VMPreparedOperand {
+ private VMOpType Type;
+ private int Data;
+ private int Base;
+ private int offset;
+
+
+ public int getBase() {
+ return Base;
+ }
+ public void setBase(int base) {
+ Base = base;
+ }
+ public int getData() {
+ return Data;
+ }
+ public void setData(int data) {
+ Data = data;
+ }
+ public VMOpType getType() {
+ return Type;
+ }
+ public void setType(VMOpType type) {
+ Type = type;
+ }
+ public int getOffset() {
+ return offset;
+ }
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedProgram.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedProgram.java
new file mode 100644
index 0000000..497b4a9
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMPreparedProgram.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class VMPreparedProgram
+{
+ private List<VMPreparedCommand> Cmd = new ArrayList<VMPreparedCommand>();
+ private List<VMPreparedCommand> AltCmd =new ArrayList<VMPreparedCommand>();
+ private int CmdCount;
+
+
+
+ private Vector<Byte> GlobalData = new Vector<Byte>();
+ private Vector<Byte> StaticData = new Vector<Byte>(); // static data contained in DB operators
+ private int InitR[] = new int[7];
+
+ private int FilteredDataOffset;
+ private int FilteredDataSize;
+
+ public VMPreparedProgram()
+ {
+ AltCmd=null;
+ }
+
+
+
+ public List<VMPreparedCommand> getAltCmd() {
+ return AltCmd;
+ }
+
+
+
+ public void setAltCmd(List<VMPreparedCommand> altCmd) {
+ AltCmd = altCmd;
+ }
+
+
+
+ public List<VMPreparedCommand> getCmd() {
+ return Cmd;
+ }
+
+ public void setCmd(List<VMPreparedCommand> cmd) {
+ Cmd = cmd;
+ }
+
+ public int getCmdCount() {
+ return CmdCount;
+ }
+
+ public void setCmdCount(int cmdCount) {
+ CmdCount = cmdCount;
+ }
+
+
+
+ public int getFilteredDataOffset() {
+ return FilteredDataOffset;
+ }
+
+
+
+ public void setFilteredDataOffset(int filteredDataOffset) {
+ FilteredDataOffset = filteredDataOffset;
+ }
+
+
+
+ public int getFilteredDataSize() {
+ return FilteredDataSize;
+ }
+
+ public void setFilteredDataSize(int filteredDataSize) {
+ FilteredDataSize = filteredDataSize;
+ }
+
+ public Vector<Byte> getGlobalData() {
+ return GlobalData;
+ }
+
+ public void setGlobalData(Vector<Byte> globalData) {
+ GlobalData = globalData;
+ }
+
+ public int[] getInitR() {
+ return InitR;
+ }
+
+ public void setInitR(int[] initR) {
+ InitR = initR;
+ }
+
+ public Vector<Byte> getStaticData() {
+ return StaticData;
+ }
+
+ public void setStaticData(Vector<Byte> staticData) {
+ StaticData = staticData;
+ }
+
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilterSignature.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilterSignature.java
new file mode 100644
index 0000000..ca7ab73
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilterSignature.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 04.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class VMStandardFilterSignature {
+ private int length;
+
+ private int CRC;
+
+ private VMStandardFilters type;
+
+ public VMStandardFilterSignature(int length, int crc, VMStandardFilters type) {
+ super();
+ this.length = length;
+ CRC = crc;
+ this.type = type;
+ }
+
+ public int getCRC() {
+ return CRC;
+ }
+
+ public void setCRC(int crc) {
+ CRC = crc;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public VMStandardFilters getType() {
+ return type;
+ }
+
+ public void setType(VMStandardFilters type) {
+ this.type = type;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilters.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilters.java
new file mode 100644
index 0000000..ceff170
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unpack/vm/VMStandardFilters.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 31.05.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unpack.vm;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public enum VMStandardFilters {
+ VMSF_NONE ((int)0),
+ VMSF_E8 ((int)1),
+ VMSF_E8E9 ((int)2),
+ VMSF_ITANIUM( (int)3),
+ VMSF_RGB ((int)4),
+ VMSF_AUDIO ((int)5),
+ VMSF_DELTA ((int)6),
+ VMSF_UPCASE ((int)7);
+
+ private int filter;
+
+ private VMStandardFilters(int filter){
+ this.filter=filter;
+ }
+
+ public int getFilter() {
+ return filter;
+ }
+
+ public boolean equals(int filter){
+ return this.filter == filter;
+ }
+
+ public static VMStandardFilters findFilter(int filter){
+ if (VMSF_NONE.equals(filter)) {
+ return VMSF_NONE;
+ }
+
+ if (VMSF_E8.equals(filter)) {
+ return VMSF_E8;
+ }
+
+ if (VMSF_E8E9.equals(filter)) {
+ return VMSF_E8E9;
+ }
+ if (VMSF_ITANIUM.equals(filter)) {
+ return VMSF_ITANIUM;
+ }
+
+ if (VMSF_RGB.equals(filter)) {
+ return VMSF_RGB;
+ }
+
+ if (VMSF_AUDIO.equals(filter)) {
+ return VMSF_AUDIO;
+ }
+ if (VMSF_DELTA.equals(filter)) {
+ return VMSF_DELTA;
+ }
+ return null;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedByte.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedByte.java
new file mode 100644
index 0000000..f59ce96
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedByte.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 04.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unsigned;
+
+import com.github.junrar.crc.RarCRC;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class UnsignedByte {
+
+ public static byte longToByte(long unsignedByte1){
+ return (byte) (unsignedByte1&0xff);
+ }
+ public static byte intToByte(int unsignedByte1){
+ return (byte) (unsignedByte1&0xff);
+ }
+ public static byte shortToByte(short unsignedByte1){
+ return (byte) (unsignedByte1&0xff);
+ }
+
+
+ public static short add(byte unsignedByte1, byte unsignedByte2){
+ return (short) (unsignedByte1 + unsignedByte2);
+ }
+
+ public static short sub(byte unsignedByte1, byte unsignedByte2){
+
+ return (short) (unsignedByte1 - unsignedByte2);
+ }
+
+
+ public static void main(String[] args)
+ {
+ //tests unsigned (signed)
+ //add
+ System.out.println(add((byte)0xfe,(byte)0x01)); //255 (-1)
+ System.out.println(add((byte)0xff,(byte)0x01)); //0 (0)
+ System.out.println(add((byte)0x7f,(byte)0x01)); //128 (-128)
+ System.out.println(add((byte)0xff,(byte)0xff)); //254 (-2)
+
+ //sub
+ System.out.println(sub((byte)0xfe,(byte)0x01)); //253 (-3)
+ System.out.println(sub((byte)0x00,(byte)0x01)); //255 (-1)
+ System.out.println(sub((byte)0x80,(byte)0x01)); //127 (127)
+ //mul
+ System.out.println((byte)-1*(byte)-1);
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedInteger.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedInteger.java
new file mode 100644
index 0000000..0f12c5c
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedInteger.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 04.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unsigned;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class UnsignedInteger {
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedLong.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedLong.java
new file mode 100644
index 0000000..a859929
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedLong.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 04.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unsigned;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class UnsignedLong {
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedShort.java b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedShort.java
new file mode 100644
index 0000000..e9131e9
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/unsigned/UnsignedShort.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
+ * Original author: Edmund Wagner
+ * Creation date: 04.06.2007
+ *
+ * Source: $HeadURL$
+ * Last changed: $LastChangedDate$
+ *
+ *
+ * the unrar licence applies to all junrar source and binary distributions
+ * you are not allowed to use this source to re-create the RAR compression algorithm
+ *
+ * Here some html entities which can be used for escaping javadoc tags:
+ * "&": "&#038;" or "&amp;"
+ * "<": "&#060;" or "&lt;"
+ * ">": "&#062;" or "&gt;"
+ * "@": "&#064;"
+ */
+package com.github.junrar.unsigned;
+
+/**
+ * DOCUMENT ME
+ *
+ * @author $LastChangedBy$
+ * @version $LastChangedRevision$
+ */
+public class UnsignedShort {
+
+}
diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/util/VolumeHelper.java b/org.fox.ttcomics/src/main/java/com/github/junrar/util/VolumeHelper.java
new file mode 100644
index 0000000..4238051
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/com/github/junrar/util/VolumeHelper.java
@@ -0,0 +1,101 @@
+package com.github.junrar.util;
+
+/**
+ *
+ * @author alban
+ */
+public class VolumeHelper {
+ private VolumeHelper() {
+ }
+
+ // public static boolean mergeArchive(Archive archive, ComprDataIO dataIO)
+ // throws IOException {
+ // FileHeader hd = dataIO.getSubHeader();
+ // if (hd.getUnpVersion() >= 20 && hd.getFileCRC() != 0xffffffff
+ // && dataIO.getPackedCRC() != ~hd.getFileCRC()) {
+ // System.err.println("Data Bad CRC");
+ // }
+ //
+ // boolean oldNumbering = !archive.getMainHeader().isNewNumbering()
+ // || archive.isOldFormat();
+ // String nextName = nextVolumeName(archive.getFile().getAbsolutePath(),
+ // oldNumbering);
+ // File nextVolume = new File(nextName);
+ // UnrarCallback callback = archive.getUnrarCallback();
+ // if ((callback != null) && !callback.isNextVolumeReady(nextVolume)) {
+ // return false;
+ // }
+ // if (!nextVolume.exists()) {
+ // return false;
+ // }
+ // archive.setFile(nextVolume);
+ // hd = archive.nextFileHeader();
+ // if (hd == null) {
+ // return false;
+ // }
+ // dataIO.init(hd);
+ // return true;
+ // }
+
+ public static String nextVolumeName(String arcName, boolean oldNumbering) {
+ if (!oldNumbering) {
+ // part1.rar, part2.rar, ...
+ int len = arcName.length();
+ int indexR = len - 1;
+ while ((indexR >= 0) && !isDigit(arcName.charAt(indexR))) {
+ indexR--;
+ }
+ int index = indexR + 1;
+ int indexL = indexR - 1;
+ while ((indexL >= 0) && isDigit(arcName.charAt(indexL))) {
+ indexL--;
+ }
+ if (indexL < 0) {
+ return null;
+ }
+ indexL++;
+ StringBuilder buffer = new StringBuilder(len);
+ buffer.append(arcName, 0, indexL);
+ char[] digits = new char[indexR - indexL + 1];
+ arcName.getChars(indexL, indexR + 1, digits, 0);
+ indexR = digits.length - 1;
+ while ((indexR >= 0) && (++digits[indexR]) == '9' + 1) {
+ digits[indexR] = '0';
+ indexR--;
+ }
+ if (indexR < 0) {
+ buffer.append('1');
+ }
+ buffer.append(digits);
+ buffer.append(arcName, index, len);
+ return buffer.toString();
+ } else {
+ // .rar, .r00, .r01, ...
+ int len = arcName.length();
+ if ((len <= 4) || (arcName.charAt(len - 4) != '.')) {
+ return null;
+ }
+ StringBuilder buffer = new StringBuilder();
+ int off = len - 3;
+ buffer.append(arcName, 0, off);
+ if (!isDigit(arcName.charAt(off + 1))
+ || !isDigit(arcName.charAt(off + 2))) {
+ buffer.append("r00");
+ } else {
+ char[] ext = new char[3];
+ arcName.getChars(off, len, ext, 0);
+ int i = ext.length - 1;
+ while ((++ext[i]) == '9' + 1) {
+ ext[i] = '0';
+ i--;
+ }
+ buffer.append(ext);
+ }
+ return buffer.toString();
+ }
+ }
+
+ private static boolean isDigit(char c) {
+ return (c >= '0') && (c <= '9');
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouch.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouch.java
new file mode 100644
index 0000000..73392b1
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouch.java
@@ -0,0 +1,268 @@
+package it.sephiroth.android.library.imagezoom;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
+import android.view.ViewConfiguration;
+
+public class ImageViewTouch extends ImageViewTouchBase {
+
+ private static final float SCROLL_DELTA_THRESHOLD = 1.0f;
+ static final float MIN_ZOOM = 0.9f;
+ protected ScaleGestureDetector mScaleDetector;
+ protected GestureDetector mGestureDetector;
+ protected int mTouchSlop;
+ protected float mCurrentScaleFactor;
+ protected float mScaleFactor;
+ protected int mDoubleTapDirection;
+ protected OnGestureListener mGestureListener;
+ protected OnScaleGestureListener mScaleListener;
+ protected boolean mDoubleTapToZoomEnabled = true;
+ protected boolean mScaleEnabled = true;
+ protected boolean mScrollEnabled = true;
+
+ private OnImageViewTouchDoubleTapListener doubleTapListener;
+
+ public interface OnScaleChangedListener {
+ public void onScaleChanged(float scale);
+ }
+
+ protected OnScaleChangedListener mScaleChangedListener;
+
+ public ImageViewTouch( Context context, AttributeSet attrs ) {
+ super( context, attrs );
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ mGestureListener = getGestureListener();
+ mScaleListener = getScaleListener();
+
+ mScaleDetector = new ScaleGestureDetector( getContext(), mScaleListener );
+ mGestureDetector = new GestureDetector( getContext(), mGestureListener, null, true );
+
+ mCurrentScaleFactor = 1f;
+ mDoubleTapDirection = 1;
+ }
+
+ public void setDoubleTapListener( OnImageViewTouchDoubleTapListener doubleTapListener ){
+ this.doubleTapListener = doubleTapListener;
+ }
+
+ public void setDoubleTapToZoomEnabled( boolean value ) {
+ mDoubleTapToZoomEnabled = value;
+ }
+
+ public void setScaleEnabled( boolean value ) {
+ mScaleEnabled = value;
+ }
+
+ public void setScrollEnabled( boolean value ) {
+ mScrollEnabled = value;
+ }
+
+ public boolean getDoubleTapEnabled() {
+ return mDoubleTapToZoomEnabled;
+ }
+
+ protected OnGestureListener getGestureListener() {
+ return new GestureListener();
+ }
+
+ protected OnScaleGestureListener getScaleListener() {
+ return new ScaleListener();
+ }
+
+ @Override
+ protected void onBitmapChanged( Drawable drawable ) {
+ super.onBitmapChanged( drawable );
+
+ float v[] = new float[9];
+ mSuppMatrix.getValues( v );
+ mCurrentScaleFactor = v[Matrix.MSCALE_X];
+ }
+
+ @Override
+ protected void _setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
+ super._setImageDrawable( drawable, reset, initial_matrix, maxZoom );
+ mScaleFactor = getMaxZoom() / 3;
+ }
+
+ @Override
+ public boolean onTouchEvent( MotionEvent event ) {
+ mScaleDetector.onTouchEvent( event );
+ if ( !mScaleDetector.isInProgress() ) mGestureDetector.onTouchEvent( event );
+ int action = event.getAction();
+ switch ( action & MotionEvent.ACTION_MASK ) {
+ case MotionEvent.ACTION_UP:
+ if ( getScale() < 1f ) {
+ zoomTo( 1f, 50 );
+ }
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onZoom( float scale ) {
+ super.onZoom( scale );
+ if ( !mScaleDetector.isInProgress() ) mCurrentScaleFactor = scale;
+
+ if (mScaleChangedListener != null) {
+ mScaleChangedListener.onScaleChanged(mCurrentScaleFactor);
+ }
+ }
+
+ protected float onDoubleTapPost( float scale, float maxZoom ) {
+ if ( mDoubleTapDirection == 1 ) {
+ if (mCurrentScaleFactor - 1.0f < 0.01) { //( scale + ( mScaleFactor * 2 ) ) <= maxZoom
+
+ float scaleFactor = mScaleFactor;
+
+ RectF bitmapRect = getBitmapRect();
+
+ float w = bitmapRect.right - bitmapRect.left;
+ float h = bitmapRect.bottom - bitmapRect.top;
+
+ if (w < getWidth()) {
+ scaleFactor = (float)getWidth() / w - scale;
+ } else if (h < getHeight()) {
+ scaleFactor = (float)getHeight() / h - scale;
+ }
+
+ return scale + scaleFactor;
+ } else {
+ mDoubleTapDirection = -1;
+ return maxZoom;
+ }
+ } else {
+ mDoubleTapDirection = 1;
+ return 1f;
+ }
+ }
+
+ /**
+ * Determines whether this ImageViewTouch can be scrolled.
+ * @param direction
+ * - positive direction value means scroll from right to left,
+ * negative value means scroll from left to right
+ *
+ * @return true if there is some more place to scroll, false - otherwise.
+ */
+ public boolean canScroll(int direction) {
+ RectF bitmapRect = getBitmapRect();
+ updateRect(bitmapRect, mScrollRect);
+ Rect imageViewRect = new Rect();
+ getGlobalVisibleRect(imageViewRect);
+
+ if (bitmapRect.right >= imageViewRect.right) {
+ if (direction < 0) {
+ return Math.abs(bitmapRect.right - imageViewRect.right) > SCROLL_DELTA_THRESHOLD;
+ }
+ }
+
+ double bitmapScrollRectDelta = Math.abs(bitmapRect.left - mScrollRect.left);
+ return bitmapScrollRectDelta > SCROLL_DELTA_THRESHOLD;
+ }
+
+ public class GestureListener extends GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onDoubleTap( MotionEvent e ) {
+ Log.i( LOG_TAG, "onDoubleTap. double tap enabled? " + mDoubleTapToZoomEnabled);
+ if (mDoubleTapToZoomEnabled) {
+ float scale = getScale();
+ float targetScale = scale;
+ targetScale = onDoubleTapPost( scale, getMaxZoom() );
+ targetScale = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
+ mCurrentScaleFactor = targetScale;
+ zoomTo( targetScale, e.getX(), e.getY(), 200 );
+ invalidate();
+ }
+
+ if( null != doubleTapListener ){
+ doubleTapListener.onDoubleTap();
+ }
+
+ return super.onDoubleTap( e );
+ }
+
+ @Override
+ public void onLongPress( MotionEvent e ) {
+ if ( isLongClickable() ) {
+ if ( !mScaleDetector.isInProgress() ) {
+ setPressed( true );
+ performLongClick();
+ }
+ }
+ }
+
+ @Override
+ public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY ) {
+ if ( !mScrollEnabled ) return false;
+
+ if ( e1 == null || e2 == null ) return false;
+ if ( e1.getPointerCount() > 1 || e2.getPointerCount() > 1 ) return false;
+ if ( mScaleDetector.isInProgress() ) return false;
+ if ( getScale() == 1f ) return false;
+ scrollBy( -distanceX, -distanceY );
+ invalidate();
+ return super.onScroll( e1, e2, distanceX, distanceY );
+ }
+
+ @Override
+ public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY ) {
+ if ( !mScrollEnabled ) return false;
+
+ if ( e1.getPointerCount() > 1 || e2.getPointerCount() > 1 ) return false;
+ if ( mScaleDetector.isInProgress() ) return false;
+
+ float diffX = e2.getX() - e1.getX();
+ float diffY = e2.getY() - e1.getY();
+
+ if ( Math.abs( velocityX ) > 800 || Math.abs( velocityY ) > 800 ) {
+ scrollBy( diffX / 2, diffY / 2, 300 );
+ invalidate();
+ }
+ return super.onFling( e1, e2, velocityX, velocityY );
+ }
+ }
+
+ public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+
+ @SuppressWarnings("unused")
+ @Override
+ public boolean onScale( ScaleGestureDetector detector ) {
+ float span = detector.getCurrentSpan() - detector.getPreviousSpan();
+ float targetScale = mCurrentScaleFactor * detector.getScaleFactor();
+ if ( mScaleEnabled ) {
+ targetScale = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
+ zoomTo( targetScale, detector.getFocusX(), detector.getFocusY() );
+ mCurrentScaleFactor = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
+ mDoubleTapDirection = 1;
+ invalidate();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public interface OnImageViewTouchDoubleTapListener {
+ void onDoubleTap();
+ }
+
+ public void setOnScaleChangedListener(OnScaleChangedListener listener) {
+ mScaleChangedListener = listener;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java
new file mode 100644
index 0000000..8452a21
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java
@@ -0,0 +1,509 @@
+package it.sephiroth.android.library.imagezoom;
+
+import it.sephiroth.android.library.imagezoom.easing.Cubic;
+import it.sephiroth.android.library.imagezoom.easing.Easing;
+import it.sephiroth.android.library.imagezoom.graphics.FastBitmapDrawable;
+import it.sephiroth.android.library.imagezoom.utils.IDisposable;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.ImageView;
+
+/**
+ * Base View to manage image zoom/scrool/pinch operations
+ *
+ * @author alessandro
+ *
+ */
+public class ImageViewTouchBase extends ImageView implements IDisposable {
+
+ public interface OnBitmapChangedListener {
+
+ void onBitmapChanged( Drawable drawable );
+ };
+
+ public static final String LOG_TAG = "image";
+
+ protected Easing mEasing = new Cubic();
+ protected Matrix mBaseMatrix = new Matrix();
+ protected Matrix mSuppMatrix = new Matrix();
+ protected Handler mHandler = new Handler();
+ protected Runnable mOnLayoutRunnable = null;
+ protected float mMaxZoom;
+ protected final Matrix mDisplayMatrix = new Matrix();
+ protected final float[] mMatrixValues = new float[9];
+ protected int mThisWidth = -1, mThisHeight = -1;
+ protected boolean mFitToScreen = false;
+ protected boolean mFitToWidth = false;
+ final protected float MAX_ZOOM = 3.0f;
+
+ protected RectF mBitmapRect = new RectF();
+ protected RectF mCenterRect = new RectF();
+ protected RectF mScrollRect = new RectF();
+
+ private OnBitmapChangedListener mListener;
+
+ public ImageViewTouchBase( Context context ) {
+ super( context );
+ init();
+ }
+
+ public ImageViewTouchBase( Context context, AttributeSet attrs ) {
+ super( context, attrs );
+ init();
+ }
+
+ public void setOnBitmapChangedListener( OnBitmapChangedListener listener ) {
+ mListener = listener;
+ }
+
+ protected void init() {
+ setScaleType( ImageView.ScaleType.MATRIX );
+ }
+
+ public void clear() {
+ setImageBitmap( null, true );
+ }
+
+ public void setFitToScreen( boolean value ) {
+ if ( value != mFitToScreen ) {
+ mFitToScreen = value;
+ requestLayout();
+ }
+ }
+
+ public void setFitToWidth( boolean value ) {
+ if ( value != mFitToWidth ) {
+ mFitToWidth = value;
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected void onLayout( boolean changed, int left, int top, int right, int bottom ) {
+ super.onLayout( changed, left, top, right, bottom );
+ mThisWidth = right - left;
+ mThisHeight = bottom - top;
+ Runnable r = mOnLayoutRunnable;
+ if ( r != null ) {
+ mOnLayoutRunnable = null;
+ r.run();
+ }
+ if ( getDrawable() != null ) {
+ if ( mFitToScreen )
+ getProperBaseMatrix2( getDrawable(), mBaseMatrix );
+ else
+ getProperBaseMatrix( getDrawable(), mBaseMatrix );
+ setImageMatrix( getImageViewMatrix() );
+
+ if (mFitToWidth) zoomToWidth();
+ }
+ }
+
+ @Override
+ public void setImageBitmap( Bitmap bm ) {
+ setImageBitmap( bm, true );
+ }
+
+ @Override
+ public void setImageResource( int resId ) {
+ setImageDrawable( getContext().getResources().getDrawable( resId ) );
+ }
+
+ /**
+ * Set the new image to display and reset the internal matrix.
+ *
+ * @param bitmap
+ * - the {@link Bitmap} to display
+ * @param reset
+ * - if true the image bounds will be recreated, otherwise the old {@link Matrix} is used to display the new bitmap
+ * @see #setImageBitmap(Bitmap)
+ */
+ public void setImageBitmap( final Bitmap bitmap, final boolean reset ) {
+ setImageBitmap( bitmap, reset, null );
+ }
+
+ /**
+ * Similar to {@link #setImageBitmap(Bitmap, boolean)} but an optional view {@link Matrix} can be passed to determine the new
+ * bitmap view matrix.<br />
+ * This method is useful if you need to restore a Bitmap with the same zoom/pan values from a previous state
+ *
+ * @param bitmap
+ * - the {@link Bitmap} to display
+ * @param reset
+ * @param matrix
+ * - the {@link Matrix} to be used to display the new bitmap
+ *
+ * @see #setImageBitmap(Bitmap, boolean)
+ * @see #setImageBitmap(Bitmap)
+ * @see #getImageViewMatrix()
+ * @see #getDisplayMatrix()
+ */
+ public void setImageBitmap( final Bitmap bitmap, final boolean reset, Matrix matrix ) {
+ setImageBitmap( bitmap, reset, matrix, -1 );
+ }
+
+ /**
+ *
+ * @param bitmap
+ * @param reset
+ * @param matrix
+ * @param maxZoom
+ * - maximum zoom allowd during zoom gestures
+ *
+ * @see #setImageBitmap(Bitmap, boolean, Matrix)
+ */
+ public void setImageBitmap( final Bitmap bitmap, final boolean reset, Matrix matrix, float maxZoom ) {
+
+ Log.i( LOG_TAG, "setImageBitmap: " + bitmap );
+
+ if ( bitmap != null )
+ setImageDrawable( new FastBitmapDrawable( bitmap ), reset, matrix, maxZoom );
+ else
+ setImageDrawable( null, reset, matrix, maxZoom );
+ }
+
+ @Override
+ public void setImageDrawable( Drawable drawable ) {
+ setImageDrawable( drawable, true, null, -1 );
+ }
+
+ public void setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
+
+ final int viewWidth = getWidth();
+
+ if ( viewWidth <= 0 ) {
+ mOnLayoutRunnable = new Runnable() {
+
+ @Override
+ public void run() {
+ setImageDrawable( drawable, reset, initial_matrix, maxZoom );
+ }
+ };
+ return;
+ }
+
+ _setImageDrawable( drawable, reset, initial_matrix, maxZoom );
+ }
+
+ protected void _setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
+
+ if ( drawable != null ) {
+ if ( mFitToScreen )
+ getProperBaseMatrix2( drawable, mBaseMatrix );
+ else
+ getProperBaseMatrix( drawable, mBaseMatrix );
+ super.setImageDrawable( drawable );
+
+ if (mFitToWidth) zoomToWidth();
+
+ } else {
+ mBaseMatrix.reset();
+ super.setImageDrawable( null );
+ }
+
+ if ( reset ) {
+ mSuppMatrix.reset();
+ if ( initial_matrix != null ) {
+ mSuppMatrix = new Matrix( initial_matrix );
+ }
+ }
+
+ setImageMatrix( getImageViewMatrix() );
+
+ if ( maxZoom < 1 )
+ mMaxZoom = maxZoom();
+ else
+ mMaxZoom = maxZoom;
+
+ onBitmapChanged( drawable );
+ }
+
+ protected void onBitmapChanged( final Drawable bitmap ) {
+ if ( mListener != null ) {
+ mListener.onBitmapChanged( bitmap );
+ }
+ }
+
+ protected float maxZoom() {
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) {
+ return 1F;
+ }
+
+ float fw = (float) drawable.getIntrinsicWidth() / (float) mThisWidth;
+ float fh = (float) drawable.getIntrinsicHeight() / (float) mThisHeight;
+ float max = Math.max( fw, fh ) * 4;
+ return max;
+ }
+
+ public float getMaxZoom() {
+ return mMaxZoom;
+ }
+
+ public Matrix getImageViewMatrix() {
+ mDisplayMatrix.set( mBaseMatrix );
+ mDisplayMatrix.postConcat( mSuppMatrix );
+ return mDisplayMatrix;
+ }
+
+ /**
+ * Returns the current image display matrix. This matrix can be used in the next call to the
+ * {@link #setImageBitmap(Bitmap, boolean, Matrix)} to restore the same view state of the previous {@link Bitmap}
+ *
+ * @return
+ */
+ public Matrix getDisplayMatrix() {
+ return new Matrix( mSuppMatrix );
+ }
+
+ /**
+ * Setup the base matrix so that the image is centered and scaled properly.
+ *
+ * @param bitmap
+ * @param matrix
+ */
+ protected void getProperBaseMatrix( Drawable drawable, Matrix matrix ) {
+ float viewWidth = getWidth();
+ float viewHeight = getHeight();
+ float w = drawable.getIntrinsicWidth();
+ float h = drawable.getIntrinsicHeight();
+ matrix.reset();
+
+ if ( w > viewWidth || h > viewHeight ) {
+ float widthScale = Math.min( viewWidth / w, 2.0f );
+ float heightScale = Math.min( viewHeight / h, 2.0f );
+ float scale = Math.min( widthScale, heightScale );
+ matrix.postScale( scale, scale );
+ float tw = ( viewWidth - w * scale ) / 2.0f;
+ float th = ( viewHeight - h * scale ) / 2.0f;
+ matrix.postTranslate( tw, th );
+ } else {
+ float tw = ( viewWidth - w ) / 2.0f;
+ float th = ( viewHeight - h ) / 2.0f;
+ matrix.postTranslate( tw, th );
+ }
+ }
+
+ /**
+ * Setup the base matrix so that the image is centered and scaled properly.
+ *
+ * @param bitmap
+ * @param matrix
+ */
+ protected void getProperBaseMatrix2( Drawable bitmap, Matrix matrix ) {
+ float viewWidth = getWidth();
+ float viewHeight = getHeight();
+ float w = bitmap.getIntrinsicWidth();
+ float h = bitmap.getIntrinsicHeight();
+ matrix.reset();
+ float widthScale = Math.min( viewWidth / w, MAX_ZOOM );
+ float heightScale = Math.min( viewHeight / h, MAX_ZOOM );
+ float scale = Math.min( widthScale, heightScale );
+ matrix.postScale( scale, scale );
+ matrix.postTranslate( ( viewWidth - w * scale ) / 2.0f, ( viewHeight - h * scale ) / 2.0f );
+ }
+
+ protected float getValue( Matrix matrix, int whichValue ) {
+ matrix.getValues( mMatrixValues );
+ return mMatrixValues[whichValue];
+ }
+
+ protected RectF getBitmapRect() {
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) return null;
+ Matrix m = getImageViewMatrix();
+ mBitmapRect.set( 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight() );
+ m.mapRect( mBitmapRect );
+ return mBitmapRect;
+ }
+
+ protected float getScale( Matrix matrix ) {
+ return getValue( matrix, Matrix.MSCALE_X );
+ }
+
+ public float getRotation() {
+ return 0;
+ }
+
+ public float getScale() {
+ return getScale( mSuppMatrix );
+ }
+
+ protected void center( boolean horizontal, boolean vertical ) {
+ // Log.i(LOG_TAG, "center");
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) return;
+ RectF rect = getCenter( horizontal, vertical );
+ if ( rect.left != 0 || rect.top != 0 ) {
+ postTranslate( rect.left, rect.top );
+ }
+ }
+
+ protected RectF getCenter( boolean horizontal, boolean vertical ) {
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) return new RectF( 0, 0, 0, 0 );
+
+ RectF rect = getBitmapRect();
+ float height = rect.height();
+ float width = rect.width();
+ float deltaX = 0, deltaY = 0;
+ if ( vertical ) {
+ int viewHeight = getHeight();
+ if ( height < viewHeight ) {
+ deltaY = ( viewHeight - height ) / 2 - rect.top;
+ } else if ( rect.top > 0 ) {
+ deltaY = -rect.top;
+ } else if ( rect.bottom < viewHeight ) {
+ deltaY = getHeight() - rect.bottom;
+ }
+ }
+ if ( horizontal ) {
+ int viewWidth = getWidth();
+ if ( width < viewWidth ) {
+ deltaX = ( viewWidth - width ) / 2 - rect.left;
+ } else if ( rect.left > 0 ) {
+ deltaX = -rect.left;
+ } else if ( rect.right < viewWidth ) {
+ deltaX = viewWidth - rect.right;
+ }
+ }
+ mCenterRect.set( deltaX, deltaY, 0, 0 );
+ return mCenterRect;
+ }
+
+ protected void postTranslate( float deltaX, float deltaY ) {
+ mSuppMatrix.postTranslate( deltaX, deltaY );
+ setImageMatrix( getImageViewMatrix() );
+ }
+
+ protected void postScale( float scale, float centerX, float centerY ) {
+ mSuppMatrix.postScale( scale, scale, centerX, centerY );
+ setImageMatrix( getImageViewMatrix() );
+ }
+
+ protected void zoomTo( float scale ) {
+ float cx = getWidth() / 2F;
+ float cy = getHeight() / 2F;
+ zoomTo( scale, cx, cy );
+ }
+
+ public void zoomTo( float scale, float durationMs ) {
+ float cx = getWidth() / 2F;
+ float cy = getHeight() / 2F;
+ zoomTo( scale, cx, cy, durationMs );
+ }
+
+ protected void zoomTo( float scale, float centerX, float centerY ) {
+ if ( scale > mMaxZoom ) scale = mMaxZoom;
+ float oldScale = getScale();
+ float deltaScale = scale / oldScale;
+ postScale( deltaScale, centerX, centerY );
+ onZoom( getScale() );
+ center( true, true );
+ }
+
+ protected void onZoom( float scale ) {}
+
+ public void scrollBy( float x, float y ) {
+ panBy( x, y );
+ }
+
+ protected void panBy( double dx, double dy ) {
+ RectF rect = getBitmapRect();
+ mScrollRect.set( (float) dx, (float) dy, 0, 0 );
+ updateRect( rect, mScrollRect );
+ postTranslate( mScrollRect.left, mScrollRect.top );
+ center( true, true );
+ }
+
+ protected void updateRect( RectF bitmapRect, RectF scrollRect ) {
+ float width = getWidth();
+ float height = getHeight();
+
+ if ( bitmapRect.top >= 0 && bitmapRect.bottom <= height ) scrollRect.top = 0;
+ if ( bitmapRect.left >= 0 && bitmapRect.right <= width ) scrollRect.left = 0;
+ if ( bitmapRect.top + scrollRect.top >= 0 && bitmapRect.bottom > height ) scrollRect.top = (int) ( 0 - bitmapRect.top );
+ if ( bitmapRect.bottom + scrollRect.top <= ( height - 0 ) && bitmapRect.top < 0 )
+ scrollRect.top = (int) ( ( height - 0 ) - bitmapRect.bottom );
+ if ( bitmapRect.left + scrollRect.left >= 0 ) scrollRect.left = (int) ( 0 - bitmapRect.left );
+ if ( bitmapRect.right + scrollRect.left <= ( width - 0 ) ) scrollRect.left = (int) ( ( width - 0 ) - bitmapRect.right );
+ // Log.d( LOG_TAG, "scrollRect(2): " + scrollRect.toString() );
+ }
+
+ protected void scrollBy( float distanceX, float distanceY, final double durationMs ) {
+ final double dx = distanceX;
+ final double dy = distanceY;
+ final long startTime = System.currentTimeMillis();
+ mHandler.post( new Runnable() {
+
+ double old_x = 0;
+ double old_y = 0;
+
+ @Override
+ public void run() {
+ long now = System.currentTimeMillis();
+ double currentMs = Math.min( durationMs, now - startTime );
+ double x = mEasing.easeOut( currentMs, 0, dx, durationMs );
+ double y = mEasing.easeOut( currentMs, 0, dy, durationMs );
+ panBy( ( x - old_x ), ( y - old_y ) );
+ old_x = x;
+ old_y = y;
+ if ( currentMs < durationMs ) {
+ mHandler.post( this );
+ } else {
+ RectF centerRect = getCenter( true, true );
+ if ( centerRect.left != 0 || centerRect.top != 0 ) scrollBy( centerRect.left, centerRect.top );
+ }
+ }
+ } );
+ }
+
+ protected void zoomTo( float scale, final float centerX, final float centerY, final float durationMs ) {
+ // Log.i( LOG_TAG, "zoomTo: " + scale + ", " + centerX + ": " + centerY );
+ final long startTime = System.currentTimeMillis();
+ final float incrementPerMs = ( scale - getScale() ) / durationMs;
+ final float oldScale = getScale();
+ mHandler.post( new Runnable() {
+
+ @Override
+ public void run() {
+ long now = System.currentTimeMillis();
+ float currentMs = Math.min( durationMs, now - startTime );
+ float target = oldScale + ( incrementPerMs * currentMs );
+ zoomTo( target, centerX, centerY );
+ if ( currentMs < durationMs ) {
+ mHandler.post( this );
+ } else {
+ // if ( getScale() < 1f ) {}
+ }
+ }
+ } );
+ }
+
+ @Override
+ public void dispose() {
+ clear();
+ }
+
+ protected void zoomToWidth() {
+ RectF bitmapRect = getBitmapRect();
+
+ float w = bitmapRect.right - bitmapRect.left;
+
+ if (w < getWidth()) {
+ float scale = (float)getWidth() / w;
+
+ zoomTo(scale, 0f, 0f);
+ }
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Cubic.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Cubic.java
new file mode 100644
index 0000000..6f7e87d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Cubic.java
@@ -0,0 +1,20 @@
+package it.sephiroth.android.library.imagezoom.easing;
+
+public class Cubic implements Easing {
+
+ @Override
+ public double easeOut( double time, double start, double end, double duration ) {
+ return end * ( ( time = time / duration - 1.0 ) * time * time + 1.0 ) + start;
+ }
+
+ @Override
+ public double easeIn( double time, double start, double end, double duration ) {
+ return end * ( time /= duration ) * time * time + start;
+ }
+
+ @Override
+ public double easeInOut( double time, double start, double end, double duration ) {
+ if ( ( time /= duration / 2.0 ) < 1.0 ) return end / 2.0 * time * time * time + start;
+ return end / 2.0 * ( ( time -= 2.0 ) * time * time + 2.0 ) + start;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Easing.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Easing.java
new file mode 100644
index 0000000..202e9d9
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Easing.java
@@ -0,0 +1,10 @@
+package it.sephiroth.android.library.imagezoom.easing;
+
+public interface Easing {
+
+ double easeOut( double time, double start, double end, double duration );
+
+ double easeIn( double time, double start, double end, double duration );
+
+ double easeInOut( double time, double start, double end, double duration );
+}
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/FastBitmapDrawable.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/FastBitmapDrawable.java
new file mode 100644
index 0000000..8afc38e
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/FastBitmapDrawable.java
@@ -0,0 +1,84 @@
+package it.sephiroth.android.library.imagezoom.graphics;
+
+import java.io.InputStream;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Fast bitmap drawable. Does not support states. it only
+ * support alpha and colormatrix
+ * @author alessandro
+ *
+ */
+public class FastBitmapDrawable extends Drawable implements IBitmapDrawable {
+
+ protected Bitmap mBitmap;
+ protected Paint mPaint;
+
+ public FastBitmapDrawable( Bitmap b ) {
+ mBitmap = b;
+ mPaint = new Paint();
+ mPaint.setDither( true );
+ mPaint.setFilterBitmap( true );
+ }
+
+ public FastBitmapDrawable( Resources res, InputStream is ){
+ this(BitmapFactory.decodeStream(is));
+ }
+
+ @Override
+ public void draw( Canvas canvas ) {
+ canvas.drawBitmap( mBitmap, 0.0f, 0.0f, mPaint );
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha( int alpha ) {
+ mPaint.setAlpha( alpha );
+ }
+
+ @Override
+ public void setColorFilter( ColorFilter cf ) {
+ mPaint.setColorFilter( cf );
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mBitmap.getWidth();
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mBitmap.getHeight();
+ }
+
+ @Override
+ public int getMinimumWidth() {
+ return mBitmap.getWidth();
+ }
+
+ @Override
+ public int getMinimumHeight() {
+ return mBitmap.getHeight();
+ }
+
+ public void setAntiAlias( boolean value ){
+ mPaint.setAntiAlias( value );
+ invalidateSelf();
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+} \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/IBitmapDrawable.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/IBitmapDrawable.java
new file mode 100644
index 0000000..5a2892a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/IBitmapDrawable.java
@@ -0,0 +1,14 @@
+package it.sephiroth.android.library.imagezoom.graphics;
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouchBase;
+import android.graphics.Bitmap;
+
+/**
+ * Base interface used in the {@link ImageViewTouchBase} view
+ * @author alessandro
+ *
+ */
+public interface IBitmapDrawable {
+
+ Bitmap getBitmap();
+}
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/utils/IDisposable.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/utils/IDisposable.java
new file mode 100644
index 0000000..da991a7
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/utils/IDisposable.java
@@ -0,0 +1,6 @@
+package it.sephiroth.android.library.imagezoom.utils;
+
+public interface IDisposable {
+
+ void dispose();
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java
new file mode 100644
index 0000000..b1fb65d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java
@@ -0,0 +1,69 @@
+package org.fox.ttcomics;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.zip.ZipFile;
+
+import com.github.junrar.Archive;
+import com.github.junrar.exception.RarException;
+import com.github.junrar.rarfile.FileHeader;
+
+import android.util.Log;
+
+public class CbrComicArchive extends ComicArchive {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private Archive m_archive;
+ private ArrayList<FileHeader> m_entries = new ArrayList<FileHeader>();
+
+ @Override
+ public int getCount() {
+ return m_entries.size();
+ }
+
+ @Override
+ public InputStream getItem(int index) throws IOException {
+ try {
+ return m_archive.getInputStream(m_entries.get(index));
+ } catch (RarException e) {
+ e.printStackTrace();
+ return null;
+ } catch (OutOfMemoryError e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public CbrComicArchive(String fileName) throws IOException, RarException {
+ m_archive = new Archive(new File(fileName));
+
+ FileHeader header = m_archive.nextFileHeader();
+
+ while (header != null) {
+ if (!header.isDirectory()) {
+ String name = header.isUnicode() ? header.getFileNameW() : header.getFileNameString();
+
+ if (isValidComic(name)) {
+ m_entries.add(header);
+ }
+ }
+
+ header = m_archive.nextFileHeader();
+ }
+
+ Collections.sort(m_entries, new Comparator<FileHeader>() {
+ public int compare(FileHeader a, FileHeader b) {
+ String nameA = a.isUnicode() ? a.getFileNameW() : a.getFileNameString();
+ String nameB = b.isUnicode() ? b.getFileNameW() : b.getFileNameString();
+
+ return nameA.compareTo(nameB);
+ }
+ });
+
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java
new file mode 100644
index 0000000..5e5b54f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java
@@ -0,0 +1,50 @@
+package org.fox.ttcomics;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class CbzComicArchive extends ComicArchive {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private ZipFile m_zipFile;
+ private int m_count;
+ private ArrayList<ZipEntry> m_entries = new ArrayList<ZipEntry>();
+
+ @Override
+ public int getCount() {
+ return m_count;
+ }
+
+ @Override
+ public InputStream getItem(int index) throws IOException {
+ return m_zipFile.getInputStream(m_entries.get(index));
+ }
+
+ public CbzComicArchive(String fileName) throws IOException {
+ m_zipFile = new ZipFile(fileName);
+
+ Enumeration<? extends ZipEntry> e = m_zipFile.entries();
+
+ while (e.hasMoreElements()) {
+ ZipEntry ze = e.nextElement();
+ if (!ze.isDirectory() && isValidComic(ze.getName())) {
+ m_entries.add(ze);
+ m_count++;
+ }
+ }
+
+ Collections.sort(m_entries, new Comparator<ZipEntry>() {
+ public int compare(ZipEntry a, ZipEntry b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java
new file mode 100644
index 0000000..3070323
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java
@@ -0,0 +1,12 @@
+package org.fox.ttcomics;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ComicArchive {
+ public abstract int getCount();
+ public abstract InputStream getItem(int index) throws IOException;
+ public boolean isValidComic(String fileName) {
+ return fileName.toLowerCase().matches(".*\\.(jpg|bmp|gif|png)$");
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java
new file mode 100644
index 0000000..75d750f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java
@@ -0,0 +1,329 @@
+package org.fox.ttcomics;
+
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouch;
+
+import java.io.IOException;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBar;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class ComicFragment extends Fragment implements GestureDetector.OnDoubleTapListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private SharedPreferences m_prefs;
+ private int m_page;
+ private CommonActivity m_activity;
+ private GestureDetector m_detector;
+ private boolean m_thumbnail = false;
+
+ public ComicFragment() {
+ super();
+ }
+
+ private void setThumbnail(boolean thumbnail) {
+ if (m_thumbnail != thumbnail) {
+ m_thumbnail = thumbnail;
+
+ if (isAdded()) {
+ AsyncTask<ComicArchive, Void, Bitmap> loadTask = new AsyncTask<ComicArchive, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(ComicArchive... params) {
+ return loadImage(params[0], m_page);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ CommonActivity activity = (CommonActivity) getActivity();
+
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (activity != null && isAdded() && image != null) {
+ if (result != null) {
+ image.setImageBitmap(result);
+ } else {
+ activity.toast(R.string.error_loading_image);
+ image.setImageResource(R.drawable.badimage);
+ }
+ }
+ }
+ };
+
+ ComicPager pager = (ComicPager) getActivity().getSupportFragmentManager().findFragmentByTag(CommonActivity.FRAG_COMICS_PAGER);
+ loadTask.execute(pager.getArchive());
+ }
+ }
+ }
+
+ public void setPage(int page) {
+ m_page = page;
+ }
+
+ public Bitmap loadImage(ComicArchive archive, int page) {
+ CommonActivity activity = (CommonActivity) getActivity();
+
+ try {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(archive.getItem(page), null, options);
+ options.inJustDecodeBounds = false;
+
+ Bitmap bitmap = null;
+
+ int sampleSizes[];
+
+ if (m_thumbnail) {
+ sampleSizes = new int[] { 512, 256 };
+ } else {
+ sampleSizes = new int[] { 1024, 768, 512, 256 };
+ }
+
+ for (int sampleSize : sampleSizes) {
+ try {
+ options.inSampleSize = CommonActivity.calculateInSampleSize(options, sampleSize, sampleSize);
+ bitmap = BitmapFactory.decodeStream(archive.getItem(page), null, options);
+
+ return bitmap;
+ } catch (OutOfMemoryError e) {
+ if (sampleSize == sampleSizes[sampleSizes.length-1]) {
+ e.printStackTrace();
+
+ if (activity != null && isAdded()) {
+ activity.toast(R.string.error_out_of_memory);
+ }
+ }
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.fragment_comic, container, false);
+
+ final ImageViewTouch image = (ImageViewTouch) view.findViewById(R.id.comic_image);
+
+ if (savedInstanceState != null) {
+ m_page = savedInstanceState.getInt("page");
+ m_thumbnail = savedInstanceState.getBoolean("thumbnail");
+ }
+
+ ComicPager pager = (ComicPager) getActivity().getSupportFragmentManager().findFragmentByTag(CommonActivity.FRAG_COMICS_PAGER);
+
+ if (pager != null) {
+ if (CommonActivity.isCompatMode() && m_prefs.getBoolean("use_dark_theme", false)) {
+ image.setBackgroundColor(0xff000000);
+ }
+
+ image.setFitToScreen(true);
+
+ if (m_prefs.getBoolean("fit_to_width", false)) {
+ image.setFitToWidth(true);
+ }
+
+ AsyncTask<ComicArchive, Void, Bitmap> loadTask = new AsyncTask<ComicArchive, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(ComicArchive... params) {
+ return loadImage(params[0], m_page);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ CommonActivity activity = (CommonActivity) getActivity();
+
+ if (activity != null && isAdded()) {
+ if (result != null) {
+ image.setImageBitmap(result);
+ } else {
+ activity.toast(R.string.error_loading_image);
+ image.setImageResource(R.drawable.badimage);
+ }
+ }
+ }
+ };
+
+ loadTask.execute(pager.getArchive());
+
+ image.setOnScaleChangedListener(new ImageViewTouch.OnScaleChangedListener() {
+ @Override
+ public void onScaleChanged(float scale) {
+ // TODO: shared scale change?
+ }
+ });
+
+ image.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ return m_detector.onTouchEvent(event);
+ }
+ });
+
+ }
+
+ TextView page = (TextView) view.findViewById(R.id.comic_page);
+
+ if (page != null) {
+ page.setText(String.valueOf(m_page+1));
+ }
+
+ return view;
+
+ }
+
+ private void onLeftSideTapped() {
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (image != null) {
+ boolean atLeftEdge = !image.canScroll(1);
+
+ if (atLeftEdge) {
+ m_activity.selectPreviousComic();
+ }
+ }
+ }
+
+ public boolean canScroll(int direction) {
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (image != null) {
+ return image.canScroll(direction);
+ } else {
+ return false;
+ }
+ }
+
+ private void onRightSideTapped() {
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (image != null) {
+ boolean atRightEdge = !image.canScroll(-1);
+
+ if (atRightEdge) {
+ m_activity.selectNextComic();
+ }
+ }
+ }
+
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+
+ //setThumbnail(!isVisibleToUser); disabled
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
+ m_activity = (CommonActivity) activity;
+
+ m_detector = new GestureDetector(m_activity, new GestureDetector.OnGestureListener() {
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+ });
+
+ m_detector.setOnDoubleTapListener(this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+ out.putInt("page", m_page);
+ out.putBoolean("thumbnail", m_thumbnail);
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+
+ int width = getView().getWidth();
+
+ int x = Math.round(e.getX());
+
+ if (x <= width/10) {
+ onLeftSideTapped();
+ } else if (x >= width-(width/10)) {
+ onRightSideTapped();
+ } else {
+ ActionBar bar = m_activity.getSupportActionBar();
+
+ if (bar.isShowing()) {
+ bar.hide();
+ m_activity.hideSystemUiIfNecessary();
+ } else {
+ m_activity.showSystemUiIfNecessary();
+ bar.show();
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java
new file mode 100644
index 0000000..26166fc
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java
@@ -0,0 +1,529 @@
+package org.fox.ttcomics;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.SimpleCursorAdapter;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.github.junrar.exception.RarException;
+
+public class ComicListFragment extends Fragment implements OnItemClickListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private final static int SIZE_DIR = -100;
+
+ // corresponds to tab indexes
+ private final static int MODE_ALL = 0;
+ private final static int MODE_UNREAD = 1;
+ private final static int MODE_UNFINISHED = 2;
+ private final static int MODE_READ = 3;
+
+ private CommonActivity m_activity;
+ private SharedPreferences m_prefs;
+ private ComicsListAdapter m_adapter;
+ private int m_mode = 0;
+ private String m_baseDirectory = "";
+ private SwipeRefreshLayout m_swipeLayout;
+
+ public ComicListFragment() {
+ super();
+ }
+
+ public void setBaseDirectory(String baseDirectory) {
+ m_baseDirectory = baseDirectory;
+ }
+
+ public void setMode(int mode) {
+ m_mode = mode;
+ }
+
+ private class ComicsListAdapter extends SimpleCursorAdapter {
+ public ComicsListAdapter(Context context, int layout, Cursor c,
+ String[] from, int[] to, int flags) {
+ super(context, layout, c, from, to, flags);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = convertView;
+
+ Cursor c = (Cursor) getItem(position);
+
+ String filePath = c.getString(c.getColumnIndex("path"));
+ String fileBaseName = c.getString(c.getColumnIndex("filename"));
+ String firstChild = c.getString(c.getColumnIndex("firstchild"));
+
+ int lastPos = m_activity.getLastPosition(filePath + "/" + fileBaseName);
+ int size = m_activity.getSize(filePath + "/" + fileBaseName);
+
+ boolean isList = ComicListFragment.this.getView().findViewById(R.id.comics_list) != null;
+
+ if (v == null) {
+ LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+
+ v = vi.inflate(isList ? R.layout.comics_list_row : R.layout.comics_grid_row, null);
+
+ }
+
+ TextView name = (TextView) v.findViewById(R.id.file_name);
+
+ if (name != null) {
+ name.setText(fileBaseName);
+ }
+
+ TextView info = (TextView) v.findViewById(R.id.file_progress_info);
+
+ if (info != null) {
+ if (size != -1 && size != SIZE_DIR) {
+ if (lastPos == size - 1) {
+ info.setText(getString(R.string.file_finished));
+ } else if (lastPos > 0) {
+ info.setText(getString(R.string.file_progress_info, lastPos+1, size, (int)(lastPos/ (float)size * 100f)));
+ } else {
+ info.setText(getString(R.string.file_unread, size));
+ }
+ info.setVisibility(View.VISIBLE);
+ } else {
+ info.setVisibility(View.GONE);
+ }
+ }
+
+ ProgressBar progressBar = (ProgressBar) v.findViewById(R.id.file_progress_bar);
+
+ if (progressBar != null) {
+ if (size != -1 && size != SIZE_DIR) {
+ progressBar.setMax(size-1);
+ progressBar.setProgress(lastPos);
+ progressBar.setVisibility(View.VISIBLE);
+ } else {
+ progressBar.setVisibility(View.GONE);
+ }
+ }
+
+ ImageView overflow = (ImageView) v.findViewById(R.id.comic_overflow);
+
+ if (overflow != null) {
+ if (size == SIZE_DIR) {
+ overflow.setVisibility(View.GONE);
+ } else {
+ overflow.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ getActivity().openContextMenu(v);
+ }
+ });
+ }
+ }
+
+ File thumbnailFile = new File(m_activity.getCacheFileName(firstChild != null ? firstChild : filePath + "/" + fileBaseName));
+
+ ImageView thumbnail = (ImageView) v.findViewById(R.id.thumbnail);
+
+ if (thumbnail != null) {
+ View imageholder = v.findViewById(R.id.imageholder);
+
+ int padding = dpToPx(2);
+
+ if (imageholder != null) {
+ if (size == SIZE_DIR) {
+ imageholder.setBackgroundResource(R.drawable.comic_tile_folder);
+ imageholder.setPadding(padding, padding, padding, padding);
+ } else {
+ imageholder.setBackgroundResource(R.drawable.comic_tile);
+ imageholder.setPadding(padding, padding, padding, padding);
+ }
+ }
+
+ thumbnail.setTag("");
+ thumbnail.setImageResource(R.drawable.ic_launcher);
+
+ if (m_activity.isStorageAvailable() && thumbnailFile.exists()) {
+ thumbnail.setTag(thumbnailFile.getAbsolutePath());
+
+ CoverImageLoader imageLoader = new CoverImageLoader();
+ imageLoader.execute(thumbnail);
+ }
+ }
+
+ return v;
+ }
+ }
+
+ public int dpToPx(int dp) {
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
+ return px;
+ }
+
+ class CoverImageLoader extends AsyncTask<ImageView, Void, Bitmap> {
+ private ImageView m_thumbnail;
+ private String m_tag;
+
+ @Override
+ protected Bitmap doInBackground(ImageView... params) {
+ m_thumbnail = params[0];
+
+ if (m_thumbnail != null) {
+ m_tag = m_thumbnail.getTag().toString();
+
+ File thumbnailFile = new File(m_tag);
+
+ if (thumbnailFile.exists() && thumbnailFile.canRead()) {
+
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(thumbnailFile.getAbsolutePath(), options);
+
+ options.inSampleSize = CommonActivity.calculateInSampleSize(options, 256, 256);
+ options.inJustDecodeBounds = false;
+
+ Bitmap bmp = BitmapFactory.decodeFile(thumbnailFile.getAbsolutePath(), options);
+
+ return bmp;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bmp) {
+ if (isAdded() && bmp != null && m_tag != null && m_tag.equals(m_thumbnail.getTag().toString())) {
+ m_thumbnail.setImageBitmap(bmp);
+ }
+ }
+
+ };
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.fragment_comics_list, container, false);
+
+ if (savedInstanceState != null) {
+ m_mode = savedInstanceState.getInt("mode");
+ m_baseDirectory = savedInstanceState.getString("baseDir");
+ //m_files = savedInstanceState.getStringArrayList("files");
+ }
+
+ m_swipeLayout = (SwipeRefreshLayout) view.findViewById(R.id.comics_swipe_container);
+
+ m_swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+ @Override
+ public void onRefresh() {
+ rescan(true);
+ }
+ });
+
+ if (!CommonActivity.isCompatMode()) {
+ m_swipeLayout.setColorScheme(android.R.color.holo_green_dark,
+ android.R.color.holo_red_dark,
+ android.R.color.holo_blue_dark,
+ android.R.color.holo_orange_dark);
+ }
+
+ m_adapter = new ComicsListAdapter(getActivity(), R.layout.comics_list_row, createCursor(),
+ new String[] { "filename" }, new int[] { R.id.file_name }, 0);
+
+ if (view.findViewById(R.id.comics_list) != null) {
+ ListView list = (ListView) view.findViewById(R.id.comics_list);
+ list.setAdapter(m_adapter);
+ list.setEmptyView(view.findViewById(R.id.no_comics));
+ list.setOnItemClickListener(this);
+
+ registerForContextMenu(list);
+
+ } else {
+ GridView grid = (GridView) view.findViewById(R.id.comics_grid);
+ grid.setAdapter(m_adapter);
+ grid.setEmptyView(view.findViewById(R.id.no_comics));
+ grid.setOnItemClickListener(this);
+
+ registerForContextMenu(grid);
+
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+
+ getActivity().getMenuInflater().inflate(R.menu.comic_archive_context, menu);
+
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
+
+ Cursor c = (Cursor) m_adapter.getItem(info.position);
+
+ if (c != null) {
+ menu.setHeaderTitle(c.getString(c.getColumnIndex("filename")));
+ }
+
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+
+ Cursor c = (Cursor) m_adapter.getItem(info.position);
+ String fileName = c.getString(c.getColumnIndex("path")) + "/" + c.getString(c.getColumnIndex("filename"));
+
+ switch (item.getItemId()) {
+ case R.id.menu_reset_progress:
+ if (fileName != null) {
+ m_activity.resetProgress(fileName);
+ m_adapter.notifyDataSetChanged();
+ }
+ return true;
+ case R.id.menu_mark_as_read:
+
+ if (fileName != null) {
+ m_activity.setLastPosition(fileName, m_activity.getSize(fileName)-1);
+ m_adapter.notifyDataSetChanged();
+ }
+
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ private Cursor createCursor() {
+ String baseDir = m_baseDirectory.length() == 0 ? m_prefs.getString("comics_directory", "") : m_baseDirectory;
+
+ String selection;
+ String selectionArgs[];
+
+ switch (m_mode) {
+ case MODE_READ:
+ selection = "path = ? AND position = size - 1";
+ selectionArgs = new String[] { baseDir };
+ break;
+ case MODE_UNFINISHED:
+ selection = "path = ? AND position < size AND position > 0 AND position != size - 1";
+ selectionArgs = new String[] { baseDir };
+ break;
+ case MODE_UNREAD:
+ selection = "path = ? AND position = 0 AND size != ?";
+ selectionArgs = new String[] { baseDir, String.valueOf(SIZE_DIR) };
+ break;
+ default:
+ selection = "path = ?";
+ selectionArgs = new String[] { baseDir };
+ }
+
+ if (!m_prefs.getBoolean("enable_rar", false)) {
+ selection += " AND (UPPER(filename) NOT LIKE '%.CBR' AND UPPER(filename) NOT LIKE '%.RAR')";
+ }
+
+ return m_activity.getReadableDb().query("comics_cache", new String[] { BaseColumns._ID, "filename", "path",
+ "(SELECT path || '/' || filename FROM comics_cache AS t2 WHERE t2.path = comics_cache.path || '/' || comics_cache.filename AND filename != '' ORDER BY filename LIMIT 1) AS firstchild" },
+ selection, selectionArgs, null, null, "size != " + SIZE_DIR + ", filename, size = " + SIZE_DIR + ", filename");
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ m_activity = (CommonActivity)activity;
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
+ }
+
+ protected void rescan(final boolean fullRescan) {
+ m_swipeLayout.setRefreshing(true);
+
+ AsyncTask<String, Integer, Integer> rescanTask = new AsyncTask<String, Integer, Integer>() {
+
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ if (isAdded()) {
+ m_activity.setProgress(Math.round(((float)progress[0] / (float)progress[1]) * 10000));
+ }
+ }
+
+ @Override
+ protected Integer doInBackground(String... params) {
+ String comicsDir = params[0];
+
+ File dir = new File(comicsDir);
+
+ int fileIndex = 0;
+
+ if (dir.isDirectory()) {
+ File archives[] = dir.listFiles();
+
+ java.util.Arrays.sort(archives);
+
+ for (File archive : archives) {
+ String filePath = archive.getAbsolutePath();
+
+ if (archive.isDirectory()) {
+ m_activity.setSize(filePath, SIZE_DIR);
+
+ } else if (archive.getName().toLowerCase().matches(".*\\.(cbz|zip|cbr|rar)") && isAdded() && m_activity != null &&
+ m_activity.getWritableDb() != null && m_activity.getWritableDb().isOpen()) {
+ try {
+ int size = m_activity.getSize(filePath);
+
+ if (size == -1 || fullRescan) {
+
+ ComicArchive cba = null;
+
+ if (archive.getName().toLowerCase().matches(".*\\.(cbz|zip)")) {
+ cba = new CbzComicArchive(filePath);
+ } else {
+ try {
+ cba = new CbrComicArchive(filePath);
+ } catch (RarException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (cba != null && cba.getCount() > 0) {
+ // Get cover
+
+ try {
+ File thumbnailFile = new File(m_activity.getCacheFileName(filePath));
+
+ if (m_activity.isStorageWritable() && (!thumbnailFile.exists() || fullRescan)) {
+ InputStream is = cba.getItem(0);
+
+ if (is != null) {
+ FileOutputStream fos = new FileOutputStream(thumbnailFile);
+
+ byte[] buffer = new byte[1024];
+ int len = 0;
+ while ((len = is.read(buffer)) != -1) {
+ fos.write(buffer, 0, len);
+ }
+
+ fos.close();
+ is.close();
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ size = cba.getCount();
+
+ m_activity.setSize(filePath, size);
+ }
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ ++fileIndex;
+
+ publishProgress(Integer.valueOf(fileIndex), Integer.valueOf(archives.length));
+ }
+ }
+
+ if (isAdded() && m_activity != null) {
+ m_activity.cleanupCache(false);
+ m_activity.cleanupSqliteCache(comicsDir);
+
+ m_swipeLayout.setRefreshing(false);
+ }
+
+ return fileIndex; //m_files.size();
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ if (isAdded() && m_adapter != null) {
+ m_adapter.changeCursor(createCursor());
+ m_adapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ String comicsDir = m_prefs.getString("comics_directory", null);
+
+ if (comicsDir != null && m_activity.isStorageAvailable()) {
+ rescanTask.execute(m_baseDirectory.length() > 0 ? m_baseDirectory : comicsDir);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ m_adapter.notifyDataSetChanged();
+
+ String comicsDir = m_prefs.getString("comics_directory", "");
+
+ if (m_activity.getCachedItemCount(m_baseDirectory.length() > 0 ? m_baseDirectory : comicsDir) == 0) {
+ rescan(false);
+ } else {
+ m_adapter.notifyDataSetChanged();
+ }
+ }
+
+ public void onItemClick(AdapterView<?> av, View view, int position, long id) {
+ //Log.d(TAG, "onItemClick position=" + position);
+
+ Cursor c = (Cursor) m_adapter.getItem(position);
+ String fileName = c.getString(c.getColumnIndex("path")) + "/" + c.getString(c.getColumnIndex("filename"));
+
+ if (fileName != null) {
+ m_activity.onComicArchiveSelected(fileName);
+ }
+ }
+
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putInt("mode", m_mode);
+ out.putString("baseDir", m_baseDirectory);
+ //out.putStringArrayList("files", m_files);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java
new file mode 100644
index 0000000..4371df4
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java
@@ -0,0 +1,166 @@
+package org.fox.ttcomics;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import com.github.junrar.exception.RarException;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ComicPager extends Fragment {
+ private String m_fileName;
+ private SharedPreferences m_prefs;
+ private final String TAG = this.getClass().getSimpleName();
+ private ComicArchive m_archive;
+ private CommonActivity m_activity;
+
+ private class PagerAdapter extends FragmentStatePagerAdapter {
+ public PagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ ComicFragment cf = new ComicFragment();
+ cf.setPage(position);
+
+ return cf;
+ }
+
+ @Override
+ public int getCount() {
+ return m_archive.getCount();
+ }
+ }
+
+ private PagerAdapter m_adapter;
+
+ public ComicPager() {
+ super();
+ }
+
+ public ComicArchive getArchive() {
+ return m_archive;
+ }
+
+ public int getCount() {
+ return m_adapter.getCount();
+ }
+
+ public int getPosition() {
+ ViewPager pager = (ViewPager) getView().findViewById(R.id.comics_pager);
+
+ if (pager != null) {
+ return pager.getCurrentItem();
+ }
+
+ return 0;
+ }
+
+ public String getFileName() {
+ return m_fileName;
+ }
+
+ public void setCurrentItem(int item) {
+ ViewPager pager = (ViewPager) getView().findViewById(R.id.comics_pager);
+
+ if (pager != null) {
+ try {
+ pager.setCurrentItem(item);
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void setFileName(String fileName) {
+ m_fileName = fileName;
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ final View view = inflater.inflate(R.layout.fragment_comics_pager, container, false);
+
+ m_adapter = new PagerAdapter(getActivity().getSupportFragmentManager());
+
+ final ViewPager pager = (ViewPager) view.findViewById(R.id.comics_pager);
+
+ if (savedInstanceState != null) {
+ m_fileName = savedInstanceState.getString("fileName");
+ }
+
+ try {
+ if (m_fileName.toLowerCase().matches(".*\\.(cbz|zip)")) {
+ m_archive = new CbzComicArchive(m_fileName);
+ } else {
+ m_archive = new CbrComicArchive(m_fileName);
+ }
+
+ int position = m_activity.getLastPosition(m_fileName);
+
+ pager.setAdapter(m_adapter);
+ pager.setCurrentItem(position);
+
+ m_activity.onComicSelected(m_fileName, position);
+ m_activity.setProgress(Math.round(((float)position / (float)(m_archive.getCount()-1)) * 10000));
+ m_activity.hideSystemUiIfNecessary();
+
+ } catch (IOException e) {
+ m_activity.toast(R.string.error_could_not_open_comic_archive);
+ e.printStackTrace();
+ } catch (RarException e) {
+ m_activity.toast(R.string.error_could_not_open_comic_archive);
+ e.printStackTrace();
+ }
+
+ pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ public void onPageSelected(int position) {
+
+ m_activity.onComicSelected(m_fileName, position);
+ m_activity.setProgress(Math.round(((float)position / (float)(m_archive.getCount()-1)) * 10000));
+
+ m_activity.hideSystemUiIfNecessary();
+ }
+
+ public void onPageScrolled(int arg0, float arg1, int arg2) {
+ // TODO Auto-generated method stub
+ }
+
+ public void onPageScrollStateChanged(int arg0) {
+ // TODO Auto-generated method stub
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ m_activity = (CommonActivity) activity;
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putString("fileName", m_fileName);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CommonActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CommonActivity.java
new file mode 100644
index 0000000..6d392d2
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CommonActivity.java
@@ -0,0 +1,653 @@
+package org.fox.ttcomics;
+
+
+import java.io.File;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+
+import com.readystatesoftware.systembartint.SystemBarTintManager;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteStatement;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.support.v7.app.ActionBarActivity;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+public class CommonActivity extends ActionBarActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ protected static final String FRAG_COMICS_PAGER = "comic_pager";
+ protected static final String FRAG_COMICS_LIST = "comics_list";
+
+ protected final static int REQUEST_SHARE = 1;
+ protected static final int REQUEST_VIEWCOMIC = 2;
+
+ protected SharedPreferences m_prefs;
+ protected SyncClient m_syncClient = new SyncClient();
+
+ private boolean m_smallScreenMode = true;
+
+ private boolean m_storageAvailable;
+ private boolean m_storageWritable;
+
+ private SQLiteDatabase m_readableDb;
+ private SQLiteDatabase m_writableDb;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+
+ initDatabase();
+ initSync();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ String state = Environment.getExternalStorageState();
+
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ m_storageAvailable = true;
+ m_storageWritable = true;
+ } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+ m_storageAvailable = true;
+ m_storageWritable = false;
+ } else {
+ m_storageAvailable = false;
+ m_storageWritable = false;
+ }
+
+ initSync();
+ }
+
+ private void initSync() {
+ if (m_prefs.getBoolean("use_position_sync", false)) {
+ String googleAccount = getGoogleAccount();
+
+ if (googleAccount != null) {
+ m_syncClient.setOwner(googleAccount);
+ } else {
+ if (Build.HARDWARE.contains("goldfish")) {
+ m_syncClient.setOwner("TEST-ACCOUNT");
+
+ //toast(R.string.sync_running_in_test_mode);
+ } else {
+ m_syncClient.setOwner(null);
+ toast(R.string.error_sync_no_account);
+
+ SharedPreferences.Editor editor = m_prefs.edit();
+ editor.putBoolean("use_position_sync", false);
+ editor.commit();
+ }
+ }
+ } else {
+ m_syncClient.setOwner(null);
+ }
+ }
+
+ public static boolean isCompatMode() {
+ return android.os.Build.VERSION.SDK_INT <= 10;
+ }
+
+ protected void setSmallScreen(boolean smallScreen) {
+ Log.d(TAG, "m_smallScreenMode=" + smallScreen);
+ m_smallScreenMode = smallScreen;
+ }
+
+ public boolean isSmallScreen() {
+ return m_smallScreenMode;
+ }
+
+
+ public void onComicArchiveSelected(String fileName) {
+ //
+ }
+
+ public Cursor findComicByFileName(String fileName) {
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", null,
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ return c;
+ } else {
+ c.close();
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("INSERT INTO comics_cache " +
+ "(filename, path, position, max_position, size, checksum) VALUES (?, ?, 0, 0, -1, '')");
+ stmt.bindString(1, file.getName());
+ stmt.bindString(2, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+
+ c = getReadableDb().query("comics_cache", null,
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ return c;
+ } else {
+ c.close();
+ }
+ }
+
+ return null;
+ }
+
+ public void setSize(String fileName, int size) {
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET size = ? WHERE filename = ? AND path = ?");
+ stmt.bindLong(1, size);
+ stmt.bindString(2, file.getName());
+ stmt.bindString(3, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+ }
+
+ public void setChecksum(String fileName, String checksum) {
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET checksum = ? WHERE filename = ? AND path = ?");
+ stmt.bindString(1, checksum);
+ stmt.bindString(2, file.getName());
+ stmt.bindString(3, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+ }
+
+ public void resetProgress(final String fileName) {
+
+ if (m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner()) {
+ setLastPosition(fileName, 0);
+ setLastMaxPosition(fileName, 0);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(CommonActivity.this);
+ builder.setMessage(R.string.reset_remove_synced_progress)
+ .setCancelable(true)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ m_syncClient.clearData(sha1(new File(fileName).getName()));
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ }
+
+ }
+
+ public void setLastPosition(String fileName, int position) {
+ int lastPosition = getLastPosition(fileName);
+
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET position = ?, max_position = ? WHERE filename = ? AND path = ?");
+ stmt.bindLong(1, position);
+ stmt.bindLong(2, Math.max(position, lastPosition));
+ stmt.bindString(3, file.getName());
+ stmt.bindString(4, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+
+ }
+
+ public void setLastMaxPosition(String fileName, int position) {
+
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET max_position = ? WHERE filename = ? AND path = ?");
+ stmt.bindLong(1, position);
+ stmt.bindString(2, file.getName());
+ stmt.bindString(3, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+ }
+
+ public String getChecksum(String fileName) {
+ String checksum = null;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "checksum" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ checksum = c.getString(c.getColumnIndex("checksum"));
+ }
+
+ c.close();
+
+ return checksum;
+
+ }
+
+
+ public int getLastPosition(String fileName) {
+ int position = 0;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "position" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ position = c.getInt(c.getColumnIndex("position"));
+ }
+
+ c.close();
+
+ return position;
+
+ }
+
+ public int getCachedItemCount(String baseDir) {
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "COUNT(*)" },
+ "path = ?",
+ new String[] { baseDir }, null, null, null);
+
+ c.moveToFirst();
+ int count = c.getInt(0);
+ c.close();
+
+ //Log.d(TAG, "getCachedItemCount:" + baseDir + "=" + count);
+
+ return count;
+ }
+
+ public int getMaxPosition(String fileName) {
+ int position = 0;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "max_position" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ position = c.getInt(c.getColumnIndex("max_position"));
+ }
+
+ c.close();
+
+ return position;
+ }
+
+ public int getSize(String fileName) {
+ int size = -1;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "size" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ size = c.getInt(c.getColumnIndex("size"));
+ }
+
+ c.close();
+
+ //Log.d(TAG, "getSize:" + fileName + "=" + size);
+
+ return size;
+ }
+
+ public void onComicSelected(String fileName, int position) {
+ setLastPosition(fileName, position);
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_donate:
+ if (true) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://details?id=org.fox.ttcomics.donation"));
+ startActivity(intent);
+ } catch (ActivityNotFoundException ae) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("https://play.google.com/store/apps/details?id=org.fox.ttcomics.donation"));
+ startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ return true;
+ case R.id.menu_rescan:
+ ComicListFragment frag = (ComicListFragment) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_LIST);
+
+ if (frag != null && frag.isAdded()) {
+ frag.rescan(true);
+ }
+
+ return true;
+ case R.id.menu_settings:
+ Intent intent = new Intent(CommonActivity.this,
+ PreferencesActivity.class);
+ startActivityForResult(intent, 0);
+ return true;
+ default:
+ Log.d(TAG,
+ "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean isPortrait() {
+ Display display = getWindowManager().getDefaultDisplay();
+
+ int width = display.getWidth();
+ int height = display.getHeight();
+
+ return width < height;
+ }
+
+ protected static String md5(String s) {
+ try {
+ MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
+ digest.update(s.getBytes());
+ byte messageDigest[] = digest.digest();
+
+ StringBuffer hexString = new StringBuffer();
+ for (int i=0; i<messageDigest.length; i++)
+ hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
+
+ return hexString.toString();
+
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ protected static String sha1(String s) {
+ if (s != null) {
+ try {
+ MessageDigest digest = java.security.MessageDigest.getInstance("SHA1");
+ digest.update(s.getBytes());
+ byte messageDigest[] = digest.digest();
+
+ StringBuffer hexString = new StringBuffer();
+ for (int i=0; i<messageDigest.length; i++)
+ hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
+
+ return hexString.toString();
+
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ public String getCacheFileName(String fileName) {
+ String hash = md5(fileName);
+
+ File file = new File(getExternalCacheDir().getAbsolutePath() + "/" + hash + ".png");
+
+ return file.getAbsolutePath();
+ }
+
+ public String getGoogleAccount() {
+ AccountManager manager = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+ Account[] list = manager.getAccounts();
+
+ for (Account account: list) {
+ if (account.type.equalsIgnoreCase("com.google")) {
+ return account.name;
+ }
+ }
+ return null;
+ }
+
+ public void toast(int msgId) {
+ try {
+ Toast toast = Toast.makeText(CommonActivity.this, msgId, Toast.LENGTH_SHORT);
+ toast.show();
+ } catch (RuntimeException e) {
+ // might happen if UI lags
+ }
+ }
+
+ public void toast(String msg) {
+ try {
+ Toast toast = Toast.makeText(CommonActivity.this, msg, Toast.LENGTH_SHORT);
+ toast.show();
+ } catch (RuntimeException e) {
+ // might happen if UI lags
+ }
+ }
+
+ public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+ if (width > height) {
+ inSampleSize = Math.round((float)height / (float)reqHeight);
+ } else {
+ inSampleSize = Math.round((float)width / (float)reqWidth);
+ }
+ }
+ return inSampleSize;
+ }
+
+
+ public void cleanupCache(boolean deleteAll) {
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ File cachePath = getExternalCacheDir();
+
+ long now = new Date().getTime();
+
+ if (cachePath.isDirectory()) {
+ for (File file : cachePath.listFiles()) {
+ if (deleteAll || now - file.lastModified() > 1000*60*60*24*7) {
+ file.delete();
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isStorageAvailable() {
+ return m_storageAvailable;
+ }
+
+ public boolean isStorageWritable() {
+ return m_storageWritable;
+ }
+
+ private void initDatabase() {
+ DatabaseHelper dh = new DatabaseHelper(getApplicationContext());
+
+ m_writableDb = dh.getWritableDatabase();
+ m_readableDb = dh.getReadableDatabase();
+ }
+
+ public synchronized SQLiteDatabase getReadableDb() {
+ return m_readableDb;
+ }
+
+ public synchronized SQLiteDatabase getWritableDb() {
+ return m_writableDb;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ m_readableDb.close();
+ m_writableDb.close();
+ }
+
+ public void selectPreviousComic() {
+ ComicPager frag = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (frag != null && frag.isAdded() && frag.getPosition() > 0) {
+ frag.setCurrentItem(frag.getPosition() - 1);
+ }
+ }
+
+ public void selectNextComic() {
+ ComicPager frag = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (frag != null && frag.isAdded() && frag.getPosition() < frag.getCount()-1) {
+ frag.setCurrentItem(frag.getPosition() + 1);
+ }
+ }
+
+ public void cleanupSqliteCache(String baseDir) {
+ Cursor c = getReadableDb().query("comics_cache", null,
+ null, null, null, null, null);
+
+ if (c.moveToFirst()) {
+ while (!c.isAfterLast()) {
+ int id = c.getInt(c.getColumnIndex(BaseColumns._ID));
+ String fileName = c.getString(c.getColumnIndex("path")) + "/" + c.getString(c.getColumnIndex("filename"));
+
+ File file = new File(fileName);
+
+ if (!file.exists()) {
+ SQLiteStatement stmt = getWritableDb().compileStatement("DELETE FROM comics_cache WHERE " + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, id);
+ stmt.execute();
+ }
+
+ c.moveToNext();
+ }
+ }
+
+ c.close();
+ }
+
+ public void showSystemUiIfNecessary() {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ } else {
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_VISIBLE);
+ }
+ }
+ }
+
+ public void setStatusBarTint() {
+ if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.KITKAT) {
+ SystemBarTintManager tintManager = new SystemBarTintManager(this);
+ // enable status bar tint
+ tintManager.setStatusBarTintEnabled(true);
+ // enable navigation bar tint
+ tintManager.setNavigationBarTintEnabled(true);
+
+ TypedValue tv = new TypedValue();
+ getTheme().resolveAttribute(R.attr.statusBarHintColor, tv, true);
+
+ tintManager.setStatusBarTintColor(tv.data);
+ }
+ }
+
+ public void hideSystemUiIfNecessary() {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_IMMERSIVE);
+
+ } else if (m_prefs.getBoolean("dim_status_bar", false)) {
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ }
+
+ } else {
+
+ if (!isCompatMode()) {
+ if (m_prefs.getBoolean("dim_status_bar", false)) {
+ getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
+ }
+ }
+
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ getSupportActionBar().hide();
+ }
+
+ }
+
+
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java
new file mode 100644
index 0000000..15b7a25
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java
@@ -0,0 +1,68 @@
+package org.fox.ttcomics;
+
+import java.io.File;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteStatement;
+import android.provider.BaseColumns;
+
+public class DatabaseHelper extends SQLiteOpenHelper {
+
+ public static final String DATABASE_NAME = "ComicsCache.db";
+ public static final int DATABASE_VERSION = 2;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS comics_cache;");
+
+ db.execSQL("CREATE TABLE IF NOT EXISTS comics_cache (" +
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+ "filename TEXT, " +
+ "path TEXT, " + //v2
+ "checksum TEXT, " + //v2
+ "size INTEGER, " +
+ "position INTEGER, " +
+ "max_position INTEGER" +
+ ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion == 1 && newVersion == 2) {
+
+ db.execSQL("ALTER TABLE comics_cache ADD COLUMN path TEXT;");
+ db.execSQL("ALTER TABLE comics_cache ADD COLUMN checksum TEXT;");
+
+ Cursor c = db.query("comics_cache", null,
+ null, null, null, null, null);
+
+ if (c.moveToFirst()) {
+ while (!c.isAfterLast()) {
+ int id = c.getInt(c.getColumnIndex(BaseColumns._ID));
+ String fileName = c.getString(c.getColumnIndex("filename"));
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = db.compileStatement("UPDATE comics_cache SET filename = ?, path = ? WHERE " + BaseColumns._ID + " = ?");
+ stmt.bindString(1, file.getName());
+ stmt.bindString(2, file.getParentFile().getAbsolutePath());
+ stmt.bindLong(3, id);
+ stmt.execute();
+
+ c.moveToNext();
+ }
+ }
+
+ c.close();
+ }
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DirectoryPicker.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DirectoryPicker.java
new file mode 100644
index 0000000..d607b48
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DirectoryPicker.java
@@ -0,0 +1,169 @@
+package org.fox.ttcomics;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+/**
+Copyright (C) 2011 by Brad Greco <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+ */
+
+public class DirectoryPicker extends ListActivity {
+
+ public static final String START_DIR = "startDir";
+ public static final String ONLY_DIRS = "onlyDirs";
+ public static final String SHOW_HIDDEN = "showHidden";
+ public static final String CHOSEN_DIRECTORY = "chosenDir";
+ public static final int PICK_DIRECTORY = 43522432;
+ private File dir;
+ private boolean showHidden = false;
+ private boolean onlyDirs = true ;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Bundle extras = getIntent().getExtras();
+ dir = Environment.getExternalStorageDirectory();
+ if (extras != null) {
+ String preferredStartDir = extras.getString(START_DIR);
+ showHidden = extras.getBoolean(SHOW_HIDDEN, false);
+ onlyDirs = extras.getBoolean(ONLY_DIRS, true);
+ if(preferredStartDir != null) {
+ File startDir = new File(preferredStartDir);
+ if(startDir.isDirectory()) {
+ dir = startDir;
+ }
+ }
+ }
+
+ setContentView(R.layout.chooser_list);
+ setTitle(dir.getAbsolutePath());
+ Button btnChoose = (Button) findViewById(R.id.btnChoose);
+ String name = dir.getName();
+ if(name.length() == 0)
+ name = "/";
+ btnChoose.setText(getString(R.string.picker_choose, name));
+ btnChoose.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ returnDir(dir.getAbsolutePath());
+ }
+ });
+
+ Button btnParent = (Button) findViewById(R.id.btnParent);
+ btnParent.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(DirectoryPicker.this, DirectoryPicker.class);
+ intent.putExtra(DirectoryPicker.START_DIR, dir.getParent());
+ intent.putExtra(DirectoryPicker.SHOW_HIDDEN, showHidden);
+ intent.putExtra(DirectoryPicker.ONLY_DIRS, onlyDirs);
+ startActivityForResult(intent, PICK_DIRECTORY);
+ }
+ });
+
+ if (dir.getParent() == null) {
+ btnParent.setVisibility(View.GONE);
+ }
+
+ ListView lv = getListView();
+ lv.setTextFilterEnabled(true);
+
+ if(!dir.canRead()) {
+ Context context = getApplicationContext();
+ String msg = getString(R.string.error_could_not_read_folder_contents_);
+ Toast toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
+ toast.show();
+ return;
+ }
+
+ final ArrayList<File> files = filter(dir.listFiles(), onlyDirs, showHidden);
+ String[] names = names(files);
+ setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, names));
+
+
+ lv.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if(!files.get(position).isDirectory())
+ return;
+ String path = files.get(position).getAbsolutePath();
+ Intent intent = new Intent(DirectoryPicker.this, DirectoryPicker.class);
+ intent.putExtra(DirectoryPicker.START_DIR, path);
+ intent.putExtra(DirectoryPicker.SHOW_HIDDEN, showHidden);
+ intent.putExtra(DirectoryPicker.ONLY_DIRS, onlyDirs);
+ startActivityForResult(intent, PICK_DIRECTORY);
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if(requestCode == PICK_DIRECTORY && resultCode == RESULT_OK) {
+ Bundle extras = data.getExtras();
+ String path = (String) extras.get(DirectoryPicker.CHOSEN_DIRECTORY);
+ returnDir(path);
+ }
+ }
+
+ private void returnDir(String path) {
+ Intent result = new Intent();
+ result.putExtra(CHOSEN_DIRECTORY, path);
+ setResult(RESULT_OK, result);
+ finish();
+ }
+
+ public ArrayList<File> filter(File[] file_list, boolean onlyDirs, boolean showHidden) {
+ ArrayList<File> files = new ArrayList<File>();
+
+ for(File file: file_list) {
+ if(onlyDirs && !file.isDirectory())
+ continue;
+ if(!showHidden && file.isHidden())
+ continue;
+ files.add(file);
+ }
+ Collections.sort(files);
+
+ return files;
+ }
+
+ public String[] names(ArrayList<File> files) {
+ String[] names = new String[files.size()];
+ int i = 0;
+ for(File file: files) {
+ names[i] = file.getName();
+ i++;
+ }
+ return names;
+ }
+}
+
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/MainActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/MainActivity.java
new file mode 100644
index 0000000..0e589fa
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/MainActivity.java
@@ -0,0 +1,253 @@
+package org.fox.ttcomics;
+
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import android.animation.LayoutTransition;
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBar.Tab;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.ShareActionProvider;
+
+public class MainActivity extends CommonActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private TabListener m_tabListener;
+ private int m_selectedTab;
+ private String m_baseDirectory = "";
+ private String m_fileName = "";
+
+ @SuppressLint("NewApi")
+ private class TabListener implements ActionBar.TabListener {
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ FragmentTransaction sft = getSupportFragmentManager().beginTransaction();
+
+ if (m_selectedTab != tab.getPosition() && m_selectedTab != -1) {
+
+ ComicListFragment frag = new ComicListFragment();
+ frag.setMode(tab.getPosition());
+
+ frag.setBaseDirectory(m_baseDirectory);
+
+ sft.replace(R.id.comics_list, frag, FRAG_COMICS_LIST);
+ }
+
+ m_selectedTab = tab.getPosition();
+
+ sft.commit();
+ }
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+
+ setContentView(R.layout.activity_main);
+
+ setProgressBarVisibility(false);
+
+ setStatusBarTint();
+ setSmallScreen(findViewById(R.id.tablet_layout_hack) == null);
+
+ if (savedInstanceState == null) {
+ m_selectedTab = getIntent().getIntExtra("selectedTab", 0);
+
+ Log.d(TAG, "selTab=" + m_selectedTab);
+
+ ComicListFragment frag = new ComicListFragment();
+ frag.setMode(m_selectedTab);
+
+ if (getIntent().getStringExtra("baseDir") != null) {
+ m_baseDirectory = getIntent().getStringExtra("baseDir");
+ frag.setBaseDirectory(m_baseDirectory);
+ }
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.replace(R.id.comics_list, frag, FRAG_COMICS_LIST);
+ ft.commit();
+
+ m_selectedTab = -1;
+ } else {
+ m_selectedTab = -1;
+ m_baseDirectory = savedInstanceState.getString("baseDir");
+ m_fileName = savedInstanceState.getString("fileName");
+ }
+
+ m_tabListener = new TabListener();
+
+ ActionBar actionBar = getSupportActionBar();
+
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_all_comics)
+ .setTabListener(m_tabListener));
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_unread)
+ .setTabListener(m_tabListener));
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_unfinished)
+ .setTabListener(m_tabListener));
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_read)
+ .setTabListener(m_tabListener));
+
+ if (savedInstanceState != null) {
+ m_selectedTab = savedInstanceState.getInt("selectedTab");
+ } else {
+ m_selectedTab = getIntent().getIntExtra("selectedTab", 0);
+ }
+
+ actionBar.selectTab(actionBar.getTabAt(m_selectedTab));
+
+ actionBar.setDisplayHomeAsUpEnabled(m_baseDirectory.length() > 0);
+
+ if (m_prefs.getString("comics_directory", null) == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage(R.string.dialog_need_prefs_message)
+ .setCancelable(false)
+ .setPositiveButton(R.string.dialog_need_prefs_preferences, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // launch preferences
+
+ Intent intent = new Intent(MainActivity.this,
+ PreferencesActivity.class);
+ startActivityForResult(intent, 0);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+
+ if (!isCompatMode()) {
+ ((ViewGroup)findViewById(R.id.comics_list)).setLayoutTransition(new LayoutTransition());
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_main, menu);
+
+ boolean isDonationFound = getPackageManager().checkSignatures(
+ getPackageName(), "org.fox.ttcomics.donation") == PackageManager.SIGNATURE_MATCH;
+
+ if (isDonationFound)
+ menu.findItem(R.id.menu_donate).setVisible(false);
+
+ return true;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putInt("selectedTab", m_selectedTab);
+ out.putString("baseDir", m_baseDirectory);
+ out.putString("fileName", m_fileName);
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ if (m_baseDirectory.length() > 0) {
+ finish();
+ }
+ return true;
+ default:
+ Log.d(TAG,
+ "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onComicArchiveSelected(String fileName) {
+ super.onComicArchiveSelected(fileName);
+
+ File file = new File(fileName);
+
+ if (file.isDirectory()) {
+ Intent intent = new Intent(MainActivity.this,
+ MainActivity.class);
+
+ intent.putExtra("baseDir", fileName);
+ intent.putExtra("selectedTab", m_selectedTab);
+
+ startActivityForResult(intent, 0);
+
+ } else if (file.canRead()) {
+ Intent intent = new Intent(MainActivity.this,
+ ViewComicActivity.class);
+
+ intent.putExtra("fileName", fileName);
+ m_fileName = fileName;
+
+ startActivityForResult(intent, REQUEST_VIEWCOMIC);
+ } else {
+ toast(getString(R.string.error_cant_open_file, fileName));
+
+ ComicListFragment frag = (ComicListFragment) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_LIST);
+
+ if (frag != null && frag.isAdded()) {
+ frag.rescan(true);
+ }
+ }
+ }
+
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == REQUEST_VIEWCOMIC) {
+ Log.d(TAG, "finished viewing comic: " + m_fileName);
+
+ if (m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner()) {
+ toast(R.string.sync_uploading);
+ m_syncClient.setPosition(sha1(new File(m_fileName).getName()), getLastPosition(m_fileName));
+ }
+ }
+
+ System.gc();
+
+ super.onActivityResult(requestCode, resultCode, intent);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java
new file mode 100644
index 0000000..7aa24a5
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java
@@ -0,0 +1,124 @@
+package org.fox.ttcomics;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceManager;
+import android.widget.Toast;
+
+public class PreferencesActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ addPreferencesFromResource(R.xml.preferences);
+
+ if (CommonActivity.isCompatMode()) {
+ Preference dimPref = findPreference("dim_status_bar");
+ PreferenceCategory readingCat = (PreferenceCategory) findPreference("prefs_reading");
+ readingCat.removePreference(dimPref);
+ }
+
+ Preference dirPref = findPreference("comics_directory");
+ dirPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(PreferencesActivity.this, DirectoryPicker.class);
+
+ intent.putExtra(DirectoryPicker.START_DIR, prefs.getString("comics_directory",
+ Environment.getExternalStorageDirectory().getAbsolutePath()));
+
+ startActivityForResult(intent, DirectoryPicker.PICK_DIRECTORY);
+ return true;
+ }
+ });
+
+ Preference clearPref = findPreference("clear_sync_data");
+ clearPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(PreferencesActivity.this);
+ builder.setMessage(R.string.dialog_clear_data_title)
+ .setCancelable(false)
+ .setPositiveButton(R.string.dialog_clear_data, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+
+ String googleAccount = getGoogleAccount();
+ SyncClient m_syncClient = new SyncClient();
+
+ if (googleAccount != null) {
+ m_syncClient.setOwner(googleAccount);
+ } else {
+ if (Build.HARDWARE.equals("goldfish")) {
+ m_syncClient.setOwner("TEST-ACCOUNT");
+ } else {
+ m_syncClient.setOwner(null);
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean("use_position_sync", false);
+ editor.commit();
+
+ Toast toast = Toast.makeText(PreferencesActivity.this, R.string.error_sync_no_account, Toast.LENGTH_SHORT);
+ toast.show();
+ }
+ }
+
+ if (m_syncClient.hasOwner()) {
+ m_syncClient.clearData();
+ }
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ return false;
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if(requestCode == DirectoryPicker.PICK_DIRECTORY && resultCode == RESULT_OK) {
+ Bundle extras = data.getExtras();
+ String path = (String) extras.get(DirectoryPicker.CHOSEN_DIRECTORY);
+
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString("comics_directory", path);
+ editor.commit();
+
+ }
+ }
+
+ public String getGoogleAccount() {
+ AccountManager manager = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+ Account[] list = manager.getAccounts();
+
+ for (Account account: list) {
+ if (account.type.equalsIgnoreCase("com.google")) {
+ return account.name;
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/SyncClient.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/SyncClient.java
new file mode 100644
index 0000000..f67d01b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/SyncClient.java
@@ -0,0 +1,159 @@
+package org.fox.ttcomics;
+
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+public class SyncClient {
+ public interface PositionReceivedListener {
+ void onPositionReceived(int position);
+ }
+
+ private class HttpTask extends AsyncTask<String, Integer, Boolean> {
+ protected String m_response = null;
+ protected int m_responseCode = -1;
+
+ @Override
+ protected Boolean doInBackground(String... params) {
+
+ String requestStr = null;
+ String op = params[0];
+
+ if (op.equals("set")) {
+ requestStr = String.format("op=set&owner=%1$s&hash=%2$s&position=%3$s", m_owner, params[1], params[2]);
+ } else if (op.equals("get")) {
+ requestStr = String.format("op=get&owner=%1$s&hash=%2$s", m_owner, params[1]);
+ } else if (op.equals("clear")) {
+ if (params.length > 1) {
+ requestStr = String.format("op=clear&owner=%1$s&hash=%2$s", m_owner, params[1]);
+ } else {
+ requestStr = String.format("op=clear&owner=%1$s", m_owner);
+ }
+ }
+
+ Log.d(TAG, requestStr);
+
+ if (requestStr == null) return false;
+
+ try {
+ byte[] postData = requestStr.getBytes("UTF-8");
+
+ URL url = new URL(SYNC_ENDPOINT);
+
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+ conn.setUseCaches(false);
+ conn.setRequestMethod("POST");
+
+ OutputStream out = conn.getOutputStream();
+ out.write(postData);
+ out.close();
+
+ m_responseCode = conn.getResponseCode();
+
+ if (m_responseCode == HttpURLConnection.HTTP_OK) {
+ StringBuffer response = new StringBuffer();
+ InputStreamReader in = new InputStreamReader(conn.getInputStream(), "UTF-8");
+
+ char[] buf = new char[1024];
+ int read = 0;
+
+ while ((read = in.read(buf)) >= 0) {
+ response.append(buf, 0, read);
+ }
+
+ //Log.d(TAG, "<<< " + response);
+
+ m_response = response.toString();
+
+ if (response.indexOf("ERROR") == -1) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ Log.d(TAG, "HTTP error, code: " + m_responseCode);
+ }
+
+ conn.disconnect();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ }
+
+ private final String TAG = this.getClass().getSimpleName();
+ private static final String SYNC_ENDPOINT = "http://tt-rss.org/tcrsync/";
+ private String m_owner = null;
+
+ public void setOwner(String owner) {
+ m_owner = CommonActivity.sha1(owner);
+ }
+
+ public int getPosition(String hash, final PositionReceivedListener listener) {
+ if (m_owner != null) {
+ Log.d(TAG, "Requesting sync data...");
+
+ HttpTask task = new HttpTask() {
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (result) {
+ try {
+ listener.onPositionReceived(Integer.valueOf(m_response));
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+
+ task.execute("get", hash);
+
+ }
+ return -1;
+ }
+
+ public void setPosition(String hash, int position) {
+ if (m_owner != null) {
+ Log.d(TAG, "Uploading sync data...");
+
+ HttpTask task = new HttpTask();
+
+ task.execute("set", hash, String.valueOf(position));
+ }
+ }
+
+ public void clearData() {
+ if (m_owner != null) {
+ Log.d(TAG, "Clearing sync data...");
+
+ HttpTask task = new HttpTask();
+
+ task.execute("clear");
+ }
+ }
+
+ public void clearData(String hash) {
+ if (m_owner != null) {
+ Log.d(TAG, "Clearing sync data: " + hash);
+
+ HttpTask task = new HttpTask();
+
+ task.execute("clear", hash);
+ }
+ }
+
+ public boolean hasOwner() {
+ return m_owner != null;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewComicActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewComicActivity.java
new file mode 100644
index 0000000..6ccc0a5
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewComicActivity.java
@@ -0,0 +1,391 @@
+package org.fox.ttcomics;
+
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.NumberPicker;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class ViewComicActivity extends CommonActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private String m_fileName;
+ private String m_tmpFileName;
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!isCompatMode())
+ setTheme(m_prefs.getBoolean("use_dark_theme", false) ? R.style.ViewDarkTheme : R.style.ViewLightTheme);
+
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+
+ if (m_prefs.getBoolean("prevent_screen_sleep", false))
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ /* if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.KITKAT && !m_prefs.getBoolean("use_full_screen", false)) {
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ setStatusBarTint();
+ } */
+
+ setContentView(R.layout.activity_view_comic);
+
+ if (savedInstanceState == null) {
+ m_fileName = getIntent().getStringExtra("fileName");
+
+ ComicPager cp = new ComicPager();
+ cp.setFileName(m_fileName);
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.replace(R.id.comics_pager_container, cp, FRAG_COMICS_PAGER);
+ ft.commit();
+ } else {
+ m_fileName = savedInstanceState.getString("fileName");
+ m_tmpFileName = savedInstanceState.getString("tmpFileName");
+ }
+
+ setOrientationLock(isOrientationLocked(), true);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setTitle(new File(m_fileName).getName());
+
+ /* if (m_prefs.getBoolean("use_full_screen", false)) {
+ //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ // WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ //getSupportActionBar().hide();
+ } */
+
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && m_prefs.getBoolean("use_dark_theme", false)) {
+ getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_view_comic, menu);
+
+ menu.findItem(R.id.menu_sync_location).setVisible(m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner());
+
+ return true;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putString("fileName", m_fileName);
+ out.putString("tmpFileName", m_tmpFileName);
+ }
+
+ @Override
+ public void onComicSelected(String fileName, int position) {
+ super.onComicSelected(fileName, position);
+ }
+
+ public void onPause() {
+ super.onPause();
+
+ // upload progress
+ if (m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner()) {
+ //toast(R.string.sync_uploading);
+ m_syncClient.setPosition(sha1(new File(m_fileName).getName()), getLastPosition(m_fileName));
+ }
+ }
+
+ private void shareComic() {
+
+ ComicPager pager = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (pager != null) {
+
+ try {
+ File tmpFile = File.createTempFile("trcshare", ".jpg", getExternalCacheDir());
+
+ Log.d(TAG, "FILE=" + tmpFile);
+
+ InputStream is = pager.getArchive().getItem(pager.getPosition());
+
+ FileOutputStream fos = new FileOutputStream(tmpFile);
+
+ byte[] buffer = new byte[1024];
+ int len = 0;
+ while ((len = is.read(buffer)) != -1) {
+ fos.write(buffer, 0, len);
+ }
+
+ fos.close();
+ is.close();
+
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ shareIntent.setType("image/jpeg");
+ shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tmpFile));
+
+ m_tmpFileName = tmpFile.getAbsolutePath();
+
+ startActivityForResult(Intent.createChooser(shareIntent, getString(R.string.share_comic)), REQUEST_SHARE);
+
+ } catch (IOException e) {
+ toast(getString(R.string.error_could_not_prepare_file_for_sharing));
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == REQUEST_SHARE) {
+ File tmpFile = new File(m_tmpFileName);
+
+ if (tmpFile.exists()) {
+ tmpFile.delete();
+ }
+
+ }
+ super.onActivityResult(requestCode, resultCode, intent);
+ }
+
+ protected boolean isOrientationLocked() {
+ return m_prefs.getBoolean("prefs_lock_orientation", false);
+ }
+
+ private void setOrientationLock(boolean locked, boolean restoreLast) {
+ if (locked) {
+
+ int currentOrientation = restoreLast ? m_prefs.getInt("last_orientation", getResources().getConfiguration().orientation) :
+ getResources().getConfiguration().orientation;
+
+ if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
+ }
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
+ }
+
+ if (locked != isOrientationLocked()) {
+ SharedPreferences.Editor editor = m_prefs.edit();
+ editor.putBoolean("prefs_lock_orientation", locked);
+ editor.putInt("last_orientation", getResources().getConfiguration().orientation);
+ editor.commit();
+ }
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_share:
+ shareComic();
+ return true;
+ case R.id.menu_toggle_orientation_lock:
+ setOrientationLock(!isOrientationLocked(), false);
+ return true;
+ case R.id.menu_sync_location:
+ m_syncClient.getPosition(sha1(new File(m_fileName).getName()), new SyncClient.PositionReceivedListener() {
+ @Override
+ public void onPositionReceived(final int position) {
+ final ComicPager pager = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (pager != null && pager.isAdded()) {
+ int localPosition = pager.getPosition();
+
+ if (position > localPosition) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(ViewComicActivity.this);
+ builder.setMessage(getString(R.string.sync_server_has_further_page, localPosition+1, position+1))
+ .setCancelable(true)
+ .setPositiveButton(R.string.dialog_open_page, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ pager.setCurrentItem(position);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ } else {
+ toast(R.string.error_sync_no_data);
+ }
+
+ }
+ }
+ });
+ return true;
+ case R.id.menu_go_location:
+ Dialog dialog = new Dialog(ViewComicActivity.this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(ViewComicActivity.this)
+ .setTitle("Go to...")
+ .setItems(
+ new String[] {
+ getString(R.string.dialog_location_beginning),
+ getString(R.string.dialog_location_furthest),
+ getString(R.string.dialog_location_location),
+ getString(R.string.dialog_location_end)
+ },
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ final ComicPager cp = (ComicPager) getSupportFragmentManager().findFragmentByTag(CommonActivity.FRAG_COMICS_PAGER);
+
+ switch (which) {
+ case 0:
+ cp.setCurrentItem(0);
+ break;
+ case 1:
+ cp.setCurrentItem(getMaxPosition(m_fileName));
+ break;
+ case 2:
+ if (!isCompatMode()) {
+ LayoutInflater inflater = getLayoutInflater();
+ View contentView = inflater.inflate(R.layout.dialog_location, null);
+
+ final NumberPicker picker = (NumberPicker) contentView.findViewById(R.id.number_picker);
+
+ picker.setMinValue(1);
+ picker.setMaxValue(getSize(m_fileName));
+ picker.setValue(cp.getPosition()+1);
+
+ Dialog seekDialog = new Dialog(ViewComicActivity.this);
+ AlertDialog.Builder seekBuilder = new AlertDialog.Builder(ViewComicActivity.this)
+ .setTitle(R.string.dialog_open_location)
+ .setView(contentView)
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ }).setPositiveButton(R.string.dialog_open_location, new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+
+ cp.setCurrentItem(picker.getValue()-1);
+
+ }
+ });
+
+ seekDialog = seekBuilder.create();
+ seekDialog.show();
+ } else {
+ LayoutInflater inflater = getLayoutInflater();
+ final View contentView = inflater.inflate(R.layout.dialog_location_compat, null);
+
+ final SeekBar seeker = (SeekBar) contentView.findViewById(R.id.number_seeker);
+ final TextView pageNum = (TextView) contentView.findViewById(R.id.page_number);
+ final int size = getSize(m_fileName);
+
+ seeker.setMax(size-1);
+ seeker.setProgress(cp.getPosition());
+
+ pageNum.setText(getString(R.string.dialog_location_page_number, cp.getPosition()+1, size));
+
+ seeker.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
+
+ @Override
+ public void onProgressChanged(
+ SeekBar seekBar, int progress,
+ boolean fromUser) {
+
+ pageNum.setText(getString(R.string.dialog_location_page_number, progress+1, size));
+
+ }
+
+ @Override
+ public void onStartTrackingTouch(
+ SeekBar arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(
+ SeekBar arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ });
+
+ Dialog seekDialog = new Dialog(ViewComicActivity.this);
+ AlertDialog.Builder seekBuilder = new AlertDialog.Builder(ViewComicActivity.this)
+ .setTitle(R.string.dialog_open_location)
+ .setView(contentView)
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ }).setPositiveButton(R.string.dialog_open_location, new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+
+ cp.setCurrentItem(seeker.getProgress());
+
+ }
+ });
+
+ seekDialog = seekBuilder.create();
+ seekDialog.show();
+
+ }
+
+ break;
+ case 3:
+ cp.setCurrentItem(cp.getCount()-1);
+ break;
+ }
+
+ dialog.cancel();
+ }
+ });
+
+ dialog = builder.create();
+ dialog.show();
+
+ return true;
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ Log.d(TAG,
+ "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java
new file mode 100644
index 0000000..a81261d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java
@@ -0,0 +1,38 @@
+package org.fox.ttcomics;
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouch;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class ViewPager extends android.support.v4.view.ViewPager {
+ public ViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
+ if (v instanceof ImageViewTouch) {
+ ImageViewTouch ivt = (ImageViewTouch) v;
+ try {
+ return ivt.canScroll(dx);
+ } catch (NullPointerException e) {
+ // bad image, etc
+ return false;
+ }
+ } else {
+ return super.canScroll(v, checkV, dx, x, y);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return super.onInterceptTouchEvent(event);
+ }
+} \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/anim/appear.xml b/org.fox.ttcomics/src/main/res/anim/appear.xml
new file mode 100644
index 0000000..a60e055
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/anim/appear.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
+ <alpha
+ android:fromAlpha="0"
+ android:toAlpha="1"
+ android:duration="150"
+ />
+</set>
+
diff --git a/org.fox.ttcomics/src/main/res/anim/layout_comics.xml b/org.fox.ttcomics/src/main/res/anim/layout_comics.xml
new file mode 100644
index 0000000..93a58f1
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/anim/layout_comics.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
+ android:delay="20%"
+ android:animation="@anim/appear"
+/>
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/badimage.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/badimage.png
new file mode 100644
index 0000000..bc71802
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/badimage.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_good.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_good.png
new file mode 100644
index 0000000..594ee12
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_good.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_overflow.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_overflow.png
new file mode 100644
index 0000000..002fc4b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_action_overflow.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_launcher.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..04b4741
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_refresh_light.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_refresh_light.png
new file mode 100644
index 0000000..bb9d855
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_refresh_light.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_search_light.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_search_light.png
new file mode 100644
index 0000000..f12e005
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_search_light.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_settings.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_settings.png
new file mode 100644
index 0000000..3e4580e
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_settings.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_share_light.png b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_share_light.png
new file mode 100644
index 0000000..c329f58
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-hdpi/ic_share_light.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_good.png b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_good.png
new file mode 100644
index 0000000..49b85ca
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_good.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_overflow.png b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_overflow.png
new file mode 100644
index 0000000..cfe1287
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_action_overflow.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_launcher.png b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..3447f91
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_refresh_light.png b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_refresh_light.png
new file mode 100644
index 0000000..a7fdc0d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_refresh_light.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_search_light.png b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_search_light.png
new file mode 100644
index 0000000..3549f84
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_search_light.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_settings.png b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_settings.png
new file mode 100644
index 0000000..09b0148
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_settings.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_share_light.png b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_share_light.png
new file mode 100644
index 0000000..15549b0
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xhdpi/ic_share_light.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable-xxhdpi/ic_launcher.png b/org.fox.ttcomics/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..c2557e0
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/org.fox.ttcomics/src/main/res/drawable/comic_tile.xml b/org.fox.ttcomics/src/main/res/drawable/comic_tile.xml
new file mode 100644
index 0000000..428e246
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable/comic_tile.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item>
+ <shape android:shape="rectangle" >
+ <solid android:color="#33b5e5" />
+ <corners android:radius="2dp"/>
+ </shape>
+ </item>
+
+ <!-- <item>
+ <shape android:shape="rectangle" >
+ <solid android:color="#e0e0e0" />
+ <corners android:radius="2dp"/>
+ </shape>
+ </item> -->
+
+ <item
+ android:bottom="2dp">
+ <shape android:shape="rectangle" >
+ <solid android:color="#ffffff" />
+ <corners android:radius="2dp"/>
+ </shape>
+ </item>
+
+</layer-list> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/drawable/comic_tile_folder.xml b/org.fox.ttcomics/src/main/res/drawable/comic_tile_folder.xml
new file mode 100644
index 0000000..2cd13b5
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable/comic_tile_folder.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item>
+ <shape android:shape="rectangle" >
+ <solid android:color="#99cc00" />
+ <corners android:radius="2dp"/>
+ </shape>
+ </item>
+
+ <!-- <item>
+ <shape android:shape="rectangle" >
+ <solid android:color="#e0e0e0" />
+ <corners android:radius="2dp"/>
+ </shape>
+ </item> -->
+
+ <item
+ android:bottom="2dp">
+ <shape android:shape="rectangle" >
+ <solid android:color="#ffffff" />
+ <corners android:radius="2dp"/>
+ </shape>
+ </item>
+
+</layer-list> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/drawable/s_badimage.svg b/org.fox.ttcomics/src/main/res/drawable/s_badimage.svg
new file mode 100644
index 0000000..8c61713
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable/s_badimage.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64px"
+ height="64px"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.0 r9654"
+ inkscape:export-filename="C:\Users\fox\workspace\org.fox.ttcomics\res\drawable\badimage.png"
+ inkscape:export-xdpi="800"
+ inkscape:export-ydpi="800"
+ sodipodi:docname="s_badimage.svg">
+ <defs
+ id="defs2987" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.5"
+ inkscape:cx="3.6363636"
+ inkscape:cy="32"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="1600"
+ inkscape:window-height="1138"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="star"
+ style="opacity:0.25000000000000000;fill:#909090;fill-opacity:1;stroke:none"
+ id="path2999"
+ sodipodi:sides="3"
+ sodipodi:cx="33.090908"
+ sodipodi:cy="8.363636"
+ sodipodi:r1="33.989788"
+ sodipodi:r2="16.994896"
+ sodipodi:arg1="2.6179939"
+ sodipodi:arg2="3.6651914"
+ inkscape:flatsided="true"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="M 3.6548878,25.358529 33.090909,-25.626152 62.526928,25.358531 z"
+ inkscape:transform-center-y="-8.4974474"
+ transform="translate(-1.0909077,32.133811)" />
+ <text
+ xml:space="preserve"
+ style="font-size:23.69292450000000000px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+ x="21.763916"
+ y="50.357021"
+ id="text2993"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan2995"
+ x="21.763916"
+ y="50.357021"
+ style="font-size:33.17009354000000300px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial Bold">?</tspan></text>
+ </g>
+</svg>
diff --git a/org.fox.ttcomics/src/main/res/drawable/s_launcher.svg b/org.fox.ttcomics/src/main/res/drawable/s_launcher.svg
new file mode 100644
index 0000000..9af7a1b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/drawable/s_launcher.svg
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="96"
+ height="96"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="s_launcher.svg"
+ inkscape:export-filename="C:\Users\Andrew\workspace\Tiny-Comics-Reader\res\drawable-xxhdpi\ic_launcher.png"
+ inkscape:export-xdpi="135"
+ inkscape:export-ydpi="135">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3799">
+ <stop
+ style="stop-color:#ddc26e;stop-opacity:1;"
+ offset="0"
+ id="stop3801" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop3803" />
+ </linearGradient>
+ <filter
+ id="filter3007"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur3009"
+ in="SourceAlpha"
+ stdDeviation="2"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix3011"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.75 0 " />
+ <feOffset
+ id="feOffset3013"
+ in="bluralpha"
+ dx="1"
+ dy="1"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge3015">
+ <feMergeNode
+ id="feMergeNode3017"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode3019"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter4167"
+ inkscape:label="Colorize"
+ inkscape:menu="Color"
+ inkscape:menu-tooltip="Blend image or object with a flood color and set lightness and contrast"
+ x="0"
+ y="0"
+ width="1"
+ height="1"
+ color-interpolation-filters="sRGB">
+ <feColorMatrix
+ id="feColorMatrix4169"
+ values="1"
+ in="SourceGraphic"
+ type="saturate"
+ result="result2" />
+ <feFlood
+ id="feFlood4171"
+ flood-color="rgb(254,102,0)"
+ flood-opacity="1"
+ result="result1" />
+ <feBlend
+ id="feBlend4173"
+ in2="result2"
+ mode="multiply"
+ in="result1"
+ result="result3" />
+ <feComposite
+ id="feComposite4175"
+ in2="SourceGraphic"
+ operator="in"
+ k2="1"
+ result="result4" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#000000"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6"
+ inkscape:cx="28.826177"
+ inkscape:cy="53.814876"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1137"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:snap-nodes="false"
+ inkscape:snap-global="false">
+ <sodipodi:guide
+ orientation="1,0"
+ position="7.5761441,54.043161"
+ id="guide2993" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="54.548238,88.13581"
+ id="guide2995" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="88.388348,62.881996"
+ id="guide2997" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="59.599,8.0812204"
+ id="guide2999" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-956.36218)">
+ <path
+ sodipodi:type="star"
+ style="fill:#ff6600;fill-opacity:1;stroke:#000000;stroke-width:1.12887480999999990;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path3031"
+ sodipodi:sides="9"
+ sodipodi:cx="27.779196"
+ sodipodi:cy="34.633232"
+ sodipodi:r1="50.953285"
+ sodipodi:r2="29.552904"
+ sodipodi:arg1="0.49024887"
+ sodipodi:arg2="0.83931472"
+ inkscape:flatsided="false"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="M 72.730984,58.624355 47.519737,56.626076 46.793067,81.905951 28.7646,64.169703 11.958348,83.068117 9.5483813,57.892887 -15.47362,61.567061 -1.1374363,40.73262 -22.667116,27.463368 1.7071596,20.718381 -6.2562209,-3.2854663 16.751151,7.2150562 26.080224,-16.29172 36.955287,6.5410003 59.211636,-5.4696229 52.865828,19.011612 77.63546,24.117046 57.038055,38.791753 z"
+ transform="matrix(0.85565363,-0.2292717,0.2292717,0.85565363,16.712595,980.15403)" />
+ <path
+ sodipodi:type="star"
+ style="fill:#d40000;fill-opacity:1;stroke:#000000;stroke-width:1.61221886;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path3031-4"
+ sodipodi:sides="9"
+ sodipodi:cx="27.779196"
+ sodipodi:cy="34.633232"
+ sodipodi:r1="50.953285"
+ sodipodi:r2="29.552904"
+ sodipodi:arg1="0.49024887"
+ sodipodi:arg2="0.83931472"
+ inkscape:flatsided="false"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="M 72.730984,58.624355 47.519737,56.626076 46.793067,81.905951 28.7646,64.169703 11.958348,83.068117 9.5483813,57.892887 -15.47362,61.567061 -1.1374363,40.73262 -22.667116,27.463368 1.7071596,20.718381 -6.2562209,-3.2854663 16.751151,7.2150562 26.080224,-16.29172 36.955287,6.5410003 59.211636,-5.4696229 52.865828,19.011612 77.63546,24.117046 57.038055,38.791753 z"
+ transform="matrix(0.58890595,-0.15779687,0.15779687,0.58890595,26.466358,987.70085)" />
+ <text
+ xml:space="preserve"
+ style="font-size:36.53679657000000000px;font-style:normal;font-weight:normal;line-height:126.99999808999999000%;letter-spacing:0px;word-spacing:0px;fill:#ffe680;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans"
+ x="-128.74614"
+ y="1014.6334"
+ id="text3801"
+ sodipodi:linespacing="127%"
+ transform="matrix(0.99123282,-0.13212683,0.13212683,0.99123282,0,0)"><tspan
+ sodipodi:role="line"
+ id="tspan3803"
+ x="-128.74614"
+ y="1014.6334"
+ style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:126.99999808999999000%;fill:#ffe680;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:BD Cartoon Shout;-inkscape-font-specification:BD Cartoon Shout">POW!</tspan></text>
+ </g>
+</svg>
diff --git a/org.fox.ttcomics/src/main/res/layout-land/fragment_comics_list.xml b/org.fox.ttcomics/src/main/res/layout-land/fragment_comics_list.xml
new file mode 100644
index 0000000..9752e65
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout-land/fragment_comics_list.xml
@@ -0,0 +1,29 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout4"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <GridView
+ android:id="@+id/comics_grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:numColumns="3" >
+ </GridView>
+
+ <LinearLayout
+ android:id="@+id/no_comics"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/error_no_comic_arhives_found_" />
+ </LinearLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout-sw600dp/activity_main.xml b/org.fox.ttcomics/src/main/res/layout-sw600dp/activity_main.xml
new file mode 100644
index 0000000..81b302b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,22 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+
+ <FrameLayout
+ android:id="@+id/comics_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/tablet_layout_hack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:text="" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout-sw600dp/activity_view_comic.xml b/org.fox.ttcomics/src/main/res/layout-sw600dp/activity_view_comic.xml
new file mode 100644
index 0000000..e6479f6
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout-sw600dp/activity_view_comic.xml
@@ -0,0 +1,23 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout2"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+
+ <FrameLayout
+ android:id="@+id/comics_pager_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ </FrameLayout>
+
+
+ <TextView
+ android:id="@+id/tablet_layout_hack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:text="" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout-sw600dp/comics_grid_row.xml b/org.fox.ttcomics/src/main/res/layout-sw600dp/comics_grid_row.xml
new file mode 100644
index 0000000..8392a17
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout-sw600dp/comics_grid_row.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/comics_list_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <FrameLayout
+ android:id="@+id/imageholder"
+ android:layout_width="160dp"
+ android:layout_height="160dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="0"
+ android:background="@drawable/comic_tile"
+ android:padding="6dp" >
+
+ <ImageView
+ android:id="@+id/thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="4dp"
+ android:scaleType="centerInside"
+ android:src="@drawable/ic_launcher" />
+
+ <ImageView
+ android:id="@+id/comic_overflow"
+ android:layout_gravity="bottom|right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:paddingBottom="4dp"
+ android:src="@drawable/ic_action_overflow" />
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/linearLayout4"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:gravity="center_horizontal"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/file_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:text="Comic_1980.cbz" />
+
+ <ProgressBar
+ android:id="@+id/file_progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="128dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/file_progress_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:gravity="right"
+ android:text="12 of 325"
+ android:textSize="12dp" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout-sw600dp/fragment_comics_list.xml b/org.fox.ttcomics/src/main/res/layout-sw600dp/fragment_comics_list.xml
new file mode 100644
index 0000000..eaf8c90
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout-sw600dp/fragment_comics_list.xml
@@ -0,0 +1,35 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout4"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+
+ <GridView
+ android:id="@+id/comics_grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:numColumns="3" >
+
+ </GridView>
+
+ <LinearLayout
+ android:id="@+id/no_comics"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:orientation="vertical" >
+
+
+
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/error_no_comic_arhives_found_" />
+
+ </LinearLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/activity_main.xml b/org.fox.ttcomics/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..49a8732
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/activity_main.xml
@@ -0,0 +1,15 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout1"
+ android:fitsSystemWindows="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <FrameLayout
+ android:id="@+id/comics_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ </FrameLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/activity_view_comic.xml b/org.fox.ttcomics/src/main/res/layout/activity_view_comic.xml
new file mode 100644
index 0000000..2cc0dd3
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/activity_view_comic.xml
@@ -0,0 +1,16 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout2"
+ android:fitsSystemWindows="true"
+ android:background="?attr/viewComicsBackground"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <FrameLayout
+ android:id="@+id/comics_pager_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ </FrameLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/chooser_list.xml b/org.fox.ttcomics/src/main/res/layout/chooser_list.xml
new file mode 100644
index 0000000..f4b4a12
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/chooser_list.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:id="@+id/linearLayout2"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <Button
+ android:id="@+id/btnParent"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="@string/open_parent" />
+
+ <Button
+ android:id="@+id/btnChoose"
+ android:layout_weight="1"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/choose" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/comics_grid_row.xml b/org.fox.ttcomics/src/main/res/layout/comics_grid_row.xml
new file mode 100644
index 0000000..f021a3f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/comics_grid_row.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/comics_list_row"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <FrameLayout
+ android:id="@+id/imageholder"
+ android:layout_width="160dp"
+ android:layout_height="160dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="0"
+ android:background="@drawable/comic_tile"
+ android:padding="6dp" >
+
+ <ImageView
+ android:id="@+id/thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerInside"
+ android:padding="4dp"
+ android:src="@drawable/ic_launcher" />
+
+ <ImageView
+ android:id="@+id/comic_overflow"
+ android:layout_gravity="bottom|right"
+ android:paddingBottom="4dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:src="@drawable/ic_action_overflow" />
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/linearLayout4"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/file_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:text="Comic_1980.cbz"
+ android:textSize="11dp" />
+
+ <ProgressBar
+ android:id="@+id/file_progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="96dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/file_progress_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:gravity="right"
+ android:text="12 of 325"
+ android:textSize="8dp" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/comics_list_row.xml b/org.fox.ttcomics/src/main/res/layout/comics_list_row.xml
new file mode 100644
index 0000000..5d529f8
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/comics_list_row.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/comics_list_row"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:padding="4dp" >
+
+ <FrameLayout
+ android:id="@+id/imageholder"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_weight="0"
+ android:background="@drawable/comic_tile"
+ android:padding="2dp" >
+
+ <ImageView
+ android:id="@+id/thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/white"
+ android:padding="2dp"
+ android:scaleType="centerInside"
+ android:src="@drawable/ic_launcher" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/linearLayout5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingLeft="4dp" >
+
+ <LinearLayout
+ android:id="@+id/linearLayout4"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/file_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:text="Comic_1980.cbz" />
+
+ <TextView
+ android:id="@+id/file_progress_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:gravity="right"
+ android:text="12 of 325" />
+ </LinearLayout>
+
+ <ProgressBar
+ android:id="@+id/file_progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/comic_overflow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:src="@drawable/ic_action_overflow" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/dialog_location.xml b/org.fox.ttcomics/src/main/res/layout/dialog_location.xml
new file mode 100644
index 0000000..cabbf9a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/dialog_location.xml
@@ -0,0 +1,14 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/linearLayout1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+
+
+ <NumberPicker
+ android:id="@+id/number_picker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/dialog_location_compat.xml b/org.fox.ttcomics/src/main/res/layout/dialog_location_compat.xml
new file mode 100644
index 0000000..385b8d5
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/dialog_location_compat.xml
@@ -0,0 +1,23 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/linearLayout1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+
+ <SeekBar
+ android:id="@+id/number_seeker"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+
+
+ <TextView
+ android:id="@+id/page_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="1 of 365" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/fragment_comic.xml b/org.fox.ttcomics/src/main/res/layout/fragment_comic.xml
new file mode 100644
index 0000000..cfc54b3
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/fragment_comic.xml
@@ -0,0 +1,21 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout3"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <it.sephiroth.android.library.imagezoom.ImageViewTouch
+ android:id="@+id/comic_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <TextView
+ android:id="@+id/comic_page"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_margin="4dp"
+ android:alpha="0.25"
+ android:text="25" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/fragment_comics_list.xml b/org.fox.ttcomics/src/main/res/layout/fragment_comics_list.xml
new file mode 100644
index 0000000..2461c15
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/fragment_comics_list.xml
@@ -0,0 +1,36 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/FrameLayout4"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <android.support.v4.widget.SwipeRefreshLayout
+ android:id="@+id/comics_swipe_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <ListView
+ android:id="@+id/comics_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layoutAnimation="@anim/layout_comics" >
+ </ListView>
+ </android.support.v4.widget.SwipeRefreshLayout>
+
+ <LinearLayout
+ android:id="@+id/no_comics"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/error_no_comic_arhives_found_" />
+ </LinearLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/fragment_comics_pager.xml b/org.fox.ttcomics/src/main/res/layout/fragment_comics_pager.xml
new file mode 100644
index 0000000..9d98d34
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/fragment_comics_pager.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<org.fox.ttcomics.ViewPager
+xmlns:android="http://schemas.android.com/apk/res/android"
+android:layout_width="fill_parent"
+android:layout_height="fill_parent"
+android:id="@+id/comics_pager">
+
+</org.fox.ttcomics.ViewPager> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/layout/list_item.xml b/org.fox.ttcomics/src/main/res/layout/list_item.xml
new file mode 100644
index 0000000..1fb2f8c
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/layout/list_item.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp"
+ android:textSize="16sp" >
+</TextView> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/menu/activity_main.xml b/org.fox.ttcomics/src/main/res/menu/activity_main.xml
new file mode 100644
index 0000000..bb07f5b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/menu/activity_main.xml
@@ -0,0 +1,19 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:ugh="http://schemas.android.com/apk/res-auto">
+ <item android:id="@+id/menu_donate"
+ android:title="@string/menu_donate"
+ ugh:showAsAction="ifRoom"
+ android:icon="@drawable/ic_action_good"
+ />
+
+ <item android:id="@+id/menu_rescan"
+ android:title="@string/menu_rescan"
+ android:icon="@drawable/ic_refresh_light"
+ ugh:showAsAction="ifRoom" />
+
+ <item android:id="@+id/menu_settings"
+ android:title="@string/menu_settings"
+ android:icon="@drawable/ic_settings"
+ android:orderInCategory="100"
+ />
+</menu>
diff --git a/org.fox.ttcomics/src/main/res/menu/activity_view_comic.xml b/org.fox.ttcomics/src/main/res/menu/activity_view_comic.xml
new file mode 100644
index 0000000..45d0473
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/menu/activity_view_comic.xml
@@ -0,0 +1,26 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:ugh="http://schemas.android.com/apk/res-auto">
+ <item android:id="@+id/menu_settings"
+ android:title="@string/menu_settings"
+ android:icon="@drawable/ic_settings"
+ android:orderInCategory="100"
+ ugh:showAsAction="never" />
+
+ <item android:id="@+id/menu_share"
+ android:title="@string/menu_share"
+ android:icon="@drawable/ic_share_light"
+ ugh:showAsAction="ifRoom" />
+
+ <item android:id="@+id/menu_go_location"
+ android:title="@string/menu_go_location"
+ ugh:showAsAction="never" />
+
+ <item android:id="@+id/menu_sync_location"
+ android:title="@string/menu_sync_location"
+ ugh:showAsAction="never" />
+
+ <item android:id="@+id/menu_toggle_orientation_lock"
+ android:title="@string/menu_toggle_orientation_lock"
+ ugh:showAsAction="never" />
+
+</menu>
diff --git a/org.fox.ttcomics/src/main/res/menu/comic_archive_context.xml b/org.fox.ttcomics/src/main/res/menu/comic_archive_context.xml
new file mode 100644
index 0000000..9a39898
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/menu/comic_archive_context.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_mark_as_read"
+ android:title="Mark as read" />
+
+ <item android:id="@+id/menu_reset_progress"
+ android:title="Reset progress" />
+
+</menu>
diff --git a/org.fox.ttcomics/src/main/res/values-large/dimens.xml b/org.fox.ttcomics/src/main/res/values-large/dimens.xml
new file mode 100644
index 0000000..1ae597c
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/values-large/dimens.xml
@@ -0,0 +1,7 @@
+<resources>
+
+ <dimen name="padding_small">8dp</dimen>
+ <dimen name="padding_medium">16dp</dimen>
+ <dimen name="padding_large">16dp</dimen>
+
+</resources> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/values-v19/style.xml b/org.fox.ttcomics/src/main/res/values-v19/style.xml
new file mode 100644
index 0000000..0a821cb
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/values-v19/style.xml
@@ -0,0 +1,31 @@
+<resources>
+
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="android:windowTranslucentNavigation">false</item>
+ <item name="statusBarHintColor">#222222</item>
+ <item name="android:actionBarStyle">@style/ActionBar.Light</item>
+ </style>
+
+ <style name="ActionBar.Light" parent="Widget.AppCompat.ActionBar.Solid">
+ <item name="android:background">#222222</item>
+ <item name="android:titleTextStyle">@style/ActionBarText.Light</item>
+ </style>
+
+ <style name="ActionBarText.Light" parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="ViewLightTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <item name="viewComicsBackground">@android:color/transparent</item>
+
+ <item name="statusBarHintColor">#222222</item>
+ <item name="android:actionBarStyle">@style/ActionBar.Light</item>
+ </style>
+
+ <style name="ViewDarkTheme" parent="Theme.AppCompat">
+ <item name="viewComicsBackground">@android:color/black</item>
+ <item name="statusBarHintColor">@android:color/black</item>
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/values/attrs.xml b/org.fox.ttcomics/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..9a693d4
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/values/attrs.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="viewComicsBackground" format="reference|color" />
+ <attr name="statusBarHintColor" format="reference|color" />
+</resources> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/values/dimens.xml b/org.fox.ttcomics/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..1dc0322
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/values/dimens.xml
@@ -0,0 +1,7 @@
+<resources>
+
+ <dimen name="padding_small">8dp</dimen>
+ <dimen name="padding_medium">8dp</dimen>
+ <dimen name="padding_large">16dp</dimen>
+
+</resources> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/values/strings.xml b/org.fox.ttcomics/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7ef7de2
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/values/strings.xml
@@ -0,0 +1,67 @@
+<resources>
+
+ <string name="app_name">Pow! Comics Reader</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="menu_settings">Settings</string>
+ <string name="menu_go_location">Go to</string>
+ <string name="title_activity_main">Comics Reader</string>
+ <string name="title_activity_preferences">Settings</string>
+ <string name="prefs_comics_directory">Comics directory</string>
+ <string name="prefs_general">General</string>
+ <string name="prefs_dim_status_bar">Dim status bar</string>
+ <string name="file_progress_info">%1$d of %2$d (%3$d%%)</string>
+ <string name="menu_rescan">Reload</string>
+ <string name="tab_all_comics">All comics</string>
+ <string name="tab_unread">Unread</string>
+ <string name="tab_read">Finished</string>
+ <string name="dialog_need_prefs_message">Please configure base directory for comics archives.</string>
+ <string name="dialog_need_prefs_preferences">Settings</string>
+ <string name="cancel">Cancel</string>
+ <string name="comics_directory_default">/mnt/sdcard/Comics</string>
+ <string name="prefs_reading">Reading</string>
+ <string name="prefs_dark_theme">Dark theme</string>
+ <string name="dialog_open_location">Open location</string>
+ <string name="dialog_location_beginning">Beginning</string>
+ <string name="dialog_location_furthest">Furthest read location</string>
+ <string name="dialog_location_location">Location…</string>
+ <string name="dialog_location_end">End</string>
+ <string name="tab_unfinished">Unfinished</string>
+ <string name="menu_share">Share</string>
+ <string name="prefs_use_full_screen">Fullscreen mode</string>
+ <string name="error_could_not_prepare_file_for_sharing">Could not prepare file for sharing</string>
+ <string name="error_could_not_open_comic_archive">Could not open comic archive.</string>
+ <string name="error_out_of_memory">Out of memory</string>
+ <string name="error_loading_image">Error loading image</string>
+ <string name="error_could_not_read_folder_contents_">Could not read folder contents.</string>
+ <string name="picker_choose">Choose %1$s</string>
+ <string name="dialog_location_page_number">%1$d of %2$d</string>
+ <string name="sync_server_has_further_page">You are currently on page %1$d. Furthest read page stored on the server is %2$d. Open it instead?</string>
+ <string name="dialog_open_page">Open page</string>
+ <string name="menu_sync_location">Sync to last page read</string>
+ <string name="share_comic">Share comic</string>
+ <string name="choose">Choose</string>
+ <string name="error_no_comic_arhives_found_">No comic arhives found.</string>
+ <string name="prefs_use_position_sync">Sync last read pages</string>
+ <string name="prefs_use_position_sync_summary">Requires at least one Google account on the device. No personally identifiable information is sent.</string>
+ <string name="error_sync_no_account">No Google account found, sync disabled.</string>
+ <string name="sync_uploading">Uploading sync data…</string>
+ <string name="error_sync_no_data">No information stored or you are on the furthest read page.</string>
+ <string name="dialog_clear_data">Clear data</string>
+ <string name="dialog_clear_data_title">Clear all remotely stored sync data?</string>
+ <string name="prefs_sync">Sync</string>
+ <string name="prefs_clear_sync_data">Clear sync data</string>
+ <string name="prefs_clear_sync_data_summary">Removes all remotely stored sync data.</string>
+ <string name="open_parent">Open parent</string>
+ <string name="prefs_enable_rar_archives">Enable RAR archives</string>
+ <string name="prefs_enable_rar_summary">Using RAR (CBR) files is not recommended because of underlying RAR library issues. If you experience crashes, disable this option, and repack your archives as ZIP.</string>
+ <string name="file_unread">Unread (%1$d pages)</string>
+ <string name="file_finished">Finished</string>
+ <string name="menu_toggle_orientation_lock">(Un)lock orientation</string>
+ <string name="prefs_comics_directory_summary">Base directory for comic archives.</string>
+ <string name="prefs_fit_to_width">Fit to width</string>
+ <string name="menu_donate">Donate</string>
+ <string name="sync_running_in_test_mode">Sync running in test mode.</string>
+ <string name="reset_remove_synced_progress">Would you like to remove synced progress too?</string>
+ <string name="error_cant_open_file">Can\'t open file: %1$s</string>
+ <string name="prefs_prevent_screen_sleep">Keep screen awake</string>
+</resources> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/values/style.xml b/org.fox.ttcomics/src/main/res/values/style.xml
new file mode 100644
index 0000000..a62c354
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/values/style.xml
@@ -0,0 +1,15 @@
+<resources>
+
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+
+ </style>
+
+ <style name="ViewLightTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <item name="viewComicsBackground">@android:color/transparent</item>
+ </style>
+
+ <style name="ViewDarkTheme" parent="Theme.AppCompat">
+ <item name="viewComicsBackground">@android:color/black</item>
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/res/xml/preferences.xml b/org.fox.ttcomics/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..ce3b8dd
--- /dev/null
+++ b/org.fox.ttcomics/src/main/res/xml/preferences.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <PreferenceCategory android:title="@string/prefs_general" >
+ <Preference
+ android:key="comics_directory"
+ android:hint="@string/comics_directory_default"
+ android:summary="@string/prefs_comics_directory_summary"
+ android:title="@string/prefs_comics_directory" >
+ </Preference>
+
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="enable_rar"
+ android:title="@string/prefs_enable_rar_archives"
+ android:summary="@string/prefs_enable_rar_summary" />
+
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/prefs_sync" >
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="use_position_sync"
+ android:title="@string/prefs_use_position_sync"
+ android:summary="@string/prefs_use_position_sync_summary"
+ />
+
+ <Preference
+ android:dependency="use_position_sync"
+ android:key="clear_sync_data"
+ android:title="@string/prefs_clear_sync_data"
+ android:summary="@string/prefs_clear_sync_data_summary" >
+ </Preference>
+
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/prefs_reading" android:key="prefs_reading">
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="use_dark_theme"
+ android:title="@string/prefs_dark_theme" />
+
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="dim_status_bar"
+ android:title="@string/prefs_dim_status_bar" />
+
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="use_full_screen"
+ android:title="@string/prefs_use_full_screen" />
+
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="fit_to_width"
+ android:title="@string/prefs_fit_to_width" />
+
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="prevent_screen_sleep"
+ android:title="@string/prefs_prevent_screen_sleep" />
+
+ </PreferenceCategory>
+</PreferenceScreen> \ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..e0e41b0
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':org.fox.ttcomics'