Initial
This commit is contained in:
commit
bae38c0001
83
build.xml
Normal file
83
build.xml
Normal file
@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<project name="SieFM" default="jar" basedir=".">
|
||||
<description>Builds, tests, and runs the project .</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
pre-init: called before initialization of project properties
|
||||
post-init: called after initialization of project properties
|
||||
pre-preprocess: called before text preprocessing of sources
|
||||
post-preprocess: called after text preprocessing of sources
|
||||
pre-compile: called before source compilation
|
||||
post-compile: called after source compilation
|
||||
pre-obfuscate: called before obfuscation
|
||||
post-obfuscate: called after obfuscation
|
||||
pre-preverify: called before preverification
|
||||
post-preverify: called after preverification
|
||||
pre-jar: called before jar building
|
||||
post-jar: called after jar building
|
||||
pre-build: called before final distribution building
|
||||
post-build: called after final distribution building
|
||||
pre-clean: called before cleaning build products
|
||||
post-clean: called after cleaning build products
|
||||
|
||||
Example of pluging a my-special-task after the compilation could look like
|
||||
|
||||
<target name="post-compile">
|
||||
<my-special-task>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</my-special-task>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
Other way how to customize the build is by overriding existing main targets.
|
||||
The target of interest are:
|
||||
|
||||
preprocess: preprocessing
|
||||
extract-libs: extraction of libraries and resources
|
||||
compile: compilation
|
||||
create-jad: construction of jad and jar manifest source
|
||||
obfuscate: obfuscation
|
||||
preverify: preverification
|
||||
jar: jar archive building
|
||||
run: execution
|
||||
debug: execution in debug mode
|
||||
build: building of the final distribution
|
||||
javadoc: javadoc generation
|
||||
|
||||
Example of overriding the target for project execution could look like
|
||||
|
||||
<target name="run" depends="init,jar">
|
||||
<my-special-exec jadfile="${dist.dir}/${dist.jad}"/>
|
||||
</target>
|
||||
|
||||
Be careful about correct dependencies when overriding original target.
|
||||
Again, for list of available properties which you can use check the target
|
||||
you are overriding in nbproject/build-impl.xml file.
|
||||
|
||||
A special target for-all-configs can be used to run some specific targets for
|
||||
all project configurations in a sequence. File nbproject/build-impl.xml
|
||||
already contains some "for-all" targets:
|
||||
|
||||
jar-all
|
||||
javadoc-all
|
||||
clean-all
|
||||
|
||||
Example of definition of target iterating over all project configurations:
|
||||
|
||||
<target name="jar-all">
|
||||
<property name="target.to.call" value="jar"/>
|
||||
<antcall target="for-all-configs"/>
|
||||
</target>
|
||||
|
||||
-->
|
||||
</project>
|
1368
nbproject/build-impl.xml
Normal file
1368
nbproject/build-impl.xml
Normal file
File diff suppressed because it is too large
Load Diff
8
nbproject/genfiles.properties
Normal file
8
nbproject/genfiles.properties
Normal file
@ -0,0 +1,8 @@
|
||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||
build.xml.data.CRC32=535663c6
|
||||
build.xml.script.CRC32=94c8884a
|
||||
build.xml.stylesheet.CRC32=03eab09b
|
||||
nbproject/build-impl.xml.data.CRC32=535663c6
|
||||
nbproject/build-impl.xml.script.CRC32=0915c3e7
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=d670562e
|
7
nbproject/private/private.properties
Normal file
7
nbproject/private/private.properties
Normal file
@ -0,0 +1,7 @@
|
||||
#Tue Dec 01 01:16:58 EET 2009
|
||||
netbeans.user=C\:\\Users\\aNNiMON\\.netbeans\\6.7
|
||||
javadoc.preview=true
|
||||
config.active=
|
||||
deployment.counter=8
|
||||
app-version.autoincrement=true
|
||||
deployment.number=0.0.7
|
4
nbproject/private/private.xml
Normal file
4
nbproject/private/private.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
|
||||
</project-private>
|
140
nbproject/project.properties
Normal file
140
nbproject/project.properties
Normal file
@ -0,0 +1,140 @@
|
||||
abilities=MMAPI=1.1,SATSAJCRMI=1.0,SATSACRYPTO=1.0,JSR82=1.1,NOKIAUI=1.0,JSR226=1.0,MIDP=2.1,JSR229=1.1.0,SATSAAPDU=1.0,CLDC=1.1,JSR177=1.0,JSR179=1.0.1,J2MEWS=1.0,VSCL=2.1,WMA=2.0,JSR172=1.0,SEMC_EXT_JP8=1.0,ColorScreen,OBEX=1.0,NokiaUI=1.0,JSR238=1.0,JSR239=1.0,JSR211=1.0,JSR234=1.0,ScreenWidth=240,lib/semc_ext_jp8.jar=1.0,MascotV3=1.0,JSR75=1.0,JSR184=1.1,SATSAPKI=1.0,ScreenHeight=321,ScreenColorDepth=8,JSR180=1.0.1,J2MEXMLRPC=1.0,
|
||||
all.configurations=\
|
||||
application.args=
|
||||
application.description=
|
||||
application.description.detail=
|
||||
application.name=
|
||||
application.vendor=Vendor
|
||||
build.classes.dir=${build.dir}/compiled
|
||||
build.classes.excludes=**/*.java,**/*.form,**/*.class,**/.nbintdb,**/*.mvd,**/*.wsclient,**/*.vmd
|
||||
build.dir=build/${config.active}
|
||||
build.root.dir=build
|
||||
debug.level=debug
|
||||
deployment.copy.target=deploy
|
||||
deployment.instance=default
|
||||
deployment.jarurl=${dist.jar}
|
||||
deployment.method=NONE
|
||||
deployment.override.jarurl=false
|
||||
dist.dir=dist/${config.active}
|
||||
dist.jad=SieFM.jad
|
||||
dist.jar=SieFM.jar
|
||||
dist.javadoc.dir=${dist.dir}/doc
|
||||
dist.root.dir=dist
|
||||
extra.classpath=
|
||||
filter.exclude.tests=false
|
||||
filter.excludes=
|
||||
filter.more.excludes=**/overview.html,**/package.html
|
||||
filter.use.standard=true
|
||||
jar.compress=true
|
||||
javac.debug=true
|
||||
javac.deprecation=false
|
||||
javac.encoding=windows-1251
|
||||
javac.optimize=false
|
||||
javac.source=1.3
|
||||
javac.target=1.3
|
||||
javadoc.author=false
|
||||
javadoc.encoding=
|
||||
javadoc.noindex=false
|
||||
javadoc.nonavbar=false
|
||||
javadoc.notree=false
|
||||
javadoc.private=false
|
||||
javadoc.splitindex=true
|
||||
javadoc.use=true
|
||||
javadoc.version=false
|
||||
javadoc.windowtitle=
|
||||
libs.classpath=
|
||||
main.class=
|
||||
main.class.class=applet
|
||||
manifest.apipermissions=
|
||||
manifest.file=manifest.mf
|
||||
manifest.jad=
|
||||
manifest.manifest=
|
||||
manifest.midlets=MIDlet-1: SieFM,/img/icon32.png,ftpmid.FtpMid\n
|
||||
manifest.others=MIDlet-Vendor: SilentKnight, VMX, DiHLoS\nMIDlet-Name: SieFM\nMIDlet-Version: 3.41\n
|
||||
manifest.pushregistry=
|
||||
name=SieFM
|
||||
no.dependencies=false
|
||||
nokiaS80.application.icon=
|
||||
nsicom.application.monitorhost=
|
||||
nsicom.application.runremote=
|
||||
nsicom.application.runverbose=
|
||||
nsicom.remoteapp.location=\\My Documents\\NetBeans Applications
|
||||
nsicom.remotevm.location=\\Windows\\creme\\bin\\CrEme.exe
|
||||
obfuscated.classes.dir=${build.dir}/obfuscated
|
||||
obfuscation.custom=-keep public class * extends javax.microedition.midlet.MIDlet \n-dontusemixedcaseclassnames
|
||||
obfuscation.level=1
|
||||
obfuscator.destjar=${build.dir}/obfuscated.jar
|
||||
obfuscator.srcjar=${build.dir}/before-obfuscation.jar
|
||||
platform.active=Sony_Ericsson_SDK_2_5_0_4_for_the_Java_TM__ME_Platform_Emulator_
|
||||
platform.active.description=Sony Ericsson SDK 2.5.0.4 for the Java(TM) ME Platform(Emulator)
|
||||
platform.apis=JSR234-1.0,SATSA-APDU-1.0,JSR211-1.0,JSR75-1.0,J2ME-WS-1.0,J2ME-XMLRPC-1.0,JSR82-1.1,SATSA-JCRMI-1.0,SATSA-CRYPTO-1.0,JSR239-1.0,JSR179-1.0.1,MascotV3-1.0,JSR184-1.1,JSR238-1.0,MMAPI-1.1,NokiaUI-1.0,JSR229-1.1.0,SATSA-PKI-1.0,JSR180-1.0.1,JSR226-1.0,JSR177-1.0,SEMC_EXT_JP8-1.0,VSCL-2.0,VSCL-2.1,WMA-2.0,lib/semc_ext_jp8.jar
|
||||
platform.bootclasspath=${platform.home}/lib/mascotv3.jar:${platform.home}/lib/jsr226.jar:${platform.home}/lib/jsr256.jar:${platform.home}/lib/satsa-crypto.jar:${platform.home}/lib/jsr229.jar:${platform.home}/lib/jsr238.jar:${platform.home}/lib/j2me-xmlrpc.jar:${platform.home}/lib/jsr211.jar:${platform.home}/lib/vscl21.jar:${platform.home}/lib/satsa-jcrmi.jar:${platform.home}/lib/jsr082.jar:${platform.home}/lib/satsa-apdu.jar:${platform.home}/lib/jsr184.jar:${platform.home}/lib/nokiaext.jar:${platform.home}/lib/jsr239.jar:${platform.home}/lib/jsr75.jar:${platform.home}/lib/jsr179.jar:${platform.home}/lib/satsa-pki.jar:${platform.home}/lib/jsr180.jar:${platform.home}/lib/vscl.jar:${platform.home}/lib/mmapi.jar:${platform.home}/lib/j2me-ws.jar:${platform.home}/lib/wma20.jar:${platform.home}/lib/jsr234.jar:${platform.home}/lib/semc_ext_jp8.jar:${platform.home}/lib/cldcapi11.jar:${platform.home}/lib/midpapi20.jar
|
||||
platform.configuration=CLDC-1.1
|
||||
platform.device=SonyEricsson_JP8_240x320_Emu
|
||||
platform.fat.jar=true
|
||||
platform.profile=MIDP-2.0
|
||||
platform.trigger=CLDC
|
||||
platform.type=UEI-1.0.1
|
||||
preprocessed.dir=${build.dir}/preprocessed
|
||||
preverify.classes.dir=${build.dir}/preverified
|
||||
preverify.sources.dir=${build.dir}/preverifysrc
|
||||
resources.dir=resources
|
||||
ricoh.application.email=
|
||||
ricoh.application.fax=
|
||||
ricoh.application.icon=
|
||||
ricoh.application.target-jar=
|
||||
ricoh.application.telephone=
|
||||
ricoh.application.uid=72821905
|
||||
ricoh.application.version=
|
||||
ricoh.dalp.application-desc.auto-run=false
|
||||
ricoh.dalp.application-desc.energy-save=
|
||||
ricoh.dalp.application-desc.exec-auth=
|
||||
ricoh.dalp.application-desc.visible=true
|
||||
ricoh.dalp.argument=
|
||||
ricoh.dalp.codebase=
|
||||
ricoh.dalp.display-mode.color=true
|
||||
ricoh.dalp.display-mode.is-4line-support=false
|
||||
ricoh.dalp.display-mode.is-hvga-support=true
|
||||
ricoh.dalp.display-mode.is-vga-support=false
|
||||
ricoh.dalp.display-mode.is-wvga-support=false
|
||||
ricoh.dalp.information.abbreviation=
|
||||
ricoh.dalp.information.icon.basepath=
|
||||
ricoh.dalp.information.icon.location=
|
||||
ricoh.dalp.information.is-icon-used=true
|
||||
ricoh.dalp.install.destination=hdd
|
||||
ricoh.dalp.install.mode.auto=true
|
||||
ricoh.dalp.install.work-dir=hdd
|
||||
ricoh.dalp.is-managed=true
|
||||
ricoh.dalp.resources.dsdk.version=2.0
|
||||
ricoh.dalp.resources.jar.basepath=
|
||||
ricoh.dalp.resources.jar.version=
|
||||
ricoh.dalp.version=
|
||||
ricoh.icon.invert=false
|
||||
ricoh.platform.target.version=
|
||||
run.cmd.options=
|
||||
run.jvmargs=
|
||||
run.method=STANDARD
|
||||
run.security.domain=trusted
|
||||
run.use.security.domain=false
|
||||
savaje.application.icon=
|
||||
savaje.application.icon.focused=
|
||||
savaje.application.icon.small=
|
||||
savaje.application.uid=TBD
|
||||
savaje.bundle.base=
|
||||
savaje.bundle.debug=false
|
||||
savaje.bundle.debug.port=
|
||||
semc.application.caps=
|
||||
semc.application.icon=
|
||||
semc.application.icon.count=
|
||||
semc.application.icon.splash=
|
||||
semc.application.icon.splash.installonly=false
|
||||
semc.application.uid=E4534009
|
||||
semc.certificate.path=
|
||||
semc.private.key.password=
|
||||
semc.private.key.path=
|
||||
sign.alias=
|
||||
sign.enabled=false
|
||||
sign.keystore=
|
||||
src.dir=src
|
||||
use.emptyapis=true
|
||||
use.preprocessor=true
|
10
nbproject/project.xml
Normal file
10
nbproject/project.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.kjava.j2meproject</type>
|
||||
<configuration>
|
||||
<data xmlns="http://www.netbeans.org/ns/j2me-project">
|
||||
<name>SieFM</name>
|
||||
<minimum-ant-version>1.6</minimum-ant-version>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
206
src/com/classpath/zip/Adler32.java
Normal file
206
src/com/classpath/zip/Adler32.java
Normal file
@ -0,0 +1,206 @@
|
||||
/* Adler32.java - Computes Adler32 data checksum of a data stream
|
||||
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/*
|
||||
* Written using on-line Java Platform 1.2 API Specification, as well
|
||||
* as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
|
||||
* The actual Adler32 algorithm is taken from RFC 1950.
|
||||
* Status: Believed complete and correct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Computes Adler32 checksum for a stream of data. An Adler32
|
||||
* checksum is not as reliable as a CRC32 checksum, but a lot faster to
|
||||
* compute.
|
||||
*<p>
|
||||
* The specification for Adler32 may be found in RFC 1950.
|
||||
* (ZLIB Compressed Data Format Specification version 3.3)
|
||||
*<p>
|
||||
*<p>
|
||||
* From that document:
|
||||
*<p>
|
||||
* "ADLER32 (Adler-32 checksum)
|
||||
* This contains a checksum value of the uncompressed data
|
||||
* (excluding any dictionary data) computed according to Adler-32
|
||||
* algorithm. This algorithm is a 32-bit extension and improvement
|
||||
* of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
|
||||
* standard.
|
||||
*<p>
|
||||
* Adler-32 is composed of two sums accumulated per byte: s1 is
|
||||
* the sum of all bytes, s2 is the sum of all s1 values. Both sums
|
||||
* are done modulo 65521. s1 is initialized to 1, s2 to zero. The
|
||||
* Adler-32 checksum is stored as s2*65536 + s1 in most-
|
||||
* significant-byte first (network) order."
|
||||
*<p>
|
||||
* "8.2. The Adler-32 algorithm
|
||||
*<p>
|
||||
* The Adler-32 algorithm is much faster than the CRC32 algorithm yet
|
||||
* still provides an extremely low probability of undetected errors.
|
||||
*<p>
|
||||
* The modulo on unsigned long accumulators can be delayed for 5552
|
||||
* bytes, so the modulo operation time is negligible. If the bytes
|
||||
* are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
|
||||
* and order sensitive, unlike the first sum, which is just a
|
||||
* checksum. That 65521 is prime is important to avoid a possible
|
||||
* large class of two-byte errors that leave the check unchanged.
|
||||
* (The Fletcher checksum uses 255, which is not prime and which also
|
||||
* makes the Fletcher check insensitive to single byte changes 0 <->
|
||||
* 255.)
|
||||
*<p>
|
||||
* The sum s1 is initialized to 1 instead of zero to make the length
|
||||
* of the sequence part of s2, so that the length does not have to be
|
||||
* checked separately. (Any sequence of zeroes has a Fletcher
|
||||
* checksum of zero.)"
|
||||
*
|
||||
* @author John Leuner, Per Bothner
|
||||
* @since JDK 1.1
|
||||
*
|
||||
* @see InflaterInputStream
|
||||
* @see DeflaterOutputStream
|
||||
*/
|
||||
public class Adler32 implements Checksum
|
||||
{
|
||||
|
||||
/** largest prime smaller than 65536 */
|
||||
private static final int BASE = 65521;
|
||||
|
||||
private int checksum; //we do all in int.
|
||||
|
||||
//Note that java doesn't have unsigned integers,
|
||||
//so we have to be careful with what arithmetic
|
||||
//we do. We return the checksum as a long to
|
||||
//avoid sign confusion.
|
||||
|
||||
/**
|
||||
* Creates a new instance of the <code>Adler32</code> class.
|
||||
* The checksum starts off with a value of 1.
|
||||
*/
|
||||
public Adler32 ()
|
||||
{
|
||||
reset ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the Adler32 checksum to the initial value.
|
||||
*/
|
||||
public void reset ()
|
||||
{
|
||||
checksum = 1; //Initialize to 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the checksum with the byte b.
|
||||
*
|
||||
* @param bval the data value to add. The high byte of the int is ignored.
|
||||
*/
|
||||
public void update (int bval)
|
||||
{
|
||||
//We could make a length 1 byte array and call update again, but I
|
||||
//would rather not have that overhead
|
||||
int s1 = checksum & 0xffff;
|
||||
int s2 = checksum >>> 16;
|
||||
|
||||
s1 = (s1 + (bval & 0xFF)) % BASE;
|
||||
s2 = (s1 + s2) % BASE;
|
||||
|
||||
checksum = (s2 << 16) + s1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the checksum with the bytes taken from the array.
|
||||
*
|
||||
* @param buffer an array of bytes
|
||||
*/
|
||||
public void update (byte[] buffer)
|
||||
{
|
||||
update (buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the checksum with the bytes taken from the array.
|
||||
*
|
||||
* @param buf an array of bytes
|
||||
* @param off the start of the data used for this update
|
||||
* @param len the number of bytes to use for this update
|
||||
*/
|
||||
public void update (byte[] buf, int off, int len)
|
||||
{
|
||||
//(By Per Bothner)
|
||||
int s1 = checksum & 0xffff;
|
||||
int s2 = checksum >>> 16;
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
// We can defer the modulo operation:
|
||||
// s1 maximally grows from 65521 to 65521 + 255 * 3800
|
||||
// s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
|
||||
int n = 3800;
|
||||
if (n > len)
|
||||
n = len;
|
||||
len -= n;
|
||||
while (--n >= 0)
|
||||
{
|
||||
s1 = s1 + (buf[off++] & 0xFF);
|
||||
s2 = s2 + s1;
|
||||
}
|
||||
s1 %= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
|
||||
/*Old implementation, borrowed from somewhere:
|
||||
int n;
|
||||
|
||||
while (len-- > 0) {
|
||||
|
||||
s1 = (s1 + (bs[offset++] & 0xff)) % BASE;
|
||||
s2 = (s2 + s1) % BASE;
|
||||
}*/
|
||||
|
||||
checksum = (s2 << 16) | s1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Adler32 data checksum computed so far.
|
||||
*/
|
||||
public long getValue ()
|
||||
{
|
||||
return (long) checksum & 0xffffffffL;
|
||||
}
|
||||
}
|
135
src/com/classpath/zip/CRC32.java
Normal file
135
src/com/classpath/zip/CRC32.java
Normal file
@ -0,0 +1,135 @@
|
||||
/* CRC32.java - Computes CRC32 data checksum of a data stream
|
||||
Copyright (C) 1999. 2000, 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/*
|
||||
* Written using on-line Java Platform 1.2 API Specification, as well
|
||||
* as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
|
||||
* The actual CRC32 algorithm is taken from RFC 1952.
|
||||
* Status: Believed complete and correct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Computes CRC32 data checksum of a data stream.
|
||||
* The actual CRC32 algorithm is described in RFC 1952
|
||||
* (GZIP file format specification version 4.3).
|
||||
* Can be used to get the CRC32 over a stream if used with checked input/output
|
||||
* streams.
|
||||
*
|
||||
* @see InflaterInputStream
|
||||
* @see DeflaterOutputStream
|
||||
*
|
||||
* @author Per Bothner
|
||||
* @date April 1, 1999.
|
||||
*/
|
||||
public class CRC32 implements Checksum
|
||||
{
|
||||
/** The crc data checksum so far. */
|
||||
private int crc = 0;
|
||||
|
||||
/** The fast CRC table. Computed once when the CRC32 class is loaded. */
|
||||
private static int[] crc_table = make_crc_table ();
|
||||
|
||||
/** Make the table for a fast CRC. */
|
||||
private static int[] make_crc_table ()
|
||||
{
|
||||
int[] crc_table = new int[256];
|
||||
for (int n = 0; n < 256; n++)
|
||||
{
|
||||
int c = n;
|
||||
for (int k = 8; --k >= 0; )
|
||||
{
|
||||
if ((c & 1) != 0)
|
||||
c = 0xedb88320 ^ (c >>> 1);
|
||||
else
|
||||
c = c >>> 1;
|
||||
}
|
||||
crc_table[n] = c;
|
||||
}
|
||||
return crc_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CRC32 data checksum computed so far.
|
||||
*/
|
||||
public long getValue ()
|
||||
{
|
||||
return (long) crc & 0xffffffffL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the CRC32 data checksum as if no update was ever called.
|
||||
*/
|
||||
public void reset ()
|
||||
{ crc = 0; }
|
||||
|
||||
/**
|
||||
* Updates the checksum with the int bval.
|
||||
*
|
||||
* @param bval (the byte is taken as the lower 8 bits of bval)
|
||||
*/
|
||||
|
||||
public void update (int bval)
|
||||
{
|
||||
int c = ~crc;
|
||||
c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8);
|
||||
crc = ~c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the byte array to the data checksum.
|
||||
*
|
||||
* @param buf the buffer which contains the data
|
||||
* @param off the offset in the buffer where the data starts
|
||||
* @param len the length of the data
|
||||
*/
|
||||
public void update (byte[] buf, int off, int len)
|
||||
{
|
||||
int c = ~crc;
|
||||
while (--len >= 0)
|
||||
c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8);
|
||||
crc = ~c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the complete byte array to the data checksum.
|
||||
*/
|
||||
public void update (byte[] buf)
|
||||
{ update (buf, 0, buf.length); }
|
||||
}
|
64
src/com/classpath/zip/CentralDirectoryEndRecord.java
Normal file
64
src/com/classpath/zip/CentralDirectoryEndRecord.java
Normal file
@ -0,0 +1,64 @@
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
class CentralDirectoryEndRecord implements ZipConstants
|
||||
{
|
||||
public int numEntries, // total number of entries in the central dir
|
||||
centralSize, // size of the central directory
|
||||
centralOffset; // offset of start of central directory
|
||||
|
||||
public String comment;
|
||||
|
||||
public CentralDirectoryEndRecord()
|
||||
{
|
||||
numEntries = 0;
|
||||
centralSize = 0;
|
||||
centralOffset = 0;
|
||||
|
||||
comment = "";
|
||||
}
|
||||
|
||||
public boolean read(InputStream is) throws IOException
|
||||
{
|
||||
if(ZipFile.readLeInt(is) != ENDSIG)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
is.skip(6);
|
||||
|
||||
numEntries = ZipFile.readLeShort(is);
|
||||
centralSize = ZipFile.readLeInt(is);
|
||||
centralOffset = ZipFile.readLeInt(is);
|
||||
|
||||
int commentLen = ZipFile.readLeShort(is);
|
||||
|
||||
if(commentLen > 0)
|
||||
{
|
||||
comment = ZipFile.readString(is, commentLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
comment = "";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void write(OutputStream os) throws IOException
|
||||
{
|
||||
ZipFile.writeLeInt(os, ENDSIG);
|
||||
|
||||
ZipFile.writeLeShort(os, 0); /* disk number */
|
||||
ZipFile.writeLeShort(os, 0); /* disk with start of central dir */
|
||||
ZipFile.writeLeShort(os, numEntries);
|
||||
ZipFile.writeLeShort(os, numEntries);
|
||||
ZipFile.writeLeInt(os, centralSize);
|
||||
ZipFile.writeLeInt(os, centralOffset);
|
||||
|
||||
byte[] zipComment = ZipFile.encodeString(comment);
|
||||
ZipFile.writeLeShort(os, zipComment.length);
|
||||
os.write(zipComment);
|
||||
}
|
||||
}
|
137
src/com/classpath/zip/CheckedInputStream.java
Normal file
137
src/com/classpath/zip/CheckedInputStream.java
Normal file
@ -0,0 +1,137 @@
|
||||
/* CheckedInputStream.java - Compute checksum of data being read
|
||||
Copyright (C) 1999, 2000, 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
//import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/* Written using on-line Java Platform 1.2 API Specification
|
||||
* and JCL book.
|
||||
* Believed complete and correct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* InputStream that computes a checksum of the data being read using a
|
||||
* supplied Checksum object.
|
||||
*
|
||||
* @see Checksum
|
||||
*
|
||||
* @author Tom Tromey
|
||||
* @date May 17, 1999
|
||||
*/
|
||||
public class CheckedInputStream extends InputStream
|
||||
{
|
||||
protected InputStream in;
|
||||
/**
|
||||
* Creates a new CheckInputStream on top of the supplied OutputStream
|
||||
* using the supplied Checksum.
|
||||
*/
|
||||
public CheckedInputStream (InputStream in, Checksum sum)
|
||||
{
|
||||
this.in = in;
|
||||
this.sum = sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Checksum object used. To get the data checksum computed so
|
||||
* far call <code>getChecksum.getValue()</code>.
|
||||
*/
|
||||
public Checksum getChecksum ()
|
||||
{
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads one byte, updates the checksum and returns the read byte
|
||||
* (or -1 when the end of file was reached).
|
||||
*/
|
||||
public int read () throws IOException
|
||||
{
|
||||
int x = in.read();
|
||||
if (x != -1)
|
||||
sum.update(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads at most len bytes in the supplied buffer and updates the checksum
|
||||
* with it. Returns the number of bytes actually read or -1 when the end
|
||||
* of file was reached.
|
||||
*/
|
||||
public int read (byte[] buf, int off, int len) throws IOException
|
||||
{
|
||||
int r = in.read(buf, off, len);
|
||||
if (r != -1)
|
||||
sum.update(buf, off, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips n bytes by reading them in a temporary buffer and updating the
|
||||
* the checksum with that buffer. Returns the actual number of bytes skiped
|
||||
* which can be less then requested when the end of file is reached.
|
||||
*/
|
||||
public long skip (long n) throws IOException
|
||||
{
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
int min = (int) Math.min(n, 1024);
|
||||
byte[] buf = new byte[min];
|
||||
|
||||
long s = 0;
|
||||
while (n > 0)
|
||||
{
|
||||
int r = in.read(buf, 0, min);
|
||||
if (r == -1)
|
||||
break;
|
||||
n -= r;
|
||||
s += r;
|
||||
min = (int) Math.min(n, 1024);
|
||||
sum.update(buf, 0, r);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/** The checksum object. */
|
||||
private Checksum sum;
|
||||
}
|
103
src/com/classpath/zip/CheckedOutputStream.java
Normal file
103
src/com/classpath/zip/CheckedOutputStream.java
Normal file
@ -0,0 +1,103 @@
|
||||
/* CheckedOutputStream.java - Compute checksum of data being written.
|
||||
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
//import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/* Written using on-line Java Platform 1.2 API Specification
|
||||
* and JCL book.
|
||||
* Believed complete and correct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* OutputStream that computes a checksum of data being written using a
|
||||
* supplied Checksum object.
|
||||
*
|
||||
* @see Checksum
|
||||
*
|
||||
* @author Tom Tromey
|
||||
* @date May 17, 1999
|
||||
*/
|
||||
public class CheckedOutputStream extends OutputStream
|
||||
{
|
||||
protected OutputStream out;
|
||||
|
||||
/**
|
||||
* Creates a new CheckInputStream on top of the supplied OutputStream
|
||||
* using the supplied Checksum.
|
||||
*/
|
||||
public CheckedOutputStream (OutputStream out, Checksum cksum)
|
||||
{
|
||||
this.out = out;
|
||||
this.sum = cksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Checksum object used. To get the data checksum computed so
|
||||
* far call <code>getChecksum.getValue()</code>.
|
||||
*/
|
||||
public Checksum getChecksum ()
|
||||
{
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes one byte to the OutputStream and updates the Checksum.
|
||||
*/
|
||||
public void write (int bval) throws IOException
|
||||
{
|
||||
out.write(bval);
|
||||
sum.update(bval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the byte array to the OutputStream and updates the Checksum.
|
||||
*/
|
||||
public void write (byte[] buf, int off, int len) throws IOException
|
||||
{
|
||||
out.write(buf, off, len);
|
||||
sum.update(buf, off, len);
|
||||
}
|
||||
|
||||
/** The checksum object. */
|
||||
private Checksum sum;
|
||||
}
|
87
src/com/classpath/zip/Checksum.java
Normal file
87
src/com/classpath/zip/Checksum.java
Normal file
@ -0,0 +1,87 @@
|
||||
/* Checksum.java - Interface to compute a data checksum
|
||||
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/*
|
||||
* Written using on-line Java Platform 1.2 API Specification, as well
|
||||
* as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
|
||||
* Status: Believed complete and correct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface to compute a data checksum used by checked input/output streams.
|
||||
* A data checksum can be updated by one byte or with a byte array. After each
|
||||
* update the value of the current checksum can be returned by calling
|
||||
* <code>getValue</code>. The complete checksum object can also be reset
|
||||
* so it can be used again with new data.
|
||||
*
|
||||
* @see CheckedInputStream
|
||||
* @see CheckedOutputStream
|
||||
*
|
||||
* @author Per Bothner
|
||||
* @author Jochen Hoenicke
|
||||
*/
|
||||
public interface Checksum
|
||||
{
|
||||
/**
|
||||
* Returns the data checksum computed so far.
|
||||
*/
|
||||
long getValue ();
|
||||
|
||||
/**
|
||||
* Resets the data checksum as if no update was ever called.
|
||||
*/
|
||||
void reset ();
|
||||
|
||||
/**
|
||||
* Adds one byte to the data checksum.
|
||||
*
|
||||
* @param bval the data value to add. The high byte of the int is ignored.
|
||||
*/
|
||||
void update (int bval);
|
||||
|
||||
/**
|
||||
* Adds the byte array to the data checksum.
|
||||
*
|
||||
* @param buf the buffer which contains the data
|
||||
* @param off the offset in the buffer where the data starts
|
||||
* @param len the length of the data
|
||||
*/
|
||||
void update (byte[] buf, int off, int len);
|
||||
}
|
72
src/com/classpath/zip/DataFormatException.java
Normal file
72
src/com/classpath/zip/DataFormatException.java
Normal file
@ -0,0 +1,72 @@
|
||||
/* DataformatException.java -- thrown when compressed data is corrupt
|
||||
Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/**
|
||||
* Exception thrown when compressed data is corrupt.
|
||||
*
|
||||
* @author Tom Tromey
|
||||
* @author John Leuner
|
||||
* @since 1.1
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public class DataFormatException extends Exception
|
||||
{
|
||||
/**
|
||||
* Compatible with JDK 1.1+.
|
||||
*/
|
||||
private static final long serialVersionUID = 2219632870893641452L;
|
||||
|
||||
/**
|
||||
* Create an exception without a message.
|
||||
*/
|
||||
public DataFormatException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exception with a message.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
public DataFormatException(String msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
}
|
525
src/com/classpath/zip/Deflater.java
Normal file
525
src/com/classpath/zip/Deflater.java
Normal file
@ -0,0 +1,525 @@
|
||||
/* Deflater.java - Compress a data stream
|
||||
Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package com.classpath.zip;
|
||||
|
||||
/**
|
||||
* This is the Deflater class. The deflater class compresses input
|
||||
* with the deflate algorithm described in RFC 1951. It has several
|
||||
* compression levels and three different strategies described below.
|
||||
*
|
||||
* This class is <i>not</i> thread safe. This is inherent in the API, due
|
||||
* to the split of deflate and setInput.
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
* @author Tom Tromey
|
||||
*/
|
||||
public class Deflater
|
||||
{
|
||||
/**
|
||||
* The best and slowest compression level. This tries to find very
|
||||
* long and distant string repetitions.
|
||||
*/
|
||||
public static final int BEST_COMPRESSION = 9;
|
||||
/**
|
||||
* The worst but fastest compression level.
|
||||
*/
|
||||
public static final int BEST_SPEED = 1;
|
||||
/**
|
||||
* The default compression level.
|
||||
*/
|
||||
public static final int DEFAULT_COMPRESSION = -1;
|
||||
/**
|
||||
* This level won't compress at all but output uncompressed blocks.
|
||||
*/
|
||||
public static final int NO_COMPRESSION = 0;
|
||||
|
||||
/**
|
||||
* The default strategy.
|
||||
*/
|
||||
public static final int DEFAULT_STRATEGY = 0;
|
||||
/**
|
||||
* This strategy will only allow longer string repetitions. It is
|
||||
* useful for random data with a small character set.
|
||||
*/
|
||||
public static final int FILTERED = 1;
|
||||
|
||||
/**
|
||||
* This strategy will not look for string repetitions at all. It
|
||||
* only encodes with Huffman trees (which means, that more common
|
||||
* characters get a smaller encoding.
|
||||
*/
|
||||
public static final int HUFFMAN_ONLY = 2;
|
||||
|
||||
/**
|
||||
* The compression method. This is the only method supported so far.
|
||||
* There is no need to use this constant at all.
|
||||
*/
|
||||
public static final int DEFLATED = 8;
|
||||
|
||||
/*
|
||||
* The Deflater can do the following state transitions:
|
||||
*
|
||||
* (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---.
|
||||
* / | (2) (5) |
|
||||
* / v (5) |
|
||||
* (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
|
||||
* \ | (3) | ,-------'
|
||||
* | | | (3) /
|
||||
* v v (5) v v
|
||||
* (1) -> BUSY_STATE ----> FINISHING_STATE
|
||||
* | (6)
|
||||
* v
|
||||
* FINISHED_STATE
|
||||
* \_____________________________________/
|
||||
* | (7)
|
||||
* v
|
||||
* CLOSED_STATE
|
||||
*
|
||||
* (1) If we should produce a header we start in INIT_STATE, otherwise
|
||||
* we start in BUSY_STATE.
|
||||
* (2) A dictionary may be set only when we are in INIT_STATE, then
|
||||
* we change the state as indicated.
|
||||
* (3) Whether a dictionary is set or not, on the first call of deflate
|
||||
* we change to BUSY_STATE.
|
||||
* (4) -- intentionally left blank -- :)
|
||||
* (5) FINISHING_STATE is entered, when flush() is called to indicate that
|
||||
* there is no more INPUT. There are also states indicating, that
|
||||
* the header wasn't written yet.
|
||||
* (6) FINISHED_STATE is entered, when everything has been flushed to the
|
||||
* internal pending output buffer.
|
||||
* (7) At any time (7)
|
||||
*
|
||||
*/
|
||||
|
||||
private static final int IS_SETDICT = 0x01;
|
||||
private static final int IS_FLUSHING = 0x04;
|
||||
private static final int IS_FINISHING = 0x08;
|
||||
|
||||
private static final int INIT_STATE = 0x00;
|
||||
private static final int SETDICT_STATE = 0x01;
|
||||
private static final int INIT_FINISHING_STATE = 0x08;
|
||||
private static final int SETDICT_FINISHING_STATE = 0x09;
|
||||
private static final int BUSY_STATE = 0x10;
|
||||
private static final int FLUSHING_STATE = 0x14;
|
||||
private static final int FINISHING_STATE = 0x1c;
|
||||
private static final int FINISHED_STATE = 0x1e;
|
||||
private static final int CLOSED_STATE = 0x7f;
|
||||
|
||||
/** Compression level. */
|
||||
private int level;
|
||||
|
||||
/** should we include a header. */
|
||||
private boolean noHeader;
|
||||
|
||||
/** The current state. */
|
||||
private int state;
|
||||
|
||||
/** The total bytes of output written. */
|
||||
private int totalOut;
|
||||
|
||||
/** The pending output. */
|
||||
private DeflaterPending pending;
|
||||
|
||||
/** The deflater engine. */
|
||||
private DeflaterEngine engine;
|
||||
|
||||
/**
|
||||
* Creates a new deflater with default compression level.
|
||||
*/
|
||||
public Deflater()
|
||||
{
|
||||
this(DEFAULT_COMPRESSION, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deflater with given compression level.
|
||||
* @param lvl the compression level, a value between NO_COMPRESSION
|
||||
* and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
|
||||
* @exception IllegalArgumentException if lvl is out of range.
|
||||
*/
|
||||
public Deflater(int lvl)
|
||||
{
|
||||
this(lvl, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deflater with given compression level.
|
||||
* @param lvl the compression level, a value between NO_COMPRESSION
|
||||
* and BEST_COMPRESSION.
|
||||
* @param nowrap true, iff we should suppress the deflate header at the
|
||||
* beginning and the adler checksum at the end of the output. This is
|
||||
* useful for the GZIP format.
|
||||
* @exception IllegalArgumentException if lvl is out of range.
|
||||
*/
|
||||
public Deflater(int lvl, boolean nowrap)
|
||||
{
|
||||
if(lvl == DEFAULT_COMPRESSION)
|
||||
lvl = 6;
|
||||
else if(lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
pending = new DeflaterPending();
|
||||
engine = new DeflaterEngine(pending);
|
||||
this.noHeader = nowrap;
|
||||
setStrategy(DEFAULT_STRATEGY);
|
||||
setLevel(lvl);
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the deflater. The deflater acts afterwards as if it was
|
||||
* just created with the same compression level and strategy as it
|
||||
* had before.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
state = (noHeader ? BUSY_STATE : INIT_STATE);
|
||||
totalOut = 0;
|
||||
pending.reset();
|
||||
engine.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees all objects allocated by the compressor. There's no
|
||||
* reason to call this, since you can just rely on garbage
|
||||
* collection. Exists only for compatibility against Sun's JDK,
|
||||
* where the compressor allocates native memory.
|
||||
* If you call any method (even reset) afterwards the behaviour is
|
||||
* <i>undefined</i>.
|
||||
* @deprecated Just clear all references to deflater instead.
|
||||
*/
|
||||
public void end()
|
||||
{
|
||||
engine = null;
|
||||
pending = null;
|
||||
state = CLOSED_STATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current adler checksum of the data that was processed so
|
||||
* far.
|
||||
*/
|
||||
public int getAdler()
|
||||
{
|
||||
return engine.getAdler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of input bytes processed so far.
|
||||
*/
|
||||
public int getTotalIn()
|
||||
{
|
||||
return engine.getTotalIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of output bytes so far.
|
||||
*/
|
||||
public int getTotalOut()
|
||||
{
|
||||
return totalOut;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Finalizes this object.
|
||||
// */
|
||||
// protected void finalize ()
|
||||
// {
|
||||
// /* Exists solely for compatibility. We don't have any native state. */
|
||||
// }
|
||||
|
||||
/**
|
||||
* Flushes the current input block. Further calls to deflate() will
|
||||
* produce enough output to inflate everything in the current input
|
||||
* block. This is not part of Sun's JDK so I have made it package
|
||||
* private. It is used by DeflaterOutputStream to implement
|
||||
* flush().
|
||||
*/
|
||||
void flush()
|
||||
{
|
||||
state |= IS_FLUSHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the deflater with the current input block. It is an error
|
||||
* to give more input after this method was called. This method must
|
||||
* be called to force all bytes to be flushed.
|
||||
*/
|
||||
public void finish()
|
||||
{
|
||||
state |= IS_FLUSHING | IS_FINISHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff the stream was finished and no more output bytes
|
||||
* are available.
|
||||
*/
|
||||
public boolean finished()
|
||||
{
|
||||
return state == FINISHED_STATE && pending.isFlushed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the input buffer is empty.
|
||||
* You should then call setInput(). <br>
|
||||
*
|
||||
* <em>NOTE</em>: This method can also return true when the stream
|
||||
* was finished.
|
||||
*/
|
||||
public boolean needsInput()
|
||||
{
|
||||
return engine.needsInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data which should be compressed next. This should be only
|
||||
* called when needsInput indicates that more input is needed.
|
||||
* If you call setInput when needsInput() returns false, the
|
||||
* previous input that is still pending will be thrown away.
|
||||
* The given byte array should not be changed, before needsInput() returns
|
||||
* true again.
|
||||
* This call is equivalent to <code>setInput(input, 0, input.length)</code>.
|
||||
* @param input the buffer containing the input data.
|
||||
* @exception IllegalStateException if the buffer was finished() or ended().
|
||||
*/
|
||||
public void setInput(byte[] input)
|
||||
{
|
||||
setInput(input, 0, input.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data which should be compressed next. This should be
|
||||
* only called when needsInput indicates that more input is needed.
|
||||
* The given byte array should not be changed, before needsInput() returns
|
||||
* true again.
|
||||
* @param input the buffer containing the input data.
|
||||
* @param off the start of the data.
|
||||
* @param len the length of the data.
|
||||
* @exception IllegalStateException if the buffer was finished() or ended()
|
||||
* or if previous input is still pending.
|
||||
*/
|
||||
public void setInput(byte[] input, int off, int len)
|
||||
{
|
||||
if((state & IS_FINISHING) != 0)
|
||||
throw new IllegalStateException("finish()/end() already called");
|
||||
engine.setInput(input, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compression level. There is no guarantee of the exact
|
||||
* position of the change, but if you call this when needsInput is
|
||||
* true the change of compression level will occur somewhere near
|
||||
* before the end of the so far given input.
|
||||
* @param lvl the new compression level.
|
||||
*/
|
||||
public void setLevel(int lvl)
|
||||
{
|
||||
if(lvl == DEFAULT_COMPRESSION)
|
||||
{
|
||||
lvl = 6;
|
||||
}
|
||||
else if(lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION)
|
||||
{
|
||||
throw new IllegalArgumentException("Wrong compression level");
|
||||
}
|
||||
|
||||
if(level != lvl)
|
||||
{
|
||||
level = lvl;
|
||||
engine.setLevel(lvl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compression strategy. Strategy is one of
|
||||
* DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact
|
||||
* position where the strategy is changed, the same as for
|
||||
* setLevel() applies.
|
||||
* @param stgy the new compression strategy.
|
||||
*/
|
||||
public void setStrategy(int stgy)
|
||||
{
|
||||
if(stgy != DEFAULT_STRATEGY && stgy != FILTERED
|
||||
&& stgy != HUFFMAN_ONLY)
|
||||
throw new IllegalArgumentException();
|
||||
engine.setStrategy(stgy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deflates the current input block to the given array. It returns
|
||||
* the number of bytes compressed, or 0 if either
|
||||
* needsInput() or finished() returns true or length is zero.
|
||||
* @param output the buffer where to write the compressed data.
|
||||
*/
|
||||
public int deflate(byte[] output)
|
||||
{
|
||||
return deflate(output, 0, output.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deflates the current input block to the given array. It returns
|
||||
* the number of bytes compressed, or 0 if either
|
||||
* needsInput() or finished() returns true or length is zero.
|
||||
* @param output the buffer where to write the compressed data.
|
||||
* @param offset the offset into the output array.
|
||||
* @param length the maximum number of bytes that may be written.
|
||||
* @exception IllegalStateException if end() was called.
|
||||
* @exception IndexOutOfBoundsException if offset and/or length
|
||||
* don't match the array length.
|
||||
*/
|
||||
public int deflate(byte[] output, int offset, int length)
|
||||
{
|
||||
int origLength = length;
|
||||
|
||||
if(state == CLOSED_STATE)
|
||||
throw new IllegalStateException("Deflater closed");
|
||||
|
||||
if(state < BUSY_STATE)
|
||||
{
|
||||
/* output header */
|
||||
int header = (DEFLATED +
|
||||
((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
|
||||
int level_flags = (level - 1) >> 1;
|
||||
if(level_flags < 0 || level_flags > 3)
|
||||
level_flags = 3;
|
||||
header |= level_flags << 6;
|
||||
if((state & IS_SETDICT) != 0)
|
||||
/* Dictionary was set */
|
||||
header |= DeflaterConstants.PRESET_DICT;
|
||||
header += 31 - (header % 31);
|
||||
|
||||
pending.writeShortMSB(header);
|
||||
if((state & IS_SETDICT) != 0)
|
||||
{
|
||||
int chksum = engine.getAdler();
|
||||
engine.resetAdler();
|
||||
pending.writeShortMSB(chksum >> 16);
|
||||
pending.writeShortMSB(chksum & 0xffff);
|
||||
}
|
||||
|
||||
state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
|
||||
}
|
||||
|
||||
for(; ; )
|
||||
{
|
||||
int count = pending.flush(output, offset, length);
|
||||
offset += count;
|
||||
totalOut += count;
|
||||
length -= count;
|
||||
if(length == 0 || state == FINISHED_STATE)
|
||||
break;
|
||||
|
||||
if(!engine.deflate((state & IS_FLUSHING) != 0,
|
||||
(state & IS_FINISHING) != 0))
|
||||
{
|
||||
if(state == BUSY_STATE)
|
||||
/* We need more input now */
|
||||
return origLength - length;
|
||||
else if(state == FLUSHING_STATE)
|
||||
{
|
||||
if(level != NO_COMPRESSION)
|
||||
{
|
||||
/* We have to supply some lookahead. 8 bit lookahead
|
||||
* are needed by the zlib inflater, and we must fill
|
||||
* the next byte, so that all bits are flushed.
|
||||
*/
|
||||
int neededbits = 8 + ((-pending.getBitCount()) & 7);
|
||||
while(neededbits > 0)
|
||||
{
|
||||
/* write a static tree block consisting solely of
|
||||
* an EOF:
|
||||
*/
|
||||
pending.writeBits(2, 10);
|
||||
neededbits -= 10;
|
||||
}
|
||||
}
|
||||
state = BUSY_STATE;
|
||||
}
|
||||
else if(state == FINISHING_STATE)
|
||||
{
|
||||
pending.alignToByte();
|
||||
/* We have completed the stream */
|
||||
if(!noHeader)
|
||||
{
|
||||
int adler = engine.getAdler();
|
||||
pending.writeShortMSB(adler >> 16);
|
||||
pending.writeShortMSB(adler & 0xffff);
|
||||
}
|
||||
state = FINISHED_STATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return origLength - length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dictionary which should be used in the deflate process.
|
||||
* This call is equivalent to <code>setDictionary(dict, 0,
|
||||
* dict.length)</code>.
|
||||
* @param dict the dictionary.
|
||||
* @exception IllegalStateException if setInput () or deflate ()
|
||||
* were already called or another dictionary was already set.
|
||||
*/
|
||||
public void setDictionary(byte[] dict)
|
||||
{
|
||||
setDictionary(dict, 0, dict.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dictionary which should be used in the deflate process.
|
||||
* The dictionary should be a byte array containing strings that are
|
||||
* likely to occur in the data which should be compressed. The
|
||||
* dictionary is not stored in the compressed output, only a
|
||||
* checksum. To decompress the output you need to supply the same
|
||||
* dictionary again.
|
||||
* @param dict the dictionary.
|
||||
* @param offset an offset into the dictionary.
|
||||
* @param length the length of the dictionary.
|
||||
* @exception IllegalStateException if setInput () or deflate () were
|
||||
* already called or another dictionary was already set.
|
||||
*/
|
||||
public void setDictionary(byte[] dict, int offset, int length)
|
||||
{
|
||||
if(state != INIT_STATE)
|
||||
throw new IllegalStateException();
|
||||
|
||||
state = SETDICT_STATE;
|
||||
engine.setDictionary(dict, offset, length);
|
||||
}
|
||||
}
|
79
src/com/classpath/zip/DeflaterConstants.java
Normal file
79
src/com/classpath/zip/DeflaterConstants.java
Normal file
@ -0,0 +1,79 @@
|
||||
/* java.util.zip.DeflaterConstants
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
interface DeflaterConstants
|
||||
{
|
||||
final static boolean DEBUGGING = false;
|
||||
|
||||
final static int STORED_BLOCK = 0;
|
||||
final static int STATIC_TREES = 1;
|
||||
final static int DYN_TREES = 2;
|
||||
final static int PRESET_DICT = 0x20;
|
||||
|
||||
final static int DEFAULT_MEM_LEVEL = 8;
|
||||
|
||||
final static int MAX_MATCH = 258;
|
||||
final static int MIN_MATCH = 3;
|
||||
|
||||
final static int MAX_WBITS = 15;
|
||||
final static int WSIZE = 1 << MAX_WBITS;
|
||||
final static int WMASK = WSIZE - 1;
|
||||
|
||||
final static int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
|
||||
final static int HASH_SIZE = 1 << HASH_BITS;
|
||||
final static int HASH_MASK = HASH_SIZE - 1;
|
||||
final static int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
|
||||
|
||||
final static int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
|
||||
final static int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
|
||||
|
||||
final static int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
|
||||
final static int MAX_BLOCK_SIZE = Math.min (65535, PENDING_BUF_SIZE-5);
|
||||
|
||||
final static int DEFLATE_STORED = 0;
|
||||
final static int DEFLATE_FAST = 1;
|
||||
final static int DEFLATE_SLOW = 2;
|
||||
|
||||
final static int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 };
|
||||
final static int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 };
|
||||
final static int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 };
|
||||
final static int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 };
|
||||
final static int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 };
|
||||
}
|
706
src/com/classpath/zip/DeflaterEngine.java
Normal file
706
src/com/classpath/zip/DeflaterEngine.java
Normal file
@ -0,0 +1,706 @@
|
||||
/* java.util.zip.DeflaterEngine
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
class DeflaterEngine implements DeflaterConstants
|
||||
{
|
||||
private final static int TOO_FAR = 4096;
|
||||
|
||||
private int ins_h;
|
||||
|
||||
/**
|
||||
* Hashtable, hashing three characters to an index for window, so
|
||||
* that window[index]..window[index+2] have this hash code.
|
||||
* Note that the array should really be unsigned short, so you need
|
||||
* to and the values with 0xffff.
|
||||
*/
|
||||
private short[] head;
|
||||
|
||||
/**
|
||||
* prev[index & WMASK] points to the previous index that has the
|
||||
* same hash code as the string starting at index. This way
|
||||
* entries with the same hash code are in a linked list.
|
||||
* Note that the array should really be unsigned short, so you need
|
||||
* to and the values with 0xffff.
|
||||
*/
|
||||
private short[] prev;
|
||||
|
||||
private int matchStart, matchLen;
|
||||
private boolean prevAvailable;
|
||||
private int blockStart;
|
||||
|
||||
/**
|
||||
* strstart points to the current character in window.
|
||||
*/
|
||||
private int strstart;
|
||||
|
||||
/**
|
||||
* lookahead is the number of characters starting at strstart in
|
||||
* window that are valid.
|
||||
* So window[strstart] until window[strstart+lookahead-1] are valid
|
||||
* characters.
|
||||
*/
|
||||
private int lookahead;
|
||||
|
||||
/**
|
||||
* This array contains the part of the uncompressed stream that
|
||||
* is of relevance. The current character is indexed by strstart.
|
||||
*/
|
||||
private byte[] window;
|
||||
|
||||
private int strategy, max_chain, max_lazy, niceLength, goodLength;
|
||||
|
||||
/** The current compression function. */
|
||||
private int comprFunc;
|
||||
|
||||
/** The input data for compression. */
|
||||
private byte[] inputBuf;
|
||||
|
||||
/** The total bytes of input read. */
|
||||
private int totalIn;
|
||||
|
||||
/** The offset into inputBuf, where input data starts. */
|
||||
private int inputOff;
|
||||
|
||||
/** The end offset of the input data. */
|
||||
private int inputEnd;
|
||||
|
||||
private DeflaterPending pending;
|
||||
private DeflaterHuffman huffman;
|
||||
|
||||
/** The adler checksum */
|
||||
private Adler32 adler;
|
||||
|
||||
/* DEFLATE ALGORITHM:
|
||||
*
|
||||
* The uncompressed stream is inserted into the window array. When
|
||||
* the window array is full the first half is thrown away and the
|
||||
* second half is copied to the beginning.
|
||||
*
|
||||
* The head array is a hash table. Three characters build a hash value
|
||||
* and they the value points to the corresponding index in window of
|
||||
* the last string with this hash. The prev array implements a
|
||||
* linked list of matches with the same hash: prev[index & WMASK] points
|
||||
* to the previous index with the same hash.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
DeflaterEngine (DeflaterPending pending)
|
||||
{
|
||||
this.pending = pending;
|
||||
huffman = new DeflaterHuffman (pending);
|
||||
adler = new Adler32 ();
|
||||
|
||||
window = new byte[2*WSIZE];
|
||||
head = new short[HASH_SIZE];
|
||||
prev = new short[WSIZE];
|
||||
|
||||
/* We start at index 1, to avoid a implementation deficiency, that
|
||||
* we cannot build a repeat pattern at index 0.
|
||||
*/
|
||||
blockStart = strstart = 1;
|
||||
}
|
||||
|
||||
public void reset ()
|
||||
{
|
||||
huffman.reset ();
|
||||
adler.reset ();
|
||||
blockStart = strstart = 1;
|
||||
lookahead = 0;
|
||||
totalIn = 0;
|
||||
prevAvailable = false;
|
||||
matchLen = MIN_MATCH - 1;
|
||||
for (int i = 0; i < HASH_SIZE; i++)
|
||||
head[i] = 0;
|
||||
for (int i = 0; i < WSIZE; i++)
|
||||
prev[i] = 0;
|
||||
}
|
||||
|
||||
public final void resetAdler ()
|
||||
{
|
||||
adler.reset ();
|
||||
}
|
||||
|
||||
public final int getAdler ()
|
||||
{
|
||||
int chksum = (int) adler.getValue ();
|
||||
return chksum;
|
||||
}
|
||||
|
||||
public final int getTotalIn ()
|
||||
{
|
||||
return totalIn;
|
||||
}
|
||||
|
||||
public final void setStrategy (int strat)
|
||||
{
|
||||
strategy = strat;
|
||||
}
|
||||
|
||||
public void setLevel (int lvl)
|
||||
{
|
||||
goodLength = DeflaterConstants.GOOD_LENGTH[lvl];
|
||||
max_lazy = DeflaterConstants.MAX_LAZY[lvl];
|
||||
niceLength = DeflaterConstants.NICE_LENGTH[lvl];
|
||||
max_chain = DeflaterConstants.MAX_CHAIN[lvl];
|
||||
|
||||
if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("Change from "+comprFunc +" to "
|
||||
+ DeflaterConstants.COMPR_FUNC[lvl]);
|
||||
switch (comprFunc)
|
||||
{
|
||||
case DEFLATE_STORED:
|
||||
if (strstart > blockStart)
|
||||
{
|
||||
huffman.flushStoredBlock (window, blockStart,
|
||||
strstart - blockStart, false);
|
||||
blockStart = strstart;
|
||||
}
|
||||
updateHash ();
|
||||
break;
|
||||
case DEFLATE_FAST:
|
||||
if (strstart > blockStart)
|
||||
{
|
||||
huffman.flushBlock (window, blockStart, strstart - blockStart,
|
||||
false);
|
||||
blockStart = strstart;
|
||||
}
|
||||
break;
|
||||
case DEFLATE_SLOW:
|
||||
if (prevAvailable)
|
||||
huffman.tallyLit (window[strstart-1] & 0xff);
|
||||
if (strstart > blockStart)
|
||||
{
|
||||
huffman.flushBlock (window, blockStart, strstart - blockStart,
|
||||
false);
|
||||
blockStart = strstart;
|
||||
}
|
||||
prevAvailable = false;
|
||||
matchLen = MIN_MATCH - 1;
|
||||
break;
|
||||
}
|
||||
comprFunc = COMPR_FUNC[lvl];
|
||||
}
|
||||
}
|
||||
|
||||
private final void updateHash ()
|
||||
{
|
||||
if (DEBUGGING)
|
||||
System.err.println ("updateHash: "+strstart);
|
||||
ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the current string in the head hash and returns the previous
|
||||
* value for this hash.
|
||||
*/
|
||||
private final int insertString ()
|
||||
{
|
||||
short match;
|
||||
int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)])
|
||||
& HASH_MASK;
|
||||
|
||||
/*if (DEBUGGING)
|
||||
{
|
||||
if (hash != (((window[strstart] << (2*HASH_SHIFT))
|
||||
^ (window[strstart + 1] << HASH_SHIFT)
|
||||
^ (window[strstart + 2])) & HASH_MASK))
|
||||
throw new InternalError("hash inconsistent: "+hash+"/"
|
||||
+window[strstart]+","
|
||||
+window[strstart+1]+","
|
||||
+window[strstart+2]+","+HASH_SHIFT);
|
||||
}*/
|
||||
|
||||
prev[strstart & WMASK] = match = head[hash];
|
||||
head[hash] = (short) strstart;
|
||||
ins_h = hash;
|
||||
return match & 0xffff;
|
||||
}
|
||||
|
||||
private void slideWindow ()
|
||||
{
|
||||
System.arraycopy (window, WSIZE, window, 0, WSIZE);
|
||||
matchStart -= WSIZE;
|
||||
strstart -= WSIZE;
|
||||
blockStart -= WSIZE;
|
||||
|
||||
/* Slide the hash table (could be avoided with 32 bit values
|
||||
* at the expense of memory usage).
|
||||
*/
|
||||
for (int i = 0; i < HASH_SIZE; i++)
|
||||
{
|
||||
int m = head[i] & 0xffff;
|
||||
head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
|
||||
}
|
||||
|
||||
/* Slide the prev table.
|
||||
*/
|
||||
for (int i = 0; i < WSIZE; i++)
|
||||
{
|
||||
int m = prev[i] & 0xffff;
|
||||
prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the window when the lookahead becomes insufficient.
|
||||
* Updates strstart and lookahead.
|
||||
*
|
||||
* OUT assertions: strstart + lookahead <= 2*WSIZE
|
||||
* lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd
|
||||
*/
|
||||
private void fillWindow ()
|
||||
{
|
||||
/* If the window is almost full and there is insufficient lookahead,
|
||||
* move the upper half to the lower one to make room in the upper half.
|
||||
*/
|
||||
if (strstart >= WSIZE + MAX_DIST)
|
||||
slideWindow ();
|
||||
|
||||
/* If there is not enough lookahead, but still some input left,
|
||||
* read in the input
|
||||
*/
|
||||
while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd)
|
||||
{
|
||||
int more = 2*WSIZE - lookahead - strstart;
|
||||
|
||||
if (more > inputEnd - inputOff)
|
||||
more = inputEnd - inputOff;
|
||||
|
||||
System.arraycopy (inputBuf, inputOff,
|
||||
window, strstart + lookahead, more);
|
||||
adler.update (inputBuf, inputOff, more);
|
||||
inputOff += more;
|
||||
totalIn += more;
|
||||
lookahead += more;
|
||||
}
|
||||
|
||||
if (lookahead >= MIN_MATCH)
|
||||
updateHash ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the best (longest) string in the window matching the
|
||||
* string starting at strstart.
|
||||
*
|
||||
* Preconditions:
|
||||
* strstart + MAX_MATCH <= window.length.
|
||||
*
|
||||
*
|
||||
* @param curMatch
|
||||
*/
|
||||
private boolean findLongestMatch (int curMatch)
|
||||
{
|
||||
int chainLength = this.max_chain;
|
||||
int niceLength = this.niceLength;
|
||||
short[] prev = this.prev;
|
||||
int scan = this.strstart;
|
||||
int match;
|
||||
int best_end = this.strstart + matchLen;
|
||||
int best_len = Math.max (matchLen, MIN_MATCH - 1);
|
||||
|
||||
int limit = Math.max (strstart - MAX_DIST, 0);
|
||||
|
||||
int strend = scan + MAX_MATCH - 1;
|
||||
byte scan_end1 = window[best_end - 1];
|
||||
byte scan_end = window[best_end];
|
||||
|
||||
/* Do not waste too much time if we already have a good match: */
|
||||
if (best_len >= this.goodLength)
|
||||
chainLength >>= 2;
|
||||
|
||||
/* Do not look for matches beyond the end of the input. This is necessary
|
||||
* to make deflate deterministic.
|
||||
*/
|
||||
if (niceLength > lookahead)
|
||||
niceLength = lookahead;
|
||||
|
||||
/*if (DeflaterConstants.DEBUGGING
|
||||
&& strstart > 2*WSIZE - MIN_LOOKAHEAD)
|
||||
throw new InternalError("need lookahead");*/
|
||||
|
||||
do
|
||||
{
|
||||
/*if (DeflaterConstants.DEBUGGING && curMatch >= strstart)
|
||||
throw new InternalError("future match");*/
|
||||
if (window[curMatch + best_len] != scan_end
|
||||
|| window[curMatch + best_len - 1] != scan_end1
|
||||
|| window[curMatch] != window[scan]
|
||||
|| window[curMatch+1] != window[scan + 1])
|
||||
continue;
|
||||
|
||||
match = curMatch + 2;
|
||||
scan += 2;
|
||||
|
||||
/* We check for insufficient lookahead only every 8th comparison;
|
||||
* the 256th check will be made at strstart+258.
|
||||
*/
|
||||
while (window[++scan] == window[++match]
|
||||
&& window[++scan] == window[++match]
|
||||
&& window[++scan] == window[++match]
|
||||
&& window[++scan] == window[++match]
|
||||
&& window[++scan] == window[++match]
|
||||
&& window[++scan] == window[++match]
|
||||
&& window[++scan] == window[++match]
|
||||
&& window[++scan] == window[++match]
|
||||
&& scan < strend);
|
||||
|
||||
if (scan > best_end)
|
||||
{
|
||||
// if (DeflaterConstants.DEBUGGING && ins_h == 0)
|
||||
// System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
|
||||
matchStart = curMatch;
|
||||
best_end = scan;
|
||||
best_len = scan - strstart;
|
||||
if (best_len >= niceLength)
|
||||
break;
|
||||
|
||||
scan_end1 = window[best_end-1];
|
||||
scan_end = window[best_end];
|
||||
}
|
||||
scan = strstart;
|
||||
} while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit
|
||||
&& --chainLength != 0);
|
||||
|
||||
matchLen = Math.min (best_len, lookahead);
|
||||
return matchLen >= MIN_MATCH;
|
||||
}
|
||||
|
||||
void setDictionary (byte[] buffer, int offset, int length)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && strstart != 1)
|
||||
throw new IllegalStateException ("strstart not 1");
|
||||
adler.update (buffer, offset, length);
|
||||
if (length < MIN_MATCH)
|
||||
return;
|
||||
if (length > MAX_DIST)
|
||||
{
|
||||
offset += length - MAX_DIST;
|
||||
length = MAX_DIST;
|
||||
}
|
||||
|
||||
System.arraycopy (buffer, offset, window, strstart, length);
|
||||
|
||||
updateHash ();
|
||||
length--;
|
||||
while (--length > 0)
|
||||
{
|
||||
insertString ();
|
||||
strstart++;
|
||||
}
|
||||
strstart += 2;
|
||||
blockStart = strstart;
|
||||
}
|
||||
|
||||
private boolean deflateStored (boolean flush, boolean finish)
|
||||
{
|
||||
if (!flush && lookahead == 0)
|
||||
return false;
|
||||
|
||||
strstart += lookahead;
|
||||
lookahead = 0;
|
||||
|
||||
int storedLen = strstart - blockStart;
|
||||
|
||||
if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE)
|
||||
/* Block is full */
|
||||
|| (blockStart < WSIZE && storedLen >= MAX_DIST)
|
||||
/* Block may move out of window */
|
||||
|| flush)
|
||||
{
|
||||
boolean lastBlock = finish;
|
||||
if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE)
|
||||
{
|
||||
storedLen = DeflaterConstants.MAX_BLOCK_SIZE;
|
||||
lastBlock = false;
|
||||
}
|
||||
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("storedBlock["+storedLen+","+lastBlock+"]");
|
||||
|
||||
huffman.flushStoredBlock (window, blockStart, storedLen, lastBlock);
|
||||
blockStart += storedLen;
|
||||
return !lastBlock;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean deflateFast (boolean flush, boolean finish)
|
||||
{
|
||||
if (lookahead < MIN_LOOKAHEAD && !flush)
|
||||
return false;
|
||||
|
||||
while (lookahead >= MIN_LOOKAHEAD || flush)
|
||||
{
|
||||
if (lookahead == 0)
|
||||
{
|
||||
/* We are flushing everything */
|
||||
huffman.flushBlock (window, blockStart, strstart - blockStart,
|
||||
finish);
|
||||
blockStart = strstart;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstart > 2 * WSIZE - MIN_LOOKAHEAD)
|
||||
{
|
||||
/* slide window, as findLongestMatch need this.
|
||||
* This should only happen when flushing and the window
|
||||
* is almost full.
|
||||
*/
|
||||
slideWindow ();
|
||||
}
|
||||
|
||||
int hashHead;
|
||||
if (lookahead >= MIN_MATCH
|
||||
&& (hashHead = insertString ()) != 0
|
||||
&& strategy != Deflater.HUFFMAN_ONLY
|
||||
&& strstart - hashHead <= MAX_DIST
|
||||
&& findLongestMatch (hashHead))
|
||||
{
|
||||
/* longestMatch sets matchStart and matchLen */
|
||||
/*if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
for (int i = 0 ; i < matchLen; i++)
|
||||
{
|
||||
if (window[strstart+i] != window[matchStart + i])
|
||||
throw new InternalError();
|
||||
}
|
||||
}*/
|
||||
huffman.tallyDist (strstart - matchStart, matchLen);
|
||||
|
||||
lookahead -= matchLen;
|
||||
if (matchLen <= max_lazy && lookahead >= MIN_MATCH)
|
||||
{
|
||||
while (--matchLen > 0)
|
||||
{
|
||||
strstart++;
|
||||
insertString ();
|
||||
}
|
||||
strstart++;
|
||||
}
|
||||
else
|
||||
{
|
||||
strstart += matchLen;
|
||||
if (lookahead >= MIN_MATCH - 1)
|
||||
updateHash ();
|
||||
}
|
||||
matchLen = MIN_MATCH - 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No match found */
|
||||
huffman.tallyLit (window[strstart] & 0xff);
|
||||
strstart++;
|
||||
lookahead--;
|
||||
}
|
||||
|
||||
if (huffman.isFull ())
|
||||
{
|
||||
boolean lastBlock = finish && lookahead == 0;
|
||||
huffman.flushBlock (window, blockStart, strstart - blockStart,
|
||||
lastBlock);
|
||||
blockStart = strstart;
|
||||
return !lastBlock;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean deflateSlow (boolean flush, boolean finish)
|
||||
{
|
||||
if (lookahead < MIN_LOOKAHEAD && !flush)
|
||||
return false;
|
||||
|
||||
while (lookahead >= MIN_LOOKAHEAD || flush)
|
||||
{
|
||||
if (lookahead == 0)
|
||||
{
|
||||
if (prevAvailable)
|
||||
huffman.tallyLit (window[strstart-1] & 0xff);
|
||||
prevAvailable = false;
|
||||
|
||||
/* We are flushing everything */
|
||||
/*if (DeflaterConstants.DEBUGGING && !flush)
|
||||
throw new InternalError("Not flushing, but no lookahead");*/
|
||||
huffman.flushBlock (window, blockStart, strstart - blockStart,
|
||||
finish);
|
||||
blockStart = strstart;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD)
|
||||
{
|
||||
/* slide window, as findLongestMatch need this.
|
||||
* This should only happen when flushing and the window
|
||||
* is almost full.
|
||||
*/
|
||||
slideWindow ();
|
||||
}
|
||||
|
||||
int prevMatch = matchStart;
|
||||
int prevLen = matchLen;
|
||||
if (lookahead >= MIN_MATCH)
|
||||
{
|
||||
int hashHead = insertString ();
|
||||
if (strategy != Deflater.HUFFMAN_ONLY
|
||||
&& hashHead != 0 && strstart - hashHead <= MAX_DIST
|
||||
&& findLongestMatch (hashHead))
|
||||
{
|
||||
/* longestMatch sets matchStart and matchLen */
|
||||
|
||||
/* Discard match if too small and too far away */
|
||||
if (matchLen <= 5
|
||||
&& (strategy == Deflater.FILTERED
|
||||
|| (matchLen == MIN_MATCH
|
||||
&& strstart - matchStart > TOO_FAR)))
|
||||
{
|
||||
matchLen = MIN_MATCH - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* previous match was better */
|
||||
if (prevLen >= MIN_MATCH && matchLen <= prevLen)
|
||||
{
|
||||
/*if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
for (int i = 0 ; i < matchLen; i++)
|
||||
{
|
||||
if (window[strstart-1+i] != window[prevMatch + i])
|
||||
throw new InternalError();
|
||||
}
|
||||
}*/
|
||||
huffman.tallyDist (strstart - 1 - prevMatch, prevLen);
|
||||
prevLen -= 2;
|
||||
do
|
||||
{
|
||||
strstart++;
|
||||
lookahead--;
|
||||
if (lookahead >= MIN_MATCH)
|
||||
insertString ();
|
||||
}
|
||||
while (--prevLen > 0);
|
||||
strstart ++;
|
||||
lookahead--;
|
||||
prevAvailable = false;
|
||||
matchLen = MIN_MATCH - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prevAvailable)
|
||||
huffman.tallyLit (window[strstart-1] & 0xff);
|
||||
prevAvailable = true;
|
||||
strstart++;
|
||||
lookahead--;
|
||||
}
|
||||
|
||||
if (huffman.isFull ())
|
||||
{
|
||||
int len = strstart - blockStart;
|
||||
if (prevAvailable)
|
||||
len--;
|
||||
boolean lastBlock = (finish && lookahead == 0 && !prevAvailable);
|
||||
huffman.flushBlock (window, blockStart, len, lastBlock);
|
||||
blockStart += len;
|
||||
return !lastBlock;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean deflate (boolean flush, boolean finish)
|
||||
{
|
||||
boolean progress;
|
||||
do
|
||||
{
|
||||
fillWindow ();
|
||||
boolean canFlush = flush && inputOff == inputEnd;
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("window: ["+blockStart+","+strstart+","
|
||||
+lookahead+"], "+comprFunc+","+canFlush);
|
||||
switch (comprFunc)
|
||||
{
|
||||
case DEFLATE_STORED:
|
||||
progress = deflateStored (canFlush, finish);
|
||||
break;
|
||||
case DEFLATE_FAST:
|
||||
progress = deflateFast (canFlush, finish);
|
||||
break;
|
||||
case DEFLATE_SLOW:
|
||||
progress = deflateSlow (canFlush, finish);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (pending.isFlushed () /* repeat while we have no pending output */
|
||||
&& progress); /* and progress was made */
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
public void setInput (byte[] buf, int off, int len)
|
||||
{
|
||||
if (inputOff < inputEnd)
|
||||
throw new IllegalStateException
|
||||
("Old input was not completely processed");
|
||||
|
||||
int end = off + len;
|
||||
|
||||
/* We want to throw an ArrayIndexOutOfBoundsException early. The
|
||||
* check is very tricky: it also handles integer wrap around.
|
||||
*/
|
||||
if (0 > off || off > end || end > buf.length)
|
||||
throw new ArrayIndexOutOfBoundsException ();
|
||||
|
||||
inputBuf = buf;
|
||||
inputOff = off;
|
||||
inputEnd = end;
|
||||
}
|
||||
|
||||
public final boolean needsInput ()
|
||||
{
|
||||
return inputEnd == inputOff;
|
||||
}
|
||||
}
|
797
src/com/classpath/zip/DeflaterHuffman.java
Normal file
797
src/com/classpath/zip/DeflaterHuffman.java
Normal file
@ -0,0 +1,797 @@
|
||||
/* java.util.zip.DeflaterHuffman
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/**
|
||||
* This is the DeflaterHuffman class.
|
||||
*
|
||||
* This class is <i>not</i> thread safe. This is inherent in the API, due
|
||||
* to the split of deflate and setInput.
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
* @date Jan 6, 2000
|
||||
*/
|
||||
class DeflaterHuffman
|
||||
{
|
||||
private static final int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
|
||||
private static final int LITERAL_NUM = 286;
|
||||
private static final int DIST_NUM = 30;
|
||||
private static final int BITLEN_NUM = 19;
|
||||
private static final int REP_3_6 = 16;
|
||||
private static final int REP_3_10 = 17;
|
||||
private static final int REP_11_138 = 18;
|
||||
private static final int EOF_SYMBOL = 256;
|
||||
private static final int[] BL_ORDER =
|
||||
{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
|
||||
private final static String bit4Reverse =
|
||||
"\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017";
|
||||
|
||||
class Tree
|
||||
{
|
||||
short[] freqs;
|
||||
short[] codes;
|
||||
byte[] length;
|
||||
int[] bl_counts;
|
||||
int minNumCodes, numCodes;
|
||||
int maxLength;
|
||||
|
||||
Tree (int elems, int minCodes, int maxLength)
|
||||
{
|
||||
this.minNumCodes = minCodes;
|
||||
this.maxLength = maxLength;
|
||||
freqs = new short[elems];
|
||||
bl_counts = new int[maxLength];
|
||||
}
|
||||
|
||||
void reset ()
|
||||
{
|
||||
for (int i = 0; i < freqs.length; i++)
|
||||
freqs[i] = 0;
|
||||
codes = null;
|
||||
length = null;
|
||||
}
|
||||
|
||||
final void writeSymbol (int code)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
freqs[code]--;
|
||||
// System.err.print("writeSymbol("+freqs.length+","+code+"): ");
|
||||
}
|
||||
pending.writeBits (codes[code] & 0xffff, length[code]);
|
||||
}
|
||||
|
||||
final void checkEmpty ()
|
||||
{
|
||||
boolean empty = true;
|
||||
for (int i = 0; i < freqs.length; i++)
|
||||
if (freqs[i] != 0)
|
||||
{
|
||||
System.err.println ("freqs["+i+"] == "+freqs[i]);
|
||||
empty = false;
|
||||
}
|
||||
//if (!empty)
|
||||
//throw new Exception ();//InternalError();
|
||||
System.err.println ("checkEmpty suceeded!");
|
||||
}
|
||||
|
||||
void setStaticCodes (short[] stCodes, byte[] stLength)
|
||||
{
|
||||
codes = stCodes;
|
||||
length = stLength;
|
||||
}
|
||||
|
||||
public void buildCodes ()
|
||||
{
|
||||
int[] nextCode = new int[maxLength];
|
||||
int code = 0;
|
||||
codes = new short[freqs.length];
|
||||
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("buildCodes: "+freqs.length);
|
||||
for (int bits = 0; bits < maxLength; bits++)
|
||||
{
|
||||
nextCode[bits] = code;
|
||||
code += bl_counts[bits] << (15 - bits);
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("bits: "+(bits+1)+" count: "+bl_counts[bits]
|
||||
+" nextCode: "+Integer.toHexString (code));
|
||||
}
|
||||
if (DeflaterConstants.DEBUGGING && code != 65536)
|
||||
throw new RuntimeException ("Inconsistent bl_counts!");
|
||||
|
||||
for (int i=0; i < numCodes; i++)
|
||||
{
|
||||
int bits = length[i];
|
||||
if (bits > 0)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("codes["+i+"] = rev("
|
||||
+Integer.toHexString (nextCode[bits-1])+"),"
|
||||
+bits);
|
||||
codes[i] = bitReverse (nextCode[bits-1]);
|
||||
nextCode[bits-1] += 1 << (16 - bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildLength (int childs[])
|
||||
{
|
||||
this.length = new byte [freqs.length];
|
||||
int numNodes = childs.length / 2;
|
||||
int numLeafs = (numNodes + 1) / 2;
|
||||
int overflow = 0;
|
||||
|
||||
for (int i = 0; i < maxLength; i++)
|
||||
bl_counts[i] = 0;
|
||||
|
||||
/* First calculate optimal bit lengths */
|
||||
int lengths[] = new int[numNodes];
|
||||
lengths[numNodes-1] = 0;
|
||||
for (int i = numNodes - 1; i >= 0; i--)
|
||||
{
|
||||
if (childs[2*i+1] != -1)
|
||||
{
|
||||
int bitLength = lengths[i] + 1;
|
||||
if (bitLength > maxLength)
|
||||
{
|
||||
bitLength = maxLength;
|
||||
overflow++;
|
||||
}
|
||||
lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* A leaf node */
|
||||
int bitLength = lengths[i];
|
||||
bl_counts[bitLength - 1]++;
|
||||
this.length[childs[2*i]] = (byte) lengths[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
System.err.println ("Tree "+freqs.length+" lengths:");
|
||||
for (int i=0; i < numLeafs; i++)
|
||||
System.err.println ("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
|
||||
+ " len: "+length[childs[2*i]]);
|
||||
}
|
||||
|
||||
if (overflow == 0)
|
||||
return;
|
||||
|
||||
int incrBitLen = maxLength - 1;
|
||||
do
|
||||
{
|
||||
/* Find the first bit length which could increase: */
|
||||
while (bl_counts[--incrBitLen] == 0)
|
||||
;
|
||||
|
||||
/* Move this node one down and remove a corresponding
|
||||
* amount of overflow nodes.
|
||||
*/
|
||||
do
|
||||
{
|
||||
bl_counts[incrBitLen]--;
|
||||
bl_counts[++incrBitLen]++;
|
||||
overflow -= 1 << (maxLength - 1 - incrBitLen);
|
||||
}
|
||||
while (overflow > 0 && incrBitLen < maxLength - 1);
|
||||
}
|
||||
while (overflow > 0);
|
||||
|
||||
/* We may have overshot above. Move some nodes from maxLength to
|
||||
* maxLength-1 in that case.
|
||||
*/
|
||||
bl_counts[maxLength-1] += overflow;
|
||||
bl_counts[maxLength-2] -= overflow;
|
||||
|
||||
/* Now recompute all bit lengths, scanning in increasing
|
||||
* frequency. It is simpler to reconstruct all lengths instead of
|
||||
* fixing only the wrong ones. This idea is taken from 'ar'
|
||||
* written by Haruhiko Okumura.
|
||||
*
|
||||
* The nodes were inserted with decreasing frequency into the childs
|
||||
* array.
|
||||
*/
|
||||
int nodePtr = 2 * numLeafs;
|
||||
for (int bits = maxLength; bits != 0; bits--)
|
||||
{
|
||||
int n = bl_counts[bits-1];
|
||||
while (n > 0)
|
||||
{
|
||||
int childPtr = 2*childs[nodePtr++];
|
||||
if (childs[childPtr + 1] == -1)
|
||||
{
|
||||
/* We found another leaf */
|
||||
length[childs[childPtr]] = (byte) bits;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
System.err.println ("*** After overflow elimination. ***");
|
||||
for (int i=0; i < numLeafs; i++)
|
||||
System.err.println ("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
|
||||
+ " len: "+length[childs[2*i]]);
|
||||
}
|
||||
}
|
||||
|
||||
void buildTree ()
|
||||
{
|
||||
int numSymbols = freqs.length;
|
||||
|
||||
/* heap is a priority queue, sorted by frequency, least frequent
|
||||
* nodes first. The heap is a binary tree, with the property, that
|
||||
* the parent node is smaller than both child nodes. This assures
|
||||
* that the smallest node is the first parent.
|
||||
*
|
||||
* The binary tree is encoded in an array: 0 is root node and
|
||||
* the nodes 2*n+1, 2*n+2 are the child nodes of node n.
|
||||
*/
|
||||
int[] heap = new int[numSymbols];
|
||||
int heapLen = 0;
|
||||
int maxCode = 0;
|
||||
for (int n = 0; n < numSymbols; n++)
|
||||
{
|
||||
int freq = freqs[n];
|
||||
if (freq != 0)
|
||||
{
|
||||
/* Insert n into heap */
|
||||
int pos = heapLen++;
|
||||
int ppos;
|
||||
while (pos > 0 &&
|
||||
freqs[heap[ppos = (pos - 1) / 2]] > freq)
|
||||
{
|
||||
heap[pos] = heap[ppos];
|
||||
pos = ppos;
|
||||
}
|
||||
heap[pos] = n;
|
||||
maxCode = n;
|
||||
}
|
||||
}
|
||||
|
||||
/* We could encode a single literal with 0 bits but then we
|
||||
* don't see the literals. Therefore we force at least two
|
||||
* literals to avoid this case. We don't care about order in
|
||||
* this case, both literals get a 1 bit code.
|
||||
*/
|
||||
while (heapLen < 2)
|
||||
{
|
||||
int node = maxCode < 2 ? ++maxCode : 0;
|
||||
heap[heapLen++] = node;
|
||||
}
|
||||
|
||||
numCodes = Math.max (maxCode + 1, minNumCodes);
|
||||
|
||||
int numLeafs = heapLen;
|
||||
int[] childs = new int[4*heapLen - 2];
|
||||
int[] values = new int[2*heapLen - 1];
|
||||
int numNodes = numLeafs;
|
||||
for (int i = 0; i < heapLen; i++)
|
||||
{
|
||||
int node = heap[i];
|
||||
childs[2*i] = node;
|
||||
childs[2*i+1] = -1;
|
||||
values[i] = freqs[node] << 8;
|
||||
heap[i] = i;
|
||||
}
|
||||
|
||||
/* Construct the Huffman tree by repeatedly combining the least two
|
||||
* frequent nodes.
|
||||
*/
|
||||
do
|
||||
{
|
||||
int first = heap[0];
|
||||
int last = heap[--heapLen];
|
||||
|
||||
/* Propagate the hole to the leafs of the heap */
|
||||
int ppos = 0;
|
||||
int path = 1;
|
||||
while (path < heapLen)
|
||||
{
|
||||
if (path + 1 < heapLen
|
||||
&& values[heap[path]] > values[heap[path+1]])
|
||||
path++;
|
||||
|
||||
heap[ppos] = heap[path];
|
||||
ppos = path;
|
||||
path = path * 2 + 1;
|
||||
}
|
||||
|
||||
/* Now propagate the last element down along path. Normally
|
||||
* it shouldn't go too deep.
|
||||
*/
|
||||
int lastVal = values[last];
|
||||
while ((path = ppos) > 0
|
||||
&& values[heap[ppos = (path - 1)/2]] > lastVal)
|
||||
heap[path] = heap[ppos];
|
||||
heap[path] = last;
|
||||
|
||||
|
||||
int second = heap[0];
|
||||
|
||||
/* Create a new node father of first and second */
|
||||
last = numNodes++;
|
||||
childs[2*last] = first;
|
||||
childs[2*last+1] = second;
|
||||
int mindepth = Math.min (values[first] & 0xff, values[second] & 0xff);
|
||||
values[last] = lastVal = values[first] + values[second] - mindepth + 1;
|
||||
|
||||
/* Again, propagate the hole to the leafs */
|
||||
ppos = 0;
|
||||
path = 1;
|
||||
while (path < heapLen)
|
||||
{
|
||||
if (path + 1 < heapLen
|
||||
&& values[heap[path]] > values[heap[path+1]])
|
||||
path++;
|
||||
|
||||
heap[ppos] = heap[path];
|
||||
ppos = path;
|
||||
path = ppos * 2 + 1;
|
||||
}
|
||||
|
||||
/* Now propagate the new element down along path */
|
||||
while ((path = ppos) > 0
|
||||
&& values[heap[ppos = (path - 1)/2]] > lastVal)
|
||||
heap[path] = heap[ppos];
|
||||
heap[path] = last;
|
||||
}
|
||||
while (heapLen > 1);
|
||||
|
||||
if (heap[0] != childs.length / 2 - 1)
|
||||
throw new RuntimeException ("Weird!");
|
||||
|
||||
buildLength (childs);
|
||||
}
|
||||
|
||||
int getEncodedLength ()
|
||||
{
|
||||
int len = 0;
|
||||
for (int i = 0; i < freqs.length; i++)
|
||||
len += freqs[i] * length[i];
|
||||
return len;
|
||||
}
|
||||
|
||||
void calcBLFreq (Tree blTree)
|
||||
{
|
||||
int max_count; /* max repeat count */
|
||||
int min_count; /* min repeat count */
|
||||
int count; /* repeat count of the current code */
|
||||
int curlen = -1; /* length of current code */
|
||||
|
||||
int i = 0;
|
||||
while (i < numCodes)
|
||||
{
|
||||
count = 1;
|
||||
int nextlen = length[i];
|
||||
if (nextlen == 0)
|
||||
{
|
||||
max_count = 138;
|
||||
min_count = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
max_count = 6;
|
||||
min_count = 3;
|
||||
if (curlen != nextlen)
|
||||
{
|
||||
blTree.freqs[nextlen]++;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
curlen = nextlen;
|
||||
i++;
|
||||
|
||||
while (i < numCodes && curlen == length[i])
|
||||
{
|
||||
i++;
|
||||
if (++count >= max_count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (count < min_count)
|
||||
blTree.freqs[curlen] += count;
|
||||
else if (curlen != 0)
|
||||
blTree.freqs[REP_3_6]++;
|
||||
else if (count <= 10)
|
||||
blTree.freqs[REP_3_10]++;
|
||||
else
|
||||
blTree.freqs[REP_11_138]++;
|
||||
}
|
||||
}
|
||||
|
||||
void writeTree (Tree blTree)
|
||||
{
|
||||
int max_count; /* max repeat count */
|
||||
int min_count; /* min repeat count */
|
||||
int count; /* repeat count of the current code */
|
||||
int curlen = -1; /* length of current code */
|
||||
|
||||
int i = 0;
|
||||
while (i < numCodes)
|
||||
{
|
||||
count = 1;
|
||||
int nextlen = length[i];
|
||||
if (nextlen == 0)
|
||||
{
|
||||
max_count = 138;
|
||||
min_count = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
max_count = 6;
|
||||
min_count = 3;
|
||||
if (curlen != nextlen)
|
||||
{
|
||||
blTree.writeSymbol (nextlen);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
curlen = nextlen;
|
||||
i++;
|
||||
|
||||
while (i < numCodes && curlen == length[i])
|
||||
{
|
||||
i++;
|
||||
if (++count >= max_count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (count < min_count)
|
||||
{
|
||||
while (count-- > 0)
|
||||
blTree.writeSymbol (curlen);
|
||||
}
|
||||
else if (curlen != 0)
|
||||
{
|
||||
blTree.writeSymbol (REP_3_6);
|
||||
pending.writeBits (count - 3, 2);
|
||||
}
|
||||
else if (count <= 10)
|
||||
{
|
||||
blTree.writeSymbol (REP_3_10);
|
||||
pending.writeBits (count - 3, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
blTree.writeSymbol (REP_11_138);
|
||||
pending.writeBits (count - 11, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DeflaterPending pending;
|
||||
private Tree literalTree, distTree, blTree;
|
||||
|
||||
private short d_buf[];
|
||||
private byte l_buf[];
|
||||
private int last_lit;
|
||||
private int extra_bits;
|
||||
|
||||
private static short staticLCodes[];
|
||||
private static byte staticLLength[];
|
||||
private static short staticDCodes[];
|
||||
private static byte staticDLength[];
|
||||
|
||||
/**
|
||||
* Reverse the bits of a 16 bit value.
|
||||
*/
|
||||
static short bitReverse (int value)
|
||||
{
|
||||
return (short) (bit4Reverse.charAt (value & 0xf) << 12
|
||||
| bit4Reverse.charAt ((value >> 4) & 0xf) << 8
|
||||
| bit4Reverse.charAt ((value >> 8) & 0xf) << 4
|
||||
| bit4Reverse.charAt (value >> 12));
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
/* See RFC 1951 3.2.6 */
|
||||
/* Literal codes */
|
||||
staticLCodes = new short[LITERAL_NUM];
|
||||
staticLLength = new byte[LITERAL_NUM];
|
||||
int i = 0;
|
||||
while (i < 144)
|
||||
{
|
||||
staticLCodes[i] = bitReverse ((0x030 + i) << 8);
|
||||
staticLLength[i++] = 8;
|
||||
}
|
||||
while (i < 256)
|
||||
{
|
||||
staticLCodes[i] = bitReverse ((0x190 - 144 + i) << 7);
|
||||
staticLLength[i++] = 9;
|
||||
}
|
||||
while (i < 280)
|
||||
{
|
||||
staticLCodes[i] = bitReverse ((0x000 - 256 + i) << 9);
|
||||
staticLLength[i++] = 7;
|
||||
}
|
||||
while (i < LITERAL_NUM)
|
||||
{
|
||||
staticLCodes[i] = bitReverse ((0x0c0 - 280 + i) << 8);
|
||||
staticLLength[i++] = 8;
|
||||
}
|
||||
|
||||
/* Distant codes */
|
||||
staticDCodes = new short[DIST_NUM];
|
||||
staticDLength = new byte[DIST_NUM];
|
||||
for (i = 0; i < DIST_NUM; i++)
|
||||
{
|
||||
staticDCodes[i] = bitReverse (i << 11);
|
||||
staticDLength[i] = 5;
|
||||
}
|
||||
}
|
||||
|
||||
public DeflaterHuffman (DeflaterPending pending)
|
||||
{
|
||||
this.pending = pending;
|
||||
|
||||
literalTree = new Tree (LITERAL_NUM, 257, 15);
|
||||
distTree = new Tree (DIST_NUM, 1, 15);
|
||||
blTree = new Tree (BITLEN_NUM, 4, 7);
|
||||
|
||||
d_buf = new short[BUFSIZE];
|
||||
l_buf = new byte [BUFSIZE];
|
||||
}
|
||||
|
||||
public final void reset ()
|
||||
{
|
||||
last_lit = 0;
|
||||
extra_bits = 0;
|
||||
literalTree.reset ();
|
||||
distTree.reset ();
|
||||
blTree.reset ();
|
||||
}
|
||||
|
||||
private final int l_code (int len)
|
||||
{
|
||||
if (len == 255)
|
||||
return 285;
|
||||
|
||||
int code = 257;
|
||||
while (len >= 8)
|
||||
{
|
||||
code += 4;
|
||||
len >>= 1;
|
||||
}
|
||||
return code + len;
|
||||
}
|
||||
|
||||
private final int d_code (int distance)
|
||||
{
|
||||
int code = 0;
|
||||
while (distance >= 4)
|
||||
{
|
||||
code += 2;
|
||||
distance >>= 1;
|
||||
}
|
||||
return code + distance;
|
||||
}
|
||||
|
||||
public void sendAllTrees (int blTreeCodes)
|
||||
{
|
||||
blTree.buildCodes ();
|
||||
literalTree.buildCodes ();
|
||||
distTree.buildCodes ();
|
||||
pending.writeBits (literalTree.numCodes - 257, 5);
|
||||
pending.writeBits (distTree.numCodes - 1, 5);
|
||||
pending.writeBits (blTreeCodes - 4, 4);
|
||||
for (int rank = 0; rank < blTreeCodes; rank++)
|
||||
pending.writeBits (blTree.length[BL_ORDER[rank]], 3);
|
||||
literalTree.writeTree (blTree);
|
||||
distTree.writeTree (blTree);
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
blTree.checkEmpty ();
|
||||
}
|
||||
|
||||
public void compressBlock ()
|
||||
{
|
||||
for (int i = 0; i < last_lit; i++)
|
||||
{
|
||||
int litlen = l_buf[i] & 0xff;
|
||||
int dist = d_buf[i];
|
||||
if (dist-- != 0)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.print ("["+(dist+1)+","+(litlen+3)+"]: ");
|
||||
|
||||
int lc = l_code (litlen);
|
||||
literalTree.writeSymbol (lc);
|
||||
|
||||
int bits = (lc - 261) / 4;
|
||||
if (bits > 0 && bits <= 5)
|
||||
pending.writeBits (litlen & ((1 << bits) - 1), bits);
|
||||
|
||||
int dc = d_code (dist);
|
||||
distTree.writeSymbol (dc);
|
||||
|
||||
bits = dc / 2 - 1;
|
||||
if (bits > 0)
|
||||
pending.writeBits (dist & ((1 << bits) - 1), bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
if (litlen > 32 && litlen < 127)
|
||||
System.err.print ("("+(char)litlen+"): ");
|
||||
else
|
||||
System.err.print ("{"+litlen+"}: ");
|
||||
}
|
||||
literalTree.writeSymbol (litlen);
|
||||
}
|
||||
}
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.print ("EOF: ");
|
||||
literalTree.writeSymbol (EOF_SYMBOL);
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
literalTree.checkEmpty ();
|
||||
distTree.checkEmpty ();
|
||||
}
|
||||
}
|
||||
|
||||
public void flushStoredBlock (byte[] stored,
|
||||
int stored_offset, int stored_len,
|
||||
boolean lastBlock)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("Flushing stored block "+ stored_len);
|
||||
pending.writeBits ((DeflaterConstants.STORED_BLOCK << 1)
|
||||
+ (lastBlock ? 1 : 0), 3);
|
||||
pending.alignToByte ();
|
||||
pending.writeShort (stored_len);
|
||||
pending.writeShort (~stored_len);
|
||||
pending.writeBlock (stored, stored_offset, stored_len);
|
||||
reset ();
|
||||
}
|
||||
|
||||
public void flushBlock (byte[] stored, int stored_offset, int stored_len,
|
||||
boolean lastBlock)
|
||||
{
|
||||
literalTree.freqs[EOF_SYMBOL]++;
|
||||
|
||||
/* Build trees */
|
||||
literalTree.buildTree ();
|
||||
distTree.buildTree ();
|
||||
|
||||
/* Calculate bitlen frequency */
|
||||
literalTree.calcBLFreq (blTree);
|
||||
distTree.calcBLFreq (blTree);
|
||||
|
||||
/* Build bitlen tree */
|
||||
blTree.buildTree ();
|
||||
|
||||
int blTreeCodes = 4;
|
||||
for (int i = 18; i > blTreeCodes; i--)
|
||||
{
|
||||
if (blTree.length[BL_ORDER[i]] > 0)
|
||||
blTreeCodes = i+1;
|
||||
}
|
||||
int opt_len = 14 + blTreeCodes * 3 + blTree.getEncodedLength ()
|
||||
+ literalTree.getEncodedLength () + distTree.getEncodedLength ()
|
||||
+ extra_bits;
|
||||
|
||||
int static_len = extra_bits;
|
||||
for (int i = 0; i < LITERAL_NUM; i++)
|
||||
static_len += literalTree.freqs[i] * staticLLength[i];
|
||||
for (int i = 0; i < DIST_NUM; i++)
|
||||
static_len += distTree.freqs[i] * staticDLength[i];
|
||||
if (opt_len >= static_len)
|
||||
{
|
||||
/* Force static trees */
|
||||
opt_len = static_len;
|
||||
}
|
||||
|
||||
if (stored_offset >= 0 && stored_len+4 < opt_len >> 3)
|
||||
{
|
||||
/* Store Block */
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("Storing, since " + stored_len + " < " + opt_len
|
||||
+ " <= " + static_len);
|
||||
flushStoredBlock (stored, stored_offset, stored_len, lastBlock);
|
||||
}
|
||||
else if (opt_len == static_len)
|
||||
{
|
||||
/* Encode with static tree */
|
||||
pending.writeBits ((DeflaterConstants.STATIC_TREES << 1)
|
||||
+ (lastBlock ? 1 : 0), 3);
|
||||
literalTree.setStaticCodes (staticLCodes, staticLLength);
|
||||
distTree.setStaticCodes (staticDCodes, staticDLength);
|
||||
compressBlock ();
|
||||
reset ();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Encode with dynamic tree */
|
||||
pending.writeBits ((DeflaterConstants.DYN_TREES << 1)
|
||||
+ (lastBlock ? 1 : 0), 3);
|
||||
sendAllTrees (blTreeCodes);
|
||||
compressBlock ();
|
||||
reset ();
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isFull ()
|
||||
{
|
||||
return last_lit == BUFSIZE;
|
||||
}
|
||||
|
||||
public final boolean tallyLit (int lit)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
{
|
||||
if (lit > 32 && lit < 127)
|
||||
System.err.println ("("+(char)lit+")");
|
||||
else
|
||||
System.err.println ("{"+lit+"}");
|
||||
}
|
||||
d_buf[last_lit] = 0;
|
||||
l_buf[last_lit++] = (byte) lit;
|
||||
literalTree.freqs[lit]++;
|
||||
return last_lit == BUFSIZE;
|
||||
}
|
||||
|
||||
public final boolean tallyDist (int dist, int len)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("["+dist+","+len+"]");
|
||||
|
||||
d_buf[last_lit] = (short) dist;
|
||||
l_buf[last_lit++] = (byte) (len - 3);
|
||||
|
||||
int lc = l_code (len-3);
|
||||
literalTree.freqs[lc]++;
|
||||
if (lc >= 265 && lc < 285)
|
||||
extra_bits += (lc - 261) / 4;
|
||||
|
||||
int dc = d_code (dist-1);
|
||||
distTree.freqs[dc]++;
|
||||
if (dc >= 4)
|
||||
extra_bits += dc / 2 - 1;
|
||||
return last_lit == BUFSIZE;
|
||||
}
|
||||
}
|
223
src/com/classpath/zip/DeflaterOutputStream.java
Normal file
223
src/com/classpath/zip/DeflaterOutputStream.java
Normal file
@ -0,0 +1,223 @@
|
||||
/* DeflaterOutputStream.java - Output filter for compressing.
|
||||
Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/* Written using on-line Java Platform 1.2 API Specification
|
||||
* and JCL book.
|
||||
* Believed complete and correct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a special FilterOutputStream deflating the bytes that are
|
||||
* written through it. It uses the Deflater for deflating.
|
||||
*
|
||||
* A special thing to be noted is that flush() doesn't flush
|
||||
* everything in Sun's JDK, but it does so in jazzlib. This is because
|
||||
* Sun's Deflater doesn't have a way to flush() everything, without
|
||||
* finishing the stream.
|
||||
*
|
||||
* @author Tom Tromey, Jochen Hoenicke
|
||||
* @date Jan 11, 2001
|
||||
*/
|
||||
public class DeflaterOutputStream extends OutputStream
|
||||
{
|
||||
protected OutputStream out;
|
||||
/**
|
||||
* This buffer is used temporarily to retrieve the bytes from the
|
||||
* deflater and write them to the underlying output stream.
|
||||
*/
|
||||
protected byte[] buf;
|
||||
|
||||
/**
|
||||
* The deflater which is used to deflate the stream.
|
||||
*/
|
||||
protected Deflater def;
|
||||
|
||||
/**
|
||||
* Deflates everything in the def's input buffers. This will call
|
||||
* <code>def.deflate()</code> until all bytes from the input buffers
|
||||
* are processed.
|
||||
*/
|
||||
protected void deflate() throws IOException
|
||||
{
|
||||
while(!def.needsInput())
|
||||
{
|
||||
int len = def.deflate(buf, 0, buf.length);
|
||||
|
||||
// System.err.println("DOS deflated " + len + " out of " + buf.length);
|
||||
|
||||
if(len <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
|
||||
if(!def.needsInput())
|
||||
{
|
||||
throw new IOException("Can't deflate all input?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DeflaterOutputStream with a default Deflater and
|
||||
* default buffer size.
|
||||
* @param out the output stream where deflated output should be written.
|
||||
*/
|
||||
public DeflaterOutputStream(OutputStream out)
|
||||
{
|
||||
this(out, new Deflater(), 512);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DeflaterOutputStream with the given Deflater and
|
||||
* default buffer size.
|
||||
* @param out the output stream where deflated output should be written.
|
||||
* @param defl the underlying deflater.
|
||||
*/
|
||||
public DeflaterOutputStream(OutputStream out, Deflater defl)
|
||||
{
|
||||
this(out, defl, 512);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DeflaterOutputStream with the given Deflater and
|
||||
* buffer size.
|
||||
* @param out the output stream where deflated output should be written.
|
||||
* @param defl the underlying deflater.
|
||||
* @param bufsize the buffer size.
|
||||
* @exception IllegalArgumentException if bufsize isn't positive.
|
||||
*/
|
||||
public DeflaterOutputStream(OutputStream out, Deflater defl, int bufsize)
|
||||
{
|
||||
//super (out);
|
||||
this.out = out;
|
||||
if(bufsize <= 0)
|
||||
throw new IllegalArgumentException("bufsize <= 0");
|
||||
buf = new byte[bufsize];
|
||||
def = defl;
|
||||
}
|
||||
|
||||
public void setLevel(int level)
|
||||
{
|
||||
def.setLevel(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the stream by calling flush() on the deflater and then
|
||||
* on the underlying stream. This ensures that all bytes are
|
||||
* flushed. This function doesn't work in Sun's JDK, but only in
|
||||
* jazzlib.
|
||||
*/
|
||||
public void flush() throws IOException
|
||||
{
|
||||
def.flush();
|
||||
deflate();
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the stream by calling finish() on the deflater. This
|
||||
* was the only way to ensure that all bytes are flushed in Sun's
|
||||
* JDK.
|
||||
*/
|
||||
public void finish() throws IOException
|
||||
{
|
||||
def.finish();
|
||||
|
||||
while(!def.finished())
|
||||
{
|
||||
int len = def.deflate(buf, 0, buf.length);
|
||||
|
||||
if(len <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
|
||||
if(!def.finished())
|
||||
{
|
||||
throw new IOException("Can't deflate all input?");
|
||||
}
|
||||
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls finish () and closes the stream.
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
finish();
|
||||
out.close();
|
||||
}
|
||||
|
||||
public int getTotalOut()
|
||||
{
|
||||
return def.getTotalOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single byte to the compressed output stream.
|
||||
* @param bval the byte value.
|
||||
*/
|
||||
public void write(int bval) throws IOException
|
||||
{
|
||||
byte[] b = new byte[1];
|
||||
b[0] = (byte)bval;
|
||||
write(b, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a len bytes from an array to the compressed stream.
|
||||
* @param buf the byte array.
|
||||
* @param off the offset into the byte array where to start.
|
||||
* @param len the number of bytes to write.
|
||||
*/
|
||||
public void write(byte[] buf, int off, int len) throws IOException
|
||||
{
|
||||
def.setInput(buf, off, len);
|
||||
deflate();
|
||||
}
|
||||
}
|
55
src/com/classpath/zip/DeflaterPending.java
Normal file
55
src/com/classpath/zip/DeflaterPending.java
Normal file
@ -0,0 +1,55 @@
|
||||
/* java.util.zip.DeflaterPending
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/**
|
||||
* This class stores the pending output of the Deflater.
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
* @date Jan 5, 2000
|
||||
*/
|
||||
|
||||
class DeflaterPending extends PendingBuffer
|
||||
{
|
||||
public DeflaterPending ()
|
||||
{
|
||||
super (DeflaterConstants.PENDING_BUF_SIZE);
|
||||
}
|
||||
}
|
||||
|
356
src/com/classpath/zip/GZIPInputStream.java
Normal file
356
src/com/classpath/zip/GZIPInputStream.java
Normal file
@ -0,0 +1,356 @@
|
||||
/* GZIPInputStream.java - Input filter for reading gzip file
|
||||
Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* This filter stream is used to decompress a "GZIP" format stream.
|
||||
* The "GZIP" format is described in RFC 1952.
|
||||
*
|
||||
* @author John Leuner
|
||||
* @author Tom Tromey
|
||||
* @since JDK 1.1
|
||||
*/
|
||||
public class GZIPInputStream
|
||||
extends InflaterInputStream
|
||||
{
|
||||
/**
|
||||
* The magic number found at the start of a GZIP stream.
|
||||
*/
|
||||
public static final int GZIP_MAGIC = 0x8b1f;
|
||||
|
||||
/**
|
||||
* The mask for bit 0 of the flag byte.
|
||||
*/
|
||||
static final int FTEXT = 0x1;
|
||||
|
||||
/**
|
||||
* The mask for bit 1 of the flag byte.
|
||||
*/
|
||||
static final int FHCRC = 0x2;
|
||||
|
||||
/**
|
||||
* The mask for bit 2 of the flag byte.
|
||||
*/
|
||||
static final int FEXTRA = 0x4;
|
||||
|
||||
/**
|
||||
* The mask for bit 3 of the flag byte.
|
||||
*/
|
||||
static final int FNAME = 0x8;
|
||||
|
||||
/**
|
||||
* The mask for bit 4 of the flag byte.
|
||||
*/
|
||||
static final int FCOMMENT = 0x10;
|
||||
|
||||
/**
|
||||
* The CRC-32 checksum value for uncompressed data.
|
||||
*/
|
||||
protected CRC32 crc;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the end of the stream has been reached.
|
||||
*/
|
||||
protected boolean eos;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the GZIP header has been read in.
|
||||
*/
|
||||
private boolean readGZIPHeader;
|
||||
|
||||
/**
|
||||
* Creates a GZIPInputStream with the default buffer size.
|
||||
*
|
||||
* @param in The stream to read compressed data from
|
||||
* (in GZIP format).
|
||||
*
|
||||
* @throws IOException if an error occurs during an I/O operation.
|
||||
*/
|
||||
public GZIPInputStream(InputStream in)
|
||||
throws IOException
|
||||
{
|
||||
this(in, 4096);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GZIPInputStream with the specified buffer size.
|
||||
*
|
||||
* @param in The stream to read compressed data from
|
||||
* (in GZIP format).
|
||||
* @param size The size of the buffer to use.
|
||||
*
|
||||
* @throws IOException if an error occurs during an I/O operation.
|
||||
* @throws IllegalArgumentException if <code>size</code>
|
||||
* is less than or equal to 0.
|
||||
*/
|
||||
public GZIPInputStream(InputStream in, int size)
|
||||
throws IOException
|
||||
{
|
||||
super(in, new Inflater(true), size);
|
||||
crc = new CRC32();
|
||||
readHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the input stream.
|
||||
*
|
||||
* @throws IOException if an error occurs during an I/O operation.
|
||||
*/
|
||||
public void close()
|
||||
throws IOException
|
||||
{
|
||||
// Nothing to do here.
|
||||
super.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in GZIP-compressed data and stores it in uncompressed form
|
||||
* into an array of bytes. The method will block until either
|
||||
* enough input data becomes available or the compressed stream
|
||||
* reaches its end.
|
||||
*
|
||||
* @param buf the buffer into which the uncompressed data will
|
||||
* be stored.
|
||||
* @param offset the offset indicating where in <code>buf</code>
|
||||
* the uncompressed data should be placed.
|
||||
* @param len the number of uncompressed bytes to be read.
|
||||
*/
|
||||
public int read(byte[] buf, int offset, int len) throws IOException
|
||||
{
|
||||
// We first have to slurp in the GZIP header, then we feed all the
|
||||
// rest of the data to the superclass.
|
||||
//
|
||||
// As we do that we continually update the CRC32. Once the data is
|
||||
// finished, we check the CRC32.
|
||||
//
|
||||
// This means we don't need our own buffer, as everything is done
|
||||
// in the superclass.
|
||||
if(!readGZIPHeader)
|
||||
readHeader();
|
||||
|
||||
if(eos)
|
||||
return -1;
|
||||
|
||||
// System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len);
|
||||
|
||||
/* We don't have to read the header,
|
||||
* so we just grab data from the superclass.
|
||||
*/
|
||||
int numRead = super.read(buf, offset, len);
|
||||
if(numRead > 0)
|
||||
crc.update(buf, offset, numRead);
|
||||
|
||||
if(inf.finished())
|
||||
readFooter();
|
||||
return numRead;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads in the GZIP header.
|
||||
*/
|
||||
private void readHeader() throws IOException
|
||||
{
|
||||
/* 1. Check the two magic bytes */
|
||||
CRC32 headCRC = new CRC32();
|
||||
int magic = in.read();
|
||||
if(magic < 0)
|
||||
{
|
||||
eos = true;
|
||||
return;
|
||||
}
|
||||
int magic2 = in.read();
|
||||
if((magic + (magic2 << 8)) != GZIP_MAGIC)
|
||||
throw new IOException("Error in GZIP header, bad magic code");
|
||||
headCRC.update(magic);
|
||||
headCRC.update(magic2);
|
||||
|
||||
/* 2. Check the compression type (must be 8) */
|
||||
int CM = in.read();
|
||||
if(CM != Deflater.DEFLATED)
|
||||
throw new IOException("Error in GZIP header, data not in deflate format");
|
||||
headCRC.update(CM);
|
||||
|
||||
/* 3. Check the flags */
|
||||
int flags = in.read();
|
||||
if(flags < 0)
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
headCRC.update(flags);
|
||||
|
||||
/* This flag byte is divided into individual bits as follows:
|
||||
|
||||
bit 0 FTEXT
|
||||
bit 1 FHCRC
|
||||
bit 2 FEXTRA
|
||||
bit 3 FNAME
|
||||
bit 4 FCOMMENT
|
||||
bit 5 reserved
|
||||
bit 6 reserved
|
||||
bit 7 reserved
|
||||
*/
|
||||
|
||||
/* 3.1 Check the reserved bits are zero */
|
||||
if((flags & 0xd0) != 0)
|
||||
throw new IOException("Reserved flag bits in GZIP header != 0");
|
||||
|
||||
/* 4.-6. Skip the modification time, extra flags, and OS type */
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
int readByte = in.read();
|
||||
if(readByte < 0)
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
headCRC.update(readByte);
|
||||
}
|
||||
|
||||
/* 7. Read extra field */
|
||||
if((flags & FEXTRA) != 0)
|
||||
{
|
||||
/* Skip subfield id */
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
int readByte = in.read();
|
||||
if(readByte < 0)
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
headCRC.update(readByte);
|
||||
}
|
||||
if(in.read() < 0 || in.read() < 0)
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
|
||||
int len1, len2, extraLen;
|
||||
len1 = in.read();
|
||||
len2 = in.read();
|
||||
if((len1 < 0) || (len2 < 0))
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
headCRC.update(len1);
|
||||
headCRC.update(len2);
|
||||
|
||||
extraLen = (len1 << 8) | len2;
|
||||
for(int i = 0; i < extraLen; i++)
|
||||
{
|
||||
int readByte = in.read();
|
||||
if(readByte < 0)
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
headCRC.update(readByte);
|
||||
}
|
||||
}
|
||||
|
||||
/* 8. Read file name */
|
||||
if((flags & FNAME) != 0)
|
||||
{
|
||||
int readByte;
|
||||
while((readByte = in.read()) > 0)
|
||||
headCRC.update(readByte);
|
||||
if(readByte < 0)
|
||||
throw new EOFException("Early EOF in GZIP file name");
|
||||
headCRC.update(readByte);
|
||||
}
|
||||
|
||||
/* 9. Read comment */
|
||||
if((flags & FCOMMENT) != 0)
|
||||
{
|
||||
int readByte;
|
||||
while((readByte = in.read()) > 0)
|
||||
headCRC.update(readByte);
|
||||
|
||||
if(readByte < 0)
|
||||
throw new EOFException("Early EOF in GZIP comment");
|
||||
headCRC.update(readByte);
|
||||
}
|
||||
|
||||
/* 10. Read header CRC */
|
||||
if((flags & FHCRC) != 0)
|
||||
{
|
||||
int tempByte;
|
||||
int crcval = in.read();
|
||||
if(crcval < 0)
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
|
||||
tempByte = in.read();
|
||||
if(tempByte < 0)
|
||||
throw new EOFException("Early EOF in GZIP header");
|
||||
|
||||
crcval = (crcval << 8) | tempByte;
|
||||
if(crcval != ((int)headCRC.getValue() & 0xffff))
|
||||
throw new IOException("Header CRC value mismatch");
|
||||
}
|
||||
|
||||
readGZIPHeader = true;
|
||||
//System.err.println("Read GZIP header");
|
||||
}
|
||||
|
||||
private void readFooter() throws IOException
|
||||
{
|
||||
byte[] footer = new byte[8];
|
||||
int avail = inf.getRemaining();
|
||||
if(avail > 8)
|
||||
avail = 8;
|
||||
System.arraycopy(buf, len - inf.getRemaining(), footer, 0, avail);
|
||||
int needed = 8 - avail;
|
||||
while(needed > 0)
|
||||
{
|
||||
int count = in.read(footer, 8 - needed, needed);
|
||||
if(count <= 0)
|
||||
throw new EOFException("Early EOF in GZIP footer");
|
||||
needed -= count; //Jewel Jan 16
|
||||
}
|
||||
|
||||
int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8)
|
||||
| ((footer[2] & 0xff) << 16) | (footer[3] << 24);
|
||||
if(crcval != (int)crc.getValue())
|
||||
throw new IOException("GZIP crc sum mismatch, theirs \""
|
||||
+ Integer.toHexString(crcval)
|
||||
+ "\" and ours \""
|
||||
+ Integer.toHexString((int)crc.getValue()));
|
||||
|
||||
int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8)
|
||||
| ((footer[6] & 0xff) << 16) | (footer[7] << 24);
|
||||
if(total != inf.getTotalOut())
|
||||
throw new IOException("Number of bytes mismatch");
|
||||
|
||||
/* FIXME" XXX Should we support multiple members.
|
||||
* Difficult, since there may be some bytes still in buf
|
||||
*/
|
||||
eos = true;
|
||||
}
|
||||
}
|
152
src/com/classpath/zip/GZIPOutputStream.java
Normal file
152
src/com/classpath/zip/GZIPOutputStream.java
Normal file
@ -0,0 +1,152 @@
|
||||
/* GZIPOutputStream.java - Create a file in gzip format
|
||||
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* This filter stream is used to compress a stream into a "GZIP" stream.
|
||||
* The "GZIP" format is described in RFC 1952.
|
||||
*
|
||||
* @author John Leuner
|
||||
* @author Tom Tromey
|
||||
* @since JDK 1.1
|
||||
*/
|
||||
|
||||
/* Written using on-line Java Platform 1.2 API Specification
|
||||
* and JCL book.
|
||||
* Believed complete and correct.
|
||||
*/
|
||||
|
||||
public class GZIPOutputStream extends DeflaterOutputStream
|
||||
{
|
||||
/**
|
||||
* CRC-32 value for uncompressed data
|
||||
*/
|
||||
protected CRC32 crc;
|
||||
|
||||
/**
|
||||
* Creates a GZIPOutputStream with the default buffer size
|
||||
*
|
||||
* @param out The stream to read data (to be compressed) from
|
||||
*
|
||||
*/
|
||||
public GZIPOutputStream(OutputStream out) throws IOException
|
||||
{
|
||||
this(out, 4096);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GZIPOutputStream with the specified buffer size
|
||||
*
|
||||
* @param out The stream to read compressed data from
|
||||
* @param size Size of the buffer to use
|
||||
*/
|
||||
public GZIPOutputStream(OutputStream out, int size) throws IOException
|
||||
{
|
||||
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size);
|
||||
crc = new CRC32();
|
||||
int mod_time = (int)(System.currentTimeMillis() / 1000L);
|
||||
byte[] gzipHeader =
|
||||
{
|
||||
/* The two magic bytes */
|
||||
(byte) GZIPInputStream.GZIP_MAGIC,
|
||||
(byte) (GZIPInputStream.GZIP_MAGIC >> 8),
|
||||
|
||||
/* The compression type */
|
||||
(byte) Deflater.DEFLATED,
|
||||
|
||||
/* The flags (not set) */
|
||||
0,
|
||||
|
||||
/* The modification time */
|
||||
(byte) mod_time, (byte) (mod_time >> 8),
|
||||
(byte) (mod_time >> 16), (byte) (mod_time >> 24),
|
||||
|
||||
/* The extra flags */
|
||||
0,
|
||||
|
||||
/* The OS type (unknown) */
|
||||
(byte) 255
|
||||
};
|
||||
|
||||
out.write(gzipHeader);
|
||||
// System.err.println("wrote GZIP header (" + gzipHeader.length + " bytes )");
|
||||
}
|
||||
|
||||
public synchronized void write(byte[] buf, int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
super.write(buf, off, len);
|
||||
crc.update(buf, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes remaining compressed output data to the output stream
|
||||
* and closes it.
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
finish();
|
||||
out.close();
|
||||
}
|
||||
|
||||
public void finish() throws IOException
|
||||
{
|
||||
super.finish();
|
||||
|
||||
int totalin = def.getTotalIn();
|
||||
int crcval = (int)(crc.getValue() & 0xffffffff);
|
||||
|
||||
// System.err.println("CRC val is " + Integer.toHexString( crcval ) + " and length " + Integer.toHexString(totalin));
|
||||
|
||||
byte[] gzipFooter =
|
||||
{
|
||||
(byte) crcval, (byte) (crcval >> 8),
|
||||
(byte) (crcval >> 16), (byte) (crcval >> 24),
|
||||
|
||||
(byte) totalin, (byte) (totalin >> 8),
|
||||
(byte) (totalin >> 16), (byte) (totalin >> 24)
|
||||
};
|
||||
|
||||
out.write(gzipFooter);
|
||||
// System.err.println("wrote GZIP trailer (" + gzipFooter.length + " bytes )");
|
||||
}
|
||||
}
|
743
src/com/classpath/zip/Inflater.java
Normal file
743
src/com/classpath/zip/Inflater.java
Normal file
@ -0,0 +1,743 @@
|
||||
/* Inflater.java - Decompress a data stream
|
||||
Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/* Written using on-line Java Platform 1.2 API Specification
|
||||
* and JCL book.
|
||||
* Believed complete and correct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Inflater is used to decompress data that has been compressed according
|
||||
* to the "deflate" standard described in rfc1950.
|
||||
*
|
||||
* The usage is as following. First you have to set some input with
|
||||
* <code>setInput()</code>, then inflate() it. If inflate doesn't
|
||||
* inflate any bytes there may be three reasons:
|
||||
* <ul>
|
||||
* <li>needsInput() returns true because the input buffer is empty.
|
||||
* You have to provide more input with <code>setInput()</code>.
|
||||
* NOTE: needsInput() also returns true when, the stream is finished.
|
||||
* </li>
|
||||
* <li>needsDictionary() returns true, you have to provide a preset
|
||||
* dictionary with <code>setDictionary()</code>.</li>
|
||||
* <li>finished() returns true, the inflater has finished.</li>
|
||||
* </ul>
|
||||
* Once the first output byte is produced, a dictionary will not be
|
||||
* needed at a later stage.
|
||||
*
|
||||
* @author John Leuner, Jochen Hoenicke
|
||||
* @author Tom Tromey
|
||||
* @date May 17, 1999
|
||||
* @since JDK 1.1
|
||||
*/
|
||||
public class Inflater
|
||||
{
|
||||
/* Copy lengths for literal codes 257..285 */
|
||||
private static final int CPLENS[] =
|
||||
{
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
||||
};
|
||||
|
||||
/* Extra bits for literal codes 257..285 */
|
||||
private static final int CPLEXT[] =
|
||||
{
|
||||
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, 0
|
||||
};
|
||||
|
||||
/* Copy offsets for distance codes 0..29 */
|
||||
private static final int CPDIST[] = {
|
||||
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||
8193, 12289, 16385, 24577
|
||||
};
|
||||
|
||||
/* Extra bits for distance codes */
|
||||
private static final int CPDEXT[] = {
|
||||
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
|
||||
};
|
||||
|
||||
/* This are the state in which the inflater can be. */
|
||||
private static final int DECODE_HEADER = 0;
|
||||
private static final int DECODE_DICT = 1;
|
||||
private static final int DECODE_BLOCKS = 2;
|
||||
private static final int DECODE_STORED_LEN1 = 3;
|
||||
private static final int DECODE_STORED_LEN2 = 4;
|
||||
private static final int DECODE_STORED = 5;
|
||||
private static final int DECODE_DYN_HEADER = 6;
|
||||
private static final int DECODE_HUFFMAN = 7;
|
||||
private static final int DECODE_HUFFMAN_LENBITS = 8;
|
||||
private static final int DECODE_HUFFMAN_DIST = 9;
|
||||
private static final int DECODE_HUFFMAN_DISTBITS = 10;
|
||||
private static final int DECODE_CHKSUM = 11;
|
||||
private static final int FINISHED = 12;
|
||||
|
||||
/** This variable contains the current state. */
|
||||
private int mode;
|
||||
|
||||
/**
|
||||
* The adler checksum of the dictionary or of the decompressed
|
||||
* stream, as it is written in the header resp. footer of the
|
||||
* compressed stream. <br>
|
||||
*
|
||||
* Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
|
||||
*/
|
||||
private int readAdler;
|
||||
/**
|
||||
* The number of bits needed to complete the current state. This
|
||||
* is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
|
||||
* DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.
|
||||
*/
|
||||
private int neededBits;
|
||||
private int repLength, repDist;
|
||||
private int uncomprLen;
|
||||
/**
|
||||
* True, if the last block flag was set in the last block of the
|
||||
* inflated stream. This means that the stream ends after the
|
||||
* current block.
|
||||
*/
|
||||
private boolean isLastBlock;
|
||||
|
||||
/**
|
||||
* The total number of inflated bytes.
|
||||
*/
|
||||
private long totalOut;
|
||||
/**
|
||||
* The total number of bytes set with setInput(). This is not the
|
||||
* value returned by getTotalIn(), since this also includes the
|
||||
* unprocessed input.
|
||||
*/
|
||||
private long totalIn;
|
||||
/**
|
||||
* This variable stores the nowrap flag that was given to the constructor.
|
||||
* True means, that the inflated stream doesn't contain a header nor the
|
||||
* checksum in the footer.
|
||||
*/
|
||||
private boolean nowrap;
|
||||
|
||||
private StreamManipulator input;
|
||||
private OutputWindow outputWindow;
|
||||
private InflaterDynHeader dynHeader;
|
||||
private InflaterHuffmanTree litlenTree, distTree;
|
||||
private Adler32 adler;
|
||||
|
||||
/**
|
||||
* Creates a new inflater.
|
||||
*/
|
||||
public Inflater()
|
||||
{
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new inflater.
|
||||
* @param nowrap true if no header and checksum field appears in the
|
||||
* stream. This is used for GZIPed input. For compatibility with
|
||||
* Sun JDK you should provide one byte of input more than needed in
|
||||
* this case.
|
||||
*/
|
||||
public Inflater(boolean nowrap)
|
||||
{
|
||||
this.nowrap = nowrap;
|
||||
this.adler = new Adler32();
|
||||
input = new StreamManipulator();
|
||||
outputWindow = new OutputWindow();
|
||||
mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Finalizes this object.
|
||||
// */
|
||||
// protected void finalize ()
|
||||
// {
|
||||
// /* Exists only for compatibility */
|
||||
// }
|
||||
|
||||
/**
|
||||
* Frees all objects allocated by the inflater. There's no reason
|
||||
* to call this, since you can just rely on garbage collection (even
|
||||
* for the Sun implementation). Exists only for compatibility
|
||||
* with Sun's JDK, where the compressor allocates native memory.
|
||||
* If you call any method (even reset) afterwards the behaviour is
|
||||
* <i>undefined</i>.
|
||||
*/
|
||||
public void end()
|
||||
{
|
||||
outputWindow = null;
|
||||
input = null;
|
||||
dynHeader = null;
|
||||
litlenTree = null;
|
||||
distTree = null;
|
||||
adler = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the inflater has finished. This means, that no
|
||||
* input is needed and no output can be produced.
|
||||
*/
|
||||
public boolean finished()
|
||||
{
|
||||
return mode == FINISHED && outputWindow.getAvailable() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the adler checksum. This is either the checksum of all
|
||||
* uncompressed bytes returned by inflate(), or if needsDictionary()
|
||||
* returns true (and thus no output was yet produced) this is the
|
||||
* adler checksum of the expected dictionary.
|
||||
* @returns the adler checksum.
|
||||
*/
|
||||
public int getAdler()
|
||||
{
|
||||
return needsDictionary() ? readAdler : (int)adler.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of unprocessed input. Useful, if the end of the
|
||||
* stream is reached and you want to further process the bytes after
|
||||
* the deflate stream.
|
||||
* @return the number of bytes of the input which were not processed.
|
||||
*/
|
||||
public int getRemaining()
|
||||
{
|
||||
return input.getAvailableBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of processed compressed input bytes.
|
||||
* @return the total number of bytes of processed input bytes.
|
||||
*/
|
||||
/* @Deprecated */
|
||||
public int getTotalIn()
|
||||
{
|
||||
return (int)(totalIn - getRemaining());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of processed compressed input bytes.
|
||||
* @return the total number of bytes of processed input bytes.
|
||||
* @since 1.5
|
||||
*/
|
||||
public long getBytesRead()
|
||||
{
|
||||
return totalIn - getRemaining();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of output bytes returned by inflate().
|
||||
* @return the total number of output bytes.
|
||||
*/
|
||||
/* @Deprecated */
|
||||
public int getTotalOut()
|
||||
{
|
||||
return (int)totalOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of output bytes returned by inflate().
|
||||
* @return the total number of output bytes.
|
||||
* @since 1.5
|
||||
*/
|
||||
public long getBytesWritten()
|
||||
{
|
||||
return totalOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the compressed stream to the output buffer. If this
|
||||
* returns 0, you should check, whether needsDictionary(),
|
||||
* needsInput() or finished() returns true, to determine why no
|
||||
* further output is produced.
|
||||
* @param buf the output buffer.
|
||||
* @return the number of bytes written to the buffer, 0 if no further
|
||||
* output can be produced.
|
||||
* @exception DataFormatException if deflated stream is invalid.
|
||||
* @exception IllegalArgumentException if buf has length 0.
|
||||
*/
|
||||
public int inflate(byte[] buf) throws DataFormatException
|
||||
{
|
||||
return inflate(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the compressed stream to the output buffer. If this
|
||||
* returns 0, you should check, whether needsDictionary(),
|
||||
* needsInput() or finished() returns true, to determine why no
|
||||
* further output is produced.
|
||||
* @param buf the output buffer.
|
||||
* @param off the offset into buffer where the output should start.
|
||||
* @param len the maximum length of the output.
|
||||
* @return the number of bytes written to the buffer, 0 if no further
|
||||
* output can be produced.
|
||||
* @exception DataFormatException if deflated stream is invalid.
|
||||
* @exception IndexOutOfBoundsException if the off and/or len are wrong.
|
||||
*/
|
||||
public int inflate(byte[] buf, int off, int len) throws DataFormatException
|
||||
{
|
||||
/* Special case: len may be zero */
|
||||
if(len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for correct buff, off, len triple */
|
||||
if(0 > off || off > off + len || off + len > buf.length)
|
||||
{
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int more;
|
||||
do
|
||||
{
|
||||
if(mode != DECODE_CHKSUM)
|
||||
{
|
||||
/* Don't give away any output, if we are waiting for the
|
||||
* checksum in the input stream.
|
||||
*
|
||||
* With this trick we have always:
|
||||
* needsInput() and not finished()
|
||||
* implies more output can be produced.
|
||||
*/
|
||||
more = outputWindow.copyOutput(buf, off, len);
|
||||
adler.update(buf, off, more);
|
||||
off += more;
|
||||
count += more;
|
||||
totalOut += more;
|
||||
len -= more;
|
||||
if(len == 0)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
while(decode() || (outputWindow.getAvailable() > 0
|
||||
&& mode != DECODE_CHKSUM));
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if a preset dictionary is needed to inflate the input.
|
||||
*/
|
||||
public boolean needsDictionary()
|
||||
{
|
||||
return mode == DECODE_DICT && neededBits == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the input buffer is empty.
|
||||
* You should then call setInput(). <br>
|
||||
*
|
||||
* <em>NOTE</em>: This method also returns true when the stream is finished.
|
||||
*/
|
||||
public boolean needsInput()
|
||||
{
|
||||
return input.needsInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the inflater so that a new stream can be decompressed. All
|
||||
* pending input and output will be discarded.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
|
||||
totalIn = totalOut = 0;
|
||||
input.reset();
|
||||
outputWindow.reset();
|
||||
dynHeader = null;
|
||||
litlenTree = null;
|
||||
distTree = null;
|
||||
isLastBlock = false;
|
||||
adler.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preset dictionary. This should only be called, if
|
||||
* needsDictionary() returns true and it should set the same
|
||||
* dictionary, that was used for deflating. The getAdler()
|
||||
* function returns the checksum of the dictionary needed.
|
||||
* @param buffer the dictionary.
|
||||
* @exception IllegalStateException if no dictionary is needed.
|
||||
* @exception IllegalArgumentException if the dictionary checksum is
|
||||
* wrong.
|
||||
*/
|
||||
public void setDictionary(byte[] buffer)
|
||||
{
|
||||
setDictionary(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preset dictionary. This should only be called, if
|
||||
* needsDictionary() returns true and it should set the same
|
||||
* dictionary, that was used for deflating. The getAdler()
|
||||
* function returns the checksum of the dictionary needed.
|
||||
* @param buffer the dictionary.
|
||||
* @param off the offset into buffer where the dictionary starts.
|
||||
* @param len the length of the dictionary.
|
||||
* @exception IllegalStateException if no dictionary is needed.
|
||||
* @exception IllegalArgumentException if the dictionary checksum is
|
||||
* wrong.
|
||||
* @exception IndexOutOfBoundsException if the off and/or len are wrong.
|
||||
*/
|
||||
public void setDictionary(byte[] buffer, int off, int len)
|
||||
{
|
||||
if(!needsDictionary())
|
||||
throw new IllegalStateException();
|
||||
|
||||
adler.update(buffer, off, len);
|
||||
if((int)adler.getValue() != readAdler)
|
||||
throw new IllegalArgumentException("Wrong adler checksum");
|
||||
adler.reset();
|
||||
outputWindow.copyDict(buffer, off, len);
|
||||
mode = DECODE_BLOCKS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input. This should only be called, if needsInput()
|
||||
* returns true.
|
||||
* @param buf the input.
|
||||
* @exception IllegalStateException if no input is needed.
|
||||
*/
|
||||
public void setInput(byte[] buf)
|
||||
{
|
||||
setInput(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input. This should only be called, if needsInput()
|
||||
* returns true.
|
||||
* @param buf the input.
|
||||
* @param off the offset into buffer where the input starts.
|
||||
* @param len the length of the input.
|
||||
* @exception IllegalStateException if no input is needed.
|
||||
* @exception IndexOutOfBoundsException if the off and/or len are wrong.
|
||||
*/
|
||||
public void setInput(byte[] buf, int off, int len)
|
||||
{
|
||||
input.setInput(buf, off, len);
|
||||
totalIn += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the deflate header.
|
||||
* @return false if more input is needed.
|
||||
* @exception DataFormatException if header is invalid.
|
||||
*/
|
||||
private boolean decodeHeader() throws DataFormatException
|
||||
{
|
||||
int header = input.peekBits(16);
|
||||
if(header < 0)
|
||||
return false;
|
||||
input.dropBits(16);
|
||||
|
||||
/* The header is written in "wrong" byte order */
|
||||
header = ((header << 8) | (header >> 8)) & 0xffff;
|
||||
if(header % 31 != 0)
|
||||
throw new DataFormatException("Header checksum illegal");
|
||||
|
||||
if((header & 0x0f00) != (Deflater.DEFLATED << 8))
|
||||
throw new DataFormatException("Compression Method unknown");
|
||||
|
||||
/* Maximum size of the backwards window in bits.
|
||||
* We currently ignore this, but we could use it to make the
|
||||
* inflater window more space efficient. On the other hand the
|
||||
* full window (15 bits) is needed most times, anyway.
|
||||
int max_wbits = ((header & 0x7000) >> 12) + 8;
|
||||
*/
|
||||
|
||||
if((header & 0x0020) == 0) // Dictionary flag?
|
||||
{
|
||||
mode = DECODE_BLOCKS;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = DECODE_DICT;
|
||||
neededBits = 32;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the dictionary checksum after the deflate header.
|
||||
* @return false if more input is needed.
|
||||
*/
|
||||
private boolean decodeDict()
|
||||
{
|
||||
while(neededBits > 0)
|
||||
{
|
||||
int dictByte = input.peekBits(8);
|
||||
if(dictByte < 0)
|
||||
return false;
|
||||
input.dropBits(8);
|
||||
readAdler = (readAdler << 8) | dictByte;
|
||||
neededBits -= 8;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the huffman encoded symbols in the input stream.
|
||||
* @return false if more input is needed, true if output window is
|
||||
* full or the current block ends.
|
||||
* @exception DataFormatException if deflated stream is invalid.
|
||||
*/
|
||||
private boolean decodeHuffman() throws DataFormatException
|
||||
{
|
||||
int free = outputWindow.getFreeSpace();
|
||||
while(free >= 258)
|
||||
{
|
||||
int symbol;
|
||||
switch(mode)
|
||||
{
|
||||
case DECODE_HUFFMAN:
|
||||
/* This is the inner loop so it is optimized a bit */
|
||||
while(((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0)
|
||||
{
|
||||
outputWindow.write(symbol);
|
||||
if(--free < 258)
|
||||
return true;
|
||||
}
|
||||
if(symbol < 257)
|
||||
{
|
||||
if(symbol < 0)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
/* symbol == 256: end of block */
|
||||
distTree = null;
|
||||
litlenTree = null;
|
||||
mode = DECODE_BLOCKS;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
repLength = CPLENS[symbol - 257];
|
||||
neededBits = CPLEXT[symbol - 257];
|
||||
}
|
||||
catch(ArrayIndexOutOfBoundsException ex)
|
||||
{
|
||||
throw new DataFormatException("Illegal rep length code");
|
||||
}
|
||||
/* fall through */
|
||||
case DECODE_HUFFMAN_LENBITS:
|
||||
if(neededBits > 0)
|
||||
{
|
||||
mode = DECODE_HUFFMAN_LENBITS;
|
||||
int i = input.peekBits(neededBits);
|
||||
if(i < 0)
|
||||
return false;
|
||||
input.dropBits(neededBits);
|
||||
repLength += i;
|
||||
}
|
||||
mode = DECODE_HUFFMAN_DIST;
|
||||
/* fall through */
|
||||
case DECODE_HUFFMAN_DIST:
|
||||
symbol = distTree.getSymbol(input);
|
||||
if(symbol < 0)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
repDist = CPDIST[symbol];
|
||||
neededBits = CPDEXT[symbol];
|
||||
}
|
||||
catch(ArrayIndexOutOfBoundsException ex)
|
||||
{
|
||||
throw new DataFormatException("Illegal rep dist code");
|
||||
}
|
||||
/* fall through */
|
||||
case DECODE_HUFFMAN_DISTBITS:
|
||||
if(neededBits > 0)
|
||||
{
|
||||
mode = DECODE_HUFFMAN_DISTBITS;
|
||||
int i = input.peekBits(neededBits);
|
||||
if(i < 0)
|
||||
return false;
|
||||
input.dropBits(neededBits);
|
||||
repDist += i;
|
||||
}
|
||||
outputWindow.repeat(repLength, repDist);
|
||||
free -= repLength;
|
||||
mode = DECODE_HUFFMAN;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the adler checksum after the deflate stream.
|
||||
* @return false if more input is needed.
|
||||
* @exception DataFormatException if checksum doesn't match.
|
||||
*/
|
||||
private boolean decodeChksum() throws DataFormatException
|
||||
{
|
||||
while(neededBits > 0)
|
||||
{
|
||||
int chkByte = input.peekBits(8);
|
||||
if(chkByte < 0)
|
||||
return false;
|
||||
input.dropBits(8);
|
||||
readAdler = (readAdler << 8) | chkByte;
|
||||
neededBits -= 8;
|
||||
}
|
||||
if((int)adler.getValue() != readAdler)
|
||||
throw new DataFormatException("Adler chksum doesn't match: "
|
||||
+ Integer.toHexString((int)adler.getValue())
|
||||
+ " vs. " + Integer.toHexString(readAdler));
|
||||
mode = FINISHED;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the deflated stream.
|
||||
* @return false if more input is needed, or if finished.
|
||||
* @exception DataFormatException if deflated stream is invalid.
|
||||
*/
|
||||
private boolean decode() throws DataFormatException
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case DECODE_HEADER:
|
||||
return decodeHeader();
|
||||
case DECODE_DICT:
|
||||
return decodeDict();
|
||||
case DECODE_CHKSUM:
|
||||
return decodeChksum();
|
||||
|
||||
case DECODE_BLOCKS:
|
||||
if(isLastBlock)
|
||||
{
|
||||
if(nowrap)
|
||||
{
|
||||
mode = FINISHED;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
input.skipToByteBoundary();
|
||||
neededBits = 32;
|
||||
mode = DECODE_CHKSUM;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int type = input.peekBits(3);
|
||||
if(type < 0)
|
||||
return false;
|
||||
input.dropBits(3);
|
||||
|
||||
if((type & 1) != 0)
|
||||
isLastBlock = true;
|
||||
switch(type >> 1)
|
||||
{
|
||||
case DeflaterConstants.STORED_BLOCK:
|
||||
input.skipToByteBoundary();
|
||||
mode = DECODE_STORED_LEN1;
|
||||
break;
|
||||
case DeflaterConstants.STATIC_TREES:
|
||||
litlenTree = InflaterHuffmanTree.defLitLenTree;
|
||||
distTree = InflaterHuffmanTree.defDistTree;
|
||||
mode = DECODE_HUFFMAN;
|
||||
break;
|
||||
case DeflaterConstants.DYN_TREES:
|
||||
dynHeader = new InflaterDynHeader();
|
||||
mode = DECODE_DYN_HEADER;
|
||||
break;
|
||||
default:
|
||||
throw new DataFormatException("Unknown block type " + type);
|
||||
}
|
||||
return true;
|
||||
|
||||
case DECODE_STORED_LEN1:
|
||||
{
|
||||
if((uncomprLen = input.peekBits(16)) < 0)
|
||||
return false;
|
||||
input.dropBits(16);
|
||||
mode = DECODE_STORED_LEN2;
|
||||
}
|
||||
/* fall through */
|
||||
case DECODE_STORED_LEN2:
|
||||
{
|
||||
int nlen = input.peekBits(16);
|
||||
if(nlen < 0)
|
||||
return false;
|
||||
input.dropBits(16);
|
||||
if(nlen != (uncomprLen ^ 0xffff))
|
||||
throw new DataFormatException("broken uncompressed block");
|
||||
mode = DECODE_STORED;
|
||||
}
|
||||
/* fall through */
|
||||
case DECODE_STORED:
|
||||
{
|
||||
int more = outputWindow.copyStored(input, uncomprLen);
|
||||
uncomprLen -= more;
|
||||
if(uncomprLen == 0)
|
||||
{
|
||||
mode = DECODE_BLOCKS;
|
||||
return true;
|
||||
}
|
||||
return !input.needsInput();
|
||||
}
|
||||
|
||||
case DECODE_DYN_HEADER:
|
||||
if(!dynHeader.decode(input))
|
||||
return false;
|
||||
litlenTree = dynHeader.buildLitLenTree();
|
||||
distTree = dynHeader.buildDistTree();
|
||||
mode = DECODE_HUFFMAN;
|
||||
/* fall through */
|
||||
case DECODE_HUFFMAN:
|
||||
case DECODE_HUFFMAN_LENBITS:
|
||||
case DECODE_HUFFMAN_DIST:
|
||||
case DECODE_HUFFMAN_DISTBITS:
|
||||
return decodeHuffman();
|
||||
case FINISHED:
|
||||
return false;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
204
src/com/classpath/zip/InflaterDynHeader.java
Normal file
204
src/com/classpath/zip/InflaterDynHeader.java
Normal file
@ -0,0 +1,204 @@
|
||||
/* java.util.zip.InflaterDynHeader
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
class InflaterDynHeader
|
||||
{
|
||||
private static final int LNUM = 0;
|
||||
private static final int DNUM = 1;
|
||||
private static final int BLNUM = 2;
|
||||
private static final int BLLENS = 3;
|
||||
private static final int LENS = 4;
|
||||
private static final int REPS = 5;
|
||||
|
||||
private static final int repMin[] = { 3, 3, 11 };
|
||||
private static final int repBits[] = { 2, 3, 7 };
|
||||
|
||||
|
||||
private byte[] blLens;
|
||||
private byte[] litdistLens;
|
||||
|
||||
private InflaterHuffmanTree blTree;
|
||||
|
||||
private int mode;
|
||||
private int lnum, dnum, blnum, num;
|
||||
private int repSymbol;
|
||||
private byte lastLen;
|
||||
private int ptr;
|
||||
|
||||
private static final int[] BL_ORDER =
|
||||
{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
|
||||
public InflaterDynHeader()
|
||||
{
|
||||
}
|
||||
|
||||
public boolean decode(StreamManipulator input) throws DataFormatException
|
||||
{
|
||||
decode_loop:
|
||||
for (;;)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case LNUM:
|
||||
lnum = input.peekBits(5);
|
||||
if (lnum < 0)
|
||||
return false;
|
||||
lnum += 257;
|
||||
input.dropBits(5);
|
||||
// System.err.println("LNUM: "+lnum);
|
||||
mode = DNUM;
|
||||
/* fall through */
|
||||
case DNUM:
|
||||
dnum = input.peekBits(5);
|
||||
if (dnum < 0)
|
||||
return false;
|
||||
dnum++;
|
||||
input.dropBits(5);
|
||||
// System.err.println("DNUM: "+dnum);
|
||||
num = lnum+dnum;
|
||||
litdistLens = new byte[num];
|
||||
mode = BLNUM;
|
||||
/* fall through */
|
||||
case BLNUM:
|
||||
blnum = input.peekBits(4);
|
||||
if (blnum < 0)
|
||||
return false;
|
||||
blnum += 4;
|
||||
input.dropBits(4);
|
||||
blLens = new byte[19];
|
||||
ptr = 0;
|
||||
// System.err.println("BLNUM: "+blnum);
|
||||
mode = BLLENS;
|
||||
/* fall through */
|
||||
case BLLENS:
|
||||
while (ptr < blnum)
|
||||
{
|
||||
int len = input.peekBits(3);
|
||||
if (len < 0)
|
||||
return false;
|
||||
input.dropBits(3);
|
||||
// System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);
|
||||
blLens[BL_ORDER[ptr]] = (byte) len;
|
||||
ptr++;
|
||||
}
|
||||
blTree = new InflaterHuffmanTree(blLens);
|
||||
blLens = null;
|
||||
ptr = 0;
|
||||
mode = LENS;
|
||||
/* fall through */
|
||||
case LENS:
|
||||
{
|
||||
int symbol;
|
||||
while (((symbol = blTree.getSymbol(input)) & ~15) == 0)
|
||||
{
|
||||
/* Normal case: symbol in [0..15] */
|
||||
|
||||
// System.err.println("litdistLens["+ptr+"]: "+symbol);
|
||||
litdistLens[ptr++] = lastLen = (byte) symbol;
|
||||
|
||||
if (ptr == num)
|
||||
{
|
||||
/* Finished */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* need more input ? */
|
||||
if (symbol < 0)
|
||||
return false;
|
||||
|
||||
/* otherwise repeat code */
|
||||
if (symbol >= 17)
|
||||
{
|
||||
/* repeat zero */
|
||||
// System.err.println("repeating zero");
|
||||
lastLen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ptr == 0)
|
||||
throw new DataFormatException();
|
||||
}
|
||||
repSymbol = symbol-16;
|
||||
mode = REPS;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case REPS:
|
||||
{
|
||||
int bits = repBits[repSymbol];
|
||||
int count = input.peekBits(bits);
|
||||
if (count < 0)
|
||||
return false;
|
||||
input.dropBits(bits);
|
||||
count += repMin[repSymbol];
|
||||
// System.err.println("litdistLens repeated: "+count);
|
||||
|
||||
if (ptr + count > num)
|
||||
throw new DataFormatException();
|
||||
while (count-- > 0)
|
||||
litdistLens[ptr++] = lastLen;
|
||||
|
||||
if (ptr == num)
|
||||
{
|
||||
/* Finished */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mode = LENS;
|
||||
continue decode_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InflaterHuffmanTree buildLitLenTree() throws DataFormatException
|
||||
{
|
||||
byte[] litlenLens = new byte[lnum];
|
||||
System.arraycopy(litdistLens, 0, litlenLens, 0, lnum);
|
||||
return new InflaterHuffmanTree(litlenLens);
|
||||
}
|
||||
|
||||
public InflaterHuffmanTree buildDistTree() throws DataFormatException
|
||||
{
|
||||
byte[] distLens = new byte[dnum];
|
||||
System.arraycopy(litdistLens, lnum, distLens, 0, dnum);
|
||||
return new InflaterHuffmanTree(distLens);
|
||||
}
|
||||
}
|
218
src/com/classpath/zip/InflaterHuffmanTree.java
Normal file
218
src/com/classpath/zip/InflaterHuffmanTree.java
Normal file
@ -0,0 +1,218 @@
|
||||
/* InflaterHuffmanTree.java --
|
||||
Copyright (C) 2001, 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
class InflaterHuffmanTree
|
||||
{
|
||||
private static final int MAX_BITLEN = 15;
|
||||
|
||||
private short[] tree;
|
||||
|
||||
static InflaterHuffmanTree defLitLenTree, defDistTree;
|
||||
|
||||
static
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] codeLengths = new byte[288];
|
||||
int i = 0;
|
||||
while (i < 144)
|
||||
codeLengths[i++] = 8;
|
||||
while (i < 256)
|
||||
codeLengths[i++] = 9;
|
||||
while (i < 280)
|
||||
codeLengths[i++] = 7;
|
||||
while (i < 288)
|
||||
codeLengths[i++] = 8;
|
||||
defLitLenTree = new InflaterHuffmanTree(codeLengths);
|
||||
|
||||
codeLengths = new byte[32];
|
||||
i = 0;
|
||||
while (i < 32)
|
||||
codeLengths[i++] = 5;
|
||||
defDistTree = new InflaterHuffmanTree(codeLengths);
|
||||
}
|
||||
catch (DataFormatException ex)
|
||||
{
|
||||
// throw new Exception
|
||||
// ("InflaterHuffmanTree: static tree length illegal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Huffman tree from the array of code lengths.
|
||||
*
|
||||
* @param codeLengths the array of code lengths
|
||||
*/
|
||||
InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException
|
||||
{
|
||||
buildTree(codeLengths);
|
||||
}
|
||||
|
||||
private void buildTree(byte[] codeLengths) throws DataFormatException
|
||||
{
|
||||
int[] blCount = new int[MAX_BITLEN+1];
|
||||
int[] nextCode = new int[MAX_BITLEN+1];
|
||||
for (int i = 0; i < codeLengths.length; i++)
|
||||
{
|
||||
int bits = codeLengths[i];
|
||||
if (bits > 0)
|
||||
blCount[bits]++;
|
||||
}
|
||||
|
||||
int code = 0;
|
||||
int treeSize = 512;
|
||||
for (int bits = 1; bits <= MAX_BITLEN; bits++)
|
||||
{
|
||||
nextCode[bits] = code;
|
||||
code += blCount[bits] << (16 - bits);
|
||||
if (bits >= 10)
|
||||
{
|
||||
/* We need an extra table for bit lengths >= 10. */
|
||||
int start = nextCode[bits] & 0x1ff80;
|
||||
int end = code & 0x1ff80;
|
||||
treeSize += (end - start) >> (16 - bits);
|
||||
}
|
||||
}
|
||||
if (code != 65536)
|
||||
throw new DataFormatException("Code lengths don't add up properly.");
|
||||
|
||||
/* Now create and fill the extra tables from longest to shortest
|
||||
* bit len. This way the sub trees will be aligned.
|
||||
*/
|
||||
tree = new short[treeSize];
|
||||
int treePtr = 512;
|
||||
for (int bits = MAX_BITLEN; bits >= 10; bits--)
|
||||
{
|
||||
int end = code & 0x1ff80;
|
||||
code -= blCount[bits] << (16 - bits);
|
||||
int start = code & 0x1ff80;
|
||||
for (int i = start; i < end; i += 1 << 7)
|
||||
{
|
||||
tree[DeflaterHuffman.bitReverse(i)]
|
||||
= (short) ((-treePtr << 4) | bits);
|
||||
treePtr += 1 << (bits-9);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < codeLengths.length; i++)
|
||||
{
|
||||
int bits = codeLengths[i];
|
||||
if (bits == 0)
|
||||
continue;
|
||||
code = nextCode[bits];
|
||||
int revcode = DeflaterHuffman.bitReverse(code);
|
||||
if (bits <= 9)
|
||||
{
|
||||
do
|
||||
{
|
||||
tree[revcode] = (short) ((i << 4) | bits);
|
||||
revcode += 1 << bits;
|
||||
}
|
||||
while (revcode < 512);
|
||||
}
|
||||
else
|
||||
{
|
||||
int subTree = tree[revcode & 511];
|
||||
int treeLen = 1 << (subTree & 15);
|
||||
subTree = -(subTree >> 4);
|
||||
do
|
||||
{
|
||||
tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);
|
||||
revcode += 1 << bits;
|
||||
}
|
||||
while (revcode < treeLen);
|
||||
}
|
||||
nextCode[bits] = code + (1 << (16 - bits));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next symbol from input. The symbol is encoded using the
|
||||
* huffman tree.
|
||||
* @param input the input source.
|
||||
* @return the next symbol, or -1 if not enough input is available.
|
||||
*/
|
||||
int getSymbol(StreamManipulator input) throws DataFormatException
|
||||
{
|
||||
int lookahead, symbol;
|
||||
if ((lookahead = input.peekBits(9)) >= 0)
|
||||
{
|
||||
if ((symbol = tree[lookahead]) >= 0)
|
||||
{
|
||||
input.dropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
}
|
||||
int subtree = -(symbol >> 4);
|
||||
int bitlen = symbol & 15;
|
||||
if ((lookahead = input.peekBits(bitlen)) >= 0)
|
||||
{
|
||||
symbol = tree[subtree | (lookahead >> 9)];
|
||||
input.dropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
int bits = input.getAvailableBits();
|
||||
lookahead = input.peekBits(bits);
|
||||
symbol = tree[subtree | (lookahead >> 9)];
|
||||
if ((symbol & 15) <= bits)
|
||||
{
|
||||
input.dropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int bits = input.getAvailableBits();
|
||||
lookahead = input.peekBits(bits);
|
||||
symbol = tree[lookahead];
|
||||
if (symbol >= 0 && (symbol & 15) <= bits)
|
||||
{
|
||||
input.dropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
308
src/com/classpath/zip/InflaterInputStream.java
Normal file
308
src/com/classpath/zip/InflaterInputStream.java
Normal file
@ -0,0 +1,308 @@
|
||||
/* InflaterInputStream.java - Input stream filter for decompressing
|
||||
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* This filter stream is used to decompress data compressed in the "deflate"
|
||||
* format. The "deflate" format is described in RFC 1951.
|
||||
*
|
||||
* This stream may form the basis for other decompression filters, such
|
||||
* as the <code>GZIPInputStream</code>.
|
||||
*
|
||||
* @author John Leuner
|
||||
* @author Tom Tromey
|
||||
* @since 1.1
|
||||
*/
|
||||
public class InflaterInputStream extends InputStream
|
||||
{
|
||||
/**
|
||||
* Decompressor for this filter
|
||||
*/
|
||||
protected Inflater inf;
|
||||
|
||||
/**
|
||||
* Byte array used as a buffer
|
||||
*/
|
||||
protected byte[] buf;
|
||||
|
||||
/**
|
||||
* Size of buffer
|
||||
*/
|
||||
protected int len;
|
||||
|
||||
// We just use this if we are decoding one byte at a time with the
|
||||
// read() call.
|
||||
private byte[] onebytebuffer = new byte[1];
|
||||
|
||||
protected InputStream in;
|
||||
|
||||
/**
|
||||
* Create an InflaterInputStream with the default decompresseor
|
||||
* and a default buffer size.
|
||||
*
|
||||
* @param in the InputStream to read bytes from
|
||||
*/
|
||||
public InflaterInputStream(InputStream in)
|
||||
{
|
||||
this(in, new Inflater(), 4096);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an InflaterInputStream with the specified decompresseor
|
||||
* and a default buffer size.
|
||||
*
|
||||
* @param in the InputStream to read bytes from
|
||||
* @param inf the decompressor used to decompress data read from in
|
||||
*/
|
||||
public InflaterInputStream(InputStream in, Inflater inf)
|
||||
{
|
||||
this(in, inf, 4096);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an InflaterInputStream with the specified decompresseor
|
||||
* and a specified buffer size.
|
||||
*
|
||||
* @param in the InputStream to read bytes from
|
||||
* @param inf the decompressor used to decompress data read from in
|
||||
* @param size size of the buffer to use
|
||||
*/
|
||||
public InflaterInputStream(InputStream in, Inflater inf, int size)
|
||||
{
|
||||
if(in == null)
|
||||
{
|
||||
throw new NullPointerException("in may not be null");
|
||||
}
|
||||
|
||||
if(inf == null)
|
||||
{
|
||||
throw new NullPointerException("inf may not be null");
|
||||
}
|
||||
|
||||
if(size < 0)
|
||||
{
|
||||
throw new IllegalArgumentException("size may not be negative");
|
||||
}
|
||||
|
||||
this.in = in;
|
||||
this.inf = inf;
|
||||
this.buf = new byte[size];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 once the end of the stream (EOF) has been reached.
|
||||
* Otherwise returns 1.
|
||||
*/
|
||||
public int available() throws IOException
|
||||
{
|
||||
// According to the JDK 1.2 docs, this should only ever return 0
|
||||
// or 1 and should not be relied upon by Java programs.
|
||||
|
||||
if(inf == null)
|
||||
{
|
||||
throw new IOException("stream closed");
|
||||
}
|
||||
|
||||
return inf.finished() ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the input stream
|
||||
*/
|
||||
public synchronized void close() throws IOException
|
||||
{
|
||||
if(in != null)
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
|
||||
in = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the buffer with more data to decompress.
|
||||
*/
|
||||
protected void fill() throws IOException
|
||||
{
|
||||
if(in == null)
|
||||
{
|
||||
throw new ZipException("InflaterInputStream is closed");
|
||||
}
|
||||
|
||||
len = in.read(buf, 0, buf.length);
|
||||
|
||||
if(len < 0)
|
||||
{
|
||||
throw new ZipException("Deflated stream ends early.");
|
||||
}
|
||||
|
||||
inf.setInput(buf, 0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads one byte of decompressed data.
|
||||
*
|
||||
* The byte is in the lower 8 bits of the int.
|
||||
*/
|
||||
public int read() throws IOException
|
||||
{
|
||||
int nread = read(onebytebuffer, 0, 1);
|
||||
|
||||
if(nread > 0)
|
||||
{
|
||||
return onebytebuffer[0] & 0xff;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompresses data into the byte array
|
||||
*
|
||||
* @param b the array to read and decompress data into
|
||||
* @param off the offset indicating where the data should be placed
|
||||
* @param len the number of bytes to decompress
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if(inf == null)
|
||||
{
|
||||
throw new IOException("stream closed");
|
||||
}
|
||||
|
||||
if(len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
while(true)
|
||||
{
|
||||
try
|
||||
{
|
||||
count = inf.inflate(b, off, len);
|
||||
}
|
||||
catch(DataFormatException dfe)
|
||||
{
|
||||
throw new ZipException(dfe.getMessage());
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
if(inf.needsDictionary() | inf.finished())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if(inf.needsInput())
|
||||
{
|
||||
fill();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ZipException("Don't know what to do");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip specified number of bytes of uncompressed data
|
||||
*
|
||||
* @param n number of bytes to skip
|
||||
*/
|
||||
public long skip(long n) throws IOException
|
||||
{
|
||||
if(inf == null)
|
||||
{
|
||||
throw new IOException("stream closed");
|
||||
}
|
||||
|
||||
if(n < 0)
|
||||
{
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if(n == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int buflen = (int)Math.min(n, 2048);
|
||||
byte[] tmpbuf = new byte[buflen];
|
||||
|
||||
long skipped = 0L;
|
||||
|
||||
while(n > 0L)
|
||||
{
|
||||
int numread = read(tmpbuf, 0, buflen);
|
||||
|
||||
if(numread <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
n -= numread;
|
||||
skipped += numread;
|
||||
buflen = (int)Math.min(n, 2048);
|
||||
}
|
||||
|
||||
return skipped;
|
||||
}
|
||||
|
||||
public boolean markSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void mark(int readLimit)
|
||||
{
|
||||
}
|
||||
|
||||
public void reset() throws IOException
|
||||
{
|
||||
throw new IOException("reset not supported");
|
||||
}
|
||||
}
|
180
src/com/classpath/zip/OutputWindow.java
Normal file
180
src/com/classpath/zip/OutputWindow.java
Normal file
@ -0,0 +1,180 @@
|
||||
/* OutputWindow.java --
|
||||
Copyright (C) 2001, 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/**
|
||||
* Contains the output from the Inflation process.
|
||||
*
|
||||
* We need to have a window so that we can refer backwards into the output stream
|
||||
* to repeat stuff.
|
||||
*
|
||||
* @author John Leuner
|
||||
* @since 1.1
|
||||
*/
|
||||
class OutputWindow
|
||||
{
|
||||
private static final int WINDOW_SIZE = 1 << 15;
|
||||
private static final int WINDOW_MASK = WINDOW_SIZE - 1;
|
||||
|
||||
private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes
|
||||
private int window_end = 0;
|
||||
private int window_filled = 0;
|
||||
|
||||
public void write(int abyte)
|
||||
{
|
||||
if(window_filled++ == WINDOW_SIZE)
|
||||
throw new IllegalStateException("Window full");
|
||||
window[window_end++] = (byte)abyte;
|
||||
window_end &= WINDOW_MASK;
|
||||
}
|
||||
|
||||
private void slowRepeat(int rep_start, int len, int dist)
|
||||
{
|
||||
while(len-- > 0)
|
||||
{
|
||||
window[window_end++] = window[rep_start++];
|
||||
window_end &= WINDOW_MASK;
|
||||
rep_start &= WINDOW_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
public void repeat(int len, int dist)
|
||||
{
|
||||
if((window_filled += len) > WINDOW_SIZE)
|
||||
throw new IllegalStateException("Window full");
|
||||
|
||||
int rep_start = (window_end - dist) & WINDOW_MASK;
|
||||
int border = WINDOW_SIZE - len;
|
||||
if(rep_start <= border && window_end < border)
|
||||
{
|
||||
if(len <= dist)
|
||||
{
|
||||
System.arraycopy(window, rep_start, window, window_end, len);
|
||||
window_end += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have to copy manually, since the repeat pattern overlaps.
|
||||
*/
|
||||
while(len-- > 0)
|
||||
window[window_end++] = window[rep_start++];
|
||||
}
|
||||
}
|
||||
else
|
||||
slowRepeat(rep_start, len, dist);
|
||||
}
|
||||
|
||||
public int copyStored(StreamManipulator input, int len)
|
||||
{
|
||||
len = Math.min(Math.min(len, WINDOW_SIZE - window_filled),
|
||||
input.getAvailableBytes());
|
||||
int copied;
|
||||
|
||||
int tailLen = WINDOW_SIZE - window_end;
|
||||
if(len > tailLen)
|
||||
{
|
||||
copied = input.copyBytes(window, window_end, tailLen);
|
||||
if(copied == tailLen)
|
||||
copied += input.copyBytes(window, 0, len - tailLen);
|
||||
}
|
||||
else
|
||||
copied = input.copyBytes(window, window_end, len);
|
||||
|
||||
window_end = (window_end + copied) & WINDOW_MASK;
|
||||
window_filled += copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
public void copyDict(byte[] dict, int offset, int len)
|
||||
{
|
||||
if(window_filled > 0)
|
||||
throw new IllegalStateException();
|
||||
|
||||
if(len > WINDOW_SIZE)
|
||||
{
|
||||
offset += len - WINDOW_SIZE;
|
||||
len = WINDOW_SIZE;
|
||||
}
|
||||
System.arraycopy(dict, offset, window, 0, len);
|
||||
window_end = len & WINDOW_MASK;
|
||||
}
|
||||
|
||||
public int getFreeSpace()
|
||||
{
|
||||
return WINDOW_SIZE - window_filled;
|
||||
}
|
||||
|
||||
public int getAvailable()
|
||||
{
|
||||
return window_filled;
|
||||
}
|
||||
|
||||
public int copyOutput(byte[] output, int offset, int len)
|
||||
{
|
||||
int copy_end = window_end;
|
||||
if(len > window_filled)
|
||||
len = window_filled;
|
||||
else
|
||||
copy_end = (window_end - window_filled + len) & WINDOW_MASK;
|
||||
|
||||
int copied = len;
|
||||
int tailLen = len - copy_end;
|
||||
|
||||
if(tailLen > 0)
|
||||
{
|
||||
System.arraycopy(window, WINDOW_SIZE - tailLen,
|
||||
output, offset, tailLen);
|
||||
offset += tailLen;
|
||||
len = copy_end;
|
||||
}
|
||||
System.arraycopy(window, copy_end - len, output, offset, len);
|
||||
window_filled -= copied;
|
||||
if(window_filled < 0)
|
||||
throw new IllegalStateException();
|
||||
return copied;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
window_filled = window_end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
160
src/com/classpath/zip/PartialInputStream.java
Normal file
160
src/com/classpath/zip/PartialInputStream.java
Normal file
@ -0,0 +1,160 @@
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.*;
|
||||
import com.one.*;
|
||||
|
||||
public class PartialInputStream extends RandomAccessInputStream
|
||||
{
|
||||
protected RandomAccessInputStream is;
|
||||
protected int currpos, markedpos;
|
||||
protected int start, end;
|
||||
|
||||
// We may need to supply an extra dummy byte to our reader.
|
||||
// See Inflater. We use a count here to simplify the logic
|
||||
// elsewhere in this class. Note that we ignore the dummy
|
||||
// byte in methods where we know it is not needed.
|
||||
protected int dummyByteCount;
|
||||
|
||||
public PartialInputStream(RandomAccessInputStream rais, int off, int len) throws IOException
|
||||
{
|
||||
is = rais;
|
||||
|
||||
if(off >= 0)
|
||||
{
|
||||
start = off;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if(len >= 0)
|
||||
{
|
||||
end = start + len;
|
||||
}
|
||||
else
|
||||
{
|
||||
end = rais.getCapacity();
|
||||
}
|
||||
|
||||
currpos = start;
|
||||
markedpos = currpos;
|
||||
dummyByteCount = 0;
|
||||
}
|
||||
|
||||
public void addDummyByte()
|
||||
{
|
||||
dummyByteCount = 1;
|
||||
}
|
||||
|
||||
public int available()
|
||||
{
|
||||
return end - currpos;
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
}
|
||||
|
||||
public void mark(int readLimit)
|
||||
{
|
||||
markedpos = currpos;
|
||||
}
|
||||
|
||||
public int read() throws IOException
|
||||
{
|
||||
if(currpos >= end)
|
||||
{
|
||||
if(dummyByteCount > 0)
|
||||
{
|
||||
dummyByteCount--;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
is.seek(currpos++);
|
||||
|
||||
return is.read();
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException
|
||||
{
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if(len > available() + dummyByteCount)
|
||||
{
|
||||
len = available() + dummyByteCount;
|
||||
}
|
||||
|
||||
is.seek(currpos);
|
||||
int n = is.read(b, off, len);
|
||||
|
||||
if(n < len && dummyByteCount > 0)
|
||||
{
|
||||
dummyByteCount--;
|
||||
b[off + n] = 0;
|
||||
}
|
||||
|
||||
currpos += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
currpos = markedpos;
|
||||
}
|
||||
|
||||
public long skip(long n)
|
||||
{
|
||||
if(n > available())
|
||||
{
|
||||
n = available();
|
||||
}
|
||||
|
||||
currpos += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void seek(int pos) throws IOException
|
||||
{
|
||||
if(pos == currpos)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(pos < start)
|
||||
{
|
||||
pos = start;
|
||||
}
|
||||
else if(pos > end)
|
||||
{
|
||||
pos = end;
|
||||
}
|
||||
|
||||
currpos = pos;
|
||||
}
|
||||
|
||||
public int tell()
|
||||
{
|
||||
return currpos - start;
|
||||
}
|
||||
|
||||
public int getCapacity()
|
||||
{
|
||||
return end - start;
|
||||
}
|
||||
|
||||
public void update() throws IOException
|
||||
{
|
||||
is.update();
|
||||
}
|
||||
}
|
208
src/com/classpath/zip/PendingBuffer.java
Normal file
208
src/com/classpath/zip/PendingBuffer.java
Normal file
@ -0,0 +1,208 @@
|
||||
/* java.util.zip.PendingBuffer
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/**
|
||||
* This class is general purpose class for writing data to a buffer.
|
||||
*
|
||||
* It allows you to write bits as well as bytes
|
||||
*
|
||||
* Based on DeflaterPending.java
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
* @date Jan 5, 2000
|
||||
*/
|
||||
|
||||
class PendingBuffer
|
||||
{
|
||||
protected byte[] buf;
|
||||
int start;
|
||||
int end;
|
||||
|
||||
int bits;
|
||||
int bitCount;
|
||||
|
||||
public PendingBuffer ()
|
||||
{
|
||||
this (4096);
|
||||
}
|
||||
|
||||
public PendingBuffer (int bufsize)
|
||||
{
|
||||
buf = new byte[bufsize];
|
||||
}
|
||||
|
||||
public final void reset ()
|
||||
{
|
||||
start = end = bitCount = 0;
|
||||
}
|
||||
|
||||
public final void writeByte (int b)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0)
|
||||
throw new IllegalStateException ();
|
||||
buf[end++] = (byte) b;
|
||||
}
|
||||
|
||||
public final void writeShort (int s)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0)
|
||||
throw new IllegalStateException ();
|
||||
buf[end++] = (byte) s;
|
||||
buf[end++] = (byte) (s >> 8);
|
||||
}
|
||||
|
||||
public final void writeInt (int s)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0)
|
||||
throw new IllegalStateException ();
|
||||
buf[end++] = (byte) s;
|
||||
buf[end++] = (byte) (s >> 8);
|
||||
buf[end++] = (byte) (s >> 16);
|
||||
buf[end++] = (byte) (s >> 24);
|
||||
}
|
||||
|
||||
public final void writeBlock (byte[] block, int offset, int len)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0)
|
||||
throw new IllegalStateException ();
|
||||
System.arraycopy (block, offset, buf, end, len);
|
||||
end += len;
|
||||
}
|
||||
|
||||
public final int getBitCount ()
|
||||
{
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
public final void alignToByte ()
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0)
|
||||
throw new IllegalStateException ();
|
||||
if (bitCount > 0)
|
||||
{
|
||||
buf[end++] = (byte) bits;
|
||||
if (bitCount > 8)
|
||||
buf[end++] = (byte) (bits >>> 8);
|
||||
}
|
||||
bits = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
|
||||
public final void writeBits (int b, int count)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0)
|
||||
throw new IllegalStateException ();
|
||||
if (DeflaterConstants.DEBUGGING)
|
||||
System.err.println ("writeBits("+Integer.toHexString (b)+","+count+")");
|
||||
bits |= b << bitCount;
|
||||
bitCount += count;
|
||||
if (bitCount >= 16)
|
||||
{
|
||||
buf[end++] = (byte) bits;
|
||||
buf[end++] = (byte) (bits >>> 8);
|
||||
bits >>>= 16;
|
||||
bitCount -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
public final void writeShortMSB (int s)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0)
|
||||
throw new IllegalStateException ();
|
||||
buf[end++] = (byte) (s >> 8);
|
||||
buf[end++] = (byte) s;
|
||||
}
|
||||
|
||||
public final boolean isFlushed ()
|
||||
{
|
||||
return end == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the pending buffer into the given output array. If the
|
||||
* output array is to small, only a partial flush is done.
|
||||
*
|
||||
* @param output the output array;
|
||||
* @param offset the offset into output array;
|
||||
* @param length the maximum number of bytes to store;
|
||||
* @exception IndexOutOfBoundsException if offset or length are
|
||||
* invalid.
|
||||
*/
|
||||
public final int flush (byte[] output, int offset, int length)
|
||||
{
|
||||
if (bitCount >= 8)
|
||||
{
|
||||
buf[end++] = (byte) bits;
|
||||
bits >>>= 8;
|
||||
bitCount -= 8;
|
||||
}
|
||||
if (length > end - start)
|
||||
{
|
||||
length = end - start;
|
||||
System.arraycopy (buf, start, output, offset, length);
|
||||
start = 0;
|
||||
end = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.arraycopy (buf, start, output, offset, length);
|
||||
start += length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the pending buffer and returns that data in a new array
|
||||
*
|
||||
* @param output the output stream
|
||||
*/
|
||||
|
||||
public final byte[] toByteArray ()
|
||||
{
|
||||
byte[] ret = new byte[ end - start ];
|
||||
System.arraycopy (buf, start, ret, 0, ret.length);
|
||||
start = 0;
|
||||
end = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
217
src/com/classpath/zip/StreamManipulator.java
Normal file
217
src/com/classpath/zip/StreamManipulator.java
Normal file
@ -0,0 +1,217 @@
|
||||
/* java.util.zip.StreamManipulator
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
/**
|
||||
* This class allows us to retrieve a specified amount of bits from
|
||||
* the input buffer, as well as copy big byte blocks.
|
||||
*
|
||||
* It uses an int buffer to store up to 31 bits for direct
|
||||
* manipulation. This guarantees that we can get at least 16 bits,
|
||||
* but we only need at most 15, so this is all safe.
|
||||
*
|
||||
* There are some optimizations in this class, for example, you must
|
||||
* never peek more then 8 bits more than needed, and you must first
|
||||
* peek bits before you may drop them. This is not a general purpose
|
||||
* class but optimized for the behaviour of the Inflater.
|
||||
*
|
||||
* @author John Leuner, Jochen Hoenicke
|
||||
*/
|
||||
|
||||
class StreamManipulator
|
||||
{
|
||||
private byte[] window;
|
||||
private int window_start = 0;
|
||||
private int window_end = 0;
|
||||
|
||||
private int buffer = 0;
|
||||
private int bits_in_buffer = 0;
|
||||
|
||||
/**
|
||||
* Get the next n bits but don't increase input pointer. n must be
|
||||
* less or equal 16 and if you if this call succeeds, you must drop
|
||||
* at least n-8 bits in the next call.
|
||||
*
|
||||
* @return the value of the bits, or -1 if not enough bits available. */
|
||||
public final int peekBits(int n)
|
||||
{
|
||||
if (bits_in_buffer < n)
|
||||
{
|
||||
if (window_start == window_end)
|
||||
return -1;
|
||||
buffer |= (window[window_start++] & 0xff
|
||||
| (window[window_start++] & 0xff) << 8) << bits_in_buffer;
|
||||
bits_in_buffer += 16;
|
||||
}
|
||||
return buffer & ((1 << n) - 1);
|
||||
}
|
||||
|
||||
/* Drops the next n bits from the input. You should have called peekBits
|
||||
* with a bigger or equal n before, to make sure that enough bits are in
|
||||
* the bit buffer.
|
||||
*/
|
||||
public final void dropBits(int n)
|
||||
{
|
||||
buffer >>>= n;
|
||||
bits_in_buffer -= n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next n bits and increases input pointer. This is equivalent
|
||||
* to peekBits followed by dropBits, except for correct error handling.
|
||||
* @return the value of the bits, or -1 if not enough bits available.
|
||||
*/
|
||||
public final int getBits(int n)
|
||||
{
|
||||
int bits = peekBits(n);
|
||||
if (bits >= 0)
|
||||
dropBits(n);
|
||||
return bits;
|
||||
}
|
||||
/**
|
||||
* Gets the number of bits available in the bit buffer. This must be
|
||||
* only called when a previous peekBits() returned -1.
|
||||
* @return the number of bits available.
|
||||
*/
|
||||
public final int getAvailableBits()
|
||||
{
|
||||
return bits_in_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes available.
|
||||
* @return the number of bytes available.
|
||||
*/
|
||||
public final int getAvailableBytes()
|
||||
{
|
||||
return window_end - window_start + (bits_in_buffer >> 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips to the next byte boundary.
|
||||
*/
|
||||
public void skipToByteBoundary()
|
||||
{
|
||||
buffer >>= (bits_in_buffer & 7);
|
||||
bits_in_buffer &= ~7;
|
||||
}
|
||||
|
||||
public final boolean needsInput() {
|
||||
return window_start == window_end;
|
||||
}
|
||||
|
||||
|
||||
/* Copies length bytes from input buffer to output buffer starting
|
||||
* at output[offset]. You have to make sure, that the buffer is
|
||||
* byte aligned. If not enough bytes are available, copies fewer
|
||||
* bytes.
|
||||
* @param length the length to copy, 0 is allowed.
|
||||
* @return the number of bytes copied, 0 if no byte is available.
|
||||
*/
|
||||
public int copyBytes(byte[] output, int offset, int length)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new IllegalArgumentException("length negative");
|
||||
if ((bits_in_buffer & 7) != 0)
|
||||
/* bits_in_buffer may only be 0 or 8 */
|
||||
throw new IllegalStateException("Bit buffer is not aligned!");
|
||||
|
||||
int count = 0;
|
||||
while (bits_in_buffer > 0 && length > 0)
|
||||
{
|
||||
output[offset++] = (byte) buffer;
|
||||
buffer >>>= 8;
|
||||
bits_in_buffer -= 8;
|
||||
length--;
|
||||
count++;
|
||||
}
|
||||
if (length == 0)
|
||||
return count;
|
||||
|
||||
int avail = window_end - window_start;
|
||||
if (length > avail)
|
||||
length = avail;
|
||||
System.arraycopy(window, window_start, output, offset, length);
|
||||
window_start += length;
|
||||
|
||||
if (((window_start - window_end) & 1) != 0)
|
||||
{
|
||||
/* We always want an even number of bytes in input, see peekBits */
|
||||
buffer = (window[window_start++] & 0xff);
|
||||
bits_in_buffer = 8;
|
||||
}
|
||||
return count + length;
|
||||
}
|
||||
|
||||
public StreamManipulator()
|
||||
{
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
window_start = window_end = buffer = bits_in_buffer = 0;
|
||||
}
|
||||
|
||||
public void setInput(byte[] buf, int off, int len)
|
||||
{
|
||||
if (window_start < window_end)
|
||||
throw new IllegalStateException
|
||||
("Old input was not completely processed");
|
||||
|
||||
int end = off + len;
|
||||
|
||||
/* We want to throw an ArrayIndexOutOfBoundsException early. The
|
||||
* check is very tricky: it also handles integer wrap around.
|
||||
*/
|
||||
if (0 > off || off > end || end > buf.length)
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
|
||||
if ((len & 1) != 0)
|
||||
{
|
||||
/* We always want an even number of bytes in input, see peekBits */
|
||||
buffer |= (buf[off++] & 0xff) << bits_in_buffer;
|
||||
bits_in_buffer += 8;
|
||||
}
|
||||
|
||||
window = buf;
|
||||
window_start = off;
|
||||
window_end = end;
|
||||
}
|
||||
}
|
||||
|
97
src/com/classpath/zip/ZipConstants.java
Normal file
97
src/com/classpath/zip/ZipConstants.java
Normal file
@ -0,0 +1,97 @@
|
||||
/* java.util.zip.ZipConstants
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package com.classpath.zip;
|
||||
|
||||
interface ZipConstants
|
||||
{
|
||||
/* The local file header */
|
||||
int LOCHDR = 30;
|
||||
int LOCSIG = 'P' | ('K' << 8) | (3 << 16) | (4 << 24);
|
||||
|
||||
int LOCVER = 4;
|
||||
int LOCFLG = 6;
|
||||
int LOCHOW = 8;
|
||||
int LOCTIM = 10;
|
||||
int LOCCRC = 14;
|
||||
int LOCSIZ = 18;
|
||||
int LOCLEN = 22;
|
||||
int LOCNAM = 26;
|
||||
int LOCEXT = 28;
|
||||
|
||||
/* The Data descriptor */
|
||||
int EXTSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24);
|
||||
int EXTHDR = 16;
|
||||
|
||||
int EXTCRC = 4;
|
||||
int EXTSIZ = 8;
|
||||
int EXTLEN = 12;
|
||||
|
||||
/* The central directory file header */
|
||||
int CENSIG = 'P' | ('K' << 8) | (1 << 16) | (2 << 24);
|
||||
int CENHDR = 46;
|
||||
|
||||
int CENVEM = 4;
|
||||
int CENVER = 6;
|
||||
int CENFLG = 8;
|
||||
int CENHOW = 10;
|
||||
int CENTIM = 12;
|
||||
int CENCRC = 16;
|
||||
int CENSIZ = 20;
|
||||
int CENLEN = 24;
|
||||
int CENNAM = 28;
|
||||
int CENEXT = 30;
|
||||
int CENCOM = 32;
|
||||
int CENDSK = 34;
|
||||
int CENATT = 36;
|
||||
int CENATX = 38;
|
||||
int CENOFF = 42;
|
||||
|
||||
/* The entries in the end of central directory */
|
||||
int ENDSIG = 'P' | ('K' << 8) | (5 << 16) | (6 << 24);
|
||||
int ENDHDR = 22;
|
||||
|
||||
/* The following two fields are missing in SUN JDK */
|
||||
int ENDNRD = 4;
|
||||
int ENDDCD = 6;
|
||||
int ENDSUB = 8;
|
||||
int ENDTOT = 10;
|
||||
int ENDSIZ = 12;
|
||||
int ENDOFF = 16;
|
||||
int ENDCOM = 20;
|
||||
}
|
||||
|
631
src/com/classpath/zip/ZipEntry.java
Normal file
631
src/com/classpath/zip/ZipEntry.java
Normal file
@ -0,0 +1,631 @@
|
||||
/* java.util.zip.ZipEntry
|
||||
Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* This class represents a member of a zip archive. ZipFile and
|
||||
* ZipInputStream will give you instances of this class as information
|
||||
* about the members in an archive. On the other hand ZipOutputStream
|
||||
* needs an instance of this class to create a new member.
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
*/
|
||||
public class ZipEntry implements ZipConstants
|
||||
{
|
||||
private static int KNOWN_SIZE = 1;
|
||||
private static int KNOWN_CSIZE = 2;
|
||||
private static int KNOWN_CRC = 4;
|
||||
private static int KNOWN_TIME = 8;
|
||||
|
||||
private static Calendar cal = Calendar.getInstance();
|
||||
|
||||
private String name;
|
||||
private int size;
|
||||
private int compressedSize;
|
||||
private int crc;
|
||||
private int dostime;
|
||||
private short known = 0;
|
||||
private short method = -1;
|
||||
private byte[] extra = null;
|
||||
private String comment = null;
|
||||
|
||||
public int flags; /* used by ZipOutputStream */
|
||||
public int offset; /* used by ZipFile and ZipOutputStream */
|
||||
|
||||
/**
|
||||
* Creates an empty zip entry.
|
||||
*/
|
||||
public ZipEntry()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a zip entry with the given name.
|
||||
* @param name the name. May include directory components separated by '/'.
|
||||
*
|
||||
* @exception NullPointerException when name is null.
|
||||
* @exception IllegalArgumentException when name is bigger then 65535 chars.
|
||||
*/
|
||||
public ZipEntry(String name)
|
||||
{
|
||||
setName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given zip entry.
|
||||
* @param e the entry to copy.
|
||||
*/
|
||||
public ZipEntry(ZipEntry e)
|
||||
{
|
||||
name = e.name;
|
||||
known = e.known;
|
||||
size = e.size;
|
||||
compressedSize = e.compressedSize;
|
||||
crc = e.crc;
|
||||
dostime = e.dostime;
|
||||
method = e.method;
|
||||
extra = e.extra;
|
||||
comment = e.comment;
|
||||
}
|
||||
|
||||
public int getLocalSize()
|
||||
{
|
||||
if((known & KNOWN_CSIZE) == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int localSize = LOCHDR + compressedSize;
|
||||
|
||||
if(name != null)
|
||||
{
|
||||
localSize += ZipFile.getEncodedLength(name);
|
||||
}
|
||||
|
||||
if(extra != null)
|
||||
{
|
||||
localSize += extra.length;
|
||||
}
|
||||
|
||||
if((flags & 8) != 0)
|
||||
{
|
||||
localSize += EXTHDR;
|
||||
}
|
||||
|
||||
return localSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read this entry from central file header.
|
||||
* Return true if successful, false if central header not found.
|
||||
*/
|
||||
public boolean readCentralHeader(InputStream is) throws IOException
|
||||
{
|
||||
if(ZipFile.readLeInt(is) != CENSIG)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
is.skip(4);
|
||||
|
||||
flags = ZipFile.readLeShort(is);
|
||||
|
||||
setMethod(ZipFile.readLeShort(is));
|
||||
setDOSTime(ZipFile.readLeInt(is));
|
||||
setCRC(ZipFile.readLeInt(is));
|
||||
setCompressedSize(ZipFile.readLeInt(is));
|
||||
setSize(ZipFile.readLeInt(is));
|
||||
|
||||
int nameLen = ZipFile.readLeShort(is);
|
||||
int extraLen = ZipFile.readLeShort(is);
|
||||
int commentLen = ZipFile.readLeShort(is);
|
||||
|
||||
is.skip(8);
|
||||
|
||||
offset = ZipFile.readLeInt(is);
|
||||
|
||||
if(nameLen > 0)
|
||||
{
|
||||
setName(ZipFile.readString(is, nameLen));
|
||||
}
|
||||
|
||||
if(extraLen > 0)
|
||||
{
|
||||
byte[] b = new byte[extraLen];
|
||||
is.read(b, 0, extraLen);
|
||||
|
||||
setExtra(b);
|
||||
}
|
||||
|
||||
if(commentLen > 0)
|
||||
{
|
||||
setComment(ZipFile.readString(is, commentLen));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write central file header for this entry,
|
||||
* return number of bytes written.
|
||||
*/
|
||||
public int writeCentralHeader(OutputStream os) throws IOException
|
||||
{
|
||||
ZipFile.writeLeInt(os, CENSIG);
|
||||
|
||||
int method = getMethod();
|
||||
|
||||
ZipFile.writeLeShort(os, method == ZipFile.STORED ? ZipFile.ZIP_STORED_VERSION : ZipFile.ZIP_DEFLATED_VERSION);
|
||||
ZipFile.writeLeShort(os, method == ZipFile.STORED ? ZipFile.ZIP_STORED_VERSION : ZipFile.ZIP_DEFLATED_VERSION);
|
||||
ZipFile.writeLeShort(os, flags);
|
||||
ZipFile.writeLeShort(os, method);
|
||||
ZipFile.writeLeInt(os, getDOSTime());
|
||||
ZipFile.writeLeInt(os, getCRC());
|
||||
ZipFile.writeLeInt(os, getCompressedSize());
|
||||
ZipFile.writeLeInt(os, getSize());
|
||||
|
||||
byte[] name = ZipFile.encodeString(getName());
|
||||
|
||||
if(name.length > 0xFFFF)
|
||||
{
|
||||
throw new ZipException("Name too long.");
|
||||
}
|
||||
|
||||
byte[] extra = getExtra();
|
||||
|
||||
if(extra == null)
|
||||
{
|
||||
extra = new byte[0];
|
||||
}
|
||||
|
||||
String strComment = getComment();
|
||||
|
||||
byte[] comment = strComment != null ? ZipFile.encodeString(strComment) : new byte[0];
|
||||
|
||||
if(comment.length > 0xFFFF)
|
||||
{
|
||||
throw new ZipException("Comment too long.");
|
||||
}
|
||||
|
||||
ZipFile.writeLeShort(os, name.length);
|
||||
ZipFile.writeLeShort(os, extra.length);
|
||||
ZipFile.writeLeShort(os, comment.length);
|
||||
ZipFile.writeLeShort(os, 0); /* disk number */
|
||||
ZipFile.writeLeShort(os, 0); /* internal file attr */
|
||||
ZipFile.writeLeInt(os, 0); /* external file attr */
|
||||
ZipFile.writeLeInt(os, offset);
|
||||
|
||||
os.write(name);
|
||||
os.write(extra);
|
||||
os.write(comment);
|
||||
|
||||
return CENHDR + name.length + extra.length + comment.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read this entry from local file header.
|
||||
* Return true if successful, false if local header not found.
|
||||
*/
|
||||
public boolean readLocalHeader(InputStream is) throws IOException
|
||||
{
|
||||
if(ZipFile.readLeInt(is) != LOCSIG)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
is.skip(2);
|
||||
|
||||
flags = ZipFile.readLeShort(is);
|
||||
|
||||
setMethod(ZipFile.readLeShort(is));
|
||||
setDOSTime(ZipFile.readLeInt(is));
|
||||
setCRC(ZipFile.readLeInt(is));
|
||||
setCompressedSize(ZipFile.readLeInt(is));
|
||||
setSize(ZipFile.readLeInt(is));
|
||||
|
||||
int nameLen = ZipFile.readLeShort(is);
|
||||
int extraLen = ZipFile.readLeShort(is);
|
||||
|
||||
if(nameLen > 0)
|
||||
{
|
||||
setName(ZipFile.readString(is, nameLen));
|
||||
}
|
||||
|
||||
if(extraLen > 0)
|
||||
{
|
||||
byte[] b = new byte[extraLen];
|
||||
is.read(b, 0, extraLen);
|
||||
|
||||
setExtra(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write local file header for this entry,
|
||||
* return number of bytes written.
|
||||
*/
|
||||
public int writeLocalHeader(OutputStream os) throws IOException
|
||||
{
|
||||
ZipFile.writeLeInt(os, LOCSIG);
|
||||
|
||||
int method = getMethod();
|
||||
|
||||
ZipFile.writeLeShort(os, method == ZipFile.STORED ? ZipFile.ZIP_STORED_VERSION : ZipFile.ZIP_DEFLATED_VERSION);
|
||||
ZipFile.writeLeShort(os, flags);
|
||||
ZipFile.writeLeShort(os, method);
|
||||
ZipFile.writeLeInt(os, getDOSTime());
|
||||
|
||||
if((flags & 8) == 0)
|
||||
{
|
||||
ZipFile.writeLeInt(os, getCRC());
|
||||
ZipFile.writeLeInt(os, getCompressedSize());
|
||||
ZipFile.writeLeInt(os, getSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
ZipFile.writeLeInt(os, 0);
|
||||
ZipFile.writeLeInt(os, 0);
|
||||
ZipFile.writeLeInt(os, 0);
|
||||
}
|
||||
|
||||
byte[] name = ZipFile.encodeString(getName());
|
||||
|
||||
if(name.length > 0xFFFF)
|
||||
{
|
||||
throw new ZipException("Name too long.");
|
||||
}
|
||||
|
||||
byte[] extra = getExtra();
|
||||
|
||||
if(extra == null)
|
||||
{
|
||||
extra = new byte[0];
|
||||
}
|
||||
|
||||
ZipFile.writeLeShort(os, name.length);
|
||||
ZipFile.writeLeShort(os, extra.length);
|
||||
|
||||
os.write(name);
|
||||
os.write(extra);
|
||||
|
||||
return LOCHDR + name.length + extra.length;
|
||||
}
|
||||
|
||||
public void setDOSTime(int dostime)
|
||||
{
|
||||
this.dostime = dostime;
|
||||
known |= KNOWN_TIME;
|
||||
}
|
||||
|
||||
public int getDOSTime()
|
||||
{
|
||||
if((known & KNOWN_TIME) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return dostime;
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
if(name.length() > 65535)
|
||||
{
|
||||
throw new IllegalArgumentException("Name too long");
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry name. The path components in the entry are
|
||||
* always separated by slashes ('/').
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time of last modification of the entry.
|
||||
* @time the time of last modification of the entry.
|
||||
*/
|
||||
public void setTime(long time)
|
||||
{
|
||||
synchronized(cal)
|
||||
{
|
||||
cal.setTime(new Date(time * 1000L));
|
||||
|
||||
dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25
|
||||
| (cal.get(Calendar.MONTH) + 1) << 21
|
||||
| (cal.get(Calendar.DAY_OF_MONTH)) << 16
|
||||
| (cal.get(Calendar.HOUR_OF_DAY)) << 11
|
||||
| (cal.get(Calendar.MINUTE)) << 5
|
||||
| (cal.get(Calendar.SECOND)) >> 1;
|
||||
}
|
||||
|
||||
dostime = (int)(dostime / 1000L);
|
||||
this.known |= KNOWN_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time of last modification of the entry.
|
||||
* @return the time of last modification of the entry, or -1 if unknown.
|
||||
*/
|
||||
public long getTime()
|
||||
{
|
||||
if((known & KNOWN_TIME) == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sec = 2 * (dostime & 0x1f);
|
||||
int min = (dostime >> 5) & 0x3f;
|
||||
int hrs = (dostime >> 11) & 0x1f;
|
||||
int day = (dostime >> 16) & 0x1f;
|
||||
int mon = ((dostime >> 21) & 0xf) - 1;
|
||||
int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */
|
||||
|
||||
try
|
||||
{
|
||||
synchronized(cal)
|
||||
{
|
||||
cal.set(cal.YEAR, year);
|
||||
cal.set(cal.MONTH, mon);
|
||||
cal.set(cal.DAY_OF_MONTH, day);
|
||||
cal.set(cal.HOUR_OF_DAY, hrs);
|
||||
cal.set(cal.MINUTE, min);
|
||||
cal.set(cal.SECOND, sec);
|
||||
|
||||
return cal.getTime().getTime();
|
||||
}
|
||||
}
|
||||
catch(RuntimeException ex)
|
||||
{
|
||||
/* Ignore illegal time stamp */
|
||||
known &= ~KNOWN_TIME;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the uncompressed data.
|
||||
* @exception IllegalArgumentException if size is not in 0..0xffffffffL
|
||||
*/
|
||||
public void setSize(int size)
|
||||
{
|
||||
this.size = size;
|
||||
this.known |= KNOWN_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the uncompressed data.
|
||||
* @return the size or -1 if unknown.
|
||||
*/
|
||||
public int getSize()
|
||||
{
|
||||
return (known & KNOWN_SIZE) != 0 ? size : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the compressed data.
|
||||
* @exception IllegalArgumentException if size is not in 0..0xffffffffL
|
||||
*/
|
||||
public void setCompressedSize(int csize)
|
||||
{
|
||||
this.compressedSize = csize;
|
||||
this.known |= KNOWN_CSIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the compressed data.
|
||||
* @return the size or -1 if unknown.
|
||||
*/
|
||||
public int getCompressedSize()
|
||||
{
|
||||
return (known & KNOWN_CSIZE) != 0 ? compressedSize : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the crc of the uncompressed data.
|
||||
* @exception IllegalArgumentException if crc is not in 0..0xffffffffL
|
||||
*/
|
||||
public void setCRC(int crc)
|
||||
{
|
||||
this.crc = crc;
|
||||
this.known |= KNOWN_CRC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the crc of the uncompressed data.
|
||||
* @return the crc or -1 if unknown.
|
||||
*/
|
||||
public int getCRC()
|
||||
{
|
||||
return (known & KNOWN_CRC) != 0 ? crc : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compression method. Only DEFLATED and STORED are
|
||||
* supported.
|
||||
* @exception IllegalArgumentException if method is not supported.
|
||||
* @see ZipOutputStream#DEFLATED
|
||||
* @see ZipOutputStream#STORED
|
||||
*/
|
||||
public void setMethod(int method)
|
||||
{
|
||||
if(method != ZipFile.STORED && method != ZipFile.DEFLATED)
|
||||
{
|
||||
throw new IllegalArgumentException("Method not STORED or DEFLATED");
|
||||
}
|
||||
|
||||
this.method = (short)method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compression method.
|
||||
* @return the compression method or -1 if unknown.
|
||||
*/
|
||||
public int getMethod()
|
||||
{
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the extra data.
|
||||
* @exception IllegalArgumentException if extra is longer than 0xffff bytes.
|
||||
*/
|
||||
public void setExtra(byte[] extra)
|
||||
{
|
||||
if(extra == null)
|
||||
{
|
||||
this.extra = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if(extra.length > 0xffff)
|
||||
{
|
||||
throw new IllegalArgumentException("Extra too long");
|
||||
}
|
||||
|
||||
this.extra = extra;
|
||||
|
||||
try
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
while(pos < extra.length)
|
||||
{
|
||||
int sig = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8;
|
||||
int len = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8;
|
||||
|
||||
if(sig == 0x5455)
|
||||
{
|
||||
/* extended time stamp */
|
||||
int flags = extra[pos];
|
||||
|
||||
if((flags & 1) != 0)
|
||||
{
|
||||
long time = ((extra[pos + 1] & 0xff) |
|
||||
(extra[pos + 2] & 0xff) << 8 |
|
||||
(extra[pos + 3] & 0xff) << 16 |
|
||||
(extra[pos + 4] & 0xff) << 24);
|
||||
|
||||
setTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
pos += len;
|
||||
}
|
||||
}
|
||||
catch(ArrayIndexOutOfBoundsException ex)
|
||||
{
|
||||
/* be lenient */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extra data.
|
||||
* @return the extra data or null if not set.
|
||||
*/
|
||||
public byte[] getExtra()
|
||||
{
|
||||
return extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entry comment.
|
||||
* @exception IllegalArgumentException if comment is longer than 0xffff.
|
||||
*/
|
||||
public void setComment(String comment)
|
||||
{
|
||||
if(comment != null && comment.length() > 0xffff)
|
||||
{
|
||||
throw new IllegalArgumentException("Comment too long");
|
||||
}
|
||||
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comment.
|
||||
* @return the comment or null if not set.
|
||||
*/
|
||||
public String getComment()
|
||||
{
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets true, if the entry is a directory. This is solely
|
||||
* determined by the name, a trailing slash '/' marks a directory.
|
||||
*/
|
||||
public boolean isDirectory()
|
||||
{
|
||||
int nlen = name.length();
|
||||
return nlen > 0 && name.charAt(nlen - 1) == '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of this ZipEntry. This is just
|
||||
* the name as returned by getName().
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hashCode of this ZipEntry. This is just the hashCode
|
||||
* of the name. Note that the equals method isn't changed, though.
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
73
src/com/classpath/zip/ZipException.java
Normal file
73
src/com/classpath/zip/ZipException.java
Normal file
@ -0,0 +1,73 @@
|
||||
/* ZipException.java - exception representing a zip related error
|
||||
Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown during the creation or input of a zip file.
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
* @author Per Bothner
|
||||
* @status updated to 1.4
|
||||
*/
|
||||
public class ZipException extends IOException
|
||||
{
|
||||
/**
|
||||
* Compatible with JDK 1.0+.
|
||||
*/
|
||||
private static final long serialVersionUID = 8000196834066748623L;
|
||||
|
||||
/**
|
||||
* Create an exception without a message.
|
||||
*/
|
||||
public ZipException ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exception with a message.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
public ZipException (String msg)
|
||||
{
|
||||
super (msg);
|
||||
}
|
||||
}
|
753
src/com/classpath/zip/ZipFile.java
Normal file
753
src/com/classpath/zip/ZipFile.java
Normal file
@ -0,0 +1,753 @@
|
||||
/* ZipFile.java --
|
||||
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.microedition.io.file.*;
|
||||
import javax.microedition.io.*;
|
||||
|
||||
import com.vmx.*;
|
||||
import com.one.*;
|
||||
import filemanager.*;
|
||||
|
||||
/**
|
||||
* This class represents a Zip archive. You can ask for the contained
|
||||
* entries, or get an input stream for a file entry. The entry is
|
||||
* automatically decompressed.
|
||||
*
|
||||
* This class is thread safe: You can open input streams for arbitrary
|
||||
* entries in different threads.
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
* @author Artur Biesiadowski
|
||||
*/
|
||||
public class ZipFile implements ZipConstants
|
||||
{
|
||||
/**
|
||||
* Our Zip version is hard coded to 1.0 resp. 2.0
|
||||
*/
|
||||
public final static int ZIP_STORED_VERSION = 10;
|
||||
public final static int ZIP_DEFLATED_VERSION = 20;
|
||||
|
||||
/**
|
||||
* Compression method. This method doesn't compress at all.
|
||||
*/
|
||||
public final static int STORED = 0;
|
||||
|
||||
/**
|
||||
* Compression method. This method uses the Deflater.
|
||||
*/
|
||||
public final static int DEFLATED = 8;
|
||||
|
||||
// File from which zip entries are read.
|
||||
private FileConnection fc = null;
|
||||
private RandomAccessInputStream rais;
|
||||
private boolean closed = false;
|
||||
private boolean finished = true;
|
||||
|
||||
private CentralDirectoryEndRecord cder = new CentralDirectoryEndRecord();
|
||||
|
||||
// The entries of this zip file when initialized and not yet closed.
|
||||
private Hashtable entries;
|
||||
|
||||
public ZipFile(InputStream is) throws IOException
|
||||
{
|
||||
if(is instanceof RandomAccessInputStream)
|
||||
{
|
||||
rais = (RandomAccessInputStream)is;
|
||||
}
|
||||
else
|
||||
{
|
||||
rais = new BufferedInputStream(is);
|
||||
}
|
||||
|
||||
checkZipFile();
|
||||
}
|
||||
|
||||
public ZipFile(String fileName, boolean create) throws IOException
|
||||
{
|
||||
fc = (FileConnection)Connector.open("file:///" + fileName);
|
||||
|
||||
if(fc.isDirectory())
|
||||
{
|
||||
fc.close();
|
||||
throw new IOException("Not a file: " + fileName);
|
||||
}
|
||||
else if(!fc.exists())
|
||||
{
|
||||
if(create)
|
||||
{
|
||||
fc.create();
|
||||
}
|
||||
else
|
||||
{
|
||||
fc.close();
|
||||
throw new IOException("File does not exist: " + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
rais = new FileInputStream(fc);
|
||||
|
||||
if(rais.getCapacity() == 0)
|
||||
{
|
||||
entries = new Hashtable();
|
||||
finished = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkZipFile();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkZipFile() throws ZipException
|
||||
{
|
||||
boolean valid = false;
|
||||
|
||||
try
|
||||
{
|
||||
byte[] buf = new byte[4];
|
||||
rais.read(buf);
|
||||
int sig = buf[0] & 0xFF | ((buf[1] & 0xFF) << 8) | ((buf[2] & 0xFF) << 16) | ((buf[3] & 0xFF) << 24);
|
||||
valid = (sig == LOCSIG) || (sig == ENDSIG);
|
||||
}
|
||||
catch(IOException _)
|
||||
{
|
||||
}
|
||||
|
||||
if(!valid)
|
||||
{
|
||||
try
|
||||
{
|
||||
rais.close();
|
||||
}
|
||||
catch(IOException _)
|
||||
{
|
||||
}
|
||||
|
||||
throw new ZipException("Not a valid zip file");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReadOnly()
|
||||
{
|
||||
return fc == null;
|
||||
}
|
||||
|
||||
private void checkReadOnly() throws IOException
|
||||
{
|
||||
if(isReadOnly())
|
||||
{
|
||||
throw new IOException("ZipFile is read only");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if file is closed and throws an exception.
|
||||
*/
|
||||
private void checkClosed()
|
||||
{
|
||||
if(closed)
|
||||
{
|
||||
throw new IllegalStateException("ZipFile is closed");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkEntries()
|
||||
{
|
||||
if(entries == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
readEntries();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
System.err.println("*** " + e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the central directory of a zip file and fill the entries
|
||||
* array. This is called exactly once when first needed. It is called
|
||||
* while holding the lock on <code>rais</code>.
|
||||
*
|
||||
* @exception IOException if a i/o error occured.
|
||||
* @exception ZipException if the central directory is malformed
|
||||
*/
|
||||
private void readEntries() throws IOException
|
||||
{
|
||||
/*
|
||||
* Search for the End Of Central Directory. When a zip comment is
|
||||
* present the directory may start earlier.
|
||||
* Note that a comment has a maximum length of 64K, so that is the
|
||||
* maximum we search backwards.
|
||||
*/
|
||||
|
||||
rais.seek(0);
|
||||
BufDataInputStream inp = new BufDataInputStream(rais);
|
||||
|
||||
int pos = rais.getCapacity() - ENDHDR;
|
||||
int top = Math.max(0, pos - 0xFFFF);
|
||||
|
||||
/* -- VERY slow with current implementation
|
||||
do
|
||||
{
|
||||
if(pos < top)
|
||||
{
|
||||
throw new ZipException("Central Directory not found, probably not a zip file");
|
||||
}
|
||||
|
||||
inp.seek(pos--);
|
||||
} while(!cder.read(inp));
|
||||
*/
|
||||
|
||||
inp.seek(pos);
|
||||
|
||||
if(!cder.read(inp))
|
||||
{
|
||||
byte[] sig = new byte[4];
|
||||
sig[0] = (byte)(ENDSIG & 0xFF);
|
||||
sig[1] = (byte)((ENDSIG >> 8) & 0xFF);
|
||||
sig[2] = (byte)((ENDSIG >> 16) & 0xFF);
|
||||
sig[3] = (byte)((ENDSIG >> 24) & 0xFF);
|
||||
|
||||
pos = inp.indexOf(sig, 0, 4, top, pos);
|
||||
|
||||
if(pos >= 0)
|
||||
{
|
||||
inp.seek(pos);
|
||||
cder.read(inp);
|
||||
}
|
||||
else
|
||||
{
|
||||
entries = new Hashtable();
|
||||
throw new ZipException("Central Directory not found, probably not a zip file");
|
||||
}
|
||||
}
|
||||
|
||||
entries = new Hashtable(cder.numEntries + cder.numEntries / 2);
|
||||
ZipEntry entry;
|
||||
|
||||
inp.seek(cder.centralOffset);
|
||||
|
||||
for(int i = 0; i < cder.numEntries; i++)
|
||||
{
|
||||
entry = new ZipEntry();
|
||||
|
||||
if(!entry.readCentralHeader(inp))
|
||||
{
|
||||
throw new ZipException("Wrong Central Directory signature");
|
||||
}
|
||||
|
||||
entries.put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new entry to the current zip archive.
|
||||
* If entry with such name already exists,
|
||||
* it is deleted and then new entry is added.
|
||||
* If source stream is null, then an empty entry is written.
|
||||
* The file data is entirely copied from specified input stream,
|
||||
* compressed with DEFTATED method and specified compression level,
|
||||
* and zip entry with specified name is written into the file,
|
||||
* overwriting central directory (if present).
|
||||
* Therefore zip file is no longer valid until finish() is called
|
||||
* and correct central directory is restored at it's end.
|
||||
*/
|
||||
public boolean addEntry(InputStream source, String name, long time, int level, boolean replace, ProgressCallback callback) throws IOException
|
||||
{
|
||||
if(replace)
|
||||
{
|
||||
deleteEntry(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkReadOnly();
|
||||
|
||||
if(getEntry(name) != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(callback != null)
|
||||
{
|
||||
int index = name.lastIndexOf('/');
|
||||
|
||||
if(index >= 0 && index < name.length())
|
||||
{
|
||||
callback.setText(name.substring(index + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.setText(name);
|
||||
}
|
||||
}
|
||||
|
||||
ZipEntry entry = new ZipEntry(name);
|
||||
|
||||
if(time < 0)
|
||||
{
|
||||
time = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
entry.setTime(time);
|
||||
entry.setMethod(DEFLATED);
|
||||
entry.flags = 8;
|
||||
entry.offset = cder.centralOffset;
|
||||
|
||||
OutputStream os = fc.openOutputStream(cder.centralOffset);
|
||||
finished = false;
|
||||
|
||||
/* Write the local file header */
|
||||
cder.centralOffset += entry.writeLocalHeader(os);
|
||||
|
||||
CRC32 crc = new CRC32();
|
||||
DeflaterOutputStream dos = new DeflaterOutputStream(os, new Deflater(level, true));
|
||||
int size = 0;
|
||||
|
||||
if(source != null)
|
||||
{
|
||||
byte[] buf = new byte[main.ARCBUFSIZE];
|
||||
int len;
|
||||
|
||||
if(callback != null)
|
||||
{
|
||||
while(source.available() > 0)
|
||||
{
|
||||
len = source.read(buf);
|
||||
|
||||
dos.write(buf, 0, len);
|
||||
crc.update(buf, 0, len);
|
||||
|
||||
size += len;
|
||||
callback.progress(len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(source.available() > 0)
|
||||
{
|
||||
len = source.read(buf);
|
||||
|
||||
dos.write(buf, 0, len);
|
||||
crc.update(buf, 0, len);
|
||||
|
||||
size += len;
|
||||
}
|
||||
}
|
||||
|
||||
source.close();
|
||||
}
|
||||
|
||||
dos.finish();
|
||||
|
||||
int csize = dos.getTotalOut();
|
||||
cder.centralOffset += csize;
|
||||
|
||||
entry.setSize(size);
|
||||
entry.setCompressedSize(csize);
|
||||
entry.setCRC((int)crc.getValue());
|
||||
|
||||
/* Now write the data descriptor entry if needed. */
|
||||
if((entry.flags & 8) != 0)
|
||||
{
|
||||
writeLeInt(os, EXTSIG);
|
||||
|
||||
writeLeInt(os, entry.getCRC());
|
||||
writeLeInt(os, entry.getCompressedSize());
|
||||
writeLeInt(os, entry.getSize());
|
||||
|
||||
cder.centralOffset += EXTHDR;
|
||||
}
|
||||
|
||||
dos.close();
|
||||
|
||||
entries.put(name, entry);
|
||||
rais.update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an entry with specified name from the current zip archive.
|
||||
* It also totally truncates central directory,
|
||||
* so finish() must be called to restore this zip file valid.
|
||||
*/
|
||||
public boolean deleteEntry(String name) throws IOException
|
||||
{
|
||||
checkReadOnly();
|
||||
|
||||
ZipEntry entry = getEntry(name);
|
||||
|
||||
if(entry == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
finished = false;
|
||||
|
||||
rais.close();
|
||||
|
||||
int delta = filesystem.updateFileData(fc, new byte[0], entry.offset, entry.offset + entry.getLocalSize());
|
||||
cder.centralOffset += delta;
|
||||
fc.truncate(cder.centralOffset);
|
||||
|
||||
rais = new FileInputStream(fc);
|
||||
|
||||
updateOffset(entry.offset, delta);
|
||||
entries.remove(entry.getName());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename specified entry.
|
||||
*/
|
||||
public boolean renameEntry(String name, String newName) throws IOException
|
||||
{
|
||||
checkReadOnly();
|
||||
|
||||
ZipEntry entry = getEntry(name);
|
||||
|
||||
if(entry == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] b = encodeString(newName);
|
||||
finished = false;
|
||||
|
||||
rais.close();
|
||||
|
||||
int delta = filesystem.updateFileData(fc, b, entry.offset + LOCHDR, entry.offset + LOCHDR + getEncodedLength(entry.getName()));
|
||||
cder.centralOffset += delta;
|
||||
fc.truncate(cder.centralOffset);
|
||||
|
||||
int len = b.length;
|
||||
|
||||
b = new byte[2];
|
||||
b[0] = (byte)(len & 0xFF);
|
||||
b[1] = (byte)((len & 0xFF) >> 8);
|
||||
|
||||
filesystem.updateFileData(fc, b, entry.offset + LOCNAM, entry.offset + LOCNAM + 2);
|
||||
|
||||
rais = new FileInputStream(fc);
|
||||
|
||||
updateOffset(entry.offset, delta);
|
||||
entries.remove(entry.getName());
|
||||
entry.setName(newName);
|
||||
entries.put(entry.getName(), entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update offsets of entries in hashtable.
|
||||
*/
|
||||
private void updateOffset(int base, int delta) throws IOException
|
||||
{
|
||||
if(finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
checkReadOnly();
|
||||
|
||||
Enumeration e = entries();
|
||||
ZipEntry entry;
|
||||
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
entry = (ZipEntry)e.nextElement();
|
||||
|
||||
if(entry.offset > base)
|
||||
{
|
||||
entry.offset += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write central directory at the end of file.
|
||||
*/
|
||||
public void finish() throws IOException
|
||||
{
|
||||
if(finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
checkReadOnly();
|
||||
|
||||
Enumeration enm = entries();
|
||||
ZipEntry entry;
|
||||
|
||||
cder.centralSize = 0;
|
||||
cder.numEntries = 0;
|
||||
|
||||
OutputStream os = fc.openOutputStream(cder.centralOffset);
|
||||
|
||||
while(enm.hasMoreElements())
|
||||
{
|
||||
entry = (ZipEntry)enm.nextElement();
|
||||
|
||||
cder.centralSize += entry.writeCentralHeader(os);
|
||||
cder.numEntries++;
|
||||
}
|
||||
|
||||
cder.write(os);
|
||||
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
finished = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the ZipFile. This also closes all input streams given by
|
||||
* this class. After this is called, no further method should be called.
|
||||
*
|
||||
* @exception IOException if a i/o error occured.
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
if(!finished)
|
||||
{
|
||||
finish();
|
||||
}
|
||||
|
||||
closed = true;
|
||||
entries = null;
|
||||
|
||||
if(rais != null)
|
||||
{
|
||||
rais.close();
|
||||
rais = null;
|
||||
}
|
||||
|
||||
if(fc != null)
|
||||
{
|
||||
fc.close();
|
||||
fc = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enumeration of all Zip entries in this Zip file.
|
||||
*
|
||||
* @exception IllegalStateException when the ZipFile has already been closed
|
||||
*/
|
||||
public Enumeration entries()
|
||||
{
|
||||
checkClosed();
|
||||
checkEntries();
|
||||
|
||||
return entries.elements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a zip entry in this archive with the given name.
|
||||
*
|
||||
* @param name the name. May contain directory components separated by
|
||||
* slashes ('/').
|
||||
* @return the zip entry, or null if no entry with that name exists.
|
||||
*
|
||||
* @exception IllegalStateException when the ZipFile has already been closed
|
||||
*/
|
||||
public ZipEntry getEntry(String name)
|
||||
{
|
||||
checkClosed();
|
||||
checkEntries();
|
||||
|
||||
ZipEntry entry = (ZipEntry)entries.get(name);
|
||||
|
||||
// If we didn't find it, maybe it's a directory.
|
||||
// -- Such behaviour can cause promlems when adding
|
||||
// an entry with replacement of existing one
|
||||
// (we might replace a folder with a file in particular).
|
||||
/*
|
||||
if(entry == null && !name.endsWith("/"))
|
||||
{
|
||||
entry = (ZipEntry)entries.get(name + '/');
|
||||
}
|
||||
*/
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an input stream reading the given zip entry as
|
||||
* uncompressed data. Normally zip entry should be an entry
|
||||
* returned by getEntry() or entries().
|
||||
*
|
||||
* This implementation returns null if the requested entry does not
|
||||
* exist. This decision is not obviously correct, however, it does
|
||||
* appear to mirror Sun's implementation, and it is consistant with
|
||||
* their javadoc. On the other hand, the old JCL book, 2nd Edition,
|
||||
* claims that this should return a "non-null ZIP entry". We have
|
||||
* chosen for now ignore the old book, as modern versions of Ant (an
|
||||
* important application) depend on this behaviour. See discussion
|
||||
* in this thread:
|
||||
* http://gcc.gnu.org/ml/java-patches/2004-q2/msg00602.html
|
||||
*
|
||||
* @param entry the entry to create an InputStream for.
|
||||
* @return the input stream, or null if the requested entry does not exist.
|
||||
*
|
||||
* @exception IllegalStateException when the ZipFile has already been closed
|
||||
* @exception IOException if a i/o error occured.
|
||||
* @exception ZipException if the Zip archive is malformed.
|
||||
*/
|
||||
public InputStream getInputStream(String name) throws IOException
|
||||
{
|
||||
ZipEntry entry = getEntry(name);
|
||||
|
||||
if(entry == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ZipEntry readEntry = new ZipEntry();
|
||||
|
||||
rais.seek(entry.offset);
|
||||
|
||||
if(!readEntry.readLocalHeader(rais))
|
||||
{
|
||||
throw new ZipException("Wrong Local Header signature");
|
||||
}
|
||||
|
||||
PartialInputStream inp = new PartialInputStream(rais, rais.tell(), entry.getCompressedSize());
|
||||
|
||||
switch(entry.getMethod())
|
||||
{
|
||||
case STORED:
|
||||
return inp;
|
||||
|
||||
case DEFLATED:
|
||||
inp.addDummyByte();
|
||||
final Inflater inf = new Inflater(true);
|
||||
final int sz = entry.getSize();
|
||||
|
||||
return new InflaterInputStream(inp, inf)
|
||||
{
|
||||
public int available() throws IOException
|
||||
{
|
||||
if(sz == -1)
|
||||
{
|
||||
return super.available();
|
||||
}
|
||||
|
||||
if(super.available() != 0)
|
||||
{
|
||||
return sz - inf.getTotalOut();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
throw new ZipException("Unknown compression method " + entry.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of entries in this zip file.
|
||||
*
|
||||
* @exception IllegalStateException when the ZipFile has already been closed
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
checkClosed();
|
||||
checkEntries();
|
||||
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
public static int readLeInt(InputStream is) throws IOException
|
||||
{
|
||||
return (is.read() & 0xFF) | ((is.read() & 0xFF) << 8) | ((is.read() & 0xFF) << 16) | ((is.read() & 0xFF) << 24);
|
||||
}
|
||||
|
||||
public static int readLeShort(InputStream is) throws IOException
|
||||
{
|
||||
return (is.read() & 0xFF) | ((is.read() & 0xFF) << 8);
|
||||
}
|
||||
|
||||
public static String readString(InputStream is, int len) throws IOException
|
||||
{
|
||||
byte[] b = new byte[len];
|
||||
len = is.read(b, 0, len);
|
||||
|
||||
return StringEncoder.decodeString(b, 0, len, options.arcEnc);
|
||||
}
|
||||
|
||||
public static void writeLeInt(OutputStream os, int value) throws IOException
|
||||
{
|
||||
os.write(value & 0xFF);
|
||||
os.write((value >> 8) & 0xFF);
|
||||
os.write((value >> 16) & 0xFF);
|
||||
os.write((value >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
public static void writeLeShort(OutputStream os, int value) throws IOException
|
||||
{
|
||||
os.write(value & 0xFF);
|
||||
os.write((value >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
public static byte[] encodeString(String value)
|
||||
{
|
||||
return StringEncoder.encodeString(value, options.arcEnc);
|
||||
}
|
||||
|
||||
public static int getEncodedLength(String value)
|
||||
{
|
||||
return StringEncoder.getEncodedLength(value, options.arcEnc);
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[ZipFile] " + s);
|
||||
// }
|
||||
}
|
382
src/com/classpath/zip/ZipInputStream.java
Normal file
382
src/com/classpath/zip/ZipInputStream.java
Normal file
@ -0,0 +1,382 @@
|
||||
/* ZipInputStream.java --
|
||||
Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
|
||||
//package java.util.zip;
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* This is a FilterInputStream that reads the files in an zip archive
|
||||
* one after another. It has a special method to get the zip entry of
|
||||
* the next file. The zip entry contains information about the file name
|
||||
* size, compressed size, CRC, etc.
|
||||
*
|
||||
* It includes support for STORED and DEFLATED entries.
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
*/
|
||||
public class ZipInputStream extends InflaterInputStream implements ZipConstants
|
||||
{
|
||||
private CRC32 crc = new CRC32();
|
||||
private ZipEntry entry = null;
|
||||
|
||||
private int csize;
|
||||
private int size;
|
||||
private int method;
|
||||
private int flags;
|
||||
private int avail;
|
||||
private boolean entryAtEOF;
|
||||
|
||||
/**
|
||||
* Creates a new Zip input stream, reading a zip archive.
|
||||
*/
|
||||
public ZipInputStream(InputStream in)
|
||||
{
|
||||
super(in, new Inflater(true));
|
||||
}
|
||||
|
||||
private void fillBuf() throws IOException
|
||||
{
|
||||
avail = len = in.read(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
private int readBuf(byte[] out, int offset, int length) throws IOException
|
||||
{
|
||||
if(avail <= 0)
|
||||
{
|
||||
fillBuf();
|
||||
if(avail <= 0)
|
||||
return -1;
|
||||
}
|
||||
if(length > avail)
|
||||
length = avail;
|
||||
System.arraycopy(buf, len - avail, out, offset, length);
|
||||
avail -= length;
|
||||
return length;
|
||||
}
|
||||
|
||||
private void readFully(byte[] out) throws IOException
|
||||
{
|
||||
int off = 0;
|
||||
int len = out.length;
|
||||
while(len > 0)
|
||||
{
|
||||
int count = readBuf(out, off, len);
|
||||
if(count == -1)
|
||||
throw new EOFException();
|
||||
off += count;
|
||||
len -= count;
|
||||
}
|
||||
}
|
||||
|
||||
private int readLeByte() throws IOException
|
||||
{
|
||||
if(avail <= 0)
|
||||
{
|
||||
fillBuf();
|
||||
if(avail <= 0)
|
||||
throw new ZipException("EOF in header");
|
||||
}
|
||||
return buf[len - avail--] & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an unsigned short in little endian byte order.
|
||||
*/
|
||||
private int readLeShort() throws IOException
|
||||
{
|
||||
return readLeByte() | (readLeByte() << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an int in little endian byte order.
|
||||
*/
|
||||
private int readLeInt() throws IOException
|
||||
{
|
||||
return readLeShort() | (readLeShort() << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the next entry from the zip archive, and return its description.
|
||||
* If the previous entry wasn't closed, this method will close it.
|
||||
*/
|
||||
public ZipEntry getNextEntry() throws IOException
|
||||
{
|
||||
if(crc == null)
|
||||
throw new IOException("Stream closed.");
|
||||
if(entry != null)
|
||||
closeEntry();
|
||||
|
||||
int header = readLeInt();
|
||||
if(header == CENSIG)
|
||||
{
|
||||
/* Central Header reached. */
|
||||
close();
|
||||
return null;
|
||||
}
|
||||
if(header != LOCSIG)
|
||||
throw new ZipException("Wrong Local header signature: "
|
||||
+ Integer.toHexString(header));
|
||||
/* skip version */
|
||||
readLeShort();
|
||||
flags = readLeShort();
|
||||
method = readLeShort();
|
||||
int dostime = readLeInt();
|
||||
int crc = readLeInt();
|
||||
csize = readLeInt();
|
||||
size = readLeInt();
|
||||
int nameLen = readLeShort();
|
||||
int extraLen = readLeShort();
|
||||
|
||||
if(method == ZipOutputStream.STORED && csize != size)
|
||||
throw new ZipException("Stored, but compressed != uncompressed");
|
||||
|
||||
|
||||
byte[] buffer = new byte[nameLen];
|
||||
readFully(buffer);
|
||||
String name;
|
||||
try
|
||||
{
|
||||
name = new String(buffer, "UTF-8");
|
||||
}
|
||||
catch(UnsupportedEncodingException uee)
|
||||
{
|
||||
throw new IOException("Assertion error: " + uee.getMessage());
|
||||
}
|
||||
|
||||
entry = createZipEntry(name);
|
||||
entryAtEOF = false;
|
||||
entry.setMethod(method);
|
||||
if((flags & 8) == 0)
|
||||
{
|
||||
entry.setCRC(crc);
|
||||
entry.setSize(size);
|
||||
entry.setCompressedSize(csize);
|
||||
}
|
||||
entry.setDOSTime(dostime);
|
||||
if(extraLen > 0)
|
||||
{
|
||||
byte[] extra = new byte[extraLen];
|
||||
readFully(extra);
|
||||
entry.setExtra(extra);
|
||||
}
|
||||
|
||||
if(method == ZipOutputStream.DEFLATED && avail > 0)
|
||||
{
|
||||
System.arraycopy(buf, len - avail, buf, 0, avail);
|
||||
len = avail;
|
||||
avail = 0;
|
||||
inf.setInput(buf, 0, len);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void readDataDescr() throws IOException
|
||||
{
|
||||
if(readLeInt() != EXTSIG)
|
||||
throw new ZipException("Data descriptor signature not found");
|
||||
entry.setCRC(readLeInt());
|
||||
csize = readLeInt();
|
||||
size = readLeInt();
|
||||
entry.setSize(size);
|
||||
entry.setCompressedSize(csize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current zip entry and moves to the next one.
|
||||
*/
|
||||
public void closeEntry() throws IOException
|
||||
{
|
||||
if(crc == null)
|
||||
throw new IOException("Stream closed.");
|
||||
if(entry == null)
|
||||
return;
|
||||
|
||||
if(method == ZipOutputStream.DEFLATED)
|
||||
{
|
||||
if((flags & 8) != 0)
|
||||
{
|
||||
/* We don't know how much we must skip, read until end. */
|
||||
byte[] tmp = new byte[2048];
|
||||
while(read(tmp) > 0)
|
||||
;
|
||||
|
||||
/* read will close this entry */
|
||||
return;
|
||||
}
|
||||
csize -= inf.getTotalIn();
|
||||
avail = inf.getRemaining();
|
||||
}
|
||||
|
||||
if(avail > csize && csize >= 0)
|
||||
avail -= csize;
|
||||
else
|
||||
{
|
||||
csize -= avail;
|
||||
avail = 0;
|
||||
while(csize != 0)
|
||||
{
|
||||
long skipped = in.skip(csize & 0xffffffffL);
|
||||
if(skipped <= 0)
|
||||
throw new ZipException("zip archive ends early.");
|
||||
csize -= skipped;
|
||||
}
|
||||
}
|
||||
|
||||
size = 0;
|
||||
crc.reset();
|
||||
if(method == ZipOutputStream.DEFLATED)
|
||||
inf.reset();
|
||||
entry = null;
|
||||
entryAtEOF = true;
|
||||
}
|
||||
|
||||
public int available() throws IOException
|
||||
{
|
||||
return entryAtEOF ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte from the current zip entry.
|
||||
* @return the byte or -1 on EOF.
|
||||
* @exception IOException if a i/o error occured.
|
||||
* @exception ZipException if the deflated stream is corrupted.
|
||||
*/
|
||||
public int read() throws IOException
|
||||
{
|
||||
byte[] b = new byte[1];
|
||||
if(read(b, 0, 1) <= 0)
|
||||
return -1;
|
||||
return b[0] & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a block of bytes from the current zip entry.
|
||||
* @return the number of bytes read (may be smaller, even before
|
||||
* EOF), or -1 on EOF.
|
||||
* @exception IOException if a i/o error occured.
|
||||
* @exception ZipException if the deflated stream is corrupted.
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if(len == 0)
|
||||
return 0;
|
||||
if(crc == null)
|
||||
throw new IOException("Stream closed.");
|
||||
if(entry == null)
|
||||
return -1;
|
||||
boolean finished = false;
|
||||
switch(method)
|
||||
{
|
||||
case ZipOutputStream.DEFLATED:
|
||||
len = super.read(b, off, len);
|
||||
if(len < 0)
|
||||
{
|
||||
if(!inf.finished())
|
||||
throw new ZipException("Inflater not finished!?");
|
||||
avail = inf.getRemaining();
|
||||
if((flags & 8) != 0)
|
||||
readDataDescr();
|
||||
|
||||
if(inf.getTotalIn() != csize
|
||||
|| inf.getTotalOut() != size)
|
||||
throw new ZipException("size mismatch: " + csize + ";" + size + " <-> " + inf.getTotalIn() + ";" + inf.getTotalOut());
|
||||
inf.reset();
|
||||
finished = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZipOutputStream.STORED:
|
||||
|
||||
if(len > csize && csize >= 0)
|
||||
len = csize;
|
||||
|
||||
len = readBuf(b, off, len);
|
||||
if(len > 0)
|
||||
{
|
||||
csize -= len;
|
||||
size -= len;
|
||||
}
|
||||
|
||||
if(csize == 0)
|
||||
finished = true;
|
||||
else if(len < 0)
|
||||
throw new ZipException("EOF in stored block");
|
||||
break;
|
||||
}
|
||||
|
||||
if(len > 0)
|
||||
crc.update(b, off, len);
|
||||
|
||||
if(finished)
|
||||
{
|
||||
if((crc.getValue() & 0xffffffffL) != entry.getCRC())
|
||||
throw new ZipException("CRC mismatch");
|
||||
crc.reset();
|
||||
entry = null;
|
||||
entryAtEOF = true;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the zip file.
|
||||
* @exception IOException if a i/o error occured.
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
crc = null;
|
||||
entry = null;
|
||||
entryAtEOF = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new zip entry for the given name. This is equivalent
|
||||
* to new ZipEntry(name).
|
||||
* @param name the name of the zip entry.
|
||||
*/
|
||||
protected ZipEntry createZipEntry(String name)
|
||||
{
|
||||
return new ZipEntry(name);
|
||||
}
|
||||
}
|
467
src/com/classpath/zip/ZipOutputStream.java
Normal file
467
src/com/classpath/zip/ZipOutputStream.java
Normal file
@ -0,0 +1,467 @@
|
||||
/* java.util.zip.ZipOutputStream
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package com.classpath.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This is a FilterOutputStream that writes the files into a zip
|
||||
* archive one after another. It has a special method to start a new
|
||||
* zip entry. The zip entries contains information about the file name
|
||||
* size, compressed size, CRC, etc.
|
||||
*
|
||||
* It includes support for STORED and DEFLATED entries.
|
||||
*
|
||||
* This class is not thread safe.
|
||||
* Referenced classes from java.util.zip:
|
||||
* DeflaterOutputStream, Deflater, ZipConstants, ZipEntry, ZipException
|
||||
*
|
||||
* @author Jochen Hoenicke
|
||||
*/
|
||||
public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants
|
||||
{
|
||||
private Vector entries = new Vector();
|
||||
private CRC32 crc = new CRC32();
|
||||
private ZipEntry curEntry = null;
|
||||
|
||||
private int curMethod;
|
||||
private int size;
|
||||
private int offset = 0;
|
||||
|
||||
private byte[] zipComment = new byte[0];
|
||||
private int defaultMethod = DEFLATED;
|
||||
|
||||
/**
|
||||
* Our Zip version is hard coded to 1.0 resp. 2.0
|
||||
*/
|
||||
private final static int ZIP_STORED_VERSION = 10;
|
||||
private final static int ZIP_DEFLATED_VERSION = 20;
|
||||
|
||||
/**
|
||||
* Compression method. This method doesn't compress at all.
|
||||
*/
|
||||
public final static int STORED = 0;
|
||||
/**
|
||||
* Compression method. This method uses the Deflater.
|
||||
*/
|
||||
public final static int DEFLATED = 8;
|
||||
|
||||
/**
|
||||
* Creates a new Zip output stream, writing a zip archive.
|
||||
* @param out the output stream to which the zip archive is written.
|
||||
*/
|
||||
public ZipOutputStream(OutputStream out)
|
||||
{
|
||||
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the zip file comment.
|
||||
* @param comment the comment.
|
||||
* @exception IllegalArgumentException if encoding of comment is
|
||||
* longer than 0xffff bytes.
|
||||
*/
|
||||
public void setComment(String comment)
|
||||
{
|
||||
byte[] commentBytes;
|
||||
commentBytes = comment.getBytes();
|
||||
if(commentBytes.length > 0xffff)
|
||||
throw new IllegalArgumentException("Comment too long.");
|
||||
zipComment = commentBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default compression method. If the Zip entry specifies
|
||||
* another method its method takes precedence.
|
||||
* @param method the method.
|
||||
* @exception IllegalArgumentException if method is not supported.
|
||||
* @see #STORED
|
||||
* @see #DEFLATED
|
||||
*/
|
||||
public void setMethod(int method)
|
||||
{
|
||||
if(method != STORED && method != DEFLATED)
|
||||
{
|
||||
throw new IllegalArgumentException("Method not supported.");
|
||||
}
|
||||
|
||||
defaultMethod = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default compression level. The new level will be activated
|
||||
* immediately.
|
||||
* @exception IllegalArgumentException if level is not supported.
|
||||
* @see Deflater
|
||||
*/
|
||||
public void setLevel(int level)
|
||||
{
|
||||
def.setLevel(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an unsigned short in little endian byte order.
|
||||
*/
|
||||
private final void writeLeShort(int value) throws IOException
|
||||
{
|
||||
out.write(value & 0xff);
|
||||
out.write((value >> 8) & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an int in little endian byte order.
|
||||
*/
|
||||
private final void writeLeInt(int value) throws IOException
|
||||
{
|
||||
writeLeShort(value);
|
||||
writeLeShort(value >> 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new Zip entry. It automatically closes the previous
|
||||
* entry if present. If the compression method is stored, the entry
|
||||
* must have a valid size and crc, otherwise all elements (except
|
||||
* name) are optional, but must be correct if present. If the time
|
||||
* is not set in the entry, the current time is used.
|
||||
* @param entry the entry.
|
||||
* @exception IOException if an I/O error occured.
|
||||
* @exception ZipException if stream was finished.
|
||||
*/
|
||||
public void putNextEntry(ZipEntry entry) throws IOException
|
||||
{
|
||||
if(entries == null)
|
||||
{
|
||||
throw new ZipException("ZipOutputStream was finished");
|
||||
}
|
||||
|
||||
int method = entry.getMethod();
|
||||
int flags = 0;
|
||||
|
||||
if(method == -1)
|
||||
{
|
||||
method = defaultMethod;
|
||||
}
|
||||
|
||||
if(method == STORED)
|
||||
{
|
||||
if(entry.getCompressedSize() >= 0)
|
||||
{
|
||||
if(entry.getSize() < 0)
|
||||
{
|
||||
entry.setSize(entry.getCompressedSize());
|
||||
}
|
||||
else if(entry.getSize() != entry.getCompressedSize())
|
||||
{
|
||||
throw new ZipException("Method STORED, but compressed size != size");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.setCompressedSize(entry.getSize());
|
||||
}
|
||||
|
||||
if(entry.getSize() < 0)
|
||||
{
|
||||
throw new ZipException("Method STORED, but size not set");
|
||||
}
|
||||
|
||||
if(entry.getCRC() < 0)
|
||||
{
|
||||
throw new ZipException("Method STORED, but crc not set");
|
||||
}
|
||||
}
|
||||
else if(method == DEFLATED)
|
||||
{
|
||||
if(entry.getCompressedSize() < 0 || entry.getSize() < 0 || entry.getCRC() < 0)
|
||||
{
|
||||
flags |= 8;
|
||||
}
|
||||
}
|
||||
|
||||
if(curEntry != null)
|
||||
{
|
||||
closeEntry();
|
||||
}
|
||||
|
||||
if(entry.getTime() < 0)
|
||||
{
|
||||
entry.setTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
entry.flags = flags;
|
||||
entry.offset = offset;
|
||||
entry.setMethod(method);
|
||||
curMethod = method;
|
||||
|
||||
/* Write the local file header */
|
||||
writeLeInt(LOCSIG);
|
||||
writeLeShort(method == STORED ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
|
||||
writeLeShort(flags);
|
||||
writeLeShort(method);
|
||||
writeLeInt(entry.getDOSTime());
|
||||
|
||||
if((flags & 8) == 0)
|
||||
{
|
||||
writeLeInt((int)entry.getCRC());
|
||||
writeLeInt((int)entry.getCompressedSize());
|
||||
writeLeInt((int)entry.getSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
writeLeInt(0);
|
||||
writeLeInt(0);
|
||||
writeLeInt(0);
|
||||
}
|
||||
|
||||
byte[] name = entry.getName().getBytes();
|
||||
|
||||
if(name.length > 0xffff)
|
||||
{
|
||||
throw new ZipException("Name too long.");
|
||||
}
|
||||
|
||||
byte[] extra = entry.getExtra();
|
||||
|
||||
if(extra == null)
|
||||
{
|
||||
extra = new byte[0];
|
||||
}
|
||||
|
||||
writeLeShort(name.length);
|
||||
writeLeShort(extra.length);
|
||||
|
||||
out.write(name);
|
||||
out.write(extra);
|
||||
|
||||
offset += LOCHDR + name.length + extra.length;
|
||||
|
||||
/* Activate the entry. */
|
||||
|
||||
curEntry = entry;
|
||||
|
||||
crc.reset();
|
||||
|
||||
if(method == DEFLATED)
|
||||
{
|
||||
def.reset();
|
||||
}
|
||||
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current entry.
|
||||
* @exception IOException if an I/O error occured.
|
||||
* @exception ZipException if no entry is active.
|
||||
*/
|
||||
public void closeEntry() throws IOException
|
||||
{
|
||||
if(curEntry == null)
|
||||
{
|
||||
throw new ZipException("No open entry");
|
||||
}
|
||||
|
||||
/* First finish the deflater, if appropriate */
|
||||
if(curMethod == DEFLATED)
|
||||
{
|
||||
super.finish();
|
||||
}
|
||||
|
||||
int csize = curMethod == DEFLATED ? def.getTotalOut() : size;
|
||||
|
||||
if(curEntry.getSize() < 0)
|
||||
{
|
||||
curEntry.setSize(size);
|
||||
}
|
||||
else if(curEntry.getSize() != size)
|
||||
{
|
||||
throw new ZipException("size was " + size + ", but I expected " + curEntry.getSize());
|
||||
}
|
||||
|
||||
if(curEntry.getCompressedSize() < 0)
|
||||
{
|
||||
curEntry.setCompressedSize(csize);
|
||||
}
|
||||
else if(curEntry.getCompressedSize() != csize)
|
||||
{
|
||||
throw new ZipException("compressed size was " + csize + ", but I expected " + curEntry.getSize());
|
||||
}
|
||||
|
||||
if(curEntry.getCRC() < 0)
|
||||
{
|
||||
curEntry.setCRC((int)crc.getValue());
|
||||
}
|
||||
else if(curEntry.getCRC() != crc.getValue())
|
||||
{
|
||||
throw new ZipException("crc was " + Long.toString(crc.getValue(), 16) + ", but I expected " + Long.toString(curEntry.getCRC(), 16));
|
||||
}
|
||||
|
||||
offset += csize;
|
||||
|
||||
/* Now write the data descriptor entry if needed. */
|
||||
if(curMethod == DEFLATED && (curEntry.flags & 8) != 0)
|
||||
{
|
||||
writeLeInt(EXTSIG);
|
||||
writeLeInt((int)curEntry.getCRC());
|
||||
writeLeInt((int)curEntry.getCompressedSize());
|
||||
writeLeInt((int)curEntry.getSize());
|
||||
offset += EXTHDR;
|
||||
}
|
||||
|
||||
entries.addElement(curEntry);
|
||||
curEntry = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given buffer to the current entry.
|
||||
* @exception IOException if an I/O error occured.
|
||||
* @exception ZipException if no entry is active.
|
||||
*/
|
||||
public void write(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if(curEntry == null)
|
||||
throw new ZipException("No open entry.");
|
||||
|
||||
switch(curMethod)
|
||||
{
|
||||
case DEFLATED:
|
||||
super.write(b, off, len);
|
||||
break;
|
||||
|
||||
case STORED:
|
||||
out.write(b, off, len);
|
||||
break;
|
||||
}
|
||||
|
||||
crc.update(b, off, len);
|
||||
size += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the stream. This will write the central directory at the
|
||||
* end of the zip file and flush the stream.
|
||||
* @exception IOException if an I/O error occured.
|
||||
*/
|
||||
public void finish() throws IOException
|
||||
{
|
||||
if(entries == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(curEntry != null)
|
||||
{
|
||||
closeEntry();
|
||||
}
|
||||
|
||||
int numEntries = 0;
|
||||
int sizeEntries = 0;
|
||||
|
||||
Enumeration enm = entries.elements();
|
||||
|
||||
while(enm.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = (ZipEntry)enm.nextElement();
|
||||
|
||||
int method = entry.getMethod();
|
||||
|
||||
writeLeInt(CENSIG);
|
||||
|
||||
writeLeShort(method == STORED ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
|
||||
writeLeShort(method == STORED ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
|
||||
writeLeShort(entry.flags);
|
||||
writeLeShort(method);
|
||||
writeLeInt(entry.getDOSTime());
|
||||
writeLeInt((int)entry.getCRC());
|
||||
writeLeInt((int)entry.getCompressedSize());
|
||||
writeLeInt((int)entry.getSize());
|
||||
|
||||
byte[] name = entry.getName().getBytes();
|
||||
|
||||
if(name.length > 0xffff)
|
||||
{
|
||||
throw new ZipException("Name too long.");
|
||||
}
|
||||
|
||||
byte[] extra = entry.getExtra();
|
||||
|
||||
if(extra == null)
|
||||
{
|
||||
extra = new byte[0];
|
||||
}
|
||||
|
||||
String strComment = entry.getComment();
|
||||
|
||||
byte[] comment = strComment != null ? strComment.getBytes() : new byte[0];
|
||||
|
||||
if(comment.length > 0xffff)
|
||||
{
|
||||
throw new ZipException("Comment too long.");
|
||||
}
|
||||
|
||||
writeLeShort(name.length);
|
||||
writeLeShort(extra.length);
|
||||
writeLeShort(comment.length);
|
||||
writeLeShort(0); /* disk number */
|
||||
writeLeShort(0); /* internal file attr */
|
||||
writeLeInt(0); /* external file attr */
|
||||
writeLeInt(entry.offset);
|
||||
|
||||
out.write(name);
|
||||
out.write(extra);
|
||||
out.write(comment);
|
||||
numEntries++;
|
||||
sizeEntries += CENHDR + name.length + extra.length + comment.length;
|
||||
}
|
||||
|
||||
writeLeInt(ENDSIG);
|
||||
writeLeShort(0); /* disk number */
|
||||
writeLeShort(0); /* disk with start of central dir */
|
||||
writeLeShort(numEntries);
|
||||
writeLeShort(numEntries);
|
||||
writeLeInt(sizeEntries);
|
||||
writeLeInt(offset);
|
||||
writeLeShort(zipComment.length);
|
||||
out.write(zipComment);
|
||||
out.flush();
|
||||
entries = null;
|
||||
}
|
||||
}
|
947
src/com/ibxm/Channel.java
Normal file
947
src/com/ibxm/Channel.java
Normal file
@ -0,0 +1,947 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
public class Channel {
|
||||
public int pattern_loop_row;
|
||||
|
||||
private Module module;
|
||||
private Instrument instrument;
|
||||
private Sample sample;
|
||||
private int[] global_volume, current_note;
|
||||
private boolean linear_periods, fast_volume_slides, key_on, silent;
|
||||
private int sample_idx, sample_frac, step, left_gain, right_gain;
|
||||
private int volume, panning, fine_tune, period, porta_period, key_add;
|
||||
private int tremolo_speed, tremolo_depth, tremolo_tick, tremolo_wave, tremolo_add;
|
||||
private int vibrato_speed, vibrato_depth, vibrato_tick, vibrato_wave, vibrato_add;
|
||||
private int volume_slide_param, portamento_param, retrig_param;
|
||||
private int volume_envelope_tick, panning_envelope_tick;
|
||||
private int effect_tick, trigger_tick, fade_out_volume, random_seed;
|
||||
|
||||
private int log_2_sampling_rate;
|
||||
private static final int LOG_2_29024 = LogTable.log_2( 29024 );
|
||||
private static final int LOG_2_8287 = LogTable.log_2( 8287 );
|
||||
private static final int LOG_2_8363 = LogTable.log_2( 8363 );
|
||||
private static final int LOG_2_1712 = LogTable.log_2( 1712 );
|
||||
|
||||
private static final int[] sine_table = new int[] {
|
||||
0, 24 , 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253,
|
||||
255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24
|
||||
};
|
||||
|
||||
public Channel( Module mod, int sampling_rate, int[] global_vol ) {
|
||||
module = mod;
|
||||
global_volume = global_vol;
|
||||
linear_periods = module.linear_periods;
|
||||
fast_volume_slides = module.fast_volume_slides;
|
||||
current_note = new int[ 5 ];
|
||||
log_2_sampling_rate = LogTable.log_2( sampling_rate );
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
tremolo_speed = 0;
|
||||
tremolo_depth = 0;
|
||||
tremolo_wave = 0;
|
||||
vibrato_speed = 0;
|
||||
vibrato_depth = 0;
|
||||
vibrato_wave = 0;
|
||||
volume_slide_param = 0;
|
||||
portamento_param = 0;
|
||||
retrig_param = 0;
|
||||
random_seed = 0xABC123;
|
||||
instrument = module.get_instrument( 0 );
|
||||
row( 48, 256, 0, 0, 0 );
|
||||
}
|
||||
|
||||
public void resample( int[] mixing_buffer, int frame_offset, int frames, int quality ) {
|
||||
if( !silent ) {
|
||||
switch( quality ) {
|
||||
default:
|
||||
sample.resample_nearest( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
|
||||
break;
|
||||
case 1:
|
||||
sample.resample_linear( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
|
||||
break;
|
||||
case 2:
|
||||
sample.resample_sinc( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void update_sample_idx( int samples ) {
|
||||
sample_frac += step * samples;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
|
||||
public void set_volume( int vol ) {
|
||||
if( vol < 0 ) {
|
||||
vol = 0;
|
||||
}
|
||||
if( vol > 64 ) {
|
||||
vol = 64;
|
||||
}
|
||||
volume = vol;
|
||||
}
|
||||
|
||||
public void set_panning( int pan ) {
|
||||
if( pan < 0 ) {
|
||||
pan = 0;
|
||||
}
|
||||
if( pan > 255 ) {
|
||||
pan = 255;
|
||||
}
|
||||
panning = pan;
|
||||
}
|
||||
|
||||
public void row( int key, int inst_idx, int volume_column, int effect, int effect_param ) {
|
||||
effect = effect & 0xFF;
|
||||
if( effect >= 0x30 ) {
|
||||
/* Effects above 0x30 are internal.*/
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x00 && effect_param != 0 ) {
|
||||
/* Arpeggio.*/
|
||||
effect = 0x40;
|
||||
}
|
||||
if( effect == 0x0E ) {
|
||||
/* Renumber 0x0Ex effect command.*/
|
||||
effect = 0x30 + ( ( effect_param & 0xF0 ) >> 4 );
|
||||
effect_param = effect_param & 0x0F;
|
||||
}
|
||||
if( effect == 0x21 ) {
|
||||
/* Renumber 0x21x effect command.*/
|
||||
effect = 0x40 + ( ( effect_param & 0xF0 ) >> 4 );
|
||||
effect_param = effect_param & 0x0F;
|
||||
}
|
||||
current_note[ 0 ] = key;
|
||||
current_note[ 1 ] = inst_idx;
|
||||
current_note[ 2 ] = volume_column;
|
||||
current_note[ 3 ] = effect;
|
||||
current_note[ 4 ] = effect_param;
|
||||
effect_tick = 0;
|
||||
trigger_tick += 1;
|
||||
update_envelopes();
|
||||
key_add = 0;
|
||||
vibrato_add = 0;
|
||||
tremolo_add = 0;
|
||||
if( ! ( effect == 0x3D && effect_param > 0 ) ) {
|
||||
/* Not note delay.*/
|
||||
trigger( key, inst_idx, volume_column, effect );
|
||||
/* Handle volume column.*/
|
||||
switch( volume_column & 0xF0 ) {
|
||||
case 0x00:
|
||||
/* Do nothing.*/
|
||||
break;
|
||||
case 0x60:
|
||||
/* Volume slide down.*/
|
||||
break;
|
||||
case 0x70:
|
||||
/* Volume slide up.*/
|
||||
break;
|
||||
case 0x80:
|
||||
/* Fine volume slide down.*/
|
||||
set_volume( volume - ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0x90:
|
||||
/* Fine volume slide up.*/
|
||||
set_volume( volume + ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xA0:
|
||||
/* Set vibrato speed.*/
|
||||
set_vibrato_speed( volume_column & 0x0F );
|
||||
break;
|
||||
case 0xB0:
|
||||
/* Vibrato.*/
|
||||
set_vibrato_depth( volume_column & 0x0F );
|
||||
vibrato();
|
||||
break;
|
||||
case 0xC0:
|
||||
/* Set panning.*/
|
||||
set_panning( ( volume_column & 0x0F ) << 4 );
|
||||
break;
|
||||
case 0xD0:
|
||||
/* Panning slide left.*/
|
||||
break;
|
||||
case 0xE0:
|
||||
/* Panning slide right.*/
|
||||
break;
|
||||
case 0xF0:
|
||||
/* Tone portamento.*/
|
||||
set_portamento_param( volume_column & 0x0F );
|
||||
break;
|
||||
default:
|
||||
/* Set volume.*/
|
||||
set_volume( volume_column - 0x10 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( instrument.vibrato_depth > 0 ) {
|
||||
auto_vibrato();
|
||||
}
|
||||
switch( effect ) {
|
||||
case 0x01:
|
||||
/* Portmento Up.*/
|
||||
set_portamento_param( effect_param );
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x02:
|
||||
/* Portamento Down.*/
|
||||
set_portamento_param( effect_param );
|
||||
portamento_down();
|
||||
break;
|
||||
case 0x03:
|
||||
/* Tone Portamento.*/
|
||||
set_portamento_param( effect_param );
|
||||
break;
|
||||
case 0x04:
|
||||
/* Vibrato.*/
|
||||
set_vibrato_speed( ( effect_param & 0xF0 ) >> 4 );
|
||||
set_vibrato_depth( effect_param & 0x0F );
|
||||
vibrato();
|
||||
break;
|
||||
case 0x05:
|
||||
/* Tone Portamento + Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x06:
|
||||
/* Vibrato + Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
vibrato();
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x07:
|
||||
/* Tremolo.*/
|
||||
set_tremolo_speed( ( effect_param & 0xF0 ) >> 4 );
|
||||
set_tremolo_depth( effect_param & 0x0F );
|
||||
tremolo();
|
||||
break;
|
||||
case 0x08:
|
||||
/* Set Panning.*/
|
||||
set_panning( effect_param );
|
||||
break;
|
||||
case 0x09:
|
||||
/* Set Sample Index.*/
|
||||
set_sample_index( effect_param << 8 );
|
||||
break;
|
||||
case 0x0A:
|
||||
/* Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x0B:
|
||||
/* Pattern Jump.*/
|
||||
break;
|
||||
case 0x0C:
|
||||
/* Set volume.*/
|
||||
set_volume( effect_param );
|
||||
break;
|
||||
case 0x0D:
|
||||
/* Pattern Break.*/
|
||||
break;
|
||||
case 0x0E:
|
||||
/* Extended Commands (See 0x30-0x3F).*/
|
||||
break;
|
||||
case 0x0F:
|
||||
/* Set Speed/Tempo.*/
|
||||
break;
|
||||
case 0x10:
|
||||
/* Set Global Volume.*/
|
||||
set_global_volume( effect_param );
|
||||
break;
|
||||
case 0x11:
|
||||
/* global Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
break;
|
||||
case 0x14:
|
||||
/* Key Off*/
|
||||
if( effect_param == 0 ) {
|
||||
key_on = false;
|
||||
}
|
||||
break;
|
||||
case 0x15:
|
||||
/* Set Envelope Tick.*/
|
||||
set_envelope_tick( effect_param );
|
||||
break;
|
||||
case 0x19:
|
||||
/* Panning Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
break;
|
||||
case 0x1B:
|
||||
/* Retrig + Volume Slide.*/
|
||||
set_retrig_param( effect_param );
|
||||
retrig_volume_slide();
|
||||
break;
|
||||
case 0x1D:
|
||||
/* Tremor.*/
|
||||
set_retrig_param( effect_param );
|
||||
tremor();
|
||||
break;
|
||||
case 0x24:
|
||||
/* S3M Fine Vibrato.*/
|
||||
set_vibrato_speed( ( effect_param & 0xF0 ) >> 4 );
|
||||
set_vibrato_depth( effect_param & 0x0F );
|
||||
fine_vibrato();
|
||||
break;
|
||||
case 0x25:
|
||||
/* S3M Set Speed.*/
|
||||
break;
|
||||
case 0x30:
|
||||
/* Amiga Set Filter.*/
|
||||
break;
|
||||
case 0x31:
|
||||
/* Fine Portamento Up.*/
|
||||
set_portamento_param( 0xF0 | effect_param );
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x32:
|
||||
/* Fine Portamento Down.*/
|
||||
set_portamento_param( 0xF0 | effect_param );
|
||||
portamento_down();
|
||||
break;
|
||||
case 0x33:
|
||||
/* Set Glissando Mode.*/
|
||||
break;
|
||||
case 0x34:
|
||||
/* Set Vibrato Waveform.*/
|
||||
set_vibrato_wave( effect_param );
|
||||
break;
|
||||
case 0x35:
|
||||
/* Set Fine Tune.*/
|
||||
break;
|
||||
case 0x36:
|
||||
/* Pattern Loop.*/
|
||||
break;
|
||||
case 0x37:
|
||||
/* Set Tremolo Waveform.*/
|
||||
set_tremolo_wave( effect_param );
|
||||
break;
|
||||
case 0x38:
|
||||
/* Set Panning(Obsolete).*/
|
||||
break;
|
||||
case 0x39:
|
||||
/* Retrig.*/
|
||||
set_retrig_param( effect_param );
|
||||
break;
|
||||
case 0x3A:
|
||||
/* Fine Volume Slide Up.*/
|
||||
set_volume_slide_param( ( effect_param << 4 ) | 0x0F );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x3B:
|
||||
/* Fine Volume Slide Down.*/
|
||||
set_volume_slide_param( 0xF0 | effect_param );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x3C:
|
||||
/* Note Cut.*/
|
||||
if( effect_param == 0 ) {
|
||||
set_volume( 0 );
|
||||
}
|
||||
break;
|
||||
case 0x3D:
|
||||
/* Note Delay.*/
|
||||
break;
|
||||
case 0x3E:
|
||||
/* Pattern Delay.*/
|
||||
break;
|
||||
case 0x3F:
|
||||
/* Invert Loop.*/
|
||||
break;
|
||||
case 0x40:
|
||||
/* Arpeggio.*/
|
||||
break;
|
||||
case 0x41:
|
||||
/* Extra Fine Porta Up.*/
|
||||
set_portamento_param( 0xE0 | effect_param );
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x42:
|
||||
/* Extra Fine Porta Down.*/
|
||||
set_portamento_param( 0xE0 | effect_param );
|
||||
portamento_down();
|
||||
break;
|
||||
}
|
||||
calculate_amplitude();
|
||||
calculate_frequency();
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
int volume_column, effect, effect_param;
|
||||
volume_column = current_note[ 2 ];
|
||||
effect = current_note[ 3 ];
|
||||
effect_param = current_note[ 4 ];
|
||||
effect_tick += 1;
|
||||
if( effect == 0x3D && effect_param == effect_tick ) {
|
||||
/* Note delay.*/
|
||||
row( current_note[ 0 ], current_note[ 1 ], volume_column, 0, 0 );
|
||||
} else {
|
||||
trigger_tick += 1;
|
||||
vibrato_tick += 1;
|
||||
tremolo_tick += 1;
|
||||
update_envelopes();
|
||||
key_add = 0;
|
||||
vibrato_add = 0;
|
||||
tremolo_add = 0;
|
||||
if( instrument.vibrato_depth > 0 ) {
|
||||
auto_vibrato();
|
||||
}
|
||||
switch( volume_column & 0xF0 ) {
|
||||
case 0x60:
|
||||
/* Volume Slide Down.*/
|
||||
set_volume( volume - ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0x70:
|
||||
/* Volume Slide Up.*/
|
||||
set_volume( volume + ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xB0:
|
||||
/* Vibrato.*/
|
||||
vibrato();
|
||||
break;
|
||||
case 0xD0:
|
||||
/* Panning Slide Left.*/
|
||||
set_panning( panning - ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xE0:
|
||||
/* Panning Slide Right.*/
|
||||
set_panning( panning + ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xF0:
|
||||
/* Tone Portamento.*/
|
||||
tone_portamento();
|
||||
break;
|
||||
}
|
||||
switch( effect ) {
|
||||
case 0x01:
|
||||
/* Portamento Up.*/
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x02:
|
||||
/* Portamento Down.*/
|
||||
portamento_down();
|
||||
break;
|
||||
case 0x03:
|
||||
/* Tone Portamento.*/
|
||||
tone_portamento();
|
||||
break;
|
||||
case 0x04:
|
||||
/* Vibrato.*/
|
||||
vibrato();
|
||||
break;
|
||||
case 0x05:
|
||||
/* Tone Portamento + Volume Slide.*/
|
||||
tone_portamento();
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x06:
|
||||
/* Vibrato + Volume Slide */
|
||||
vibrato();
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x07:
|
||||
/* Tremolo.*/
|
||||
tremolo();
|
||||
break;
|
||||
case 0x0A:
|
||||
/* Volume Slide.*/
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x11:
|
||||
/* Global Volume Slide.*/
|
||||
global_volume_slide();
|
||||
break;
|
||||
case 0x14:
|
||||
/* Key off.*/
|
||||
if( effect_tick == effect_param ) {
|
||||
key_on = false;
|
||||
}
|
||||
break;
|
||||
case 0x19:
|
||||
/* Panning Slide.*/
|
||||
panning_slide();
|
||||
break;
|
||||
case 0x1B:
|
||||
/* Retrig + Volume Slide.*/
|
||||
retrig_volume_slide();
|
||||
break;
|
||||
case 0x1D:
|
||||
/* Tremor.*/
|
||||
tremor();
|
||||
break;
|
||||
case 0x24:
|
||||
/* S3M Fine Vibrato.*/
|
||||
fine_vibrato();
|
||||
break;
|
||||
case 0x39:
|
||||
/* Retrig.*/
|
||||
retrig_volume_slide();
|
||||
break;
|
||||
case 0x3C:
|
||||
/* Note Cut.*/
|
||||
if( effect_tick == effect_param ) {
|
||||
set_volume( 0 );
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
/* Arpeggio.*/
|
||||
switch( effect_tick % 3 ) {
|
||||
case 1:
|
||||
key_add = ( effect_param & 0xF0 ) >> 4;
|
||||
break;
|
||||
case 2:
|
||||
key_add = effect_param & 0x0F;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
calculate_amplitude();
|
||||
calculate_frequency();
|
||||
}
|
||||
|
||||
private void set_vibrato_speed( int speed ) {
|
||||
if( speed > 0 ) {
|
||||
vibrato_speed = speed;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_vibrato_depth( int depth ) {
|
||||
if( depth > 0 ) {
|
||||
vibrato_depth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_vibrato_wave( int wave ) {
|
||||
if( wave < 0 || wave > 7 ) {
|
||||
wave = 0;
|
||||
}
|
||||
vibrato_wave = wave;
|
||||
}
|
||||
|
||||
private void set_tremolo_speed( int speed ) {
|
||||
if( speed > 0 ) {
|
||||
tremolo_speed = speed;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_tremolo_depth( int depth ) {
|
||||
if( depth > 0 ) {
|
||||
tremolo_depth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_tremolo_wave( int wave ) {
|
||||
if( wave < 0 || wave > 7 ) {
|
||||
wave = 0;
|
||||
}
|
||||
tremolo_wave = wave;
|
||||
}
|
||||
|
||||
private void vibrato() {
|
||||
int vibrato_phase;
|
||||
vibrato_phase = vibrato_tick * vibrato_speed;
|
||||
vibrato_add += waveform( vibrato_phase, vibrato_wave ) * vibrato_depth >> 5;
|
||||
}
|
||||
|
||||
private void fine_vibrato() {
|
||||
int vibrato_phase;
|
||||
vibrato_phase = vibrato_tick * vibrato_speed;
|
||||
vibrato_add += waveform( vibrato_phase, vibrato_wave ) * vibrato_depth >> 7;
|
||||
}
|
||||
|
||||
private void tremolo() {
|
||||
int tremolo_phase;
|
||||
tremolo_phase = tremolo_tick * tremolo_speed;
|
||||
tremolo_add += waveform( tremolo_phase, tremolo_wave ) * tremolo_depth >> 6;
|
||||
}
|
||||
|
||||
private void set_portamento_param( int param ) {
|
||||
if( param != 0 ) {
|
||||
portamento_param = param;
|
||||
}
|
||||
}
|
||||
|
||||
private void tone_portamento() {
|
||||
int new_period;
|
||||
if( porta_period < period ) {
|
||||
new_period = period - ( portamento_param << 2 );
|
||||
if( new_period < porta_period ) {
|
||||
new_period = porta_period;
|
||||
}
|
||||
set_period( new_period );
|
||||
}
|
||||
if( porta_period > period ) {
|
||||
new_period = period + ( portamento_param << 2 );
|
||||
if( new_period > porta_period ) {
|
||||
new_period = porta_period;
|
||||
}
|
||||
set_period( new_period );
|
||||
}
|
||||
}
|
||||
|
||||
private void portamento_up() {
|
||||
if( ( portamento_param & 0xF0 ) == 0xE0 ) {
|
||||
/* Extra-fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period - ( portamento_param & 0x0F ) );
|
||||
}
|
||||
} else if( ( portamento_param & 0xF0 ) == 0xF0 ) {
|
||||
/* Fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period - ( ( portamento_param & 0x0F ) << 2 ) );
|
||||
}
|
||||
} else {
|
||||
/* Normal porta.*/
|
||||
if( effect_tick > 0 ) {
|
||||
set_period( period - ( portamento_param << 2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void portamento_down() {
|
||||
if( ( portamento_param & 0xF0 ) == 0xE0 ) {
|
||||
/* Extra-fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period + ( portamento_param & 0x0F ) );
|
||||
}
|
||||
} else if( ( portamento_param & 0xF0 ) == 0xF0 ) {
|
||||
/* Fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period + ( ( portamento_param & 0x0F ) << 2 ) );
|
||||
}
|
||||
} else {
|
||||
/* Normal porta.*/
|
||||
if( effect_tick > 0 ) {
|
||||
set_period( period + ( portamento_param << 2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void set_period( int p ) {
|
||||
if( p < 32 ) {
|
||||
p = 32;
|
||||
}
|
||||
if( p > 32768 ) {
|
||||
p = 32768;
|
||||
}
|
||||
period = p;
|
||||
}
|
||||
|
||||
private void set_global_volume( int vol ) {
|
||||
if( vol < 0 ) {
|
||||
vol = 0;
|
||||
}
|
||||
if( vol > 64 ) {
|
||||
vol = 64;
|
||||
}
|
||||
global_volume[ 0 ] = vol;
|
||||
}
|
||||
|
||||
private void set_volume_slide_param( int param ) {
|
||||
if( param != 0 ) {
|
||||
volume_slide_param = param;
|
||||
}
|
||||
}
|
||||
|
||||
private void global_volume_slide() {
|
||||
int up, down;
|
||||
up = ( volume_slide_param & 0xF0 ) >> 4;
|
||||
down = volume_slide_param & 0x0F;
|
||||
set_global_volume( global_volume[ 0 ] + up - down );
|
||||
}
|
||||
|
||||
private void volume_slide() {
|
||||
int up, down;
|
||||
up = ( volume_slide_param & 0xF0 ) >> 4;
|
||||
down = volume_slide_param & 0x0F;
|
||||
if( down == 0x0F && up > 0 ) {
|
||||
/* Fine slide up.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_volume( volume + up );
|
||||
}
|
||||
} else if( up == 0x0F && down > 0 ) {
|
||||
/* Fine slide down.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_volume( volume - down );
|
||||
}
|
||||
} else {
|
||||
/* Normal slide.*/
|
||||
if( effect_tick > 0 || fast_volume_slides ) {
|
||||
set_volume( volume + up - down );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void panning_slide() {
|
||||
int left, right;
|
||||
left = ( volume_slide_param & 0xF0 ) >> 4;
|
||||
right = volume_slide_param & 0x0F;
|
||||
set_panning( panning - left + right );
|
||||
}
|
||||
|
||||
private void set_retrig_param( int param ) {
|
||||
if( param != 0 ) {
|
||||
retrig_param = param;
|
||||
}
|
||||
}
|
||||
|
||||
private void tremor() {
|
||||
int on_ticks, cycle_length, cycle_index;
|
||||
on_ticks = ( ( retrig_param & 0xF0 ) >> 4 ) + 1;
|
||||
cycle_length = on_ticks + ( retrig_param & 0x0F ) + 1;
|
||||
cycle_index = trigger_tick % cycle_length;
|
||||
if( cycle_index >= on_ticks ) {
|
||||
tremolo_add = -64;
|
||||
}
|
||||
}
|
||||
|
||||
private void retrig_volume_slide() {
|
||||
int retrig_volume, retrig_tick;
|
||||
retrig_volume = ( retrig_param & 0xF0 ) >> 4;
|
||||
retrig_tick = retrig_param & 0x0F;
|
||||
if( retrig_tick > 0 && ( trigger_tick % retrig_tick ) == 0 ) {
|
||||
set_sample_index( 0 );
|
||||
switch( retrig_volume ) {
|
||||
case 0x01:
|
||||
set_volume( volume - 1 );
|
||||
break;
|
||||
case 0x02:
|
||||
set_volume( volume - 2 );
|
||||
break;
|
||||
case 0x03:
|
||||
set_volume( volume - 4 );
|
||||
break;
|
||||
case 0x04:
|
||||
set_volume( volume - 8 );
|
||||
break;
|
||||
case 0x05:
|
||||
set_volume( volume - 16 );
|
||||
break;
|
||||
case 0x06:
|
||||
set_volume( volume - volume / 3 );
|
||||
break;
|
||||
case 0x07:
|
||||
set_volume( volume / 2 );
|
||||
break;
|
||||
case 0x09:
|
||||
set_volume( volume + 1 );
|
||||
break;
|
||||
case 0x0A:
|
||||
set_volume( volume + 2 );
|
||||
break;
|
||||
case 0x0B:
|
||||
set_volume( volume + 4 );
|
||||
break;
|
||||
case 0x0C:
|
||||
set_volume( volume + 8 );
|
||||
break;
|
||||
case 0x0D:
|
||||
set_volume( volume + 16 );
|
||||
break;
|
||||
case 0x0E:
|
||||
set_volume( volume + volume / 2 );
|
||||
break;
|
||||
case 0x0F:
|
||||
set_volume( volume * 2 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void set_sample_index( int index ) {
|
||||
if( index < 0 ) {
|
||||
index = 0;
|
||||
}
|
||||
sample_idx = index;
|
||||
sample_frac = 0;
|
||||
}
|
||||
|
||||
private void set_envelope_tick( int tick ) {
|
||||
volume_envelope_tick = tick;
|
||||
panning_envelope_tick = tick;
|
||||
}
|
||||
|
||||
private void trigger( int key, int instrument_idx, int volume_column, int effect ) {
|
||||
if( instrument_idx > 0 ) {
|
||||
instrument = module.get_instrument( instrument_idx );
|
||||
sample = instrument.get_sample_from_key( key );
|
||||
set_volume( sample.volume );
|
||||
if( sample.set_panning ) {
|
||||
set_panning( sample.panning );
|
||||
}
|
||||
set_envelope_tick( 0 );
|
||||
fade_out_volume = 32768;
|
||||
key_on = true;
|
||||
}
|
||||
if( key > 0 ) {
|
||||
if( key < 97 ) {
|
||||
porta_period = key_to_period( key );
|
||||
if( effect != 0x03 && effect != 0x05 ) {
|
||||
if( ( volume_column & 0xF0 ) != 0xF0 ) {
|
||||
/* Not portamento.*/
|
||||
trigger_tick = 0;
|
||||
if( vibrato_wave < 4 ) {
|
||||
vibrato_tick = 0;
|
||||
}
|
||||
if( tremolo_wave < 4 ) {
|
||||
tremolo_tick = 0;
|
||||
}
|
||||
set_period( porta_period );
|
||||
set_sample_index( 0 );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Key off.*/
|
||||
key_on = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void update_envelopes() {
|
||||
Envelope envelope;
|
||||
if( instrument.volume_envelope_active ) {
|
||||
if( !key_on ) {
|
||||
fade_out_volume -= instrument.volume_fade_out & 0xFFFF;
|
||||
if( fade_out_volume < 0 ) {
|
||||
fade_out_volume = 0;
|
||||
}
|
||||
}
|
||||
envelope = instrument.get_volume_envelope();
|
||||
volume_envelope_tick = envelope.next_tick( volume_envelope_tick, key_on );
|
||||
}
|
||||
if( instrument.panning_envelope_active ) {
|
||||
envelope = instrument.get_panning_envelope();
|
||||
panning_envelope_tick = envelope.next_tick( panning_envelope_tick, key_on );
|
||||
}
|
||||
}
|
||||
|
||||
private void auto_vibrato() {
|
||||
int sweep, depth, rate;
|
||||
sweep = instrument.vibrato_sweep & 0xFF;
|
||||
depth = instrument.vibrato_depth & 0x0F;
|
||||
rate = instrument.vibrato_rate & 0x3F;
|
||||
if( trigger_tick < sweep ) {
|
||||
depth = depth * trigger_tick / sweep;
|
||||
}
|
||||
vibrato_add += waveform( trigger_tick * rate, 0 ) * depth >> 9;
|
||||
}
|
||||
|
||||
private int waveform( int phase, int wform ) {
|
||||
int amplitude;
|
||||
amplitude = 0;
|
||||
switch( wform & 0x3 ) {
|
||||
case 0:
|
||||
/* Sine. */
|
||||
if( ( phase & 0x20 ) == 0 ) {
|
||||
amplitude = sine_table[ phase & 0x1F ];
|
||||
} else {
|
||||
amplitude = -sine_table[ phase & 0x1F ];
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* Saw. */
|
||||
if( ( phase & 0x20 ) == 0 ) {
|
||||
amplitude = ( phase & 0x1F ) << 3;
|
||||
} else {
|
||||
amplitude = ( ( phase & 0x1F ) << 3 ) - 255;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* Square. */
|
||||
if( ( phase & 0x20 ) == 0 ) {
|
||||
amplitude = 255;
|
||||
} else {
|
||||
amplitude = -255;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* Random. */
|
||||
amplitude = ( random_seed >> 15 ) - 255;
|
||||
random_seed = ( random_seed * 65 + 17 ) & 0xFFFFFF;
|
||||
break;
|
||||
}
|
||||
return amplitude;
|
||||
}
|
||||
|
||||
private int key_to_period( int key ) {
|
||||
int octave, log_2_period, period_out;
|
||||
octave = ( key << IBXM.FP_SHIFT ) / 12 + sample.transpose;
|
||||
if( linear_periods ) {
|
||||
period_out = 7744 - ( octave * 768 >> IBXM.FP_SHIFT );
|
||||
} else {
|
||||
log_2_period = LOG_2_29024 - octave;
|
||||
period_out = LogTable.raise_2( log_2_period );
|
||||
period_out = period_out >> ( IBXM.FP_SHIFT - 1 );
|
||||
period_out = ( period_out >> 1 ) + ( period_out & 1 );
|
||||
}
|
||||
return period_out;
|
||||
}
|
||||
|
||||
private void calculate_amplitude() {
|
||||
int envelope_volume, tremolo_volume, amplitude;
|
||||
int envelope_panning, mixer_panning, panning_range;
|
||||
Envelope envelope;
|
||||
envelope_volume = 0;
|
||||
if( instrument.volume_envelope_active ) {
|
||||
envelope = instrument.get_volume_envelope();
|
||||
envelope_volume = envelope.calculate_ampl( volume_envelope_tick );
|
||||
} else {
|
||||
if( key_on ) {
|
||||
envelope_volume = 64;
|
||||
}
|
||||
}
|
||||
tremolo_volume = volume + tremolo_add;
|
||||
if( tremolo_volume < 0 ) {
|
||||
tremolo_volume = 0;
|
||||
}
|
||||
if( tremolo_volume > 64 ) {
|
||||
tremolo_volume = 64;
|
||||
}
|
||||
amplitude = tremolo_volume << IBXM.FP_SHIFT - 6;
|
||||
amplitude = amplitude * envelope_volume >> 6;
|
||||
amplitude = amplitude * fade_out_volume >> 15;
|
||||
amplitude = amplitude * global_volume[ 0 ] >> 6;
|
||||
amplitude = amplitude * module.channel_gain >> IBXM.FP_SHIFT;
|
||||
silent = sample.has_finished( sample_idx );
|
||||
if( amplitude <= 0 ) {
|
||||
silent = true;
|
||||
} else {
|
||||
envelope_panning = 32;
|
||||
if( instrument.panning_envelope_active ) {
|
||||
envelope = instrument.get_panning_envelope();
|
||||
envelope_panning = envelope.calculate_ampl( panning_envelope_tick );
|
||||
}
|
||||
mixer_panning = ( panning & 0xFF ) << IBXM.FP_SHIFT - 8;
|
||||
panning_range = IBXM.FP_ONE - mixer_panning;
|
||||
if( panning_range > mixer_panning ) {
|
||||
panning_range = mixer_panning;
|
||||
}
|
||||
mixer_panning = mixer_panning + ( panning_range * ( envelope_panning - 32 ) >> 5 );
|
||||
left_gain = amplitude * ( IBXM.FP_ONE - mixer_panning ) >> IBXM.FP_SHIFT;
|
||||
right_gain = amplitude * mixer_panning >> IBXM.FP_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
private void calculate_frequency() {
|
||||
int vibrato_period, log_2_freq;
|
||||
vibrato_period = period + vibrato_add;
|
||||
if( vibrato_period < 32 ) {
|
||||
vibrato_period = 32;
|
||||
}
|
||||
if( vibrato_period > 32768 ) {
|
||||
vibrato_period = 32768;
|
||||
}
|
||||
if( linear_periods ) {
|
||||
log_2_freq = LOG_2_8363 + ( 4608 - vibrato_period << IBXM.FP_SHIFT ) / 768;
|
||||
} else {
|
||||
log_2_freq = module.pal ? LOG_2_8287 : LOG_2_8363;
|
||||
log_2_freq = log_2_freq + LOG_2_1712 - LogTable.log_2( vibrato_period );
|
||||
}
|
||||
log_2_freq += ( key_add << IBXM.FP_SHIFT ) / 12;
|
||||
step = LogTable.raise_2( log_2_freq - log_2_sampling_rate );
|
||||
}
|
||||
}
|
||||
|
112
src/com/ibxm/Envelope.java
Normal file
112
src/com/ibxm/Envelope.java
Normal file
@ -0,0 +1,112 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
public class Envelope {
|
||||
public boolean sustain, looped;
|
||||
private int sustain_tick, loop_start_tick, loop_end_tick;
|
||||
private int[] ticks, ampls;
|
||||
|
||||
public Envelope() {
|
||||
set_num_points( 1 );
|
||||
}
|
||||
|
||||
public void set_num_points( int num_points ) {
|
||||
int point;
|
||||
if( num_points <= 0 ) {
|
||||
num_points = 1;
|
||||
}
|
||||
ticks = new int[ num_points ];
|
||||
ampls = new int[ num_points ];
|
||||
set_point( 0, 0, 0, false );
|
||||
}
|
||||
|
||||
/* When you set a point, all subsequent points are reset. */
|
||||
public void set_point( int point, int tick, int ampl, boolean delta ) {
|
||||
if( point >= 0 && point < ticks.length ) {
|
||||
if( point == 0 ) {
|
||||
tick = 0;
|
||||
}
|
||||
if( point > 0 ) {
|
||||
if( delta ) tick += ticks[ point - 1 ];
|
||||
if( tick <= ticks[ point - 1 ] ) {
|
||||
System.out.println( "Envelope: Point not valid (" + tick + " <= " + ticks[ point - 1 ] + ")");
|
||||
tick = ticks[ point - 1 ] + 1;
|
||||
}
|
||||
}
|
||||
ticks[ point ] = tick;
|
||||
ampls[ point ] = ampl;
|
||||
point += 1;
|
||||
while( point < ticks.length ) {
|
||||
ticks[ point ] = ticks[ point - 1 ] + 1;
|
||||
ampls[ point ] = 0;
|
||||
point += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set_sustain_point( int point ) {
|
||||
if( point < 0 ) {
|
||||
point = 0;
|
||||
}
|
||||
if( point >= ticks.length ) {
|
||||
point = ticks.length - 1;
|
||||
}
|
||||
sustain_tick = ticks[ point ];
|
||||
}
|
||||
|
||||
public void set_loop_points( int start, int end ) {
|
||||
if( start < 0 ) {
|
||||
start = 0;
|
||||
}
|
||||
if( start >= ticks.length ) {
|
||||
start = ticks.length - 1;
|
||||
}
|
||||
if( end < start || end >= ticks.length ) {
|
||||
end = start;
|
||||
}
|
||||
loop_start_tick = ticks[ start ];
|
||||
loop_end_tick = ticks[ end ];
|
||||
}
|
||||
|
||||
public int next_tick( int tick, boolean key_on ) {
|
||||
tick = tick + 1;
|
||||
if( looped && tick >= loop_end_tick ) {
|
||||
tick = loop_start_tick;
|
||||
}
|
||||
if( sustain && key_on && tick >= sustain_tick ) {
|
||||
tick = sustain_tick;
|
||||
}
|
||||
return tick;
|
||||
}
|
||||
|
||||
public int calculate_ampl( int tick ) {
|
||||
int idx, point, delta_t, delta_a, ampl;
|
||||
ampl = ampls[ ticks.length - 1 ];
|
||||
if( tick < ticks[ ticks.length - 1 ] ) {
|
||||
point = 0;
|
||||
for( idx = 1; idx < ticks.length; idx++ ) {
|
||||
if( ticks[ idx ] <= tick ) {
|
||||
point = idx;
|
||||
}
|
||||
}
|
||||
delta_t = ticks[ point + 1 ] - ticks[ point ];
|
||||
delta_a = ampls[ point + 1 ] - ampls[ point ];
|
||||
ampl = ( delta_a << IBXM.FP_SHIFT ) / delta_t;
|
||||
ampl = ampl * ( tick - ticks[ point ] ) >> IBXM.FP_SHIFT;
|
||||
ampl = ampl + ampls[ point ];
|
||||
}
|
||||
return ampl;
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
int idx, tick;
|
||||
for( idx = 0; idx < ticks.length; idx++ ) {
|
||||
System.out.println( ticks[ idx ] + ", " + ampls[ idx ] );
|
||||
}
|
||||
for( tick = 0; tick < 222; tick++ ) {
|
||||
System.out.print( calculate_ampl( tick ) + ", " );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
295
src/com/ibxm/FastTracker2.java
Normal file
295
src/com/ibxm/FastTracker2.java
Normal file
@ -0,0 +1,295 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class FastTracker2
|
||||
{
|
||||
public static boolean is_xm(byte[] header_60_bytes)
|
||||
{
|
||||
String xm_identifier;
|
||||
xm_identifier = ascii_text(header_60_bytes, 0, 17);
|
||||
return xm_identifier.equals("Extended Module: ");
|
||||
}
|
||||
|
||||
public static Module load_xm(byte[] header_60_bytes, DataInput data_input) throws IOException
|
||||
{
|
||||
int xm_version, song_header_length, sequence_length;
|
||||
int num_channels, num_patterns, num_instruments, xm_flags, idx;
|
||||
byte[] structure_header, song_header;
|
||||
boolean delta_env;
|
||||
String tracker_name;
|
||||
Instrument instrument;
|
||||
Module module;
|
||||
if(!is_xm(header_60_bytes))
|
||||
{
|
||||
throw new IllegalArgumentException("Not an XM file!");
|
||||
}
|
||||
xm_version = unsigned_short_le(header_60_bytes, 58);
|
||||
if(xm_version != 0x0104)
|
||||
{
|
||||
throw new IllegalArgumentException("Sorry, XM version " + xm_version + " is not supported!");
|
||||
}
|
||||
module = new Module();
|
||||
module.song_title = ascii_text(header_60_bytes, 17, 20);
|
||||
tracker_name = ascii_text(header_60_bytes, 38, 20);
|
||||
delta_env = tracker_name.startsWith("DigiBooster Pro");
|
||||
structure_header = new byte[4];
|
||||
data_input.readFully(structure_header);
|
||||
song_header_length = int_le(structure_header, 0);
|
||||
song_header = new byte[song_header_length];
|
||||
data_input.readFully(song_header, 4, song_header_length - 4);
|
||||
sequence_length = unsigned_short_le(song_header, 4);
|
||||
module.restart_sequence_index = unsigned_short_le(song_header, 6);
|
||||
num_channels = unsigned_short_le(song_header, 8);
|
||||
num_patterns = unsigned_short_le(song_header, 10);
|
||||
num_instruments = unsigned_short_le(song_header, 12);
|
||||
xm_flags = unsigned_short_le(song_header, 14);
|
||||
module.linear_periods = (xm_flags & 0x1) == 0x1;
|
||||
module.global_volume = 64;
|
||||
module.channel_gain = IBXM.FP_ONE * 3 / 8;
|
||||
module.default_speed = unsigned_short_le(song_header, 16);
|
||||
module.default_tempo = unsigned_short_le(song_header, 18);
|
||||
module.set_num_channels(num_channels);
|
||||
for(idx = 0; idx < num_channels; idx++)
|
||||
{
|
||||
module.set_initial_panning(idx, 128);
|
||||
}
|
||||
module.set_sequence_length(sequence_length);
|
||||
for(idx = 0; idx < sequence_length; idx++)
|
||||
{
|
||||
module.set_sequence(idx, song_header[20 + idx] & 0xFF);
|
||||
}
|
||||
module.set_num_patterns(num_patterns);
|
||||
for(idx = 0; idx < num_patterns; idx++)
|
||||
{
|
||||
module.set_pattern(idx, read_xm_pattern(data_input, num_channels));
|
||||
}
|
||||
module.set_num_instruments(num_instruments);
|
||||
for(idx = 1; idx <= num_instruments; idx++)
|
||||
{
|
||||
try
|
||||
{
|
||||
instrument = read_xm_instrument(data_input, delta_env);
|
||||
module.set_instrument(idx, instrument);
|
||||
}
|
||||
catch(EOFException e)
|
||||
{
|
||||
System.out.println("Instrument " + idx + " is missing!");
|
||||
}
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static Pattern read_xm_pattern(DataInput data_input, int num_channels) throws IOException
|
||||
{
|
||||
int pattern_header_length, packing_type, num_rows, pattern_data_length;
|
||||
byte[] structure_header, pattern_header, pattern_data;
|
||||
Pattern pattern;
|
||||
structure_header = new byte[4];
|
||||
data_input.readFully(structure_header);
|
||||
pattern_header_length = int_le(structure_header, 0);
|
||||
pattern_header = new byte[pattern_header_length];
|
||||
data_input.readFully(pattern_header, 4, pattern_header_length - 4);
|
||||
packing_type = pattern_header[4];
|
||||
if(packing_type != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Pattern packing type " + packing_type + " is not supported!");
|
||||
}
|
||||
pattern = new Pattern();
|
||||
pattern.num_rows = unsigned_short_le(pattern_header, 5);
|
||||
pattern_data_length = unsigned_short_le(pattern_header, 7);
|
||||
pattern_data = new byte[pattern_data_length];
|
||||
data_input.readFully(pattern_data);
|
||||
pattern.set_pattern_data(pattern_data);
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static Instrument read_xm_instrument(DataInput data_input, boolean delta_env) throws IOException
|
||||
{
|
||||
int instrument_header_length, num_samples, idx;
|
||||
int env_tick, env_ampl, env_num_points, flags;
|
||||
byte[] structure_header, instrument_header, sample_headers;
|
||||
Instrument instrument;
|
||||
Envelope envelope;
|
||||
structure_header = new byte[4];
|
||||
data_input.readFully(structure_header);
|
||||
instrument_header_length = int_le(structure_header, 0);
|
||||
instrument_header = new byte[instrument_header_length];
|
||||
data_input.readFully(instrument_header, 4, instrument_header_length - 4);
|
||||
instrument = new Instrument();
|
||||
instrument.name = ascii_text(instrument_header, 4, 22);
|
||||
num_samples = unsigned_short_le(instrument_header, 27);
|
||||
if(num_samples > 0)
|
||||
{
|
||||
instrument.set_num_samples(num_samples);
|
||||
for(idx = 0; idx < 96; idx++)
|
||||
{
|
||||
instrument.set_key_to_sample(idx + 1, instrument_header[33 + idx] & 0xFF);
|
||||
}
|
||||
envelope = new Envelope();
|
||||
env_num_points = instrument_header[225] & 0xFF;
|
||||
envelope.set_num_points(env_num_points);
|
||||
for(idx = 0; idx < env_num_points; idx++)
|
||||
{
|
||||
env_tick = unsigned_short_le(instrument_header, 129 + idx * 4);
|
||||
env_ampl = unsigned_short_le(instrument_header, 131 + idx * 4);
|
||||
envelope.set_point(idx, env_tick, env_ampl, delta_env);
|
||||
}
|
||||
envelope.set_sustain_point(instrument_header[227] & 0xFF);
|
||||
envelope.set_loop_points(instrument_header[228] & 0xFF, instrument_header[229] & 0xFF);
|
||||
flags = instrument_header[233] & 0xFF;
|
||||
instrument.volume_envelope_active = (flags & 0x1) == 0x1;
|
||||
envelope.sustain = (flags & 0x2) == 0x2;
|
||||
envelope.looped = (flags & 0x4) == 0x4;
|
||||
instrument.set_volume_envelope(envelope);
|
||||
envelope = new Envelope();
|
||||
env_num_points = instrument_header[226] & 0xFF;
|
||||
envelope.set_num_points(env_num_points);
|
||||
for(idx = 0; idx < env_num_points; idx++)
|
||||
{
|
||||
env_tick = unsigned_short_le(instrument_header, 177 + idx * 4);
|
||||
env_ampl = unsigned_short_le(instrument_header, 179 + idx * 4);
|
||||
envelope.set_point(idx, env_tick, env_ampl, delta_env);
|
||||
}
|
||||
envelope.set_sustain_point(instrument_header[230] & 0xFF);
|
||||
envelope.set_loop_points(instrument_header[231] & 0xFF, instrument_header[232] & 0xFF);
|
||||
flags = instrument_header[234] & 0xFF;
|
||||
instrument.panning_envelope_active = (flags & 0x1) == 0x1;
|
||||
envelope.sustain = (flags & 0x2) == 0x2;
|
||||
envelope.looped = (flags & 0x4) == 0x4;
|
||||
instrument.set_panning_envelope(envelope);
|
||||
instrument.vibrato_type = instrument_header[235] & 0xFF;
|
||||
instrument.vibrato_sweep = instrument_header[236] & 0xFF;
|
||||
instrument.vibrato_depth = instrument_header[237] & 0xFF;
|
||||
instrument.vibrato_rate = instrument_header[238] & 0xFF;
|
||||
instrument.volume_fade_out = unsigned_short_le(instrument_header, 239);
|
||||
sample_headers = new byte[num_samples*40];
|
||||
data_input.readFully(sample_headers);
|
||||
for(idx = 0; idx < num_samples; idx++)
|
||||
{
|
||||
instrument.set_sample(idx, read_xm_sample(sample_headers, idx, data_input));
|
||||
}
|
||||
}
|
||||
return instrument;
|
||||
}
|
||||
|
||||
private static Sample read_xm_sample(byte[] sample_headers, int sample_idx, DataInput data_input) throws IOException
|
||||
{
|
||||
int header_offset, sample_length, loop_start, loop_length;
|
||||
int flags, in_idx, out_idx, sam, last_sam;
|
||||
int fine_tune, relative_note;
|
||||
boolean sixteen_bit, ping_pong;
|
||||
byte[] raw_sample_data;
|
||||
short[] decoded_sample_data;
|
||||
Sample sample;
|
||||
header_offset = sample_idx * 40;
|
||||
sample = new Sample();
|
||||
sample_length = int_le(sample_headers, header_offset);
|
||||
loop_start = int_le(sample_headers, header_offset + 4);
|
||||
loop_length = int_le(sample_headers, header_offset + 8);
|
||||
sample.volume = sample_headers[header_offset + 12] & 0xFF;
|
||||
fine_tune = sample_headers[header_offset + 13];
|
||||
fine_tune = (fine_tune << IBXM.FP_SHIFT) / 1536;
|
||||
sample.set_panning = true;
|
||||
flags = sample_headers[header_offset + 14] & 0xFF;
|
||||
if((flags & 0x03) == 0)
|
||||
{
|
||||
loop_length = 0;
|
||||
}
|
||||
ping_pong = (flags & 0x02) == 0x02;
|
||||
sixteen_bit = (flags & 0x10) == 0x10;
|
||||
sample.panning = sample_headers[header_offset + 15] & 0xFF;
|
||||
relative_note = sample_headers[header_offset + 16];
|
||||
relative_note = (relative_note << IBXM.FP_SHIFT) / 12;
|
||||
sample.transpose = relative_note + fine_tune;
|
||||
sample.name = ascii_text(sample_headers, header_offset + 18, 22);
|
||||
raw_sample_data = new byte[sample_length];
|
||||
try
|
||||
{
|
||||
data_input.readFully(raw_sample_data);
|
||||
}
|
||||
catch(EOFException e)
|
||||
{
|
||||
System.out.println("Sample has been truncated!");
|
||||
}
|
||||
in_idx = 0;
|
||||
out_idx = 0;
|
||||
sam = 0;
|
||||
last_sam = 0;
|
||||
if(sixteen_bit)
|
||||
{
|
||||
decoded_sample_data = new short[sample_length>>1];
|
||||
while(in_idx < raw_sample_data.length)
|
||||
{
|
||||
sam = raw_sample_data[in_idx] & 0xFF;
|
||||
sam = sam | ((raw_sample_data[in_idx + 1] & 0xFF) << 8);
|
||||
last_sam = last_sam + sam;
|
||||
decoded_sample_data[out_idx] = (short)last_sam;
|
||||
in_idx += 2;
|
||||
out_idx += 1;
|
||||
}
|
||||
sample.set_sample_data(decoded_sample_data, loop_start >> 1, loop_length >> 1, ping_pong);
|
||||
}
|
||||
else
|
||||
{
|
||||
decoded_sample_data = new short[sample_length];
|
||||
while(in_idx < raw_sample_data.length)
|
||||
{
|
||||
sam = raw_sample_data[in_idx] & 0xFF;
|
||||
last_sam = last_sam + sam;
|
||||
decoded_sample_data[out_idx] = (short)(last_sam << 8);
|
||||
in_idx += 1;
|
||||
out_idx += 1;
|
||||
}
|
||||
sample.set_sample_data(decoded_sample_data, loop_start, loop_length, ping_pong);
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
private static int unsigned_short_le(byte[] buffer, int offset)
|
||||
{
|
||||
int value;
|
||||
value = buffer[offset] & 0xFF;
|
||||
value = value | ((buffer[offset + 1] & 0xFF) << 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static int int_le(byte[] buffer, int offset)
|
||||
{
|
||||
int value;
|
||||
value = buffer[offset] & 0xFF;
|
||||
value = value | ((buffer[offset + 1] & 0xFF) << 8);
|
||||
value = value | ((buffer[offset + 2] & 0xFF) << 16);
|
||||
value = value | ((buffer[offset + 3] & 0x7F) << 24);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String ascii_text(byte[] buffer, int offset, int length)
|
||||
{
|
||||
int idx, chr;
|
||||
byte[] string_buffer;
|
||||
String string;
|
||||
string_buffer = new byte[length];
|
||||
for(idx = 0; idx < length; idx++)
|
||||
{
|
||||
chr = buffer[offset + idx];
|
||||
if(chr < 32)
|
||||
{
|
||||
chr = 32;
|
||||
}
|
||||
string_buffer[idx] = (byte)chr;
|
||||
}
|
||||
try
|
||||
{
|
||||
string = new String(string_buffer, 0, length, "ISO-8859-1");
|
||||
}
|
||||
catch(UnsupportedEncodingException e)
|
||||
{
|
||||
string = "";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
416
src/com/ibxm/IBXM.java
Normal file
416
src/com/ibxm/IBXM.java
Normal file
@ -0,0 +1,416 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
public class IBXM
|
||||
{
|
||||
public static final String VERSION = "ibxm alpha 51 (c)2008 mumart@gmail.com";
|
||||
|
||||
public static final int FP_SHIFT = 15;
|
||||
public static final int FP_ONE = 1 << FP_SHIFT;
|
||||
public static final int FP_MASK = FP_ONE - 1;
|
||||
|
||||
private int sampling_rate, resampling_quality, volume_ramp_length;
|
||||
private int tick_length_samples, current_tick_samples;
|
||||
private int[] mixing_buffer, volume_ramp_buffer;
|
||||
|
||||
private Module module;
|
||||
private Channel[] channels;
|
||||
private int[] global_volume, note;
|
||||
private int current_sequence_index, next_sequence_index;
|
||||
private int current_row, next_row;
|
||||
private int tick_counter, ticks_per_row;
|
||||
private int pattern_loop_count, pattern_loop_channel;
|
||||
|
||||
public IBXM(int sample_rate)
|
||||
{
|
||||
//System.out.println(VERSION);
|
||||
if(sample_rate < 8000)
|
||||
{
|
||||
sample_rate = 8000;
|
||||
}
|
||||
sampling_rate = sample_rate;
|
||||
volume_ramp_length = sampling_rate >> 10;
|
||||
volume_ramp_buffer = new int[volume_ramp_length*2];
|
||||
mixing_buffer = new int[sampling_rate/6];
|
||||
global_volume = new int[1];
|
||||
note = new int[5];
|
||||
set_module(new Module());
|
||||
set_resampling_quality(1);
|
||||
}
|
||||
|
||||
public void set_module(Module m)
|
||||
{
|
||||
int channel_idx;
|
||||
module = m;
|
||||
channels = new Channel[module.get_num_channels()];
|
||||
for(channel_idx = 0; channel_idx < channels.length; channel_idx++)
|
||||
{
|
||||
channels[channel_idx] = new Channel(module, sampling_rate, global_volume);
|
||||
}
|
||||
set_sequence_index(0, 0);
|
||||
}
|
||||
|
||||
public void set_resampling_quality(int quality)
|
||||
{
|
||||
resampling_quality = quality;
|
||||
}
|
||||
|
||||
public int calculate_song_duration()
|
||||
{
|
||||
int song_duration;
|
||||
set_sequence_index(0, 0);
|
||||
next_tick();
|
||||
song_duration = tick_length_samples;
|
||||
while(!next_tick())
|
||||
{
|
||||
song_duration += tick_length_samples;
|
||||
}
|
||||
set_sequence_index(0, 0);
|
||||
return song_duration;
|
||||
}
|
||||
|
||||
public void set_sequence_index(int sequence_index, int row)
|
||||
{
|
||||
int channel_idx;
|
||||
global_volume[0] = 64;
|
||||
for(channel_idx = 0; channel_idx < channels.length; channel_idx++)
|
||||
{
|
||||
channels[channel_idx].reset();
|
||||
channels[channel_idx].set_panning(module.get_initial_panning(channel_idx));
|
||||
}
|
||||
set_global_volume(module.global_volume);
|
||||
set_speed(6);
|
||||
set_speed(module.default_speed);
|
||||
set_tempo(125);
|
||||
set_tempo(module.default_tempo);
|
||||
pattern_loop_count = -1;
|
||||
next_sequence_index = sequence_index;
|
||||
next_row = row;
|
||||
tick_counter = 0;
|
||||
current_tick_samples = tick_length_samples;
|
||||
clear_vol_ramp_buffer();
|
||||
}
|
||||
|
||||
public void seek(int sample_position)
|
||||
{
|
||||
int idx;
|
||||
set_sequence_index(0, 0);
|
||||
next_tick();
|
||||
while(sample_position > tick_length_samples)
|
||||
{
|
||||
sample_position -= tick_length_samples;
|
||||
next_tick();
|
||||
}
|
||||
mix_tick();
|
||||
current_tick_samples = sample_position;
|
||||
}
|
||||
|
||||
public void get_audio(byte[] output_buffer, int frames)
|
||||
{
|
||||
int output_idx, mix_idx, mix_end, count, amplitude;
|
||||
|
||||
output_idx = 0;
|
||||
|
||||
while(frames > 0)
|
||||
{
|
||||
count = tick_length_samples - current_tick_samples;
|
||||
|
||||
if(count > frames)
|
||||
{
|
||||
count = frames;
|
||||
}
|
||||
|
||||
mix_idx = current_tick_samples << 1;
|
||||
mix_end = mix_idx + (count << 1) - 1;
|
||||
|
||||
while(mix_idx <= mix_end)
|
||||
{
|
||||
amplitude = mixing_buffer[mix_idx];
|
||||
|
||||
if(amplitude > 32767)
|
||||
{
|
||||
amplitude = 32767;
|
||||
}
|
||||
|
||||
if(amplitude < -32768)
|
||||
{
|
||||
amplitude = -32768;
|
||||
}
|
||||
|
||||
output_buffer[output_idx] = (byte)(amplitude & 0xFF);
|
||||
output_buffer[output_idx + 1] = (byte)(amplitude >> 8);
|
||||
|
||||
output_idx += 2;
|
||||
mix_idx += 1;
|
||||
}
|
||||
|
||||
current_tick_samples = mix_idx >> 1;
|
||||
frames -= count;
|
||||
|
||||
if(frames > 0)
|
||||
{
|
||||
next_tick();
|
||||
mix_tick();
|
||||
current_tick_samples = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mix_tick()
|
||||
{
|
||||
int channel_idx, mix_idx, mix_len;
|
||||
mix_idx = 0;
|
||||
mix_len = tick_length_samples + volume_ramp_length << 1;
|
||||
while(mix_idx < mix_len)
|
||||
{
|
||||
mixing_buffer[mix_idx] = 0;
|
||||
mix_idx += 1;
|
||||
}
|
||||
for(channel_idx = 0; channel_idx < channels.length; channel_idx++)
|
||||
{
|
||||
mix_len = tick_length_samples + volume_ramp_length;
|
||||
channels[channel_idx].resample(mixing_buffer, 0, mix_len, resampling_quality);
|
||||
}
|
||||
volume_ramp();
|
||||
}
|
||||
|
||||
private boolean next_tick()
|
||||
{
|
||||
int channel_idx;
|
||||
boolean song_end;
|
||||
for(channel_idx = 0; channel_idx < channels.length; channel_idx++)
|
||||
{
|
||||
channels[channel_idx].update_sample_idx(tick_length_samples);
|
||||
}
|
||||
tick_counter -= 1;
|
||||
if(tick_counter <= 0)
|
||||
{
|
||||
tick_counter = ticks_per_row;
|
||||
song_end = next_row();
|
||||
}
|
||||
else
|
||||
{
|
||||
for(channel_idx = 0; channel_idx < channels.length; channel_idx++)
|
||||
{
|
||||
channels[channel_idx].tick();
|
||||
}
|
||||
song_end = false;
|
||||
}
|
||||
return song_end;
|
||||
}
|
||||
|
||||
private boolean next_row()
|
||||
{
|
||||
int channel_idx, effect, effect_param;
|
||||
boolean song_end;
|
||||
Pattern pattern;
|
||||
song_end = false;
|
||||
if(next_sequence_index < 0)
|
||||
{
|
||||
/* Bad next sequence index.*/
|
||||
next_sequence_index = 0;
|
||||
next_row = 0;
|
||||
}
|
||||
if(next_sequence_index >= module.get_sequence_length())
|
||||
{
|
||||
/* End of sequence.*/
|
||||
song_end = true;
|
||||
next_sequence_index = module.restart_sequence_index;
|
||||
if(next_sequence_index < 0)
|
||||
{
|
||||
next_sequence_index = 0;
|
||||
}
|
||||
if(next_sequence_index >= module.get_sequence_length())
|
||||
{
|
||||
next_sequence_index = 0;
|
||||
}
|
||||
next_row = 0;
|
||||
}
|
||||
if(next_sequence_index < current_sequence_index)
|
||||
{
|
||||
/* Jump to previous pattern. */
|
||||
song_end = true;
|
||||
}
|
||||
if(next_sequence_index == current_sequence_index)
|
||||
{
|
||||
if(next_row <= current_row)
|
||||
{
|
||||
if(pattern_loop_count < 0)
|
||||
{
|
||||
/* Jump to previous row in the same pattern, but not a pattern loop. */
|
||||
song_end = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
current_sequence_index = next_sequence_index;
|
||||
pattern = module.get_pattern_from_sequence(current_sequence_index);
|
||||
if(next_row < 0 || next_row >= pattern.num_rows)
|
||||
{
|
||||
/* Bad next row.*/
|
||||
next_row = 0;
|
||||
}
|
||||
current_row = next_row;
|
||||
next_row = current_row + 1;
|
||||
if(next_row >= pattern.num_rows)
|
||||
{
|
||||
next_sequence_index = current_sequence_index + 1;
|
||||
next_row = 0;
|
||||
}
|
||||
for(channel_idx = 0; channel_idx < channels.length; channel_idx++)
|
||||
{
|
||||
pattern.get_note(note, current_row * channels.length + channel_idx);
|
||||
effect = note[3];
|
||||
effect_param = note[4];
|
||||
channels[channel_idx].row(note[0], note[1], note[2], effect, effect_param);
|
||||
switch(effect)
|
||||
{
|
||||
case 0x0B:
|
||||
/* Pattern Jump.*/
|
||||
if(pattern_loop_count < 0)
|
||||
{
|
||||
next_sequence_index = effect_param;
|
||||
next_row = 0;
|
||||
}
|
||||
break;
|
||||
case 0x0D:
|
||||
/* Pattern Break.*/
|
||||
if(pattern_loop_count < 0)
|
||||
{
|
||||
next_sequence_index = current_sequence_index + 1;
|
||||
next_row = (effect_param >> 4) * 10 + (effect_param & 0x0F);
|
||||
}
|
||||
break;
|
||||
case 0x0E:
|
||||
/* Extended.*/
|
||||
switch(effect_param & 0xF0)
|
||||
{
|
||||
case 0x60:
|
||||
/* Pattern loop.*/
|
||||
if((effect_param & 0x0F) == 0)
|
||||
{
|
||||
/* Set loop marker on this channel. */
|
||||
channels[channel_idx].pattern_loop_row = current_row;
|
||||
}
|
||||
if(channels[channel_idx].pattern_loop_row < current_row)
|
||||
{
|
||||
/* Marker and parameter are valid. Begin looping. */
|
||||
if(pattern_loop_count < 0)
|
||||
{
|
||||
/* Not already looping, begin. */
|
||||
pattern_loop_count = effect_param & 0x0F;
|
||||
pattern_loop_channel = channel_idx;
|
||||
}
|
||||
if(pattern_loop_channel == channel_idx)
|
||||
{
|
||||
/* Loop in progress on this channel. Next iteration. */
|
||||
if(pattern_loop_count == 0)
|
||||
{
|
||||
/* Loop finished. */
|
||||
/* Invalidate current marker. */
|
||||
channels[channel_idx].pattern_loop_row = current_row + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Count must be higher than zero. */
|
||||
/* Loop and cancel any breaks on this row. */
|
||||
next_row = channels[channel_idx].pattern_loop_row;
|
||||
next_sequence_index = current_sequence_index;
|
||||
}
|
||||
pattern_loop_count -= 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xE0:
|
||||
/* Pattern delay.*/
|
||||
tick_counter += ticks_per_row * (effect_param & 0x0F);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x0F:
|
||||
/* Set Speed/Tempo.*/
|
||||
if(effect_param < 32)
|
||||
{
|
||||
set_speed(effect_param);
|
||||
tick_counter = ticks_per_row;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_tempo(effect_param);
|
||||
}
|
||||
break;
|
||||
case 0x25:
|
||||
/* S3M Set Speed.*/
|
||||
set_speed(effect_param);
|
||||
tick_counter = ticks_per_row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return song_end;
|
||||
}
|
||||
|
||||
private void set_global_volume(int volume)
|
||||
{
|
||||
if(volume < 0)
|
||||
{
|
||||
volume = 0;
|
||||
}
|
||||
if(volume > 64)
|
||||
{
|
||||
volume = 64;
|
||||
}
|
||||
global_volume[0] = volume;
|
||||
}
|
||||
|
||||
private void set_speed(int speed)
|
||||
{
|
||||
if(speed > 0 && speed < 256)
|
||||
{
|
||||
ticks_per_row = speed;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_tempo(int bpm)
|
||||
{
|
||||
if(bpm > 31 && bpm < 256)
|
||||
{
|
||||
tick_length_samples = (sampling_rate * 5) / (bpm * 2);
|
||||
}
|
||||
}
|
||||
|
||||
private void volume_ramp()
|
||||
{
|
||||
int ramp_idx, next_idx, ramp_end;
|
||||
int volume_ramp_delta, volume, sample;
|
||||
sample = 0;
|
||||
volume_ramp_delta = FP_ONE / volume_ramp_length;
|
||||
volume = 0;
|
||||
ramp_idx = 0;
|
||||
next_idx = 2 * tick_length_samples;
|
||||
ramp_end = volume_ramp_length * 2 - 1;
|
||||
while(ramp_idx <= ramp_end)
|
||||
{
|
||||
sample = volume_ramp_buffer[ramp_idx] * (FP_ONE - volume) >> FP_SHIFT;
|
||||
mixing_buffer[ramp_idx] = sample + (mixing_buffer[ramp_idx] * volume >> FP_SHIFT);
|
||||
volume_ramp_buffer[ramp_idx] = mixing_buffer[next_idx + ramp_idx];
|
||||
sample = volume_ramp_buffer[ramp_idx + 1] * (FP_ONE - volume) >> FP_SHIFT;
|
||||
mixing_buffer[ramp_idx + 1] = sample + (mixing_buffer[ramp_idx + 1] * volume >> FP_SHIFT);
|
||||
volume_ramp_buffer[ramp_idx + 1] = mixing_buffer[next_idx + ramp_idx + 1];
|
||||
volume += volume_ramp_delta;
|
||||
ramp_idx += 2;
|
||||
}
|
||||
}
|
||||
|
||||
private void clear_vol_ramp_buffer()
|
||||
{
|
||||
int ramp_idx, ramp_end;
|
||||
ramp_idx = 0;
|
||||
ramp_end = volume_ramp_length * 2 - 1;
|
||||
while(ramp_idx <= ramp_end)
|
||||
{
|
||||
volume_ramp_buffer[ramp_idx] = 0;
|
||||
ramp_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
91
src/com/ibxm/Instrument.java
Normal file
91
src/com/ibxm/Instrument.java
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
public class Instrument {
|
||||
public String name;
|
||||
public int vibrato_type, vibrato_sweep;
|
||||
public int vibrato_depth, vibrato_rate;
|
||||
public boolean volume_envelope_active, panning_envelope_active;
|
||||
public int volume_fade_out;
|
||||
|
||||
private Envelope volume_envelope, panning_envelope;
|
||||
private int[] key_to_sample;
|
||||
private Sample[] samples;
|
||||
|
||||
public Instrument() {
|
||||
name = "";
|
||||
set_volume_envelope( new Envelope() );
|
||||
set_panning_envelope( new Envelope() );
|
||||
key_to_sample = new int[ 96 ];
|
||||
set_num_samples( 1 );
|
||||
}
|
||||
|
||||
public Envelope get_volume_envelope() {
|
||||
return volume_envelope;
|
||||
}
|
||||
|
||||
public void set_volume_envelope( Envelope envelope ) {
|
||||
if( envelope != null ) {
|
||||
volume_envelope = envelope;
|
||||
}
|
||||
}
|
||||
|
||||
public Envelope get_panning_envelope() {
|
||||
return panning_envelope;
|
||||
}
|
||||
|
||||
public void set_panning_envelope( Envelope envelope ) {
|
||||
if( envelope != null ) {
|
||||
panning_envelope = envelope;
|
||||
}
|
||||
}
|
||||
|
||||
public Sample get_sample_from_key( int key ) {
|
||||
int sample_idx;
|
||||
sample_idx = 0;
|
||||
if( key > 0 && key <= key_to_sample.length ) {
|
||||
sample_idx = key_to_sample[ key - 1 ];
|
||||
}
|
||||
return get_sample( sample_idx );
|
||||
}
|
||||
|
||||
public void set_key_to_sample( int key, int sample ) {
|
||||
if( key > 0 && key <= key_to_sample.length ) {
|
||||
key_to_sample[ key - 1 ] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_samples() {
|
||||
return samples.length;
|
||||
}
|
||||
|
||||
public void set_num_samples( int num_samples ) {
|
||||
if( num_samples < 1 ) {
|
||||
num_samples = 1;
|
||||
}
|
||||
samples = new Sample[ num_samples ];
|
||||
set_sample( 0, null );
|
||||
}
|
||||
|
||||
public Sample get_sample( int sample_index ) {
|
||||
Sample sample;
|
||||
sample = null;
|
||||
if( sample_index >= 0 && sample_index < samples.length ) {
|
||||
sample = samples[ sample_index ];
|
||||
}
|
||||
if( sample == null ) {
|
||||
sample = samples[ 0 ];
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
public void set_sample( int sample_index, Sample sample ) {
|
||||
if( sample_index >= 0 && sample_index < samples.length ) {
|
||||
samples[ sample_index ] = sample;
|
||||
}
|
||||
if( samples[ 0 ] == null ) {
|
||||
samples[ 0 ] = new Sample();
|
||||
}
|
||||
}
|
||||
}
|
92
src/com/ibxm/LogTable.java
Normal file
92
src/com/ibxm/LogTable.java
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
/*
|
||||
Base-2 Log and Exp functions, using linear-interpolated tables.
|
||||
*/
|
||||
public class LogTable {
|
||||
private static final int TABLE_SHIFT = 7; // 128 points (+1 for interp)
|
||||
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - TABLE_SHIFT;
|
||||
private static final int INTERP_MASK = ( 1 << INTERP_SHIFT ) - 1;
|
||||
|
||||
private static final int[] exp_2_table = {
|
||||
32768, 32945, 33124, 33304, 33485, 33667, 33850, 34033,
|
||||
34218, 34404, 34591, 34779, 34968, 35157, 35348, 35540,
|
||||
35733, 35927, 36122, 36319, 36516, 36714, 36913, 37114,
|
||||
37315, 37518, 37722, 37926, 38132, 38339, 38548, 38757,
|
||||
38967, 39179, 39392, 39606, 39821, 40037, 40254, 40473,
|
||||
40693, 40914, 41136, 41359, 41584, 41810, 42037, 42265,
|
||||
42494, 42725, 42957, 43190, 43425, 43661, 43898, 44136,
|
||||
44376, 44617, 44859, 45103, 45347, 45594, 45841, 46090,
|
||||
46340, 46592, 46845, 47099, 47355, 47612, 47871, 48131,
|
||||
48392, 48655, 48919, 49185, 49452, 49720, 49990, 50262,
|
||||
50535, 50809, 51085, 51362, 51641, 51922, 52204, 52487,
|
||||
52772, 53059, 53347, 53636, 53928, 54220, 54515, 54811,
|
||||
55108, 55408, 55709, 56011, 56315, 56621, 56928, 57238,
|
||||
57548, 57861, 58175, 58491, 58809, 59128, 59449, 59772,
|
||||
60096, 60423, 60751, 61081, 61412, 61746, 62081, 62418,
|
||||
62757, 63098, 63440, 63785, 64131, 64479, 64830, 65182,
|
||||
65536
|
||||
};
|
||||
|
||||
private static final int[] log_2_table = {
|
||||
0, 367, 732, 1095, 1454, 1811, 2165, 2517,
|
||||
2865, 3212, 3556, 3897, 4236, 4572, 4906, 5238,
|
||||
5568, 5895, 6220, 6542, 6863, 7181, 7497, 7812,
|
||||
8124, 8434, 8742, 9048, 9352, 9654, 9954, 10252,
|
||||
10548, 10843, 11136, 11427, 11716, 12003, 12289, 12573,
|
||||
12855, 13136, 13414, 13692, 13967, 14241, 14514, 14785,
|
||||
15054, 15322, 15588, 15853, 16117, 16378, 16639, 16898,
|
||||
17156, 17412, 17667, 17920, 18172, 18423, 18673, 18921,
|
||||
19168, 19413, 19657, 19900, 20142, 20383, 20622, 20860,
|
||||
21097, 21333, 21568, 21801, 22034, 22265, 22495, 22724,
|
||||
22952, 23178, 23404, 23628, 23852, 24074, 24296, 24516,
|
||||
24736, 24954, 25171, 25388, 25603, 25817, 26031, 26243,
|
||||
26455, 26665, 26875, 27084, 27292, 27499, 27705, 27910,
|
||||
28114, 28317, 28520, 28721, 28922, 29122, 29321, 29519,
|
||||
29716, 29913, 30109, 30304, 30498, 30691, 30884, 31076,
|
||||
31267, 31457, 31646, 31835, 32023, 32210, 32397, 32582,
|
||||
32768
|
||||
};
|
||||
|
||||
/*
|
||||
Calculate log-base-2 of x (non-fixed-point).
|
||||
A fixed point value is returned.
|
||||
*/
|
||||
public static int log_2( int x ) {
|
||||
int shift;
|
||||
/* Scale x to range 1.0 <= x < 2.0 */
|
||||
shift = IBXM.FP_SHIFT;
|
||||
while( x < IBXM.FP_ONE ) {
|
||||
x <<= 1;
|
||||
shift--;
|
||||
}
|
||||
while( x >= ( IBXM.FP_ONE << 1 ) ) {
|
||||
x >>= 1;
|
||||
shift++;
|
||||
}
|
||||
return ( IBXM.FP_ONE * shift ) + eval_table( log_2_table, x - IBXM.FP_ONE );
|
||||
}
|
||||
|
||||
/*
|
||||
Raise 2 to the power x (fixed point).
|
||||
A fixed point value is returned.
|
||||
*/
|
||||
public static int raise_2( int x ) {
|
||||
int y;
|
||||
y = eval_table( exp_2_table, x & IBXM.FP_MASK ) << IBXM.FP_SHIFT;
|
||||
return y >> IBXM.FP_SHIFT - ( x >> IBXM.FP_SHIFT );
|
||||
}
|
||||
|
||||
private static int eval_table( int[] table, int x ) {
|
||||
int table_idx, table_frac, c, m, y;
|
||||
table_idx = x >> INTERP_SHIFT;
|
||||
table_frac = x & INTERP_MASK;
|
||||
c = table[ table_idx ];
|
||||
m = table[ table_idx + 1 ] - c;
|
||||
y = ( m * table_frac >> INTERP_SHIFT ) + c;
|
||||
return y >> 15 - IBXM.FP_SHIFT;
|
||||
}
|
||||
}
|
||||
|
171
src/com/ibxm/Module.java
Normal file
171
src/com/ibxm/Module.java
Normal file
@ -0,0 +1,171 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
public class Module
|
||||
{
|
||||
public String song_title;
|
||||
public boolean linear_periods, fast_volume_slides, pal;
|
||||
public int global_volume, channel_gain;
|
||||
public int default_speed, default_tempo;
|
||||
public int restart_sequence_index;
|
||||
|
||||
private int[] initial_panning, sequence;
|
||||
private Pattern[] patterns;
|
||||
private Instrument[] instruments;
|
||||
|
||||
private Pattern default_pattern;
|
||||
private Instrument default_instrument;
|
||||
|
||||
public Module()
|
||||
{
|
||||
song_title = IBXM.VERSION;
|
||||
set_num_channels(1);
|
||||
set_sequence_length(1);
|
||||
set_num_patterns(0);
|
||||
set_num_instruments(0);
|
||||
default_pattern = new Pattern();
|
||||
default_instrument = new Instrument();
|
||||
}
|
||||
|
||||
public int get_num_channels()
|
||||
{
|
||||
return initial_panning.length;
|
||||
}
|
||||
|
||||
public void set_num_channels(int num_channels)
|
||||
{
|
||||
if(num_channels < 1)
|
||||
{
|
||||
num_channels = 1;
|
||||
}
|
||||
initial_panning = new int[num_channels];
|
||||
}
|
||||
|
||||
public int get_initial_panning(int channel)
|
||||
{
|
||||
int panning;
|
||||
panning = 128;
|
||||
if(channel >= 0 && channel < initial_panning.length)
|
||||
{
|
||||
panning = initial_panning[channel];
|
||||
}
|
||||
return panning;
|
||||
}
|
||||
|
||||
public void set_initial_panning(int channel, int panning)
|
||||
{
|
||||
if(channel >= 0 && channel < initial_panning.length)
|
||||
{
|
||||
initial_panning[channel] = panning;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_sequence_length()
|
||||
{
|
||||
return sequence.length;
|
||||
}
|
||||
|
||||
public void set_sequence_length(int sequence_length)
|
||||
{
|
||||
if(sequence_length < 0)
|
||||
{
|
||||
sequence_length = 0;
|
||||
}
|
||||
sequence = new int[sequence_length];
|
||||
}
|
||||
|
||||
public void set_sequence(int sequence_index, int pattern_index)
|
||||
{
|
||||
if(sequence_index >= 0 && sequence_index < sequence.length)
|
||||
{
|
||||
sequence[sequence_index] = pattern_index;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_patterns()
|
||||
{
|
||||
return patterns.length;
|
||||
}
|
||||
|
||||
public void set_num_patterns(int num_patterns)
|
||||
{
|
||||
if(num_patterns < 0)
|
||||
{
|
||||
num_patterns = 0;
|
||||
}
|
||||
patterns = new Pattern[num_patterns];
|
||||
}
|
||||
|
||||
public Pattern get_pattern_from_sequence(int sequence_index)
|
||||
{
|
||||
Pattern pattern;
|
||||
pattern = default_pattern;
|
||||
if(sequence_index >= 0 && sequence_index < sequence.length)
|
||||
{
|
||||
pattern = get_pattern(sequence[sequence_index]);
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public Pattern get_pattern(int pattern_index)
|
||||
{
|
||||
Pattern pattern;
|
||||
pattern = null;
|
||||
if(pattern_index >= 0 && pattern_index < patterns.length)
|
||||
{
|
||||
pattern = patterns[pattern_index];
|
||||
}
|
||||
if(pattern == null)
|
||||
{
|
||||
pattern = default_pattern;
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void set_pattern(int pattern_index, Pattern pattern)
|
||||
{
|
||||
if(pattern_index >= 0 && pattern_index < patterns.length)
|
||||
{
|
||||
patterns[pattern_index] = pattern;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_instruments()
|
||||
{
|
||||
return instruments.length;
|
||||
}
|
||||
|
||||
public void set_num_instruments(int num_instruments)
|
||||
{
|
||||
if(num_instruments < 0)
|
||||
{
|
||||
num_instruments = 0;
|
||||
}
|
||||
instruments = new Instrument[num_instruments];
|
||||
}
|
||||
|
||||
public Instrument get_instrument(int instrument_index)
|
||||
{
|
||||
Instrument instrument;
|
||||
instrument = null;
|
||||
if(instrument_index > 0 && instrument_index <= instruments.length)
|
||||
{
|
||||
instrument = instruments[instrument_index - 1];
|
||||
}
|
||||
if(instrument == null)
|
||||
{
|
||||
instrument = default_instrument;
|
||||
}
|
||||
return instrument;
|
||||
}
|
||||
|
||||
public void set_instrument(int instrument_index, Instrument instrument)
|
||||
{
|
||||
if(instrument_index > 0 && instrument_index <= instruments.length)
|
||||
{
|
||||
instruments[instrument_index - 1] = instrument;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/com/ibxm/Pattern.java
Normal file
62
src/com/ibxm/Pattern.java
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
public class Pattern {
|
||||
public int num_rows;
|
||||
|
||||
private int data_offset, note_index;
|
||||
private byte[] pattern_data;
|
||||
|
||||
public Pattern() {
|
||||
num_rows = 1;
|
||||
set_pattern_data( new byte[ 0 ] );
|
||||
}
|
||||
|
||||
public void set_pattern_data( byte[] data ) {
|
||||
if( data != null ) {
|
||||
pattern_data = data;
|
||||
}
|
||||
data_offset = 0;
|
||||
note_index = 0;
|
||||
}
|
||||
|
||||
public void get_note( int[] note, int index ) {
|
||||
if( index < note_index ) {
|
||||
note_index = 0;
|
||||
data_offset = 0;
|
||||
}
|
||||
while( note_index <= index ) {
|
||||
data_offset = next_note( data_offset, note );
|
||||
note_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int next_note( int data_offset, int[] note ) {
|
||||
int bitmask, field;
|
||||
if( data_offset < 0 ) {
|
||||
data_offset = pattern_data.length;
|
||||
}
|
||||
bitmask = 0x80;
|
||||
if( data_offset < pattern_data.length ) {
|
||||
bitmask = pattern_data[ data_offset ] & 0xFF;
|
||||
}
|
||||
if( ( bitmask & 0x80 ) == 0x80 ) {
|
||||
data_offset += 1;
|
||||
} else {
|
||||
bitmask = 0x1F;
|
||||
}
|
||||
for( field = 0; field < 5; field++ ) {
|
||||
note[ field ] = 0;
|
||||
if( ( bitmask & 0x01 ) == 0x01 ) {
|
||||
if( data_offset < pattern_data.length ) {
|
||||
note[ field ] = pattern_data[ data_offset ] & 0xFF;
|
||||
data_offset += 1;
|
||||
}
|
||||
}
|
||||
bitmask = bitmask >> 1;
|
||||
}
|
||||
return data_offset;
|
||||
}
|
||||
}
|
||||
|
235
src/com/ibxm/ProTracker.java
Normal file
235
src/com/ibxm/ProTracker.java
Normal file
@ -0,0 +1,235 @@
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ProTracker {
|
||||
public static boolean is_mod( byte[] header_1084_bytes ) {
|
||||
boolean is_mod;
|
||||
is_mod = false;
|
||||
if( calculate_num_channels( header_1084_bytes ) > 0 ) {
|
||||
is_mod = true;
|
||||
}
|
||||
return is_mod;
|
||||
}
|
||||
|
||||
public static Module load_mod( byte[] header_1084_bytes, DataInput data_input ) throws IOException {
|
||||
int num_channels, channel_idx, panning;
|
||||
int sequence_length, restart_idx, sequence_idx;
|
||||
int num_patterns, pattern_idx, instrument_idx;
|
||||
Module module;
|
||||
num_channels = calculate_num_channels( header_1084_bytes );
|
||||
if( num_channels < 1 ) {
|
||||
throw new IllegalArgumentException( "ProTracker: Unrecognised module format!" );
|
||||
}
|
||||
module = new Module();
|
||||
module.song_title = ascii_text( header_1084_bytes, 0, 20 );
|
||||
module.pal = ( num_channels == 4 );
|
||||
module.global_volume = 64;
|
||||
module.channel_gain = IBXM.FP_ONE * 3 / 8;
|
||||
module.default_speed = 6;
|
||||
module.default_tempo = 125;
|
||||
module.set_num_channels( num_channels );
|
||||
for( channel_idx = 0; channel_idx < num_channels; channel_idx++ ) {
|
||||
panning = 64;
|
||||
if( ( channel_idx & 0x03 ) == 0x01 || ( channel_idx & 0x03 ) == 0x02 ) {
|
||||
panning = 192;
|
||||
}
|
||||
module.set_initial_panning( channel_idx, panning );
|
||||
}
|
||||
sequence_length = header_1084_bytes[ 950 ] & 0x7F;
|
||||
restart_idx = header_1084_bytes[ 951 ] & 0x7F;
|
||||
if( restart_idx >= sequence_length ) {
|
||||
restart_idx = 0;
|
||||
}
|
||||
module.restart_sequence_index = restart_idx;
|
||||
module.set_sequence_length( sequence_length );
|
||||
for( sequence_idx = 0; sequence_idx < sequence_length; sequence_idx++ ) {
|
||||
module.set_sequence( sequence_idx, header_1084_bytes[ 952 + sequence_idx ] & 0x7F );
|
||||
}
|
||||
num_patterns = calculate_num_patterns( header_1084_bytes );
|
||||
module.set_num_patterns( num_patterns );
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
module.set_pattern( pattern_idx, read_mod_pattern( data_input, num_channels ) );
|
||||
}
|
||||
module.set_num_instruments( 31 );
|
||||
for( instrument_idx = 1; instrument_idx <= 31; instrument_idx++ ) {
|
||||
module.set_instrument( instrument_idx, read_mod_instrument( header_1084_bytes, instrument_idx, data_input ) );
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static int calculate_num_patterns( byte[] module_header ) {
|
||||
int num_patterns, order_entry, pattern_idx;
|
||||
num_patterns = 0;
|
||||
for( pattern_idx = 0; pattern_idx < 128; pattern_idx++ ) {
|
||||
order_entry = module_header[ 952 + pattern_idx ] & 0x7F;
|
||||
if( order_entry >= num_patterns ) {
|
||||
num_patterns = order_entry + 1;
|
||||
}
|
||||
}
|
||||
return num_patterns;
|
||||
}
|
||||
|
||||
private static int calculate_num_channels( byte[] module_header ) {
|
||||
int num_channels;
|
||||
switch( ( module_header[ 1082 ] << 8 ) | module_header[ 1083 ] ) {
|
||||
case 0x4b2e: /* M.K. */
|
||||
case 0x4b21: /* M!K! */
|
||||
case 0x542e: /* N.T. */
|
||||
case 0x5434: /* FLT4 */
|
||||
num_channels = 4;
|
||||
break;
|
||||
case 0x484e: /* xCHN */
|
||||
num_channels = module_header[ 1080 ] - 48;
|
||||
break;
|
||||
case 0x4348: /* xxCH */
|
||||
num_channels = ( ( module_header[ 1080 ] - 48 ) * 10 ) + ( module_header[ 1081 ] - 48 );
|
||||
break;
|
||||
default:
|
||||
/* Not recognised. */
|
||||
num_channels = 0;
|
||||
break;
|
||||
}
|
||||
return num_channels;
|
||||
}
|
||||
|
||||
private static Pattern read_mod_pattern( DataInput data_input, int num_channels ) throws IOException {
|
||||
int input_idx, output_idx;
|
||||
int period, instrument, effect, effect_param;
|
||||
Pattern pattern;
|
||||
byte[] input_pattern_data, output_pattern_data;
|
||||
pattern = new Pattern();
|
||||
pattern.num_rows = 64;
|
||||
input_pattern_data = new byte[ 64 * num_channels * 4 ];
|
||||
output_pattern_data = new byte[ 64 * num_channels * 5 ];
|
||||
data_input.readFully( input_pattern_data );
|
||||
input_idx = 0;
|
||||
output_idx = 0;
|
||||
while( input_idx < input_pattern_data.length ) {
|
||||
period = ( input_pattern_data[ input_idx ] & 0x0F ) << 8;
|
||||
period = period | ( input_pattern_data[ input_idx + 1 ] & 0xFF );
|
||||
output_pattern_data[ output_idx ] = to_key( period );
|
||||
instrument = input_pattern_data[ input_idx ] & 0x10;
|
||||
instrument = instrument | ( ( input_pattern_data[ input_idx + 2 ] & 0xF0 ) >> 4 );
|
||||
output_pattern_data[ output_idx + 1 ] = ( byte ) instrument;
|
||||
effect = input_pattern_data[ input_idx + 2 ] & 0x0F;
|
||||
effect_param = input_pattern_data[ input_idx + 3 ] & 0xFF;
|
||||
if( effect == 0x01 && effect_param == 0 ) {
|
||||
/* Portamento up of zero has no effect. */
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x02 && effect_param == 0 ) {
|
||||
/* Portamento down of zero has no effect. */
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x08 && num_channels == 4 ) {
|
||||
/* Some Amiga mods use effect 0x08 for reasons other than panning.*/
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
}
|
||||
if( effect == 0x0A && effect_param == 0 ) {
|
||||
/* Volume slide of zero has no effect.*/
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x05 && effect_param == 0 ) {
|
||||
/* Porta + Volume slide of zero has no effect.*/
|
||||
effect = 0x03;
|
||||
}
|
||||
if( effect == 0x06 && effect_param == 0 ) {
|
||||
/* Vibrato + Volume slide of zero has no effect.*/
|
||||
effect = 0x04;
|
||||
}
|
||||
output_pattern_data[ output_idx + 3 ] = ( byte ) effect;
|
||||
output_pattern_data[ output_idx + 4 ] = ( byte ) effect_param;
|
||||
input_idx += 4;
|
||||
output_idx += 5;
|
||||
}
|
||||
pattern.set_pattern_data( output_pattern_data );
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static Instrument read_mod_instrument( byte[] mod_header, int idx, DataInput data_input ) throws IOException {
|
||||
int header_offset, sample_data_length;
|
||||
int loop_start, loop_length, sample_idx, fine_tune;
|
||||
Instrument instrument;
|
||||
Sample sample;
|
||||
byte[] raw_sample_data;
|
||||
short[] sample_data;
|
||||
header_offset = ( idx - 1 ) * 30 + 20;
|
||||
instrument = new Instrument();
|
||||
instrument.name = ascii_text( mod_header, header_offset, 22 );
|
||||
sample = new Sample();
|
||||
sample_data_length = unsigned_short_be( mod_header, header_offset + 22 ) << 1;
|
||||
fine_tune = mod_header[ header_offset + 24 ] & 0x0F;
|
||||
if( fine_tune > 7 ) {
|
||||
fine_tune -= 16;
|
||||
}
|
||||
sample.transpose = ( fine_tune << IBXM.FP_SHIFT ) / 96;
|
||||
sample.volume = mod_header[ header_offset + 25 ] & 0x7F;
|
||||
loop_start = unsigned_short_be( mod_header, header_offset + 26 ) << 1;
|
||||
loop_length = unsigned_short_be( mod_header, header_offset + 28 ) << 1;
|
||||
if( loop_length < 4 ) {
|
||||
loop_length = 0;
|
||||
}
|
||||
raw_sample_data = new byte[ sample_data_length ];
|
||||
sample_data = new short[ sample_data_length ];
|
||||
try {
|
||||
data_input.readFully( raw_sample_data );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "ProTracker: Instrument " + idx + " has samples missing." );
|
||||
}
|
||||
for( sample_idx = 0; sample_idx < raw_sample_data.length; sample_idx++ ) {
|
||||
sample_data[ sample_idx ] = ( short ) ( raw_sample_data[ sample_idx ] << 8 );
|
||||
}
|
||||
sample.set_sample_data( sample_data, loop_start, loop_length, false );
|
||||
instrument.set_num_samples( 1 );
|
||||
instrument.set_sample( 0, sample );
|
||||
return instrument;
|
||||
}
|
||||
|
||||
private static byte to_key( int period ) {
|
||||
int oct, key;
|
||||
if( period < 32 ) {
|
||||
key = 0;
|
||||
} else {
|
||||
oct = LogTable.log_2( 7256 ) - LogTable.log_2( period );
|
||||
if( oct < 0 ) {
|
||||
key = 0;
|
||||
} else {
|
||||
key = oct * 12;
|
||||
key = key >> ( IBXM.FP_SHIFT - 1 );
|
||||
key = ( key >> 1 ) + ( key & 1 );
|
||||
}
|
||||
}
|
||||
return ( byte ) key;
|
||||
}
|
||||
|
||||
private static int unsigned_short_be( byte[] buf, int offset ) {
|
||||
int value;
|
||||
value = ( buf[ offset ] & 0xFF ) << 8;
|
||||
value = value | ( buf[ offset + 1 ] & 0xFF );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String ascii_text( byte[] buffer, int offset, int length ) {
|
||||
int idx, chr;
|
||||
byte[] string_buffer;
|
||||
String string;
|
||||
string_buffer = new byte[ length ];
|
||||
for( idx = 0; idx < length; idx++ ) {
|
||||
chr = buffer[ offset + idx ];
|
||||
if( chr < 32 ) {
|
||||
chr = 32;
|
||||
}
|
||||
string_buffer[ idx ] = ( byte ) chr;
|
||||
}
|
||||
try {
|
||||
string = new String( string_buffer, 0, length, "ISO-8859-1" );
|
||||
} catch( UnsupportedEncodingException e ) {
|
||||
string = "";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
240
src/com/ibxm/Sample.java
Normal file
240
src/com/ibxm/Sample.java
Normal file
@ -0,0 +1,240 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
public class Sample {
|
||||
public String name;
|
||||
public boolean set_panning;
|
||||
public int volume, panning;
|
||||
public int transpose;
|
||||
|
||||
private int loop_start, loop_length;
|
||||
private short[] sample_data;
|
||||
|
||||
/* For the sinc interpolator.*/
|
||||
private static final int POINT_SHIFT = 4;
|
||||
private static final int POINTS = 1 << POINT_SHIFT;
|
||||
private static final int OVERLAP = POINTS >> 1;
|
||||
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - 4;
|
||||
private static final int INTERP_BITMASK = ( 1 << INTERP_SHIFT ) - 1;
|
||||
private static final short[] sinc_table = {
|
||||
0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0, 0,
|
||||
0, 0, -5, 36, -142, 450, -1439, 32224, 2302, -974, 455, -190, 64, -15, 2, 0,
|
||||
0, 6, -33, 128, -391, 1042, -2894, 31584, 4540, -1765, 786, -318, 105, -25, 3, 0,
|
||||
0, 10, -55, 204, -597, 1533, -4056, 30535, 6977, -2573, 1121, -449, 148, -36, 5, 0,
|
||||
-1, 13, -71, 261, -757, 1916, -4922, 29105, 9568, -3366, 1448, -578, 191, -47, 7, 0,
|
||||
-1, 15, -81, 300, -870, 2185, -5498, 27328, 12263, -4109, 1749, -698, 232, -58, 9, 0,
|
||||
-1, 15, -86, 322, -936, 2343, -5800, 25249, 15006, -4765, 2011, -802, 269, -68, 10, 0,
|
||||
-1, 15, -87, 328, -957, 2394, -5849, 22920, 17738, -5298, 2215, -885, 299, -77, 12, 0,
|
||||
0, 14, -83, 319, -938, 2347, -5671, 20396, 20396, -5671, 2347, -938, 319, -83, 14, 0,
|
||||
0, 12, -77, 299, -885, 2215, -5298, 17738, 22920, -5849, 2394, -957, 328, -87, 15, -1,
|
||||
0, 10, -68, 269, -802, 2011, -4765, 15006, 25249, -5800, 2343, -936, 322, -86, 15, -1,
|
||||
0, 9, -58, 232, -698, 1749, -4109, 12263, 27328, -5498, 2185, -870, 300, -81, 15, -1,
|
||||
0, 7, -47, 191, -578, 1448, -3366, 9568, 29105, -4922, 1916, -757, 261, -71, 13, -1,
|
||||
0, 5, -36, 148, -449, 1121, -2573, 6977, 30535, -4056, 1533, -597, 204, -55, 10, 0,
|
||||
0, 3, -25, 105, -318, 786, -1765, 4540, 31584, -2894, 1042, -391, 128, -33, 6, 0,
|
||||
0, 2, -15, 64, -190, 455, -974, 2302, 32224, -1439, 450, -142, 36, -5, 0, 0,
|
||||
0, 0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0
|
||||
};
|
||||
|
||||
public Sample() {
|
||||
name = "";
|
||||
set_sample_data( new short[ 0 ], 0, 0, false );
|
||||
}
|
||||
|
||||
public void set_sample_data( short[] data, int loop_start, int loop_length, boolean ping_pong ) {
|
||||
int offset;
|
||||
short sample;
|
||||
if( loop_start < 0 ) {
|
||||
loop_start = 0;
|
||||
}
|
||||
if( loop_start >= data.length ) {
|
||||
loop_start = data.length - 1;
|
||||
}
|
||||
if( loop_start + loop_length > data.length ) {
|
||||
loop_length = data.length - loop_start;
|
||||
}
|
||||
if( loop_length <= 1 ) {
|
||||
sample_data = new short[ OVERLAP + data.length + OVERLAP * 3 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, data.length );
|
||||
offset = 0;
|
||||
while( offset < OVERLAP ) {
|
||||
sample = sample_data[ OVERLAP + data.length - 1 ];
|
||||
sample = ( short ) ( sample * ( OVERLAP - offset ) / OVERLAP );
|
||||
sample_data[ OVERLAP + data.length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
loop_start = OVERLAP + data.length + OVERLAP;
|
||||
loop_length = 1;
|
||||
} else {
|
||||
if( ping_pong ) {
|
||||
sample_data = new short[ OVERLAP + loop_start + loop_length * 2 + OVERLAP * 2 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
|
||||
offset = 0;
|
||||
while( offset < loop_length ) {
|
||||
sample = data[ loop_start + loop_length - offset - 1 ];
|
||||
sample_data[ OVERLAP + loop_start + loop_length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
loop_start = loop_start + OVERLAP;
|
||||
loop_length = loop_length * 2;
|
||||
} else {
|
||||
sample_data = new short[ OVERLAP + loop_start + loop_length + OVERLAP * 2 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
|
||||
loop_start = loop_start + OVERLAP;
|
||||
}
|
||||
offset = 0;
|
||||
while( offset < OVERLAP * 2 ) {
|
||||
sample = sample_data[ loop_start + offset ];
|
||||
sample_data[ loop_start + loop_length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
this.loop_start = loop_start;
|
||||
this.loop_length = loop_length;
|
||||
}
|
||||
|
||||
public void resample_nearest(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int loop_end, offset, end, max_sample_idx;
|
||||
sample_idx += OVERLAP;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( frames > 0 ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
|
||||
if( max_sample_idx > loop_end ) {
|
||||
while( sample_idx <= loop_end ) {
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
} else {
|
||||
while( offset <= end ) {
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
frames = ( end - offset + 2 ) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void resample_linear(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int loop_end, offset, end, max_sample_idx, amplitude;
|
||||
sample_idx += OVERLAP;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( frames > 0 ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
|
||||
if( max_sample_idx > loop_end ) {
|
||||
while( sample_idx <= loop_end ) {
|
||||
amplitude = sample_data[ sample_idx ];
|
||||
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
} else {
|
||||
while( offset <= end ) {
|
||||
amplitude = sample_data[ sample_idx ];
|
||||
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
frames = ( end - offset + 2 ) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void resample_sinc(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int offset, end, loop_end, table_idx, a1, a2, amplitude;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( offset <= end ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
table_idx = ( sample_frac >> INTERP_SHIFT ) << POINT_SHIFT;
|
||||
a1 = sinc_table[ table_idx + 0 ] * sample_data[ sample_idx + 0 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 1 ] * sample_data[ sample_idx + 1 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 2 ] * sample_data[ sample_idx + 2 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 3 ] * sample_data[ sample_idx + 3 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 4 ] * sample_data[ sample_idx + 4 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 5 ] * sample_data[ sample_idx + 5 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 6 ] * sample_data[ sample_idx + 6 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 7 ] * sample_data[ sample_idx + 7 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 8 ] * sample_data[ sample_idx + 8 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 9 ] * sample_data[ sample_idx + 9 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 10 ] * sample_data[ sample_idx + 10 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 11 ] * sample_data[ sample_idx + 11 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 12 ] * sample_data[ sample_idx + 12 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 13 ] * sample_data[ sample_idx + 13 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 14 ] * sample_data[ sample_idx + 14 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 15 ] * sample_data[ sample_idx + 15 ] >> 15;
|
||||
a2 = sinc_table[ table_idx + 16 ] * sample_data[ sample_idx + 0 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 17 ] * sample_data[ sample_idx + 1 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 18 ] * sample_data[ sample_idx + 2 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 19 ] * sample_data[ sample_idx + 3 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 20 ] * sample_data[ sample_idx + 4 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 21 ] * sample_data[ sample_idx + 5 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 22 ] * sample_data[ sample_idx + 6 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 23 ] * sample_data[ sample_idx + 7 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 24 ] * sample_data[ sample_idx + 8 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 25 ] * sample_data[ sample_idx + 9 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 26 ] * sample_data[ sample_idx + 10 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 27 ] * sample_data[ sample_idx + 11 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 28 ] * sample_data[ sample_idx + 12 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 29 ] * sample_data[ sample_idx + 13 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 30 ] * sample_data[ sample_idx + 14 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 31 ] * sample_data[ sample_idx + 15 ] >> 15;
|
||||
amplitude = a1 + ( ( a2 - a1 ) * ( sample_frac & INTERP_BITMASK ) >> INTERP_SHIFT );
|
||||
mix_buffer[ offset ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset + 1 ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
offset += 2;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean has_finished( int sample_idx ) {
|
||||
boolean finished;
|
||||
finished = false;
|
||||
if( loop_length <= 1 && sample_idx > loop_start ) {
|
||||
finished = true;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
}
|
490
src/com/ibxm/ScreamTracker3.java
Normal file
490
src/com/ibxm/ScreamTracker3.java
Normal file
@ -0,0 +1,490 @@
|
||||
|
||||
//package ibxm;
|
||||
package com.ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ScreamTracker3 {
|
||||
private static final int[] effect_map = new int[] {
|
||||
0xFF,
|
||||
0x25, /* A: Set Speed.*/
|
||||
0x0B, /* B: Pattern Jump.*/
|
||||
0x0D, /* C: Pattern Break.*/
|
||||
0x0A, /* D: Volume Slide.*/
|
||||
0x02, /* E: Portamento Down.*/
|
||||
0x01, /* F: Portamento Up.*/
|
||||
0x03, /* G: Tone Portamento.*/
|
||||
0x04, /* H: Vibrato.*/
|
||||
0x1D, /* I: Tremor.*/
|
||||
0x00, /* J: Arpeggio.*/
|
||||
0x06, /* K: Vibrato + Volume Slide.*/
|
||||
0x05, /* L: Tone Portamento + Volume Slide.*/
|
||||
0xFF, /* M: */
|
||||
0xFF, /* N: */
|
||||
0x09, /* O: Sample Offset.*/
|
||||
0xFF, /* P: */
|
||||
0x1B, /* Q: Retrig + Volume Slide.*/
|
||||
0x07, /* R: Tremolo.*/
|
||||
0x0E, /* S: Extended Effects.*/
|
||||
0x0F, /* T: Set Tempo.*/
|
||||
0x24, /* U: Fine Vibrato.*/
|
||||
0x10, /* V: Set Global Volume. */
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
private static final int[] effect_s_map = new int[] {
|
||||
0x00, /* 0: Set Filter.*/
|
||||
0x03, /* 1: Glissando.*/
|
||||
0x05, /* 2: Set Fine Tune.*/
|
||||
0x04, /* 3: Set Vibrato Waveform.*/
|
||||
0x07, /* 4: Set Tremolo Waveform.*/
|
||||
0xFF, /* 5: */
|
||||
0xFF, /* 6: */
|
||||
0xFF, /* 7: */
|
||||
0x08, /* 8: Set Panning.*/
|
||||
0xFF, /* 9: */
|
||||
0x09, /* A: Stereo Control.*/
|
||||
0x06, /* B: Pattern Loop.*/
|
||||
0x0C, /* C: Note Cut.*/
|
||||
0x0D, /* D: Note Delay.*/
|
||||
0x0E, /* E: Pattern Delay.*/
|
||||
0x0F /* F: Invert Loop.*/
|
||||
};
|
||||
|
||||
public static boolean is_s3m( byte[] header_96_bytes ) {
|
||||
String s3m_identifier;
|
||||
s3m_identifier = ascii_text( header_96_bytes, 44, 4 );
|
||||
return s3m_identifier.equals( "SCRM" );
|
||||
}
|
||||
|
||||
public static Module load_s3m( byte[] header_96_bytes, DataInput data_input ) throws IOException {
|
||||
int num_pattern_orders, num_instruments, num_patterns, num_channels;
|
||||
int flags, tracker_version, master_volume, panning, channel_config, sequence_length;
|
||||
int instrument_idx, pattern_idx, channel_idx, order_idx, panning_offset;
|
||||
boolean signed_samples, stereo_mode, default_panning;
|
||||
int[] channel_map, sequence;
|
||||
byte[] s3m_file;
|
||||
Module module;
|
||||
Instrument instrument;
|
||||
s3m_file = read_s3m_file( header_96_bytes, data_input );
|
||||
module = new Module();
|
||||
module.song_title = ascii_text( s3m_file, 0, 28 );
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
num_instruments = get_num_instruments( s3m_file );
|
||||
num_patterns = get_num_patterns( s3m_file );
|
||||
flags = unsigned_short_le( s3m_file, 38 );
|
||||
tracker_version = unsigned_short_le( s3m_file, 40 );
|
||||
if( ( flags & 0x40 ) == 0x40 || tracker_version == 0x1300 ) {
|
||||
module.fast_volume_slides = true;
|
||||
}
|
||||
signed_samples = false;
|
||||
if( unsigned_short_le( s3m_file, 42 ) == 0x01 ) {
|
||||
signed_samples = true;
|
||||
}
|
||||
module.global_volume = s3m_file[ 48 ] & 0xFF;
|
||||
module.default_speed = s3m_file[ 49 ] & 0xFF;
|
||||
module.default_tempo = s3m_file[ 50 ] & 0xFF;
|
||||
master_volume = s3m_file[ 51 ] & 0x7F;
|
||||
module.channel_gain = ( master_volume << IBXM.FP_SHIFT ) >> 7;
|
||||
stereo_mode = ( s3m_file[ 51 ] & 0x80 ) == 0x80;
|
||||
default_panning = ( s3m_file[ 53 ] & 0xFF ) == 0xFC;
|
||||
channel_map = new int[ 32 ];
|
||||
num_channels = 0;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
channel_config = s3m_file[ 64 + channel_idx ] & 0xFF;
|
||||
channel_map[ channel_idx ] = -1;
|
||||
if( channel_config < 16 ) {
|
||||
channel_map[ channel_idx ] = num_channels;
|
||||
num_channels += 1;
|
||||
}
|
||||
}
|
||||
module.set_num_channels( num_channels );
|
||||
panning_offset = 96 + num_pattern_orders + num_instruments * 2 + num_patterns * 2;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
if( channel_map[ channel_idx ] < 0 ) continue;
|
||||
panning = 7;
|
||||
if( stereo_mode ) {
|
||||
panning = 12;
|
||||
if( ( s3m_file[ 64 + channel_idx ] & 0xFF ) < 8 ) {
|
||||
panning = 3;
|
||||
}
|
||||
}
|
||||
if( default_panning ) {
|
||||
flags = s3m_file[ panning_offset + channel_idx ] & 0xFF;
|
||||
if( ( flags & 0x20 ) == 0x20 ) {
|
||||
panning = flags & 0xF;
|
||||
}
|
||||
}
|
||||
module.set_initial_panning( channel_map[ channel_idx ], panning * 17 );
|
||||
}
|
||||
sequence = read_s3m_sequence( s3m_file );
|
||||
module.set_sequence_length( sequence.length );
|
||||
for( order_idx = 0; order_idx < sequence.length; order_idx++ ) {
|
||||
module.set_sequence( order_idx, sequence[ order_idx ] );
|
||||
}
|
||||
module.set_num_instruments( num_instruments );
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument = read_s3m_instrument( s3m_file, instrument_idx, signed_samples );
|
||||
module.set_instrument( instrument_idx + 1, instrument );
|
||||
}
|
||||
module.set_num_patterns( num_patterns );
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
module.set_pattern( pattern_idx, read_s3m_pattern( s3m_file, pattern_idx, channel_map ) );
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static int[] read_s3m_sequence( byte[] s3m_file ) {
|
||||
int num_pattern_orders, sequence_length;
|
||||
int sequence_idx, order_idx, pattern_order;
|
||||
int[] sequence;
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
sequence_length = 0;
|
||||
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
|
||||
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
|
||||
if( pattern_order == 255 ) {
|
||||
break;
|
||||
} else if( pattern_order < 254 ) {
|
||||
sequence_length += 1;
|
||||
}
|
||||
}
|
||||
sequence = new int[ sequence_length ];
|
||||
sequence_idx = 0;
|
||||
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
|
||||
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
|
||||
if( pattern_order == 255 ) {
|
||||
break;
|
||||
} else if( pattern_order < 254 ) {
|
||||
sequence[ sequence_idx ] = pattern_order;
|
||||
sequence_idx += 1;
|
||||
}
|
||||
}
|
||||
return sequence;
|
||||
}
|
||||
|
||||
private static Instrument read_s3m_instrument( byte[] s3m_file, int instrument_idx, boolean signed_samples ) {
|
||||
int instrument_offset;
|
||||
int sample_data_offset, sample_data_length;
|
||||
int loop_start, loop_length, c2_rate, sample_idx, amplitude;
|
||||
boolean sixteen_bit;
|
||||
Instrument instrument;
|
||||
Sample sample;
|
||||
short[] sample_data;
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
instrument = new Instrument();
|
||||
instrument.name = ascii_text( s3m_file, instrument_offset + 48, 28 );
|
||||
sample = new Sample();
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_length = get_sample_data_length( s3m_file, instrument_offset );
|
||||
loop_start = unsigned_short_le( s3m_file, instrument_offset + 20 );
|
||||
loop_length = unsigned_short_le( s3m_file, instrument_offset + 24 ) - loop_start;
|
||||
sample.volume = s3m_file[ instrument_offset + 28 ] & 0xFF;
|
||||
if( s3m_file[ instrument_offset + 30 ] != 0 ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Packed samples not supported!" );
|
||||
}
|
||||
if( ( s3m_file[ instrument_offset + 31 ] & 0x01 ) == 0 ) {
|
||||
loop_length = 0;
|
||||
}
|
||||
if( ( s3m_file[ instrument_offset + 31 ] & 0x02 ) != 0 ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Stereo samples not supported!" );
|
||||
}
|
||||
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
|
||||
c2_rate = unsigned_short_le( s3m_file, instrument_offset + 32 );
|
||||
sample.transpose = LogTable.log_2( c2_rate ) - LogTable.log_2( 8363 );
|
||||
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
|
||||
if( sixteen_bit ) {
|
||||
if( signed_samples ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Signed 16-bit samples not supported!" );
|
||||
}
|
||||
sample_data_length >>= 1;
|
||||
sample_data = new short[ sample_data_length ];
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = s3m_file[ sample_data_offset + sample_idx * 2 ] & 0xFF;
|
||||
amplitude |= ( s3m_file[ sample_data_offset + sample_idx * 2 + 1 ] & 0xFF ) << 8;
|
||||
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
|
||||
}
|
||||
} else {
|
||||
sample_data = new short[ sample_data_length ];
|
||||
if( signed_samples ) {
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = s3m_file[ sample_data_offset + sample_idx ] << 8;
|
||||
sample_data[ sample_idx ] = ( short ) amplitude;
|
||||
}
|
||||
} else {
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = ( s3m_file[ sample_data_offset + sample_idx ] & 0xFF ) << 8;
|
||||
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
|
||||
}
|
||||
}
|
||||
}
|
||||
sample.set_sample_data( sample_data, loop_start, loop_length, false );
|
||||
}
|
||||
instrument.set_num_samples( 1 );
|
||||
instrument.set_sample( 0, sample );
|
||||
return instrument;
|
||||
}
|
||||
|
||||
private static Pattern read_s3m_pattern( byte[] s3m_file, int pattern_idx, int[] channel_map ) {
|
||||
int pattern_offset;
|
||||
int num_channels, num_notes;
|
||||
int row_idx, channel_idx, note_idx;
|
||||
int token, key, volume_column, effect, effect_param;
|
||||
byte[] pattern_data;
|
||||
Pattern pattern;
|
||||
num_channels = 0;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
if( channel_map[ channel_idx ] >= num_channels ) {
|
||||
num_channels = channel_idx + 1;
|
||||
}
|
||||
}
|
||||
num_notes = num_channels * 64;
|
||||
pattern_data = new byte[ num_notes * 5 ];
|
||||
row_idx = 0;
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx ) + 2;
|
||||
while( row_idx < 64 ) {
|
||||
token = s3m_file[ pattern_offset ] & 0xFF;
|
||||
pattern_offset += 1;
|
||||
if( token > 0 ) {
|
||||
channel_idx = channel_map[ token & 0x1F ];
|
||||
note_idx = ( num_channels * row_idx + channel_idx ) * 5;
|
||||
if( ( token & 0x20 ) == 0x20 ) {
|
||||
/* Key + Instrument.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
key = s3m_file[ pattern_offset ] & 0xFF;
|
||||
if( key == 255 ) {
|
||||
key = 0;
|
||||
} else if( key == 254 ) {
|
||||
key = 97;
|
||||
} else {
|
||||
key = ( ( key & 0xF0 ) >> 4 ) * 12 + ( key & 0x0F ) + 1;
|
||||
while( key > 96 ) {
|
||||
key = key - 12;
|
||||
}
|
||||
}
|
||||
pattern_data[ note_idx ] = ( byte ) key;
|
||||
pattern_data[ note_idx + 1 ] = s3m_file[ pattern_offset + 1 ];
|
||||
}
|
||||
pattern_offset += 2;
|
||||
}
|
||||
if( ( token & 0x40 ) == 0x40 ) {
|
||||
/* Volume.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
volume_column = ( s3m_file[ pattern_offset ] & 0xFF ) + 0x10;
|
||||
pattern_data[ note_idx + 2 ] = ( byte ) volume_column;
|
||||
}
|
||||
pattern_offset += 1;
|
||||
}
|
||||
if( ( token & 0x80 ) == 0x80 ) {
|
||||
/* Effect + Param.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
effect = s3m_file[ pattern_offset ] & 0xFF;
|
||||
effect_param = s3m_file[ pattern_offset + 1 ] & 0xFF;
|
||||
effect = effect_map[ effect & 0x1F ];
|
||||
if( effect == 0xFF ) {
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
}
|
||||
if( effect == 0x0E ) {
|
||||
effect = effect_s_map[ ( effect_param & 0xF0 ) >> 4 ];
|
||||
effect_param = effect_param & 0x0F;
|
||||
switch( effect ) {
|
||||
case 0x08:
|
||||
effect = 0x08;
|
||||
effect_param = effect_param * 17;
|
||||
break;
|
||||
case 0x09:
|
||||
effect = 0x08;
|
||||
if( effect_param > 7 ) {
|
||||
effect_param -= 8;
|
||||
} else {
|
||||
effect_param += 8;
|
||||
}
|
||||
effect_param = effect_param * 17;
|
||||
break;
|
||||
case 0xFF:
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
break;
|
||||
default:
|
||||
effect_param = ( ( effect & 0x0F ) << 4 ) | ( effect_param & 0x0F );
|
||||
effect = 0x0E;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pattern_data[ note_idx + 3 ] = ( byte ) effect;
|
||||
pattern_data[ note_idx + 4 ] = ( byte ) effect_param;
|
||||
}
|
||||
pattern_offset += 2;
|
||||
}
|
||||
} else {
|
||||
row_idx += 1;
|
||||
}
|
||||
}
|
||||
pattern = new Pattern();
|
||||
pattern.num_rows = 64;
|
||||
pattern.set_pattern_data( pattern_data );
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static byte[] read_s3m_file( byte[] header_96_bytes, DataInput data_input ) throws IOException {
|
||||
int s3m_file_length;
|
||||
int num_pattern_orders, num_instruments, num_patterns;
|
||||
int instrument_idx, pattern_idx;
|
||||
int instrument_offset, sample_data_offset, pattern_offset;
|
||||
byte[] s3m_file;
|
||||
if( !is_s3m( header_96_bytes ) ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Not an S3M file!" );
|
||||
}
|
||||
s3m_file = header_96_bytes;
|
||||
s3m_file_length = header_96_bytes.length;
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
num_instruments = get_num_instruments( s3m_file );
|
||||
num_patterns = get_num_patterns( s3m_file );
|
||||
s3m_file_length += num_pattern_orders;
|
||||
s3m_file_length += num_instruments * 2;
|
||||
s3m_file_length += num_patterns * 2;
|
||||
/* Read enough of file to calculate the length.*/
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
instrument_offset += 80;
|
||||
if( instrument_offset > s3m_file_length ) {
|
||||
s3m_file_length = instrument_offset;
|
||||
}
|
||||
}
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
|
||||
pattern_offset += 2;
|
||||
if( pattern_offset > s3m_file_length ) {
|
||||
s3m_file_length = pattern_offset;
|
||||
}
|
||||
}
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
/* Read rest of file.*/
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
|
||||
sample_data_offset += get_sample_data_length( s3m_file, instrument_offset );
|
||||
if( sample_data_offset > s3m_file_length ) {
|
||||
s3m_file_length = sample_data_offset;
|
||||
}
|
||||
}
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
|
||||
pattern_offset += get_pattern_length( s3m_file, pattern_offset );
|
||||
pattern_offset += 2;
|
||||
if( pattern_offset > s3m_file_length ) {
|
||||
s3m_file_length = pattern_offset;
|
||||
}
|
||||
}
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
return s3m_file;
|
||||
}
|
||||
|
||||
private static int get_num_pattern_orders( byte[] s3m_file ) {
|
||||
int num_pattern_orders;
|
||||
num_pattern_orders = unsigned_short_le( s3m_file, 32 );
|
||||
return num_pattern_orders;
|
||||
}
|
||||
|
||||
private static int get_num_instruments( byte[] s3m_file ) {
|
||||
int num_instruments;
|
||||
num_instruments = unsigned_short_le( s3m_file, 34 );
|
||||
return num_instruments;
|
||||
}
|
||||
|
||||
private static int get_num_patterns( byte[] s3m_file ) {
|
||||
int num_patterns;
|
||||
num_patterns = unsigned_short_le( s3m_file, 36 );
|
||||
return num_patterns;
|
||||
}
|
||||
|
||||
private static int get_instrument_offset( byte[] s3m_file, int instrument_idx ) {
|
||||
int instrument_offset, pointer_offset;
|
||||
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
|
||||
instrument_offset = unsigned_short_le( s3m_file, pointer_offset + instrument_idx * 2 ) << 4;
|
||||
return instrument_offset;
|
||||
}
|
||||
|
||||
private static int get_sample_data_offset( byte[] s3m_file, int instrument_offset ) {
|
||||
int sample_data_offset;
|
||||
sample_data_offset = 0;
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_offset = ( s3m_file[ instrument_offset + 13 ] & 0xFF ) << 20;
|
||||
sample_data_offset |= unsigned_short_le( s3m_file, instrument_offset + 14 ) << 4;
|
||||
}
|
||||
return sample_data_offset;
|
||||
}
|
||||
|
||||
private static int get_sample_data_length( byte[] s3m_file, int instrument_offset ) {
|
||||
int sample_data_length;
|
||||
boolean sixteen_bit;
|
||||
sample_data_length = 0;
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_length = unsigned_short_le( s3m_file, instrument_offset + 16 );
|
||||
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
|
||||
if( sixteen_bit ) {
|
||||
sample_data_length <<= 1;
|
||||
}
|
||||
}
|
||||
return sample_data_length;
|
||||
}
|
||||
|
||||
private static int get_pattern_offset( byte[] s3m_file, int pattern_idx ) {
|
||||
int pattern_offset, pointer_offset;
|
||||
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
|
||||
pointer_offset += get_num_instruments( s3m_file ) * 2;
|
||||
pattern_offset = unsigned_short_le( s3m_file, pointer_offset + pattern_idx * 2 ) << 4;
|
||||
return pattern_offset;
|
||||
}
|
||||
|
||||
private static int get_pattern_length( byte[] s3m_file, int pattern_offset ) {
|
||||
int pattern_length;
|
||||
pattern_length = unsigned_short_le( s3m_file, pattern_offset );
|
||||
return pattern_length;
|
||||
}
|
||||
|
||||
private static byte[] read_more( byte[] old_data, int new_length, DataInput data_input ) throws IOException {
|
||||
byte[] new_data;
|
||||
new_data = old_data;
|
||||
if( new_length > old_data.length ) {
|
||||
new_data = new byte[ new_length ];
|
||||
System.arraycopy( old_data, 0, new_data, 0, old_data.length );
|
||||
try {
|
||||
data_input.readFully( new_data, old_data.length, new_data.length - old_data.length );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "ScreamTracker3: Module has been truncated!" );
|
||||
}
|
||||
}
|
||||
return new_data;
|
||||
}
|
||||
|
||||
private static int unsigned_short_le( byte[] buffer, int offset ) {
|
||||
int value;
|
||||
value = buffer[ offset ] & 0xFF;
|
||||
value = value | ( ( buffer[ offset + 1 ] & 0xFF ) << 8 );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String ascii_text( byte[] buffer, int offset, int length ) {
|
||||
int idx, chr;
|
||||
byte[] string_buffer;
|
||||
String string;
|
||||
string_buffer = new byte[ length ];
|
||||
for( idx = 0; idx < length; idx++ ) {
|
||||
chr = buffer[ offset + idx ];
|
||||
if( chr < 32 ) {
|
||||
chr = 32;
|
||||
}
|
||||
string_buffer[ idx ] = ( byte ) chr;
|
||||
}
|
||||
try {
|
||||
string = new String( string_buffer, 0, length, "ISO-8859-1" );
|
||||
} catch( UnsupportedEncodingException e ) {
|
||||
string = "";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
102
src/com/ibxm/util/WavHeader.java
Normal file
102
src/com/ibxm/util/WavHeader.java
Normal file
@ -0,0 +1,102 @@
|
||||
package com.ibxm.util;
|
||||
|
||||
/*
|
||||
WAV Header format. All values little endian.
|
||||
|
||||
CHAR[4] "RIFF"
|
||||
UINT32 Size of following data. Sample data length+36. Must be even.
|
||||
CHAR[4] "WAVE"
|
||||
CHAR[4] "fmt "
|
||||
UINT32 PCM Header chunk size = 16
|
||||
UINT16 0x0001 (PCM)
|
||||
UINT16 NumChannels
|
||||
UINT32 SampleRate
|
||||
UINT32 BytesPerSec = samplerate*frame size
|
||||
UINT16 frame Size (eg 4 bytes for stereo PCM16)
|
||||
UINT16 BitsPerSample
|
||||
CHAR[4] "data"
|
||||
UINT32 Length of sample data.
|
||||
<Samples>
|
||||
|
||||
Example from real, valid wave file. 16 bit mono 8khz.
|
||||
|
||||
524946466061000057415645666D74201000000001000100401F0000
|
||||
R I F F ^(dsiz) W A V E f m t ^(PCMHS)^(P)^(C)^(Rate)
|
||||
803E000002001000646174613C610000<Samples>
|
||||
^(Byt/s)^(F)^(b)d a t a ^(slen)
|
||||
|
||||
Total 44 bytes.
|
||||
|
||||
Audio data that follows is:
|
||||
Unsigned if 8 bit.
|
||||
Signed little-endian if 16 bit or above.
|
||||
L-R interleaved if stereo.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple tool for computing and generating
|
||||
* basic, single chunk RIFF-WAV headers.
|
||||
*
|
||||
* @author Martin Cameron
|
||||
*/
|
||||
public class WavHeader {
|
||||
|
||||
/** Header size in bytes. */
|
||||
public static final int SIZE = 44;
|
||||
|
||||
/**
|
||||
* <p>Write a WAV header for the specified format.</p>
|
||||
* <p>For a valid header you also need to specify the number of samples
|
||||
* that will immediately follow the header. This must result in an even number of bytes.</p>
|
||||
*
|
||||
* @param outBuf the buffer to write header into, from index 0
|
||||
* @param channels the number of channels in audio stream.
|
||||
* @param rate the sampling rate.
|
||||
* @param bytes the number of bytes per sample (i.e. 2 for 16bit)
|
||||
* @param numSamples the number of samples of audio data to follow.
|
||||
* @return the number of bytes taken up by the header.
|
||||
*/
|
||||
public static int writeHeader(byte[] outBuf, int channels, int rate, int bytes, int numSamples) {
|
||||
outBuf[0] = (byte) 0x52; //R
|
||||
outBuf[1] = (byte) 0x49; //I
|
||||
outBuf[2] = (byte) 0x46; //F
|
||||
outBuf[3] = (byte) 0x46; //F
|
||||
writeINT32LE(outBuf, 4, channels * bytes * numSamples + 36);
|
||||
outBuf[8] = (byte) 0x57; //W
|
||||
outBuf[9] = (byte) 0x41; //A
|
||||
outBuf[10] = (byte) 0x56; //V
|
||||
outBuf[11] = (byte) 0x45; //E
|
||||
outBuf[12] = (byte) 0x66; //f
|
||||
outBuf[13] = (byte) 0x6D; //m
|
||||
outBuf[14] = (byte) 0x74; //t
|
||||
outBuf[15] = (byte) 0x20; //Space
|
||||
writeINT32LE(outBuf, 16, 16);
|
||||
writeINT16LE(outBuf, 20, 1);
|
||||
writeINT16LE(outBuf, 22, channels);
|
||||
writeINT32LE(outBuf, 24, rate);
|
||||
writeINT32LE(outBuf, 28, channels * bytes * rate);
|
||||
writeINT16LE(outBuf, 32, channels * bytes);
|
||||
writeINT16LE(outBuf, 34, bytes * 8);
|
||||
outBuf[36] = (byte) 0x64; //d
|
||||
outBuf[37] = (byte) 0x61; //a
|
||||
outBuf[38] = (byte) 0x74; //t
|
||||
outBuf[39] = (byte) 0x61; //a
|
||||
writeINT32LE(outBuf, 40, channels * bytes * numSamples);
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
/** Write a little-endian short into the specified array. */
|
||||
public static void writeINT16LE(byte[] outBuf, int offset, int value) {
|
||||
outBuf[offset] = (byte) (value & 0xFF);
|
||||
outBuf[offset + 1] = (byte) (value >> 8);
|
||||
}
|
||||
|
||||
/** Write a little-endian int into the specified array. */
|
||||
public static void writeINT32LE(byte[] outBuf, int offset, int value) {
|
||||
outBuf[offset] = (byte) (value & 0xFF);
|
||||
outBuf[offset + 1] = (byte) ((value >> 8) & 0xFF);
|
||||
outBuf[offset + 2] = (byte) ((value >> 16) & 0xFF);
|
||||
outBuf[offset + 3] = (byte) (value >> 24);
|
||||
}
|
||||
}
|
||||
|
10
src/com/one/ArraySearchListener.java
Normal file
10
src/com/one/ArraySearchListener.java
Normal file
@ -0,0 +1,10 @@
|
||||
package com.one;
|
||||
|
||||
/**
|
||||
* Èíòåðôåéñ, ïðèíèìàþùèé ñîîáùåíèÿ îò ArraySearchThread
|
||||
* î íàéäåííûõ ýëåìåíòàõ
|
||||
*/
|
||||
public interface ArraySearchListener
|
||||
{
|
||||
public void elementFound(Object[] array, Object element, int index);
|
||||
}
|
59
src/com/one/ArraySearchThread.java
Normal file
59
src/com/one/ArraySearchThread.java
Normal file
@ -0,0 +1,59 @@
|
||||
package com.one;
|
||||
|
||||
/**
|
||||
* Ïîòîê äëÿ ïîèñêà ýëåìåíòîâ â ìàññèâå
|
||||
*/
|
||||
public class ArraySearchThread implements Runnable
|
||||
{
|
||||
protected Object[] array;
|
||||
protected Object element;
|
||||
protected ArraySearchListener listener;
|
||||
protected boolean running;
|
||||
protected Thread t;
|
||||
|
||||
public ArraySearchThread(Object[] array, Object element, ArraySearchListener listener)
|
||||
{
|
||||
this.array = array;
|
||||
this.element = element;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
running = true;
|
||||
t = new Thread(this);
|
||||
t.start();
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
|
||||
public boolean isAlive()
|
||||
{
|
||||
return running;
|
||||
}
|
||||
|
||||
public void join() throws InterruptedException
|
||||
{
|
||||
if(running && t != null)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
for(int i = 0; (i < array.length) && running; i++)
|
||||
{
|
||||
if(array[i].equals(element))
|
||||
{
|
||||
listener.elementFound(array, element, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
running = false;
|
||||
}
|
||||
}
|
10
src/com/one/AutoAdvanceScreen.java
Normal file
10
src/com/one/AutoAdvanceScreen.java
Normal file
@ -0,0 +1,10 @@
|
||||
package com.one;
|
||||
|
||||
import javax.microedition.lcdui.CommandListener;
|
||||
|
||||
public interface AutoAdvanceScreen extends Runnable
|
||||
{
|
||||
public boolean finiteTimeout();
|
||||
public void setCommandListener(CommandListener cl);
|
||||
public void run();
|
||||
}
|
175
src/com/one/BufferedInputStream.java
Normal file
175
src/com/one/BufferedInputStream.java
Normal file
@ -0,0 +1,175 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import filemanager.main;
|
||||
|
||||
/**
|
||||
* Âõîäíîé ïîòîê ñ ïîëíîé áóôåðèçàöèåé.
|
||||
* Ïðèçâàí ðàáîòàòü òàì, ãäå òðåáóåòñÿ ïîääåðæêà mark / reset, íî åå ïî÷åìó - òî íåò.
|
||||
*/
|
||||
public class BufferedInputStream extends RandomAccessInputStream
|
||||
{
|
||||
protected InputStream is = null;
|
||||
protected byte[] buf = null;
|
||||
protected int currpos; // ïîçèöèÿ ïåðâîãî íåñ÷èòàííîãî áàéòà
|
||||
protected int readpos; // ïîçèöèÿ ïåðâîãî íåèíèöèàëèçèðîâàííîãî áàéòà
|
||||
protected int markedpos; // ìàðêèðîâàííàÿ ïîçèöèÿ
|
||||
|
||||
public BufferedInputStream(InputStream is) throws IOException
|
||||
{
|
||||
System.gc();
|
||||
|
||||
this.is = is;
|
||||
|
||||
buf = new byte[is.available()];
|
||||
|
||||
currpos = 0;
|
||||
readpos = 0;
|
||||
markedpos = 0;
|
||||
}
|
||||
|
||||
public BufferedInputStream(byte[] data)
|
||||
{
|
||||
this.is = null;
|
||||
|
||||
buf = data;
|
||||
|
||||
currpos = 0;
|
||||
readpos = data.length;
|
||||
markedpos = 0;
|
||||
}
|
||||
|
||||
protected BufferedInputStream()
|
||||
{
|
||||
}
|
||||
|
||||
// public byte[] getBuffer()
|
||||
// {
|
||||
// bufferize(buf.length - 1);
|
||||
// return buf;
|
||||
// }
|
||||
|
||||
public int available()
|
||||
{
|
||||
return buf.length - currpos;
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
if(is != null)
|
||||
{
|
||||
is.close();
|
||||
is = null;
|
||||
}
|
||||
|
||||
buf = null;
|
||||
|
||||
System.gc();
|
||||
}
|
||||
|
||||
public void mark(int readLimit)
|
||||
{
|
||||
markedpos = currpos;
|
||||
}
|
||||
|
||||
public int read() throws IOException
|
||||
{
|
||||
if(currpos >= buf.length) // êîíåö ïîòîêà
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
bufferize(currpos);
|
||||
|
||||
return ((int)buf[currpos++]) & 0xFF;
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException
|
||||
{
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if(len > available()) // íåëüçÿ ñ÷èòàòü áîëüøå, ÷åì åùå îñòàëîñü
|
||||
{
|
||||
len = available();
|
||||
}
|
||||
|
||||
bufferize(currpos + len - 1);
|
||||
|
||||
System.arraycopy(buf, currpos, b, off, len);
|
||||
|
||||
currpos += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
currpos = markedpos;
|
||||
}
|
||||
|
||||
public long skip(long n)
|
||||
{
|
||||
if(n > available())
|
||||
{
|
||||
n = available();
|
||||
}
|
||||
|
||||
currpos += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void seek(int pos)
|
||||
{
|
||||
if(pos == currpos)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(pos < 0)
|
||||
{
|
||||
currpos = 0;
|
||||
}
|
||||
else if(pos > getCapacity())
|
||||
{
|
||||
currpos = getCapacity();
|
||||
}
|
||||
else
|
||||
{
|
||||
currpos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
public int tell()
|
||||
{
|
||||
return currpos;
|
||||
}
|
||||
|
||||
public int getCapacity()
|
||||
{
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàïîëíèòü áóôåð, ÷òîáû èç íåãî ìîæíî áûëî ïîëó÷èòü áàéò ÍÀ ÏÎÇÈÖÈÈ target
|
||||
* Îáÿçàííîñòü âûçûâàþùåãî ñëåäèòü ÷òîáû âûïîëíÿëîñü target < buf.length
|
||||
*/
|
||||
protected void bufferize(int target) throws IOException
|
||||
{
|
||||
if(is != null)
|
||||
{
|
||||
while(readpos <= target)
|
||||
{
|
||||
buf[readpos++] = (byte)is.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
112
src/com/one/CryptThread.java
Normal file
112
src/com/one/CryptThread.java
Normal file
@ -0,0 +1,112 @@
|
||||
package com.one;
|
||||
|
||||
import com.vmx.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.microedition.io.Connector;
|
||||
import javax.microedition.io.file.FileConnection;
|
||||
import filemanager.filesystem;
|
||||
|
||||
/**
|
||||
* Ïîòîê øèôðîâàíèÿ ôàéëîâ
|
||||
*/
|
||||
public class CryptThread implements Runnable
|
||||
{
|
||||
protected RTEF rtef;
|
||||
protected Vector files;
|
||||
protected ProgressCallback callback;
|
||||
public String error;
|
||||
public boolean interrupt;
|
||||
|
||||
public CryptThread(String password, Vector filelist, ProgressCallback cb)
|
||||
{
|
||||
rtef = new RTEF(password);
|
||||
files = filelist;
|
||||
callback = cb;
|
||||
error = null;
|
||||
interrupt = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ôóíêöèÿ, îñóùåñòâëÿþùàÿ ñîáñòâåííî øèôðîâàíèå
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
FileConnection fc = null;
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
boolean usecb = callback != null;
|
||||
|
||||
try
|
||||
{
|
||||
if(usecb)
|
||||
{
|
||||
int max = 0;
|
||||
|
||||
for(int i = 0; i < files.size(); i++)
|
||||
{
|
||||
max += filesystem.getSize((String)files.elementAt(i));
|
||||
}
|
||||
|
||||
callback.setMax(max);
|
||||
callback.setProgress(0);
|
||||
}
|
||||
|
||||
int cp;
|
||||
String fname;
|
||||
|
||||
for(int i = 0; i < files.size() && !interrupt; i++)
|
||||
{
|
||||
fname = (String)files.elementAt(i);
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.setText(fname.substring(fname.lastIndexOf('/') + 1));
|
||||
}
|
||||
|
||||
fc = (FileConnection)Connector.open("file:///" + fname);
|
||||
is = fc.openInputStream();
|
||||
os = fc.openOutputStream();
|
||||
|
||||
cp = is.available();
|
||||
|
||||
rtef.crypt(is, os, callback);
|
||||
|
||||
os.flush();
|
||||
os.close();
|
||||
is.close();
|
||||
fc.close();
|
||||
|
||||
os = null;
|
||||
is = null;
|
||||
fc = null;
|
||||
System.gc();
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.progress(cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception x)
|
||||
{
|
||||
error = x.toString(); // x.getClass().getName() + ": " + x.getMessage();
|
||||
|
||||
if(fc != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fc.close();
|
||||
}
|
||||
catch(Exception xx)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[CryptThread] " + s);
|
||||
// }
|
||||
}
|
146
src/com/one/DisplayManager.java
Normal file
146
src/com/one/DisplayManager.java
Normal file
@ -0,0 +1,146 @@
|
||||
package com.one;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
|
||||
public class DisplayManager implements CommandListener
|
||||
{
|
||||
protected Display dsp;
|
||||
protected Renderer rn;
|
||||
protected boolean renderMode;
|
||||
protected Object next;
|
||||
|
||||
public DisplayManager(Display d)
|
||||
{
|
||||
dsp = d;
|
||||
rn = new Renderer();
|
||||
renderMode = false;
|
||||
}
|
||||
|
||||
public void commandAction(Command c, Displayable d)
|
||||
{
|
||||
if(next != null)
|
||||
{
|
||||
setCurrent(next);
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Renderer getRenderer()
|
||||
{
|
||||
return rn;
|
||||
}
|
||||
|
||||
public void setCurrent(Object obj)
|
||||
{
|
||||
if(obj instanceof PaintableObject)
|
||||
{
|
||||
rn.setCurrent((PaintableObject)obj);
|
||||
|
||||
if(!renderMode)
|
||||
{
|
||||
dsp.setCurrent(rn);
|
||||
renderMode = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dsp.setCurrent((Displayable)obj);
|
||||
renderMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrent(AutoAdvanceScreen aas, Object obj)
|
||||
{
|
||||
setCurrent(aas);
|
||||
|
||||
if(aas.finiteTimeout())
|
||||
{
|
||||
next = obj;
|
||||
|
||||
aas.setCommandListener(this);
|
||||
(new Thread(aas)).start();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrent(Alert al, Object obj)
|
||||
{
|
||||
if(obj instanceof PaintableObject)
|
||||
{
|
||||
rn.setCurrent((PaintableObject)obj);
|
||||
dsp.setCurrent(al, rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
dsp.setCurrent(al, (Displayable)obj);
|
||||
}
|
||||
|
||||
renderMode = false;
|
||||
}
|
||||
|
||||
public Object getCurrent()
|
||||
{
|
||||
if(renderMode)
|
||||
{
|
||||
return rn.getCurrent();
|
||||
}
|
||||
else
|
||||
{
|
||||
return dsp.getCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
public void callSerially(Runnable r)
|
||||
{
|
||||
dsp.callSerially(r);
|
||||
}
|
||||
|
||||
public boolean flashBacklight(int duration)
|
||||
{
|
||||
return dsp.flashBacklight(duration);
|
||||
}
|
||||
|
||||
public int getBestImageHeight(int imageType)
|
||||
{
|
||||
return dsp.getBestImageHeight(imageType);
|
||||
}
|
||||
|
||||
public int getBestImageWidth(int imageType)
|
||||
{
|
||||
return dsp.getBestImageWidth(imageType);
|
||||
}
|
||||
|
||||
public int getBorderStyle(boolean highlighted)
|
||||
{
|
||||
return dsp.getBorderStyle(highlighted);
|
||||
}
|
||||
|
||||
public int getColor(int colorSpecifier)
|
||||
{
|
||||
return dsp.getColor(colorSpecifier);
|
||||
}
|
||||
|
||||
public boolean isColor()
|
||||
{
|
||||
return dsp.isColor();
|
||||
}
|
||||
|
||||
public int numAlphaLevels()
|
||||
{
|
||||
return dsp.numAlphaLevels();
|
||||
}
|
||||
|
||||
public int numColors()
|
||||
{
|
||||
return dsp.numColors();
|
||||
}
|
||||
|
||||
public void setCurrentItem(Item item)
|
||||
{
|
||||
dsp.setCurrentItem(item);
|
||||
}
|
||||
|
||||
public boolean vibrate(int duration)
|
||||
{
|
||||
return dsp.vibrate(duration);
|
||||
}
|
||||
}
|
191
src/com/one/FileInputStream.java
Normal file
191
src/com/one/FileInputStream.java
Normal file
@ -0,0 +1,191 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
import javax.microedition.io.file.*;
|
||||
|
||||
/**
|
||||
* Ôàéëîâûé âõîäíîé ïîòîê.
|
||||
* Ïðèçâàí ðàáîòàòü òàì, ãäå òðåáóåòñÿ ïîääåðæêà mark / reset, íî åå ïî÷åìó - òî íåò.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ÇÀÌÅ×ÀÍÈÅ
|
||||
*
|
||||
* Â íåêîòîðûõ ìåñòàõ íàïèñàíî ÷òî-òî âðîäå
|
||||
*
|
||||
* is.skip(n);
|
||||
* currpos += n;
|
||||
*
|
||||
* õîòÿ, âîçìîæíî, áûëî áû ïðàâèëüíåå íàïèñàòü
|
||||
*
|
||||
* n = is.skip(n);
|
||||
* currpos += n;
|
||||
*
|
||||
* Îäíàêî âûÿñíèëîñü, ÷òî skip() íå âñåãäà âîçâðàùàåò
|
||||
* èìåííî êîëè÷åñòâî ïðîïóùåííûõ áàéò.
|
||||
* Âèäèìî, ïîâåäåíèå ýòîãî ìåòîäà, à âîçìîæíî, è äðóãèõ,
|
||||
* çàâèñèò îò ïëàòôîðìû (íàïðèìåð, íà òåëåôîíàõ SonyEricsson
|
||||
* ýòîò ìåòîä âîçâðàùàåò òåêóùóþ ïîçèöèþ â ïîòîêå).
|
||||
* Äëÿ èñêëþ÷åíèÿ ïîäîáíûõ îøèáîê â äàííîì êëàññå
|
||||
* çíà÷åíèÿ, âîçâðàùàåìûå ýòèìè ìåòîäàìè, íå èñïîëüçóþòñÿ.
|
||||
* Âìåñòî ýòîãî îñóùåñòâëÿåòñÿ ÿâíàÿ ïðîâåðêà
|
||||
* êîëè÷åñòâà äîñòóïíûõ äëÿ ñ÷èòûâàíèÿ áàéò.
|
||||
*/
|
||||
public class FileInputStream extends RandomAccessInputStream
|
||||
{
|
||||
protected FileConnection fc;
|
||||
protected InputStream is;
|
||||
protected int currpos; // ïîçèöèÿ ïåðâîãî íåñ÷èòàííîãî áàéòà
|
||||
protected int markedpos; // ìàðêèðîâàííàÿ ïîçèöèÿ
|
||||
protected boolean marksupp; // ïîääåðæêà mark / reset áàçîâûì ïîòîêîì
|
||||
|
||||
public FileInputStream(FileConnection fc) throws IOException
|
||||
{
|
||||
this.fc = fc;
|
||||
is = fc.openInputStream();
|
||||
|
||||
if(marksupp = is.markSupported())
|
||||
{
|
||||
is.mark(is.available() + 0x100);
|
||||
}
|
||||
|
||||
currpos = 0;
|
||||
markedpos = 0;
|
||||
}
|
||||
|
||||
public int available() throws IOException
|
||||
{
|
||||
return is.available();
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
is.close();
|
||||
is = null;
|
||||
fc = null;
|
||||
}
|
||||
|
||||
public void mark(int readLimit)
|
||||
{
|
||||
markedpos = currpos;
|
||||
}
|
||||
|
||||
public int read() throws IOException
|
||||
{
|
||||
if(is.available() <= 0) // êîíåö ïîòîêà
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
currpos++;
|
||||
|
||||
return is.read();
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException
|
||||
{
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if(len > is.available()) // íåëüçÿ ñ÷èòàòü áîëüøå, ÷åì åùå îñòàëîñü
|
||||
{
|
||||
len = is.available();
|
||||
}
|
||||
|
||||
is.read(b, off, len);
|
||||
|
||||
currpos += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
public void reset() throws IOException
|
||||
{
|
||||
seek(markedpos);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException
|
||||
{
|
||||
if(n > is.available())
|
||||
{
|
||||
n = is.available();
|
||||
}
|
||||
|
||||
is.skip(n);
|
||||
|
||||
currpos += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void seek(int pos) throws IOException
|
||||
{
|
||||
if(pos == currpos)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(pos < 0)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
else if(pos > getCapacity())
|
||||
{
|
||||
pos = getCapacity();
|
||||
}
|
||||
|
||||
if(pos < currpos)
|
||||
{
|
||||
if(marksupp)
|
||||
{
|
||||
is.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
is.close();
|
||||
is = fc.openInputStream();
|
||||
}
|
||||
|
||||
is.skip(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
is.skip(pos - currpos);
|
||||
}
|
||||
|
||||
currpos = pos;
|
||||
}
|
||||
|
||||
public int tell()
|
||||
{
|
||||
return currpos;
|
||||
}
|
||||
|
||||
public int getCapacity() throws IOException
|
||||
{
|
||||
return is.available() + currpos;
|
||||
}
|
||||
|
||||
public void update() throws IOException
|
||||
{
|
||||
int pos = currpos;
|
||||
currpos = 0;
|
||||
|
||||
is.close();
|
||||
is = fc.openInputStream();
|
||||
|
||||
if(marksupp = is.markSupported())
|
||||
{
|
||||
is.mark(is.available() + 0x100);
|
||||
}
|
||||
|
||||
seek(pos);
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[FileInputStream] " + s);
|
||||
// }
|
||||
}
|
228
src/com/one/ImageProcessor.java
Normal file
228
src/com/one/ImageProcessor.java
Normal file
@ -0,0 +1,228 @@
|
||||
package com.one;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
import java.io.*;
|
||||
|
||||
import filemanager.main;
|
||||
|
||||
/**
|
||||
* Êëàññ äëÿ îáðàáîòêè èçîáðàæåíèé
|
||||
*/
|
||||
public class ImageProcessor
|
||||
{
|
||||
/** ïîëîæåíèå ôèêñèðîâàííîé òî÷êè */
|
||||
private static final int SHIFT = 16;
|
||||
|
||||
/**
|
||||
* Ôóíêöèÿ ìàñøòàáèðîâàíèÿ èçîáðàæåíèé
|
||||
*/
|
||||
public static Image scaleImage(Image source, int destw, int desth)
|
||||
{
|
||||
if(source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int srcw, srch;
|
||||
int[] srcPixels;
|
||||
int[] destPixels;
|
||||
int wscan, hscan, scancount;
|
||||
int x, y, sx, sy, cx, cy;
|
||||
int r, g, b;
|
||||
int idx;
|
||||
int rx, ry;
|
||||
|
||||
try
|
||||
{
|
||||
/* ðàçìåðû èñõîäíîãî èçîáðàæåíèÿ */
|
||||
srcw = source.getWidth();
|
||||
srch = source.getHeight();
|
||||
|
||||
/* êîýôôèöèåíò ìàñøòàáèðîâàíèÿ */
|
||||
rx = (srcw << SHIFT) / destw;
|
||||
ry = (srch << SHIFT) / desth;
|
||||
|
||||
System.gc();
|
||||
|
||||
srcPixels = new int[srcw * srch];
|
||||
source.getRGB(srcPixels, 0, srcw, 0, 0, srcw, srch);
|
||||
|
||||
source = null;
|
||||
System.gc();
|
||||
|
||||
destPixels = new int[destw * desth];
|
||||
|
||||
/* ñêîëüêî òî÷åê ó÷àñòâóþò â óñðåäíåíèè */
|
||||
wscan = rx >>> SHIFT;
|
||||
hscan = ry >>> SHIFT;
|
||||
|
||||
if(wscan == 0)
|
||||
{
|
||||
wscan = 1;
|
||||
}
|
||||
|
||||
if(hscan == 0)
|
||||
{
|
||||
hscan = 1;
|
||||
}
|
||||
|
||||
scancount = wscan * hscan;
|
||||
|
||||
for(x = 0; x < destw; x++)
|
||||
{
|
||||
for(y = 0; y < desth; y++)
|
||||
{
|
||||
/* íà÷àëüíûå êîîðäèíàòû äëÿ óñðåäíåíèÿ */
|
||||
sx = (x * rx) >>> SHIFT ;
|
||||
sy = (y * ry) >>> SHIFT;
|
||||
|
||||
r = g = b = 0;
|
||||
|
||||
for(cx = 0; cx < wscan; cx++)
|
||||
{
|
||||
for(cy = 0; cy < hscan; cy++)
|
||||
{
|
||||
idx = sx + cx + (sy + cy) * srcw;
|
||||
|
||||
r += (srcPixels[idx] >> 16) & 0xFF;
|
||||
g += (srcPixels[idx] >> 8) & 0xFF;
|
||||
b += (srcPixels[idx]) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
r /= scancount;
|
||||
g /= scancount;
|
||||
b /= scancount;
|
||||
|
||||
destPixels[x + destw * y] = (r << 16) | (g << 8) | (b);
|
||||
}
|
||||
}
|
||||
|
||||
srcPixels = null;
|
||||
System.gc();
|
||||
|
||||
source = Image.createRGBImage(destPixels, destw, desth, false);
|
||||
|
||||
destPixels = null;
|
||||
System.gc();
|
||||
}
|
||||
catch(OutOfMemoryError ome)
|
||||
{
|
||||
// óâû, íè÷åãî íå ïîäåëàåøü,
|
||||
// âîçâðàùàåì îðèãèíàëüíóþ êàðòèíêó
|
||||
}
|
||||
catch(Throwable e)
|
||||
{
|
||||
// à âîò ýòî íå ïðåäóñìîòðåíî,
|
||||
// íàäî ïîêàçàòü ñîîáùåíèå ñ îøèáêîé
|
||||
main.showErrMsg(38, e);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Êà÷åñòâåííî óâåëè÷èòü êàðòèíêó.
|
||||
* Ñíà÷àëà óâåëè÷èâàåì â öåëîå ÷èñëî ðàç,
|
||||
* çàòåì óìåíüøàåì äî íåîáõîäèìûõ ðàçìåðîâ.
|
||||
* Òàêèì îáðàçîì îñóùåñòâëÿåòñÿ ñãëàæèâàíèå.
|
||||
*/
|
||||
public static Image scaleImageHQ(Image source, int destw, int desth)
|
||||
{
|
||||
int origw = source.getWidth();
|
||||
int origh = source.getHeight();
|
||||
|
||||
if(destw <= origw && desth <= origh)
|
||||
{
|
||||
return scaleImage(source, destw, desth);
|
||||
}
|
||||
else
|
||||
{
|
||||
int tempw = origw;
|
||||
int temph = origh;
|
||||
|
||||
while(tempw < destw || temph < desth)
|
||||
{
|
||||
tempw += origw;
|
||||
temph += origh;
|
||||
}
|
||||
|
||||
return scaleImage(scaleImage(source, tempw, temph), destw, desth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðåîáîðàçîâàíèå èçîáðàæåíèÿ source â ïðîçðà÷íîå
|
||||
* ñ çàäàííîé ïðîçðà÷íîñòüþ
|
||||
*/
|
||||
public static Image transImage(Image source, int newtrans)
|
||||
{
|
||||
if(source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] rgbdata;
|
||||
|
||||
try
|
||||
{
|
||||
rgbdata = new int[source.getWidth() * source.getHeight()];
|
||||
|
||||
source.getRGB(rgbdata, 0, source.getWidth(), 0, 0, source.getWidth(), source.getHeight());
|
||||
|
||||
int alpha;
|
||||
|
||||
for(int i = 0; i < rgbdata.length; i++)
|
||||
{
|
||||
alpha = rgbdata[i] >>> 24;
|
||||
|
||||
if(alpha > newtrans)
|
||||
{
|
||||
alpha = newtrans;
|
||||
}
|
||||
|
||||
rgbdata[i] = (rgbdata[i] & 0x00FFFFFF) | (alpha << 24);
|
||||
}
|
||||
|
||||
return Image.createRGBImage(rgbdata, source.getWidth(), source.getHeight(), true);
|
||||
}
|
||||
catch(OutOfMemoryError oome)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðåîáîðàçîâàíèå â èçîáðàæåíèè source öâåòà color â newcolor
|
||||
*/
|
||||
public static Image transColor(Image source, int color, int newcolor)
|
||||
{
|
||||
if(source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] rgbdata;
|
||||
|
||||
try
|
||||
{
|
||||
rgbdata = new int[source.getWidth() * source.getHeight()];
|
||||
|
||||
source.getRGB(rgbdata, 0, source.getWidth(), 0, 0, source.getWidth(), source.getHeight());
|
||||
|
||||
for(int i = 0; i < rgbdata.length; i++)
|
||||
{
|
||||
if(rgbdata[i] == color)
|
||||
{
|
||||
rgbdata[i] = newcolor;
|
||||
}
|
||||
}
|
||||
|
||||
return Image.createRGBImage(rgbdata, source.getWidth(), source.getHeight(), true);
|
||||
}
|
||||
catch(OutOfMemoryError oome)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
109
src/com/one/IniFile.java
Normal file
109
src/com/one/IniFile.java
Normal file
@ -0,0 +1,109 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
import com.vmx.*;
|
||||
|
||||
/**
|
||||
* Ïàðñåð INI ôàéëîâ.
|
||||
* Ñåêöèè íå ïîäåðæèâàþòñÿ.
|
||||
*/
|
||||
public class IniFile
|
||||
{
|
||||
protected InputStreamDecoder isd;
|
||||
|
||||
/**
|
||||
* Îòêðûòèå ðåñóðñà êàê INI ôàéëà
|
||||
*/
|
||||
public IniFile(String resname) throws IOException
|
||||
{
|
||||
isd = InputStreamDecoder.getResourceDecoder(resname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Îòêðûòèå INI ôàéëà
|
||||
*/
|
||||
public IniFile(InputStreamDecoder isd)
|
||||
{
|
||||
this.isd = isd;
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
isd.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ôàéëà ñëåäóþùóþ ñòðîêó
|
||||
* è ïðåîáðàçîâàòü åå â IniRecord
|
||||
*/
|
||||
public IniRecord getNextRecord() throws IOException
|
||||
{
|
||||
if(isd == null || isd.available() <= 0)
|
||||
{
|
||||
// åñëè ôàéë êîí÷èëñÿ, âîçâðàùàåì null
|
||||
return null;
|
||||
}
|
||||
|
||||
String s;
|
||||
char c;
|
||||
int index;
|
||||
|
||||
// âíåøíèé öèêë - ÷èòàåì ñòðîêè,
|
||||
// ïîêà íå âñòðåòèòñÿ íå ïóñòàÿ
|
||||
while(isd.available() > 0)
|
||||
{
|
||||
s = "";
|
||||
|
||||
// âíóòðåííèé öèêë - ÷òåíèå ñòðîêè
|
||||
while(isd.available() > 0)
|
||||
{
|
||||
c = isd.readChar();
|
||||
|
||||
if(c == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(c == '\t')
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
if(c != '\r')
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
}
|
||||
|
||||
index = s.indexOf(';');
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
// óáèðàåì êîììåíòàðèè
|
||||
s = s.substring(0, index);
|
||||
}
|
||||
|
||||
s = s.trim();
|
||||
|
||||
if(s.length() > 0)
|
||||
{
|
||||
// åñëè ñòðîêà íå ïóñòàÿ, òî âîçâðàùàåì ñîîòâåòñòâóþùóþ IniRecord
|
||||
IniRecord record = new IniRecord();
|
||||
index = s.indexOf('=');
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
record.key = s.substring(0, index).trim();
|
||||
record.value = s.substring(index + 1).trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
record.key = record.value = s;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
13
src/com/one/IniRecord.java
Normal file
13
src/com/one/IniRecord.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.one;
|
||||
|
||||
/**
|
||||
* Çàïèñü INI
|
||||
*/
|
||||
public class IniRecord
|
||||
{
|
||||
/** Êëþ÷ */
|
||||
public String key;
|
||||
|
||||
/** Çíà÷åíèå */
|
||||
public String value;
|
||||
}
|
380
src/com/one/MD5.java
Normal file
380
src/com/one/MD5.java
Normal file
@ -0,0 +1,380 @@
|
||||
package com.one;
|
||||
|
||||
public class MD5
|
||||
{
|
||||
private static final int BLOCK_SIZE = 64; // inner block size in bytes
|
||||
|
||||
private long count;
|
||||
private byte[] buffer;
|
||||
|
||||
/** 128-bit interim result. */
|
||||
private int h0, h1, h2, h3;
|
||||
|
||||
public MD5()
|
||||
{
|
||||
buffer = new byte[BLOCK_SIZE];
|
||||
resetContext();
|
||||
}
|
||||
|
||||
public void update(byte b)
|
||||
{
|
||||
// compute number of bytes still unhashed; ie. present in buffer
|
||||
|
||||
int i = (int)(count % BLOCK_SIZE);
|
||||
count++;
|
||||
buffer[i] = b;
|
||||
|
||||
if(i == (BLOCK_SIZE - 1))
|
||||
{
|
||||
transform(buffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(byte[] b)
|
||||
{
|
||||
update(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void update(byte[] b, int offset, int len)
|
||||
{
|
||||
int n = (int)(count % BLOCK_SIZE);
|
||||
count += len;
|
||||
int partLen = BLOCK_SIZE - n;
|
||||
int i = 0;
|
||||
|
||||
if(len >= partLen)
|
||||
{
|
||||
System.arraycopy(b, offset, buffer, n, partLen);
|
||||
transform(buffer, 0);
|
||||
|
||||
for(i = partLen; i + BLOCK_SIZE - 1 < len; i += BLOCK_SIZE)
|
||||
{
|
||||
transform(b, offset + i);
|
||||
}
|
||||
|
||||
n = 0;
|
||||
}
|
||||
|
||||
if(i < len)
|
||||
{
|
||||
System.arraycopy(b, offset + i, buffer, n, len - i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the result from the contents of the current context.
|
||||
*
|
||||
* @return the output of the completed hash operation.
|
||||
*/
|
||||
public int[] digest()
|
||||
{
|
||||
byte[] tail = padBuffer(); // pad remaining bytes in buffer
|
||||
update(tail, 0, tail.length); // last transform of a message
|
||||
|
||||
// make a result out of context
|
||||
int[] result = new int[4];
|
||||
result[0] = (h0 & 0xFF) << 24 |
|
||||
((h0 >> 8) & 0xFF) << 16 |
|
||||
((h0 >> 16) & 0xFF) << 8 |
|
||||
((h0 >> 24) & 0xFF);
|
||||
result[1] = (h1 & 0xFF) << 24 |
|
||||
((h1 >> 8) & 0xFF) << 16 |
|
||||
((h1 >> 16) & 0xFF) << 8 |
|
||||
((h1 >> 24) & 0xFF);
|
||||
result[2] = (h2 & 0xFF) << 24 |
|
||||
((h2 >> 8) & 0xFF) << 16 |
|
||||
((h2 >> 16) & 0xFF) << 8 |
|
||||
((h2 >> 24) & 0xFF);
|
||||
result[3] = (h3 & 0xFF) << 24 |
|
||||
((h3 >> 8) & 0xFF) << 16 |
|
||||
((h3 >> 16) & 0xFF) << 8 |
|
||||
((h3 >> 24) & 0xFF);
|
||||
|
||||
reset(); // reset this instance for future re-use
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
count = 0L;
|
||||
|
||||
for(int i = 0; i < BLOCK_SIZE; buffer[i++] = 0);
|
||||
|
||||
resetContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte array to use as padding before completing a hash
|
||||
* operation.
|
||||
*
|
||||
* @return the bytes to pad the remaining bytes in the buffer before
|
||||
* completing a hash operation.
|
||||
*/
|
||||
private byte[] padBuffer()
|
||||
{
|
||||
int n = (int)(count % BLOCK_SIZE);
|
||||
int padding = (n < 56) ? (56 - n) : (120 - n);
|
||||
byte[] result = new byte[padding+8];
|
||||
|
||||
// padding is always binary 1 followed by binary 0s
|
||||
result[0] = (byte)0x80;
|
||||
|
||||
// save number of bits, casting the long to an array of 8 bytes
|
||||
long bits = count << 3;
|
||||
|
||||
result[padding++] = (byte)bits;
|
||||
result[padding++] = (byte)(bits >>> 8);
|
||||
result[padding++] = (byte)(bits >>> 16);
|
||||
result[padding++] = (byte)(bits >>> 24);
|
||||
result[padding++] = (byte)(bits >>> 32);
|
||||
result[padding++] = (byte)(bits >>> 40);
|
||||
result[padding++] = (byte)(bits >>> 48);
|
||||
result[padding] = (byte)(bits >>> 56);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void resetContext()
|
||||
{
|
||||
h0 = 0x67452301;
|
||||
h1 = 0xEFCDAB89;
|
||||
h2 = 0x98BADCFE;
|
||||
h3 = 0x10325476;
|
||||
}
|
||||
|
||||
/**
|
||||
* The block digest transformation per se.
|
||||
*
|
||||
* @param in the <i>blockSize</i> long block, as an array of bytes to digest.
|
||||
* @param offset the index where the data to digest is located within the
|
||||
* input buffer.
|
||||
*/
|
||||
private void transform(byte[] in, int i)
|
||||
{
|
||||
int X0 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X1 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X2 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X3 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X4 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X5 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X6 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X7 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X8 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X9 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X10 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X11 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X12 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X13 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X14 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i++] << 24;
|
||||
int X15 = (in[i++] & 0xFF)
|
||||
| (in[i++] & 0xFF) << 8
|
||||
| (in[i++] & 0xFF) << 16
|
||||
| in[i] << 24;
|
||||
|
||||
int A = h0;
|
||||
int B = h1;
|
||||
int C = h2;
|
||||
int D = h3;
|
||||
|
||||
// hex constants are from md5.c in FSF Gnu Privacy Guard 0.9.2
|
||||
|
||||
// round 1
|
||||
A += ((B & C) | (~B & D)) + X0 + 0xD76AA478;
|
||||
A = B + (A << 7 | A >>> -7);
|
||||
D += ((A & B) | (~A & C)) + X1 + 0xE8C7B756;
|
||||
D = A + (D << 12 | D >>> -12);
|
||||
C += ((D & A) | (~D & B)) + X2 + 0x242070DB;
|
||||
C = D + (C << 17 | C >>> -17);
|
||||
B += ((C & D) | (~C & A)) + X3 + 0xC1BDCEEE;
|
||||
B = C + (B << 22 | B >>> -22);
|
||||
|
||||
A += ((B & C) | (~B & D)) + X4 + 0xF57C0FAF;
|
||||
A = B + (A << 7 | A >>> -7);
|
||||
D += ((A & B) | (~A & C)) + X5 + 0x4787C62A;
|
||||
D = A + (D << 12 | D >>> -12);
|
||||
C += ((D & A) | (~D & B)) + X6 + 0xA8304613;
|
||||
C = D + (C << 17 | C >>> -17);
|
||||
B += ((C & D) | (~C & A)) + X7 + 0xFD469501;
|
||||
B = C + (B << 22 | B >>> -22);
|
||||
|
||||
A += ((B & C) | (~B & D)) + X8 + 0x698098D8;
|
||||
A = B + (A << 7 | A >>> -7);
|
||||
D += ((A & B) | (~A & C)) + X9 + 0x8B44F7AF;
|
||||
D = A + (D << 12 | D >>> -12);
|
||||
C += ((D & A) | (~D & B)) + X10 + 0xFFFF5BB1;
|
||||
C = D + (C << 17 | C >>> -17);
|
||||
B += ((C & D) | (~C & A)) + X11 + 0x895CD7BE;
|
||||
B = C + (B << 22 | B >>> -22);
|
||||
|
||||
A += ((B & C) | (~B & D)) + X12 + 0x6B901122;
|
||||
A = B + (A << 7 | A >>> -7);
|
||||
D += ((A & B) | (~A & C)) + X13 + 0xFD987193;
|
||||
D = A + (D << 12 | D >>> -12);
|
||||
C += ((D & A) | (~D & B)) + X14 + 0xA679438E;
|
||||
C = D + (C << 17 | C >>> -17);
|
||||
B += ((C & D) | (~C & A)) + X15 + 0x49B40821;
|
||||
B = C + (B << 22 | B >>> -22);
|
||||
|
||||
// round 2
|
||||
A += ((B & D) | (C & ~D)) + X1 + 0xF61E2562;
|
||||
A = B + (A << 5 | A >>> -5);
|
||||
D += ((A & C) | (B & ~C)) + X6 + 0xC040B340;
|
||||
D = A + (D << 9 | D >>> -9);
|
||||
C += ((D & B) | (A & ~B)) + X11 + 0x265E5A51;
|
||||
C = D + (C << 14 | C >>> -14);
|
||||
B += ((C & A) | (D & ~A)) + X0 + 0xE9B6C7AA;
|
||||
B = C + (B << 20 | B >>> -20);
|
||||
|
||||
A += ((B & D) | (C & ~D)) + X5 + 0xD62F105D;
|
||||
A = B + (A << 5 | A >>> -5);
|
||||
D += ((A & C) | (B & ~C)) + X10 + 0x02441453;
|
||||
D = A + (D << 9 | D >>> -9);
|
||||
C += ((D & B) | (A & ~B)) + X15 + 0xD8A1E681;
|
||||
C = D + (C << 14 | C >>> -14);
|
||||
B += ((C & A) | (D & ~A)) + X4 + 0xE7D3FBC8;
|
||||
B = C + (B << 20 | B >>> -20);
|
||||
|
||||
A += ((B & D) | (C & ~D)) + X9 + 0x21E1CDE6;
|
||||
A = B + (A << 5 | A >>> -5);
|
||||
D += ((A & C) | (B & ~C)) + X14 + 0xC33707D6;
|
||||
D = A + (D << 9 | D >>> -9);
|
||||
C += ((D & B) | (A & ~B)) + X3 + 0xF4D50D87;
|
||||
C = D + (C << 14 | C >>> -14);
|
||||
B += ((C & A) | (D & ~A)) + X8 + 0x455A14ED;
|
||||
B = C + (B << 20 | B >>> -20);
|
||||
|
||||
A += ((B & D) | (C & ~D)) + X13 + 0xA9E3E905;
|
||||
A = B + (A << 5 | A >>> -5);
|
||||
D += ((A & C) | (B & ~C)) + X2 + 0xFCEFA3F8;
|
||||
D = A + (D << 9 | D >>> -9);
|
||||
C += ((D & B) | (A & ~B)) + X7 + 0x676F02D9;
|
||||
C = D + (C << 14 | C >>> -14);
|
||||
B += ((C & A) | (D & ~A)) + X12 + 0x8D2A4C8A;
|
||||
B = C + (B << 20 | B >>> -20);
|
||||
|
||||
// round 3
|
||||
A += (B ^ C ^ D) + X5 + 0xFFFA3942;
|
||||
A = B + (A << 4 | A >>> -4);
|
||||
D += (A ^ B ^ C) + X8 + 0x8771F681;
|
||||
D = A + (D << 11 | D >>> -11);
|
||||
C += (D ^ A ^ B) + X11 + 0x6D9D6122;
|
||||
C = D + (C << 16 | C >>> -16);
|
||||
B += (C ^ D ^ A) + X14 + 0xFDE5380C;
|
||||
B = C + (B << 23 | B >>> -23);
|
||||
|
||||
A += (B ^ C ^ D) + X1 + 0xA4BEEA44;
|
||||
A = B + (A << 4 | A >>> -4);
|
||||
D += (A ^ B ^ C) + X4 + 0x4BDECFA9;
|
||||
D = A + (D << 11 | D >>> -11);
|
||||
C += (D ^ A ^ B) + X7 + 0xF6BB4B60;
|
||||
C = D + (C << 16 | C >>> -16);
|
||||
B += (C ^ D ^ A) + X10 + 0xBEBFBC70;
|
||||
B = C + (B << 23 | B >>> -23);
|
||||
|
||||
A += (B ^ C ^ D) + X13 + 0x289B7EC6;
|
||||
A = B + (A << 4 | A >>> -4);
|
||||
D += (A ^ B ^ C) + X0 + 0xEAA127FA;
|
||||
D = A + (D << 11 | D >>> -11);
|
||||
C += (D ^ A ^ B) + X3 + 0xD4EF3085;
|
||||
C = D + (C << 16 | C >>> -16);
|
||||
B += (C ^ D ^ A) + X6 + 0x04881D05;
|
||||
B = C + (B << 23 | B >>> -23);
|
||||
|
||||
A += (B ^ C ^ D) + X9 + 0xD9D4D039;
|
||||
A = B + (A << 4 | A >>> -4);
|
||||
D += (A ^ B ^ C) + X12 + 0xE6DB99E5;
|
||||
D = A + (D << 11 | D >>> -11);
|
||||
C += (D ^ A ^ B) + X15 + 0x1FA27CF8;
|
||||
C = D + (C << 16 | C >>> -16);
|
||||
B += (C ^ D ^ A) + X2 + 0xC4AC5665;
|
||||
B = C + (B << 23 | B >>> -23);
|
||||
|
||||
// round 4
|
||||
A += (C ^ (B | ~D)) + X0 + 0xF4292244;
|
||||
A = B + (A << 6 | A >>> -6);
|
||||
D += (B ^ (A | ~C)) + X7 + 0x432AFF97;
|
||||
D = A + (D << 10 | D >>> -10);
|
||||
C += (A ^ (D | ~B)) + X14 + 0xAB9423A7;
|
||||
C = D + (C << 15 | C >>> -15);
|
||||
B += (D ^ (C | ~A)) + X5 + 0xFC93A039;
|
||||
B = C + (B << 21 | B >>> -21);
|
||||
|
||||
A += (C ^ (B | ~D)) + X12 + 0x655B59C3;
|
||||
A = B + (A << 6 | A >>> -6);
|
||||
D += (B ^ (A | ~C)) + X3 + 0x8F0CCC92;
|
||||
D = A + (D << 10 | D >>> -10);
|
||||
C += (A ^ (D | ~B)) + X10 + 0xFFEFF47D;
|
||||
C = D + (C << 15 | C >>> -15);
|
||||
B += (D ^ (C | ~A)) + X1 + 0x85845dd1;
|
||||
B = C + (B << 21 | B >>> -21);
|
||||
|
||||
A += (C ^ (B | ~D)) + X8 + 0x6FA87E4F;
|
||||
A = B + (A << 6 | A >>> -6);
|
||||
D += (B ^ (A | ~C)) + X15 + 0xFE2CE6E0;
|
||||
D = A + (D << 10 | D >>> -10);
|
||||
C += (A ^ (D | ~B)) + X6 + 0xA3014314;
|
||||
C = D + (C << 15 | C >>> -15);
|
||||
B += (D ^ (C | ~A)) + X13 + 0x4E0811A1;
|
||||
B = C + (B << 21 | B >>> -21);
|
||||
|
||||
A += (C ^ (B | ~D)) + X4 + 0xF7537E82;
|
||||
A = B + (A << 6 | A >>> -6);
|
||||
D += (B ^ (A | ~C)) + X11 + 0xBD3AF235;
|
||||
D = A + (D << 10 | D >>> -10);
|
||||
C += (A ^ (D | ~B)) + X2 + 0x2AD7D2BB;
|
||||
C = D + (C << 15 | C >>> -15);
|
||||
B += (D ^ (C | ~A)) + X9 + 0xEB86D391;
|
||||
B = C + (B << 21 | B >>> -21);
|
||||
|
||||
h0 += A;
|
||||
h1 += B;
|
||||
h2 += C;
|
||||
h3 += D;
|
||||
}
|
||||
}
|
146
src/com/one/PaintableObject.java
Normal file
146
src/com/one/PaintableObject.java
Normal file
@ -0,0 +1,146 @@
|
||||
package com.one;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
|
||||
public class PaintableObject
|
||||
{
|
||||
public static final int KEY_NUM0 = 48;
|
||||
public static final int KEY_NUM1 = 49;
|
||||
public static final int KEY_NUM2 = 50;
|
||||
public static final int KEY_NUM3 = 51;
|
||||
public static final int KEY_NUM4 = 52;
|
||||
public static final int KEY_NUM5 = 53;
|
||||
public static final int KEY_NUM6 = 54;
|
||||
public static final int KEY_NUM7 = 55;
|
||||
public static final int KEY_NUM8 = 56;
|
||||
public static final int KEY_NUM9 = 57;
|
||||
public static final int KEY_STAR = 42;
|
||||
public static final int KEY_POUND = 35;
|
||||
|
||||
public static final int UP = 1;
|
||||
public static final int LEFT = 2;
|
||||
public static final int RIGHT = 5;
|
||||
public static final int DOWN = 6;
|
||||
public static final int FIRE = 8;
|
||||
public static final int GAME_A = 9;
|
||||
public static final int GAME_B = 10;
|
||||
public static final int GAME_C = 11;
|
||||
public static final int GAME_D = 12;
|
||||
|
||||
private static Renderer renderer;
|
||||
|
||||
public static void setRenderer(Renderer rn)
|
||||
{
|
||||
renderer = rn;
|
||||
}
|
||||
|
||||
public static Renderer getRenderer()
|
||||
{
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public void repaint()
|
||||
{
|
||||
renderer.repaint(this);
|
||||
}
|
||||
|
||||
public void repaint(int x, int y, int width, int height)
|
||||
{
|
||||
renderer.repaint(this, x, y, width, height);
|
||||
}
|
||||
|
||||
public void serviceRepaints()
|
||||
{
|
||||
renderer.serviceRepaints(this);
|
||||
}
|
||||
|
||||
public void setFullScreenMode(boolean mode)
|
||||
{
|
||||
renderer.setFullScreenMode(this, mode);
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return renderer.getWidth();
|
||||
}
|
||||
|
||||
public int getHeight()
|
||||
{
|
||||
return renderer.getHeight();
|
||||
}
|
||||
|
||||
public int getGameAction(int keyCode)
|
||||
{
|
||||
return renderer.getGameAction(keyCode);
|
||||
}
|
||||
|
||||
public int getKeyCode(int gameAction)
|
||||
{
|
||||
return renderer.getKeyCode(gameAction);
|
||||
}
|
||||
|
||||
public String getKeyName(int keyCode)
|
||||
{
|
||||
return renderer.getKeyName(keyCode);
|
||||
}
|
||||
|
||||
public boolean hasPointerEvents()
|
||||
{
|
||||
return renderer.hasPointerEvents();
|
||||
}
|
||||
|
||||
public boolean hasPointerMotionEvents()
|
||||
{
|
||||
return renderer.hasPointerMotionEvents();
|
||||
}
|
||||
|
||||
public boolean hasRepeatEvents()
|
||||
{
|
||||
return renderer.hasRepeatEvents();
|
||||
}
|
||||
|
||||
public boolean isDoubleBuffered()
|
||||
{
|
||||
return renderer.isDoubleBuffered();
|
||||
}
|
||||
|
||||
protected void showNotify()
|
||||
{
|
||||
}
|
||||
|
||||
protected void hideNotify()
|
||||
{
|
||||
}
|
||||
|
||||
protected void keyPressed(int keyCode)
|
||||
{
|
||||
}
|
||||
|
||||
protected void keyReleased(int keyCode)
|
||||
{
|
||||
}
|
||||
|
||||
protected void keyRepeated(int keyCode)
|
||||
{
|
||||
}
|
||||
|
||||
protected void paint(Graphics g)
|
||||
{
|
||||
}
|
||||
|
||||
protected void pointerDragged(int x, int y)
|
||||
{
|
||||
}
|
||||
|
||||
protected void pointerPressed(int x, int y)
|
||||
{
|
||||
}
|
||||
|
||||
protected void pointerReleased(int x, int y)
|
||||
{
|
||||
}
|
||||
|
||||
protected void sizeChanged(int w, int h)
|
||||
{
|
||||
}
|
||||
}
|
234
src/com/one/PakFile.java
Normal file
234
src/com/one/PakFile.java
Normal file
@ -0,0 +1,234 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Hashtable;
|
||||
import com.vmx.*;
|
||||
import filemanager.main;
|
||||
|
||||
/**
|
||||
* Êëàññ äëÿ ðàáîòû ñ PAK ôàéëàìè.
|
||||
*/
|
||||
public class PakFile
|
||||
{
|
||||
protected RandomAccessInputStream bis;
|
||||
protected ByteArrayOutputStream baos_header, baos_files;
|
||||
protected DataOutputStream dos_header;
|
||||
|
||||
protected short currindex = 0;
|
||||
protected int curroffset = 0;
|
||||
protected byte[] buf = null;
|
||||
|
||||
protected short header; // ðàçìåð çàãîëîâêà (ñìåùåíèå ïåðâîãî ôàéëà)
|
||||
protected short count; // ÷èñëî ôàéëîâ
|
||||
protected String[] names; // èìÿ ôàéëà
|
||||
protected int[] offset; // ñìåùåíèå ôàéëà îòíîñèòåëüíî êîíöà çàãîëîâêà
|
||||
protected short[] size; // ðàçìåð ôàéëà
|
||||
protected Hashtable entries; // òàáëèöà ñîîòâåòñòâèÿ èíäåêñîâ èìåíàì
|
||||
|
||||
/**
|
||||
* Îòêðûòèå PAK ôàéëà èç ïîòîêà â ðåæèìå ÷òåíèÿ
|
||||
*/
|
||||
public PakFile(RandomAccessInputStream is) throws IOException
|
||||
{
|
||||
closeOutput();
|
||||
|
||||
//bis = new BufferedInputStream(is);
|
||||
bis = is;
|
||||
BufDataInputStream dis = new BufDataInputStream(2048, bis);
|
||||
|
||||
header = dis.readShort();
|
||||
count = dis.readShort();
|
||||
|
||||
names = new String[count];
|
||||
offset = new int[count];
|
||||
size = new short[count];
|
||||
|
||||
entries = new Hashtable(count + count / 2);
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
names[i] = dis.readUTF();
|
||||
offset[i] = dis.readInt(); // + header;
|
||||
size[i] = dis.readShort();
|
||||
|
||||
entries.put(names[i], new Integer(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñîçäàíèå PAK ôàéëà â ðåæèìå çàïèñè.
|
||||
*
|
||||
* @param newcount ÷èñëî ýëåìåíòîâ
|
||||
*/
|
||||
public PakFile(int newcount) throws IOException
|
||||
{
|
||||
closeInput();
|
||||
|
||||
baos_header = new ByteArrayOutputStream();
|
||||
dos_header = new DataOutputStream(baos_header);
|
||||
baos_files = new ByteArrayOutputStream();
|
||||
buf = new byte[main.COPYBUFSIZE];
|
||||
currindex = 0;
|
||||
curroffset = 0;
|
||||
|
||||
header = 0;
|
||||
count = (short)newcount;
|
||||
|
||||
dos_header.writeShort(header);
|
||||
dos_header.writeShort(count);
|
||||
|
||||
names = new String[count];
|
||||
offset = new int[count];
|
||||
size = new short[count];
|
||||
|
||||
entries = null;
|
||||
}
|
||||
|
||||
protected void closeInput() throws IOException
|
||||
{
|
||||
if(bis != null)
|
||||
{
|
||||
bis.close();
|
||||
bis = null;
|
||||
}
|
||||
|
||||
names = null;
|
||||
offset = null;
|
||||
size = null;
|
||||
entries = null;
|
||||
}
|
||||
|
||||
protected void closeOutput() throws IOException
|
||||
{
|
||||
if(dos_header != null)
|
||||
{
|
||||
dos_header.close();
|
||||
dos_header = null;
|
||||
baos_header = null;
|
||||
}
|
||||
|
||||
if(baos_files != null)
|
||||
{
|
||||
baos_files.close();
|
||||
baos_files = null;
|
||||
}
|
||||
|
||||
buf = null;
|
||||
System.gc();
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
closeInput();
|
||||
closeOutput();
|
||||
}
|
||||
|
||||
public String[] listElements()
|
||||
{
|
||||
if(names != null)
|
||||
{
|
||||
String[] s = new String[names.length];
|
||||
System.arraycopy(names, 0, s, 0, names.length);
|
||||
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getElementAsStream(String name) throws IOException, IllegalStateException
|
||||
{
|
||||
if(bis == null)
|
||||
{
|
||||
throw new IllegalStateException("Pak file was not open for reading");
|
||||
}
|
||||
|
||||
Integer index = (Integer)entries.get(name);
|
||||
|
||||
if(index == null)
|
||||
{
|
||||
throw new IOException("File \"" + name + "\" not found");
|
||||
}
|
||||
|
||||
return RandomAccessInputStream.getPartialInputStream(bis, offset[index.intValue()] + header, size[index.intValue()]);
|
||||
}
|
||||
|
||||
public short getElementSize(String name) throws IOException, IllegalStateException
|
||||
{
|
||||
if(bis == null)
|
||||
{
|
||||
throw new IllegalStateException("Pak file was not open for reading");
|
||||
}
|
||||
|
||||
Integer index = (Integer)entries.get(name);
|
||||
|
||||
if(index == null)
|
||||
{
|
||||
throw new IOException("File \"" + name + "\" not found");
|
||||
}
|
||||
|
||||
return size[index.intValue()];
|
||||
}
|
||||
|
||||
public void addElement(String name, InputStream is, ProgressCallback callback) throws IOException, IllegalStateException
|
||||
{
|
||||
if(baos_header == null || baos_files == null)
|
||||
{
|
||||
throw new IllegalStateException("Pak file was not open for writing");
|
||||
}
|
||||
else if(currindex >= count)
|
||||
{
|
||||
throw new IllegalStateException("All available elements have been set");
|
||||
}
|
||||
|
||||
dos_header.writeUTF(name);
|
||||
dos_header.writeInt(curroffset);
|
||||
dos_header.writeShort(is.available());
|
||||
|
||||
curroffset += is.available();
|
||||
currindex++;
|
||||
|
||||
if(callback != null)
|
||||
{
|
||||
int buflen;
|
||||
|
||||
while(is.available() > 0)
|
||||
{
|
||||
baos_files.write(buf, 0, buflen = is.read(buf));
|
||||
callback.progress(buflen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(is.available() > 0)
|
||||
{
|
||||
baos_files.write(buf, 0, is.read(buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(OutputStream os) throws IOException, IllegalStateException
|
||||
{
|
||||
if(baos_header == null || baos_files == null)
|
||||
{
|
||||
throw new IllegalStateException("Pak file was not open for writing");
|
||||
}
|
||||
|
||||
buf = baos_header.toByteArray();
|
||||
|
||||
header = (short)buf.length;
|
||||
buf[0] = (byte)((header >> 8) & 0xFF);
|
||||
buf[1] = (byte)(header & 0xFF);
|
||||
|
||||
os.write(buf);
|
||||
os.write(baos_files.toByteArray());
|
||||
os.flush();
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[PakFile] " + s);
|
||||
// }
|
||||
}
|
115
src/com/one/PakThread.java
Normal file
115
src/com/one/PakThread.java
Normal file
@ -0,0 +1,115 @@
|
||||
package com.one;
|
||||
|
||||
import com.vmx.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.microedition.io.Connector;
|
||||
import javax.microedition.io.file.FileConnection;
|
||||
import filemanager.filesystem;
|
||||
import javax.microedition.lcdui.Gauge;
|
||||
|
||||
/**
|
||||
* Ïîòîê ñîçäàíèÿ PAK àðõèâà
|
||||
*/
|
||||
public class PakThread implements Runnable
|
||||
{
|
||||
protected String filename;
|
||||
protected Vector files;
|
||||
protected ProgressCallback callback;
|
||||
public String error;
|
||||
public boolean interrupt;
|
||||
protected boolean usecb;
|
||||
|
||||
public PakThread(String fn, Vector filelist, ProgressCallback cb)
|
||||
{
|
||||
filename = fn;
|
||||
files = filelist;
|
||||
callback = cb;
|
||||
error = null;
|
||||
interrupt = false;
|
||||
usecb = callback != null;
|
||||
}
|
||||
|
||||
protected void addToPak(PakFile pf, String file) throws IOException, IllegalStateException
|
||||
{
|
||||
if(usecb)
|
||||
{
|
||||
callback.setText(file.substring(file.lastIndexOf('/') + 1));
|
||||
}
|
||||
|
||||
InputStream is = Connector.openInputStream("file:///" + file);
|
||||
int cp = is.available();
|
||||
|
||||
pf.addElement(file.substring(file.lastIndexOf('/') + 1), is, callback);
|
||||
is.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ôóíêöèÿ, îñóùåñòâëÿþùàÿ ñîáñòâåííî ñîçäàíèå PAK ôàéëà
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
FileConnection fc = null;
|
||||
|
||||
try
|
||||
{
|
||||
fc = (FileConnection)Connector.open("file:///" + filename);
|
||||
|
||||
if(fc.exists() && fc.isDirectory())
|
||||
{
|
||||
fc.delete();
|
||||
}
|
||||
|
||||
if(!fc.exists())
|
||||
{
|
||||
fc.create();
|
||||
}
|
||||
|
||||
PakFile pf = new PakFile(files.size());
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
int max = 0;
|
||||
|
||||
for(int i = 0; i < files.size(); i++)
|
||||
{
|
||||
max += filesystem.getSize((String)files.elementAt(i));
|
||||
}
|
||||
|
||||
callback.setMax(max);
|
||||
callback.setProgress(0);
|
||||
}
|
||||
|
||||
for(int i = 0; i < files.size() && !interrupt; i++)
|
||||
{
|
||||
addToPak(pf, (String)files.elementAt(i));
|
||||
}
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.setMax(Gauge.INDEFINITE);
|
||||
callback.setProgress(Gauge.CONTINUOUS_RUNNING);
|
||||
}
|
||||
|
||||
OutputStream os = fc.openOutputStream();
|
||||
pf.write(os);
|
||||
os.close();
|
||||
fc.close();
|
||||
}
|
||||
catch(Exception x)
|
||||
{
|
||||
error = x.toString(); // x.getClass().getName() + ": " + x.getMessage();
|
||||
|
||||
if(fc != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fc.close();
|
||||
}
|
||||
catch(Exception xx)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
106
src/com/one/PlayListFile.java
Normal file
106
src/com/one/PlayListFile.java
Normal file
@ -0,0 +1,106 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
import javax.microedition.io.*;
|
||||
import com.vmx.*;
|
||||
import java.util.Vector;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Êëàññ äëÿ ðàáîòû ñî ñïèñêàìè âîñïðîèçâåäåíèÿ
|
||||
*/
|
||||
public class PlayListFile
|
||||
{
|
||||
/*
|
||||
public static void writePlayList(String[] pl, OutputStream os) throws IOException
|
||||
{
|
||||
DataOutputStream dos = new DataOutputStream(os);
|
||||
|
||||
dos.writeShort((short)pl.length);
|
||||
|
||||
for(int i = 0; i < pl.length; i++)
|
||||
{
|
||||
dos.writeUTF(pl[i]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public static void writePlayList(Vector vpl, String path, boolean allowRelPath, OutputStream os) throws IOException
|
||||
{
|
||||
DataOutputStream dos = new DataOutputStream(os);
|
||||
|
||||
dos.writeShort((short)vpl.size());
|
||||
dos.writeBoolean(allowRelPath);
|
||||
|
||||
Enumeration e = vpl.elements();
|
||||
|
||||
if(e == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(allowRelPath)
|
||||
{
|
||||
int newstart = path.length();
|
||||
String s;
|
||||
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
s = (String)e.nextElement();
|
||||
|
||||
if(s.startsWith(path))
|
||||
{
|
||||
dos.writeUTF(s.substring(newstart));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
dos.writeUTF((String)e.nextElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] readPlayList(String fname) throws IOException
|
||||
{
|
||||
String s = null;
|
||||
int index = fname.lastIndexOf('/');
|
||||
|
||||
if(index > 0)
|
||||
{
|
||||
s = fname.substring(0, index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = fname;
|
||||
}
|
||||
|
||||
return readPlayList(Connector.openInputStream("file:///" + fname), s);
|
||||
}
|
||||
|
||||
public static String[] readPlayList(InputStream is, String path) throws IOException
|
||||
{
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
|
||||
String[] pl = new String[dis.readShort()];
|
||||
|
||||
if(dis.readBoolean())
|
||||
{
|
||||
for(int i = 0; i < pl.length; i++)
|
||||
{
|
||||
pl[i] = path + dis.readUTF();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < pl.length; i++)
|
||||
{
|
||||
pl[i] = dis.readUTF();
|
||||
}
|
||||
}
|
||||
|
||||
return pl;
|
||||
}
|
||||
}
|
101
src/com/one/PlayListGenerator.java
Normal file
101
src/com/one/PlayListGenerator.java
Normal file
@ -0,0 +1,101 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
import javax.microedition.io.*;
|
||||
import javax.microedition.io.file.*;
|
||||
import java.util.Vector;
|
||||
import java.util.Enumeration;
|
||||
import com.vmx.*;
|
||||
import filemanager.filesystem;
|
||||
import javax.microedition.lcdui.Gauge;
|
||||
|
||||
/**
|
||||
* Ïîòîê ñîçäàíèÿ ñïèñêà âîñïðîèçâåäåíèÿ
|
||||
*/
|
||||
public class PlayListGenerator implements Runnable
|
||||
{
|
||||
public String error;
|
||||
public boolean interrupt;
|
||||
|
||||
protected String name;
|
||||
protected Vector files;
|
||||
protected int type;
|
||||
protected boolean relPath;
|
||||
protected ProgressCallback callback;
|
||||
|
||||
public PlayListGenerator(String listname, int listtype, boolean allowRelPath, Vector filelist, ProgressCallback cb)
|
||||
{
|
||||
error = null;
|
||||
interrupt = false;
|
||||
|
||||
name = listname;
|
||||
type = listtype;
|
||||
relPath = allowRelPath;
|
||||
files = filelist;
|
||||
callback = cb;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
boolean usecb = callback != null;
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.setMax(files.size());
|
||||
callback.setProgress(0);
|
||||
}
|
||||
|
||||
Vector v = new Vector();
|
||||
Enumeration e = files.elements();
|
||||
String s = null;
|
||||
|
||||
if(e == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
s = (String)e.nextElement();
|
||||
|
||||
if(filesystem.fileType(s) == type)
|
||||
{
|
||||
v.addElement(s);
|
||||
}
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.progress(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.setMax(Gauge.INDEFINITE);
|
||||
callback.setProgress(Gauge.CONTINUOUS_RUNNING);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileConnection fc = (FileConnection)Connector.open("file:///" + name);
|
||||
|
||||
if(fc.exists() && fc.isDirectory())
|
||||
{
|
||||
fc.delete();
|
||||
}
|
||||
|
||||
if(!fc.exists())
|
||||
{
|
||||
fc.create();
|
||||
}
|
||||
|
||||
PlayListFile.writePlayList(v, name.substring(0, name.lastIndexOf('/') + 1), relPath, fc.openOutputStream());
|
||||
|
||||
fc.close();
|
||||
}
|
||||
catch(Exception x)
|
||||
{
|
||||
error = x.toString();
|
||||
}
|
||||
}
|
||||
}
|
115
src/com/one/RTEF.java
Normal file
115
src/com/one/RTEF.java
Normal file
@ -0,0 +1,115 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
import com.vmx.ProgressCallback;
|
||||
import filemanager.main;
|
||||
|
||||
/**
|
||||
* Øèôðàòîð.
|
||||
* Äëÿ øèôðîâàíèÿ èñïîëüçóåòñÿ ìåòîä ãàììèðîâàíèÿ ñ ïñåâäîñëó÷àéíûì êëþ÷îì.
|
||||
*/
|
||||
public class RTEF
|
||||
{
|
||||
private static final int MAX_SHIFT = 64;
|
||||
private static final int SHIFT_STEP = 8;
|
||||
|
||||
protected Random rnd;
|
||||
|
||||
/**
|
||||
* Õåøèðîâàíèÿ ñòðîêè.
|
||||
* Õåø âû÷èñëÿåòñÿ ïî àëãîðèòìó SHA-1, ñòðîêà ðàññìàòðèâàåòñÿ
|
||||
* êàê ïîñëåäîâàòåëüíîñòü ñèìâîëîâ â Þíèêîäå.
|
||||
*/
|
||||
public static int[] hashString(String s)
|
||||
{
|
||||
SHA1 md = new SHA1();
|
||||
int len = s.length();
|
||||
char c;
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
c = s.charAt(i);
|
||||
|
||||
md.update((byte)(c >> 8));
|
||||
md.update((byte)c);
|
||||
}
|
||||
|
||||
return md.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñîçäàíèå øèôðàòîðà ñ ïàðîëåì â âèäå ñòðîêè
|
||||
*/
|
||||
public RTEF(String password)
|
||||
{
|
||||
int[] hash = hashString(password);
|
||||
|
||||
rnd = new Random(((long)hash[0] << 32 | (long)hash[2]) ^ ((long)hash[1] << 16));
|
||||
|
||||
int offset = (hash[3] ^ hash[4]) % 0xFFFF;
|
||||
|
||||
for(int i = 0; i < offset; i++)
|
||||
{
|
||||
rnd.nextLong();
|
||||
}
|
||||
|
||||
rnd.mark();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñîçäàíèå øèôðàòîðà ñ ïàðîëåì â âèäå ïàðàìåòðîâ ÃÑ×
|
||||
*/
|
||||
public RTEF(long key, int offset)
|
||||
{
|
||||
rnd = new Random(key);
|
||||
|
||||
for(int i = 0; i < offset; i++)
|
||||
{
|
||||
rnd.nextLong();
|
||||
}
|
||||
|
||||
rnd.mark();
|
||||
}
|
||||
|
||||
/**
|
||||
* Øèôðîâàíèå ïîòîêà.
|
||||
* Äëÿ êàæäîé îïåðàöèè øèôðîâàíèÿ èñïîëüçóåòñÿ îäèíàêîâàÿ ãàììà.
|
||||
*/
|
||||
public void crypt(InputStream is, OutputStream os, ProgressCallback callback) throws IOException
|
||||
{
|
||||
rnd.reset();
|
||||
|
||||
long k = rnd.nextLong();
|
||||
int shift = 0;
|
||||
|
||||
byte[] buf = new byte[main.COPYBUFSIZE];
|
||||
int buflen, i;
|
||||
|
||||
boolean usecb = callback != null;
|
||||
|
||||
while(is.available() > 0)
|
||||
{
|
||||
buflen = is.read(buf);
|
||||
|
||||
for(i = 0; i < buflen; i++)
|
||||
{
|
||||
buf[i] ^= (byte)((k >> shift) & 0xFF);
|
||||
|
||||
shift += SHIFT_STEP;
|
||||
|
||||
if(shift >= MAX_SHIFT)
|
||||
{
|
||||
k = rnd.nextLong();
|
||||
shift = 0;
|
||||
}
|
||||
}
|
||||
|
||||
os.write(buf, 0, buflen);
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.progress(buflen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
94
src/com/one/Random.java
Normal file
94
src/com/one/Random.java
Normal file
@ -0,0 +1,94 @@
|
||||
package com.one;
|
||||
|
||||
/**
|
||||
* Ãåíåðàòîð ïñåâäîñëó÷àéíûõ ÷èñåë.
|
||||
* Èñïîëüçóåò àëãîðèòì Äæîðäæà Ìàðñàãëèè - "Xorshift 128".
|
||||
* Ïî ôóíêöèîíàëüíîñòè èäåíòè÷åí ñòàíäàðòíîìó ãåíåðàòîðó,
|
||||
* íî ïðåâîñõîäèò åãî ïî êà÷åñòâó ïîëó÷àåìûõ ïîñëåäîâàòåëüíîñòåé.
|
||||
*/
|
||||
public class Random
|
||||
{
|
||||
protected static final long L253 = 1L << 53;
|
||||
protected static final double D253 = (double)L253;
|
||||
protected static final long L224 = 1L << 24;
|
||||
protected static final float F224 = (float)L224;
|
||||
|
||||
protected static final long SEED1 = 362436069L;
|
||||
protected static final long SEED2 = 521288629L;
|
||||
protected static final long SEED3 = 88675123L;
|
||||
|
||||
protected long t, x, y, z, w;
|
||||
protected long x1, y1, z1, w1;
|
||||
|
||||
public Random()
|
||||
{
|
||||
this(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Random(long seed)
|
||||
{
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
protected long next()
|
||||
{
|
||||
t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
public double nextDouble()
|
||||
{
|
||||
return (next() & L253) / D253;
|
||||
}
|
||||
|
||||
public float nextFloat()
|
||||
{
|
||||
return (next() & L224) / F224;
|
||||
}
|
||||
|
||||
public int nextInt()
|
||||
{
|
||||
return (int)(next() % 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
public int nextInt(int n)
|
||||
{
|
||||
return nextInt() % n;
|
||||
}
|
||||
|
||||
public long nextLong()
|
||||
{
|
||||
return next();
|
||||
}
|
||||
|
||||
public void setSeed(long seed)
|
||||
{
|
||||
x = seed;
|
||||
y = SEED1;
|
||||
z = SEED2;
|
||||
w = SEED3;
|
||||
|
||||
mark();
|
||||
}
|
||||
|
||||
public void mark()
|
||||
{
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
z1 = z;
|
||||
w1 = w;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
x = x1;
|
||||
y = y1;
|
||||
z = z1;
|
||||
w = w1;
|
||||
}
|
||||
}
|
123
src/com/one/RandomAccessInputStream.java
Normal file
123
src/com/one/RandomAccessInputStream.java
Normal file
@ -0,0 +1,123 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Âõîäíîé ïîòîê ñ ïðîèçâîëüíûì äîñòóïîì.
|
||||
*/
|
||||
public abstract class RandomAccessInputStream extends InputStream
|
||||
{
|
||||
/**
|
||||
* Ïåðåõîä íà ïðîèçâîëüíóþ ïîçèöèþ
|
||||
*/
|
||||
public abstract void seek(int pos) throws IOException;
|
||||
|
||||
/**
|
||||
* Òåêóùàÿ ïîçèöèÿ â ïîòîêå
|
||||
*/
|
||||
public abstract int tell() throws IOException;
|
||||
|
||||
/**
|
||||
* Îáùàÿ åìêîñòü ïîòîêà
|
||||
*/
|
||||
public abstract int getCapacity() throws IOException;
|
||||
|
||||
/**
|
||||
* Îáíîâëåíèå ïîòîêà
|
||||
*/
|
||||
public abstract void update() throws IOException;
|
||||
|
||||
/**
|
||||
* Mark / Reset ïîääåðæèâàåòñÿ ïî îïðåäåëåíèþ
|
||||
*/
|
||||
public final boolean markSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîèñê äàííûõ â ïîòîêå.
|
||||
* Åñëè startIndex >= 0, òî ïîèñê âåäåòñÿ ñî startIndex, èíà÷å ñ òåêóùåé ïîçèöèè.
|
||||
* Åñëè endIndex >= 0, òî ïîèñê èäåò ìàêñèìóì äî endIndex, èíà÷å äî êîíöà ïîòîêà.
|
||||
* Ïîñëå ïîèñêà ïîòîê âîçâðàùàåòñÿ íà ïðåæíþþ ïîçèöèþ.
|
||||
*/
|
||||
public int indexOf(byte[] data, int off, int len, int startIndex, int endIndex) throws IOException
|
||||
{
|
||||
int prevpos = tell();
|
||||
|
||||
if(startIndex >= 0)
|
||||
{
|
||||
seek(startIndex);
|
||||
}
|
||||
|
||||
int end = off + len;
|
||||
int index = -1;
|
||||
int i = off;
|
||||
|
||||
byte b;
|
||||
|
||||
while(available() > 0)
|
||||
{
|
||||
b = (byte)read();
|
||||
|
||||
if(b == data[i])
|
||||
{
|
||||
if(index < 0)
|
||||
{
|
||||
index = tell() - 1;
|
||||
}
|
||||
|
||||
if(++i >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(b == data[off])
|
||||
{
|
||||
index = tell() - 1;
|
||||
i = off;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = -1;
|
||||
i = off;
|
||||
}
|
||||
|
||||
if(endIndex >= 0 && tell() >= endIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seek(prevpos);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü ÷àñòü íåêîòîðîãî ïîòîêà
|
||||
* êàê îòäåëüíûé ïîòîê.
|
||||
*/
|
||||
public static InputStream getPartialInputStream(RandomAccessInputStream is, int off, int len) throws IOException
|
||||
{
|
||||
byte[] b;
|
||||
|
||||
try
|
||||
{
|
||||
b = new byte[len];
|
||||
|
||||
int pos = is.tell();
|
||||
|
||||
is.seek(off);
|
||||
len = is.read(b, 0, len);
|
||||
is.seek(pos);
|
||||
}
|
||||
catch(OutOfMemoryError oome)
|
||||
{
|
||||
b = new byte[0];
|
||||
len = 0;
|
||||
}
|
||||
|
||||
return new ByteArrayInputStream(b, 0, len);
|
||||
}
|
||||
}
|
114
src/com/one/Renderer.java
Normal file
114
src/com/one/Renderer.java
Normal file
@ -0,0 +1,114 @@
|
||||
package com.one;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
|
||||
public class Renderer extends Canvas
|
||||
{
|
||||
protected PaintableObject current;
|
||||
protected boolean shown;
|
||||
|
||||
public Renderer()
|
||||
{
|
||||
setFullScreenMode(true);
|
||||
current = new PaintableObject();
|
||||
shown = false;
|
||||
}
|
||||
|
||||
public void setCurrent(PaintableObject po)
|
||||
{
|
||||
if(shown)
|
||||
{
|
||||
current.hideNotify();
|
||||
po.showNotify();
|
||||
}
|
||||
|
||||
current = po;
|
||||
|
||||
repaint();
|
||||
serviceRepaints();
|
||||
}
|
||||
|
||||
public PaintableObject getCurrent()
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
public void repaint(PaintableObject po)
|
||||
{
|
||||
if(current == po)
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void repaint(PaintableObject po, int x, int y, int width, int height)
|
||||
{
|
||||
if(current == po)
|
||||
{
|
||||
repaint(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public void serviceRepaints(PaintableObject po)
|
||||
{
|
||||
if(current == po)
|
||||
{
|
||||
serviceRepaints();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFullScreenMode(PaintableObject po, boolean mode)
|
||||
{
|
||||
if(current == po)
|
||||
{
|
||||
setFullScreenMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
current.paint(g);
|
||||
}
|
||||
|
||||
public void showNotify()
|
||||
{
|
||||
current.showNotify();
|
||||
shown = true;
|
||||
}
|
||||
|
||||
public void hideNotify()
|
||||
{
|
||||
current.hideNotify();
|
||||
shown = false;
|
||||
}
|
||||
|
||||
public void keyPressed(int keyCode)
|
||||
{
|
||||
current.keyPressed(keyCode);
|
||||
}
|
||||
|
||||
public void keyRepeated(int keyCode)
|
||||
{
|
||||
current.keyRepeated(keyCode);
|
||||
}
|
||||
|
||||
public void keyReleased(int keyCode)
|
||||
{
|
||||
current.keyReleased(keyCode);
|
||||
}
|
||||
|
||||
public void pointerPressed(int x, int y)
|
||||
{
|
||||
current.pointerPressed(x, y);
|
||||
}
|
||||
|
||||
public void pointerDragged(int x, int y)
|
||||
{
|
||||
current.pointerDragged(x, y);
|
||||
}
|
||||
|
||||
public void pointerReleased(int x, int y)
|
||||
{
|
||||
current.pointerReleased(x, y);
|
||||
}
|
||||
}
|
265
src/com/one/ReplaceableInputStream.java
Normal file
265
src/com/one/ReplaceableInputStream.java
Normal file
@ -0,0 +1,265 @@
|
||||
package com.one;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Ïîòîê ñ âîçìîæíîñòüþ çàìåíû ïðîèçâîëüíîãî ó÷àñòêà.
|
||||
*/
|
||||
public class ReplaceableInputStream extends RandomAccessInputStream
|
||||
{
|
||||
protected RandomAccessInputStream is;
|
||||
protected int currpos, markedpos;
|
||||
|
||||
protected int repstart; // íà÷àëî çàìåíÿåìîãî ôðàãìåíòà
|
||||
protected int repend; // êîíåö çàìåíÿåìîãî ôðàãìåíòà
|
||||
protected int replen; // äëèíà çàìåíÿþùåãî ôðàãìåíòà
|
||||
protected int delta; // èçìåíåíèå ðàçìåðà ïîòîêà
|
||||
protected int repbufend; // ïîçèöèÿ, íà êîòîðîé çàêàí÷èâàåòñÿ çàìåíà
|
||||
|
||||
protected byte[] repbuf;
|
||||
|
||||
public ReplaceableInputStream(RandomAccessInputStream is)
|
||||
{
|
||||
this.is = is;
|
||||
|
||||
markedpos = currpos = 0;
|
||||
|
||||
removeReplace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Óñòàíîâêà çàìåíû.
|
||||
* Äàííûå â ïîòîêå ñî Start âêëþ÷àÿ ïî End íå âêëþ÷àÿ
|
||||
* çàìåíÿåòñÿ íà äàííûå èç ìàññèâà B.
|
||||
* Ðàçìåð çàìåùàþùåãî ìàññèâà ìîæåò íå ñîâïàäàòü
|
||||
* ñ ðàçìåðîì çàìåùàåìîãî ó÷àñòêà, â ýòîì ñëó÷àå
|
||||
* ñîîòâåòñòâåííî èçìåíÿåòñÿ ðàçìåð âñåãî ïîòîêà.
|
||||
*
|
||||
* @param b çàìåùàþùèé ìàññèâ
|
||||
* @param start íà÷àëî çàìåùàåìîãî ó÷àñòêà
|
||||
* @param end êîíåö çàìåùàåìîãî ó÷àñòêà
|
||||
*/
|
||||
public void setReplace(byte[] b, int start, int end)
|
||||
{
|
||||
repbuf = b;
|
||||
|
||||
repstart = start;
|
||||
repend = end;
|
||||
replen = repbuf.length;
|
||||
|
||||
repbufend = repstart + replen;
|
||||
delta = replen - repend + repstart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Îòìåíà çàìåíû.
|
||||
*/
|
||||
public void removeReplace()
|
||||
{
|
||||
repbuf = null;
|
||||
System.gc();
|
||||
|
||||
repstart = repend = replen = repbufend = -1;
|
||||
delta = 0;
|
||||
}
|
||||
|
||||
public byte[] getReplace()
|
||||
{
|
||||
return repbuf;
|
||||
}
|
||||
|
||||
public int getReplaceStart()
|
||||
{
|
||||
return repstart;
|
||||
}
|
||||
|
||||
public int getReplaceEnd()
|
||||
{
|
||||
return repend;
|
||||
}
|
||||
|
||||
public boolean isReplaceSet()
|
||||
{
|
||||
return replen >= 0;
|
||||
}
|
||||
|
||||
public int available() throws IOException
|
||||
{
|
||||
return getCapacity() - currpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Îñâîáîæåíèå ðåñóðñîâ, èñïîëüçóåìûõ ýòèì ïîòîêîì.
|
||||
* Áàçîâûé ïîòîê íå çàêðûâàåòñÿ äëÿ äàëüíåéøåãî èñïîëüçîâàíèÿ.
|
||||
*/
|
||||
public void close()
|
||||
{
|
||||
removeReplace();
|
||||
}
|
||||
|
||||
public void mark(int readLimit)
|
||||
{
|
||||
markedpos = currpos;
|
||||
}
|
||||
|
||||
public int read() throws IOException
|
||||
{
|
||||
if(replen < 0 || currpos < repstart)
|
||||
{
|
||||
is.seek(currpos);
|
||||
currpos++;
|
||||
|
||||
return is.read();
|
||||
}
|
||||
else if(currpos < repbufend)
|
||||
{
|
||||
return ((int)repbuf[currpos++ - repstart]) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
is.seek(currpos - delta);
|
||||
currpos++;
|
||||
|
||||
return is.read();
|
||||
}
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException
|
||||
{
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
int read = 0;
|
||||
|
||||
if(replen < 0)
|
||||
{
|
||||
is.seek(currpos);
|
||||
|
||||
read = is.read(b, off, len);
|
||||
|
||||
currpos += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
if(len > available())
|
||||
{
|
||||
len = available();
|
||||
}
|
||||
|
||||
int temp;
|
||||
|
||||
while(len > 0)
|
||||
{
|
||||
if(currpos < repstart)
|
||||
{
|
||||
is.seek(currpos);
|
||||
|
||||
temp = repstart - currpos;
|
||||
|
||||
if(temp > len)
|
||||
{
|
||||
temp = len;
|
||||
}
|
||||
|
||||
temp = is.read(b, off, temp);
|
||||
|
||||
currpos += temp;
|
||||
read += temp;
|
||||
off += temp;
|
||||
len -= temp;
|
||||
}
|
||||
else if(currpos < repbufend)
|
||||
{
|
||||
temp = repbufend - currpos;
|
||||
|
||||
if(temp > len)
|
||||
{
|
||||
temp = len;
|
||||
}
|
||||
|
||||
System.arraycopy(repbuf, currpos - repstart, b, off, temp);
|
||||
|
||||
currpos += temp;
|
||||
read += temp;
|
||||
off += temp;
|
||||
len -= temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
is.seek(currpos - delta);
|
||||
|
||||
temp = is.read(b, off, len);
|
||||
|
||||
currpos += temp;
|
||||
read += temp;
|
||||
off += temp;
|
||||
len -= temp;
|
||||
}
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
currpos = markedpos;
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException
|
||||
{
|
||||
if(n > available())
|
||||
{
|
||||
n = available();
|
||||
}
|
||||
|
||||
currpos += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void seek(int pos) throws IOException
|
||||
{
|
||||
if(pos == currpos)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(pos < 0)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
else if(pos > getCapacity())
|
||||
{
|
||||
pos = getCapacity();
|
||||
}
|
||||
|
||||
currpos = pos;
|
||||
}
|
||||
|
||||
public int tell()
|
||||
{
|
||||
return currpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷åíèå åìêîñòè ïîòîêà.
|
||||
* Âîçâðàùàåìîå çíà÷åíèå ìîæåò èçìåíÿòüñÿ ïðè óñòàíîâêå çàìåíû.
|
||||
*/
|
||||
public int getCapacity() throws IOException
|
||||
{
|
||||
return is.getCapacity() + delta;
|
||||
}
|
||||
|
||||
public void update() throws IOException
|
||||
{
|
||||
is.update();
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[ReplaceableInputStream] " + s);
|
||||
// }
|
||||
}
|
206
src/com/one/SHA1.java
Normal file
206
src/com/one/SHA1.java
Normal file
@ -0,0 +1,206 @@
|
||||
package com.one;
|
||||
|
||||
public class SHA1
|
||||
{
|
||||
private static final int BLOCK_SIZE = 64;
|
||||
|
||||
private long count;
|
||||
private byte[] buffer;
|
||||
private int[] w = new int[80];
|
||||
|
||||
/** 160-bit interim result. */
|
||||
private int h0, h1, h2, h3, h4;
|
||||
|
||||
public SHA1()
|
||||
{
|
||||
buffer = new byte[BLOCK_SIZE];
|
||||
resetContext();
|
||||
}
|
||||
|
||||
public void update(byte b)
|
||||
{
|
||||
// compute number of bytes still unhashed; ie. present in buffer
|
||||
|
||||
int i = (int)(count % BLOCK_SIZE);
|
||||
count++;
|
||||
buffer[i] = b;
|
||||
|
||||
if(i == (BLOCK_SIZE - 1))
|
||||
{
|
||||
transform(buffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(byte[] b)
|
||||
{
|
||||
update(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void update(byte[] b, int offset, int len)
|
||||
{
|
||||
int n = (int)(count % BLOCK_SIZE);
|
||||
count += len;
|
||||
int partLen = BLOCK_SIZE - n;
|
||||
int i = 0;
|
||||
|
||||
if(len >= partLen)
|
||||
{
|
||||
System.arraycopy(b, offset, buffer, n, partLen);
|
||||
transform(buffer, 0);
|
||||
|
||||
for(i = partLen; i + BLOCK_SIZE - 1 < len; i += BLOCK_SIZE)
|
||||
{
|
||||
transform(b, offset + i);
|
||||
}
|
||||
|
||||
n = 0;
|
||||
}
|
||||
|
||||
if(i < len)
|
||||
{
|
||||
System.arraycopy(b, offset + i, buffer, n, len - i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the result from the contents of the current context.
|
||||
*
|
||||
* @return the output of the completed hash operation.
|
||||
*/
|
||||
public int[] digest()
|
||||
{
|
||||
byte[] tail = padBuffer(); // pad remaining bytes in buffer
|
||||
update(tail, 0, tail.length); // last transform of a message
|
||||
|
||||
int[] result = new int[] {h0, h1, h2, h3, h4}; // make a result out of context
|
||||
|
||||
reset(); // reset this instance for future re-use
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
count = 0L;
|
||||
|
||||
for(int i = 0; i < BLOCK_SIZE; buffer[i++] = 0);
|
||||
|
||||
resetContext();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the byte array to use as padding before completing a hash
|
||||
* operation.
|
||||
*
|
||||
* @return the bytes to pad the remaining bytes in the buffer before
|
||||
* completing a hash operation.
|
||||
*/
|
||||
private byte[] padBuffer()
|
||||
{
|
||||
int n = (int)(count % BLOCK_SIZE);
|
||||
int padding = (n < 56) ? (56 - n) : (120 - n);
|
||||
byte[] result = new byte[padding+8];
|
||||
|
||||
// padding is always binary 1 followed by binary 0s
|
||||
result[0] = (byte)0x80;
|
||||
|
||||
// save number of bits, casting the long to an array of 8 bytes
|
||||
long bits = count << 3;
|
||||
|
||||
result[padding++] = (byte)(bits >>> 56);
|
||||
result[padding++] = (byte)(bits >>> 48);
|
||||
result[padding++] = (byte)(bits >>> 40);
|
||||
result[padding++] = (byte)(bits >>> 32);
|
||||
result[padding++] = (byte)(bits >>> 24);
|
||||
result[padding++] = (byte)(bits >>> 16);
|
||||
result[padding++] = (byte)(bits >>> 8);
|
||||
result[padding] = (byte)bits;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Resets the instance for future re-use. */
|
||||
private void resetContext()
|
||||
{
|
||||
h0 = 0x67452301;
|
||||
h1 = 0xEFCDAB89;
|
||||
h2 = 0x98BADCFE;
|
||||
h3 = 0x10325476;
|
||||
h4 = 0xC3D2E1F0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The block digest transformation per se.
|
||||
*
|
||||
* @param in the <i>blockSize</i> long block, as an array of bytes to digest.
|
||||
* @param offset the index where the data to digest is located within the
|
||||
* input buffer.
|
||||
*/
|
||||
private void transform(byte[] in, int offset)
|
||||
{
|
||||
int A = h0;
|
||||
int B = h1;
|
||||
int C = h2;
|
||||
int D = h3;
|
||||
int E = h4;
|
||||
int r, T;
|
||||
|
||||
for(r = 0; r < 16; r++)
|
||||
{
|
||||
w[r] = in[offset++] << 24 | (in[offset++] & 0xFF) << 16 | (in[offset++] & 0xFF) << 8 | (in[offset++] & 0xFF);
|
||||
}
|
||||
|
||||
for(r = 16; r < 80; r++)
|
||||
{
|
||||
T = w[r - 3] ^ w[r - 8] ^ w[r - 14] ^ w[r - 16];
|
||||
w[r] = T << 1 | T >>> 31;
|
||||
}
|
||||
|
||||
for(r = 0; r < 20; r++) // rounds 0-19
|
||||
{
|
||||
T = (A << 5 | A >>> 27) + ((B & C) | (~B & D)) + E + w[r] + 0x5A827999;
|
||||
E = D;
|
||||
D = C;
|
||||
C = B << 30 | B >>> 2;
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
|
||||
for(r = 20; r < 40; r++) // rounds 20-39
|
||||
{
|
||||
T = (A << 5 | A >>> 27) + (B ^ C ^ D) + E + w[r] + 0x6ED9EBA1;
|
||||
E = D;
|
||||
D = C;
|
||||
C = B << 30 | B >>> 2;
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
|
||||
for(r = 40; r < 60; r++) // rounds 40-59
|
||||
{
|
||||
T = (A << 5 | A >>> 27) + (B & C | B & D | C & D) + E + w[r] + 0x8F1BBCDC;
|
||||
E = D;
|
||||
D = C;
|
||||
C = B << 30 | B >>> 2;
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
|
||||
for(r = 60; r < 80; r++) // rounds 60-79
|
||||
{
|
||||
T = (A << 5 | A >>> 27) + (B ^ C ^ D) + E + w[r] + 0xCA62C1D6;
|
||||
E = D;
|
||||
D = C;
|
||||
C = B << 30 | B >>> 2;
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
|
||||
h0 += A;
|
||||
h1 += B;
|
||||
h2 += C;
|
||||
h3 += D;
|
||||
h4 += E;
|
||||
}
|
||||
}
|
53
src/com/one/SlidingNumber.java
Normal file
53
src/com/one/SlidingNumber.java
Normal file
@ -0,0 +1,53 @@
|
||||
package com.one;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class SlidingNumber
|
||||
{
|
||||
protected static final int FP_SHIFT = 16;
|
||||
protected static Random rnd = new Random();
|
||||
|
||||
protected int current, target, delta;
|
||||
protected int minvalue, maxvalue;
|
||||
protected int mindelta, maxdelta;
|
||||
|
||||
public SlidingNumber(int minvalue, int maxvalue, int minsteps, int maxsteps)
|
||||
{
|
||||
this.minvalue = minvalue;
|
||||
this.maxvalue = maxvalue;
|
||||
|
||||
mindelta = ((maxvalue - minvalue) << FP_SHIFT) / maxsteps;
|
||||
maxdelta = ((maxvalue - minvalue) << FP_SHIFT) / minsteps;
|
||||
|
||||
current = (minvalue + (rnd.nextInt() & 0x7FFFFFFF) % (maxvalue - minvalue + 1)) << FP_SHIFT;
|
||||
updateCourse();
|
||||
}
|
||||
|
||||
public int nextValue()
|
||||
{
|
||||
current += delta;
|
||||
|
||||
if(delta > 0)
|
||||
{
|
||||
if(current >= target)
|
||||
{
|
||||
updateCourse();
|
||||
}
|
||||
}
|
||||
else if(delta < 0)
|
||||
{
|
||||
if(current <= target)
|
||||
{
|
||||
updateCourse();
|
||||
}
|
||||
}
|
||||
|
||||
return current >>> FP_SHIFT;
|
||||
}
|
||||
|
||||
protected void updateCourse()
|
||||
{
|
||||
target = (minvalue + (rnd.nextInt() & 0x7FFFFFFF) % (maxvalue - minvalue + 1)) << FP_SHIFT;
|
||||
delta = (target < current ? -1 : 1) * (mindelta + (rnd.nextInt() & 0x7FFFFFFF) % (maxdelta - mindelta + 1));
|
||||
}
|
||||
}
|
80
src/com/one/TestCanvas.java
Normal file
80
src/com/one/TestCanvas.java
Normal file
@ -0,0 +1,80 @@
|
||||
package com.one;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
|
||||
/**
|
||||
* Ñëóæåáíûé êëàññ.
|
||||
*/
|
||||
public class TestCanvas extends PaintableObject implements Runnable
|
||||
{
|
||||
protected String key, fps;
|
||||
protected Font font;
|
||||
protected int x, y;
|
||||
protected boolean flag;
|
||||
protected long time, prevtime, delta;
|
||||
protected int framecount;
|
||||
|
||||
public TestCanvas()
|
||||
{
|
||||
setFullScreenMode(true);
|
||||
|
||||
font = Font.getDefaultFont();
|
||||
|
||||
key = "0";
|
||||
fps = "0 FPS";
|
||||
|
||||
x = getWidth() / 2;
|
||||
y = (getHeight() - font.getHeight()) / 2;
|
||||
}
|
||||
|
||||
public void keyPressed(int keyCode)
|
||||
{
|
||||
key = Integer.toString(keyCode);
|
||||
|
||||
repaint();
|
||||
serviceRepaints();
|
||||
}
|
||||
|
||||
public void showNotify()
|
||||
{
|
||||
flag = true;
|
||||
(new Thread(this)).start();
|
||||
}
|
||||
|
||||
public void hideNotify()
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
g.setColor(0x008080);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
g.setColor(0xE0E0E0);
|
||||
g.setFont(font);
|
||||
|
||||
g.drawString(key, 0, 0, Graphics.LEFT | Graphics.TOP);
|
||||
g.drawString(fps, getWidth(), 0, Graphics.RIGHT | Graphics.TOP);
|
||||
g.drawString(Long.toString(time, 10).toUpperCase(), x, y, Graphics.HCENTER | Graphics.TOP);
|
||||
|
||||
time = System.currentTimeMillis();
|
||||
delta = time - prevtime;
|
||||
framecount++;
|
||||
|
||||
if(delta > 1000)
|
||||
{
|
||||
fps = Long.toString(framecount * 1000 / delta, 10).toUpperCase() + " FPS";
|
||||
prevtime = time;
|
||||
framecount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
while(flag)
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
679
src/com/vmx/BufDataInputStream.java
Normal file
679
src/com/vmx/BufDataInputStream.java
Normal file
@ -0,0 +1,679 @@
|
||||
/**************************************\
|
||||
* Áóôåðèçîâàííûé ââîä/âûâîä *
|
||||
** **
|
||||
* Áóôåðèçîâàííûé ïîòîê ââîäà *
|
||||
* (c) 2005+, Vitali Filippov [VMX] *
|
||||
* *
|
||||
* BufDataInputStream.java *
|
||||
* Created on 24 Oct 2005, 13:25 *
|
||||
\**************************************/
|
||||
|
||||
package com.vmx;
|
||||
|
||||
import java.io.*;
|
||||
import javax.microedition.io.*;
|
||||
|
||||
import com.one.RandomAccessInputStream;
|
||||
|
||||
/**
|
||||
* Class for buffered data input.
|
||||
* For documentation, see java.io.DataInput.
|
||||
*/
|
||||
public class BufDataInputStream extends RandomAccessInputStream implements DataInput
|
||||
{
|
||||
protected byte buffer[];
|
||||
protected int bmax, blen, bpos;
|
||||
protected int bstreampos;
|
||||
protected int markedPos;
|
||||
protected int capacity, is_available;
|
||||
protected RandomAccessInputStream is;
|
||||
|
||||
/**
|
||||
* Êîíñòðóêòîð.
|
||||
* Èñïîëüçóåòñÿ áóôåð ñòàíäàðòíîãî ðàçìåðà (4 ÊÁ).
|
||||
*/
|
||||
public BufDataInputStream(RandomAccessInputStream iis) throws IOException
|
||||
{
|
||||
this(4096, iis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Êîíñòðóêòîð.
|
||||
* Èñïîëüçóåòñÿ áóôåð çàäàííîãî ðàçìåðà.
|
||||
*/
|
||||
public BufDataInputStream(int bufsize, RandomAccessInputStream iis) throws IOException
|
||||
{
|
||||
if(bufsize <= 0)
|
||||
{
|
||||
throw new IOException("Buffer size must be greater than 0");
|
||||
}
|
||||
|
||||
bmax = bufsize;
|
||||
bpos = blen = 0;
|
||||
bstreampos = 0;
|
||||
buffer = new byte[bmax];
|
||||
|
||||
is = iis;
|
||||
|
||||
capacity = is.getCapacity();
|
||||
is_available = is.available();
|
||||
|
||||
is.mark(capacity + 0x100);
|
||||
|
||||
markedPos = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Óñòàíîâèòü íîâûé áàçîâûé ïîòîê äëÿ ýòîãî áóôåðèçîâàííîãî ïîòîêà.
|
||||
* Ïðè ýòîì áóôåð àâòîìàòè÷åñêè îáíîâëÿåòñÿ.
|
||||
*/
|
||||
public void setInputStream(RandomAccessInputStream iis) throws IOException
|
||||
{
|
||||
is = iis;
|
||||
|
||||
capacity = is.getCapacity();
|
||||
|
||||
is.seek(bstreampos);
|
||||
bufferize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàêðûòèå áóôåðèçîâàííîãî ïîòîêà âìåñòå ñ òåì, íà êîòîðîì
|
||||
* îí îñíîâàí.
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
is.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Âîçâðàùàåò êîëè÷åñòâî áàéò, êîòîðûå åù¸ âîçìîæíî ïðî÷åñòü èç
|
||||
* ýòîãî áóôåðèçîâàííîãî ïîòîêà.
|
||||
*/
|
||||
public int available()
|
||||
{
|
||||
return blen - bpos + is_available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü îáú¸ì ïîòîêà
|
||||
*/
|
||||
public int getCapacity()
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïåðåéòè ê ïîëîæåíèþ pos
|
||||
*/
|
||||
public void seek(int pos) throws IOException
|
||||
{
|
||||
//out(bstreampos + " <= " + pos + " < " + (bstreampos + blen));
|
||||
|
||||
if(pos >= bstreampos && pos < bstreampos + blen)
|
||||
{
|
||||
bpos = pos - bstreampos;
|
||||
}
|
||||
else
|
||||
{
|
||||
is.seek(pos);
|
||||
bufferize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Âîçâðàùàåò òåêóùóþ ïîçèöèþ â áóôåðèçîâàííîì ïîòîêå.
|
||||
*/
|
||||
public int tell()
|
||||
{
|
||||
return capacity - available();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñòàâèò ìåòêó, íà êîòîðóþ âîçâðàùàòüñÿ ïîòîì ìîæíî ïî reset.
|
||||
*/
|
||||
public void mark(int readlimit)
|
||||
{
|
||||
markedPos = tell();
|
||||
}
|
||||
|
||||
/**
|
||||
* Âîçâðàùàåò, äîñòóïíû ëè ôóíêöèè mark() è reset()?
|
||||
*/
|
||||
/*
|
||||
public boolean markSupported()
|
||||
{
|
||||
return markSupp; // <- ìîæåò âåðíóòü false, õîòÿ ïî ëîãèêå çäåñü ó íàñ true
|
||||
//return true;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ïåðåéòè íà ïîñëåäíþþ çàäàííóþ mark'îì ïîçèöèþ.
|
||||
*/
|
||||
public void reset() throws IOException
|
||||
{
|
||||
if(markedPos >= 0)
|
||||
{
|
||||
seek(markedPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("call mark() before reset()");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ïðîïóñòèòü n áàéò
|
||||
*/
|
||||
public long skip(long n) throws IOException
|
||||
{
|
||||
if(n < blen - bpos)
|
||||
{
|
||||
bpos += n;
|
||||
return n;
|
||||
}
|
||||
else
|
||||
{
|
||||
long act = blen - bpos;
|
||||
n -= blen - bpos;
|
||||
act += is.skip(n);
|
||||
bufferize();
|
||||
|
||||
return act;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü ìàññèâ èç ïîòîêà: ïðî÷èòàòü è çàïèñàòü ìàêñèìóì len áàéò,
|
||||
* çàïèñàòü èõ â b[], íà÷èíàÿ ñî ñìåùåíèÿ off, è âåðíóòü êîëè÷åñòâî
|
||||
* ñ÷èòàííûõ áàéò.
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
int rest = len, pos = off, n;
|
||||
|
||||
while(rest > 0)
|
||||
{
|
||||
if(bpos >= blen && bufferize() <= 0) // äàííûå êîí÷èëèñü?
|
||||
{
|
||||
blen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// åñëè áóôåð êîí÷èëñÿ, à bufferize çàïîëíèëî åãî ÷åì-òî - áóäåò îê
|
||||
n = rest;
|
||||
|
||||
if(n > blen - bpos)
|
||||
{
|
||||
n = blen - bpos;
|
||||
}
|
||||
|
||||
System.arraycopy(buffer, bpos, b, pos, n);
|
||||
pos += n;
|
||||
bpos += n;
|
||||
rest -= n;
|
||||
}
|
||||
|
||||
return len - rest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü ìàññèâ b[] ïîëíîñòüþ - ýêâèâàëåíòíî read (b, 0, b.length);
|
||||
*/
|
||||
public int read(byte[] b) throws IOException
|
||||
{
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü 1 áàéò èç ïîòîêà, âåðíóòü åãî, åñëè óñïåøíî, è -1,
|
||||
* åñëè äîñòèãíóò êîíåö ïîòîêà.
|
||||
*/
|
||||
public int read() throws IOException
|
||||
{
|
||||
if(bpos >= blen && bufferize() <= 0) // äàííûå êîí÷èëèñü?
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ((int)buffer[bpos++]) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü 1 áàéò èç ïîòîêà íàçàä, åñëè íà÷àëî ôàéëà - âåðíóòü -1.
|
||||
* Äëÿ ðàáîòû òðåáóåòñÿ ïîääåðæêà mark() è reset()
|
||||
*/
|
||||
public int readBack() throws IOException
|
||||
{
|
||||
if(bpos == 0)
|
||||
{
|
||||
if(available() == capacity)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int old = tell();
|
||||
|
||||
bstreampos -= bmax;
|
||||
|
||||
if(bstreampos < 0)
|
||||
{
|
||||
bstreampos = 0;
|
||||
}
|
||||
|
||||
is.seek(bstreampos);
|
||||
bufferize();
|
||||
bpos = old - bstreampos;
|
||||
}
|
||||
|
||||
return ((int)buffer[--bpos]) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Åñëè áóôåð íå ïîëîí, äî÷èòàòü è äîïîëíèòü åãî.
|
||||
*/
|
||||
public void flush() throws IOException
|
||||
{
|
||||
if(is_available > 0)
|
||||
{
|
||||
if(bpos != 0 && bpos != blen)
|
||||
{
|
||||
System.arraycopy(buffer, bpos, buffer, 0, blen - bpos);
|
||||
}
|
||||
|
||||
blen -= bpos;
|
||||
bstreampos += bpos;
|
||||
bpos = 0;
|
||||
int blenp = is.read(buffer, blen, bmax - blen);
|
||||
blen += blenp;
|
||||
is_available -= blenp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Óáèâàåò òåêóùèé áóôåð è áóôåðèçóåò ñ òåêóùåãî ïîëîæåíèÿ InputStream'à
|
||||
*/
|
||||
protected int bufferize() throws IOException
|
||||
{
|
||||
is_available = is.available();
|
||||
|
||||
bstreampos = capacity - is_available;
|
||||
|
||||
blen = bpos = 0;
|
||||
|
||||
if(is_available > 0)
|
||||
{
|
||||
blen = is.read(buffer, 0, bmax);
|
||||
is_available -= blen;
|
||||
}
|
||||
|
||||
return blen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Îáíîâèòü ñîäåðæèìîå áóôåðà â ñîîòâåòñòâèè ñ ïîòîêîì
|
||||
*/
|
||||
protected void updateBuffer() throws IOException
|
||||
{
|
||||
capacity = is.getCapacity(); // ïîòîê ìîæåò áûòü ïåðåìåííîé äëèíû
|
||||
|
||||
is.seek(bstreampos);
|
||||
bufferize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Îáùåå îáíîâëåíèå ïîòîêà.
|
||||
*/
|
||||
public void update() throws IOException
|
||||
{
|
||||
int pos = tell();
|
||||
is.update();
|
||||
updateBuffer();
|
||||
seek(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü áóëåâî çíà÷åíèå èç ïîòîêà (ñì. DataInput)
|
||||
*/
|
||||
public boolean readBoolean() throws IOException
|
||||
{
|
||||
int r = read();
|
||||
|
||||
if(r == -1)
|
||||
{
|
||||
throw new IOException("EOF");
|
||||
}
|
||||
|
||||
return r != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü áàéò èç ïîòîêà; åñëè äîñòèãíóò êîíåö ïîòîêà,
|
||||
* ãåíåðèðóåòñÿ èñêëþ÷åíèå IOException ñ ñîîáùåíèåì "EOF" (ñì. DataInput)
|
||||
*/
|
||||
public byte readByte() throws IOException
|
||||
{
|
||||
int r = read();
|
||||
|
||||
if(r == -1)
|
||||
{
|
||||
throw new IOException("EOF");
|
||||
}
|
||||
|
||||
return (byte)r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü ñèìâîë (Unicode Big Endian) èç ïîòîêà (ñì. DataInput)
|
||||
*/
|
||||
public char readChar() throws IOException
|
||||
{
|
||||
//return (char)((readUnsignedByte() << 8) | readUnsignedByte());
|
||||
return (char)((read() << 8) | read());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü ñèìâîë (Unicode Big Endian) èç ïîòîêà ÍÀÇÀÄ (ñì. DataInput)
|
||||
*/
|
||||
public char readCharBack() throws IOException
|
||||
{
|
||||
return (char)(readBack() | (readBack() << 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü ÷èñëî ñ ïëàâàþùåé òî÷êîé äâîéíîé òî÷íîñòè (ñì. DataInput)
|
||||
*/
|
||||
public double readDouble() throws IOException
|
||||
{
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü ÷èñëî ñ ïëàâàþùåé òî÷êîé îäèíàðíîé òî÷íîñòè (ñì. DataInput)
|
||||
*/
|
||||
public float readFloat() throws IOException
|
||||
{
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü ìàññèâ b[] èç ïîòîêà öåëèêîì, åñëè öåëèêîì íå ïîëó÷èòñÿ,
|
||||
* ñãåíåðèðîâàòü èñêëþ÷åíèå IOException ñ ñîîáùåíèåì "EOF"
|
||||
*/
|
||||
public void readFully(byte[] b) throws IOException
|
||||
{
|
||||
if(read(b) < b.length)
|
||||
{
|
||||
throw new IOException("EOF");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü â òî÷íîñòè len áàéò è çàïèñàòü èõ â ìàññèâ b[], íà÷èíàÿ
|
||||
* ñî ñìåùåíèÿ off. Åñëè äîñòèãíóò êîíåö ôàéëà - ñãåíåðèðîâàòü
|
||||
* èñêëþ÷åíèå IOException ñ ñîîáùåíèåì "EOF"
|
||||
*/
|
||||
public void readFully(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if(read(b, off, len) < len)
|
||||
{
|
||||
throw new IOException("EOF");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà öåëîå ÷èñëî (ñì. DataInput)
|
||||
*/
|
||||
public int readInt() throws IOException
|
||||
{
|
||||
return (readUnsignedByte() << 24) |
|
||||
(readUnsignedByte() << 16) |
|
||||
(readUnsignedByte() << 8) |
|
||||
(readUnsignedByte());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà öåëîå ÷èñëî (Little Endian)
|
||||
*/
|
||||
public int readLeInt() throws IOException
|
||||
{
|
||||
return (readUnsignedByte()) |
|
||||
(readUnsignedByte() << 8) |
|
||||
(readUnsignedByte() << 16) |
|
||||
(readUnsignedByte() << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà äëèííîå öåëîå ÷èñëî (ñì. DataInput)
|
||||
*/
|
||||
public long readLong() throws IOException
|
||||
{
|
||||
return ((long)readUnsignedByte() << 56) |
|
||||
((long)readUnsignedByte() << 48) |
|
||||
((long)readUnsignedByte() << 40) |
|
||||
((long)readUnsignedByte() << 32) |
|
||||
((long)readUnsignedByte() << 24) |
|
||||
((long)readUnsignedByte() << 16) |
|
||||
((long)readUnsignedByte() << 8) |
|
||||
((long)readUnsignedByte());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà êîðîòêîå öåëîå ÷èñëî (ñì. DataInput)
|
||||
*/
|
||||
public short readShort() throws IOException
|
||||
{
|
||||
return (short)((readUnsignedByte() << 8) | readUnsignedByte());
|
||||
}
|
||||
|
||||
public short readLeShort() throws IOException
|
||||
{
|
||||
return (short)(readUnsignedByte() | (readUnsignedByte() << 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà áåççíàêîâûé áàéò (ñì. DataInput)
|
||||
*/
|
||||
public int readUnsignedByte() throws IOException
|
||||
{
|
||||
return ((int)readByte()) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà áåççíàêîâîå êîðîòêîå öåëîå (ñì. DataInput)
|
||||
*/
|
||||
public int readUnsignedShort() throws IOException
|
||||
{
|
||||
return ((int)readShort()) & 0xFFFF;
|
||||
}
|
||||
|
||||
public int readUnsignedLeShort() throws IOException
|
||||
{
|
||||
return ((int)readLeShort()) & 0xFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðîïóñòèòü len áàéò (ñì. DataInput)
|
||||
*/
|
||||
public int skipBytes(int len) throws IOException
|
||||
{
|
||||
return (int)skip(len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà ñòðîêó â UTF-8 â ñîîòâåòñòâèè ñî
|
||||
* ñïåöèôèêàöèåé â DataInput (ñì. DataInput)
|
||||
*/
|
||||
public String readUTF() throws IOException, UTFDataFormatException
|
||||
{
|
||||
String s = "";
|
||||
int n = readUnsignedShort();
|
||||
byte b[] = new byte[n];
|
||||
readFully(b);
|
||||
return new String(b, 0, b.length, "UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà ñèìâîë â êîäèðîâêå UTF-8.
|
||||
* Ïîñêîëüêó âîçìîæíà ñèòóàöèÿ, êîãäà òåêñò
|
||||
* ÍÅ â UTF-8 áóäåò ÷èòàòüñÿ êàê UTF-8,
|
||||
* UTFDataFormatException çäåñü íå èñïîëüçóåòñÿ.
|
||||
* Âìåñòî ýòîãî âîçâðàùàåòñÿ íóëåâîé ñèìâîë.
|
||||
*/
|
||||
public char readCharUTF() throws IOException
|
||||
{
|
||||
int b, c, d;
|
||||
|
||||
b = read();
|
||||
|
||||
if(b == -1)
|
||||
{
|
||||
return (char)-1;
|
||||
}
|
||||
|
||||
if((b & 0x80) == 0)
|
||||
{
|
||||
return (char)b;
|
||||
}
|
||||
else if((b & 0xE0) == 0xC0)
|
||||
{
|
||||
c = read();
|
||||
|
||||
if((c & 0xC0) == 0x80)
|
||||
{
|
||||
return (char)(((b & 0x1F) << 6) | (c & 0x3F));
|
||||
}
|
||||
}
|
||||
else if((b & 0xF0) == 0xE0)
|
||||
{
|
||||
c = read();
|
||||
d = read();
|
||||
|
||||
if((c & 0xC0) == 0x80 && (d & 0xC0) == 0x80)
|
||||
{
|
||||
return (char)(((b & 0x0F) << 12) | ((c & 0x3F) << 6) | (d & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà ñèìâîë â êîäèðîâêå UTF-8 ÍÀÇÀÄ.
|
||||
* Ïîñêîëüêó âîçìîæíà ñèòóàöèÿ, êîãäà òåêñò
|
||||
* ÍÅ â UTF-8 áóäåò ÷èòàòüñÿ êàê UTF-8,
|
||||
* UTFDataFormatException çäåñü íå èñïîëüçóåòñÿ.
|
||||
* Âìåñòî ýòîãî âîçâðàùàåòñÿ íóëåâîé ñèìâîë.
|
||||
*/
|
||||
public char readCharBackUTF() throws IOException
|
||||
{
|
||||
int b, c, d;
|
||||
|
||||
d = readBack();
|
||||
|
||||
if(d == -1)
|
||||
{
|
||||
return (char)-1;
|
||||
}
|
||||
|
||||
if((d & 0x80) == 0)
|
||||
{
|
||||
return (char)d;
|
||||
}
|
||||
else if((d & 0xC0) == 0x80)
|
||||
{
|
||||
c = readBack();
|
||||
|
||||
if((c & 0xE0) == 0xC0)
|
||||
{
|
||||
return (char)(((c & 0x1F) << 6) | (d & 0x3F));
|
||||
}
|
||||
else if((c & 0xC0) == 0x80)
|
||||
{
|
||||
b = readBack();
|
||||
|
||||
if((b & 0xF0) == 0xE0)
|
||||
{
|
||||
return (char)(((b & 0x0F) << 12) | ((c & 0x3F) << 6) | (d & 0x3F));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà ñèìâîë â êîäèðîâêå UTF-16 (Little Endian)
|
||||
*/
|
||||
public char readLeChar() throws IOException
|
||||
{
|
||||
return (char)(read() | (read() << 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà ñèìâîë â êîäèðîâêå UTF-16 (Little Endian) ÍÀÇÀÄ
|
||||
*/
|
||||
public char readLeCharBack() throws IOException
|
||||
{
|
||||
return (char)((readBack() << 8) | readBack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðî÷èòàòü èç ïîòîêà ìàêñèìóì count ñèìâîëîâ â êîäèðîâêå UTF-8
|
||||
*/
|
||||
public String readUTF(int count) throws IOException, UTFDataFormatException
|
||||
{
|
||||
String s = "";
|
||||
|
||||
for(int i = 0; i < count && available() > 0; i++)
|
||||
{
|
||||
s += readCharUTF();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðîïóñòèòü â ïîòîêå ìàêñèìóì count ñèìâîëîâ â êîäèðîâêå UTF-8.
|
||||
* Íå î÷åíü ÷¸òêî ïðîâåðÿåò ñîîòâåòñòâèå äàííûõ êîäèðîâêå.
|
||||
*/
|
||||
public int skipUTF(int count) throws IOException, UTFDataFormatException
|
||||
{
|
||||
byte b;
|
||||
int i = 0, r = 0;
|
||||
|
||||
while(i < count)
|
||||
{
|
||||
b = readByte();
|
||||
|
||||
if((b & 0x80) == 0)
|
||||
{
|
||||
r++;
|
||||
}
|
||||
else if((((int)b) & 0xE0) == 0xC0)
|
||||
{
|
||||
readByte();
|
||||
r += 2;
|
||||
}
|
||||
else if((((int)b) & 0xF0) == 0xE0)
|
||||
{
|
||||
readShort();
|
||||
r += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new UTFDataFormatException();
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[BufDataInputStream] " + s);
|
||||
// }
|
||||
}
|
163
src/com/vmx/BufDataOutputStream.java
Normal file
163
src/com/vmx/BufDataOutputStream.java
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* BufDataOutputStream.java
|
||||
*
|
||||
* Created on 24 Îêòÿáðü 2005 ã., 14:26
|
||||
*
|
||||
* To change this template, choose Tools | Template Manager
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package com.vmx;
|
||||
|
||||
import java.io.*;
|
||||
import javax.microedition.io.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Âèòàëèé
|
||||
*/
|
||||
public class BufDataOutputStream implements DataOutput
|
||||
{
|
||||
final byte [] buffer;
|
||||
int bmax, bpos;
|
||||
OutputStream os;
|
||||
|
||||
/** Creates a new instance of BufDataOutputStream */
|
||||
public BufDataOutputStream (int bufsize, OutputStream oos) throws IOException
|
||||
{
|
||||
if (bufsize <= 0)
|
||||
throw new IOException ("Buffer size must be greater than 0");
|
||||
bmax = bufsize;
|
||||
bpos = 0;
|
||||
buffer = new byte [bmax];
|
||||
os = oos;
|
||||
}
|
||||
|
||||
public void close () throws IOException
|
||||
{
|
||||
flush ();
|
||||
os.close ();
|
||||
}
|
||||
|
||||
public void flush () throws IOException
|
||||
{
|
||||
os.write (buffer, 0, bpos);
|
||||
bpos = 0;
|
||||
}
|
||||
|
||||
public void write (byte [] b, int off, int len) throws IOException
|
||||
{
|
||||
int rest = len, n, pos = off;
|
||||
while (rest > 0)
|
||||
{
|
||||
if (bpos >= bmax)
|
||||
flush ();
|
||||
n = rest;
|
||||
if (n > bmax-bpos)
|
||||
n = bmax-bpos;
|
||||
System.arraycopy (b, pos, buffer, bpos, n);
|
||||
pos += n;
|
||||
bpos += n;
|
||||
rest -= n;
|
||||
}
|
||||
}
|
||||
|
||||
public void write (byte [] b) throws IOException
|
||||
{
|
||||
write (b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write (int b) throws IOException
|
||||
{
|
||||
if (bpos >= bmax)
|
||||
flush ();
|
||||
buffer [bpos++] = (byte)b;
|
||||
}
|
||||
|
||||
public void writeBoolean (boolean b) throws IOException
|
||||
{
|
||||
byte bb = 0;
|
||||
if (b)
|
||||
bb = 1;
|
||||
write (bb);
|
||||
}
|
||||
|
||||
public void writeByte (int v) throws IOException
|
||||
{
|
||||
write (v);
|
||||
}
|
||||
|
||||
public void writeChar (int c) throws IOException
|
||||
{
|
||||
write ((c >> 8) & 0xFF);
|
||||
write (c & 0xFF);
|
||||
}
|
||||
|
||||
public void writeChars (String s) throws IOException
|
||||
{
|
||||
for (int i = 0; i < s.length (); i++)
|
||||
writeChar (s.charAt (i));
|
||||
}
|
||||
|
||||
public void writeDouble (double d) throws IOException
|
||||
{
|
||||
writeLong (Double.doubleToLongBits (d));
|
||||
}
|
||||
|
||||
public void writeFloat (float f) throws IOException
|
||||
{
|
||||
writeInt (Float.floatToIntBits (f));
|
||||
}
|
||||
|
||||
public void writeInt (int i) throws IOException
|
||||
{
|
||||
write ((i >> 24)&0xFF);
|
||||
write ((i >> 16)&0xFF);
|
||||
write ((i >> 8)&0xFF);
|
||||
write ((i )&0xFF);
|
||||
}
|
||||
|
||||
public void writeLong (long l) throws IOException
|
||||
{
|
||||
write ((int)((l >> 56)&0xFF));
|
||||
write ((int)((l >> 48)&0xFF));
|
||||
write ((int)((l >> 40)&0xFF));
|
||||
write ((int)((l >> 32)&0xFF));
|
||||
write ((int)((l >> 24)&0xFF));
|
||||
write ((int)((l >> 16)&0xFF));
|
||||
write ((int)((l >> 8)&0xFF));
|
||||
write ((int)((l )&0xFF));
|
||||
}
|
||||
|
||||
public void writeShort (int s) throws IOException
|
||||
{
|
||||
write ((s >> 8) & 0xFF);
|
||||
write (s & 0xFF);
|
||||
}
|
||||
|
||||
public void writeUTF (String s, boolean writeLength) throws IOException
|
||||
{
|
||||
flush ();
|
||||
if (!writeLength)
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream ();
|
||||
DataOutputStream dos = new DataOutputStream (baos);
|
||||
dos.writeUTF (s);
|
||||
dos.flush ();
|
||||
byte [] bytes = baos.toByteArray ();
|
||||
os.write (bytes, 2, bytes.length-2);
|
||||
dos.close ();
|
||||
}
|
||||
else
|
||||
{
|
||||
DataOutputStream dos = new DataOutputStream (os);
|
||||
dos.writeUTF (s);
|
||||
dos.flush ();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeUTF (String s) throws IOException
|
||||
{
|
||||
writeUTF (s, true);
|
||||
}
|
||||
}
|
209
src/com/vmx/FontWidthCache.java
Normal file
209
src/com/vmx/FontWidthCache.java
Normal file
@ -0,0 +1,209 @@
|
||||
package com.vmx;
|
||||
|
||||
import javax.microedition.lcdui.Font;
|
||||
import java.util.*;
|
||||
|
||||
public class FontWidthCache
|
||||
{
|
||||
protected static Vector fwc = new Vector();
|
||||
protected static Vector fwc_f = new Vector();
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü êýø øèðèíû äëÿ øðèôòà font
|
||||
*/
|
||||
public static FontWidthCache getCache(Font font)
|
||||
{
|
||||
for(int i = 0; i < fwc.size(); i++)
|
||||
{
|
||||
if(fwc_f.elementAt(i).equals(font))
|
||||
{
|
||||
return (FontWidthCache)fwc.elementAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
fwc_f.addElement(font);
|
||||
FontWidthCache f;
|
||||
fwc.addElement(f = new FontWidthCache(font));
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
protected Font font;
|
||||
protected byte caches[][];
|
||||
protected int hi, lo;
|
||||
|
||||
/**
|
||||
* Çàùèù¸ííûé êîíñòðóêòîð
|
||||
*/
|
||||
protected FontWidthCache(Font font)
|
||||
{
|
||||
this.font = font;
|
||||
caches = new byte[256][];
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
caches[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîäñ÷èòàòü (èëè âçÿòü èç êýøà) è âåðíóòü øèðèíó ñèìâîëà ch
|
||||
*/
|
||||
public int charWidth(char ch)
|
||||
{
|
||||
hi = (ch >> 8) & 0xFF;
|
||||
lo = (ch) & 0xFF;
|
||||
|
||||
if(caches[hi] == null)
|
||||
{
|
||||
caches[hi] = new byte[256];
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
caches[hi][i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(caches[hi][lo] == -1)
|
||||
{
|
||||
caches[hi][lo] = (byte)font.charWidth(ch);
|
||||
}
|
||||
|
||||
return ((int)caches[hi][lo]) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîäñ÷èòàòü è âåðíóòü øèðèíó ñòðîêè s
|
||||
*/
|
||||
public int stringWidth(String s)
|
||||
{
|
||||
int l = s.length(), i, r;
|
||||
|
||||
for(r = 0, i = 0; i < l; i++)
|
||||
{
|
||||
r += charWidth(s.charAt(i));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîäñ÷èòàòü è âåðíóòü âûñîòó ñòðîêè s
|
||||
*/
|
||||
public int stringHeight(String s)
|
||||
{
|
||||
if(s == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i, l = s.length(), c;
|
||||
|
||||
for(c = 1, i = 0; i < l; i++)
|
||||
{
|
||||
if(s.charAt(i) == '\n')
|
||||
{
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
return c * font.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ðàçáèòü ñòðîêó, ÷òîáû îíà âïèñûâàëàñü ïî øèðèíå
|
||||
* â ýêðàí øèðèíîé maxStrWidth
|
||||
*/
|
||||
public String[] splitString(String text, int maxStrWidth)
|
||||
{
|
||||
int afterspace;
|
||||
int cut;
|
||||
int strwidth;
|
||||
int strstart, strend;
|
||||
boolean wordWrap;
|
||||
int i;
|
||||
char c = 0;
|
||||
|
||||
Vector v = new Vector();
|
||||
|
||||
strstart = 0;
|
||||
i = 0;
|
||||
|
||||
while(i < text.length())
|
||||
{
|
||||
afterspace = -1;
|
||||
cut = 0;
|
||||
strwidth = 0;
|
||||
wordWrap = true;
|
||||
|
||||
while(strwidth < maxStrWidth)
|
||||
{
|
||||
/* áåðåì ñèìâîë */
|
||||
if(i < text.length())
|
||||
{
|
||||
c = text.charAt(i++);
|
||||
}
|
||||
else
|
||||
{
|
||||
wordWrap = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* åñëè ýòî íå ñèìâîë êîíöà ñòðîêè è íå êîíåö ôàéëà,
|
||||
òî äîáàâëÿåì åãî ê ñòðîêå è îáíîâëÿåì åå äëèíó.
|
||||
 ïðîòèâíîì ñëó÷àå ñîîáùàåì, ÷òî ïåðåíîñ ñëîâ íå òðåáóåòñÿ è âûõîäèì èç öèêëà */
|
||||
if(c == '\n')
|
||||
{
|
||||
wordWrap = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
strwidth += charWidth(c);
|
||||
}
|
||||
|
||||
if(c == ' ')
|
||||
{
|
||||
afterspace = 0;
|
||||
}
|
||||
else if(afterspace >= 0)
|
||||
{
|
||||
afterspace++;
|
||||
}
|
||||
}
|
||||
|
||||
if(wordWrap && afterspace >= 0)
|
||||
{
|
||||
cut += afterspace;
|
||||
}
|
||||
else if(strwidth > maxStrWidth && c != ' ')
|
||||
{
|
||||
cut = 1;
|
||||
}
|
||||
|
||||
if(cut > 0)
|
||||
{
|
||||
i -= cut;
|
||||
}
|
||||
|
||||
v.addElement(text.substring(strstart, i));
|
||||
|
||||
strstart = i;
|
||||
}
|
||||
|
||||
if(strstart < i)
|
||||
{
|
||||
v.addElement(text.substring(strstart, i));
|
||||
}
|
||||
|
||||
String[] res = new String[v.size()];
|
||||
v.copyInto(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[FontWidthCache] " + s);
|
||||
// }
|
||||
}
|
9
src/com/vmx/GenericFileConnection.java
Normal file
9
src/com/vmx/GenericFileConnection.java
Normal file
@ -0,0 +1,9 @@
|
||||
package com.vmx;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface GenericFileConnection
|
||||
{
|
||||
public InputStream openInputStream () throws IOException;
|
||||
public void close ();
|
||||
}
|
257
src/com/vmx/InputStreamDecoder.java
Normal file
257
src/com/vmx/InputStreamDecoder.java
Normal file
@ -0,0 +1,257 @@
|
||||
package com.vmx;
|
||||
|
||||
import java.io.*;
|
||||
import com.one.*;
|
||||
import filemanager.main;
|
||||
|
||||
public class InputStreamDecoder
|
||||
{
|
||||
BufDataInputStream bdis;
|
||||
int enc;
|
||||
|
||||
/**
|
||||
* Êîíñòðóêòîð
|
||||
*/
|
||||
public InputStreamDecoder(BufDataInputStream bdis, int enc) // throws UnsupportedEncodingException
|
||||
{
|
||||
this.enc = enc;
|
||||
this.bdis = bdis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñîçäàòü InputStreamDecoder äëÿ äåêîäèðîâàíèÿ ðåñóðñà resname
|
||||
*/
|
||||
public static InputStreamDecoder getResourceDecoder(String resname) throws IOException
|
||||
{
|
||||
RandomAccessInputStream is = new BufferedInputStream(main.loader.getResourceAsStream(resname));
|
||||
int encoding = detectEncoding(is, StringEncoder.ENC_DEFAULT);
|
||||
return new InputStreamDecoder(new BufDataInputStream(is), encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Îñîçíàòü, â êàêîé êîäèðîâêå is.
|
||||
* Åñëè â UTF (åñòü BOM ñèãíàòóðà), âåðíóòü ENC_UTFx.
|
||||
* Åñëè íåò, òî âåðíóòü ïåðåäàííóþ êîäèðîâêó (ïî óìîë÷àíèþ).
|
||||
*/
|
||||
public static int detectEncoding(RandomAccessInputStream is, int defenc) throws IOException
|
||||
{
|
||||
is.mark(4);
|
||||
|
||||
/*
|
||||
* BOM ñèãíàòóðà ïðåäñòàâëÿåò ñîáîé ñèìâîë
|
||||
* U+FEFF (íåðàçðûâíûé ïðîáåë ñ íóëåâîé øèðèíîé),
|
||||
* çàïèñàíûé òåì èëè èíûì îáðàçîì.
|
||||
* Ñîîòâåòñòâåííî çäåñü ïðîâåðÿåòñÿ íàëè÷èå
|
||||
* â íà÷àëå ïîòîêà ýòîãî ñèìâîëà â ôîðìàòå
|
||||
* UTF-16 Big Endian (0xFEFF),
|
||||
* UTF-16 Little Endian (0xFFFE) è
|
||||
* UTF-8 (0xEFBBBF)
|
||||
*/
|
||||
|
||||
int a, b, c;
|
||||
|
||||
if(is.available() >= 2)
|
||||
{
|
||||
a = is.read();
|
||||
b = is.read();
|
||||
|
||||
if(a == 0xFE && b == 0xFF)
|
||||
{
|
||||
return StringEncoder.ENC_UTF16N;
|
||||
}
|
||||
else if(a == 0xFF && b == 0xFE)
|
||||
{
|
||||
return StringEncoder.ENC_UTF16L;
|
||||
}
|
||||
else if(is.available() >= 1)
|
||||
{
|
||||
c = is.read();
|
||||
|
||||
if(a == 0xEF && b == 0xBB && c == 0xBF)
|
||||
{
|
||||
return StringEncoder.ENC_UTF8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ìû ñ÷èòàëè íå ñèãíàòóðó, âîçâðàùàåìñÿ íàçàä */
|
||||
is.reset();
|
||||
return defenc; // StringEncoder.ENC_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñ÷èòàòü ñèìâîë
|
||||
*/
|
||||
public char readChar() throws IOException
|
||||
{
|
||||
if(bdis.available() > 0)
|
||||
{
|
||||
if(enc >= 0)
|
||||
{
|
||||
return StringEncoder.decodeChar(bdis.read(), enc);
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF8)
|
||||
{
|
||||
return bdis.readCharUTF();
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16N)
|
||||
{
|
||||
return bdis.readChar();
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16L)
|
||||
{
|
||||
return bdis.readLeChar();
|
||||
}
|
||||
}
|
||||
|
||||
return (char)-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñ÷èòàòü ñèìâîë íàçàä
|
||||
*/
|
||||
public char readCharBack() throws IOException
|
||||
{
|
||||
if(bdis.tell() > 0)
|
||||
{
|
||||
if(enc >= 0)
|
||||
{
|
||||
return StringEncoder.decodeChar(bdis.readBack(), enc);
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF8)
|
||||
{
|
||||
return bdis.readCharBackUTF();
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16N)
|
||||
{
|
||||
return bdis.readCharBack();
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16L)
|
||||
{
|
||||
return bdis.readLeCharBack();
|
||||
}
|
||||
}
|
||||
|
||||
return (char)-1;
|
||||
}
|
||||
|
||||
public int gotoLineNum(int linenum) throws IOException
|
||||
{
|
||||
bdis.seek(0);
|
||||
detectEncoding(bdis, enc);
|
||||
|
||||
int currline = 1;
|
||||
|
||||
while(bdis.available() > 0 && currline < linenum)
|
||||
{
|
||||
if(readChar() == '\n')
|
||||
{
|
||||
currline++;
|
||||
}
|
||||
}
|
||||
|
||||
return bdis.tell();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîèñê òåêñòà â ïîòîêå.
|
||||
* Åñëè startIndex >= 0, òî ïîèñê âåäåòñÿ ñî startIndex, èíà÷å ñ òåêóùåé ïîçèöèè.
|
||||
* Åñëè endIndex >= 0, òî ïîèñê èäåò ìàêñèìóì äî endIndex, èíà÷å äî êîíöà ïîòîêà.
|
||||
* Åñëè ignoreCase = true, òî ïîèñê âåäåòñÿ áåç ó÷åòà ðåãèñòðà.
|
||||
* Ïîñëå ïîèñêà ïîòîê âîçâðàùàåòñÿ íà ïðåæíþþ ïîçèöèþ.
|
||||
*/
|
||||
public int indexOf(String text, int startIndex, int endIndex, boolean ignoreCase) throws IOException
|
||||
{
|
||||
int prevpos = bdis.tell();
|
||||
|
||||
if(startIndex >= 0)
|
||||
{
|
||||
bdis.seek(startIndex);
|
||||
}
|
||||
|
||||
char[] data;
|
||||
|
||||
if(ignoreCase)
|
||||
{
|
||||
data = text.toLowerCase().toCharArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
data = text.toCharArray();
|
||||
}
|
||||
|
||||
int end = data.length;
|
||||
int index = -1;
|
||||
int i = 0;
|
||||
|
||||
char c;
|
||||
|
||||
while(bdis.available() > 0)
|
||||
{
|
||||
if(ignoreCase)
|
||||
{
|
||||
c = Character.toLowerCase(readChar());
|
||||
}
|
||||
else
|
||||
{
|
||||
c = readChar();
|
||||
}
|
||||
|
||||
if(c == data[i])
|
||||
{
|
||||
if(index < 0)
|
||||
{
|
||||
index = bdis.tell() - 1;
|
||||
}
|
||||
|
||||
if(++i >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(c == data[0])
|
||||
{
|
||||
index = bdis.tell() - 1;
|
||||
i = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = -1;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if(endIndex >= 0 && bdis.tell() >= endIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bdis.seek(prevpos);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñêîëüêî åùå ìîæíî ñ÷èòàòü èç ýòîãî ïîòîêà.
|
||||
* Âîçâðàùàåòñÿ êîëè÷åñòâî áàéò, à íå ñèìâîëîâ,
|
||||
* ïîýòîìó ïðè èñïîëüçîâàíèè UTF-8 ýòî çíà÷åíèå
|
||||
* èìååò ñìûñë ïðîâåðÿòü ëèøü íà íåðàâåíñòâî 0.
|
||||
*/
|
||||
public int available() throws IOException
|
||||
{
|
||||
return bdis.available();
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàêðûòèå äåêîäåðà âìåñòå ñ ïîòîêîì, íà êîòîðîì îí îñíîâàí
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
bdis.close();
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[InputStreamDecoder] " + s);
|
||||
// }
|
||||
}
|
112
src/com/vmx/IntVector.java
Normal file
112
src/com/vmx/IntVector.java
Normal file
@ -0,0 +1,112 @@
|
||||
package com.vmx;
|
||||
|
||||
public class IntVector
|
||||
{
|
||||
protected int[] array;
|
||||
protected int count;
|
||||
public final int arrplus;
|
||||
|
||||
public IntVector()
|
||||
{
|
||||
array = new int[0];
|
||||
count = 0;
|
||||
arrplus = 32;
|
||||
}
|
||||
|
||||
public IntVector(int capacity)
|
||||
{
|
||||
array = new int[capacity];
|
||||
count = 0;
|
||||
arrplus = 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Óäàëèòü ýëåìåíòû ñ first ïî first + n - 1
|
||||
*/
|
||||
public void remove(int first, int n)
|
||||
{
|
||||
if(first < 0 || first >= count || n <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(first + n >= count)
|
||||
{
|
||||
count = first;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = first; i < count - n; i++)
|
||||
{
|
||||
array[i] = array[i + n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Äîáàâèòü â array çíà÷åíèå n
|
||||
*/
|
||||
public void add(int el)
|
||||
{
|
||||
if(count >= array.length)
|
||||
{
|
||||
int[] newarray = new int[array.length + arrplus];
|
||||
System.arraycopy(array, 0, newarray, 0, array.length);
|
||||
array = newarray;
|
||||
}
|
||||
|
||||
array[count++] = el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü ýëåìåíò íîìåð index
|
||||
*/
|
||||
public int get(int index)
|
||||
{
|
||||
return array[index];
|
||||
}
|
||||
|
||||
public void set(int index, int value)
|
||||
{
|
||||
if(index >= 0 && index < count)
|
||||
{
|
||||
array[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Íàéòè value â âåêòîðå
|
||||
*/
|
||||
public int find(int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
if(array[i] == value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i >= count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü ðàçìåð
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
public void copyInto(int[] dest)
|
||||
{
|
||||
System.arraycopy(array, 0, dest, 0, count);
|
||||
}
|
||||
}
|
95
src/com/vmx/OutputStreamEncoder.java
Normal file
95
src/com/vmx/OutputStreamEncoder.java
Normal file
@ -0,0 +1,95 @@
|
||||
package com.vmx;
|
||||
|
||||
import java.io.*;
|
||||
import com.one.*;
|
||||
import filemanager.main;
|
||||
|
||||
public class OutputStreamEncoder
|
||||
{
|
||||
OutputStream os;
|
||||
int enc;
|
||||
|
||||
/**
|
||||
* Êîíñòðóêòîð
|
||||
*/
|
||||
public OutputStreamEncoder(OutputStream os, int enc) // throws UnsupportedEncodingException
|
||||
{
|
||||
this.enc = enc;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàïèñàòü BOM ñèãíàòóðó
|
||||
*/
|
||||
public void writeBOM() throws IOException
|
||||
{
|
||||
if(enc == StringEncoder.ENC_UTF8)
|
||||
{
|
||||
os.write(0xEF);
|
||||
os.write(0xBB);
|
||||
os.write(0xBF);
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16N)
|
||||
{
|
||||
os.write(0xFE);
|
||||
os.write(0xFF);
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16L)
|
||||
{
|
||||
os.write(0xFF);
|
||||
os.write(0xFE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàïèñàòü ñèìâîë
|
||||
*/
|
||||
public void writeChar(char ch) throws IOException
|
||||
{
|
||||
if(enc >= 0)
|
||||
{
|
||||
os.write(StringEncoder.encodeChar(ch, enc));
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF8)
|
||||
{
|
||||
if(ch <= 0x7F)
|
||||
{
|
||||
os.write(ch);
|
||||
}
|
||||
else if(ch <= 0x7FF)
|
||||
{
|
||||
os.write(0xC0 | ((ch >> 6) & 0x1F));
|
||||
os.write(0x80 | (ch & 0x3F));
|
||||
}
|
||||
else
|
||||
{
|
||||
os.write(0xE0 | ((ch >> 12) & 0xF));
|
||||
os.write(0x80 | ((ch >> 6) & 0x3F));
|
||||
os.write(0x80 | (ch & 0x3F));
|
||||
}
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16N)
|
||||
{
|
||||
os.write(ch >>> 8);
|
||||
os.write(ch & 0xFF);
|
||||
}
|
||||
else if(enc == StringEncoder.ENC_UTF16L)
|
||||
{
|
||||
os.write(ch & 0xFF);
|
||||
os.write(ch >>> 8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàêðûòèå êîäèðîâùèêà âìåñòå ñ ïîòîêîì, íà êîòîðîì îí îñíîâàí
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
os.close();
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[OutputStreamEncoder] " + s);
|
||||
// }
|
||||
}
|
9
src/com/vmx/ProgressCallback.java
Normal file
9
src/com/vmx/ProgressCallback.java
Normal file
@ -0,0 +1,9 @@
|
||||
package com.vmx;
|
||||
|
||||
public interface ProgressCallback
|
||||
{
|
||||
public void setMax(int max);
|
||||
public void setProgress(int progress);
|
||||
public void progress(int plus);
|
||||
public void setText(String text);
|
||||
}
|
337
src/com/vmx/StringEncoder.java
Normal file
337
src/com/vmx/StringEncoder.java
Normal file
@ -0,0 +1,337 @@
|
||||
package com.vmx;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Vector;
|
||||
import filemanager.main;
|
||||
import com.one.*;
|
||||
|
||||
public class StringEncoder
|
||||
{
|
||||
/** Òàáëèöû êîäèðîâîê */
|
||||
protected static char[][] codepage;
|
||||
|
||||
/** Íàçâàíèÿ êîäèðîâîê */
|
||||
public static String[] encodings;
|
||||
public static String[] encnames;
|
||||
|
||||
public static final int ENC_UTF8 = -1;
|
||||
public static final int ENC_UTF16N = -2; // Network Order
|
||||
public static final int ENC_UTF16L = -3; // Little Endian
|
||||
|
||||
public static final String ENC_NAME_UTF8 = "Unicode UTF-8";
|
||||
public static final String ENC_NAME_UTF16N = "Unicode UTF-16N";
|
||||
public static final String ENC_NAME_UTF16L = "Unicode UTF-16L";
|
||||
|
||||
public static int ENC_DEFAULT = 0;
|
||||
|
||||
/**
|
||||
* Êîíñòðóêòîð. Ïóñòîé.
|
||||
*/
|
||||
public StringEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàãðóçèòü êîäîâûå òàáëèöû
|
||||
*/
|
||||
public static void loadCodePages() throws IOException
|
||||
{
|
||||
Vector v = new Vector();
|
||||
String defenc = null;
|
||||
|
||||
RandomAccessInputStream is = new BufferedInputStream(main.loader.getResourceAsStream("/enc/enc.ini"));
|
||||
|
||||
int enc = InputStreamDecoder.detectEncoding(is, ENC_UTF8);
|
||||
//int pos = is.tell();
|
||||
//is.reset();
|
||||
|
||||
BufDataInputStream bdis = new BufDataInputStream(is);
|
||||
//bdis.seek(pos);
|
||||
|
||||
InputStreamDecoder isd = new InputStreamDecoder(bdis, enc);
|
||||
|
||||
IniFile ini = new IniFile(isd);
|
||||
IniRecord record = null;
|
||||
|
||||
while((record = ini.getNextRecord()) != null)
|
||||
{
|
||||
if(defenc == null && record.key.equals("DEFAULT"))
|
||||
{
|
||||
defenc = record.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.addElement(record);
|
||||
}
|
||||
}
|
||||
|
||||
ini.close();
|
||||
|
||||
codepage = new char[v.size()][128];
|
||||
encnames = new String[v.size()];
|
||||
encodings = new String[v.size()];
|
||||
|
||||
DataInputStream dis = null;
|
||||
|
||||
for(int i = 0; i < v.size(); i++)
|
||||
{
|
||||
encnames[i] = ((IniRecord)v.elementAt(i)).key;
|
||||
encodings[i] = ((IniRecord)v.elementAt(i)).value;
|
||||
|
||||
try
|
||||
{
|
||||
dis = new DataInputStream(main.loader.getResourceAsStream("/enc/" + encodings[i] + ".enc"));
|
||||
|
||||
for(int j = 0; j < 128; j++)
|
||||
{
|
||||
codepage[i][j] = dis.readChar();
|
||||
}
|
||||
|
||||
dis.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
main.showErrMsg(39, e);
|
||||
}
|
||||
}
|
||||
|
||||
if(defenc != null)
|
||||
{
|
||||
ENC_DEFAULT = findEncoding(defenc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Êîäèðîâàòü ñòðîêó s â êîäèðîâêó enc
|
||||
*/
|
||||
public static byte[] encodeString(String s, int enc)
|
||||
{
|
||||
if(enc >= 0)
|
||||
{
|
||||
byte[] bs = new byte[s.length()];
|
||||
|
||||
for(int i = 0; i < s.length(); i++)
|
||||
{
|
||||
bs[i] = (byte)encodeChar(s.charAt(i), enc);
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
else if(enc == ENC_UTF8)
|
||||
{
|
||||
try
|
||||
{
|
||||
return s.getBytes("UTF-8");
|
||||
}
|
||||
catch(UnsupportedEncodingException x)
|
||||
{
|
||||
main.showErrMsg(40, x);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if(enc == ENC_UTF16N)
|
||||
{
|
||||
byte[] bs = new byte[s.length() * 2];
|
||||
char c;
|
||||
|
||||
for(int i = 0; i < s.length(); i++)
|
||||
{
|
||||
c = s.charAt(i);
|
||||
|
||||
bs[i * 2] = (byte)(c >>> 8);
|
||||
bs[i * 2 + 1] = (byte)(c & 0xFF);
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
else if(enc == ENC_UTF16L)
|
||||
{
|
||||
byte[] bs = new byte[s.length() * 2];
|
||||
char c;
|
||||
|
||||
for(int i = 0; i < s.length(); i++)
|
||||
{
|
||||
c = s.charAt(i);
|
||||
|
||||
bs[i * 2] = (byte)(c & 0xFF);
|
||||
bs[i * 2 + 1] = (byte)(c >>> 8);
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü äëèíó ñòðîêè s â áàéòàõ â êîäèðîâêå enc
|
||||
*/
|
||||
public static int getEncodedLength(String s, int enc)
|
||||
{
|
||||
if(enc >= 0)
|
||||
{
|
||||
return s.length();
|
||||
}
|
||||
else if(enc == ENC_UTF8)
|
||||
{
|
||||
try
|
||||
{
|
||||
return s.getBytes("UTF-8").length;
|
||||
}
|
||||
catch(UnsupportedEncodingException x)
|
||||
{
|
||||
main.showErrMsg(41, x);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if(enc == ENC_UTF16N || enc == ENC_UTF16L)
|
||||
{
|
||||
return s.length() * 2;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Äåêîäèðîâàòü ó÷àñòîê ìàññèâà b äëèíîé len ñî ñìåùåíèÿ off èç êîäèðîâêè enc
|
||||
*/
|
||||
public static String decodeString(byte[] bs, int off, int len, int enc)
|
||||
{
|
||||
if(enc >= 0)
|
||||
{
|
||||
String s = "";
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
s += decodeChar(bs[off + i], enc);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
else if(enc == ENC_UTF8)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new String(bs, off, len, "UTF-8");
|
||||
}
|
||||
catch(UnsupportedEncodingException x)
|
||||
{
|
||||
main.showErrMsg(42, x);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if(enc == ENC_UTF16N)
|
||||
{
|
||||
String s = "";
|
||||
|
||||
if(len % 2 != 0)
|
||||
{
|
||||
len--;
|
||||
}
|
||||
|
||||
for(int i = off; i < off + len; i += 2)
|
||||
{
|
||||
s += (char)(((char)bs[i] << 8) | bs[i + 1]);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
else if(enc == ENC_UTF16L)
|
||||
{
|
||||
String s = "";
|
||||
|
||||
if(len % 2 != 0)
|
||||
{
|
||||
len--;
|
||||
}
|
||||
|
||||
for(int i = off; i < off + len; i += 2)
|
||||
{
|
||||
s += (char)(((char)bs[i + 1] << 8) | bs[i]);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Äåêîäèðîâàòü ñèìâîë
|
||||
*/
|
||||
public static char decodeChar(int b, int enc)
|
||||
{
|
||||
if(b < 0)
|
||||
{
|
||||
b += 256;
|
||||
}
|
||||
|
||||
if(b < 128)
|
||||
{
|
||||
return (char)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
return codepage[enc][b - 128];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Êîäèðîâàòü ñèìâîë
|
||||
*/
|
||||
public static int encodeChar(char ch, int enc)
|
||||
{
|
||||
if(ch < 128)
|
||||
{
|
||||
return (int)ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 128; i++)
|
||||
{
|
||||
if(ch == codepage[enc][i])
|
||||
{
|
||||
return i + 128;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)' '; //0x3F; // '?' - íåèçâåñòíûé ñèìâîë
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïåðåêîäèðîâàòü äàííûå èç ïîòîêà is èç êîäèðîâêè srcenc
|
||||
* â ïîòîê os â êîäèðîâêó dstenc
|
||||
*/
|
||||
public static void transcode(InputStream is, OutputStream os, int srcenc, int dstenc) throws IOException
|
||||
{
|
||||
if(srcenc >= 0 && dstenc >= 0)
|
||||
{
|
||||
while(is.available() > 0)
|
||||
{
|
||||
os.write(encodeChar(decodeChar(is.read(), srcenc), dstenc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü íîìåð êîäèðîâêè enc
|
||||
*/
|
||||
public static int findEncoding(String enc)
|
||||
{
|
||||
for(int i = 0; i < encodings.length; i++)
|
||||
{
|
||||
if(encodings[i].equals(enc))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[StringEncoder] " + s);
|
||||
// }
|
||||
}
|
177
src/com/vmx/gkcCanvas.java
Normal file
177
src/com/vmx/gkcCanvas.java
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* gkcCanvas.java
|
||||
*
|
||||
* Get Key Codes canvas
|
||||
* (c) 2006, Âèòàëèé Ôèëèïïîâ [VMX]
|
||||
*/
|
||||
|
||||
package com.vmx;
|
||||
|
||||
import filemanager.main;
|
||||
import java.util.Vector;
|
||||
import com.one.*;
|
||||
|
||||
public abstract class gkcCanvas extends PaintableObject
|
||||
{
|
||||
public static int KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_FIRE;
|
||||
public static int KEY_LSK, KEY_RSK, KEY_BACK, KEY_CLEAR, KEY_DIAL, KEY_CANCEL;
|
||||
public static final int KEY_INVALID = 0;
|
||||
|
||||
public static final int KEY_HELD = 0x80000000;
|
||||
|
||||
public static String[] platforms;
|
||||
public static int[][] keycodes;
|
||||
public static final int KEYS_COUNT = 11;
|
||||
|
||||
public static int getDisplayWidth()
|
||||
{
|
||||
return getRenderer().getWidth();
|
||||
}
|
||||
|
||||
public static int getDisplayHeight()
|
||||
{
|
||||
return getRenderer().getHeight();
|
||||
}
|
||||
|
||||
// public Canvas getRenderer()
|
||||
// {
|
||||
// return this;
|
||||
// }
|
||||
|
||||
public gkcCanvas()
|
||||
{
|
||||
}
|
||||
|
||||
public static void readKeyMapList()
|
||||
{
|
||||
Vector vplatform = new Vector();
|
||||
Vector vcode = new Vector();
|
||||
|
||||
try
|
||||
{
|
||||
IniFile ini = new IniFile("/config/keymap.ini");
|
||||
IniRecord record = null;
|
||||
|
||||
int[] array;
|
||||
String s;
|
||||
int index;
|
||||
|
||||
while((record = ini.getNextRecord()) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
vplatform.addElement(record.key);
|
||||
s = record.value;
|
||||
array = new int[KEYS_COUNT];
|
||||
|
||||
for(int i = 0; i < KEYS_COUNT; i++)
|
||||
{
|
||||
index = s.indexOf(',');
|
||||
|
||||
if(index > 0)
|
||||
{
|
||||
array[i] = Integer.parseInt(s.substring(0, index).trim());
|
||||
s = s.substring(index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
array[i] = Integer.parseInt(s.trim());
|
||||
}
|
||||
}
|
||||
|
||||
vcode.addElement(array);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
ini.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
main.showErrMsg(0, e);
|
||||
}
|
||||
|
||||
platforms = new String[vplatform.size()];
|
||||
vplatform.copyInto(platforms);
|
||||
|
||||
keycodes = new int[vcode.size()][];
|
||||
vcode.copyInto(keycodes);
|
||||
}
|
||||
|
||||
public static void setKeyMap(int plindex)
|
||||
{
|
||||
if(plindex < 0 || plindex >= keycodes.length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* ïëàòôîðìî - íåçàâèñèìûå êîäû */
|
||||
//KEY_LEFT = tc.getKeyCode(LEFT);
|
||||
//KEY_RIGHT = tc.getKeyCode(RIGHT);
|
||||
//KEY_UP = tc.getKeyCode(UP);
|
||||
//KEY_DOWN = tc.getKeyCode(DOWN);
|
||||
//KEY_FIRE = tc.getKeyCode(FIRE);
|
||||
|
||||
KEY_UP = keycodes[plindex][0];
|
||||
KEY_DOWN = keycodes[plindex][1];
|
||||
KEY_LEFT = keycodes[plindex][2];
|
||||
KEY_RIGHT = keycodes[plindex][3];
|
||||
KEY_FIRE = keycodes[plindex][4];
|
||||
|
||||
/* ïëàòôîðìî - çàâèñèìûå êîäû */
|
||||
KEY_LSK = keycodes[plindex][5];
|
||||
KEY_RSK = keycodes[plindex][6];
|
||||
KEY_DIAL = keycodes[plindex][7];
|
||||
KEY_CANCEL = keycodes[plindex][8];
|
||||
KEY_BACK = keycodes[plindex][9];
|
||||
KEY_CLEAR = keycodes[plindex][10];
|
||||
}
|
||||
|
||||
/**
|
||||
* Óñòàíàâëèâàåì âåçäå àâòîìàòè÷åñêóþ
|
||||
* îáðàáîòêó ïîâòîðåíèé êàê íàæàòèé.
|
||||
*/
|
||||
protected void keyRepeated(int keyCode)
|
||||
{
|
||||
keyPressed(keyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïðîâåðêà, ÿâëÿåòñÿ ëè íàæàòàÿ êëàâèøà öèôðîâîé.
|
||||
*/
|
||||
public static boolean isKeyNum(int keyCode)
|
||||
{
|
||||
keyCode &= ~KEY_HELD;
|
||||
return (keyCode >= KEY_NUM0 && keyCode <= KEY_NUM9) || keyCode == KEY_STAR || keyCode == KEY_POUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Òðàíñëèðîâàòü íàæàòèÿ öèôðîâûõ êëàâèø
|
||||
* ê íàæàòèÿì äæîéñòèêà.
|
||||
*/
|
||||
public static int translateKey(int keyCode)
|
||||
{
|
||||
switch(keyCode)
|
||||
{
|
||||
case KEY_NUM2:
|
||||
return KEY_UP;
|
||||
|
||||
case KEY_NUM4:
|
||||
return KEY_LEFT;
|
||||
|
||||
case KEY_NUM5:
|
||||
return KEY_FIRE;
|
||||
|
||||
case KEY_NUM6:
|
||||
return KEY_RIGHT;
|
||||
|
||||
case KEY_NUM8:
|
||||
return KEY_DOWN;
|
||||
|
||||
default:
|
||||
return keyCode;
|
||||
}
|
||||
}
|
||||
}
|
14
src/config/ext.ini
Normal file
14
src/config/ext.ini
Normal file
@ -0,0 +1,14 @@
|
||||
0 = .mid .amr .wav .aac .mp3 .imy .m4a .xmf .awb .midi .wma ; SOUND
|
||||
1 = .jpg .jpe .gif .png .bmx .jpeg .wbmp .ico .bmp ; PICTURE
|
||||
2 = .3gp .mp4 .m4v .wmv .rm ; VIDEO
|
||||
3 = .j .txt .jad .log .ini .inf .cdf .xml .col .java .jcc ; TEXT
|
||||
4 = .zip .jar .sdt .scs .nth ; ZIP
|
||||
5 = .tmo .vcs .vnt ; TMO
|
||||
6 = .mod .xm .s3m ; SOUND_MODULE
|
||||
7 = .pak .res ; PAK
|
||||
8 = .gz .gzip ; GZIP
|
||||
9 = .apl ; AUDIO_PLAYLIST
|
||||
10 = .vpl ; VIDEO_PLAYLIST
|
||||
11 = .mcs .ths ; COLOR_SCHEME
|
||||
12 = .tpl ; TEMPLATE
|
||||
13 = .mvi ; VECTOR_PICTURE
|
3
src/config/keymap.ini
Normal file
3
src/config/keymap.ini
Normal file
@ -0,0 +1,3 @@
|
||||
Siemens = -59, -60, -61, -62, -26, -1, -4, -11, -12, 0, 0
|
||||
Sony Ericsson = -1, -2, -3, -4, -5, -6, -7, 0, 0, -10, -8
|
||||
Motorola = -1, -6, -2, -5, -20, -21, -22, -10, 0, 0, 0
|
19
src/config/mime.ini
Normal file
19
src/config/mime.ini
Normal file
@ -0,0 +1,19 @@
|
||||
; audio
|
||||
.mid = audio/midi
|
||||
.midi = audio/midi
|
||||
.kar = audio/midi
|
||||
.imy = audio/imy
|
||||
.bas = audio/bas
|
||||
.wav = audio/x-wav
|
||||
.wave = audio/x-wav
|
||||
.amr = audio/amr
|
||||
.mp3 = audio/mpeg
|
||||
.m4a = audio/m4a
|
||||
.aac = audio/aac
|
||||
.wma = audio/x-ms-wma
|
||||
|
||||
; video
|
||||
.3gp = video/3gpp
|
||||
.mp4 = video/mp4
|
||||
.wmv = video/x-ms-wmv
|
||||
.mpeg = video/mpeg
|
BIN
src/enc/cp1251.enc
Normal file
BIN
src/enc/cp1251.enc
Normal file
Binary file not shown.
BIN
src/enc/cp1252.enc
Normal file
BIN
src/enc/cp1252.enc
Normal file
Binary file not shown.
BIN
src/enc/cp437.enc
Normal file
BIN
src/enc/cp437.enc
Normal file
Binary file not shown.
BIN
src/enc/cp866.enc
Normal file
BIN
src/enc/cp866.enc
Normal file
Binary file not shown.
9
src/enc/enc.ini
Normal file
9
src/enc/enc.ini
Normal file
@ -0,0 +1,9 @@
|
||||
Windows Latin = cp1252
|
||||
Windows Cyrillic = cp1251
|
||||
DOS Latin = cp437
|
||||
DOS Cyrillic = cp866
|
||||
KOI-8 Russian = koi8r
|
||||
|
||||
; Internal resource encoding
|
||||
; If not present, first one will be used
|
||||
DEFAULT = cp1251
|
BIN
src/enc/koi8r.enc
Normal file
BIN
src/enc/koi8r.enc
Normal file
Binary file not shown.
78
src/filemanager/Accelerometer.java
Normal file
78
src/filemanager/Accelerometer.java
Normal file
@ -0,0 +1,78 @@
|
||||
package filemanager;
|
||||
|
||||
import javax.microedition.sensor.*;
|
||||
import javax.microedition.io.Connector;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Accelerometer
|
||||
{
|
||||
protected int[] channels = new int[3];
|
||||
protected String[] channelNames = new String[3];
|
||||
protected SensorConnection sensor;
|
||||
|
||||
public Accelerometer() throws ClassNotFoundException, IOException
|
||||
{
|
||||
Class asclass = null;
|
||||
|
||||
try
|
||||
{
|
||||
asclass = Class.forName("javax.microedition.sensor.SensorManager");
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
}
|
||||
|
||||
if(asclass == null)
|
||||
{
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
|
||||
SensorInfo[] info = SensorManager.findSensors("acceleration", null);
|
||||
|
||||
if(info == null || info.length == 0)
|
||||
{
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
|
||||
ChannelInfo[] ci = info[0].getChannelInfos();
|
||||
|
||||
for(int r = 0; r < ci.length; r++)
|
||||
{
|
||||
channelNames[r] = ci[r].getName();
|
||||
}
|
||||
|
||||
sensor = (SensorConnection)Connector.open(info[0].getUrl());
|
||||
}
|
||||
|
||||
public int getDelta(int channel, int threshold)
|
||||
{
|
||||
int delta = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Data[] data = sensor.getData(1, -1, false, false, false);
|
||||
int drx = 0;
|
||||
|
||||
for(int i = 0; i < data.length; i++)
|
||||
{
|
||||
if(data[i].getChannelInfo().getName().equals(channelNames[channel]))
|
||||
{
|
||||
drx = data[i].getIntValues()[0];
|
||||
}
|
||||
}
|
||||
|
||||
delta = channels[channel] - drx;
|
||||
channels[channel] = drx;
|
||||
|
||||
if(Math.abs(delta) < threshold)
|
||||
{
|
||||
delta = 0;
|
||||
}
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
}
|
691
src/filemanager/Buffer.java
Normal file
691
src/filemanager/Buffer.java
Normal file
@ -0,0 +1,691 @@
|
||||
package filemanager;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
import java.util.*;
|
||||
import com.vmx.*;
|
||||
import com.one.*;
|
||||
|
||||
class CopyMoveThread implements Runnable
|
||||
{
|
||||
public boolean interrupt = false;
|
||||
protected ProgressCallback callback;
|
||||
protected boolean usecb;
|
||||
|
||||
public CopyMoveThread(ProgressCallback cb)
|
||||
{
|
||||
interrupt = false;
|
||||
callback = cb;
|
||||
usecb = callback != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ôóíêöèÿ ïîòîêà êîïèðîâàíèÿ/ïåðåìåùåíèÿ
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
if(usecb)
|
||||
{
|
||||
String[] files = Buffer.getBuffer();
|
||||
long size = 0;
|
||||
|
||||
for(int i = 0; i < files.length; i++)
|
||||
{
|
||||
size += filesystem.getSize(files[i]);
|
||||
}
|
||||
|
||||
callback.setMax((int)size);
|
||||
}
|
||||
|
||||
// ïàïêà, êóäà êîïèðóåì/ïåðåìåùàåì
|
||||
String targetPath = main.currentPath;
|
||||
|
||||
// ôàéë äëÿ îïåðàöèè
|
||||
String sourceFileFullName;
|
||||
String sourceOnlyName;
|
||||
boolean mustMove;
|
||||
boolean forAll = false, yes = false, fileExists = false;
|
||||
|
||||
while(Buffer.buf.size() > 0 && !interrupt)
|
||||
{
|
||||
mustMove = (Buffer.flags.get(Buffer.buf.size() - 1) & Buffer.FLAG_MOVE) != 0; // true, åñëè íàäî ïåðåìåùàòü!
|
||||
|
||||
sourceFileFullName = noLastSlash((String)Buffer.buf.elementAt(Buffer.buf.size() - 1)); // ïîñë ôàéë èç áóôåðà
|
||||
sourceOnlyName = sourceFileFullName.substring(sourceFileFullName.lastIndexOf('/') + 1);
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.setText(sourceOnlyName);
|
||||
}
|
||||
|
||||
fileExists = filesystem.isFileExist(targetPath + sourceOnlyName);
|
||||
|
||||
if(fileExists && !forAll) // ôàéë ñóùåñòâóåò è åù¸ íå áûëî êîìàíäû "... äëÿ âñåõ!"
|
||||
{
|
||||
alConfirmOverwrite al = new alConfirmOverwrite(sourceOnlyName, main.FileSelect);
|
||||
main.dsp.setCurrent(al);
|
||||
al.t.start();
|
||||
|
||||
try
|
||||
{
|
||||
al.t.join(); // æä¸ì, ïîêà îí ÑÄÎÕÍÅÒ :)
|
||||
}
|
||||
catch(InterruptedException x)
|
||||
{
|
||||
}
|
||||
|
||||
yes = false;
|
||||
|
||||
if(al.modalResult == al.cmdYes || al.modalResult == al.cmdYesForAll)
|
||||
{
|
||||
// åñëè äà èëè äà äëÿ âñåõ => yes!
|
||||
yes = true;
|
||||
}
|
||||
|
||||
if(al.modalResult == al.cmdCancel)
|
||||
{
|
||||
// åñëè "îòìåíà", çíà÷èò ñòîï.
|
||||
break;
|
||||
}
|
||||
|
||||
if(al.modalResult == al.cmdYesForAll || al.modalResult == al.cmdNoForAll)
|
||||
{
|
||||
// êîìàíäà äëÿ âñåõ
|
||||
forAll = true;
|
||||
}
|
||||
|
||||
al = null;
|
||||
main.dsp.setCurrent(Buffer.waitScreen);
|
||||
}
|
||||
|
||||
if(!fileExists || yes)
|
||||
{
|
||||
if(!mustMove) // êîïèðîâàíèå
|
||||
{
|
||||
if(!filesystem.copyFile(sourceFileFullName, targetPath + sourceOnlyName, callback))
|
||||
{
|
||||
Buffer.errors += Locale.Strings[Locale.FILE] + " " + sourceOnlyName + " " + Locale.Strings[Locale.FILE_NOT_COPIED] + "\n\n";
|
||||
}
|
||||
}
|
||||
else // ïåðåìåùåíèå
|
||||
{
|
||||
if(filesystem.copyFile(sourceFileFullName, targetPath + sourceOnlyName, callback)) // ñêîïèðîâàí?
|
||||
{
|
||||
if(filesystem.isReadOnly(sourceFileFullName)) // èñòî÷íèê readonly
|
||||
{
|
||||
Buffer.errors += sourceOnlyName + " " + Locale.Strings[Locale.SOURCE_FILE_READONLY_BE_COPIED] + "\n\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
filesystem.deleteFile(sourceFileFullName, true, callback); // óäàëÿåì èñõîäíûé ôàéë
|
||||
}
|
||||
}
|
||||
else // íå ïåðåìåùåí
|
||||
{
|
||||
Buffer.errors += Locale.Strings[Locale.FILE] + " " + sourceOnlyName + " " + Locale.Strings[Locale.FILE_NOT_MOVED] + "\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// óäàëèòü ïîñëåäíèé èç áóôåðà
|
||||
Buffer.remove(Buffer.buf.size() - 1);
|
||||
}
|
||||
|
||||
Buffer.waitScreen = null;
|
||||
main.dsp.setCurrent(new alMessage(Buffer.errors));
|
||||
}
|
||||
|
||||
/** Îáðåçàíèå ïîñëåäíåãî ñëýøà ó s */
|
||||
public static String noLastSlash(String s)
|
||||
{
|
||||
if(s.charAt(s.length() - 1) == '/')
|
||||
{
|
||||
return s.substring(0, s.length() - 1);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
class GaugeCallback implements ProgressCallback
|
||||
{
|
||||
protected Gauge gg;
|
||||
//protected StringItem si;
|
||||
|
||||
public GaugeCallback(Gauge assoc) // , StringItem text)
|
||||
{
|
||||
gg = assoc;
|
||||
//si = text;
|
||||
}
|
||||
|
||||
public void setMax(int max)
|
||||
{
|
||||
gg.setMaxValue(max);
|
||||
}
|
||||
|
||||
public void setProgress(int progress)
|
||||
{
|
||||
gg.setValue(progress);
|
||||
}
|
||||
|
||||
public void progress(int plus)
|
||||
{
|
||||
gg.setValue(gg.getValue() + plus);
|
||||
}
|
||||
|
||||
public void setText(String text)
|
||||
{
|
||||
//si.setText(text + "\n");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
class WaitComLis implements CommandListener
|
||||
{
|
||||
public void commandAction(Command c, Displayable d)
|
||||
{
|
||||
Buffer.stopThread();
|
||||
main.dsp.setCurrent(main.FileSelect);
|
||||
}
|
||||
}
|
||||
|
||||
class AfterburnThread implements Runnable
|
||||
{
|
||||
protected Thread t;
|
||||
|
||||
public AfterburnThread(Thread wait)
|
||||
{
|
||||
t = wait;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
if(t.isAlive())
|
||||
{
|
||||
try
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
catch(Exception xx)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Buffer.clear();
|
||||
|
||||
if(Buffer.zz != null)
|
||||
{
|
||||
main.dsp.setCurrent(new alMessage(Buffer.zz.error));
|
||||
Buffer.zz = null;
|
||||
}
|
||||
|
||||
if(Buffer.gz != null)
|
||||
{
|
||||
if(Buffer.gzipTempFile != null)
|
||||
{
|
||||
filesystem.deleteFile(Buffer.gzipTempFile + Buffer.TEMP_EXT, false, null);
|
||||
Buffer.gzipTempFile = null;
|
||||
}
|
||||
|
||||
main.dsp.setCurrent(new alMessage(Buffer.gz.error));
|
||||
Buffer.gz = null;
|
||||
}
|
||||
|
||||
if(Buffer.pt != null)
|
||||
{
|
||||
main.dsp.setCurrent(new alMessage(Buffer.pt.error));
|
||||
Buffer.pt = null;
|
||||
}
|
||||
|
||||
if(Buffer.ct != null)
|
||||
{
|
||||
main.dsp.setCurrent(new alMessage(Buffer.ct.error));
|
||||
Buffer.ct = null;
|
||||
}
|
||||
|
||||
if(Buffer.plg != null)
|
||||
{
|
||||
main.dsp.setCurrent(new alMessage(Buffer.plg.error));
|
||||
Buffer.plg = null;
|
||||
}
|
||||
|
||||
if(Buffer.rr != null)
|
||||
{
|
||||
main.dsp.setCurrent(new alMessage(Buffer.rr.error));
|
||||
Buffer.rr = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MultiGZipThread implements Runnable
|
||||
{
|
||||
protected Thread t;
|
||||
protected String s;
|
||||
|
||||
public MultiGZipThread(Thread wait)
|
||||
{
|
||||
t = wait;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
if(t.isAlive())
|
||||
{
|
||||
try
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if(Buffer.pt != null)
|
||||
{
|
||||
Buffer.pt = null;
|
||||
Buffer.clear();
|
||||
Buffer.add(Buffer.gzipTempFile + Buffer.TEMP_EXT, Buffer.FLAG_NORMAL);
|
||||
Buffer.gzipFiles(Buffer.gzipTempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Buffer
|
||||
{
|
||||
public static final int FLAG_NORMAL = 0;
|
||||
public static final int FLAG_MOVE = 1;
|
||||
public static final int FLAG_INZIP = 2;
|
||||
|
||||
protected static Thread t = null;
|
||||
public static String errors = "";
|
||||
|
||||
public static Object waitScreen = null;
|
||||
public static ProgressCallback callback = null;
|
||||
protected static WaitComLis wcl = null;
|
||||
|
||||
public static Vector buf = new Vector();
|
||||
public static IntVector flags = new IntVector();
|
||||
protected static IntVector hbuf = new IntVector();
|
||||
|
||||
public static Zipper zz = null;
|
||||
public static GZipper gz = null;
|
||||
public static PakThread pt = null;
|
||||
public static CryptThread ct = null;
|
||||
public static CopyMoveThread cmt = null;
|
||||
public static PlayListGenerator plg = null;
|
||||
public static Renamer rr = null;
|
||||
|
||||
public static String gzipTempFile = null;
|
||||
|
||||
public static final String TEMP_EXT = ".tmp";
|
||||
|
||||
/**
|
||||
* Äîáàâèòü â áóôåð
|
||||
*/
|
||||
public static void add(String file, int flag)
|
||||
{
|
||||
add(file, flag, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Äîáàâèòü â áóôåð
|
||||
* Åñëè flatten = true, òî ñòðóêòóðà ôàéëîâ
|
||||
* èç äîáàâëÿåìûõ ïàïîê ðàçâîðà÷èâàåòñÿ
|
||||
*/
|
||||
private static void add(String file, int flag, boolean flatten)
|
||||
{
|
||||
if(flatten && filesystem.isDir(file))
|
||||
{
|
||||
String[] files = new String[0];
|
||||
|
||||
try
|
||||
{
|
||||
files = filesystem.listNoArchives(file, options.showHidden);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
for(int i = 0; i < files.length; i++)
|
||||
{
|
||||
add(file + files[i], flag, flatten);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int hash = file.hashCode();
|
||||
int index = hbuf.find(hash);
|
||||
|
||||
if(index < 0)
|
||||
{
|
||||
buf.addElement(file);
|
||||
hbuf.add(hash);
|
||||
flags.add(flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
flags.set(index, flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ðàçâåðíóòü ôàéëîâóþ ñòðóêòóðó â áóôåðå,
|
||||
* ÷òîáû òàì îñòàëèñü òîëüêî ôàéëû
|
||||
*/
|
||||
public static void flattenBuffer()
|
||||
{
|
||||
String[] tbuf = new String[buf.size()];
|
||||
buf.copyInto(tbuf);
|
||||
|
||||
int[] tflags = new int[flags.size()];
|
||||
flags.copyInto(tflags);
|
||||
|
||||
clear();
|
||||
|
||||
for(int i = 0; i < tbuf.length; i++)
|
||||
{
|
||||
add(tbuf[i], tflags[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Óäàëèòü èç áóôåðà
|
||||
*/
|
||||
public static void remove(int index)
|
||||
{
|
||||
buf.removeElementAt(index);
|
||||
hbuf.remove(index, 1);
|
||||
flags.remove(index, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Î÷èñòèòü áóôåð
|
||||
*/
|
||||
public static void clear()
|
||||
{
|
||||
buf.removeAllElements();
|
||||
hbuf.remove(0, hbuf.size());
|
||||
flags.remove(0, flags.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Âåðíóòü áóôåð êàê ìàññèâ ñòðîê
|
||||
*/
|
||||
public static String[] getBuffer()
|
||||
{
|
||||
String bufs[] = new String[buf.size()];
|
||||
buf.copyInto(bufs);
|
||||
|
||||
return bufs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàïóñòèòü ïîòîê êîïèðîâàíèÿ/ïåðåìåùåíèÿ
|
||||
*/
|
||||
public static void copyMoveFiles()
|
||||
{
|
||||
errors = "";
|
||||
|
||||
if(buf.size() > 0)
|
||||
{
|
||||
createWait();
|
||||
|
||||
//Gauge gg = new Gauge(null, false, 1, 0);
|
||||
//waitAlert.setIndicator(gg);
|
||||
//waitForm.append(gg);
|
||||
//GaugeCallback callback = new GaugeCallback(gg);
|
||||
|
||||
main.dsp.setCurrent(waitScreen);
|
||||
|
||||
stopThread();
|
||||
|
||||
cmt = new CopyMoveThread(callback);
|
||||
t = new Thread(cmt);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void renameFiles(String template)
|
||||
{
|
||||
errors = "";
|
||||
|
||||
if(buf.size() > 0)
|
||||
{
|
||||
createWait();
|
||||
|
||||
main.dsp.setCurrent(waitScreen);
|
||||
|
||||
callback.setMax(Gauge.INDEFINITE);
|
||||
callback.setProgress(Gauge.CONTINUOUS_RUNNING);
|
||||
|
||||
flattenBuffer();
|
||||
|
||||
stopThread();
|
||||
|
||||
rr = new Renamer(template, buf, callback);
|
||||
t = new Thread(rr);
|
||||
t.start();
|
||||
|
||||
Thread t2 = new Thread(new AfterburnThread(t));
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void createPlayList(String name, int type, boolean allowRelPath)
|
||||
{
|
||||
errors = "";
|
||||
|
||||
if(buf.size() > 0)
|
||||
{
|
||||
createWait();
|
||||
|
||||
main.dsp.setCurrent(waitScreen);
|
||||
|
||||
callback.setMax(Gauge.INDEFINITE);
|
||||
callback.setProgress(Gauge.CONTINUOUS_RUNNING);
|
||||
|
||||
flattenBuffer();
|
||||
|
||||
stopThread();
|
||||
|
||||
plg = new PlayListGenerator(name, type, allowRelPath, buf, callback);
|
||||
t = new Thread(plg);
|
||||
t.start();
|
||||
|
||||
Thread t2 = new Thread(new AfterburnThread(t));
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàïóñòèòü ïîòîê ñæàòèÿ ôàéëîâ
|
||||
*/
|
||||
public static void zipFiles(String zipfile, String path, int zipLevel)
|
||||
{
|
||||
errors = "";
|
||||
|
||||
if(buf.size() > 0)
|
||||
{
|
||||
createWait();
|
||||
|
||||
main.dsp.setCurrent(waitScreen);
|
||||
|
||||
stopThread();
|
||||
|
||||
zz = new Zipper(zipfile, zipLevel, buf, path, callback);
|
||||
t = new Thread(zz);
|
||||
t.start();
|
||||
|
||||
Thread t2 = new Thread(new AfterburnThread(t));
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàïóñòèòü ïîòîê ñîçäàíèÿ PAK ôàéëà
|
||||
*/
|
||||
public static void pakFiles(String pakName)
|
||||
{
|
||||
errors = "";
|
||||
|
||||
if(buf.size() > 0)
|
||||
{
|
||||
createWait();
|
||||
|
||||
main.dsp.setCurrent(waitScreen);
|
||||
|
||||
callback.setMax(Gauge.INDEFINITE);
|
||||
callback.setProgress(Gauge.CONTINUOUS_RUNNING);
|
||||
|
||||
flattenBuffer();
|
||||
|
||||
stopThread();
|
||||
|
||||
pt = new PakThread(pakName, buf, callback);
|
||||
t = new Thread(pt);
|
||||
t.start();
|
||||
|
||||
Thread t2 = new Thread(new AfterburnThread(t));
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void gzipFiles(String gzipName)
|
||||
{
|
||||
errors = "";
|
||||
|
||||
if(buf.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
createWait();
|
||||
|
||||
main.dsp.setCurrent(waitScreen);
|
||||
|
||||
callback.setMax(Gauge.INDEFINITE);
|
||||
callback.setProgress(Gauge.CONTINUOUS_RUNNING);
|
||||
|
||||
flattenBuffer();
|
||||
|
||||
stopThread();
|
||||
|
||||
if(buf.size() > 1)
|
||||
{
|
||||
// â GZIP áîëüøå 1 ôàéëà íå âëåçåò,
|
||||
// òàê ÷òî ñíà÷àëà óïàêîâûâàåì â PAK
|
||||
|
||||
gzipTempFile = gzipName;
|
||||
|
||||
pt = new PakThread(gzipName + TEMP_EXT, buf, callback);
|
||||
t = new Thread(pt);
|
||||
t.start();
|
||||
|
||||
Thread t2 = new Thread(new MultiGZipThread(t));
|
||||
t2.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
gz = new GZipper(gzipName, (String)buf.elementAt(0), callback);
|
||||
t = new Thread(gz);
|
||||
t.start();
|
||||
|
||||
Thread t2 = new Thread(new AfterburnThread(t));
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàïóñòèòü ïîòîê øèôðîâàíèÿ ôàéëîâ
|
||||
*/
|
||||
public static void cryptFiles(String password)
|
||||
{
|
||||
errors = "";
|
||||
|
||||
if(buf.size() > 0)
|
||||
{
|
||||
createWait();
|
||||
|
||||
main.dsp.setCurrent(waitScreen);
|
||||
|
||||
callback.setMax(Gauge.INDEFINITE);
|
||||
callback.setProgress(Gauge.CONTINUOUS_RUNNING);
|
||||
|
||||
flattenBuffer();
|
||||
|
||||
stopThread();
|
||||
|
||||
ct = new CryptThread(password, buf, callback);
|
||||
t = new Thread(ct);
|
||||
t.start();
|
||||
|
||||
Thread t2 = new Thread(new AfterburnThread(t));
|
||||
t2.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñîçäàòü waitAlert
|
||||
*/
|
||||
public static void createWait()
|
||||
{
|
||||
/*
|
||||
waitScreen = new frmProgress(Locale.Strings[Locale.WAIT]);
|
||||
waitScreen.addCommand(new Command(Locale.Strings[Locale.CANCEL_CMD], Command.CANCEL, 1));
|
||||
waitScreen.setCommandListener(wcl = new WaitComLis());
|
||||
callback = (ProgressCallback)waitScreen;
|
||||
callback.setText(Locale.Strings[Locale.WAIT_PLEASE]);
|
||||
*/
|
||||
|
||||
waitScreen = new cvsAlert(Locale.Strings[Locale.WAIT]);
|
||||
((cvsAlert)waitScreen).setType(AlertType.WARNING);
|
||||
((cvsAlert)waitScreen).setIndicator(true);
|
||||
((cvsAlert)waitScreen).addCommand(new Command(Locale.Strings[Locale.CANCEL_CMD], Command.CANCEL, 1));
|
||||
((cvsAlert)waitScreen).setCommandListener(wcl = new WaitComLis());
|
||||
callback = (ProgressCallback)waitScreen;
|
||||
callback.setText(Locale.Strings[Locale.WAIT_PLEASE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Îñòàíîâèòü ïðîöåññ
|
||||
*/
|
||||
public static void stopThread()
|
||||
{
|
||||
if(t != null)
|
||||
{
|
||||
if(zz != null)
|
||||
{
|
||||
zz.interrupt = true;
|
||||
}
|
||||
|
||||
if(pt != null)
|
||||
{
|
||||
pt.interrupt = true;
|
||||
}
|
||||
|
||||
if(gz != null)
|
||||
{
|
||||
gz.interrupt = true;
|
||||
}
|
||||
|
||||
if(cmt != null)
|
||||
{
|
||||
cmt.interrupt = true;
|
||||
}
|
||||
|
||||
if(t.isAlive())
|
||||
{
|
||||
try
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
catch(Exception x)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
t = null;
|
||||
}
|
||||
}
|
||||
}
|
207
src/filemanager/Colors.java
Normal file
207
src/filemanager/Colors.java
Normal file
@ -0,0 +1,207 @@
|
||||
package filemanager;
|
||||
|
||||
import java.io.*;
|
||||
import com.one.*;
|
||||
import com.vmx.*;
|
||||
import java.util.Vector;
|
||||
import javax.microedition.io.*;
|
||||
import javax.microedition.lcdui.Image;
|
||||
|
||||
/**
|
||||
* Êëàññ ñîäåðæèò öâåòîâóþ ñõåìó
|
||||
*/
|
||||
public class Colors
|
||||
{
|
||||
public static final byte BLOCK_BACK_IMAGE = 1,
|
||||
BLOCK_CURSOR_MODE = 2,
|
||||
BLOCK_CURSOR_IMAGE = 3,
|
||||
BLOCK_ICONS_MODE = 4,
|
||||
BLOCK_ICONS_PARAM = 5,
|
||||
BLOCK_ICONS_IMAGE = 6,
|
||||
BLOCK_SUBST_ICONS = 7,
|
||||
BLOCK_WAIT_PARAM = 8,
|
||||
BLOCK_WAIT_IMAGE = 9,
|
||||
BLOCK_PLAYER_COLORS = 10,
|
||||
BLOCK_PLAYER_BACK_IMAGE = 11,
|
||||
BLOCK_PLAYER_UI_PARAM = 12,
|
||||
BLOCK_PLAYER_UI_IMAGE = 13,
|
||||
BLOCK_PLAYER_BUTTON_PARAM = 14,
|
||||
BLOCK_PLAYER_BUTTON_IMAGE = 15,
|
||||
BLOCK_PLAYER_ANIM_PARAM = 16,
|
||||
BLOCK_PLAYER_ANIM_IMAGE = 17;
|
||||
|
||||
|
||||
public static final int SCHEME_EDIT = -1;
|
||||
public static final int SCHEME_EXTERNAL = -2;
|
||||
|
||||
public static String[] schemes;
|
||||
|
||||
public static int fore = 0xFF000080,
|
||||
fore1 = 0xFF008000,
|
||||
fore2 = 0xFFFF0000,
|
||||
border = 0xFF000000,
|
||||
disabled = 0xFF808080,
|
||||
back1 = 0xFFD0D0D0,
|
||||
back2 = 0xFFD0D0D0,
|
||||
selfore = 0xFFFFFFFF,
|
||||
selback1 = 0xFF000080,
|
||||
selback2 = 0xFF000080;
|
||||
|
||||
public static Image backImage = null;
|
||||
|
||||
public static void readSchemeList()
|
||||
{
|
||||
Vector v = new Vector();
|
||||
|
||||
try
|
||||
{
|
||||
IniFile ini = new IniFile("/style/style.ini");
|
||||
IniRecord record = null;
|
||||
|
||||
while((record = ini.getNextRecord()) != null)
|
||||
{
|
||||
v.addElement(record.value);
|
||||
}
|
||||
|
||||
ini.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
main.showErrMsg(43, e);
|
||||
}
|
||||
|
||||
schemes = new String[v.size()];
|
||||
v.copyInto(schemes);
|
||||
}
|
||||
|
||||
public static void loadColorScheme(int scheme) throws IOException
|
||||
{
|
||||
if(scheme < 0 || scheme >= schemes.length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
readSchemeData(main.loader.getResourceAsStream("/style/" + schemes[scheme] + ".mcs"));
|
||||
}
|
||||
|
||||
public static void loadExtColorScheme(String fname) throws IOException
|
||||
{
|
||||
readSchemeData(Connector.openInputStream("file:///" + fname));
|
||||
}
|
||||
|
||||
public static void readSchemeData(InputStream is) throws IOException
|
||||
{
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
|
||||
byte[] sig = new byte[3];
|
||||
dis.readFully(sig);
|
||||
|
||||
boolean mcsext = (sig[0] == (byte)0x4D) && (sig[1] == (byte)0x43) && (sig[2] == (byte)0x53); // MCS ñèãíàòóðà
|
||||
|
||||
fore = dis.readInt();
|
||||
fore1 = dis.readInt();
|
||||
fore2 = dis.readInt();
|
||||
border = dis.readInt();
|
||||
disabled = dis.readInt();
|
||||
back1 = dis.readInt();
|
||||
back2 = dis.readInt();
|
||||
selfore = dis.readInt();
|
||||
selback1 = dis.readInt();
|
||||
selback2 = dis.readInt();
|
||||
|
||||
backImage = null;
|
||||
|
||||
if(mcsext)
|
||||
{
|
||||
int count = dis.readUnsignedShort();
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
switch(dis.readUnsignedByte())
|
||||
{
|
||||
case BLOCK_BACK_IMAGE:
|
||||
int len = dis.readInt();
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
byte[] b = new byte[len];
|
||||
dis.readFully(b);
|
||||
|
||||
backImage = ImageProcessor.scaleImage(Image.createImage(b, 0, b.length), gkcCanvas.getDisplayWidth(), gkcCanvas.getDisplayHeight());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BLOCK_CURSOR_MODE:
|
||||
options.frameCursor = dis.readBoolean();
|
||||
break;
|
||||
|
||||
case BLOCK_CURSOR_IMAGE:
|
||||
dis.skip(dis.readInt());
|
||||
break;
|
||||
|
||||
case BLOCK_ICONS_MODE:
|
||||
options.iconsVista = dis.readBoolean();
|
||||
images.loadIcons();
|
||||
break;
|
||||
|
||||
case BLOCK_ICONS_PARAM:
|
||||
dis.skip(4);
|
||||
break;
|
||||
|
||||
case BLOCK_ICONS_IMAGE:
|
||||
dis.skip(dis.readInt());
|
||||
break;
|
||||
|
||||
case BLOCK_SUBST_ICONS:
|
||||
dis.skip(dis.readShort() * 4);
|
||||
break;
|
||||
|
||||
case BLOCK_WAIT_PARAM:
|
||||
dis.skip(4);
|
||||
break;
|
||||
|
||||
case BLOCK_WAIT_IMAGE:
|
||||
dis.skip(dis.readInt());
|
||||
|
||||
case BLOCK_PLAYER_COLORS:
|
||||
dis.skip(16);
|
||||
break;
|
||||
|
||||
case BLOCK_PLAYER_BACK_IMAGE:
|
||||
dis.skip(dis.readInt());
|
||||
break;
|
||||
|
||||
case BLOCK_PLAYER_UI_PARAM:
|
||||
dis.skip(14);
|
||||
break;
|
||||
|
||||
case BLOCK_PLAYER_UI_IMAGE:
|
||||
dis.skip(dis.readInt());
|
||||
break;
|
||||
|
||||
case BLOCK_PLAYER_BUTTON_PARAM:
|
||||
dis.skip(4);
|
||||
break;
|
||||
|
||||
case BLOCK_PLAYER_BUTTON_IMAGE:
|
||||
dis.skip(dis.readInt());
|
||||
break;
|
||||
|
||||
case BLOCK_PLAYER_ANIM_PARAM:
|
||||
dis.skip(4);
|
||||
break;
|
||||
|
||||
case BLOCK_PLAYER_ANIM_IMAGE:
|
||||
dis.skip(dis.readInt());
|
||||
break;
|
||||
|
||||
default:
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dis.close();
|
||||
}
|
||||
}
|
102
src/filemanager/ContextMenu.java
Normal file
102
src/filemanager/ContextMenu.java
Normal file
@ -0,0 +1,102 @@
|
||||
package filemanager;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
import java.util.Vector;
|
||||
import com.vmx.*;
|
||||
|
||||
public class ContextMenu implements MenuListener
|
||||
{
|
||||
protected cvsContextMenu mn;
|
||||
|
||||
public ContextMenu(cvsContextMenu menu)
|
||||
{
|
||||
mn = menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Îáðàáîò÷èê äëÿ ìåíþ
|
||||
*/
|
||||
public void menuAction(int string)
|
||||
{
|
||||
mn.ret();
|
||||
|
||||
if(main.dsp.getCurrent() == main.textEditor)
|
||||
{
|
||||
switch(string)
|
||||
{
|
||||
case Locale.SAVE_CMD:
|
||||
if(main.textEditor.savePossible())
|
||||
{
|
||||
main.textEditor.saveEditText();
|
||||
}
|
||||
break;
|
||||
|
||||
case Locale.SAVE_AS_CMD:
|
||||
main.textEditor.showSave();
|
||||
break;
|
||||
|
||||
case Locale.EDIT_CMD:
|
||||
if(main.textEditor.editPossible())
|
||||
{
|
||||
main.textEditor.editText();
|
||||
}
|
||||
break;
|
||||
|
||||
case Locale.UNDO_CMD:
|
||||
if(main.textEditor.undoPossible())
|
||||
{
|
||||
main.textEditor.undo();
|
||||
}
|
||||
break;
|
||||
|
||||
case Locale.REDO_CMD:
|
||||
if(main.textEditor.redoPossible())
|
||||
{
|
||||
main.textEditor.redo();
|
||||
}
|
||||
break;
|
||||
|
||||
case Locale.GOTO_CMD:
|
||||
main.textEditor.showGoto();
|
||||
break;
|
||||
|
||||
case Locale.GOTO_START_CMD:
|
||||
main.textEditor.gotoStart();
|
||||
break;
|
||||
|
||||
case Locale.GOTO_END_CMD:
|
||||
main.textEditor.gotoEnd();
|
||||
break;
|
||||
|
||||
case Locale.FIND_CMD:
|
||||
main.textEditor.showFind();
|
||||
break;
|
||||
|
||||
case Locale.FIND_NEXT_CMD:
|
||||
main.textEditor.findNext(false);
|
||||
break;
|
||||
|
||||
case Locale.FONT_CMD:
|
||||
main.textEditor.showFont();
|
||||
break;
|
||||
|
||||
case Locale.ENCODING_CMD:
|
||||
if(main.textEditor.encPossible())
|
||||
{
|
||||
main.textEditor.showEncoding();
|
||||
}
|
||||
break;
|
||||
|
||||
case Locale.MINIMIZE_CMD:
|
||||
try
|
||||
{
|
||||
main.dsp.setCurrent(null);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
src/filemanager/GZipper.java
Normal file
100
src/filemanager/GZipper.java
Normal file
@ -0,0 +1,100 @@
|
||||
package filemanager;
|
||||
|
||||
import com.vmx.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.microedition.io.Connector;
|
||||
import javax.microedition.io.file.FileConnection;
|
||||
import com.classpath.zip.*;
|
||||
|
||||
public class GZipper implements Runnable
|
||||
{
|
||||
protected String filename; // ÊÓÄÀ ïàêîâàòü
|
||||
protected String file; // ×ÒÎ ïàêîâàòü
|
||||
protected ProgressCallback callback;
|
||||
public String error;
|
||||
public boolean interrupt;
|
||||
|
||||
public GZipper(String fn, String f, ProgressCallback cb)
|
||||
{
|
||||
filename = fn;
|
||||
file = f;
|
||||
callback = cb;
|
||||
error = null;
|
||||
interrupt = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Êîïèðîâàòü ôàéë â ïîòîê
|
||||
*/
|
||||
protected void copyFileToStream(String fileName, OutputStream os, ProgressCallback callback) throws IOException
|
||||
{
|
||||
InputStream is = Connector.openInputStream("file:///" + fileName);
|
||||
|
||||
byte[] buf = new byte[main.COPYBUFSIZE];
|
||||
int buflen;
|
||||
|
||||
while(is.available() > 0 && !interrupt)
|
||||
{
|
||||
os.write(buf, 0, buflen = is.read(buf));
|
||||
|
||||
if(callback != null)
|
||||
{
|
||||
callback.progress(buflen);
|
||||
}
|
||||
}
|
||||
|
||||
is.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ôóíêöèÿ, îñóùåñòâëÿþùàÿ ñîáñòâåííî ñîçäàíèå GZIP ôàéëà
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
FileConnection fc = null;
|
||||
|
||||
try
|
||||
{
|
||||
fc = (FileConnection)Connector.open("file:///" + filename);
|
||||
|
||||
if(fc.exists() && fc.isDirectory())
|
||||
{
|
||||
fc.delete();
|
||||
}
|
||||
|
||||
if(!fc.exists())
|
||||
{
|
||||
fc.create();
|
||||
}
|
||||
|
||||
GZIPOutputStream gzos = new GZIPOutputStream(fc.openOutputStream());
|
||||
|
||||
if(callback != null)
|
||||
{
|
||||
callback.setMax((int)filesystem.getSize(file));
|
||||
callback.setProgress(0);
|
||||
}
|
||||
|
||||
copyFileToStream(file, gzos, callback);
|
||||
|
||||
gzos.close();
|
||||
fc.close();
|
||||
}
|
||||
catch(Exception x)
|
||||
{
|
||||
error = x.toString(); // x.getClass().getName() + ": " + x.getMessage();
|
||||
|
||||
if(fc != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fc.close();
|
||||
}
|
||||
catch(Exception xx)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
246
src/filemanager/Locale.java
Normal file
246
src/filemanager/Locale.java
Normal file
@ -0,0 +1,246 @@
|
||||
package filemanager;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Vector;
|
||||
import java.util.Hashtable;
|
||||
import com.vmx.*;
|
||||
import com.one.*;
|
||||
|
||||
public class Locale
|
||||
{
|
||||
public static final int SELECT_DRIVE = 0, FAVOURITE = 1, OPTIONS_CMD = 2,
|
||||
EXIT_CMD = 3, DISK = 4, DISK_INFO = 5, DISK_TOTAL_SIZE = 6, KB = 7,
|
||||
BYTE = 8, DISK_AVAILABLE = 9, SELECT_CMD = 10, DELETE_CMD = 11,
|
||||
DELETE_ALL_CMD = 12, BACK_CMD = 13, PATH_NOT_EXIST = 14,
|
||||
CONFIRMATION = 15, DEL_RECORD_FAVOR = 16, DEL_ALL_FAVOR = 17,
|
||||
YES_CMD = 18, NO_CMD = 19, SAVE_CMD = 20, CLEAR_CMD = 21,
|
||||
ERROR = 22, FILE_NAME_EXIST = 23, FILE_NOT_COPIED = 24,
|
||||
FILE_NOT_MOVED = 25, FORMAT_NOT_SUPP = 26, OPEN_CMD = 27,
|
||||
INSERT_CMD = 28, PROPERTY_CMD = 29, RENAME_CMD = 30, COPY_CMD = 31,
|
||||
MOVE_CMD = 32, NEW_FOLDER_CMD = 33, NEW_FILE_CMD = 34,
|
||||
TO_FAVOUR_CMD = 35, DISK_INFO_CMD = 36, MMC_INFO_CMD = 37,
|
||||
PREFERENCES_CMD = 38, HELP_CMD = 39, OK_CMD = 40, CANCEL_CMD = 41,
|
||||
SELECT_PASTE_IN_FOLDER = 42, INFORMATION = 43, FOLDER_NAME = 44,
|
||||
FILE_NAME = 45, SIZE = 46, ATTR = 47, LAST_MODIF = 48, RENAME = 49,
|
||||
NAME = 50, NAME_EXIST_SELECT_ANOTHER = 51, READ_ONLY = 52,
|
||||
NOT_RENAMED = 53, PREF_SHOW_HIDDEN_FILES = 54, PREF_VISUAL = 55,
|
||||
PREF_BROWSE = 56, PREF_ICONS_VISTA = 57, PREF_NO_SPLASH = 58,
|
||||
PREF_SHOW_ERRORS = 59, CREATE_NEW_FOLDER = 60, CREATE_NEW_FILE = 61,
|
||||
NOT_CREATE_NEW_FOLDER = 62, WAIT_PLEASE = 63, WAIT = 64,
|
||||
PLAYER_STOP = 65, PLAYER_PLAY = 66, PLAYER_PAUSE = 67,
|
||||
IMAGEVIEW_SCALED = 68, ATTENTION = 69, SOURCE_FILE_READONLY_BE_COPIED = 70,
|
||||
DEL_SELECTED_FILE = 71, DEL_MARKED_FILES = 72, FOLDER_DELETED = 73,
|
||||
FOLDER_NOT_EMPTY = 74, FILE_DELETED = 75, FILE_NOT_DELETED = 76,
|
||||
LICENSE_AGR = 77, FILE_TYPE = 78, FILE_NOT_SAVED_EXIT = 79,
|
||||
SAVED = 80, BUFFER = 81, DEL_FROM_BUFFER = 82,
|
||||
DEL_ALL_FROM_BUFFER = 83, OPERATION_OK = 84, FILE = 85,
|
||||
FILE_NOT_SAVED = 86, BUFFER_EMPTY = 87, MARK_CMD = 88,
|
||||
MARK_ALL_CMD = 89, DEMARK_ALL_CMD = 90, PREF_OPEN_NOT_SUPP = 91,
|
||||
PREF_USE_ALERT_ICONS = 92, FILE_TOO_BIG = 93, NEED_RESTART = 94,
|
||||
EXTRACT_CMD = 95, EXTRACT_ALL_CMD = 96, EXTRACT_TO = 97,
|
||||
FOLDER_IMPOSSIBLE = 98, FILES_EXTRACTED = 99, YES_FOR_ALL = 100,
|
||||
NO_FOR_ALL = 101, OVERWRITE_QUESTION = 102, COMPRESSED_SIZE = 103,
|
||||
MENU_FILE = 104, MENU_ARCHIVE = 105, MENU_PROPERTIES = 106,
|
||||
MENU_OPERATIONS = 107, MENU_CREATE = 108, MENU_SHOW = 109,
|
||||
PREF_NO_EFFECTS = 110, CREATE_ARCHIVE = 111, COMPRESSION_LEVEL = 112,
|
||||
TAG_EDITOR = 113, ID3_SONG = 114, ID3_ARTIST = 115, ID3_ALBUM = 116,
|
||||
ID3_YEAR = 117, ID3_COMMENT = 118, ID3_TRACKNUM = 119, EDIT_ID3_CMD = 120,
|
||||
MB = 121, KEY_LEFT = 122, KEY_RIGHT = 123, KEY_UP = 124, KEY_DOWN = 125,
|
||||
KEY_LSK = 126, KEY_RSK = 127, KEY_JOY = 128, KEY_DIAL = 129,
|
||||
KEY_CANCEL = 130, PREV_SCREEN_CMD = 131, NEXT_SCREEN_CMD = 132,
|
||||
PREV_FILE_CMD = 133, NEXT_FILE_CMD = 134, UP_LEVEL_CMD = 135,
|
||||
MENU_ADDITIONAL = 136, PANELS = 137, PANEL_NUMS = 138 /*[+0..9]*/,
|
||||
FULLSCREEN_CMD = 148, KEY_NO_ACTION = 149, KEYBOARD_CONFIG_CMD = 150,
|
||||
PREF_LANG = 151, PREF_COLOR_SCHEME = 152, PREF_CHECK_FILE_ATTRIB = 153,
|
||||
PREF_ACCURATE_DIR_CHECK = 154, PREF_SORT_FILE_LIST = 155, ATTR_READ_ONLY = 156,
|
||||
ATTR_HIDDEN = 157, KEY_BACK = 158, KEY_CLEAR = 159, PREF_PLAYER_SETUP = 160,
|
||||
PREF_PLAY_USE_ID3 = 161, PREF_SHOW_PLAY_PROGRESS = 162, PREF_PLAY_TEMP = 163,
|
||||
DONE = 164, PREF_TEXT_SETUP = 165, PREF_SHOW_CR = 166, PREF_SHOW_LF = 167,
|
||||
PREF_SUBST_TAB = 168, GOTO_CMD = 169, PREF_PAGE_SCROLL = 170, PREF_KEYMAP = 171,
|
||||
INITIAL_SETUP = 172, CLOCK_MODE = 173, SWITCH = 174, NEXT_PANEL = 175,
|
||||
PREV_PANEL = 176, NEXT_FREE = 177, PREV_FREE = 178, QUICK_SWITCH = 179,
|
||||
EDIT_CMD = 180, MENU_SEARCH = 181, FONT_CMD = 182, ENCODING_CMD = 183,
|
||||
PREF_EDIT_COLOR_SCHEME = 184, HASH_SUM = 185, CRYPT_CMD = 186, PASSWORD = 187,
|
||||
CRYPT_USAGE = 188, SIZE_SMALL = 189, SIZE_MEDIUM = 190, SIZE_LARGE = 191,
|
||||
FONT_FACE = 192, FONT_STYLE = 193, FONT_BOLD = 194, FONT_ITALIC = 195,
|
||||
FONT_MONOSPACE = 196, FONT_PROPORTIONAL = 197, FONT_SYSTEM = 198,
|
||||
FONT_UNDERLINED = 199, PREF_SHOW_MENU_NUM = 200, PREF_SHUFFLE_PLAY = 201,
|
||||
TRANSCODE = 202, PREF_EXT_COLOR_SCHEME = 203, CREATE_PLAYLIST = 204,
|
||||
PREF_ARCHIVE_ENCODING = 205, AUDIO = 206, VIDEO = 207,
|
||||
PATH_SAVE = 208, PATH_ABSOLUTE = 209, PATH_RELATIVE = 210, COLOR_INFO = 211 /*[+0..9]*/,
|
||||
PREF_FRAME_CURSOR = 221, MODE_NO_CHANGE = 222, MODE_SET = 223, MODE_CLEAR = 224,
|
||||
EXT_BACK_IMAGE = 225, NAME_TEMPLATE = 226, MINIMIZE_CMD = 227, CONFIRM_SAVE = 228,
|
||||
SAVE_AS_CMD = 229, MENU_EDIT = 230, UNDO_CMD = 231, REDO_CMD = 232, FIND_CMD = 233,
|
||||
FIND_NEXT_CMD = 234, MENU_TEXT = 235, TRANSCODE_IMPOSSIBLE = 236, LINE_NUMBER = 237,
|
||||
PREF_REMEMBER_PATH = 238, TEMPLATES_CMD = 239, USE_UTF_BOM = 240,
|
||||
SEARCH_EOF_QUESTION = 241, GOTO_START_CMD = 242, GOTO_END_CMD = 243, ADD_CMD = 244,
|
||||
PREF_PLAY_ALERT_SOUNDS = 245, PREF_SWAP_SOFT_KEYS = 246, IGNORE_CASE = 247,
|
||||
SORT_NONE = 248, SORT_BY_NAME = 249, SORT_BY_TYPE = 250, SORT_BY_DATE = 251, SORT_BY_SIZE = 252;
|
||||
|
||||
public static String ABOUT_MIDLET_NAME = "SieFM v" + main.loader.getAppProperty("MIDlet-Version");
|
||||
|
||||
public static String[] Strings;
|
||||
public static final int STRING_COUNT = 253;
|
||||
|
||||
public static String lang;
|
||||
|
||||
public static String[] locales = null; // èìåíà ÿçûêîâ òèïà "ru", "en"...
|
||||
public static String[] languages = null; // íàçâàíèÿ ÿçûêîâ (ðóññêèé, english, ...)
|
||||
|
||||
private static final char cmin1 = (char)-1;
|
||||
|
||||
/**
|
||||
* Êîíñòðóêòîð
|
||||
*/
|
||||
public Locale()
|
||||
{
|
||||
}
|
||||
|
||||
public static void loadLocale(String newlang) throws Exception
|
||||
{
|
||||
lang = newlang;
|
||||
|
||||
Strings = new String[STRING_COUNT];
|
||||
|
||||
for(int k = 0; k < STRING_COUNT; k++)
|
||||
{
|
||||
Strings[k] = "#" + Integer.toString(k);
|
||||
}
|
||||
|
||||
IniFile ini = new IniFile("/lang/" + lang + "/strings.ini");
|
||||
IniRecord record = null;
|
||||
|
||||
while((record = ini.getNextRecord()) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Strings[Integer.parseInt(record.key)] = record.value;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
ini.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü ïîòîê ñ òåêñòîì about'à
|
||||
*/
|
||||
public static InputStream getAboutStream()
|
||||
{
|
||||
try
|
||||
{
|
||||
return main.loader.getResourceAsStream("/lang/" + lang + "/about.txt");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
main.showErrMsg(54, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Òåêñò ëèöåíçèè
|
||||
public static String getLicenseString()
|
||||
{
|
||||
try
|
||||
{
|
||||
InputStreamDecoder isd = InputStreamDecoder.getResourceDecoder("/lang/" + lang + "/license.txt");
|
||||
|
||||
String s = "";
|
||||
|
||||
while(isd.available() > 0)
|
||||
{
|
||||
s += isd.readChar();
|
||||
}
|
||||
|
||||
isd.close();
|
||||
|
||||
return s;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
main.showErrMsg(55, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîëó÷èòü ñïèñîê äîñòóïíûõ ÿçûêîâ
|
||||
*/
|
||||
public static boolean readLocaleList()
|
||||
{
|
||||
Vector v = new Vector();
|
||||
String s;
|
||||
char c;
|
||||
int index;
|
||||
|
||||
try
|
||||
{
|
||||
InputStreamDecoder isd = InputStreamDecoder.getResourceDecoder("/lang/lang.ini");
|
||||
|
||||
while(isd.available() > 0)
|
||||
{
|
||||
s = "";
|
||||
|
||||
while(isd.available() > 0)
|
||||
{
|
||||
c = isd.readChar();
|
||||
|
||||
if(c == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(c == '\t')
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
if(c != '\r')
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
}
|
||||
|
||||
index = s.indexOf(';');
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
s = s.substring(0, index);
|
||||
}
|
||||
|
||||
s = s.trim();
|
||||
|
||||
if(s.length() > 0)
|
||||
{
|
||||
index = s.indexOf('=');
|
||||
|
||||
if(index > 0)
|
||||
{
|
||||
v.addElement(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isd.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
main.showErrMsg(56, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
languages = new String[v.size()];
|
||||
locales = new String[v.size()];
|
||||
|
||||
for(int i = 0; i < languages.length; i++)
|
||||
{
|
||||
s = (String)(v.elementAt(i));
|
||||
index = s.indexOf("=");
|
||||
locales[i] = s.substring(0, index).trim();
|
||||
languages[i] = s.substring(index + 1).trim();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
9
src/filemanager/MenuListener.java
Normal file
9
src/filemanager/MenuListener.java
Normal file
@ -0,0 +1,9 @@
|
||||
package filemanager;
|
||||
|
||||
public interface MenuListener
|
||||
{
|
||||
/**
|
||||
* Âûïîëíèòü äåéñòâèå ñ íàçâàíèåì Locale.Strings[code]
|
||||
*/
|
||||
public void menuAction (int code);
|
||||
}
|
537
src/filemanager/PanelManager.java
Normal file
537
src/filemanager/PanelManager.java
Normal file
@ -0,0 +1,537 @@
|
||||
package filemanager;
|
||||
|
||||
import javax.microedition.lcdui.*;
|
||||
import javax.microedition.rms.*;
|
||||
import java.io.*;
|
||||
import com.one.*;
|
||||
|
||||
/**
|
||||
* Êëàññ óïðàâëåíèÿ ïàíåëÿìè.
|
||||
* Íà ïàíåëÿõ ìîæåò áûòü íå òîëüêî FileSelect,
|
||||
* íî è ïëååð, ïðîñìîòð òåêñòà è ò. ä.
|
||||
*/
|
||||
public class PanelManager
|
||||
{
|
||||
protected static final String panelsStoreName = "SieFM_panels";
|
||||
|
||||
protected static RecordStore panelsStore;
|
||||
|
||||
protected String[] currpath;
|
||||
protected String[] currfile;
|
||||
protected Object[] currdisp;
|
||||
protected boolean[] implicit;
|
||||
protected int[] associat;
|
||||
|
||||
protected DisplayManager dsp;
|
||||
protected int pcount;
|
||||
protected int currpanel, prevpanel;
|
||||
protected int prevpanelreq;
|
||||
|
||||
public PanelManager(DisplayManager dsp, int pcount)
|
||||
{
|
||||
this.dsp = dsp;
|
||||
this.pcount = pcount;
|
||||
|
||||
currpath = new String[pcount];
|
||||
currfile = new String[pcount];
|
||||
currdisp = new Object[pcount];
|
||||
associat = new int[pcount];
|
||||
|
||||
for(int i = 0; i < pcount; i++)
|
||||
{
|
||||
currpath[i] = "";
|
||||
currfile[i] = "";
|
||||
currdisp[i] = main.FileSelect;
|
||||
associat[i] = -1;
|
||||
}
|
||||
|
||||
currpanel = 0;
|
||||
prevpanel = pcount > 1 ? currpanel + 1 : currpanel;
|
||||
prevpanelreq = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñîõðàíåíèå äàííûõ ïàíåëåé.
|
||||
* Ñîõðàíÿåòñÿ òîëüêî ïóòü è èìÿ òåêóùåãî ôàéëà.
|
||||
*/
|
||||
public void savePanels()
|
||||
{
|
||||
if(panelsStore != null)
|
||||
{
|
||||
byte[] data = null;
|
||||
|
||||
try
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
|
||||
dos.writeInt(pcount);
|
||||
dos.writeInt(currpanel);
|
||||
dos.writeInt(prevpanel);
|
||||
|
||||
for(int i = 0; i < pcount; i++)
|
||||
{
|
||||
if(currpath[i] == null)
|
||||
{
|
||||
currpath[i] = "";
|
||||
}
|
||||
|
||||
if(currfile[i] == null)
|
||||
{
|
||||
currfile[i] = "";
|
||||
}
|
||||
|
||||
dos.writeUTF(currpath[i]);
|
||||
dos.writeUTF(currfile[i]);
|
||||
}
|
||||
|
||||
dos.flush();
|
||||
data = baos.toByteArray();
|
||||
dos.close();
|
||||
panelsStore.setRecord(1, data, 0, data.length);
|
||||
}
|
||||
catch(InvalidRecordIDException ridex)
|
||||
{
|
||||
// Çàïèñü #1 íå ñóùåñòâóåò, ñîçäàòü íîâóþ
|
||||
try
|
||||
{
|
||||
panelsStore.addRecord(data, 0, data.length);
|
||||
}
|
||||
catch(RecordStoreException ex)
|
||||
{
|
||||
main.showErrMsg(83, ex);
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
main.showErrMsg(84, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if(panelsStore != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
panelsStore.closeRecordStore();
|
||||
panelsStore = null;
|
||||
}
|
||||
catch(RecordStoreException ex)
|
||||
{
|
||||
main.showErrMsg(85, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Âîññòàíîâèòü äàííûå ïàíåëåé.
|
||||
*/
|
||||
public void restorePanels()
|
||||
{
|
||||
try
|
||||
{
|
||||
panelsStore = RecordStore.openRecordStore(panelsStoreName, true);
|
||||
}
|
||||
catch(RecordStoreException ex)
|
||||
{
|
||||
panelsStore = null;
|
||||
main.showErrMsg(86, ex);
|
||||
}
|
||||
|
||||
if(panelsStore != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(panelsStore.getRecord(1)));
|
||||
|
||||
int count = dis.readInt();
|
||||
|
||||
if(count > pcount)
|
||||
{
|
||||
count = pcount;
|
||||
}
|
||||
|
||||
currpanel = dis.readInt();
|
||||
prevpanel = dis.readInt();
|
||||
|
||||
if(currpanel < 0)
|
||||
{
|
||||
currpanel = 0;
|
||||
}
|
||||
else if(currpanel >= pcount)
|
||||
{
|
||||
currpanel = pcount - 1;
|
||||
}
|
||||
|
||||
if(prevpanel < 0)
|
||||
{
|
||||
prevpanel = 0;
|
||||
}
|
||||
else if(prevpanel >= pcount)
|
||||
{
|
||||
prevpanel = pcount - 1;
|
||||
}
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
currpath[i] = dis.readUTF();
|
||||
currfile[i] = dis.readUTF();
|
||||
}
|
||||
|
||||
dis.close();
|
||||
}
|
||||
catch(InvalidRecordIDException iride)
|
||||
{
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
main.showErrMsg(87, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void storePanel(int panel)
|
||||
{
|
||||
currpath[panel] = main.currentPath;
|
||||
currfile[panel] = main.currentFile;
|
||||
currdisp[panel] = dsp.getCurrent();
|
||||
}
|
||||
|
||||
public void showPanel(int panel)
|
||||
{
|
||||
main.currentPath = currpath[panel];
|
||||
main.currentFile = currfile[panel];
|
||||
|
||||
if(currdisp[panel] == main.FileSelect)
|
||||
{
|
||||
main.FileSelect.showWait(currfile[panel]);
|
||||
}
|
||||
else
|
||||
{
|
||||
dsp.setCurrent(currdisp[panel]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Âîçâðàò ê FileSelect
|
||||
*/
|
||||
public void ret()
|
||||
{
|
||||
if(associat[currpanel] >= 0)
|
||||
{
|
||||
// åñëè ñ ýòîé ïàíåëüþ êàêàÿ-òî áûëà ñâÿçàíà,
|
||||
// òî ïåðåõîäèì ê ñâÿçàííîé,
|
||||
int nextpanel = associat[currpanel];
|
||||
|
||||
// à ýòó âîçâðàùàåì â èñõîäíîå ñîñòîÿíèå
|
||||
associat[currpanel] = -1;
|
||||
currpath[currpanel] = null;
|
||||
currfile[currpanel] = null;
|
||||
currdisp[currpanel] = main.FileSelect;
|
||||
|
||||
currpanel = nextpanel;
|
||||
|
||||
showPanel(currpanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
setCurrent(main.FileSelect, currpanel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîêàçûâàåì ÷òî-íèáóäü íà çàäàííîé ïàíåëè
|
||||
*/
|
||||
public void setCurrent(Object dp, int panel)
|
||||
{
|
||||
if(dp != main.wait && dp != main.FileSelect)
|
||||
{
|
||||
deassociate(panel);
|
||||
}
|
||||
|
||||
currdisp[panel] = dp;
|
||||
|
||||
if(panel == currpanel)
|
||||
{
|
||||
dsp.setCurrent(dp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîêàçûâàåì ÷òî-íèáóäü íà òåêóùåé ïàíåëè
|
||||
*/
|
||||
public void setCurrent(Object dp)
|
||||
{
|
||||
if(dp != main.wait && dp != main.FileSelect)
|
||||
{
|
||||
deassociate(currpanel);
|
||||
}
|
||||
|
||||
currdisp[currpanel] = dp;
|
||||
|
||||
dsp.setCurrent(dp);
|
||||
}
|
||||
|
||||
public void setCurrent(Alert al, Object dp, int panel)
|
||||
{
|
||||
if(dp != main.wait && dp != main.FileSelect)
|
||||
{
|
||||
deassociate(panel);
|
||||
}
|
||||
|
||||
currdisp[panel] = dp;
|
||||
|
||||
dsp.setCurrent(al, currdisp[currpanel]);
|
||||
}
|
||||
|
||||
public void setCurrent(AutoAdvanceScreen al, Object dp, int panel)
|
||||
{
|
||||
if(dp != main.wait && dp != main.FileSelect)
|
||||
{
|
||||
deassociate(panel);
|
||||
}
|
||||
|
||||
currdisp[panel] = dp;
|
||||
|
||||
dsp.setCurrent(al, currdisp[currpanel]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîêàçàòü ïàíåëü nextpanel
|
||||
*/
|
||||
public void changePanel(int nextpanel)
|
||||
{
|
||||
if(nextpanel < 0 || nextpanel >= pcount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(nextpanel != currpanel)
|
||||
{
|
||||
storePanel(currpanel);
|
||||
|
||||
prevpanel = currpanel;
|
||||
currpanel = nextpanel;
|
||||
}
|
||||
|
||||
showPanel(currpanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîêàçàòü ñëåäóþùóþ ïàíåëü
|
||||
*/
|
||||
public void showNextPanel()
|
||||
{
|
||||
storePanel(currpanel);
|
||||
|
||||
prevpanel = currpanel;
|
||||
|
||||
if(++currpanel >= pcount)
|
||||
{
|
||||
currpanel = 0;
|
||||
}
|
||||
|
||||
showPanel(currpanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîêàçàòü ïðåäûäóùóþ ïàíåëü
|
||||
*/
|
||||
public void showPrevPanel()
|
||||
{
|
||||
storePanel(currpanel);
|
||||
|
||||
prevpanel = currpanel;
|
||||
|
||||
if(--currpanel < 0)
|
||||
{
|
||||
currpanel = pcount - 1;
|
||||
}
|
||||
|
||||
showPanel(currpanel);
|
||||
}
|
||||
|
||||
public void showNextFreePanel()
|
||||
{
|
||||
storePanel(currpanel);
|
||||
|
||||
prevpanel = currpanel;
|
||||
|
||||
do
|
||||
{
|
||||
if(++currpanel >= pcount)
|
||||
{
|
||||
currpanel = 0;
|
||||
}
|
||||
|
||||
if(currdisp[currpanel] == main.FileSelect)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while(currpanel != prevpanel);
|
||||
|
||||
showPanel(currpanel);
|
||||
}
|
||||
|
||||
public void showPrevFreePanel()
|
||||
{
|
||||
storePanel(currpanel);
|
||||
|
||||
prevpanel = currpanel;
|
||||
|
||||
do
|
||||
{
|
||||
if(--currpanel < 0)
|
||||
{
|
||||
currpanel = pcount - 1;
|
||||
}
|
||||
|
||||
if(currdisp[currpanel] == main.FileSelect)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while(currpanel != prevpanel);
|
||||
|
||||
showPanel(currpanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñâåðíóòü òåêóùóþ ïàíåëü - ñêîïèðîâàòü ïóòü â äðóãóþ
|
||||
*/
|
||||
public void minimizePanel()
|
||||
{
|
||||
storePanel(currpanel);
|
||||
|
||||
prevpanel = currpanel;
|
||||
|
||||
if(associat[currpanel] < 0)
|
||||
{
|
||||
associat[currpanel] = newPanel();
|
||||
|
||||
currpath[associat[currpanel]] = currpath[currpanel];
|
||||
currfile[associat[currpanel]] = currfile[currpanel];
|
||||
}
|
||||
|
||||
currpanel = associat[currpanel];
|
||||
showPanel(currpanel);
|
||||
}
|
||||
|
||||
public void updateAssociation(int panel)
|
||||
{
|
||||
if(panel != currpanel)
|
||||
{
|
||||
associat[panel] = currpanel;
|
||||
}
|
||||
|
||||
currpath[panel] = main.currentPath;
|
||||
currfile[panel] = main.currentFile;
|
||||
}
|
||||
|
||||
protected void deassociate(int panel)
|
||||
{
|
||||
for(int i = 0; i < pcount; i++)
|
||||
{
|
||||
if(associat[i] == panel)
|
||||
{
|
||||
associat[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Óçíàòü íîìåð òåêóùåé ïàíåëè
|
||||
*/
|
||||
public int currentPanel()
|
||||
{
|
||||
return currpanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Óçíàòü îáùåå ÷èñëî ïàíåëåé
|
||||
*/
|
||||
public int panelsCount()
|
||||
{
|
||||
return pcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Óçíàòü íîìåð ïðåäûäóùåé ïàíåëè
|
||||
*/
|
||||
public int prevPanel()
|
||||
{
|
||||
return prevpanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Íàéòè íåçàíÿòóþ ïàíåëü (íà êîòîðîé îòîáðàæàåòñÿ FileSelect)
|
||||
*/
|
||||
public int newPanel()
|
||||
{
|
||||
for(int i = 0; i < pcount; i++)
|
||||
{
|
||||
if(currdisp[i] == main.FileSelect && (currpath[i] == null || "".equals(currpath[i])))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return prevpanel;
|
||||
}
|
||||
|
||||
public boolean isShown(Displayable dp)
|
||||
{
|
||||
return dsp.getCurrent() == dp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ïîäãîòîâêà ê îòêðûòèþ ôàéëà filename èç ïàíåëè panel
|
||||
*/
|
||||
public void requirePanel(int panel) throws Exception
|
||||
{
|
||||
if(panel < 0 || panel >= pcount || panel == currpanel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
storePanel(currpanel);
|
||||
|
||||
prevpanelreq = currpanel;
|
||||
currpanel = panel;
|
||||
|
||||
main.FileSelect.lock();
|
||||
|
||||
/*
|
||||
int index = filename.lastIndexOf('/') + 1;
|
||||
|
||||
main.currentPath = filename.substring(0, index);
|
||||
main.currentFile = filename.substring(index);
|
||||
*/
|
||||
|
||||
main.currentPath = currpath[currpanel];
|
||||
main.currentFile = currfile[currpanel];
|
||||
|
||||
main.FileSelect.list(main.currentFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Çàâåðøåíèå ïðîöåäóðû îòêðûòèÿ ôàéëà
|
||||
*/
|
||||
public void releaseRequiredPanel()
|
||||
{
|
||||
main.FileSelect.unlock();
|
||||
|
||||
if(prevpanelreq < 0 || prevpanelreq >= pcount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currpanel = prevpanelreq;
|
||||
prevpanelreq = -1;
|
||||
|
||||
showPanel(currpanel);
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[PanelManager] " + s);
|
||||
// }
|
||||
}
|
289
src/filemanager/Renamer.java
Normal file
289
src/filemanager/Renamer.java
Normal file
@ -0,0 +1,289 @@
|
||||
package filemanager;
|
||||
|
||||
import com.vmx.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.microedition.io.Connector;
|
||||
import javax.microedition.io.file.FileConnection;
|
||||
|
||||
public class Renamer implements Runnable
|
||||
{
|
||||
protected String template;
|
||||
protected Vector files;
|
||||
protected ProgressCallback callback;
|
||||
public String error;
|
||||
public boolean interrupt;
|
||||
protected int counter;
|
||||
|
||||
public Renamer(String template, Vector filelist, ProgressCallback cb)
|
||||
{
|
||||
this.template = template;
|
||||
files = filelist;
|
||||
callback = cb;
|
||||
error = null;
|
||||
interrupt = false;
|
||||
counter = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ôóíêöèÿ, îñóùåñòâëÿþùàÿ ñîáñòâåííî ïåðåèìåíîâàíèå
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
FileConnection fc = null;
|
||||
String path = null;
|
||||
String name = null;
|
||||
int index = -1;
|
||||
boolean usecb = callback != null;
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.setMax(files.size());
|
||||
callback.setProgress(0);
|
||||
}
|
||||
|
||||
initState();
|
||||
|
||||
try
|
||||
{
|
||||
for(int i = 0; i < files.size() && !interrupt; i++)
|
||||
{
|
||||
name = (String)files.elementAt(i);
|
||||
|
||||
index = name.lastIndexOf('/') + 1;
|
||||
|
||||
path = name.substring(0, index);
|
||||
name = name.substring(index);
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.setText(name);
|
||||
}
|
||||
|
||||
fc = (FileConnection)Connector.open("file:///" + path + name);
|
||||
fc.rename(getNewName(name));
|
||||
fc.close();
|
||||
|
||||
if(usecb)
|
||||
{
|
||||
callback.progress(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception x)
|
||||
{
|
||||
error = x.toString(); // x.getClass().getName() + ": " + x.getMessage();
|
||||
|
||||
if(fc != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fc.close();
|
||||
}
|
||||
catch(Exception xx)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void initState()
|
||||
{
|
||||
String s = "";
|
||||
int len = template.length();
|
||||
boolean intoken = false;
|
||||
boolean incounter = false;
|
||||
char c;
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
c = template.charAt(i);
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case '[':
|
||||
if(intoken)
|
||||
{
|
||||
intoken = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
intoken = true;
|
||||
}
|
||||
|
||||
s += c;
|
||||
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
case 'c':
|
||||
if(intoken)
|
||||
{
|
||||
if(counter < 0)
|
||||
{
|
||||
counter = 0;
|
||||
incounter = true;
|
||||
}
|
||||
}
|
||||
|
||||
s += c;
|
||||
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
if(incounter)
|
||||
{
|
||||
counter = counter * 10 + (c - '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ']':
|
||||
if(intoken)
|
||||
{
|
||||
incounter = false;
|
||||
intoken = false;
|
||||
}
|
||||
|
||||
s += c;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
s += c;
|
||||
}
|
||||
}
|
||||
|
||||
template = s;
|
||||
}
|
||||
|
||||
protected String getNewName(String oldname)
|
||||
{
|
||||
String s = "";
|
||||
String ext = "";
|
||||
int len = template.length();
|
||||
boolean intoken = false;
|
||||
boolean incounter = false;
|
||||
char c;
|
||||
|
||||
int index = oldname.lastIndexOf('.');
|
||||
|
||||
if(index >= 0 && index < oldname.length())
|
||||
{
|
||||
ext = oldname.substring(index + 1);
|
||||
oldname = oldname.substring(0, index);
|
||||
}
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
c = template.charAt(i);
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case '[':
|
||||
if(intoken)
|
||||
{
|
||||
intoken = false;
|
||||
s += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
intoken = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
case 'n':
|
||||
if(intoken)
|
||||
{
|
||||
s += oldname;
|
||||
}
|
||||
else
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
case 'e':
|
||||
if(intoken)
|
||||
{
|
||||
s += ext;
|
||||
}
|
||||
else
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
case 'c':
|
||||
if(intoken)
|
||||
{
|
||||
s += Integer.toString(counter);
|
||||
}
|
||||
else
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case '+':
|
||||
if(intoken)
|
||||
{
|
||||
counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case '-':
|
||||
if(intoken)
|
||||
{
|
||||
counter--;
|
||||
}
|
||||
else
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
break;
|
||||
|
||||
case ']':
|
||||
if(intoken)
|
||||
{
|
||||
intoken = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
s += c;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
s += c;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// private static void out(String s)
|
||||
// {
|
||||
// System.out.println("[Renamer] " + s);
|
||||
// }
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user