diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..24db480 --- /dev/null +++ b/build.xml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/buildall.bat b/buildall.bat index 92b40bf..bf0104e 100644 --- a/buildall.bat +++ b/buildall.bat @@ -1,33 +1,28 @@ -@echo off -:begin -setlocal - -set SOURCES_DIR=src -set CLASSFILES_DIR=build.~ -set LIBRARY_PATH=lib -set JARFILE=dmgextractor.jar -set MANIFEST=meta\metafile.txt - -pushd %~dp0 -echo Removing all class files... -if not exist %CLASSFILES_DIR% mkdir %CLASSFILES_DIR% -del /f /q %CLASSFILES_DIR%\*.* -echo Incrementing build number... -java -cp .\buildenumerator BuildEnumerator src\BuildNumber.java -echo Compiling... -javac -sourcepath %SOURCES_DIR% -d %CLASSFILES_DIR% -Xlint:unchecked %SOURCES_DIR%\*.java -set JAVAC_EXIT_CODE=%ERRORLEVEL% -if not "%JAVAC_EXIT_CODE%"=="0" goto error -echo Building jar-file... -if not exist %LIBRARY_PATH% mkdir %LIBRARY_PATH% -jar cvfm %LIBRARY_PATH%\%JARFILE% %MANIFEST% -C %CLASSFILES_DIR% . >NUL: -if "%ERRORLEVEL%"=="0" (echo Done!) else echo Problems while building jar-file... -popd -goto end - -:error -echo There were errors... -goto end - -:end -endlocal +@echo off +:begin +setlocal + +set SOURCES_DIR=%~dp0src +set BUILDTOOLS_CP=%~dp0lib\buildenumerator.jar + +pushd %~dp0 + +echo Incrementing build number... +java -cp "%BUILDTOOLS_CP%" BuildEnumerator "%SOURCES_DIR%\org\catacombae\dmgextractor\BuildNumber.java" 1 +if not "%ERRORLEVEL%"=="0" goto error + +echo Building with ant... +call ant build-application +if "%ERRORLEVEL%"=="0" (echo Done!) else echo Problems while building with ant... && goto error + +popd +goto end + +:error +echo There were errors... +echo Decrementing build number... +java -cp "%BUILDTOOLS_CP%" BuildEnumerator "%SOURCES_DIR%\org\catacombae\dmgextractor\BuildNumber.java" -1 +goto end + +:end +endlocal diff --git a/buildall.sh b/buildall.sh old mode 100644 new mode 100755 index 9e04556..6140484 --- a/buildall.sh +++ b/buildall.sh @@ -1,60 +1,45 @@ -#!/bin/sh +#!/bin/bash + +SOURCES_DIR=src +BUILDTOOLS_CP=lib/buildenumerator.jar error() { echo "There were errors..." - echo "Decrementing build number..." - java -cp $BUILDTOOLS_CP BuildEnumerator $SOURCES_DIR/org/catacombae/dmgx/BuildNumber.java -1 + decrement_buildnumber + exit 1 +} +jobCompleted() { + echo "Done!" } -SOURCES_DIR=src -CLASSFILES_DIR=build.~ -LIBRARY_PATH=lib -MANIFEST=meta/metafile.txt -BUILD_CP=$CLASSFILES_DIR -#:$LIBRARY_PATH/filedrop.jar -BUILDTOOLS_CP=buildenumerator/buildenumerator.jar -JARFILE=dmgextractor.jar - -if [ -d "$CLASSFILES_DIR" ]; then # if exists $CLASSFILES_DIR... - echo "Removing all class files..." - rm -r $CLASSFILES_DIR -fi -mkdir $CLASSFILES_DIR - -echo "Extracting swing-layout to classfiles directory..." -cd $CLASSFILES_DIR -jar xf "../$LIBRARY_PATH/swing-layout-1.0.1-stripped.jar" -cd .. +increment_buildnumber() { + echo "Incrementing build number..." + java -cp $BUILDTOOLS_CP BuildEnumerator $SOURCES_DIR/org/catacombae/dmgextractor/BuildNumber.java 1 +} -echo "Extracting filedrop to classfiles directory..." -cd $CLASSFILES_DIR -jar xf "../$LIBRARY_PATH/filedrop.jar" -cd .. +decrement_buildnumber() { + echo "Decrementing build number..." + java -cp $BUILDTOOLS_CP BuildEnumerator $SOURCES_DIR/org/catacombae/dmgextractor/BuildNumber.java -1 +} -#echo "Extracting base64 to classfiles directory..." -#cd $CLASSFILES_DIR -#jar xf "../$LIBRARY_PATH/base64.jar" -#cd .. +ant_build() { + ant build-application + return $? +} -echo "Incrementing build number..." -java -cp $BUILDTOOLS_CP BuildEnumerator $SOURCES_DIR/org/catacombae/dmgx/BuildNumber.java 1 -echo "Compiling org.catacombae.dmgx..." -javac -cp $BUILD_CP -sourcepath $SOURCES_DIR -d $CLASSFILES_DIR -Xlint:deprecation -Xlint:unchecked $SOURCES_DIR/org/catacombae/dmgx/*.java -echo "Compiling org.catacombae.dmgx.gui..." -javac -cp $BUILD_CP -sourcepath $SOURCES_DIR -d $CLASSFILES_DIR -Xlint:deprecation -Xlint:unchecked $SOURCES_DIR/org/catacombae/dmgx/gui/*.java -JAVAC_EXIT_CODE=$? -if [ "$JAVAC_EXIT_CODE" == 0 ]; then - echo "Building jar-file..." - if [ ! -d "$LIBRARY_PATH" ]; then # if not exists $LIBRARY_PATH... - echo "Making library path" - mkdir $LIBRARY_PATH - fi - jar cfm $LIBRARY_PATH/$JARFILE $MANIFEST -C $CLASSFILES_DIR . +main() { + increment_buildnumber if [ "$?" == 0 ]; then - echo Done! + ant_build + if [ "$?" == 0 ]; then + jobCompleted + else + error + fi else error fi -else - error -fi +} + +# Entry point +main diff --git a/buildenumerator/BuildEnumerator.java b/buildenumerator/BuildEnumerator.java deleted file mode 100644 index 2570096..0000000 --- a/buildenumerator/BuildEnumerator.java +++ /dev/null @@ -1,26 +0,0 @@ -import java.io.*; - -public class BuildEnumerator { - public static void main(String[] args) throws IOException { - RandomAccessFile raf = new RandomAccessFile(args[0], "rw"); - String line1 = raf.readLine(); - String line2 = raf.readLine(); - long currentBuild = Long.parseLong(raf.readLine()); - String line4 = raf.readLine(); - String line5 = raf.readLine(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos); - ps.println(line1); - ps.println(line2); - ps.println(++currentBuild + ""); - System.err.println("Current build number: " + currentBuild); - ps.println(line4); - ps.println(line5); - ps.flush(); - byte[] newBytes = baos.toByteArray(); - raf.setLength(newBytes.length); - raf.seek(0); - raf.write(newBytes); - raf.close(); - } -} diff --git a/buildhfsxlib.bat b/buildhfsxlib.bat new file mode 100644 index 0000000..82bcafe --- /dev/null +++ b/buildhfsxlib.bat @@ -0,0 +1,21 @@ +@echo off +:begin +setlocal + +set SOURCES_DIR=%~dp0src + +pushd %~dp0 + +echo Building with ant... +call ant build-hfsxlib +if "%ERRORLEVEL%"=="0" (echo Done!) else echo Problems while building with ant... && goto error + +popd +goto end + +:error +echo There were errors... +goto end + +:end +endlocal diff --git a/buildhfsxlib.sh b/buildhfsxlib.sh new file mode 100755 index 0000000..e96cfdb --- /dev/null +++ b/buildhfsxlib.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +SOURCES_DIR=src + +error() { + echo "There were errors..." + exit 1 +} +jobCompleted() { + echo "Done!" +} + +ant_build() { + ant build-hfsxlib + return $? +} + +main() { + ant_build + if [ "$?" == 0 ]; then + jobCompleted + else + error + fi +} + +# Entry point +main diff --git a/buildstandalone.bat b/buildstandalone.bat new file mode 100644 index 0000000..cfd26cb --- /dev/null +++ b/buildstandalone.bat @@ -0,0 +1,21 @@ +@echo off +:begin +setlocal + +set SOURCES_DIR=%~dp0src + +pushd %~dp0 + +echo Building with ant... +call ant build-standalone +if "%ERRORLEVEL%"=="0" (echo Done!) else echo Problems while building with ant... && goto error + +popd +goto end + +:error +echo There were errors... +goto end + +:end +endlocal diff --git a/buildstandalone.sh b/buildstandalone.sh new file mode 100755 index 0000000..050fdbd --- /dev/null +++ b/buildstandalone.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +SOURCES_DIR=src + +error() { + echo "There were errors..." + exit 1 +} +jobCompleted() { + echo "Done!" +} + +ant_build() { + ant build-standalone + return $? +} + +main() { + ant_build + if [ "$?" == 0 ]; then + jobCompleted + else + error + fi +} + +# Entry point +main diff --git a/createjavadoc.bat b/createjavadoc.bat new file mode 100644 index 0000000..9c3ecd6 --- /dev/null +++ b/createjavadoc.bat @@ -0,0 +1 @@ +@call ant javadoc diff --git a/createjavadoc.sh b/createjavadoc.sh new file mode 100755 index 0000000..5bcf5cf --- /dev/null +++ b/createjavadoc.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +ant javadoc diff --git a/definevars.bat b/definevars.bat new file mode 100644 index 0000000..2fbe27d --- /dev/null +++ b/definevars.bat @@ -0,0 +1,3 @@ +set LIBDIR=%~dp0targets\application\lib + +set DMGX_CLASSPATH=%LIBDIR%\dmgextractor.jar diff --git a/definevars.sh b/definevars.sh new file mode 100755 index 0000000..fbb9385 --- /dev/null +++ b/definevars.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +export LIBDIR="targets/application/lib" + +export DMGX_CLASSPATH="$LIBDIR/dmgextractor.jar" +echo "$DMGX_CLASSPATH" diff --git a/dmginfo.sh b/dmginfo.sh old mode 100644 new mode 100755 index ea58931..9554da0 --- a/dmginfo.sh +++ b/dmginfo.sh @@ -1,2 +1,5 @@ #!/bin/sh -java -cp lib/dmgextractor.jar org.catacombae.dmgx.DMGInfoWindow $1 $2 $3 $4 $5 $6 $7 $8 $9 + +DMGX_CLASSPATH=`./definevars.sh` + +java -cp "$DMGX_CLASSPATH" org.catacombae.dmgx.DMGInfoWindow $1 $2 $3 $4 $5 $6 $7 $8 $9 diff --git a/dmgx.bat b/dmgx.bat index a495b63..0e49be9 100644 --- a/dmgx.bat +++ b/dmgx.bat @@ -1,4 +1 @@ -@echo off -pushd %~dp0 -java -cp lib\dmgextractor.jar DMGExtractor -startupcommand dmgx %1 %2 %3 %4 %5 %6 %7 %8 %9 -popd +@call "%~dp0targets\application\bin\dmgx.bat" %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/dmgx.sh b/dmgx.sh new file mode 100755 index 0000000..5c33c47 --- /dev/null +++ b/dmgx.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +DMGX_CLASSPATH=`./definevars.sh` + +java -cp "$DMGX_CLASSPATH" org.catacombae.dmgextractor.DMGExtractor -startupcommand "$0" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" diff --git a/doc/changelog.txt b/doc/changelog.txt new file mode 100644 index 0000000..f6c604d --- /dev/null +++ b/doc/changelog.txt @@ -0,0 +1,50 @@ +DMGExtractor changelog +---------------------- + +0.70 (build 437) +---------------- +- Support for AES-128 encrypted .dmg images added after studying VileFault + ( http://crypto.nsa.org/vilefault/ ). +- Program now suggests a sensible default output file. +- User interaction slightly overhauled when restructuring the program. Should be more + informative and practical in many cases. +- Program now accepts raw .dmg files, though a warning is issued telling the user that + essentially the program will only copy the file contents from one location to another. +- Developers: The package org.catacombae.udif moved to org.catacombae.dmg.udif, and a new + package called org.catacombae.dmg.encrypted was created where the implementation of the + CEncryptedEncoding layer resides. Developers can easily read encrypted .dmg format files + with the class: + org.catacombae.dmg.encrypted.ReadableCEncryptedEncodingStream + +0.60 (build 386) +---------------- +- Upgraded license to GPL version 3 in order to use the Apache bzip2 code which is released + under the Apache Software License Version 2.0, incompatible with GPLv2, but compatible + with GPLv3. +- Support for bzip2-compressed images (type UDBZ) through the Apache Ant bzip2 library. +- APX XML parser overhaul, reducing memory footprint and working with streams/readers + instead of arrays when getting results from the parser. This should result in an ability + to extract larger DMG files. +- Made DMGExtractor startable through Java Web Start. +- Restructured the app, separating the application and the library part. For developers + that wish to process UDIF disk images there are two simple classes that you should take a + look at: + org.catacombae.udif.UDIFRandomAccessStream (for random read access to UDIF disk images) + org.catacombae.udif.UDIFInputStream (for just reading the contents sequentially) + +0.51pre1 (build 226) +-------------------- +- Switched XML-parser to a homebrew parser after getting frustrated with not being able + to turn off the SAX parser's inability to work in offline conditions, just because it + needed to contact www.apple.com every single time to get a DTD. + If my own XML parser bugs out, you can always supply the switch -saxparser to the command + line to use the SAX parser. +- Worked further on compatibility issues... discovered a new block type which supposedly + also means zero fill... at least in the cases I tested. The new version should be more + compatible than the previous (I have no DMG images in my possession that it should handle + and yet doesn't). +- Code cleanup started, but needs more work. It's a mess... + +0.5 (build 48) +-------------- +- First release diff --git a/extractplist.sh b/extractplist.sh new file mode 100755 index 0000000..5e12762 --- /dev/null +++ b/extractplist.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +DMGX_CLASSPATH=`./definevars.sh` + +java -cp "$DMGX_CLASSPATH" org.catacombae.dmgx.ExtractPlist $1 $2 $3 $4 $5 $6 $7 $8 $9 diff --git a/hfsx.bat b/hfsx.bat deleted file mode 100644 index 8c8b93a..0000000 --- a/hfsx.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -pushd %~dp0 -java -cp lib\dmgexplorer.jar HFSExplorer %1 %2 %3 %4 %5 %6 %7 %8 %9 -popd diff --git a/lib/.cvsignore b/lib/.cvsignore new file mode 100644 index 0000000..38d196e --- /dev/null +++ b/lib/.cvsignore @@ -0,0 +1,4 @@ +*~ +*.class +.DS_Store +Thumbs.db diff --git a/buildenumerator/buildenumerator.jar b/lib/buildenumerator.jar similarity index 100% rename from buildenumerator/buildenumerator.jar rename to lib/buildenumerator.jar diff --git a/lib/dmgextractor.jar b/lib/dmgextractor.jar deleted file mode 100644 index d9da8e1..0000000 Binary files a/lib/dmgextractor.jar and /dev/null differ diff --git a/lib/swing-layout-1.0.1-stripped.jar b/lib/swing-layout-1.0.1-stripped.jar deleted file mode 100644 index 04bcad4..0000000 Binary files a/lib/swing-layout-1.0.1-stripped.jar and /dev/null differ diff --git a/makebindist.sh b/makebindist.sh new file mode 100755 index 0000000..05bb32f --- /dev/null +++ b/makebindist.sh @@ -0,0 +1,99 @@ +#!/bin/sh + +match () { + local SUBFILE="$1" + local DIRNAME="$2" + CMPRES=`expr "${SUBFILE}" : "${DIRNAME}"` + RETVAL="$?" + if [ "${RETVAL}" -eq 0 ]; then + if [ -d "${SUBFILE}" ]; then + echo "`pwd`/${SUBFILE}/" + rm -r "${SUBFILE}" + if [ ! $? -eq 0 ]; then return 1; fi + else + echo "`pwd`/${SUBFILE}" + rm "${SUBFILE}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + elif [ "${RETVAL}" -eq 1 ]; then + if [ -d "${SUBFILE}" ]; then + recursiveRmdir "${DIRNAME}" "${SUBFILE}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + fi + return 0 +} + +recursiveRmdir () { + local DIRNAME="$1" + #local DIRNAMELEN="${#DIRNAME}" + shift 1 + while [ ! $# -eq 0 ]; do + local FILE="$1" + shift 1 + + if [ -d "${FILE}" ]; then + + pushd "${FILE}" > /dev/null + + for SUBFILE in .*; do + if [ ! "${SUBFILE}" = ".." ] && [ ! "${SUBFILE}" = "." ]; then + match "${SUBFILE}" "${DIRNAME}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + done + + for SUBFILE in *; do + if [ ! "${SUBFILE}" = "*" ]; then + match "${SUBFILE}" "${DIRNAME}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + done + + popd > /dev/null + else + match "${FILE}" "${DIRNAME}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + done + return 0 +} + +error () { + echo "There were errors..." + exit 1 +} + +checkerror () { + if [ ! $1 -eq 0 ]; then error; fi +} + +TEMPDIR="disttemp.~" +DISTDIR="targets/application" +OUTFILE="releases/current-bin.zip" + +echo "Cleaning temp dir..." +rm -r "${TEMPDIR}" +mkdir "${TEMPDIR}" +checkerror $? + +echo "Copying files..." +cp -r ${DISTDIR}/* "${TEMPDIR}" +checkerror $? + +echo "Setting execute permissions for shell scripts..." +chmod a+x ${TEMPDIR}/bin/*.sh +checkerror $? + +echo "Removing CVS directories..." +recursiveRmdir "^CVS$" "${TEMPDIR}" +checkerror $? + +echo "Building zip file..." +cd "${TEMPDIR}" +rm "../${OUTFILE}" +zip -9 -r "../${OUTFILE}" * +checkerror $? +cd .. + +echo "Done! Zip file generated in ${OUTFILE}" diff --git a/makesrcdist.sh b/makesrcdist.sh new file mode 100755 index 0000000..197110d --- /dev/null +++ b/makesrcdist.sh @@ -0,0 +1,130 @@ +#!/bin/bash + +match () { + local SUBFILE="$1" + local DIRNAME="$2" + CMPRES=`expr "${SUBFILE}" : "${DIRNAME}"` + RETVAL="$?" + if [ "${RETVAL}" -eq 0 ]; then + if [ -d "${SUBFILE}" ]; then + echo "`pwd`/${SUBFILE}/" + rm -r "${SUBFILE}" + if [ ! $? -eq 0 ]; then return 1; fi + else + echo "`pwd`/${SUBFILE}" + rm "${SUBFILE}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + elif [ "${RETVAL}" -eq 1 ]; then + if [ -d "${SUBFILE}" ]; then + recursiveRmdir "${DIRNAME}" "${SUBFILE}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + fi + return 0 +} + +recursiveRmdir () { + local DIRNAME="$1" + #local DIRNAMELEN="${#DIRNAME}" + shift 1 + while [ ! $# -eq 0 ]; do + local FILE="$1" + shift 1 + + if [ -d "${FILE}" ]; then + + pushd "${FILE}" > /dev/null + + for SUBFILE in .*; do + if [ ! "${SUBFILE}" = ".." ] && [ ! "${SUBFILE}" = "." ]; then + match "${SUBFILE}" "${DIRNAME}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + done + + for SUBFILE in *; do + if [ ! "${SUBFILE}" = "*" ]; then + match "${SUBFILE}" "${DIRNAME}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + done + + popd > /dev/null + else + match "${FILE}" "${DIRNAME}" + if [ ! $? -eq 0 ]; then return 1; fi + fi + done + return 0 +} + +error () { + echo "There were errors..." + exit 1 +} + +checkerror () { + if [ ! $1 -eq 0 ]; then error; fi +} + +TEMPDIR="srcdisttemp.~" +OUTFILE="releases/current-src.zip" + +copydir () { + cp -r $1 "${TEMPDIR}" + checkerror $? +} + +echo "Cleaning temp dir..." +rm -r "${TEMPDIR}" +mkdir "${TEMPDIR}" +checkerror $? + +echo "Copying files..." +cp *.sh ${TEMPDIR} +checkerror $? +cp *.bat ${TEMPDIR} +checkerror $? +cp *.xml ${TEMPDIR} +checkerror $? + +copydir lib +copydir src +copydir src.JNLP-INF +copydir src.META-INF +copydir targets + +echo "Removing CVS directories..." +recursiveRmdir "^CVS$" "${TEMPDIR}" +checkerror $? +echo "Removing emacs backup files (*~)..." +recursiveRmdir ".*~$" "${TEMPDIR}" +checkerror $? +echo "Removing emacs temporary files (#*#)..." +recursiveRmdir "^#.*#$" "${TEMPDIR}" +checkerror $? +echo "Removing Thumbs.db files..." +recursiveRmdir "^Thumbs\.db$" "${TEMPDIR}" +checkerror $? +echo "Removing .DS_Store files..." +recursiveRmdir "^\.DS_Store$" "${TEMPDIR}" +checkerror $? +echo "Removing .cvsignore files..." +recursiveRmdir "^\.cvsignore$" "${TEMPDIR}" +checkerror $? + +echo "Setting execute permissions for shell scripts..." +chmod a+x ${TEMPDIR}/*.sh +checkerror $? +chmod a+x ${TEMPDIR}/targets/application/bin/*.sh +checkerror $? + +echo "Building zip file..." +cd "${TEMPDIR}" +rm "../${OUTFILE}" +zip -9 -r "../${OUTFILE}" * +checkerror $? +cd .. + +echo "Done! Zip file generated in ${OUTFILE}" diff --git a/meta/metafile.txt b/meta/metafile.txt deleted file mode 100644 index 9a57cee..0000000 --- a/meta/metafile.txt +++ /dev/null @@ -1 +0,0 @@ -Main-Class: DMGExtractorGraphical diff --git a/src.JNLP-INF/standalone/dmgextractor.jnlp b/src.JNLP-INF/standalone/dmgextractor.jnlp new file mode 100644 index 0000000..c99f216 --- /dev/null +++ b/src.JNLP-INF/standalone/dmgextractor.jnlp @@ -0,0 +1,24 @@ + + + + + DMGExtractor + Catacombae Software + An application that extracts the contents of UDIF disk images, usually with file extension .dmg, to raw data (like .iso files). + + Extracts the contents of .dmg files. + + + + + + + + + + + + + + + diff --git a/src.JNLP-INF/standalone/jnlp-notes.txt b/src.JNLP-INF/standalone/jnlp-notes.txt new file mode 100644 index 0000000..beb2dcf --- /dev/null +++ b/src.JNLP-INF/standalone/jnlp-notes.txt @@ -0,0 +1,4 @@ +För att få en jnlp-fil att köra med alla privilegier som ett vanligt Java-program har +behöver man dels se till att en exakt kopia av filen ligger lagrad i huvud-JARen under +JNLP-INF\APPLICATION.JNLP (uppercase är viktigt), och dels måste man signera JAR-filen. +Har gjort ett litet script signjar.bat för det ändamålet. diff --git a/src.META-INF/application/MANIFEST.MF b/src.META-INF/application/MANIFEST.MF new file mode 100644 index 0000000..0b96277 --- /dev/null +++ b/src.META-INF/application/MANIFEST.MF @@ -0,0 +1,2 @@ +Main-Class: org.catacombae.dmgextractor.DMGExtractorGraphical +Class-Path: csframework.jar apache-ant-1.7.0-bzip2.jar iharder-base64.jar swing-layout-1.0.3.jar diff --git a/src.META-INF/standalone/MANIFEST.MF b/src.META-INF/standalone/MANIFEST.MF new file mode 100644 index 0000000..e0767f7 --- /dev/null +++ b/src.META-INF/standalone/MANIFEST.MF @@ -0,0 +1 @@ +Main-Class: org.catacombae.dmgextractor.DMGExtractorGraphical diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/Base64.java b/src/Base64.java deleted file mode 100644 index c6d79b6..0000000 --- a/src/Base64.java +++ /dev/null @@ -1,1449 +0,0 @@ -/** - * Encodes and decodes to and from Base64 notation. - * - *

- * Change Log: - *

- *
    - *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
  • - *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
  • - *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
  • - *
  • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
  • - *
  • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
  • - *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
  • - *
  • v1.4 - Added helper methods to read/write files.
  • - *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • - *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
  • - *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • - *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • - *
- * - *

- * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

- * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.1 - */ -public class Base64 -{ - -/* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify encoding. */ - public final static int ENCODE = 1; - - - /** Specify decoding. */ - public final static int DECODE = 0; - - - /** Specify that data should be gzip-compressed. */ - public final static int GZIP = 2; - - - /** Don't break lines when encoding (violates strict Base64 specification) */ - public final static int DONT_BREAK_LINES = 8; - - -/* ******** P R I V A T E F I E L D S ******** */ - - - /** Maximum line length (76) of Base64 output. */ - private final static int MAX_LINE_LENGTH = 76; - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** The new line character (\n) as a byte. */ - private final static byte NEW_LINE = (byte)'\n'; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "UTF-8"; - - - /** The 64 valid Base64 values. */ - private final static byte[] ALPHABET; - private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ - { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' - }; - - /** Determine which ALPHABET to use. */ - static - { - byte[] __bytes; - try - { - __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException use) - { - __bytes = _NATIVE_ALPHABET; // Fall back to native encoding - } // end catch - ALPHABET = __bytes; - } // end static - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - // I think I end up not using the BAD_ENCODING indicator. - //private final static byte BAD_ENCODING = -9; // Indicates error in encoding - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - - /** Defeats instantiation. */ - private Base64(){} - - - -/* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) - { - encode3to4( threeBytes, 0, numSigBytes, b4, 0 ); - return b4; - } // end encode3to4 - - - /** - * Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset ) - { - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) - | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) - | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); - - switch( numSigBytes ) - { - case 3: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; - return destination; - - case 2: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - case 1: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = EQUALS_SIGN; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - *

- * Valid options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DONT_BREAK_LINES: don't break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeObject( myObj, Base64.GZIP ) or - *

- * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - { - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.io.ObjectOutputStream oos = null; - java.util.zip.GZIPOutputStream gzos = null; - - // Isolate options - int gzip = (options & GZIP); - int dontBreakLines = (options & DONT_BREAK_LINES); - - try - { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); - - // GZip? - if( gzip == GZIP ) - { - gzos = new java.util.zip.GZIPOutputStream( b64os ); - oos = new java.io.ObjectOutputStream( gzos ); - } // end if: gzip - else - oos = new java.io.ObjectOutputStream( b64os ); - - oos.writeObject( serializableObject ); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - return null; - } // end catch - finally - { - try{ oos.close(); } catch( Exception e ){} - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @since 1.4 - */ - public static String encodeBytes( byte[] source ) - { - return encodeBytes( source, 0, source.length, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Valid options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DONT_BREAK_LINES: don't break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param options Specified options - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int options ) - { - return encodeBytes( source, 0, source.length, options ); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @since 1.4 - */ - public static String encodeBytes( byte[] source, int off, int len ) - { - return encodeBytes( source, off, len, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Valid options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DONT_BREAK_LINES: don't break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int off, int len, int options ) - { - // Isolate options - int dontBreakLines = ( options & DONT_BREAK_LINES ); - int gzip = ( options & GZIP ); - - // Compress? - if( gzip == GZIP ) - { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - - try - { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - - gzos.write( source, off, len ); - gzos.close(); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - return null; - } // end catch - finally - { - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else - { - // Convert option to boolean in way that code likes it. - boolean breakLines = dontBreakLines == 0; - - int len43 = len * 4 / 3; - byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for( ; d < len2; d+=3, e+=4 ) - { - encode3to4( source, d+off, 3, outBuff, e ); - - lineLength += 4; - if( breakLines && lineLength == MAX_LINE_LENGTH ) - { - outBuff[e+4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if( d < len ) - { - encode3to4( source, d+off, len - d, outBuff, e ); - e += 4; - } // end if: some padding needed - - - // Return value according to relevant encoding. - try - { - return new String( outBuff, 0, e, PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( outBuff, 0, e ); - } // end catch - - } // end else: don't compress - - } // end encodeBytes - - - - - -/* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the number of decoded bytes converted - * @since 1.3 - */ - private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) - { - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else - { - try{ - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - }catch( Exception e){ - System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); - System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); - System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); - System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); - return -1; - } //e nd catch - } - } // end decodeToBytes - - - - - /** - * Very low-level access to decoding ASCII characters in - * the form of a byte array. Does not support automatically - * gunzipping or any other "fancy" features. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @return decoded data - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len ) - { - int len34 = len * 3 / 4; - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; - - byte[] b4 = new byte[4]; - int b4Posn = 0; - int i = 0; - byte sbiCrop = 0; - byte sbiDecode = 0; - for( i = off; i < off+len; i++ ) - { - sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits - sbiDecode = DECODABET[ sbiCrop ]; - - if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better - { - if( sbiDecode >= EQUALS_SIGN_ENC ) - { - b4[ b4Posn++ ] = sbiCrop; - if( b4Posn > 3 ) - { - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( sbiCrop == EQUALS_SIGN ) - break; - } // end if: quartet built - - } // end if: equals sign or better - - } // end if: white space, equals sign or better - else - { - System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); - return null; - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @since 1.4 - */ - public static byte[] decode( String s ) - { - byte[] bytes; - try - { - bytes = s.getBytes( PREFERRED_ENCODING ); - } // end try - catch( java.io.UnsupportedEncodingException uee ) - { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode( bytes, 0, bytes.length ); - - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - if( bytes != null && bytes.length >= 4 ) - { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) - { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try - { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) - { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) - { - // Just return originally-decoded bytes - } // end catch - finally - { - try{ baos.close(); } catch( Exception e ){} - try{ gzis.close(); } catch( Exception e ){} - try{ bais.close(); } catch( Exception e ){} - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @since 1.5 - */ - public static Object decodeToObject( String encodedObject ) - { - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try - { - bais = new java.io.ByteArrayInputStream( objBytes ); - ois = new java.io.ObjectInputStream( bais ); - - obj = ois.readObject(); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - obj = null; - } // end catch - catch( java.lang.ClassNotFoundException e ) - { - e.printStackTrace(); - obj = null; - } // end catch - finally - { - try{ bais.close(); } catch( Exception e ){} - try{ ois.close(); } catch( Exception e ){} - } // end finally - - return obj; - } // end decodeObject - - - - /** - * Convenience method for encoding data to a file. - * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean encodeToFile( byte[] dataToEncode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - success = true; - } // end try - catch( java.io.IOException e ) - { - - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean decodeToFile( String dataToDecode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - success = true; - } // end try - catch( java.io.IOException e ) - { - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - * @param filename Filename for reading encoded data - * @return decoded byte array or null if unsuccessful - * - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - { - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); - return null; - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error decoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - * @param filename Filename for reading binary data - * @return base64-encoded string or null if unsuccessful - * - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - { - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ (int)(file.length() * 1.4) ]; - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error encoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream - { - private boolean encode; // Encoding or decoding - private int position; // Current position in the buffer - private byte[] buffer; // Small buffer holding converted data - private int bufferLength; // Length of buffer (3 or 4) - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - private boolean breakLines; // Break lines at less than 80 characters - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream( java.io.InputStream in ) - { - this( in, DECODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DONT_BREAK_LINES: don't break lines at 76 characters
-         *     (only meaningful when encoding)
-         *     Note: Technically, this makes your encoding non-compliant.
-         * 
- *

- * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public InputStream( java.io.InputStream in, int options ) - { - super( in ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[ bufferLength ]; - this.position = -1; - this.lineLength = 0; - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @since 1.3 - */ - public int read() throws java.io.IOException - { - // Do we need to get data? - if( position < 0 ) - { - if( encode ) - { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for( int i = 0; i < 3; i++ ) - { - try - { - int b = in.read(); - - // If end of stream, b is -1. - if( b >= 0 ) - { - b3[i] = (byte)b; - numBinaryBytes++; - } // end if: not end of stream - - } // end try: read - catch( java.io.IOException e ) - { - // Only a problem if we got no data at all. - if( i == 0 ) - throw e; - - } // end catch - } // end for: each needed input byte - - if( numBinaryBytes > 0 ) - { - encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); - position = 0; - numSigBytes = 4; - } // end if: got data - else - { - return -1; - } // end else - } // end if: encoding - - // Else decoding - else - { - byte[] b4 = new byte[4]; - int i = 0; - for( i = 0; i < 4; i++ ) - { - // Read four "meaningful" bytes: - int b = 0; - do{ b = in.read(); } - while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); - - if( b < 0 ) - break; // Reads a -1 if end of stream - - b4[i] = (byte)b; - } // end for: each needed input byte - - if( i == 4 ) - { - numSigBytes = decode4to3( b4, 0, buffer, 0 ); - position = 0; - } // end if: got four characters - else if( i == 0 ){ - return -1; - } // end else if: also padded correctly - else - { - // Must have broken out from above. - throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if( position >= 0 ) - { - // End of relevant data? - if( /*!encode &&*/ position >= numSigBytes ) - return -1; - - if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) - { - lineLength = 0; - return '\n'; - } // end if - else - { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[ position++ ]; - - if( position >= bufferLength ) - position = -1; - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else - { - // When JDK1.4 is more accepted, use an assertion here. - throw new java.io.IOException( "Error in Base64 code reading stream." ); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - public int read( byte[] dest, int off, int len ) throws java.io.IOException - { - int i; - int b; - for( i = 0; i < len; i++ ) - { - b = read(); - - //if( b < 0 && i == 0 ) - // return -1; - - if( b >= 0 ) - dest[off + i] = (byte)b; - else if( i == 0 ) - return -1; - else - break; // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream - { - private boolean encode; - private int position; - private byte[] buffer; - private int bufferLength; - private int lineLength; - private boolean breakLines; - private byte[] b4; // Scratch used in a few places - private boolean suspendEncoding; - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out ) - { - this( out, ENCODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DONT_BREAK_LINES: don't break lines at 76 characters
-         *     (only meaningful when encoding)
-         *     Note: Technically, this makes your encoding non-compliant.
-         * 
- *

- * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out, int options ) - { - super( out ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[ bufferLength ]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @since 1.3 - */ - public void write(int theByte) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theByte ); - return; - } // end if: supsended - - // Encode? - if( encode ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to encode. - { - out.write( encode3to4( b4, buffer, bufferLength ) ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) - { - out.write( NEW_LINE ); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else - { - // Meaningful Base64 character? - if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to output. - { - int len = Base64.decode4to3( buffer, 0, b4, 0 ); - out.write( b4, 0, len ); - //out.write( Base64.decode4to3( buffer ) ); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) - { - throw new java.io.IOException( "Invalid character in Base64 data." ); - } // end else: not white space either - } // end else: decoding - } // end write - - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @since 1.3 - */ - public void write( byte[] theBytes, int off, int len ) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theBytes, off, len ); - return; - } // end if: supsended - - for( int i = 0; i < len; i++ ) - { - write( theBytes[ off + i ] ); - } // end for: each byte written - - } // end write - - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - */ - public void flushBase64() throws java.io.IOException - { - if( position > 0 ) - { - if( encode ) - { - out.write( encode3to4( b4, buffer, position ) ); - position = 0; - } // end if: encoding - else - { - throw new java.io.IOException( "Base64 input not properly padded." ); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @since 1.3 - */ - public void close() throws java.io.IOException - { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException - { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() - { - this.suspendEncoding = false; - } // end resumeEncoding - - - - } // end inner class OutputStream - - -} // end class Base64 diff --git a/src/DMGExtractor.java b/src/DMGExtractor.java deleted file mode 100644 index c33dcd3..0000000 --- a/src/DMGExtractor.java +++ /dev/null @@ -1,826 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * (C) 2004 vu1tur (not the actual code but...) - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -import java.io.*; -import java.util.LinkedList; -import java.util.Iterator; -import java.util.zip.Inflater; -import java.util.zip.DataFormatException; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.swing.JOptionPane; -import javax.swing.JFileChooser; -import javax.swing.ProgressMonitor; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -public class DMGExtractor { - public static final String APPNAME = "DMGExtractor 0.51pre"; - public static final String BUILDSTRING = "(Build #" + BuildNumber.BUILD_NUMBER + ")"; - public static final boolean DEBUG = false; - // Constants defining block types in the dmg file - public static final int BT_ADC = 0x80000004; - public static final int BT_ZLIB = 0x80000005; - public static final int BT_BZIP2 = 0x80000006; - public static final int BT_COPY = 0x00000001; - public static final int BT_ZERO = 0x00000002; - public static final int BT_END = 0xffffffff; - public static final int BT_UNKNOWN = 0x7ffffffe; - public static final long PLIST_ADDRESS_1 = 0x1E0; - public static final long PLIST_ADDRESS_2 = 0x128; - public static final String BACKSPACE79 = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; -// public static PrintStream stdout = System.out; -// public static PrintStream stderr = System.err; - public static BufferedReader stdin = - new BufferedReader(new InputStreamReader(System.in)); - - public static boolean verbose = false; - public static boolean graphical = false; - public static String startupCommand = "java DMGExtractor"; - public static File dmgFile = null; - public static File isoFile = null; - - public static ProgressMonitor progmon; - - public static void main(String[] args) throws Exception { - try { - notmain(args); - } catch(Exception e) { - if(graphical) - JOptionPane.showMessageDialog(null, "The program encountered an unexpected error: " + e.toString() + - "\nClosing...", "Error", JOptionPane.ERROR_MESSAGE); - throw e; - } - } - - public static void notmain(String[] args) throws Exception { - System.setProperty("swing.aatext", "true"); //Antialiased text - try { javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName()); } - catch(Exception e) {} - - if(DEBUG) verbose = true; - - parseArgs(args); - - printlnVerbose("Processing: " + dmgFile); - RandomAccessFile dmgRaf = new RandomAccessFile(dmgFile, "r"); - RandomAccessFile isoRaf = null; - boolean testOnly = false; - if(isoFile != null) { - isoRaf = new RandomAccessFile(isoFile, "rw"); - isoRaf.setLength(0); - printlnVerbose("Extracting to: " + isoFile); - } - else { - testOnly = true; - printlnVerbose("Simulating extraction..."); - } - - dmgRaf.seek(dmgRaf.length()-PLIST_ADDRESS_1); - long plistBegin1 = dmgRaf.readLong(); - long plistEnd = dmgRaf.readLong(); - dmgRaf.seek(dmgRaf.length()-PLIST_ADDRESS_2); - long plistBegin2 = dmgRaf.readLong(); - long plistSize = dmgRaf.readLong(); - - if(DEBUG) { - println("Read addresses:", - " " + plistBegin1, - " " + plistBegin2); - } - if(plistBegin1 != plistBegin2) { - println("Addresses not equal! Assumption broken... =/", - plistBegin1 + " != " + plistBegin2); - System.exit(0); - } - if(plistSize != (plistEnd-plistBegin1)) { - println("plistSize field does not match plistEnd marker!", - "plistSize=" + plistSize + " plistBegin1=" + plistBegin1 + " plistEnd=" + plistEnd + " plistEnd-plistBegin1=" + (plistEnd-plistBegin1)); - } - printlnVerbose("Jumping to address..."); - dmgRaf.seek(plistBegin1); - byte[] buffer = new byte[(int)plistSize]; - dmgRaf.read(buffer); - - InputStream is = new ByteArrayInputStream(buffer); - - NodeBuilder handler = new NodeBuilder(); - SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); - try { -// System.out.println("validation: " + saxParser.getProperty("validation")); -// System.out.println("external-general-entities: " + saxParser.getProperty("external-general-entities")); -// System.out.println("external-parameter-entities: " + saxParser.getProperty("external-parameter-entities")); -// System.out.println("is-standalone: " + saxParser.getProperty("is-standalone")); -// System.out.println("lexical-handler: " + saxParser.getProperty("lexical-handler")); -// System.out.println("parameter-entities: " + saxParser.getProperty("parameter-entities")); -// System.out.println("namespaces: " + saxParser.getProperty("namespaces")); -// System.out.println("namespace-prefixes: " + saxParser.getProperty("namespace-prefixes")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); - System.out.println("isValidating: " + saxParser.isValidating()); - saxParser.parse(is, handler); - } catch(SAXException se) { - se.printStackTrace(); - System.err.println("Could not read the partition list... exiting."); - System.exit(1); - } - - XMLNode[] rootNodes = handler.getRoots(); - if(rootNodes.length != 1) { - println("Could not parse DMG-file!"); - System.exit(0); - } - - /* Ok, now we have a tree built from the XML-document. Let's walk to the right place. */ - /* cd plist - cd dict - cdkey resource-fork (type:dict) - cdkey blkx (type:array) */ - XMLNode current; - XMLElement[] children; - boolean keyFound; - current = rootNodes[0]; //We are at plist... probably (there should be only one root node) - - current = current.cd("dict"); - current = current.cdkey("resource-fork"); - current = current.cdkey("blkx"); - printlnVerbose("Found " + current.getChildren().length + " partitions:"); - - byte[] tmp = new byte[0x40000]; - byte[] otmp = new byte[0x40000]; - - byte[] zeroblock = new byte[4096]; - /* I think java always zeroes its arrays on creation... - but let's play safe. */ - for(int y = 0; y < zeroblock.length; ++y) - zeroblock[y] = 0; - - LinkedList blocks = new LinkedList(); - - //long lastOffs = 0; - long lastOutOffset = 0; - long lastInOffset = 0; - long totalSize = 0; - boolean errorsFound = false; - reportProgress(0); - for(XMLElement xe : current.getChildren()) { - if(progmon != null && progmon.isCanceled()) System.exit(0); - if(xe instanceof XMLNode) { - XMLNode xn = (XMLNode)xe; - byte[] data = Base64.decode(xn.getKeyValue("Data")); - - long partitionSize = calculatePartitionSize(data); - totalSize += partitionSize; - - printlnVerbose(" " + xn.getKeyValue("Name")); - printlnVerbose(" ID: " + xn.getKeyValue("ID")); - printlnVerbose(" Attributes: " + xn.getKeyValue("Attributes")); - printlnVerbose(" Partition map data length: " + data.length + " bytes"); - printlnVerbose(" Partition size: " + partitionSize + " bytes"); - if(verbose) { - printlnVerbose(" Dumping blkx..."); - FileOutputStream fos = new FileOutputStream(xn.getKeyValue("ID") + ".blkx"); - fos.write(data); - fos.close(); - } - - if(DEBUG) { - File dumpFile = new File("data " + xn.getKeyValue("ID") + ".bin"); - println(" Dumping partition map to file: " + dumpFile); - - FileOutputStream dump = new FileOutputStream(dumpFile); - dump.write(data); - dump.close(); - } - - int offset = 0xCC; - int blockType = 0; - - /* Offset of the input data for the current block in the input file */ - long inOffset = 0; - /* Size of the input data for the current block */ - long inSize = 0; - /* Offset of the output data for the current block in the output file */ - long outOffset = 0; - /* Size of the output data (possibly larger than inSize because of - decompression, zero expansion...) */ - long outSize = 0; - - long lastByteReadInBlock = -1; - - boolean addInOffset = false; - - //, lastInOffs = 0; - int blockCount = 0; - long previousPercentage = -1; - while(blockType != BT_END) { - if(progmon != null && progmon.isCanceled()) System.exit(0); - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); - int bytesSkipped = 0; - while(bytesSkipped < offset) - bytesSkipped += dis.skipBytes(offset-bytesSkipped); - - blockType = dis.readInt(); - int skipped = dis.readInt(); //Skip 4 bytes forward - outOffset = dis.readLong()*0x200;//(dis.readInt() & 0xffffffffL)*0x200; //unsigned int -> long - //dis.readInt(); //Skip 4 bytes forward - outSize = dis.readLong()*0x200;//(dis.readInt() & 0xffffffffL)*0x200; //unsigned int -> long - inOffset = dis.readLong();// & 0xffffffffL; //unsigned int -> long - //dis.readInt(); //Skip 4 bytes forward - inSize = dis.readLong();//dis.readInt() & 0xffffffffL; //unsigned int -> long - - blocks.add(new DMGBlock(blockType, skipped, outOffset, outSize, inOffset, inSize)); - - if(lastByteReadInBlock == -1) - lastByteReadInBlock = inOffset; - lastByteReadInBlock += inSize; - - /* The lines below are a "hack" that I had to do to make dmgx work with - certain dmg-files. I don't understand the issue at all, which is why - this hack is here, but sometimes inOffset == 0 means that it is 0 - relative to the previous partition's last inOffset. And sometimes it - doesn't (meaning the actual position 0 in the dmg file). */ - if(addInOffset) - inOffset += lastInOffset; - else if(inOffset == 0) { - addInOffset = true; - inOffset += lastInOffset; - } - outOffset += lastOutOffset; - - if(DEBUG) { - println("outOffset=" + outOffset + " outSize=" + outSize + - " inOffset=" + inOffset + " inSize=" + inSize + - " lastOutOffset=" + lastOutOffset + " lastInOffset=" + lastInOffset - /*+ " lastInOffs=" + lastInOffs + " lastOffs=" + lastOffs*/); - } - - if(blockType == BT_ADC) { - println(" " + blockCount + ". BT_ADC not supported."); - if(!testOnly) - System.exit(0); - } - else if(blockType == BT_ZLIB) { - if(DEBUG) - println(" " + blockCount + ". BT_ZLIB processing..."); - - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_ZLIB FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - dmgRaf.seek(/*lastOffs+*/inOffset); - - if(tmp.length < inSize) - tmp = new byte[(int)inSize]; - - long totalBytesRead = 0; - while(totalBytesRead < inSize) { - totalBytesRead += dmgRaf.read(tmp, (int)totalBytesRead, Math.min((int)(inSize-totalBytesRead), tmp.length)); - } - long progressPercentage = dmgRaf.getFilePointer()*100/dmgRaf.length(); - if(progressPercentage != previousPercentage) { - reportProgress(progressPercentage); - previousPercentage = progressPercentage; - } - - Inflater inflater = new Inflater(); - inflater.setInput(tmp, 0, (int)totalBytesRead); - - if(otmp.length < outSize) - otmp = new byte[(int)outSize]; - - int bytesInflated = 0; - while(true) { - try { - int counter = 0; - while(bytesInflated < outSize && counter++ < 10) { - int old = bytesInflated; - bytesInflated += inflater.inflate(otmp, bytesInflated, (int)(outSize-bytesInflated)); - if(old == bytesInflated) - println("Nothing new! finished()=" + inflater.finished() + " needsInput()=" + inflater.needsInput() + " needsDictionary()=" + inflater.needsDictionary() + " getAdler()=" + inflater.getAdler() + " getBytesRead()=" + inflater.getBytesRead() + " getBytesWritten()=" + inflater.getBytesWritten() + " getRemaining()=" + inflater.getRemaining()); - } - //System.out.println(" Inflated " + bytesInflated + " bytes. Left in buffer: " + inflater.getRemaining() + " bytes inSize="+inSize+" outSize="+outSize); - - if(inflater.getRemaining() == 0) { - //System.out.println(" done!"); - break; - } - else { - println(" " + blockCount + ". BT_ZLIB ERROR: otmp contents lost! (should not happen...)", - " outSize=" + outSize + " inSize=" + inSize + " tmp.length=" + tmp.length + " otmp.length=" + otmp.length + " bytesInflated=" + bytesInflated + " inflater.getRemaining()=" + inflater.getRemaining()); -// if(bytesInflated == 0) - throw new RuntimeException("WTF"); - } - } - catch(DataFormatException dfe) { - println(" " + blockCount + ". BT_ZLIB Could not decode..."); - if(!DEBUG) { - println("outOffset=" + outOffset + " outSize=" + outSize + - " inOffset=" + inOffset + " inSize=" + inSize + - " lastOutOffset=" + lastOutOffset + " lastInOffset=" + lastInOffset); - } - dfe.printStackTrace(); - if(!testOnly) - System.exit(0); - else { - println(" Testing mode, so continuing..."); - //System.exit(0); - errorsFound = true; - break; - } - } - - } - inflater.end(); - - if(!testOnly) - isoRaf.write(otmp, 0, (int)outSize); - - //lastInOffs = inOffset+inSize; - } - else if(blockType == BT_BZIP2) { - println(" " + blockCount + ". BT_BZIP2 not currently supported."); - if(!testOnly) - System.exit(0); - } - else if(blockType == BT_COPY) { - if(DEBUG) - println(" " + blockCount + ". BT_COPY processing..."); - - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_COPY FP != outOffset (" + isoRaf.getFilePointer() + " != " + outOffset + ")"); - dmgRaf.seek(/*lastOffs+*/inOffset); - - int bytesRead = dmgRaf.read(tmp, 0, Math.min((int)inSize, tmp.length)); - long totalBytesRead = bytesRead; - while(bytesRead != -1) { - long progressPercentage = dmgRaf.getFilePointer()*100/dmgRaf.length(); - if(progressPercentage != previousPercentage) { - reportProgress(progressPercentage); - previousPercentage = progressPercentage; - } - - - if(!testOnly) - isoRaf.write(tmp, 0, bytesRead); - if(totalBytesRead >= inSize) - break; - bytesRead = dmgRaf.read(tmp, 0, Math.min((int)(inSize-totalBytesRead), tmp.length)); - if(bytesRead > 0) - totalBytesRead += bytesRead; - } - - //lastInOffs = inOffset+inSize; - } - else if(blockType == BT_ZERO) { - if(DEBUG) - println(" " + blockCount + ". BT_ZERO processing..."); - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_ZERO FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - long progressPercentage = dmgRaf.getFilePointer()*100/dmgRaf.length(); - if(progressPercentage != previousPercentage) { - reportProgress(progressPercentage); - previousPercentage = progressPercentage; - } - - long numberOfZeroBlocks = outSize/zeroblock.length; - int numberOfRemainingBytes = (int)(outSize%zeroblock.length); - for(int j = 0; j < numberOfZeroBlocks; ++j) { - if(!testOnly) - isoRaf.write(zeroblock); - } - if(!testOnly) - isoRaf.write(zeroblock, 0, numberOfRemainingBytes); - - //lastInOffs = inOffset+inSize; - } - else if(blockType == BT_UNKNOWN) { - /* I have no idea what this blocktype is... but it's common, and usually - doesn't appear more than 2-3 times in a dmg. As long as its input and - output sizes are 0, there's no reason to complain... is there? */ - if(DEBUG) - println(" " + blockCount + ". BT_UNKNOWN processing..."); - if(!(inSize == 0 && outSize == 0)) { - println(" " + blockCount + ". WARNING! Blocktype BT_UNKNOWN had non-zero sizes...", - " inSize=" + inSize + ", outSize=" + outSize); - //println(" The author of the program would be pleased if you contacted him about this."); - // ...or would I? - } - } - else if(blockType == BT_END) { - if(DEBUG) - println(" " + blockCount + ". BT_END processing..."); - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_END FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - //lastOffs += lastInOffs; - lastOutOffset = outOffset; - lastInOffset += lastByteReadInBlock; - } - else { - println(" " + blockCount + ". WARNING: previously unseen blocktype " + blockType + " [0x" + Integer.toHexString(blockType) + "]", - " " + blockCount + ". outOffset=" + outOffset + " outSize=" + outSize + " inOffset=" + inOffset + " inSize=" + inSize); - - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". unknown blocktype FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - } - - offset += 0x28; - ++blockCount; - } - } - } - //printlnVerbose("Progress: 100% Done!"); - reportProgress(100); - String errors = errorsFound?"There were errors...":"No errors reported."; - if(!graphical) { - newline(); - println(errors); - printlnVerbose("Total extracted bytes: " + totalSize + " B"); - } - else { - progmon.close(); - JOptionPane.showMessageDialog(null, "Extraction complete! " + errors + "\n" + - "Total extracted bytes: " + totalSize + " B", - "Information", JOptionPane.INFORMATION_MESSAGE); - System.exit(0); - } -// System.out.println("blocks.size()=" + blocks.size()); -// for(DMGBlock b : blocks) -// System.out.println(" " + b.toString()); - LinkedList merged = mergeBlocks(blocks); -// System.out.println("merged.size()=" + merged.size()); -// for(DMGBlock b : merged) -// System.out.println(" " + b.toString()); - System.out.println("Extracting all the parts not containing block data from source file:"); - int i = 1; - DMGBlock previous = null; - for(DMGBlock b : merged) { - if(previous == null && b.inOffset > 0) { - String filename = i++ + ".block"; - System.out.print(" " + filename + "..."); - FileOutputStream curFos = new FileOutputStream(new File(filename)); - dmgRaf.seek(0); - byte[] data = new byte[(int)(b.inOffset)]; - dmgRaf.read(data); - curFos.write(data); - curFos.close(); - } - else if(previous != null) { - String filename = i++ + ".block"; - System.out.print(" " + filename + "..."); - FileOutputStream curFos = new FileOutputStream(new File(filename)); - dmgRaf.seek(previous.inOffset+previous.inSize); - byte[] data = new byte[(int)(b.inOffset-(previous.inOffset+previous.inSize))]; - dmgRaf.read(data); - curFos.write(data); - curFos.close(); - } - previous = b; - } - if(previous.inOffset+previous.inSize != dmgRaf.length()) { - String filename = i++ + ".block"; - System.out.print(" " + filename + "..."); - FileOutputStream curFos = new FileOutputStream(new File(filename)); - dmgRaf.seek(previous.inOffset+previous.inSize); - byte[] data = new byte[(int)(dmgRaf.length()-(previous.inOffset+previous.inSize))]; - dmgRaf.read(data); - curFos.write(data); - curFos.close(); - } - dmgRaf.close(); - System.out.println("done!"); - } - - public static void parseArgs(String[] args) { - boolean parseSuccessful = false; - try { - /* Take care of the options... */ - int i; - for(i = 0; i < args.length; ++i) { - String cur = args[i]; - if(!cur.startsWith("-")) - break; - else if(cur.equals("-gui")) - graphical = true; - else if(cur.equals("-v")) - verbose = true; - else if(cur.equals("-startupcommand")) { - startupCommand = args[i+1]; - ++i; - } - } - - println(APPNAME + " " + BUILDSTRING, - "Copyright (c) 2006 Erik Larsson ", - " written from the source code to the original dmg2iso program", - " Copyright (c) 2004 vu1tur ", - " also using the iharder Base64 Encoder/Decoder ", - "", - "This program is distributed under the GNU General Public License version 2 or", - "later.", - "See for the details.", - ""); - - if(i == args.length) { - dmgFile = getInputFileFromUser(); - if(dmgFile == null) - System.exit(0); - if(getOutputConfirmationFromUser()) { - isoFile = getOutputFileFromUser(); - if(isoFile == null) - System.exit(0); - } - } - else { - dmgFile = new File(args[i++]); - if(!dmgFile.exists()) { - println("File \"" + dmgFile + "\" could not be found!"); - System.exit(0); - } - - if(i == args.length-1) - isoFile = new File(args[i]); - else if(i != args.length) - throw new Exception(); - } - - parseSuccessful = true; - } catch(Exception e) { - println(); - println(" usage: " + startupCommand + " [options] []"); - println(" if an iso-file is not supplied, the program will simulate an extraction"); - println(" (useful for detecting errors in dmg-files)"); - println(); - System.exit(0); - } - } - - public static long calculatePartitionSize(byte[] data) throws IOException { - long partitionSize = 0; - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); - long totalBytesRead; - totalBytesRead = 0; - while(totalBytesRead < 0xCC) - totalBytesRead += dis.skip(0xCC); - - while(totalBytesRead < data.length) { - int bytesRead = 0; - while(bytesRead < 0x10) - bytesRead += dis.skip(0x10-bytesRead); - - partitionSize += dis.readLong()*0x200; - bytesRead += 0x8; - - while(bytesRead < 0x28) - bytesRead += dis.skip(0x28-bytesRead); - totalBytesRead += bytesRead; - } - return partitionSize; - } - - /** Never used. Java is big-endian. */ - public static int swapEndian(int i) { - return - ((i & 0xff000000) >> 24) | - ((i & 0x00ff0000) >> 8 ) | - ((i & 0x0000ff00) << 8 ) | - ((i & 0x000000ff) << 24); - } - - public static void printCurrentLine(String s) { - System.out.print(BACKSPACE79); - System.out.print(s); - } - public static void println() { - System.out.print(BACKSPACE79); - System.out.println(); - } - public static void println(String... lines) { - if(!graphical) { - System.out.print(BACKSPACE79); - for(String s : lines) - System.out.println(s); - } - else { - String resultString = null; - for(String s : lines) { - if(resultString == null) - resultString = s; - else - resultString += "\n" + s; - } - JOptionPane.showMessageDialog(null, resultString, - APPNAME, JOptionPane.INFORMATION_MESSAGE); - } - } - public static void printlnVerbose() { - if(verbose) { - System.out.print(BACKSPACE79); - System.out.println(); - } - } - public static void printlnVerbose(String... lines) { - if(verbose) { - System.out.print(BACKSPACE79); - for(String s : lines) - System.out.println(s); - } - } - - public static void newline() { - System.out.println(); - } - - public static void reportProgress(long progressPercentage) { - if(!graphical) { - printCurrentLine("--->Progress: " + progressPercentage + "%"); - } - else { - if(progmon == null) { - progmon = new ProgressMonitor(null, "Extracting dmg to iso...", "0%", 0, 100); - progmon.setProgress(0); - progmon.setMillisToPopup(0); - } - progmon.setProgress((int)progressPercentage); - progmon.setNote(progressPercentage + "%"); - } - } - - public static File getInputFileFromUser() throws IOException { - if(!graphical) { - //String s = ""; - while(true) { - printCurrentLine("Please specify the path to the dmg file to extract from: "); - File f = new File(stdin.readLine().trim()); - while(!f.exists()) { - println("File does not exist!"); - printCurrentLine("Please specify the path to the dmg file to extract from: "); - f = new File(stdin.readLine().trim()); - } - return f; - } - } - else { - SimpleFileFilter sff = new SimpleFileFilter(); - sff.addExtension("dmg"); - sff.setDescription("DMG disk image files"); - JFileChooser jfc = new JFileChooser(); - jfc.setFileFilter(sff); - jfc.setMultiSelectionEnabled(false); - jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); - jfc.setDialogTitle("Choose the dmg-file to read..."); - while(true) { - if(jfc.showDialog(null, "Open") == JFileChooser.APPROVE_OPTION) { - File f = jfc.getSelectedFile(); - if(f.exists()) - return f; - else - JOptionPane.showMessageDialog(null, "The file does not exist! Choose again...", - "Error", JOptionPane.ERROR_MESSAGE); - } - else - return null; - } - } - } - public static boolean getOutputConfirmationFromUser() throws IOException { - if(!graphical) { - String s = ""; - while(true) { - printCurrentLine("Do you want to specify an output file (y/n)? "); - s = stdin.readLine().trim(); - if(s.equalsIgnoreCase("y")) - return true; - else if(s.equalsIgnoreCase("n")) - return false; - } - } - else { - return JOptionPane.showConfirmDialog(null, "Do you want to specify an output file?", - "Confirmation", JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; - } - } - public static File getOutputFileFromUser() throws IOException { - final String msg1 = "Please specify the path of the iso file to extract to: "; - final String msg2 = "The file already exists. Do you want to overwrite?"; - if(!graphical) { - while(true) { - printCurrentLine(msg1); - File f = new File(stdin.readLine().trim()); - while(f.exists()) { - while(true) { - printCurrentLine(msg2 + " (y/n)? "); - String s = stdin.readLine().trim(); - if(s.equalsIgnoreCase("y")) - return f; - else if(s.equalsIgnoreCase("n")) - break; - } - printCurrentLine(msg1); - f = new File(stdin.readLine().trim()); - } - return f; - } - } - else { - JFileChooser jfc = new JFileChooser(); - jfc.setMultiSelectionEnabled(false); - jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); - jfc.setDialogTitle("Choose the output iso-file..."); - while(true) { - if(jfc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { - File f = jfc.getSelectedFile(); - if(!f.exists()) - return f; - else if(JOptionPane.showConfirmDialog(null, msg2, "Confirmation", JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - return f; - } - } - else - return null; - } - } - } - - public static LinkedList mergeBlocks(LinkedList blockList) { - LinkedList result = new LinkedList(); - Iterator it = blockList.iterator(); - DMGBlock previous = it.next(); - DMGBlock current; - while(it.hasNext()) { - current = it.next(); - if(current.inSize != 0) { - if(current.inOffset == previous.inOffset+previous.inSize) { - DMGBlock mergedBlock = new DMGBlock(previous.blockType, previous.skipped, previous.outOffset, previous.outSize+current.outSize, previous.inOffset, previous.inSize+current.inSize); - previous = mergedBlock; - } - else { - result.addLast(previous); - previous = current; - } - } - } - result.addLast(previous); - return result; - } - - public static class DMGBlock { - public int blockType; - public int skipped; - public long outOffset; - public long outSize; - public long inOffset; - public long inSize; - - public DMGBlock(int blockType, int skipped, long outOffset, long outSize, long inOffset, long inSize) { - this.blockType = blockType; - this.skipped = skipped; - this.outOffset = outOffset; - this.outSize = outSize; - this.inOffset = inOffset; - this.inSize = inSize; - } - - public String toString() { - return "[type: 0x" + Integer.toHexString(blockType) + " skipped: 0x" + Integer.toHexString(skipped) + " outOffset: " + outOffset + " outSize: " + outSize + " inOffset: " + inOffset + " inSize: " + inSize + "]"; - } - } -} - diff --git a/src/DMGInfo.java b/src/DMGInfo.java deleted file mode 100644 index 48b32e9..0000000 --- a/src/DMGInfo.java +++ /dev/null @@ -1,147 +0,0 @@ -import java.io.*; - -public class DMGInfo { - public static void main(String[] args) throws IOException { - RandomAccessFile inRaf = new RandomAccessFile(args[0], "r"); - - // Check opening signature "koly" - inRaf.seek(inRaf.length()-512); - byte[] koly = new byte[4]; - inRaf.readFully(koly); - String kolySignature = new String(koly, "US-ASCII"); - if(!kolySignature.equals("koly")) - System.out.println("ERROR: Signature incorrect. Found \"" + kolySignature + "\" instead of \"koly\"."); - else - System.out.println("\"koly\" signature OK."); - - // Read partition list start location 1 and end location - inRaf.seek(inRaf.length()-0x1E0); - - // -0x1E0: address to plist xml structure (8 bytes) - long plistAddress1 = inRaf.readLong(); - System.out.println("Address to plist: 0x" + Long.toHexString(plistAddress1)); - - - // -0x1D8: address to end of plist xml structure (8 bytes) - long plistEndAddress = inRaf.readLong(); - System.out.println("Address to end of plist: 0x" + Long.toHexString(plistEndAddress)); - System.out.println(" Implication: plist size = " + (plistEndAddress-plistAddress1) + " B"); - - - long unknown_0x1D0 = inRaf.readLong(); - - - long unknown_0x1C8 = inRaf.readLong(); - if(unknown_0x1C8 != 0x0000000100000001L) - System.out.println("Assertion failed! unknown_0x1C8 == 0x" + - Long.toHexString(unknown_0x1C8) + " and not 0x0000000100000001"); - - - long unknown_0x1C0 = inRaf.readLong(); - - - long unknown_0x1B8 = inRaf.readLong(); - System.out.println("Some kind of signature? Value: 0x" + Long.toHexString(unknown_0x1B8)); - - - long unknown_0x1B0 = inRaf.readLong(); - if(unknown_0x1B0 != 0x0000000200000020L) - System.out.println("Assertion failed! unknown_0x1B0 == 0x" + - Long.toHexString(unknown_0x1B0) + " and not 0x0000000200000020"); - - - int unknown_0x1A8 = inRaf.readInt(); - - - int unknown_0x1A4 = inRaf.readInt(); - System.out.println("Some kind of unit size? Value: 0x" + - Integer.toHexString(unknown_0x1A4) + " / " + unknown_0x1A4); - - - // Unknown chunk of data (120 bytes) - byte[] unknown_0x1A0 = new byte[120]; - inRaf.readFully(unknown_0x1A0); - - - // -0x128: address to beginning of plist xml structure (second occurrence) (8 bytes) - long plistAddress2 = inRaf.readLong(); - System.out.println("Address to plist (2): 0x" + Long.toHexString(plistAddress2)); - - - // -0x120: size of plist xml structure (8 bytes) - long plistSize = inRaf.readLong(); - System.out.println("plist size: " + plistSize + " B"); - - - // Unknown chunk of data (120 bytes) - byte[] unknown_0x118 = new byte[120]; - inRaf.readFully(unknown_0x118); - - - // -0x0A0: Checksum type identifier (4 bytes) - System.out.print("Checksum type"); - int cs_type = inRaf.readInt(); - if(cs_type == 0x00000002) - System.out.println(": CRC-32"); - else if(cs_type == 0x00000004) - System.out.println(": MD5"); - else - System.out.println(" unknown! Data: 0x" + Integer.toHexString(cs_type)); - - - // -0x09C: Length of checksum in bits (4 bytes) - int cs_length = inRaf.readInt(); - System.out.println("Checksum length: " + cs_length + " bits"); - - - // -0x098: Checksum ((cs_length/8) bytes) - byte[] checksum = new byte[cs_length/8]; - inRaf.readFully(checksum); - System.out.println("Checksum: 0x" + byteArrayToHexString(checksum).toUpperCase()); - - /* - if(unknown_0x != 0xL) - System.out.println("Assertion failed! unknown_0x == 0x" + Long.toHexString(unknown_0x) + " and not 0xL"); - */ - } - - public static String byteArrayToHexString(byte[] array) { - StringBuilder result = new StringBuilder(); - for(byte b : array) { - String s = Integer.toHexString(b & 0xFF); - if(s.length() == 1) - s = "0" + s; - result.append(s); - } - return result.toString(); - } -} - -class DMGInfoFrame extends JFrame { - private JTabbedPane mainPane; - - public DMGInfoFrame() { - this("DMGInfo"); - - mainPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); - - StatisticsPanel statisticsPanel; - statisticsPanel = new StatisticsPanel(); - mainPane.addTab(statisticsPanel, "Statistics"); - } -} - -class StatisticsPanel extends JPanel { - JPanel blocktypeCountPanel; - - public StatisticsPanel(DMGFile dmgFile) {} -} - -class DMGFile extends RandomAccessFile { - public DMGFile(File file, String mode) { - super(file, mode); - } - public DMGFile(String name, String mode) { - super(name, mode); - } -} diff --git a/src/DMGMetadata.java b/src/DMGMetadata.java deleted file mode 100644 index 1027d51..0000000 --- a/src/DMGMetadata.java +++ /dev/null @@ -1,346 +0,0 @@ -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.DataOutput; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.RandomAccessFile; -import java.io.UnsupportedEncodingException; -import java.util.LinkedList; - -public class DMGMetadata { - public static final long PLIST_ADDRESS_1 = 0x1E0; - public static final long PLIST_ADDRESS_2 = 0x128; - - public final byte[] rawData; - - public final byte[] plistXmlData; - public final byte[] unknown1_256; - public PartitionBlockList[] blockLists; - public final byte[] unknown2_12; - public APMPartition[] partitions; - public final byte[] unknown3_unknown; - public final byte[] koly; - - public DMGMetadata(RandomAccessFile dmgFile) throws IOException { - dmgFile.seek(dmgFile.length()-PLIST_ADDRESS_1); - long plistBegin1 = dmgFile.readLong(); - long plistEnd = dmgFile.readLong(); - dmgFile.seek(dmgFile.length()-PLIST_ADDRESS_2); - long plistBegin2 = dmgFile.readLong(); - long plistSize = dmgFile.readLong(); - - rawData = new byte[(int)(dmgFile.length()-plistBegin1)]; - dmgFile.seek(plistBegin1); - dmgFile.readFully(rawData); - - plistXmlData = new byte[(int)plistSize]; - dmgFile.seek(plistBegin1); - dmgFile.readFully(plistXmlData); - - unknown1_256 = new byte[256]; - dmgFile.readFully(unknown1_256); - - LinkedList blockListList = new LinkedList(); - int length = dmgFile.readInt(); - byte[] fourcc = new byte[4]; - dmgFile.readFully(fourcc); - String fourccString = new String(fourcc, "US-ASCII"); - dmgFile.seek(dmgFile.getFilePointer()-4); - while(fourccString.equals("mish")) { - blockListList.add(new PartitionBlockList(dmgFile, length)); - length = dmgFile.readInt(); - dmgFile.readFully(fourcc); - fourccString = new String(fourcc, "US-ASCII"); - dmgFile.seek(dmgFile.getFilePointer()-4); - } - blockLists = blockListList.toArray(new PartitionBlockList[blockListList.size()]); - - unknown2_12 = new byte[12]; - dmgFile.readFully(unknown2_12); - - LinkedList partitionList = new LinkedList(); - byte[] currentPartitionEntry = new byte[0x200]; - dmgFile.readFully(currentPartitionEntry); - byte[] pmSig = new byte[2]; - pmSig[0] = currentPartitionEntry[0]; - pmSig[1] = currentPartitionEntry[1]; - while(new String(pmSig, "US-ASCII").equals("PM")) { - partitionList.addLast(new APMPartition(currentPartitionEntry)); - dmgFile.readFully(currentPartitionEntry); - pmSig[0] = currentPartitionEntry[0]; - pmSig[1] = currentPartitionEntry[1]; - } - while(onlyZeros(currentPartitionEntry)) - dmgFile.readFully(currentPartitionEntry); - partitions = partitionList.toArray(new APMPartition[partitionList.size()]); - - unknown3_unknown = new byte[(int)(dmgFile.length()-dmgFile.getFilePointer()-512)]; - dmgFile.readFully(unknown3_unknown); - - koly = new byte[512]; - dmgFile.seek(dmgFile.length()-koly.length); - dmgFile.readFully(koly); - - if(dmgFile.getFilePointer() != dmgFile.length()) - System.out.println("MISCALCULATION! FP=" + dmgFile.getFilePointer() + " LENGTH=" + dmgFile.length()); - } - - public void printInfo(PrintStream ps) { - ps.println("block list:"); - for(PartitionBlockList pbl : blockLists) - pbl.printInfo(ps); - ps.println("partitions:"); - for(APMPartition ap : partitions) - ap.printPartitionInfo(ps); - } - - private static boolean onlyZeros(byte[] array) { - for(int i = 0; i < array.length; ++i) { - if(array[i] != 0) - return false; - } - return true; - } - - public static class PartitionBlockList { - public final byte[] header = new byte[0xCC]; - public final BlockDescriptor[] descriptors; - - public PartitionBlockList(byte[] entryData) throws IOException { - this(new DataInputStream(new ByteArrayInputStream(entryData)), entryData.length); - } - - public PartitionBlockList(DataInput di, int length) throws IOException { - int position = 0; - di.readFully(header); - position += header.length; - LinkedList descs = new LinkedList(); - while(position < length) { - descs.addLast(new BlockDescriptor(di)); - position += 0x28; - } - descriptors = descs.toArray(new BlockDescriptor[descs.size()]); - } - - public void printInfo(PrintStream ps) { - for(BlockDescriptor bd : descriptors) - ps.println(bd.toString()); - } - } - - public static class BlockDescriptor { - // Known block types - public static final int BT_COPY = 0x00000001; - public static final int BT_ZERO = 0x00000002; - public static final int BT_ZLIB = 0x80000005; - public static final int BT_END = 0xffffffff; - public static final int BT_UNKNOWN1 = 0x7ffffffe; - private static final int[] KNOWN_BLOCK_TYPES = { BT_COPY, - BT_ZERO, - BT_ZLIB, - BT_END, - BT_UNKNOWN1 }; - private static final String[] KNOWN_BLOCK_TYPE_NAMES = { "BT_COPY", - "BT_ZERO", - "BT_ZLIB", - "BT_END", - "BT_UNKNOWN1" }; - - private int blockType; - private int unknown; - private long outOffset; - private long outSize; - private long inOffset; - private long inSize; - - public BlockDescriptor() {} - - public BlockDescriptor(byte[] entryData) throws IOException { - this(new DataInputStream(new ByteArrayInputStream(entryData))); - } - - public BlockDescriptor(DataInput dataIn) throws IOException { - blockType = dataIn.readInt(); - unknown = dataIn.readInt(); - outOffset = dataIn.readLong()*0x200; - outSize = dataIn.readLong()*0x200; - inOffset = dataIn.readLong(); - inSize = dataIn.readLong(); - } - - public byte[] toBytes() throws IOException { - ByteArrayOutputStream result = new ByteArrayOutputStream(0x28); - DataOutputStream dataOut = new DataOutputStream(result); - - dataOut.writeInt(blockType); // 4 bytes - dataOut.writeInt(unknown); // 4 bytes - if((outOffset % 0x200) != 0) - throw new RuntimeException("Out offset must be aligned to 0x200 block size!"); - dataOut.writeLong(outOffset/0x200); // 8 bytes - if((outSize % 0x200) != 0) - throw new RuntimeException("Out size must be aligned to 0x200 block size!"); - dataOut.writeLong(outSize/0x200); // 8 bytes - dataOut.writeLong(inOffset); // 8 bytes - dataOut.writeLong(inSize); // 8 bytes - // sum = 4 + 4 + 8 + 8 + 8 + 8 = 40 = 0x28 - - dataOut.flush(); - dataOut.close(); - return result.toByteArray(); - } - - public int getBlockType() { return blockType; } - public int getUnknown() { return unknown; } - public String getUnknownAsString() throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(4); - DataOutputStream dos = new DataOutputStream(baos); - dos.write(unknown); - dos.close(); - return new String(baos.toByteArray(), "US-ASCII"); - } - public long getOutOffset() { return outOffset; } - public long getOutSize() { return outSize; } - public long getInOffset() { return inOffset; } - public long getInSize() { return inSize; } - - public void setBlockType(int blockType) { this.blockType = blockType; } - public void setUnknown(int unknown) { this.unknown = unknown; } - public void setOutOffset(long outOffset) { - if((outOffset % 0x200) != 0) - throw new RuntimeException("Out offset must be aligned to 0x200 block size!"); - this.outOffset = outOffset; - } - public void setOutSize(long outSize) { - if((outSize % 0x200) != 0) - throw new RuntimeException("Out size must be aligned to 0x200 block size!"); - this.outSize = outSize; - } - public void setInOffset(long inOffset) { this.inOffset = inOffset; } - public void setInSize(long inSize) { this.inSize = inSize; } - - public boolean hasKnownBlockType() { - for(int current : KNOWN_BLOCK_TYPES) { - if(blockType == current) - return true; - } - return false; - } - - public String getBlockTypeName() { - for(int i = 0; i < KNOWN_BLOCK_TYPES.length; ++i) { - int current = KNOWN_BLOCK_TYPES[i]; - if(blockType == current) - return KNOWN_BLOCK_TYPE_NAMES[i]; - } - return null; - } - - public String toString() { - StringBuilder result = new StringBuilder("[BlockDescriptor"); - - String blockTypeString = "\"" + getBlockTypeName() + "\""; - if(blockTypeString == null) - blockTypeString = "0x" + Integer.toHexString(blockType) + " (unknown type)"; - - result.append(" blockType=" + blockTypeString); - result.append(" unknown=" + Integer.toHexString(unknown)); - result.append(" outOffset=" + outOffset); - result.append(" outSize=" + outSize); - result.append(" inOffset=" + inOffset); - result.append(" inSize=" + inSize); - result.append("]"); - - return result.toString(); - } - } - - // Saxat från HFSExplorer.java - public static class APMPartition { - public int pmSig; // {partition signature} - public int pmSigPad; // {reserved} - public long pmMapBlkCnt; // {number of blocks in partition map} - public long pmPyPartStart; // {first physical block of partition} - public long pmPartBlkCnt; // {number of blocks in partition} - public final byte[] pmPartName = new byte[32]; // {partition name} - public final byte[] pmParType = new byte[32]; // {partition type} - public long pmLgDataStart; // {first logical block of data area} - public long pmDataCnt; // {number of blocks in data area} - public long pmPartStatus; // {partition status information} - public long pmLgBootStart; // {first logical block of boot code} - public long pmBootSize; // {size of boot code, in bytes} - public long pmBootAddr; // {boot code load address} - public long pmBootAddr2; // {reserved} - public long pmBootEntry; // {boot code entry point} - public long pmBootEntry2; // {reserved} - public long pmBootCksum; // {boot code checksum} - public final byte[] pmProcessor = new byte[16]; // {processor type} - public final int[] pmPad = new int[188]; // {reserved} - - public APMPartition(byte[] entryData) throws IOException { - this(new DataInputStream(new ByteArrayInputStream(entryData))); - } - - public APMPartition(DataInput di) throws IOException { - // 2*2 + 4*3 + 32*2 + 10*4 + 16 + 188*2 = 512 - pmSig = di.readShort() & 0xffff; - pmSigPad = di.readShort() & 0xffff; - pmMapBlkCnt = di.readInt() & 0xffffffffL; - pmPyPartStart = di.readInt() & 0xffffffffL; - pmPartBlkCnt = di.readInt() & 0xffffffffL; - di.readFully(pmPartName); - di.readFully(pmParType); - pmLgDataStart = di.readInt() & 0xffffffffL; - pmDataCnt = di.readInt() & 0xffffffffL; - pmPartStatus = di.readInt() & 0xffffffffL; - pmLgBootStart = di.readInt() & 0xffffffffL; - pmBootSize = di.readInt() & 0xffffffffL; - pmBootAddr = di.readInt() & 0xffffffffL; - pmBootAddr2 = di.readInt() & 0xffffffffL; - pmBootEntry = di.readInt() & 0xffffffffL; - pmBootEntry2 = di.readInt() & 0xffffffffL; - pmBootCksum = di.readInt() & 0xffffffffL; - di.readFully(pmProcessor); - for(int i = 0; i < pmPad.length; ++i) - pmPad[i] = di.readShort() & 0xffff; - } - - public void printPartitionInfo(PrintStream ps) { -// String result = ""; -// result += "Partition name: \"" + new String(pmPartName) + "\"\n"; -// result += "Partition type: \"" + new String(pmParType) + "\"\n"; -// result += "Processor type: \"" + new String(pmProcessor) + "\"\n"; -// return result; - try { - ps.println("pmSig: " + pmSig); - ps.println("pmSigPad: " + pmSigPad); - ps.println("pmMapBlkCnt: " + pmMapBlkCnt); - ps.println("pmPyPartStart: " + pmPyPartStart); - ps.println("pmPartBlkCnt: " + pmPartBlkCnt); - ps.println("pmPartName: \"" + new String(pmPartName, "US-ASCII") + "\""); - ps.println("pmParType: \"" + new String(pmParType, "US-ASCII") + "\""); - ps.println("pmLgDataStart: " + pmLgDataStart); - ps.println("pmDataCnt: " + pmDataCnt); - ps.println("pmPartStatus: " + pmPartStatus); - ps.println("pmLgBootStart: " + pmLgBootStart); - ps.println("pmBootSize: " + pmBootSize); - ps.println("pmBootAddr: " + pmBootAddr); - ps.println("pmBootAddr2: " + pmBootAddr2); - ps.println("pmBootEntry: " + pmBootEntry); - ps.println("pmBootEntry2: " + pmBootEntry2); - ps.println("pmBootCksum: " + pmBootCksum); - ps.println("pmProcessor: \"" + new String(pmProcessor, "US-ASCII") + "\""); - ps.println("pmPad: " + pmPad); - } catch(UnsupportedEncodingException uee) { - uee.printStackTrace(); - } // Will never happen. Ever. Period. - } - } - - public static void main(String[] args) throws IOException { - DMGMetadata meta = new DMGMetadata(new RandomAccessFile(args[0], "r")); - meta.printInfo(System.out); - } -} diff --git a/src/XMLNode.java b/src/XMLNode.java deleted file mode 100644 index bfee766..0000000 --- a/src/XMLNode.java +++ /dev/null @@ -1,121 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -import java.util.LinkedList; -import java.io.PrintStream; - -class XMLNode extends XMLElement { - public final String namespaceURI; - public final String sName; - public final String qName; - public final Attribute[] attrs; - public final XMLNode parent; - private final LinkedList children; - - public XMLNode(String namespaceURI, String sName, - String qName, Attribute[] attrs, XMLNode parent) { - this.namespaceURI = namespaceURI; - this.sName = sName; - this.qName = qName; - this.attrs = attrs; - this.parent = parent; - this.children = new LinkedList(); - } - - public void addChild(XMLElement x) { - children.addLast(x); - } - - public void printTree(PrintStream pw) { - _printTree(pw, 0); - } - - protected void _printTree(PrintStream pw, int level) { - for(int i = 0; i < level; ++i) - pw.print(" "); - pw.print("<"); - pw.print(qName); - for(Attribute a : attrs) - pw.print(" " + a.qName + "=" + a.value); - pw.println(">"); - for(XMLElement xe : children) - xe._printTree(pw, level+1); - - for(int i = 0; i < level; ++i) - pw.print(" "); - pw.println(""); - } - public XMLElement[] getChildren() { - return children.toArray(new XMLElement[children.size()]); - } - - /** - * The concept of "changing directory" in a tree is perhabs not a - * perfect way to describe things. But this method will look up the - * first subnode of our node that is of the type type - * and return it. - * If you have more than one of the same type, tough luck. You only - * get the first. - */ - public XMLNode cd(String type) { - for(XMLElement xn : getChildren()) { - if(xn instanceof XMLNode && ((XMLNode)xn).qName.equals(type)) - return (XMLNode)xn; - } - return null; - } - /** - * This is different from the cd method in that it - * searches for a node of the type "key", and looks up the - * XMLText within. It then compares the text with the String - * key. If they match, it returns the node coming - * after the key node. Else it continues to search. If no match is - * found, null is returned. - */ - public XMLNode cdkey(String key) { - boolean keyFound = false; - for(XMLElement xn : getChildren()) { - if(xn instanceof XMLNode) { - if(keyFound) - return (XMLNode)xn; - - else if(((XMLNode)xn).qName.equals("key")) { - for(XMLElement xn2 : ((XMLNode)xn).getChildren()) { - if(xn2 instanceof XMLText && ((XMLText)xn2).text.equals(key)) - keyFound = true; - } - } - } - } - return null; - } - public String getKeyValue(String key) { - XMLNode keyNode = cdkey(key); - StringBuilder returnString = new StringBuilder(); - for(XMLElement xe : keyNode.getChildren()) { - if(xe instanceof XMLText) - returnString.append(((XMLText)xe).text); - } - if(returnString.length() == 0) - return null; - else - return returnString.toString(); - } -} diff --git a/src/XMLParse.java b/src/XMLParse.java deleted file mode 100644 index d8d2826..0000000 --- a/src/XMLParse.java +++ /dev/null @@ -1,31 +0,0 @@ -/* -public class XMLParse { - public String encoding = "US-ASCII"; - - public XMLElement parse(InputStream is) throws IOException { - readElement(is); - } - - public void readElement(InputStream is) { - LinkedList buf = new LinkedList(); - int currentByte = is.read(); - if(currentByte != '<') - throw new RuntimeException(); - - while(currentByte != '>' && currentByte != -1) { - buf.add(currentByte); - currentByte = is.read(); - } - if(currentByte == -1) - throw new RuntimeException(); - else { - buf.add(currentByte); - byte[] bytes = new byte[buf.size()]; - int i = 0; - for(int cur : buf) - bytes[i++] = cur; - String s = new String(bytes, encoding); - } - } -} -*/ diff --git a/src/XMLText.java b/src/XMLText.java deleted file mode 100644 index c7fa922..0000000 --- a/src/XMLText.java +++ /dev/null @@ -1,37 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -import java.io.PrintStream; - -class XMLText extends XMLElement { - public final String text; - public XMLText(String text) { - this.text = text; - } - protected void _printTree(PrintStream pw, int level) { - for(int i = 0; i < level; ++i) - pw.print(" "); - pw.println(text); - } - - public static void main(String[] args) { - System.out.println(args[0] + " " + args[1]); - } -} diff --git a/src/net/iharder/Base64.java b/src/net/iharder/Base64.java deleted file mode 100644 index a52d7af..0000000 --- a/src/net/iharder/Base64.java +++ /dev/null @@ -1,1452 +0,0 @@ -/** - * Encodes and decodes to and from Base64 notation. - * - *

- * Change Log: - *

- *
    - *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
  • - *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
  • - *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
  • - *
  • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
  • - *
  • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
  • - *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
  • - *
  • v1.4 - Added helper methods to read/write files.
  • - *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • - *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
  • - *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • - *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • - *
- * - *

- * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

- * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.1 - */ - -package net.iharder; - -public class Base64 -{ - -/* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify encoding. */ - public final static int ENCODE = 1; - - - /** Specify decoding. */ - public final static int DECODE = 0; - - - /** Specify that data should be gzip-compressed. */ - public final static int GZIP = 2; - - - /** Don't break lines when encoding (violates strict Base64 specification) */ - public final static int DONT_BREAK_LINES = 8; - - -/* ******** P R I V A T E F I E L D S ******** */ - - - /** Maximum line length (76) of Base64 output. */ - private final static int MAX_LINE_LENGTH = 76; - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** The new line character (\n) as a byte. */ - private final static byte NEW_LINE = (byte)'\n'; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "UTF-8"; - - - /** The 64 valid Base64 values. */ - private final static byte[] ALPHABET; - private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ - { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' - }; - - /** Determine which ALPHABET to use. */ - static - { - byte[] __bytes; - try - { - __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException use) - { - __bytes = _NATIVE_ALPHABET; // Fall back to native encoding - } // end catch - ALPHABET = __bytes; - } // end static - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - // I think I end up not using the BAD_ENCODING indicator. - //private final static byte BAD_ENCODING = -9; // Indicates error in encoding - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - - /** Defeats instantiation. */ - private Base64(){} - - - -/* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) - { - encode3to4( threeBytes, 0, numSigBytes, b4, 0 ); - return b4; - } // end encode3to4 - - - /** - * Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset ) - { - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) - | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) - | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); - - switch( numSigBytes ) - { - case 3: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; - return destination; - - case 2: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - case 1: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = EQUALS_SIGN; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - *

- * Valid options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DONT_BREAK_LINES: don't break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeObject( myObj, Base64.GZIP ) or - *

- * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - { - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.io.ObjectOutputStream oos = null; - java.util.zip.GZIPOutputStream gzos = null; - - // Isolate options - int gzip = (options & GZIP); - int dontBreakLines = (options & DONT_BREAK_LINES); - - try - { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); - - // GZip? - if( gzip == GZIP ) - { - gzos = new java.util.zip.GZIPOutputStream( b64os ); - oos = new java.io.ObjectOutputStream( gzos ); - } // end if: gzip - else - oos = new java.io.ObjectOutputStream( b64os ); - - oos.writeObject( serializableObject ); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - return null; - } // end catch - finally - { - try{ oos.close(); } catch( Exception e ){} - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @since 1.4 - */ - public static String encodeBytes( byte[] source ) - { - return encodeBytes( source, 0, source.length, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Valid options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DONT_BREAK_LINES: don't break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param options Specified options - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int options ) - { - return encodeBytes( source, 0, source.length, options ); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @since 1.4 - */ - public static String encodeBytes( byte[] source, int off, int len ) - { - return encodeBytes( source, off, len, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Valid options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DONT_BREAK_LINES: don't break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int off, int len, int options ) - { - // Isolate options - int dontBreakLines = ( options & DONT_BREAK_LINES ); - int gzip = ( options & GZIP ); - - // Compress? - if( gzip == GZIP ) - { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - - try - { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - - gzos.write( source, off, len ); - gzos.close(); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - return null; - } // end catch - finally - { - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else - { - // Convert option to boolean in way that code likes it. - boolean breakLines = dontBreakLines == 0; - - int len43 = len * 4 / 3; - byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for( ; d < len2; d+=3, e+=4 ) - { - encode3to4( source, d+off, 3, outBuff, e ); - - lineLength += 4; - if( breakLines && lineLength == MAX_LINE_LENGTH ) - { - outBuff[e+4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if( d < len ) - { - encode3to4( source, d+off, len - d, outBuff, e ); - e += 4; - } // end if: some padding needed - - - // Return value according to relevant encoding. - try - { - return new String( outBuff, 0, e, PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( outBuff, 0, e ); - } // end catch - - } // end else: don't compress - - } // end encodeBytes - - - - - -/* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the number of decoded bytes converted - * @since 1.3 - */ - private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) - { - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else - { - try{ - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - }catch( Exception e){ - System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); - System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); - System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); - System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); - return -1; - } //e nd catch - } - } // end decodeToBytes - - - - - /** - * Very low-level access to decoding ASCII characters in - * the form of a byte array. Does not support automatically - * gunzipping or any other "fancy" features. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @return decoded data - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len ) - { - int len34 = len * 3 / 4; - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; - - byte[] b4 = new byte[4]; - int b4Posn = 0; - int i = 0; - byte sbiCrop = 0; - byte sbiDecode = 0; - for( i = off; i < off+len; i++ ) - { - sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits - sbiDecode = DECODABET[ sbiCrop ]; - - if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better - { - if( sbiDecode >= EQUALS_SIGN_ENC ) - { - b4[ b4Posn++ ] = sbiCrop; - if( b4Posn > 3 ) - { - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( sbiCrop == EQUALS_SIGN ) - break; - } // end if: quartet built - - } // end if: equals sign or better - - } // end if: white space, equals sign or better - else - { - System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); - return null; - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @since 1.4 - */ - public static byte[] decode( String s ) - { - byte[] bytes; - try - { - bytes = s.getBytes( PREFERRED_ENCODING ); - } // end try - catch( java.io.UnsupportedEncodingException uee ) - { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode( bytes, 0, bytes.length ); - - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - if( bytes != null && bytes.length >= 4 ) - { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) - { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try - { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) - { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) - { - // Just return originally-decoded bytes - } // end catch - finally - { - try{ baos.close(); } catch( Exception e ){} - try{ gzis.close(); } catch( Exception e ){} - try{ bais.close(); } catch( Exception e ){} - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @since 1.5 - */ - public static Object decodeToObject( String encodedObject ) - { - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try - { - bais = new java.io.ByteArrayInputStream( objBytes ); - ois = new java.io.ObjectInputStream( bais ); - - obj = ois.readObject(); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - obj = null; - } // end catch - catch( java.lang.ClassNotFoundException e ) - { - e.printStackTrace(); - obj = null; - } // end catch - finally - { - try{ bais.close(); } catch( Exception e ){} - try{ ois.close(); } catch( Exception e ){} - } // end finally - - return obj; - } // end decodeObject - - - - /** - * Convenience method for encoding data to a file. - * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean encodeToFile( byte[] dataToEncode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - success = true; - } // end try - catch( java.io.IOException e ) - { - - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean decodeToFile( String dataToDecode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - success = true; - } // end try - catch( java.io.IOException e ) - { - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - * @param filename Filename for reading encoded data - * @return decoded byte array or null if unsuccessful - * - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - { - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); - return null; - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error decoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - * @param filename Filename for reading binary data - * @return base64-encoded string or null if unsuccessful - * - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - { - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ (int)(file.length() * 1.4) ]; - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error encoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream - { - private boolean encode; // Encoding or decoding - private int position; // Current position in the buffer - private byte[] buffer; // Small buffer holding converted data - private int bufferLength; // Length of buffer (3 or 4) - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - private boolean breakLines; // Break lines at less than 80 characters - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream( java.io.InputStream in ) - { - this( in, DECODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DONT_BREAK_LINES: don't break lines at 76 characters
-         *     (only meaningful when encoding)
-         *     Note: Technically, this makes your encoding non-compliant.
-         * 
- *

- * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public InputStream( java.io.InputStream in, int options ) - { - super( in ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[ bufferLength ]; - this.position = -1; - this.lineLength = 0; - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @since 1.3 - */ - public int read() throws java.io.IOException - { - // Do we need to get data? - if( position < 0 ) - { - if( encode ) - { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for( int i = 0; i < 3; i++ ) - { - try - { - int b = in.read(); - - // If end of stream, b is -1. - if( b >= 0 ) - { - b3[i] = (byte)b; - numBinaryBytes++; - } // end if: not end of stream - - } // end try: read - catch( java.io.IOException e ) - { - // Only a problem if we got no data at all. - if( i == 0 ) - throw e; - - } // end catch - } // end for: each needed input byte - - if( numBinaryBytes > 0 ) - { - encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); - position = 0; - numSigBytes = 4; - } // end if: got data - else - { - return -1; - } // end else - } // end if: encoding - - // Else decoding - else - { - byte[] b4 = new byte[4]; - int i = 0; - for( i = 0; i < 4; i++ ) - { - // Read four "meaningful" bytes: - int b = 0; - do{ b = in.read(); } - while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); - - if( b < 0 ) - break; // Reads a -1 if end of stream - - b4[i] = (byte)b; - } // end for: each needed input byte - - if( i == 4 ) - { - numSigBytes = decode4to3( b4, 0, buffer, 0 ); - position = 0; - } // end if: got four characters - else if( i == 0 ){ - return -1; - } // end else if: also padded correctly - else - { - // Must have broken out from above. - throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if( position >= 0 ) - { - // End of relevant data? - if( /*!encode &&*/ position >= numSigBytes ) - return -1; - - if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) - { - lineLength = 0; - return '\n'; - } // end if - else - { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[ position++ ]; - - if( position >= bufferLength ) - position = -1; - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else - { - // When JDK1.4 is more accepted, use an assertion here. - throw new java.io.IOException( "Error in Base64 code reading stream." ); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - public int read( byte[] dest, int off, int len ) throws java.io.IOException - { - int i; - int b; - for( i = 0; i < len; i++ ) - { - b = read(); - - //if( b < 0 && i == 0 ) - // return -1; - - if( b >= 0 ) - dest[off + i] = (byte)b; - else if( i == 0 ) - return -1; - else - break; // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream - { - private boolean encode; - private int position; - private byte[] buffer; - private int bufferLength; - private int lineLength; - private boolean breakLines; - private byte[] b4; // Scratch used in a few places - private boolean suspendEncoding; - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out ) - { - this( out, ENCODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DONT_BREAK_LINES: don't break lines at 76 characters
-         *     (only meaningful when encoding)
-         *     Note: Technically, this makes your encoding non-compliant.
-         * 
- *

- * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out, int options ) - { - super( out ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[ bufferLength ]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @since 1.3 - */ - public void write(int theByte) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theByte ); - return; - } // end if: supsended - - // Encode? - if( encode ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to encode. - { - out.write( encode3to4( b4, buffer, bufferLength ) ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) - { - out.write( NEW_LINE ); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else - { - // Meaningful Base64 character? - if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to output. - { - int len = Base64.decode4to3( buffer, 0, b4, 0 ); - out.write( b4, 0, len ); - //out.write( Base64.decode4to3( buffer ) ); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) - { - throw new java.io.IOException( "Invalid character in Base64 data." ); - } // end else: not white space either - } // end else: decoding - } // end write - - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @since 1.3 - */ - public void write( byte[] theBytes, int off, int len ) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theBytes, off, len ); - return; - } // end if: supsended - - for( int i = 0; i < len; i++ ) - { - write( theBytes[ off + i ] ); - } // end for: each byte written - - } // end write - - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - */ - public void flushBase64() throws java.io.IOException - { - if( position > 0 ) - { - if( encode ) - { - out.write( encode3to4( b4, buffer, position ) ); - position = 0; - } // end if: encoding - else - { - throw new java.io.IOException( "Base64 input not properly padded." ); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @since 1.3 - */ - public void close() throws java.io.IOException - { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException - { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() - { - this.suspendEncoding = false; - } // end resumeEncoding - - - - } // end inner class OutputStream - - -} // end class Base64 diff --git a/src/org/.cvsignore b/src/org/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/org/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/org/catacombae/.cvsignore b/src/org/catacombae/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/org/catacombae/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/org/catacombae/dmg/encrypted/.cvsignore b/src/org/catacombae/dmg/encrypted/.cvsignore new file mode 100644 index 0000000..90d40b4 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/.cvsignore @@ -0,0 +1,6 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db +SecretPassword.java diff --git a/src/org/catacombae/dmg/encrypted/Assert.java b/src/org/catacombae/dmg/encrypted/Assert.java new file mode 100644 index 0000000..9f2ee55 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/Assert.java @@ -0,0 +1,37 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.encrypted; + +/** + * + * @author erik + */ +public class Assert { + public static void eq(long a, long b) { + eq(a, b, null); + } + public static void eq(long a, long b, String message) { + if(a != b) + throw new InvalidAssertionException("Equality asserion " + a + + " == " + b + " failed!" + + (message!=null ? " Message: "+message : "")); + } + public static void neq(long a, long b) { + neq(a, b, null); + } + public static void neq(long a, long b, String message) { + if(a == b) + throw new InvalidAssertionException("Non-equality asserion " + a + + " != " + b + " failed!" + + (message!=null ? " Message: "+message : "")); + } + + public static class InvalidAssertionException extends RuntimeException { + public InvalidAssertionException(String message) { + super(message); + } + } +} diff --git a/src/org/catacombae/dmg/encrypted/CEncryptedEncodingUtil.java b/src/org/catacombae/dmg/encrypted/CEncryptedEncodingUtil.java new file mode 100644 index 0000000..e9e3291 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/CEncryptedEncodingUtil.java @@ -0,0 +1,57 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.encrypted; + +import org.catacombae.dmgextractor.Util; +import org.catacombae.io.ReadableRandomAccessStream; + +/** + * + * @author erik + */ +class CEncryptedEncodingUtil { + private static final String V1_SIGNATURE = "cdsaencr"; + private static final String V2_SIGNATURE = "encrcdsa"; + + public static int detectVersion(ReadableRandomAccessStream stream) { + byte[] signatureBytes = new byte[8]; + try { + stream.seek(0); + stream.readFully(signatureBytes); + if(Util.toASCIIString(signatureBytes).equals(V2_SIGNATURE)) + return 2; + } catch(Exception e) { + System.err.println("Non-critical exception while detecting version 2" + + " CEncryptedEncoding header:"); + e.printStackTrace(); + } + + try { + stream.seek(stream.length()-signatureBytes.length); + stream.readFully(signatureBytes); + if(Util.toASCIIString(signatureBytes).equals(V1_SIGNATURE)) + return 1; + } catch(Exception e) { + System.err.println("Non-critical exception while detecting version 1" + + " CEncryptedEncoding header:"); + e.printStackTrace(); + } + + return -1; + } +} diff --git a/src/org/catacombae/dmg/encrypted/CommonCEncryptedEncodingHeader.java b/src/org/catacombae/dmg/encrypted/CommonCEncryptedEncodingHeader.java new file mode 100644 index 0000000..ae19574 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/CommonCEncryptedEncodingHeader.java @@ -0,0 +1,289 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + * This code was written from studying vfdecrypt, Copyright (c) 2006 + * Ralf-Philipp Weinmann + * Jacob Appelbaum + * Christian Fromme + * + * [I'm not sure if their copyright and license terms need to be applied, + * but in case they do, the original license terms are reprinted below + * as required by the license.] + * + * The vfdecrypt license says: + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + */ + +package org.catacombae.dmg.encrypted; + +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import org.catacombae.dmgextractor.Util; + +/** + * + * @author erik + */ +public abstract class CommonCEncryptedEncodingHeader { + public static CommonCEncryptedEncodingHeader create(V1Header header) { + return new V1Implementation(header); + } + + public static CommonCEncryptedEncodingHeader create(V2Header header) { + return new V2Implementation(header); + } + + public abstract int getBlockSize(); + public abstract long getBlockDataStart(); + /** + * Returns the salt for the key derivation function. + * @return the salt for the key derivation function. + */ + public abstract byte[] getKdfSalt(); + + /** + * Returns the iteration count for the key derivation function. + * @return the iteration count for the key derivation function. + */ + public abstract int getKdfIterationCount(); + + + /** + * Returns the initialization vector for the key decryption cipher. + * @return the initialization vector for the key decryption cipher. + */ + public abstract byte[] getUnwrapInitializationVector(); + + /** + * Returns the amount of bytes at the end of the stream that are not part + * of the block data area. + * @return the amount of bytes at the end of the stream that are not part + * of the block data area. + */ + public abstract long getTrailingReservedBytes(); + + /** + * Returns the length of the data that has been encrypted. This length may + * not be aligned with the encryption block size, in which case the last + * encryption block will have been padded at the end + * @return the length of the data that has been encrypted. + */ + public abstract long getEncryptedDataLength(); + + public abstract KeySet unwrapKeys(Key derivedKey, Cipher cph) + throws GeneralSecurityException, InvalidKeyException, + InvalidAlgorithmParameterException; + + public static class KeySet { + private final byte[] aesKey; + private final byte[] hmacSha1Key; + + private KeySet(byte[] aesKey, byte[] hmacSha1Key) { + this.aesKey = aesKey; + this.hmacSha1Key = hmacSha1Key; + } + + public byte[] getAesKey() { return aesKey; } + public byte[] getHmacSha1Key() { return hmacSha1Key; } + + public void clearData() { + Util.zero(aesKey); + Util.zero(hmacSha1Key); + } + } + + private static class V1Implementation extends CommonCEncryptedEncodingHeader { + private final V1Header header; + + public V1Implementation(V1Header header) { + this.header = header; + } + + @Override + public int getBlockSize() { + return header.getBlockSize(); + } + + @Override + public long getBlockDataStart() { + return 0; // By design (I think). + } + + @Override + public long getTrailingReservedBytes() { + return header.length(); + } + + @Override + public byte[] getKdfSalt() { + return Util.createCopy(header.getKdfSalt(), 0, header.getKdfSaltLen()); + } + + @Override + public int getKdfIterationCount() { + return header.getKdfIterationCount(); + } + + @Override + public byte[] getUnwrapInitializationVector() { + return header.getUnwrapIv(); + } + + @Override + public long getEncryptedDataLength() { + return header.getDecryptedDataLength(); // Confusion! + } + + @Override + public KeySet unwrapKeys(Key derivedKey, Cipher cph) + throws GeneralSecurityException, InvalidKeyException, + InvalidAlgorithmParameterException { + byte[] aesKey = unwrapIndividualKey(derivedKey, cph, + Util.createCopy(header.getWrappedAesKey(), 0, header.getLenWrappedAesKey())); + + byte[] hmacSha1Key = unwrapIndividualKey(derivedKey, cph, + Util.createCopy(header.getWrappedHmacSha1Key(), 0, header.getLenWrappedHmacSha1Key())); + return new KeySet(aesKey, hmacSha1Key); + } + + public byte[] unwrapIndividualKey(Key key, Cipher cph, byte[] wrappedKey) + throws InvalidKeyException, InvalidAlgorithmParameterException, + GeneralSecurityException { + Debug.print("unwrapIndividualKey(" + key + ", " + cph + ", byte[" + wrappedKey.length + "]);"); + Debug.print(" wrappedKey: 0x" + Util.byteArrayToHexString(wrappedKey)); + + final byte[] initialIv = new byte[] { + (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, (byte) 0x2c, + (byte) 0x79, (byte) 0xe8, (byte) 0x21, (byte) 0x05 + }; + // irX = intermediate result X + + byte[] ir1 = new byte[wrappedKey.length]; + cph.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(initialIv)); + int ir1Len = cph.doFinal(wrappedKey, 0, wrappedKey.length, ir1, 0); + Debug.print(" ir1: 0x" + Util.byteArrayToHexString(ir1, 0, ir1Len)); + Debug.print(" ir1Len: " + ir1Len); + + byte[] ir2 = new byte[ir1Len]; + for(int i = 0; i < ir1Len; ++i) + ir2[i] = ir1[ir1Len-1-i]; + Debug.print(" ir2: 0x" + Util.byteArrayToHexString(ir2)); + Debug.print(" ir2.length: " + ir2.length); + + byte[] ir3 = new byte[ir2.length-8]; + cph.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ir2, 0, 8)); + int ir3Len = cph.doFinal(ir2, 8, ir2.length-8, ir3, 0); + Debug.print(" ir3: 0x" + Util.byteArrayToHexString(ir3, 0, ir3Len)); + Debug.print(" ir3Len: " + ir3Len); + + byte[] result = Util.createCopy(ir3, 4, ir3Len-4); + Util.zero(ir1, ir2, ir3); + return result; + } + } + + private static class V2Implementation extends CommonCEncryptedEncodingHeader { + private final V2Header header; + + public V2Implementation(V2Header header) { + this.header = header; + } + + @Override + public int getBlockSize() { + return header.getBlockSize(); + } + + @Override + public long getBlockDataStart() { + return header.getOffsetToDataStart(); + } + + @Override + public long getTrailingReservedBytes() { + return 0; + } + + @Override + public byte[] getKdfSalt() { + return Util.createCopy(header.getKdfSalt(), 0, header.getKdfSaltLen()); + } + + @Override + public int getKdfIterationCount() { + return header.getKdfIterationCount(); + } + + @Override + public byte[] getUnwrapInitializationVector() { + return Util.createCopy(header.getBlobEncIv(), 0, header.getBlobEncIvSize()); + } + + private byte[] getEncryptedKeyBlob() { + return Util.createCopy(header.getEncryptedKeyblob(), 0, header.getEncryptedKeyblobSize()); + } + + @Override + public long getEncryptedDataLength() { + return header.getEncryptedDataLength(); + } + + @Override + public KeySet unwrapKeys(Key derivedKey, Cipher cph) + throws InvalidKeyException, InvalidAlgorithmParameterException, GeneralSecurityException { + Debug.print("V2Implementation.unwrapKeys(" + derivedKey + ", " + cph + ");"); + cph.init(Cipher.DECRYPT_MODE, derivedKey, new IvParameterSpec(getUnwrapInitializationVector())); + + byte[] encryptedKeyBlob = getEncryptedKeyBlob(); + Debug.print(" encryptedKeyBlob.length=" + encryptedKeyBlob.length); + byte[] decryptedKeyBlob = new byte[encryptedKeyBlob.length]; + Debug.print(" doing update...."); + int bp = cph.update(encryptedKeyBlob, 0, encryptedKeyBlob.length, decryptedKeyBlob); + Debug.print(" bp == " + bp); + Debug.print(" doing final...."); + bp += cph.doFinal(decryptedKeyBlob, bp); + Debug.print(" bp == " + bp); + + Debug.print(" decryptedKeyBlob: 0x" + Util.byteArrayToHexString(decryptedKeyBlob)); + byte[] aesKey = new byte[16]; + byte[] hmacSha1Key = new byte[20]; + System.arraycopy(decryptedKeyBlob, 0, aesKey, 0, 16); + Debug.print(" aesKey: 0x" + Util.byteArrayToHexString(aesKey)); + System.arraycopy(decryptedKeyBlob, 16, hmacSha1Key, 0, 20); + Debug.print(" hmacSha1Key: 0x" + Util.byteArrayToHexString(hmacSha1Key)); + + Util.zero(decryptedKeyBlob); // No unused secret data in memory. + Debug.print(" decryptedKeyBlob: 0x" + Util.byteArrayToHexString(decryptedKeyBlob)); + + Debug.print("returning from V2Implementation.unwrapKeys..."); + return new KeySet(aesKey, hmacSha1Key); + } + } +} diff --git a/src/org/catacombae/dmg/encrypted/Debug.java b/src/org/catacombae/dmg/encrypted/Debug.java new file mode 100644 index 0000000..f16098f --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/Debug.java @@ -0,0 +1,50 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.encrypted; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * + * @author erik + */ +class Debug { + private static class NullOutputStream extends OutputStream { + + @Override + public void write(int b) throws IOException {} + + } + private static boolean debugEnabled = false; + public static final PrintStream ps; + + static { + if(debugEnabled) + ps = System.err; + else + ps = new PrintStream(new NullOutputStream()); + } + + public static void print(String s) { + if(debugEnabled) + ps.println(s); + } +} diff --git a/src/org/catacombae/dmg/encrypted/ExperimentalV1Header.java b/src/org/catacombae/dmg/encrypted/ExperimentalV1Header.java new file mode 100644 index 0000000..0bf84ca --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/ExperimentalV1Header.java @@ -0,0 +1,277 @@ +package org.catacombae.dmg.encrypted; + +import java.io.PrintStream; +import org.catacombae.dmgextractor.Util; + +/** This class was generated by CStructToJavaClass. */ +public class ExperimentalV1Header { + /* + * struct ExperimentalV1Header + * size: 1276 bytes + * description: + * + * BP Size Type Identifier Description + * ------------------------------------------------------------------------------------------------------------------------------- + * 0 1*16 uint8_t[16] unknown0 Unknown data. + * 16 4 uint32_t blockSize Block size of the encrypted block data. + * 20 4 uint32_t unknownInt20 Unknown integer. + * 24 4 uint32_t unknownInt24 Unknown integer. + * 28 4 uint32_t unknownInt28 Unknown integer. + * 32 4 uint32_t unknownInt32 Unknown integer. + * 36 4 uint32_t unknownInt36 Unknown integer. + * 40 4 uint32_t unknownInt40 Unknown integer. + * 44 4 uint32_t unknownInt44 Unknown integer. + * 48 4 uint32_t kdfIterationCount Iteration count for the key derivation function (normally 1000). + * 52 4 uint32_t kdfSaltLen Length of kdfSalt in bytes. + * 56 1*32 uint8_t[32] kdfSalt Salt value for the key derivation function + * 88 4 uint32_t laban Unknown variable with observed value 16/0x10. + * 92 4 uint32_t edward Unknown variable with observed value 5/0x5. + * 96 4 uint32_t palle Unknown variable with observed value 0x80000001. + * 100 4 uint32_t lisa Unknown variable with observed value 128/0x80. + * 104 1*32 uint8_t[32] unwrapIv Initialization Vector for encryption-key unwrapping. + * 136 4 uint32_t lenWrappedAesKey Length of wrappedAesKey in bytes (max 256). + * 140 1*256 uint8_t[256] wrappedAesKey The AES key (wrapped). + * 396 4 uint32_t unknownInt396 Unknown integer (observed value: 91/0x5B). + * 400 4 uint32_t unknownInt400 Unknown integer (observed value: 160/0xA0). + * 404 1*32 uint8_t[32] unknown404 Unknown data (observed: 8 bytes filled, rest 0). + * 436 4 uint32_t lenWrappedHmacSha1Key Length of wrappedHmacSha1Key in bytes (max 256). + * 440 1*256 uint8_t[256] wrappedHmacSha1Key The HMAC SHA-1 key (wrapped). + * 696 4 uint32_t unknownInt696 Unknown integer (observed value: 91/0x5B). + * 700 4 uint32_t unknownInt700 Unknown integer (observed value: 160/0xA0). + * 704 1*32 uint8_t[32] unknown704 Unknown data (obs. 8 bytes filled, rest 0). + * 736 4 uint32_t lenWrappedIntegrityKey Length of wrappedIntegrityKey. + * 740 1*256 uint8_t[256] wrappedIntegrityKey Integrity key. + * 996 4 uint32_t lenUnknown1000 Length of unknown1000. + * 1000 1*256 uint8_t[256] unknown1000 Unknown key-like field with length specified by unknownRnd95GaLen (max 256). + * 1256 8 uint64_t decryptedDataLength Length in bytes of the underlying data stream. + * 1264 4 uint32_t possibleHeaderVersion Could be a variable indicating header version (observed: 1/0x1). + * 1268 1*8 uint8_t[8] signature Header signature (ASCII: 'cdsaencr'). + */ + + public static final int STRUCTSIZE = 1276; + + private final byte[] unknown0 = new byte[1*16]; + private final byte[] blockSize = new byte[4]; + private final byte[] unknownInt20 = new byte[4]; + private final byte[] unknownInt24 = new byte[4]; + private final byte[] unknownInt28 = new byte[4]; + private final byte[] unknownInt32 = new byte[4]; + private final byte[] unknownInt36 = new byte[4]; + private final byte[] unknownInt40 = new byte[4]; + private final byte[] unknownInt44 = new byte[4]; + private final byte[] kdfIterationCount = new byte[4]; + private final byte[] kdfSaltLen = new byte[4]; + private final byte[] kdfSalt = new byte[1*32]; + private final byte[] laban = new byte[4]; + private final byte[] edward = new byte[4]; + private final byte[] palle = new byte[4]; + private final byte[] lisa = new byte[4]; + private final byte[] unwrapIv = new byte[1*32]; + private final byte[] lenWrappedAesKey = new byte[4]; + private final byte[] wrappedAesKey = new byte[1*256]; + private final byte[] unknownInt396 = new byte[4]; + private final byte[] unknownInt400 = new byte[4]; + private final byte[] unknown404 = new byte[1*32]; + private final byte[] lenWrappedHmacSha1Key = new byte[4]; + private final byte[] wrappedHmacSha1Key = new byte[1*256]; + private final byte[] unknownInt696 = new byte[4]; + private final byte[] unknownInt700 = new byte[4]; + private final byte[] unknown704 = new byte[1*32]; + private final byte[] lenWrappedIntegrityKey = new byte[4]; + private final byte[] wrappedIntegrityKey = new byte[1*256]; + private final byte[] lenUnknown1000 = new byte[4]; + private final byte[] unknown1000 = new byte[1*256]; + private final byte[] decryptedDataLength = new byte[8]; + private final byte[] possibleHeaderVersion = new byte[4]; + private final byte[] signature = new byte[1*8]; + + public ExperimentalV1Header(byte[] data, int offset) { + System.arraycopy(data, offset+0, unknown0, 0, 1*16); + System.arraycopy(data, offset+16, blockSize, 0, 4); + System.arraycopy(data, offset+20, unknownInt20, 0, 4); + System.arraycopy(data, offset+24, unknownInt24, 0, 4); + System.arraycopy(data, offset+28, unknownInt28, 0, 4); + System.arraycopy(data, offset+32, unknownInt32, 0, 4); + System.arraycopy(data, offset+36, unknownInt36, 0, 4); + System.arraycopy(data, offset+40, unknownInt40, 0, 4); + System.arraycopy(data, offset+44, unknownInt44, 0, 4); + System.arraycopy(data, offset+48, kdfIterationCount, 0, 4); + System.arraycopy(data, offset+52, kdfSaltLen, 0, 4); + System.arraycopy(data, offset+56, kdfSalt, 0, 1*32); + System.arraycopy(data, offset+88, laban, 0, 4); + System.arraycopy(data, offset+92, edward, 0, 4); + System.arraycopy(data, offset+96, palle, 0, 4); + System.arraycopy(data, offset+100, lisa, 0, 4); + System.arraycopy(data, offset+104, unwrapIv, 0, 1*32); + System.arraycopy(data, offset+136, lenWrappedAesKey, 0, 4); + System.arraycopy(data, offset+140, wrappedAesKey, 0, 1*256); + System.arraycopy(data, offset+396, unknownInt396, 0, 4); + System.arraycopy(data, offset+400, unknownInt400, 0, 4); + System.arraycopy(data, offset+404, unknown404, 0, 1*32); + System.arraycopy(data, offset+436, lenWrappedHmacSha1Key, 0, 4); + System.arraycopy(data, offset+440, wrappedHmacSha1Key, 0, 1*256); + System.arraycopy(data, offset+696, unknownInt696, 0, 4); + System.arraycopy(data, offset+700, unknownInt700, 0, 4); + System.arraycopy(data, offset+704, unknown704, 0, 1*32); + System.arraycopy(data, offset+736, lenWrappedIntegrityKey, 0, 4); + System.arraycopy(data, offset+740, wrappedIntegrityKey, 0, 1*256); + System.arraycopy(data, offset+996, lenUnknown1000, 0, 4); + System.arraycopy(data, offset+1000, unknown1000, 0, 1*256); + System.arraycopy(data, offset+1256, decryptedDataLength, 0, 8); + System.arraycopy(data, offset+1264, possibleHeaderVersion, 0, 4); + System.arraycopy(data, offset+1268, signature, 0, 1*8); + } + + public static int length() { return STRUCTSIZE; } + + /** Unknown data. */ + public byte[] getUnknown0() { return Util.readByteArrayBE(unknown0); } + /** Block size of the encrypted block data. */ + public int getBlockSize() { return Util.readIntBE(blockSize); } + /** Unknown integer. */ + public int getUnknownInt20() { return Util.readIntBE(unknownInt20); } + /** Unknown integer. */ + public int getUnknownInt24() { return Util.readIntBE(unknownInt24); } + /** Unknown integer. */ + public int getUnknownInt28() { return Util.readIntBE(unknownInt28); } + /** Unknown integer. */ + public int getUnknownInt32() { return Util.readIntBE(unknownInt32); } + /** Unknown integer. */ + public int getUnknownInt36() { return Util.readIntBE(unknownInt36); } + /** Unknown integer. */ + public int getUnknownInt40() { return Util.readIntBE(unknownInt40); } + /** Unknown integer. */ + public int getUnknownInt44() { return Util.readIntBE(unknownInt44); } + /** Iteration count for the key derivation function (normally 1000). */ + public int getKdfIterationCount() { return Util.readIntBE(kdfIterationCount); } + /** Length of kdfSalt in bytes. */ + public int getKdfSaltLen() { return Util.readIntBE(kdfSaltLen); } + /** Salt value for the key derivation function */ + public byte[] getKdfSalt() { return Util.readByteArrayBE(kdfSalt); } + /** Unknown variable with observed value 16/0x10. */ + public int getLaban() { return Util.readIntBE(laban); } + /** Unknown variable with observed value 5/0x5. */ + public int getEdward() { return Util.readIntBE(edward); } + /** Unknown variable with observed value 0x80000001. */ + public int getPalle() { return Util.readIntBE(palle); } + /** Unknown variable with observed value 128/0x80. */ + public int getLisa() { return Util.readIntBE(lisa); } + /** Initialization Vector for encryption-key unwrapping. */ + public byte[] getUnwrapIv() { return Util.readByteArrayBE(unwrapIv); } + /** Length of wrappedAesKey in bytes (max 256). */ + public int getLenWrappedAesKey() { return Util.readIntBE(lenWrappedAesKey); } + /** The AES key (wrapped). */ + public byte[] getWrappedAesKey() { return Util.readByteArrayBE(wrappedAesKey); } + /** Unknown integer (observed value: 91/0x5B). */ + public int getUnknownInt396() { return Util.readIntBE(unknownInt396); } + /** Unknown integer (observed value: 160/0xA0). */ + public int getUnknownInt400() { return Util.readIntBE(unknownInt400); } + /** Unknown data (observed: 8 bytes filled, rest 0). */ + public byte[] getUnknown404() { return Util.readByteArrayBE(unknown404); } + /** Length of wrappedHmacSha1Key in bytes (max 256). */ + public int getLenWrappedHmacSha1Key() { return Util.readIntBE(lenWrappedHmacSha1Key); } + /** The HMAC SHA-1 key (wrapped). */ + public byte[] getWrappedHmacSha1Key() { return Util.readByteArrayBE(wrappedHmacSha1Key); } + /** Unknown integer (observed value: 91/0x5B). */ + public int getUnknownInt696() { return Util.readIntBE(unknownInt696); } + /** Unknown integer (observed value: 160/0xA0). */ + public int getUnknownInt700() { return Util.readIntBE(unknownInt700); } + /** Unknown data (obs. 8 bytes filled, rest 0). */ + public byte[] getUnknown704() { return Util.readByteArrayBE(unknown704); } + /** Length of wrappedIntegrityKey. */ + public int getLenWrappedIntegrityKey() { return Util.readIntBE(lenWrappedIntegrityKey); } + /** Integrity key. */ + public byte[] getWrappedIntegrityKey() { return Util.readByteArrayBE(wrappedIntegrityKey); } + /** Length of unknown1000. */ + public int getLenUnknown1000() { return Util.readIntBE(lenUnknown1000); } + /** Unknown key-like field with length specified by unknownRnd95GaLen (max 256). */ + public byte[] getUnknown1000() { return Util.readByteArrayBE(unknown1000); } + /** Length in bytes of the underlying data stream. */ + public long getDecryptedDataLength() { return Util.readLongBE(decryptedDataLength); } + /** Could be a variable indicating header version (observed: 1/0x1). */ + public int getPossibleHeaderVersion() { return Util.readIntBE(possibleHeaderVersion); } + /** Header signature (ASCII: 'cdsaencr'). */ + public byte[] getSignature() { return Util.readByteArrayBE(signature); } + + public void printFields(PrintStream ps, String prefix) { + ps.println(prefix + " unknown0: " + getUnknown0()); + ps.println(prefix + " blockSize: " + getBlockSize()); + ps.println(prefix + " unknownInt20: " + getUnknownInt20()); + ps.println(prefix + " unknownInt24: " + getUnknownInt24()); + ps.println(prefix + " unknownInt28: " + getUnknownInt28()); + ps.println(prefix + " unknownInt32: " + getUnknownInt32()); + ps.println(prefix + " unknownInt36: " + getUnknownInt36()); + ps.println(prefix + " unknownInt40: " + getUnknownInt40()); + ps.println(prefix + " unknownInt44: " + getUnknownInt44()); + ps.println(prefix + " kdfIterationCount: " + getKdfIterationCount()); + ps.println(prefix + " kdfSaltLen: " + getKdfSaltLen()); + ps.println(prefix + " kdfSalt: " + getKdfSalt()); + ps.println(prefix + " laban: " + getLaban()); + ps.println(prefix + " edward: " + getEdward()); + ps.println(prefix + " palle: " + getPalle()); + ps.println(prefix + " lisa: " + getLisa()); + ps.println(prefix + " unwrapIv: " + getUnwrapIv()); + ps.println(prefix + " lenWrappedAesKey: " + getLenWrappedAesKey()); + ps.println(prefix + " wrappedAesKey: " + getWrappedAesKey()); + ps.println(prefix + " unknownInt396: " + getUnknownInt396()); + ps.println(prefix + " unknownInt400: " + getUnknownInt400()); + ps.println(prefix + " unknown404: " + getUnknown404()); + ps.println(prefix + " lenWrappedHmacSha1Key: " + getLenWrappedHmacSha1Key()); + ps.println(prefix + " wrappedHmacSha1Key: " + getWrappedHmacSha1Key()); + ps.println(prefix + " unknownInt696: " + getUnknownInt696()); + ps.println(prefix + " unknownInt700: " + getUnknownInt700()); + ps.println(prefix + " unknown704: " + getUnknown704()); + ps.println(prefix + " lenWrappedIntegrityKey: " + getLenWrappedIntegrityKey()); + ps.println(prefix + " wrappedIntegrityKey: " + getWrappedIntegrityKey()); + ps.println(prefix + " lenUnknown1000: " + getLenUnknown1000()); + ps.println(prefix + " unknown1000: " + getUnknown1000()); + ps.println(prefix + " decryptedDataLength: " + getDecryptedDataLength()); + ps.println(prefix + " possibleHeaderVersion: " + getPossibleHeaderVersion()); + ps.println(prefix + " signature: " + getSignature()); + } + + public void print(PrintStream ps, String prefix) { + ps.println(prefix + "ExperimentalV1Header:"); + printFields(ps, prefix); + } + + public byte[] getBytes() { + byte[] result = new byte[length()]; + int offset = 0; + System.arraycopy(this.unknown0, 0, result, offset, this.unknown0.length); offset += this.unknown0.length; + System.arraycopy(this.blockSize, 0, result, offset, this.blockSize.length); offset += this.blockSize.length; + System.arraycopy(this.unknownInt20, 0, result, offset, this.unknownInt20.length); offset += this.unknownInt20.length; + System.arraycopy(this.unknownInt24, 0, result, offset, this.unknownInt24.length); offset += this.unknownInt24.length; + System.arraycopy(this.unknownInt28, 0, result, offset, this.unknownInt28.length); offset += this.unknownInt28.length; + System.arraycopy(this.unknownInt32, 0, result, offset, this.unknownInt32.length); offset += this.unknownInt32.length; + System.arraycopy(this.unknownInt36, 0, result, offset, this.unknownInt36.length); offset += this.unknownInt36.length; + System.arraycopy(this.unknownInt40, 0, result, offset, this.unknownInt40.length); offset += this.unknownInt40.length; + System.arraycopy(this.unknownInt44, 0, result, offset, this.unknownInt44.length); offset += this.unknownInt44.length; + System.arraycopy(this.kdfIterationCount, 0, result, offset, this.kdfIterationCount.length); offset += this.kdfIterationCount.length; + System.arraycopy(this.kdfSaltLen, 0, result, offset, this.kdfSaltLen.length); offset += this.kdfSaltLen.length; + System.arraycopy(this.kdfSalt, 0, result, offset, this.kdfSalt.length); offset += this.kdfSalt.length; + System.arraycopy(this.laban, 0, result, offset, this.laban.length); offset += this.laban.length; + System.arraycopy(this.edward, 0, result, offset, this.edward.length); offset += this.edward.length; + System.arraycopy(this.palle, 0, result, offset, this.palle.length); offset += this.palle.length; + System.arraycopy(this.lisa, 0, result, offset, this.lisa.length); offset += this.lisa.length; + System.arraycopy(this.unwrapIv, 0, result, offset, this.unwrapIv.length); offset += this.unwrapIv.length; + System.arraycopy(this.lenWrappedAesKey, 0, result, offset, this.lenWrappedAesKey.length); offset += this.lenWrappedAesKey.length; + System.arraycopy(this.wrappedAesKey, 0, result, offset, this.wrappedAesKey.length); offset += this.wrappedAesKey.length; + System.arraycopy(this.unknownInt396, 0, result, offset, this.unknownInt396.length); offset += this.unknownInt396.length; + System.arraycopy(this.unknownInt400, 0, result, offset, this.unknownInt400.length); offset += this.unknownInt400.length; + System.arraycopy(this.unknown404, 0, result, offset, this.unknown404.length); offset += this.unknown404.length; + System.arraycopy(this.lenWrappedHmacSha1Key, 0, result, offset, this.lenWrappedHmacSha1Key.length); offset += this.lenWrappedHmacSha1Key.length; + System.arraycopy(this.wrappedHmacSha1Key, 0, result, offset, this.wrappedHmacSha1Key.length); offset += this.wrappedHmacSha1Key.length; + System.arraycopy(this.unknownInt696, 0, result, offset, this.unknownInt696.length); offset += this.unknownInt696.length; + System.arraycopy(this.unknownInt700, 0, result, offset, this.unknownInt700.length); offset += this.unknownInt700.length; + System.arraycopy(this.unknown704, 0, result, offset, this.unknown704.length); offset += this.unknown704.length; + System.arraycopy(this.lenWrappedIntegrityKey, 0, result, offset, this.lenWrappedIntegrityKey.length); offset += this.lenWrappedIntegrityKey.length; + System.arraycopy(this.wrappedIntegrityKey, 0, result, offset, this.wrappedIntegrityKey.length); offset += this.wrappedIntegrityKey.length; + System.arraycopy(this.lenUnknown1000, 0, result, offset, this.lenUnknown1000.length); offset += this.lenUnknown1000.length; + System.arraycopy(this.unknown1000, 0, result, offset, this.unknown1000.length); offset += this.unknown1000.length; + System.arraycopy(this.decryptedDataLength, 0, result, offset, this.decryptedDataLength.length); offset += this.decryptedDataLength.length; + System.arraycopy(this.possibleHeaderVersion, 0, result, offset, this.possibleHeaderVersion.length); offset += this.possibleHeaderVersion.length; + System.arraycopy(this.signature, 0, result, offset, this.signature.length); offset += this.signature.length; + return result; + } +} diff --git a/src/org/catacombae/dmg/encrypted/ExperimentalV1Header.struct b/src/org/catacombae/dmg/encrypted/ExperimentalV1Header.struct new file mode 100644 index 0000000..efc839c --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/ExperimentalV1Header.struct @@ -0,0 +1,36 @@ +struct ExperimentalV1Header { + uint8_t unknown0[16]; Unknown data. + uint32_t blockSize; Block size of the encrypted block data. + uint32_t unknownInt20; Unknown integer. + uint32_t unknownInt24; Unknown integer. + uint32_t unknownInt28; Unknown integer. + uint32_t unknownInt32; Unknown integer. + uint32_t unknownInt36; Unknown integer. + uint32_t kdfAlgorithm; Algorithm of the key derivation function. + uint32_t kdfPrngAlgorithm; Some other algorithm? + uint32_t kdfIterationCount; Iteration count for the key derivation function (normally 1000). + uint32_t kdfSaltLen; Length of kdfSalt in bytes. + uint8_t kdfSalt[32]; Salt value for the key derivation function + uint32_t laban; Unknown variable with observed value 16/0x10. + uint32_t edward; Unknown variable with observed value 5/0x5. + uint32_t palle; Unknown variable with observed value 0x80000001. + uint32_t lisa; Unknown variable with observed value 128/0x80. + uint8_t unwrapIv[32]; Initialization Vector for encryption-key unwrapping. + uint32_t lenWrappedAesKey; Length of wrappedAesKey in bytes (max 256). + uint8_t wrappedAesKey[256]; The AES key (wrapped). + uint32_t unknownInt396; Unknown integer (observed value: 91/0x5B). + uint32_t unknownInt400; Unknown integer (observed value: 160/0xA0). + uint8_t unknown404[32]; Unknown data (observed: 8 bytes filled, rest 0). + uint32_t lenWrappedHmacSha1Key; Length of wrappedHmacSha1Key in bytes (max 256). + uint8_t wrappedHmacSha1Key[256]; The HMAC SHA-1 key (wrapped). + uint32_t unknownInt696; Unknown integer (observed value: 91/0x5B). + uint32_t unknownInt700; Unknown integer (observed value: 160/0xA0). + uint8_t unknown704[32]; Unknown data (obs. 8 bytes filled, rest 0). + uint32_t lenWrappedIntegrityKey; Length of wrappedIntegrityKey. + uint8_t wrappedIntegrityKey[256]; Integrity key. + uint32_t lenUnknown1000; Length of unknown1000. + uint8_t unknown1000[256]; Unknown key-like field with length specified by unknownRnd95GaLen (max 256). + uint64_t decryptedDataLength; Length in bytes of the underlying data stream. + uint32_t possibleHeaderVersion; Could be a variable indicating header version (observed: 1/0x1). + uint8_t signature[8]; Header signature (ASCII: 'cdsaencr'). +}; \ No newline at end of file diff --git a/src/org/catacombae/dmg/encrypted/ExperimentalV2Header.java b/src/org/catacombae/dmg/encrypted/ExperimentalV2Header.java new file mode 100644 index 0000000..579cdd5 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/ExperimentalV2Header.java @@ -0,0 +1,242 @@ +package org.catacombae.dmg.encrypted; + +import java.io.PrintStream; +import org.catacombae.dmgextractor.Util; + +/** This class was generated by CStructToJavaClass. */ +public class ExperimentalV2Header { + /* + * struct ExperimentalV2Header + * size: 248 bytes + * description: + * + * BP Size Type Identifier Description + * ------------------------------------------------------------------------------------------------------------ + * 0 1*8 uint8_t[8] signature Header signature (ASCII: 'encrcdsa'). + * 8 4 uint32_t possibleHeaderVersion Possibly the version of the encrypted volume format. + * 12 4 uint32_t laban Unknown variable with observed value 16/0x10. + * 16 4 uint32_t edward Unknown variable with observed value 5/0x5. + * 20 4 uint32_t palle Unknown variable with observed value 0x80000001. + * 24 4 uint32_t lisa Unknown variable with observed value 128/0x80. + * 28 4 uint32_t unknownInt28 Unknown variable with observed value 91/0x5B. + * 32 4 uint32_t unknownInt32 Unknown variable with observed value 160/0xA0. + * 36 1*16 uint8_t[16] unknown1 Unknown binary data. + * 52 4 uint32_t blockSize Block size of the encrypted block data. + * 56 8 uint64_t encryptedDataLength Length in bytes of the data that has been encrypted. + * 64 8 uint64_t offsetToDataStart Offset to the start of the encrypted block data. + * 72 4 uint32_t unknownInt72 Unknown variable with observed value 1/0x1. + * 76 4 uint32_t unknownInt76 Unknown variable with observed value 1/0x1. + * 80 8 uint64_t possiblePointerToKdfAlgorithm Could be a pointer to where kdf_algorithm is located. + * 88 8 uint64_t unknownLong88 Unknown variable with observed value 616/0x268. + * 96 4 uint32_t kdfAlgorithm Algorithm of the key derivation function. + * 100 4 uint32_t kdfPrngAlgorithm ? + * 104 4 uint32_t kdfIterationCount Iteration count (normally 1000). + * 108 4 uint32_t kdfSaltLen Length of kdfSalt (in bytes). + * 112 1*32 uint8_t[32] kdfSalt Salt value for key derivation. + * 144 4 uint32_t blobEncIvSize Size of blobEncIv. + * 148 1*32 uint8_t[32] blobEncIv Initialization Vector for encryption-key unwrapping. + * 180 4 uint32_t blobEncKeyBits Number of bits in the keyblob's encryption key. + * 184 4 uint32_t blobEncAlgorithm Encryption algorithm used to encrypt the key blob. + * 188 4 uint32_t blobEncPadding Padding. (?) + * 192 4 uint32_t blobEncMode Encryption mode for the algorithm. + * 196 4 uint32_t encryptedKeyblobSize Size of encryptedKeyBlob. + * 200 1*48 uint8_t[48] encryptedKeyblob The encrypted key blob, containing all keys. + */ + + public static final int STRUCTSIZE = 248; + + private final byte[] signature = new byte[1*8]; + private final byte[] possibleHeaderVersion = new byte[4]; + private final byte[] laban = new byte[4]; + private final byte[] edward = new byte[4]; + private final byte[] palle = new byte[4]; + private final byte[] lisa = new byte[4]; + private final byte[] unknownInt28 = new byte[4]; + private final byte[] unknownInt32 = new byte[4]; + private final byte[] unknown1 = new byte[1*16]; + private final byte[] blockSize = new byte[4]; + private final byte[] encryptedDataLength = new byte[8]; + private final byte[] offsetToDataStart = new byte[8]; + private final byte[] unknownInt72 = new byte[4]; + private final byte[] unknownInt76 = new byte[4]; + private final byte[] possiblePointerToKdfAlgorithm = new byte[8]; + private final byte[] unknownLong88 = new byte[8]; + private final byte[] kdfAlgorithm = new byte[4]; + private final byte[] kdfPrngAlgorithm = new byte[4]; + private final byte[] kdfIterationCount = new byte[4]; + private final byte[] kdfSaltLen = new byte[4]; + private final byte[] kdfSalt = new byte[1*32]; + private final byte[] blobEncIvSize = new byte[4]; + private final byte[] blobEncIv = new byte[1*32]; + private final byte[] blobEncKeyBits = new byte[4]; + private final byte[] blobEncAlgorithm = new byte[4]; + private final byte[] blobEncPadding = new byte[4]; + private final byte[] blobEncMode = new byte[4]; + private final byte[] encryptedKeyblobSize = new byte[4]; + private final byte[] encryptedKeyblob = new byte[1*48]; + + public ExperimentalV2Header(byte[] data, int offset) { + System.arraycopy(data, offset+0, signature, 0, 1*8); + System.arraycopy(data, offset+8, possibleHeaderVersion, 0, 4); + System.arraycopy(data, offset+12, laban, 0, 4); + System.arraycopy(data, offset+16, edward, 0, 4); + System.arraycopy(data, offset+20, palle, 0, 4); + System.arraycopy(data, offset+24, lisa, 0, 4); + System.arraycopy(data, offset+28, unknownInt28, 0, 4); + System.arraycopy(data, offset+32, unknownInt32, 0, 4); + System.arraycopy(data, offset+36, unknown1, 0, 1*16); + System.arraycopy(data, offset+52, blockSize, 0, 4); + System.arraycopy(data, offset+56, encryptedDataLength, 0, 8); + System.arraycopy(data, offset+64, offsetToDataStart, 0, 8); + System.arraycopy(data, offset+72, unknownInt72, 0, 4); + System.arraycopy(data, offset+76, unknownInt76, 0, 4); + System.arraycopy(data, offset+80, possiblePointerToKdfAlgorithm, 0, 8); + System.arraycopy(data, offset+88, unknownLong88, 0, 8); + System.arraycopy(data, offset+96, kdfAlgorithm, 0, 4); + System.arraycopy(data, offset+100, kdfPrngAlgorithm, 0, 4); + System.arraycopy(data, offset+104, kdfIterationCount, 0, 4); + System.arraycopy(data, offset+108, kdfSaltLen, 0, 4); + System.arraycopy(data, offset+112, kdfSalt, 0, 1*32); + System.arraycopy(data, offset+144, blobEncIvSize, 0, 4); + System.arraycopy(data, offset+148, blobEncIv, 0, 1*32); + System.arraycopy(data, offset+180, blobEncKeyBits, 0, 4); + System.arraycopy(data, offset+184, blobEncAlgorithm, 0, 4); + System.arraycopy(data, offset+188, blobEncPadding, 0, 4); + System.arraycopy(data, offset+192, blobEncMode, 0, 4); + System.arraycopy(data, offset+196, encryptedKeyblobSize, 0, 4); + System.arraycopy(data, offset+200, encryptedKeyblob, 0, 1*48); + } + + public static int length() { return STRUCTSIZE; } + + /** Header signature (ASCII: 'encrcdsa'). */ + public byte[] getSignature() { return Util.readByteArrayBE(signature); } + /** Possibly the version of the encrypted volume format. */ + public int getPossibleHeaderVersion() { return Util.readIntBE(possibleHeaderVersion); } + /** Unknown variable with observed value 16/0x10. */ + public int getLaban() { return Util.readIntBE(laban); } + /** Unknown variable with observed value 5/0x5. */ + public int getEdward() { return Util.readIntBE(edward); } + /** Unknown variable with observed value 0x80000001. */ + public int getPalle() { return Util.readIntBE(palle); } + /** Unknown variable with observed value 128/0x80. */ + public int getLisa() { return Util.readIntBE(lisa); } + /** Unknown variable with observed value 91/0x5B. */ + public int getUnknownInt28() { return Util.readIntBE(unknownInt28); } + /** Unknown variable with observed value 160/0xA0. */ + public int getUnknownInt32() { return Util.readIntBE(unknownInt32); } + /** Unknown binary data. */ + public byte[] getUnknown1() { return Util.readByteArrayBE(unknown1); } + /** Block size of the encrypted block data. */ + public int getBlockSize() { return Util.readIntBE(blockSize); } + /** Length in bytes of the data that has been encrypted. */ + public long getEncryptedDataLength() { return Util.readLongBE(encryptedDataLength); } + /** Offset to the start of the encrypted block data. */ + public long getOffsetToDataStart() { return Util.readLongBE(offsetToDataStart); } + /** Unknown variable with observed value 1/0x1. */ + public int getUnknownInt72() { return Util.readIntBE(unknownInt72); } + /** Unknown variable with observed value 1/0x1. */ + public int getUnknownInt76() { return Util.readIntBE(unknownInt76); } + /** Could be a pointer to where kdf_algorithm is located. */ + public long getPossiblePointerToKdfAlgorithm() { return Util.readLongBE(possiblePointerToKdfAlgorithm); } + /** Unknown variable with observed value 616/0x268. */ + public long getUnknownLong88() { return Util.readLongBE(unknownLong88); } + /** Algorithm of the key derivation function. */ + public int getKdfAlgorithm() { return Util.readIntBE(kdfAlgorithm); } + /** ? */ + public int getKdfPrngAlgorithm() { return Util.readIntBE(kdfPrngAlgorithm); } + /** Iteration count (normally 1000). */ + public int getKdfIterationCount() { return Util.readIntBE(kdfIterationCount); } + /** Length of kdfSalt (in bytes). */ + public int getKdfSaltLen() { return Util.readIntBE(kdfSaltLen); } + /** Salt value for key derivation. */ + public byte[] getKdfSalt() { return Util.readByteArrayBE(kdfSalt); } + /** Size of blobEncIv. */ + public int getBlobEncIvSize() { return Util.readIntBE(blobEncIvSize); } + /** Initialization Vector for encryption-key unwrapping. */ + public byte[] getBlobEncIv() { return Util.readByteArrayBE(blobEncIv); } + /** Number of bits in the keyblob's encryption key. */ + public int getBlobEncKeyBits() { return Util.readIntBE(blobEncKeyBits); } + /** Encryption algorithm used to encrypt the key blob. */ + public int getBlobEncAlgorithm() { return Util.readIntBE(blobEncAlgorithm); } + /** Padding. (?) */ + public int getBlobEncPadding() { return Util.readIntBE(blobEncPadding); } + /** Encryption mode for the algorithm. */ + public int getBlobEncMode() { return Util.readIntBE(blobEncMode); } + /** Size of encryptedKeyBlob. */ + public int getEncryptedKeyblobSize() { return Util.readIntBE(encryptedKeyblobSize); } + /** The encrypted key blob, containing all keys. */ + public byte[] getEncryptedKeyblob() { return Util.readByteArrayBE(encryptedKeyblob); } + + public void printFields(PrintStream ps, String prefix) { + ps.println(prefix + " signature: " + getSignature()); + ps.println(prefix + " possibleHeaderVersion: " + getPossibleHeaderVersion()); + ps.println(prefix + " laban: " + getLaban()); + ps.println(prefix + " edward: " + getEdward()); + ps.println(prefix + " palle: " + getPalle()); + ps.println(prefix + " lisa: " + getLisa()); + ps.println(prefix + " unknownInt28: " + getUnknownInt28()); + ps.println(prefix + " unknownInt32: " + getUnknownInt32()); + ps.println(prefix + " unknown1: " + getUnknown1()); + ps.println(prefix + " blockSize: " + getBlockSize()); + ps.println(prefix + " encryptedDataLength: " + getEncryptedDataLength()); + ps.println(prefix + " offsetToDataStart: " + getOffsetToDataStart()); + ps.println(prefix + " unknownInt72: " + getUnknownInt72()); + ps.println(prefix + " unknownInt76: " + getUnknownInt76()); + ps.println(prefix + " possiblePointerToKdfAlgorithm: " + getPossiblePointerToKdfAlgorithm()); + ps.println(prefix + " unknownLong88: " + getUnknownLong88()); + ps.println(prefix + " kdfAlgorithm: " + getKdfAlgorithm()); + ps.println(prefix + " kdfPrngAlgorithm: " + getKdfPrngAlgorithm()); + ps.println(prefix + " kdfIterationCount: " + getKdfIterationCount()); + ps.println(prefix + " kdfSaltLen: " + getKdfSaltLen()); + ps.println(prefix + " kdfSalt: " + getKdfSalt()); + ps.println(prefix + " blobEncIvSize: " + getBlobEncIvSize()); + ps.println(prefix + " blobEncIv: " + getBlobEncIv()); + ps.println(prefix + " blobEncKeyBits: " + getBlobEncKeyBits()); + ps.println(prefix + " blobEncAlgorithm: " + getBlobEncAlgorithm()); + ps.println(prefix + " blobEncPadding: " + getBlobEncPadding()); + ps.println(prefix + " blobEncMode: " + getBlobEncMode()); + ps.println(prefix + " encryptedKeyblobSize: " + getEncryptedKeyblobSize()); + ps.println(prefix + " encryptedKeyblob: " + getEncryptedKeyblob()); + } + + public void print(PrintStream ps, String prefix) { + ps.println(prefix + "ExperimentalV2Header:"); + printFields(ps, prefix); + } + + public byte[] getBytes() { + byte[] result = new byte[length()]; + int offset = 0; + System.arraycopy(this.signature, 0, result, offset, this.signature.length); offset += this.signature.length; + System.arraycopy(this.possibleHeaderVersion, 0, result, offset, this.possibleHeaderVersion.length); offset += this.possibleHeaderVersion.length; + System.arraycopy(this.laban, 0, result, offset, this.laban.length); offset += this.laban.length; + System.arraycopy(this.edward, 0, result, offset, this.edward.length); offset += this.edward.length; + System.arraycopy(this.palle, 0, result, offset, this.palle.length); offset += this.palle.length; + System.arraycopy(this.lisa, 0, result, offset, this.lisa.length); offset += this.lisa.length; + System.arraycopy(this.unknownInt28, 0, result, offset, this.unknownInt28.length); offset += this.unknownInt28.length; + System.arraycopy(this.unknownInt32, 0, result, offset, this.unknownInt32.length); offset += this.unknownInt32.length; + System.arraycopy(this.unknown1, 0, result, offset, this.unknown1.length); offset += this.unknown1.length; + System.arraycopy(this.blockSize, 0, result, offset, this.blockSize.length); offset += this.blockSize.length; + System.arraycopy(this.encryptedDataLength, 0, result, offset, this.encryptedDataLength.length); offset += this.encryptedDataLength.length; + System.arraycopy(this.offsetToDataStart, 0, result, offset, this.offsetToDataStart.length); offset += this.offsetToDataStart.length; + System.arraycopy(this.unknownInt72, 0, result, offset, this.unknownInt72.length); offset += this.unknownInt72.length; + System.arraycopy(this.unknownInt76, 0, result, offset, this.unknownInt76.length); offset += this.unknownInt76.length; + System.arraycopy(this.possiblePointerToKdfAlgorithm, 0, result, offset, this.possiblePointerToKdfAlgorithm.length); offset += this.possiblePointerToKdfAlgorithm.length; + System.arraycopy(this.unknownLong88, 0, result, offset, this.unknownLong88.length); offset += this.unknownLong88.length; + System.arraycopy(this.kdfAlgorithm, 0, result, offset, this.kdfAlgorithm.length); offset += this.kdfAlgorithm.length; + System.arraycopy(this.kdfPrngAlgorithm, 0, result, offset, this.kdfPrngAlgorithm.length); offset += this.kdfPrngAlgorithm.length; + System.arraycopy(this.kdfIterationCount, 0, result, offset, this.kdfIterationCount.length); offset += this.kdfIterationCount.length; + System.arraycopy(this.kdfSaltLen, 0, result, offset, this.kdfSaltLen.length); offset += this.kdfSaltLen.length; + System.arraycopy(this.kdfSalt, 0, result, offset, this.kdfSalt.length); offset += this.kdfSalt.length; + System.arraycopy(this.blobEncIvSize, 0, result, offset, this.blobEncIvSize.length); offset += this.blobEncIvSize.length; + System.arraycopy(this.blobEncIv, 0, result, offset, this.blobEncIv.length); offset += this.blobEncIv.length; + System.arraycopy(this.blobEncKeyBits, 0, result, offset, this.blobEncKeyBits.length); offset += this.blobEncKeyBits.length; + System.arraycopy(this.blobEncAlgorithm, 0, result, offset, this.blobEncAlgorithm.length); offset += this.blobEncAlgorithm.length; + System.arraycopy(this.blobEncPadding, 0, result, offset, this.blobEncPadding.length); offset += this.blobEncPadding.length; + System.arraycopy(this.blobEncMode, 0, result, offset, this.blobEncMode.length); offset += this.blobEncMode.length; + System.arraycopy(this.encryptedKeyblobSize, 0, result, offset, this.encryptedKeyblobSize.length); offset += this.encryptedKeyblobSize.length; + System.arraycopy(this.encryptedKeyblob, 0, result, offset, this.encryptedKeyblob.length); offset += this.encryptedKeyblob.length; + return result; + } +} diff --git a/src/org/catacombae/dmg/encrypted/ExperimentalV2Header.struct b/src/org/catacombae/dmg/encrypted/ExperimentalV2Header.struct new file mode 100644 index 0000000..b381725 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/ExperimentalV2Header.struct @@ -0,0 +1,31 @@ +struct ExperimentalV2Header { + uint8_t signature[8]; Header signature (ASCII: 'encrcdsa'). + uint32_t possibleHeaderVersion; Possibly the version of the encrypted volume format. + uint32_t laban; Unknown variable with observed value 16/0x10. + uint32_t edward; Unknown variable with observed value 5/0x5. + uint32_t palle; Unknown variable with observed value 0x80000001. + uint32_t lisa; Unknown variable with observed value 128/0x80. + uint32_t unknownInt28; Unknown variable with observed value 91/0x5B. + uint32_t unknownInt32; Unknown variable with observed value 160/0xA0. + uint8_t unknown1[16]; Unknown binary data. + uint32_t blockSize; Block size of the encrypted block data. + uint64_t encryptedDataLength; Length in bytes of the data that has been encrypted. + uint64_t offsetToDataStart; Offset to the start of the encrypted block data. + uint32_t unknownInt72; Unknown variable with observed value 1/0x1. + uint32_t unknownInt76; Unknown variable with observed value 1/0x1. + uint64_t possiblePointerToKdfAlgorithm; Could be a pointer to where kdf_algorithm is located. + uint64_t unknownLong88; Unknown variable with observed value 616/0x268. + uint32_t kdfAlgorithm; Algorithm of the key derivation function. + uint32_t kdfPrngAlgorithm; Some other algorithm? + uint32_t kdfIterationCount; Iteration count (normally 1000). + uint32_t kdfSaltLen; Length of kdfSalt (in bytes). + uint8_t kdfSalt[32]; Salt value for key derivation. + uint32_t blobEncIvSize; Size of blobEncIv. + uint8_t blobEncIv[32]; Initialization Vector for encryption-key unwrapping. + uint32_t blobEncKeyBits; Number of bits in the keyblob's encryption key. + uint32_t blobEncAlgorithm; Encryption algorithm used to encrypt the key blob. + uint32_t blobEncPadding; Padding. (?) + uint32_t blobEncMode; Encryption mode for the algorithm. + uint32_t encryptedKeyblobSize; Size of encryptedKeyBlob. + uint8_t encryptedKeyblob[48]; The encrypted key blob, containing all keys. +}; \ No newline at end of file diff --git a/src/org/catacombae/dmg/encrypted/ReadableCEncryptedEncodingStream.java b/src/org/catacombae/dmg/encrypted/ReadableCEncryptedEncodingStream.java new file mode 100644 index 0000000..f88b90d --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/ReadableCEncryptedEncodingStream.java @@ -0,0 +1,432 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + * This code was written from studying vfdecrypt, Copyright (c) 2006 + * Ralf-Philipp Weinmann + * Jacob Appelbaum + * Christian Fromme + * + * [I'm not sure if their copyright and license terms need to be applied, + * but in case they do, the original license terms are reprinted below + * as required by the license.] + * + * The vfdecrypt license says: + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + */ + +package org.catacombae.dmg.encrypted; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.Key; +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import org.catacombae.dmgextractor.Util; +import org.catacombae.dmg.encrypted.CommonCEncryptedEncodingHeader.KeySet; +import org.catacombae.io.BasicReadableRandomAccessStream; +import org.catacombae.io.ReadableFileStream; +import org.catacombae.io.ReadableRandomAccessStream; +import org.catacombae.io.RuntimeIOException; + +/** + * Filtering stream that takes the data of a Mac OS X encrypted disk image and a password as input + * and acts as a transparent decryption layer, allowing the user to access the unencrypted + * underlying disk image data. (The encryption format isn't disk image specific, so it might be used + * by other parts of Mac OS X as well, making this filter even more useful...) + *

+ * Documentation on how encrypted disk images work was retrieved from the "Unlocking + * FileVault" slides, published by Jacob Appelbaum and Ralf-Philipp Weinmann, and the source code of + * the utility vfdecrypt in VileFault, copyright Ralf-Philipp Weinmann, Jacob Appelbaum and + * Christian Fromme. + * + * @author Erik Larsson + */ +public class ReadableCEncryptedEncodingStream extends BasicReadableRandomAccessStream { + private final ReadableRandomAccessStream backingStream; + private final CommonCEncryptedEncodingHeader header; + private final SecretKeySpec aesKey; + private final SecretKeySpec hmacSha1Key; + private final Mac hmacSha1; + private final Cipher aesCipher; + private final long streamLength; + + // Tracker variables + private long blockNumber = 0; + private int posInBlock = 0; + + public ReadableCEncryptedEncodingStream(ReadableRandomAccessStream backingStream, + char[] password) throws RuntimeIOException { + Debug.print("ReadableCEncryptedEncodingStream(" + backingStream + ", " + password +");"); + this.backingStream = backingStream; + int headerVersion = CEncryptedEncodingUtil.detectVersion(backingStream); + Debug.print(" headerVersion = " + headerVersion); + switch(headerVersion) { + case 1: + byte[] v1HeaderData = new byte[V1Header.length()]; + backingStream.seek(backingStream.length()-V1Header.length()); + backingStream.readFully(v1HeaderData); + V1Header v1header = new V1Header(v1HeaderData, 0); + Debug.print(" V1 header:"); + v1header.print(Debug.ps, " "); + header = CommonCEncryptedEncodingHeader.create(v1header); + break; + case 2: + byte[] v2HeaderData = new byte[V2Header.length()]; + backingStream.seek(0); + backingStream.readFully(v2HeaderData); + V2Header v2header = new V2Header(v2HeaderData, 0); + Debug.print(" V2 header:"); + v2header.print(Debug.ps, " "); + header = CommonCEncryptedEncodingHeader.create(v2header); + break; + case -1: + throw new RuntimeException("No CEncryptedEncoding header found!"); + default: + throw new RuntimeException("Unknown header version: " + headerVersion); + } + + this.streamLength = header.getEncryptedDataLength(); + /* + if(this.length % header.getBlockSize() != 0) { + System.err.println("WARNING: Block data area length (" + this.length + + ") is not aligned to block size (" + header.getBlockSize() + ")!"); + } + * */ + + try { + final String pbeAlgorithmName = "PBKDF2WithHmacSHA1"; //"PBEWithHmacSHA1AndDESede"; + + // Derive the proper key from our password. + PBEKeySpec ks = new PBEKeySpec(password, header.getKdfSalt(), + header.getKdfIterationCount(), 192); + SecretKeyFactory fact = SecretKeyFactory.getInstance(pbeAlgorithmName); + Key k = fact.generateSecret(ks); + + byte[] keyData = k.getEncoded(); + Debug.print("Derived key: 0x" + Util.byteArrayToHexString(keyData)); + + // Set up the cipher + final String cipherAlgorithmName = "DESede/CBC/PKCS5Padding"; + Cipher keyDecryptionCipher = Cipher.getInstance(cipherAlgorithmName); + SecretKeyFactory fact2 = SecretKeyFactory.getInstance("DESede"); + Key k2 = fact2.generateSecret(new DESedeKeySpec(keyData)); + + // Call the version specific unwrap function. + KeySet keys = header.unwrapKeys(k2, keyDecryptionCipher); + + Debug.print("AES key: 0x" + Util.byteArrayToHexString(keys.getAesKey())); + Debug.print("HmacSHA1 key: 0x" + Util.byteArrayToHexString(keys.getHmacSha1Key())); + + this.aesKey = new SecretKeySpec(keys.getAesKey(), "AES"); + this.hmacSha1Key = new SecretKeySpec(keys.getHmacSha1Key(), "HmacSHA1"); + + keys.clearData(); // No unused keys in memory please. + + this.hmacSha1 = Mac.getInstance("HmacSHA1"); + this.hmacSha1.init(hmacSha1Key); + + this.aesCipher = Cipher.getInstance("AES/CBC/NoPadding"); + } catch(Exception e) { + throw new RuntimeException("Exception while trying to decrypt keys.", e); + } + } + + /** + * Tells whether stream is encoded with CEncryptedEncoding or not. If this method + * returns true, the stream can be fed to the ReadableCEncryptedEncoding constructor. + * + * @param stream the stream to check for the signatures of a CEncryptedEncoding. + * @return whether stream is encoded with CEncryptedEncoding or not. + */ + public static boolean isCEncryptedEncoding(ReadableRandomAccessStream stream) { + int version = CEncryptedEncodingUtil.detectVersion(stream); + return version == 1 || version == 2; + } + + @Override + public void close() throws RuntimeIOException { + backingStream.close(); + } + + @Override + public void seek(long pos) throws RuntimeIOException { + if(pos < 0) + throw new IllegalArgumentException("Negative seek request: pos (" + pos + ") < 0"); + else if(pos > streamLength) { + // throw new IllegalArgumentException("Trying to seek beyond EOF: pos (" + pos + + // ") > length (" + length + ")"); + + // Let's just seek to the end of file instead of throwing stuff around us. + this.blockNumber = streamLength/header.getBlockSize(); + this.posInBlock = 0; + } + else { + long nextBlockNumber = pos / header.getBlockSize(); + int nextPosInBlock = (int) (pos % header.getBlockSize()); + + /* + if(header.getBlockDataStart() + (nextBlockNumber+1)*header.getBlockSize() > backingStream.length()) { + nextBlockNumber = (backingStream.length()-header.getBlockDataStart())/header.getBlockSize(); + nextPosInBlock = 0; + } + * */ + + this.blockNumber = nextBlockNumber; + this.posInBlock = nextPosInBlock; + } + } + + @Override + public long length() throws RuntimeIOException { + return streamLength; + } + + @Override + public long getFilePointer() throws RuntimeIOException { + return blockNumber*header.getBlockSize() + posInBlock; + } + + @Override + public int read(byte[] b, int off, int len) throws RuntimeIOException { + // + if(len == 0) + return 0; + else if(len < 0) + throw new IndexOutOfBoundsException("len (" + len + ") < 0"); + else if(off < 0) + throw new IndexOutOfBoundsException("off (" + off + ") < 0"); + else if(off+len > b.length) + throw new IndexOutOfBoundsException("off+len (" + (off+len) + + ") > b.length (" + b.length + ")"); + // + + backingStream.seek(header.getBlockDataStart() + blockNumber*header.getBlockSize()); + + byte[] encBlockData = new byte[header.getBlockSize()]; + byte[] decBlockData = new byte[encBlockData.length]; + + try { + int totalBytesRead = 0; + while(totalBytesRead < len && blockNumber*header.getBlockSize() < streamLength) { + int bytesRead = backingStream.read(encBlockData); + if(bytesRead != encBlockData.length) { + if(bytesRead > 0) + System.err.println("WARNING: Could not read entire block! " + + "blockNumber=" + blockNumber + ", bytesRead=" + bytesRead); + break; + } + + int bytesDecrypted = decrypt(encBlockData, decBlockData, blockNumber); + Assert.eq(bytesDecrypted, decBlockData.length); + + final long bytesLeftInStream = streamLength - blockNumber*header.getBlockSize(); + final int blockSize = + (int)(bytesLeftInStream < decBlockData.length ? bytesLeftInStream : decBlockData.length); + + + final int bytesLeftToRead = len-totalBytesRead; + final int bytesLeftInBlock = blockSize-posInBlock; + int bytesToCopy = bytesLeftToRead < bytesLeftInBlock ? bytesLeftToRead : bytesLeftInBlock; + + System.arraycopy(decBlockData, posInBlock, b, off + totalBytesRead, bytesToCopy); + + totalBytesRead += bytesToCopy; + + if(bytesToCopy == bytesLeftInBlock) { + ++blockNumber; + posInBlock = 0; + } + else { + posInBlock += bytesLeftToRead; + } + } + + if(totalBytesRead > 0) + return totalBytesRead; + else + return -1; + } finally { + Util.zero(encBlockData); + Util.zero(decBlockData); + } + } + + private int decrypt(byte[] encBlockData, byte[] decBlockData, long blockNumber) { + Debug.print("decrypt(byte[" + encBlockData.length + "], byte[" + + decBlockData.length + "], " + blockNumber + ");"); + if(blockNumber < 0 || blockNumber > Integer.MAX_VALUE) + throw new RuntimeException("Block number out of range: " + blockNumber); + int blockNumberInt = (int)(blockNumber & 0xFFFFFFFF); + hmacSha1.reset(); + hmacSha1.update(Util.toByteArrayBE(blockNumberInt)); + byte[] iv = new byte[16]; + + /* The 160-bit MAC value is truncated to 16 bytes (128 bits) to be + * used as the cipher's IV. */ + System.arraycopy(hmacSha1.doFinal(), 0, iv, 0, iv.length); + //Debug.print(" iv: 0x" + Util.byteArrayToHexString(iv)); + + try { + aesCipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv)); + int bytesDecrypted = + aesCipher.doFinal(encBlockData, 0, encBlockData.length, decBlockData, 0); + + return bytesDecrypted; + } catch(Exception e) { + throw new RuntimeException("Unexpected exception when trying to " + + "decrypt block " + blockNumber + ".", e); + } finally { + Util.zero(iv); + } + } + + private static void printHelp() { + System.err.println("usage: " + ReadableCEncryptedEncodingStream.class.getName() + + " -i in-file -p password -o out-file"); + System.exit(-1); + } + public static void main(String[] args) throws IOException { + /* + boolean debugMode = true; + if(debugMode && args.length == 0) { + String imageprefix = "v2"; + File inFile = new File("/Users/erik/devel/reference/vilefault/vfdecrypt/" + + imageprefix + "image.dmg"); + File outFile = new File("/Users/erik/devel/reference/vilefault/vfdecrypt/" + + imageprefix + "image_javadec.dmg"); + char[] password = SecretPassword.PASSWORD; + runTest(inFile, outFile, password); + } + * */ + + String inputFilename = null; + String outputFilename = null; + String password = null; + for(int i = 0; i < args.length; ++i) { + String curArg = args[i]; + if(curArg.startsWith("-i")) { + if(i+1 < args.length) + inputFilename = args[i+1]; + else + printHelp(); + } + else if(curArg.startsWith("-p")) { + if(i+1 < args.length) + password = args[i+1]; + else + printHelp(); + } + else if(curArg.startsWith("-o")) { + if(i+1 < args.length) + outputFilename = args[i+1]; + else + printHelp(); + } + } + if(inputFilename == null || outputFilename == null || password == null) + printHelp(); + + runTest(inputFilename, outputFilename, password); + } + + private static void runTest(String inputFilename, String outputFilename, String password) throws IOException { + ReadableRandomAccessStream backingStream = new ReadableFileStream(inputFilename); + + ReadableRandomAccessStream rras = + new ReadableCEncryptedEncodingStream(backingStream, password.toCharArray()); + + System.out.println("Length of encrypted data: " + rras.length() + " bytes"); + + byte[] lastBlock = new byte[4096]; + rras.seek(rras.length()-4096); + rras.readFully(lastBlock); + System.out.println("Last block: 0x" + Util.byteArrayToHexString(lastBlock)); + + byte[] sig = new byte[2]; + rras.seek(0); + rras.readFully(sig); + System.out.println("Signature: " + Util.toASCIIString(sig)); + System.out.println("fp=" + rras.getFilePointer()); + byte[] following = new byte[3]; + rras.readFully(following); + System.out.println("Following(" + following.length + "): 0x" + Util.byteArrayToHexString(following)); + System.out.println("fp=" + rras.getFilePointer()); + rras.readFully(following); + System.out.println("Following(" + following.length + "): 0x" + Util.byteArrayToHexString(following)); + System.out.println("fp=" + rras.getFilePointer()); + rras.readFully(following); + System.out.println("Following(" + following.length + "): 0x" + Util.byteArrayToHexString(following)); + System.out.println("fp=" + rras.getFilePointer()); + + rras.seek(33792); + rras.readFully(sig); + System.out.println("Signature: " + Util.toASCIIString(sig)); + System.out.println("fp=" + rras.getFilePointer()); + + System.out.println("Checking boundary passage:"); + byte[] boundaryBytes = new byte[9]; + rras.seek(36859); + rras.readFully(boundaryBytes); + System.out.println("boundaryBytes(" + boundaryBytes.length + "): 0x" + Util.byteArrayToHexString(boundaryBytes)); + System.out.println("fp=" + rras.getFilePointer()); + + System.out.println("Checking reading until eof:"); + { + byte[] buffer = new byte[5001]; + rras.seek(rras.length()-4096*3); + int bytesRead = rras.read(buffer); + long totBytesRead = 0; + while(bytesRead != -1) { + System.out.println("Read " + bytesRead + " bytes."); + totBytesRead += bytesRead; + bytesRead = rras.read(buffer); + } + System.out.println("Finished. bytesRead=" + bytesRead + " totBytesRead=" + totBytesRead); + } + + //System.exit(0); + + FileOutputStream out = new FileOutputStream(outputFilename); + System.out.println("Extracting encrypted data to file: " + outputFilename); + rras.seek(0); + byte[] buffer = new byte[9119]; + int bytesRead = rras.read(buffer); + long totalBytesWritten = 0; + while(bytesRead > 0) { + System.out.println("Read " + bytesRead + " bytes."); + out.write(buffer, 0, bytesRead); + totalBytesWritten += bytesRead; + bytesRead = rras.read(buffer); + } + System.out.println("Wrote " + totalBytesWritten + " bytes."); + out.close(); + } +} diff --git a/src/org/catacombae/dmg/encrypted/V1Header.java b/src/org/catacombae/dmg/encrypted/V1Header.java new file mode 100644 index 0000000..cd115e0 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/V1Header.java @@ -0,0 +1,316 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + * This code was written from studying vfdecrypt, Copyright (c) 2006 + * Ralf-Philipp Weinmann + * Jacob Appelbaum + * Christian Fromme + * + * [I'm not sure if their copyright and license terms need to be applied, + * but in case they do, the original license terms are reprinted below + * as required by the license.] + * + * The vfdecrypt license says: + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + */ + +package org.catacombae.dmg.encrypted; + +import java.io.PrintStream; +import org.catacombae.dmgextractor.Util; + +/** This class was generated by CStructToJavaClass. */ +public class V1Header { + /* + * struct V1Header + * size: 1276 bytes + * description: + * + * BP Size Type Identifier Description + * ------------------------------------------------------------------------------------------------------------------------------- + * 0 1*16 uint8_t[16] unknown0 Unknown data. + * 16 4 uint32_t blockSize Block size of the encrypted block data. + * 20 4 uint32_t unknownInt20 Unknown integer. + * 24 4 uint32_t unknownInt24 Unknown integer. + * 28 4 uint32_t unknownInt28 Unknown integer. + * 32 4 uint32_t unknownInt32 Unknown integer. + * 36 4 uint32_t unknownInt36 Unknown integer. + * 40 4 uint32_t unknownInt40 Unknown integer. + * 44 4 uint32_t unknownInt44 Unknown integer. + * 48 4 uint32_t kdfIterationCount Iteration count for the key derivation function (normally 1000). + * 52 4 uint32_t kdfSaltLen Length of kdfSalt in bytes. + * 56 1*32 uint8_t[32] kdfSalt Salt value for the key derivation function + * 88 4 uint32_t unknownInt88 Unknown integer. + * 92 4 uint32_t unknownInt92 Unknown integer. + * 96 4 uint32_t unknownInt96 Unknown integer. + * 100 4 uint32_t unknownInt100 Unknown integer. + * 104 1*32 uint8_t[32] unwrapIv Initialization Vector for encryption-key unwrapping. + * 136 4 uint32_t lenWrappedAesKey Length of wrappedAesKey in bytes (max 256). + * 140 1*256 uint8_t[256] wrappedAesKey The AES key (wrapped). + * 396 4 uint32_t unknownInt396 Unknown integer (observed value: 91/0x5B). + * 400 4 uint32_t unknownInt400 Unknown integer (observed value: 160/0xA0). + * 404 1*32 uint8_t[32] unknown404 Unknown data (observed: 8 bytes filled, rest 0). + * 436 4 uint32_t lenWrappedHmacSha1Key Length of wrappedHmacSha1Key in bytes (max 256). + * 440 1*256 uint8_t[256] wrappedHmacSha1Key The HMAC SHA-1 key (wrapped). + * 696 4 uint32_t unknownInt696 Unknown integer (observed value: 91/0x5B). + * 700 4 uint32_t unknownInt700 Unknown integer (observed value: 160/0xA0). + * 704 1*32 uint8_t[32] unknown704 Unknown data (obs. 8 bytes filled, rest 0). + * 736 4 uint32_t lenWrappedIntegrityKey Length of wrappedIntegrityKey. + * 740 1*256 uint8_t[256] wrappedIntegrityKey Integrity key. + * 996 4 uint32_t lenUnknown1000 Length of unknown1000. + * 1000 1*256 uint8_t[256] unknown1000 Unknown key-like field with length specified by unknownRnd95GaLen (max 256). + * 1256 8 uint64_t decryptedDataLength Length in bytes of the underlying data stream. + * 1264 4 uint32_t possibleHeaderVersion Could be a variable indicating header version (observed: 1/0x1). + * 1268 1*8 uint8_t[8] signature Header signature (ASCII: 'cdsaencr'). + */ + + public static final int STRUCTSIZE = 1276; + + private final byte[] unknown0 = new byte[1*16]; + private final byte[] blockSize = new byte[4]; + private final byte[] unknownInt20 = new byte[4]; + private final byte[] unknownInt24 = new byte[4]; + private final byte[] unknownInt28 = new byte[4]; + private final byte[] unknownInt32 = new byte[4]; + private final byte[] unknownInt36 = new byte[4]; + private final byte[] unknownInt40 = new byte[4]; + private final byte[] unknownInt44 = new byte[4]; + private final byte[] kdfIterationCount = new byte[4]; + private final byte[] kdfSaltLen = new byte[4]; + private final byte[] kdfSalt = new byte[1*32]; + private final byte[] unknownInt88 = new byte[4]; + private final byte[] unknownInt92 = new byte[4]; + private final byte[] unknownInt96 = new byte[4]; + private final byte[] unknownInt100 = new byte[4]; + private final byte[] unwrapIv = new byte[1*32]; + private final byte[] lenWrappedAesKey = new byte[4]; + private final byte[] wrappedAesKey = new byte[1*256]; + private final byte[] unknownInt396 = new byte[4]; + private final byte[] unknownInt400 = new byte[4]; + private final byte[] unknown404 = new byte[1*32]; + private final byte[] lenWrappedHmacSha1Key = new byte[4]; + private final byte[] wrappedHmacSha1Key = new byte[1*256]; + private final byte[] unknownInt696 = new byte[4]; + private final byte[] unknownInt700 = new byte[4]; + private final byte[] unknown704 = new byte[1*32]; + private final byte[] lenWrappedIntegrityKey = new byte[4]; + private final byte[] wrappedIntegrityKey = new byte[1*256]; + private final byte[] lenUnknown1000 = new byte[4]; + private final byte[] unknown1000 = new byte[1*256]; + private final byte[] decryptedDataLength = new byte[8]; + private final byte[] possibleHeaderVersion = new byte[4]; + private final byte[] signature = new byte[1*8]; + + public V1Header(byte[] data, int offset) { + System.arraycopy(data, offset+0, unknown0, 0, 1*16); + System.arraycopy(data, offset+16, blockSize, 0, 4); + System.arraycopy(data, offset+20, unknownInt20, 0, 4); + System.arraycopy(data, offset+24, unknownInt24, 0, 4); + System.arraycopy(data, offset+28, unknownInt28, 0, 4); + System.arraycopy(data, offset+32, unknownInt32, 0, 4); + System.arraycopy(data, offset+36, unknownInt36, 0, 4); + System.arraycopy(data, offset+40, unknownInt40, 0, 4); + System.arraycopy(data, offset+44, unknownInt44, 0, 4); + System.arraycopy(data, offset+48, kdfIterationCount, 0, 4); + System.arraycopy(data, offset+52, kdfSaltLen, 0, 4); + System.arraycopy(data, offset+56, kdfSalt, 0, 1*32); + System.arraycopy(data, offset+88, unknownInt88, 0, 4); + System.arraycopy(data, offset+92, unknownInt92, 0, 4); + System.arraycopy(data, offset+96, unknownInt96, 0, 4); + System.arraycopy(data, offset+100, unknownInt100, 0, 4); + System.arraycopy(data, offset+104, unwrapIv, 0, 1*32); + System.arraycopy(data, offset+136, lenWrappedAesKey, 0, 4); + System.arraycopy(data, offset+140, wrappedAesKey, 0, 1*256); + System.arraycopy(data, offset+396, unknownInt396, 0, 4); + System.arraycopy(data, offset+400, unknownInt400, 0, 4); + System.arraycopy(data, offset+404, unknown404, 0, 1*32); + System.arraycopy(data, offset+436, lenWrappedHmacSha1Key, 0, 4); + System.arraycopy(data, offset+440, wrappedHmacSha1Key, 0, 1*256); + System.arraycopy(data, offset+696, unknownInt696, 0, 4); + System.arraycopy(data, offset+700, unknownInt700, 0, 4); + System.arraycopy(data, offset+704, unknown704, 0, 1*32); + System.arraycopy(data, offset+736, lenWrappedIntegrityKey, 0, 4); + System.arraycopy(data, offset+740, wrappedIntegrityKey, 0, 1*256); + System.arraycopy(data, offset+996, lenUnknown1000, 0, 4); + System.arraycopy(data, offset+1000, unknown1000, 0, 1*256); + System.arraycopy(data, offset+1256, decryptedDataLength, 0, 8); + System.arraycopy(data, offset+1264, possibleHeaderVersion, 0, 4); + System.arraycopy(data, offset+1268, signature, 0, 1*8); + } + + public static int length() { return STRUCTSIZE; } + + /** Unknown data. */ + public byte[] getUnknown0() { return Util.readByteArrayBE(unknown0); } + /** Block size of the encrypted block data. */ + public int getBlockSize() { return Util.readIntBE(blockSize); } + /** Unknown integer. */ + public int getUnknownInt20() { return Util.readIntBE(unknownInt20); } + /** Unknown integer. */ + public int getUnknownInt24() { return Util.readIntBE(unknownInt24); } + /** Unknown integer. */ + public int getUnknownInt28() { return Util.readIntBE(unknownInt28); } + /** Unknown integer. */ + public int getUnknownInt32() { return Util.readIntBE(unknownInt32); } + /** Unknown integer. */ + public int getUnknownInt36() { return Util.readIntBE(unknownInt36); } + /** Unknown integer. */ + public int getUnknownInt40() { return Util.readIntBE(unknownInt40); } + /** Unknown integer. */ + public int getUnknownInt44() { return Util.readIntBE(unknownInt44); } + /** Iteration count for the key derivation function (normally 1000). */ + public int getKdfIterationCount() { return Util.readIntBE(kdfIterationCount); } + /** Length of kdfSalt in bytes. */ + public int getKdfSaltLen() { return Util.readIntBE(kdfSaltLen); } + /** Salt value for the key derivation function */ + public byte[] getKdfSalt() { return Util.readByteArrayBE(kdfSalt); } + /** Unknown integer. */ + public int getUnknownInt88() { return Util.readIntBE(unknownInt88); } + /** Unknown integer. */ + public int getUnknownInt92() { return Util.readIntBE(unknownInt92); } + /** Unknown integer. */ + public int getUnknownInt96() { return Util.readIntBE(unknownInt96); } + /** Unknown integer. */ + public int getUnknownInt100() { return Util.readIntBE(unknownInt100); } + /** Initialization Vector for encryption-key unwrapping. */ + public byte[] getUnwrapIv() { return Util.readByteArrayBE(unwrapIv); } + /** Length of wrappedAesKey in bytes (max 256). */ + public int getLenWrappedAesKey() { return Util.readIntBE(lenWrappedAesKey); } + /** The AES key (wrapped). */ + public byte[] getWrappedAesKey() { return Util.readByteArrayBE(wrappedAesKey); } + /** Unknown integer (observed value: 91/0x5B). */ + public int getUnknownInt396() { return Util.readIntBE(unknownInt396); } + /** Unknown integer (observed value: 160/0xA0). */ + public int getUnknownInt400() { return Util.readIntBE(unknownInt400); } + /** Unknown data (observed: 8 bytes filled, rest 0). */ + public byte[] getUnknown404() { return Util.readByteArrayBE(unknown404); } + /** Length of wrappedHmacSha1Key in bytes (max 256). */ + public int getLenWrappedHmacSha1Key() { return Util.readIntBE(lenWrappedHmacSha1Key); } + /** The HMAC SHA-1 key (wrapped). */ + public byte[] getWrappedHmacSha1Key() { return Util.readByteArrayBE(wrappedHmacSha1Key); } + /** Unknown integer (observed value: 91/0x5B). */ + public int getUnknownInt696() { return Util.readIntBE(unknownInt696); } + /** Unknown integer (observed value: 160/0xA0). */ + public int getUnknownInt700() { return Util.readIntBE(unknownInt700); } + /** Unknown data (obs. 8 bytes filled, rest 0). */ + public byte[] getUnknown704() { return Util.readByteArrayBE(unknown704); } + /** Length of wrappedIntegrityKey. */ + public int getLenWrappedIntegrityKey() { return Util.readIntBE(lenWrappedIntegrityKey); } + /** Integrity key. */ + public byte[] getWrappedIntegrityKey() { return Util.readByteArrayBE(wrappedIntegrityKey); } + /** Length of unknown1000. */ + public int getLenUnknown1000() { return Util.readIntBE(lenUnknown1000); } + /** Unknown key-like field with length specified by unknownRnd95GaLen (max 256). */ + public byte[] getUnknown1000() { return Util.readByteArrayBE(unknown1000); } + /** Length in bytes of the underlying data stream. */ + public long getDecryptedDataLength() { return Util.readLongBE(decryptedDataLength); } + /** Could be a variable indicating header version (observed: 1/0x1). */ + public int getPossibleHeaderVersion() { return Util.readIntBE(possibleHeaderVersion); } + /** Header signature (ASCII: 'cdsaencr'). */ + public byte[] getSignature() { return Util.readByteArrayBE(signature); } + + public void printFields(PrintStream ps, String prefix) { + ps.println(prefix + " unknown0: 0x" + Util.byteArrayToHexString(getUnknown0())); + ps.println(prefix + " blockSize: " + Util.unsign(getBlockSize())); + ps.println(prefix + " unknownInt20: " + getUnknownInt20()); + ps.println(prefix + " unknownInt24: " + getUnknownInt24()); + ps.println(prefix + " unknownInt28: " + getUnknownInt28()); + ps.println(prefix + " unknownInt32: " + getUnknownInt32()); + ps.println(prefix + " unknownInt36: " + getUnknownInt36()); + ps.println(prefix + " unknownInt40: " + getUnknownInt40()); + ps.println(prefix + " unknownInt44: " + getUnknownInt44()); + ps.println(prefix + " kdfIterationCount: " + Util.unsign(getKdfIterationCount())); + ps.println(prefix + " kdfSaltLen: " + Util.unsign(getKdfSaltLen())); + ps.println(prefix + " kdfSalt: 0x" + Util.byteArrayToHexString(getKdfSalt())); + ps.println(prefix + " unknownInt88: " + getUnknownInt88()); + ps.println(prefix + " unknownInt92: " + getUnknownInt92()); + ps.println(prefix + " unknownInt96: " + getUnknownInt96()); + ps.println(prefix + " unknownInt100: " + getUnknownInt100()); + ps.println(prefix + " unwrapIv: 0x" + Util.byteArrayToHexString(getUnwrapIv())); + ps.println(prefix + " lenWrappedAesKey: " + Util.unsign(getLenWrappedAesKey())); + ps.println(prefix + " wrappedAesKey: 0x" + Util.byteArrayToHexString(getWrappedAesKey())); + ps.println(prefix + " unknownInt396: " + getUnknownInt396()); + ps.println(prefix + " unknownInt400: " + getUnknownInt400()); + ps.println(prefix + " unknown404: 0x" + Util.byteArrayToHexString(getUnknown404())); + ps.println(prefix + " lenWrappedHmacSha1Key: " + Util.unsign(getLenWrappedHmacSha1Key())); + ps.println(prefix + " wrappedHmacSha1Key: 0x" + Util.byteArrayToHexString(getWrappedHmacSha1Key())); + ps.println(prefix + " unknownInt696: " + getUnknownInt696()); + ps.println(prefix + " unknownInt700: " + getUnknownInt700()); + ps.println(prefix + " unknown704: 0x" + Util.byteArrayToHexString(getUnknown704())); + ps.println(prefix + " lenWrappedIntegrityKey: " + Util.unsign(getLenWrappedIntegrityKey())); + ps.println(prefix + " wrappedIntegrityKey: 0x" + Util.byteArrayToHexString(getWrappedIntegrityKey())); + ps.println(prefix + " lenUnknown1000: " + getLenUnknown1000()); + ps.println(prefix + " unknown1000: 0x" + Util.byteArrayToHexString(getUnknown1000())); + ps.println(prefix + " decryptedDataLength: " + getDecryptedDataLength()); + ps.println(prefix + " possibleHeaderVersion: " + getPossibleHeaderVersion()); + ps.println(prefix + " signature: \"" + Util.toASCIIString(getSignature()) + "\""); + } + + public void print(PrintStream ps, String prefix) { + ps.println(prefix + "V1Header:"); + printFields(ps, prefix); + } + + public byte[] getBytes() { + byte[] result = new byte[length()]; + int offset = 0; + System.arraycopy(this.unknown0, 0, result, offset, this.unknown0.length); offset += this.unknown0.length; + System.arraycopy(this.blockSize, 0, result, offset, this.blockSize.length); offset += this.blockSize.length; + System.arraycopy(this.unknownInt20, 0, result, offset, this.unknownInt20.length); offset += this.unknownInt20.length; + System.arraycopy(this.unknownInt24, 0, result, offset, this.unknownInt24.length); offset += this.unknownInt24.length; + System.arraycopy(this.unknownInt28, 0, result, offset, this.unknownInt28.length); offset += this.unknownInt28.length; + System.arraycopy(this.unknownInt32, 0, result, offset, this.unknownInt32.length); offset += this.unknownInt32.length; + System.arraycopy(this.unknownInt36, 0, result, offset, this.unknownInt36.length); offset += this.unknownInt36.length; + System.arraycopy(this.unknownInt40, 0, result, offset, this.unknownInt40.length); offset += this.unknownInt40.length; + System.arraycopy(this.unknownInt44, 0, result, offset, this.unknownInt44.length); offset += this.unknownInt44.length; + System.arraycopy(this.kdfIterationCount, 0, result, offset, this.kdfIterationCount.length); offset += this.kdfIterationCount.length; + System.arraycopy(this.kdfSaltLen, 0, result, offset, this.kdfSaltLen.length); offset += this.kdfSaltLen.length; + System.arraycopy(this.kdfSalt, 0, result, offset, this.kdfSalt.length); offset += this.kdfSalt.length; + System.arraycopy(this.unknownInt88, 0, result, offset, this.unknownInt88.length); offset += this.unknownInt88.length; + System.arraycopy(this.unknownInt92, 0, result, offset, this.unknownInt92.length); offset += this.unknownInt92.length; + System.arraycopy(this.unknownInt96, 0, result, offset, this.unknownInt96.length); offset += this.unknownInt96.length; + System.arraycopy(this.unknownInt100, 0, result, offset, this.unknownInt100.length); offset += this.unknownInt100.length; + System.arraycopy(this.unwrapIv, 0, result, offset, this.unwrapIv.length); offset += this.unwrapIv.length; + System.arraycopy(this.lenWrappedAesKey, 0, result, offset, this.lenWrappedAesKey.length); offset += this.lenWrappedAesKey.length; + System.arraycopy(this.wrappedAesKey, 0, result, offset, this.wrappedAesKey.length); offset += this.wrappedAesKey.length; + System.arraycopy(this.unknownInt396, 0, result, offset, this.unknownInt396.length); offset += this.unknownInt396.length; + System.arraycopy(this.unknownInt400, 0, result, offset, this.unknownInt400.length); offset += this.unknownInt400.length; + System.arraycopy(this.unknown404, 0, result, offset, this.unknown404.length); offset += this.unknown404.length; + System.arraycopy(this.lenWrappedHmacSha1Key, 0, result, offset, this.lenWrappedHmacSha1Key.length); offset += this.lenWrappedHmacSha1Key.length; + System.arraycopy(this.wrappedHmacSha1Key, 0, result, offset, this.wrappedHmacSha1Key.length); offset += this.wrappedHmacSha1Key.length; + System.arraycopy(this.unknownInt696, 0, result, offset, this.unknownInt696.length); offset += this.unknownInt696.length; + System.arraycopy(this.unknownInt700, 0, result, offset, this.unknownInt700.length); offset += this.unknownInt700.length; + System.arraycopy(this.unknown704, 0, result, offset, this.unknown704.length); offset += this.unknown704.length; + System.arraycopy(this.lenWrappedIntegrityKey, 0, result, offset, this.lenWrappedIntegrityKey.length); offset += this.lenWrappedIntegrityKey.length; + System.arraycopy(this.wrappedIntegrityKey, 0, result, offset, this.wrappedIntegrityKey.length); offset += this.wrappedIntegrityKey.length; + System.arraycopy(this.lenUnknown1000, 0, result, offset, this.lenUnknown1000.length); offset += this.lenUnknown1000.length; + System.arraycopy(this.unknown1000, 0, result, offset, this.unknown1000.length); offset += this.unknown1000.length; + System.arraycopy(this.decryptedDataLength, 0, result, offset, this.decryptedDataLength.length); offset += this.decryptedDataLength.length; + System.arraycopy(this.possibleHeaderVersion, 0, result, offset, this.possibleHeaderVersion.length); offset += this.possibleHeaderVersion.length; + System.arraycopy(this.signature, 0, result, offset, this.signature.length); offset += this.signature.length; + return result; + } +} diff --git a/src/org/catacombae/dmg/encrypted/V1Header.struct b/src/org/catacombae/dmg/encrypted/V1Header.struct new file mode 100644 index 0000000..d1ad68e --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/V1Header.struct @@ -0,0 +1,15 @@ +struct V1Header { + uint8_t unknown0[48]; Unknown data. + uint32_t kdfIterationCount; Iteration count (normally 1000). + uint32_t kdfSaltLen; Length of kdfSalt (in bytes). + uint8_t kdfSalt[48]; Salt value for key derivation. + uint8_t unwrapIv[32]; Initialization Vector for encryption-key unwrapping. + uint32_t lenWrappedAesKey; Length of wrappedAesKey (max 296). + uint8_t wrappedAesKey[296]; The AES key (wrapped). + uint32_t lenWrappedHmacSha1Key; Length of wrappedHmacSha1Key (max 300). + uint8_t wrappedHmacSha1Key[300]; The HMAC SHA-1 key (wrapped). + uint32_t lenWrappedIntegrityKey; Length of wrappedIntegrityKey. + uint8_t wrappedIntegrityKey[48]; Integrity key. + uint8_t unknown792[476]; Unknown data. + uint8_t signature[8]; Header signature (ASCII: 'cdsaencr'). +}; \ No newline at end of file diff --git a/src/org/catacombae/dmg/encrypted/V2Header.java b/src/org/catacombae/dmg/encrypted/V2Header.java new file mode 100644 index 0000000..ef50829 --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/V2Header.java @@ -0,0 +1,282 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + * This code was written from studying vfdecrypt, Copyright (c) 2006 + * Ralf-Philipp Weinmann + * Jacob Appelbaum + * Christian Fromme + * + * [I'm not sure if their copyright and license terms need to be applied, + * but in case they do, the original license terms are reprinted below + * as required by the license.] + * + * The vfdecrypt license says: + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + */ + +package org.catacombae.dmg.encrypted; + +import java.io.PrintStream; +import org.catacombae.dmgextractor.Util; + +/** This class was generated by CStructToJavaClass. */ +public class V2Header { + /* + * struct V2Header + * size: 248 bytes + * description: + * + * BP Size Type Identifier Description + * ------------------------------------------------------------------------------------------------------------ + * 0 1*8 uint8_t[8] signature Header signature (ASCII: 'encrcdsa'). + * 8 4 uint32_t possibleHeaderVersion Possibly the version of the encrypted volume format. + * 12 4 uint32_t laban Unknown variable with observed value 16/0x10. + * 16 4 uint32_t edward Unknown variable with observed value 5/0x5. + * 20 4 uint32_t palle Unknown variable with observed value 0x80000001. + * 24 4 uint32_t lisa Unknown variable with observed value 128/0x80. + * 28 4 uint32_t unknownInt28 Unknown variable with observed value 91/0x5B. + * 32 4 uint32_t unknownInt32 Unknown variable with observed value 160/0xA0. + * 36 1*16 uint8_t[16] unknown1 Unknown binary data. + * 52 4 uint32_t blockSize Block size of the encrypted block data. + * 56 8 uint64_t encryptedDataLength Length in bytes of the data that has been encrypted. + * 64 8 uint64_t offsetToDataStart Offset to the start of the encrypted block data. + * 72 4 uint32_t unknownInt72 Unknown variable with observed value 1/0x1. + * 76 4 uint32_t unknownInt76 Unknown variable with observed value 1/0x1. + * 80 8 uint64_t possiblePointerToKdfAlgorithm Could be a pointer to where kdf_algorithm is located. + * 88 8 uint64_t unknownLong88 Unknown variable with observed value 616/0x268. + * 96 4 uint32_t kdfAlgorithm Algorithm of the key derivation function. + * 100 4 uint32_t kdfPrngAlgorithm ? + * 104 4 uint32_t kdfIterationCount Iteration count (normally 1000). + * 108 4 uint32_t kdfSaltLen Length of kdfSalt (in bytes). + * 112 1*32 uint8_t[32] kdfSalt Salt value for key derivation. + * 144 4 uint32_t blobEncIvSize Size of blobEncIv. + * 148 1*32 uint8_t[32] blobEncIv Initialization Vector for encryption-key unwrapping. + * 180 4 uint32_t blobEncKeyBits Number of bits in the keyblob's encryption key. + * 184 4 uint32_t blobEncAlgorithm Encryption algorithm used to encrypt the key blob. + * 188 4 uint32_t blobEncPadding Padding. (?) + * 192 4 uint32_t blobEncMode Encryption mode for the algorithm. + * 196 4 uint32_t encryptedKeyblobSize Size of encryptedKeyBlob. + * 200 1*48 uint8_t[48] encryptedKeyblob The encrypted key blob, containing all keys. + */ + + public static final int STRUCTSIZE = 248; + + private final byte[] signature = new byte[1*8]; + private final byte[] possibleHeaderVersion = new byte[4]; + private final byte[] laban = new byte[4]; + private final byte[] edward = new byte[4]; + private final byte[] palle = new byte[4]; + private final byte[] lisa = new byte[4]; + private final byte[] unknownInt28 = new byte[4]; + private final byte[] unknownInt32 = new byte[4]; + private final byte[] unknown1 = new byte[1*16]; + private final byte[] blockSize = new byte[4]; + private final byte[] encryptedDataLength = new byte[8]; + private final byte[] offsetToDataStart = new byte[8]; + private final byte[] unknownInt72 = new byte[4]; + private final byte[] unknownInt76 = new byte[4]; + private final byte[] possiblePointerToKdfAlgorithm = new byte[8]; + private final byte[] unknownLong88 = new byte[8]; + private final byte[] kdfAlgorithm = new byte[4]; + private final byte[] kdfPrngAlgorithm = new byte[4]; + private final byte[] kdfIterationCount = new byte[4]; + private final byte[] kdfSaltLen = new byte[4]; + private final byte[] kdfSalt = new byte[1*32]; + private final byte[] blobEncIvSize = new byte[4]; + private final byte[] blobEncIv = new byte[1*32]; + private final byte[] blobEncKeyBits = new byte[4]; + private final byte[] blobEncAlgorithm = new byte[4]; + private final byte[] blobEncPadding = new byte[4]; + private final byte[] blobEncMode = new byte[4]; + private final byte[] encryptedKeyblobSize = new byte[4]; + private final byte[] encryptedKeyblob = new byte[1*48]; + + public V2Header(byte[] data, int offset) { + System.arraycopy(data, offset+0, signature, 0, 1*8); + System.arraycopy(data, offset+8, possibleHeaderVersion, 0, 4); + System.arraycopy(data, offset+12, laban, 0, 4); + System.arraycopy(data, offset+16, edward, 0, 4); + System.arraycopy(data, offset+20, palle, 0, 4); + System.arraycopy(data, offset+24, lisa, 0, 4); + System.arraycopy(data, offset+28, unknownInt28, 0, 4); + System.arraycopy(data, offset+32, unknownInt32, 0, 4); + System.arraycopy(data, offset+36, unknown1, 0, 1*16); + System.arraycopy(data, offset+52, blockSize, 0, 4); + System.arraycopy(data, offset+56, encryptedDataLength, 0, 8); + System.arraycopy(data, offset+64, offsetToDataStart, 0, 8); + System.arraycopy(data, offset+72, unknownInt72, 0, 4); + System.arraycopy(data, offset+76, unknownInt76, 0, 4); + System.arraycopy(data, offset+80, possiblePointerToKdfAlgorithm, 0, 8); + System.arraycopy(data, offset+88, unknownLong88, 0, 8); + System.arraycopy(data, offset+96, kdfAlgorithm, 0, 4); + System.arraycopy(data, offset+100, kdfPrngAlgorithm, 0, 4); + System.arraycopy(data, offset+104, kdfIterationCount, 0, 4); + System.arraycopy(data, offset+108, kdfSaltLen, 0, 4); + System.arraycopy(data, offset+112, kdfSalt, 0, 1*32); + System.arraycopy(data, offset+144, blobEncIvSize, 0, 4); + System.arraycopy(data, offset+148, blobEncIv, 0, 1*32); + System.arraycopy(data, offset+180, blobEncKeyBits, 0, 4); + System.arraycopy(data, offset+184, blobEncAlgorithm, 0, 4); + System.arraycopy(data, offset+188, blobEncPadding, 0, 4); + System.arraycopy(data, offset+192, blobEncMode, 0, 4); + System.arraycopy(data, offset+196, encryptedKeyblobSize, 0, 4); + System.arraycopy(data, offset+200, encryptedKeyblob, 0, 1*48); + } + + public static int length() { return STRUCTSIZE; } + + /** Header signature (ASCII: 'encrcdsa'). */ + public byte[] getSignature() { return Util.readByteArrayBE(signature); } + /** Possibly the version of the encrypted volume format. */ + public int getPossibleHeaderVersion() { return Util.readIntBE(possibleHeaderVersion); } + /** Unknown variable with observed value 16/0x10. */ + public int getLaban() { return Util.readIntBE(laban); } + /** Unknown variable with observed value 5/0x5. */ + public int getEdward() { return Util.readIntBE(edward); } + /** Unknown variable with observed value 0x80000001. */ + public int getPalle() { return Util.readIntBE(palle); } + /** Unknown variable with observed value 128/0x80. */ + public int getLisa() { return Util.readIntBE(lisa); } + /** Unknown variable with observed value 91/0x5B. */ + public int getUnknownInt28() { return Util.readIntBE(unknownInt28); } + /** Unknown variable with observed value 160/0xA0. */ + public int getUnknownInt32() { return Util.readIntBE(unknownInt32); } + /** Unknown binary data. */ + public byte[] getUnknown1() { return Util.readByteArrayBE(unknown1); } + /** Block size of the encrypted block data. */ + public int getBlockSize() { return Util.readIntBE(blockSize); } + /** Length in bytes of the data that has been encrypted. */ + public long getEncryptedDataLength() { return Util.readLongBE(encryptedDataLength); } + /** Offset to the start of the encrypted block data. */ + public long getOffsetToDataStart() { return Util.readLongBE(offsetToDataStart); } + /** Unknown variable with observed value 1/0x1. */ + public int getUnknownInt72() { return Util.readIntBE(unknownInt72); } + /** Unknown variable with observed value 1/0x1. */ + public int getUnknownInt76() { return Util.readIntBE(unknownInt76); } + /** Could be a pointer to where kdf_algorithm is located. */ + public long getPossiblePointerToKdfAlgorithm() { return Util.readLongBE(possiblePointerToKdfAlgorithm); } + /** Unknown variable with observed value 616/0x268. */ + public long getUnknownLong88() { return Util.readLongBE(unknownLong88); } + /** Algorithm of the key derivation function. */ + public int getKdfAlgorithm() { return Util.readIntBE(kdfAlgorithm); } + /** ? */ + public int getKdfPrngAlgorithm() { return Util.readIntBE(kdfPrngAlgorithm); } + /** Iteration count (normally 1000). */ + public int getKdfIterationCount() { return Util.readIntBE(kdfIterationCount); } + /** Length of kdfSalt (in bytes). */ + public int getKdfSaltLen() { return Util.readIntBE(kdfSaltLen); } + /** Salt value for key derivation. */ + public byte[] getKdfSalt() { return Util.readByteArrayBE(kdfSalt); } + /** Size of blobEncIv. */ + public int getBlobEncIvSize() { return Util.readIntBE(blobEncIvSize); } + /** Initialization Vector for encryption-key unwrapping. */ + public byte[] getBlobEncIv() { return Util.readByteArrayBE(blobEncIv); } + /** Number of bits in the keyblob's encryption key. */ + public int getBlobEncKeyBits() { return Util.readIntBE(blobEncKeyBits); } + /** Encryption algorithm used to encrypt the key blob. */ + public int getBlobEncAlgorithm() { return Util.readIntBE(blobEncAlgorithm); } + /** Padding. (?) */ + public int getBlobEncPadding() { return Util.readIntBE(blobEncPadding); } + /** Encryption mode for the algorithm. */ + public int getBlobEncMode() { return Util.readIntBE(blobEncMode); } + /** Size of encryptedKeyBlob. */ + public int getEncryptedKeyblobSize() { return Util.readIntBE(encryptedKeyblobSize); } + /** The encrypted key blob, containing all keys. */ + public byte[] getEncryptedKeyblob() { return Util.readByteArrayBE(encryptedKeyblob); } + + public void printFields(PrintStream ps, String prefix) { + ps.println(prefix + " signature: \"" + Util.toASCIIString(getSignature()) + "\""); + ps.println(prefix + " possibleHeaderVersion: " + getPossibleHeaderVersion()); + ps.println(prefix + " laban: " + getLaban()); + ps.println(prefix + " edward: " + getEdward()); + ps.println(prefix + " palle: " + getPalle()); + ps.println(prefix + " lisa: " + getLisa()); + ps.println(prefix + " unknownInt28: " + getUnknownInt28()); + ps.println(prefix + " unknownInt32: " + getUnknownInt32()); + ps.println(prefix + " unknown1: " + getUnknown1()); + ps.println(prefix + " blockSize: " + getBlockSize()); + ps.println(prefix + " encryptedDataLength: " + getEncryptedDataLength()); + ps.println(prefix + " offsetToDataStart: " + getOffsetToDataStart()); + ps.println(prefix + " unknownInt72: " + getUnknownInt72()); + ps.println(prefix + " unknownInt76: " + getUnknownInt76()); + ps.println(prefix + " possiblePointerToKdfAlgorithm: " + getPossiblePointerToKdfAlgorithm()); + ps.println(prefix + " unknownLong88: " + getUnknownLong88()); + ps.println(prefix + " kdfAlgorithm: " + getKdfAlgorithm()); + ps.println(prefix + " kdfPrngAlgorithm: " + getKdfPrngAlgorithm()); + ps.println(prefix + " kdfIterationCount: " + getKdfIterationCount()); + ps.println(prefix + " kdfSaltLen: " + getKdfSaltLen()); + ps.println(prefix + " kdfSalt: 0x" + Util.byteArrayToHexString(getKdfSalt())); + ps.println(prefix + " blobEncIvSize: " + getBlobEncIvSize()); + ps.println(prefix + " blobEncIv: 0x" + Util.byteArrayToHexString(getBlobEncIv())); + ps.println(prefix + " blobEncKeyBits: " + getBlobEncKeyBits()); + ps.println(prefix + " blobEncAlgorithm: " + getBlobEncAlgorithm()); + ps.println(prefix + " blobEncPadding: " + getBlobEncPadding()); + ps.println(prefix + " blobEncMode: " + getBlobEncMode()); + ps.println(prefix + " encryptedKeyblobSize: " + getEncryptedKeyblobSize()); + ps.println(prefix + " encryptedKeyblob: 0x" + Util.byteArrayToHexString(getEncryptedKeyblob())); + } + + public void print(PrintStream ps, String prefix) { + ps.println(prefix + "V2Header:"); + printFields(ps, prefix); + } + + public byte[] getBytes() { + byte[] result = new byte[length()]; + int offset = 0; + System.arraycopy(this.signature, 0, result, offset, this.signature.length); offset += this.signature.length; + System.arraycopy(this.possibleHeaderVersion, 0, result, offset, this.possibleHeaderVersion.length); offset += this.possibleHeaderVersion.length; + System.arraycopy(this.laban, 0, result, offset, this.laban.length); offset += this.laban.length; + System.arraycopy(this.edward, 0, result, offset, this.edward.length); offset += this.edward.length; + System.arraycopy(this.palle, 0, result, offset, this.palle.length); offset += this.palle.length; + System.arraycopy(this.lisa, 0, result, offset, this.lisa.length); offset += this.lisa.length; + System.arraycopy(this.unknownInt28, 0, result, offset, this.unknownInt28.length); offset += this.unknownInt28.length; + System.arraycopy(this.unknownInt32, 0, result, offset, this.unknownInt32.length); offset += this.unknownInt32.length; + System.arraycopy(this.unknown1, 0, result, offset, this.unknown1.length); offset += this.unknown1.length; + System.arraycopy(this.blockSize, 0, result, offset, this.blockSize.length); offset += this.blockSize.length; + System.arraycopy(this.encryptedDataLength, 0, result, offset, this.encryptedDataLength.length); offset += this.encryptedDataLength.length; + System.arraycopy(this.offsetToDataStart, 0, result, offset, this.offsetToDataStart.length); offset += this.offsetToDataStart.length; + System.arraycopy(this.unknownInt72, 0, result, offset, this.unknownInt72.length); offset += this.unknownInt72.length; + System.arraycopy(this.unknownInt76, 0, result, offset, this.unknownInt76.length); offset += this.unknownInt76.length; + System.arraycopy(this.possiblePointerToKdfAlgorithm, 0, result, offset, this.possiblePointerToKdfAlgorithm.length); offset += this.possiblePointerToKdfAlgorithm.length; + System.arraycopy(this.unknownLong88, 0, result, offset, this.unknownLong88.length); offset += this.unknownLong88.length; + System.arraycopy(this.kdfAlgorithm, 0, result, offset, this.kdfAlgorithm.length); offset += this.kdfAlgorithm.length; + System.arraycopy(this.kdfPrngAlgorithm, 0, result, offset, this.kdfPrngAlgorithm.length); offset += this.kdfPrngAlgorithm.length; + System.arraycopy(this.kdfIterationCount, 0, result, offset, this.kdfIterationCount.length); offset += this.kdfIterationCount.length; + System.arraycopy(this.kdfSaltLen, 0, result, offset, this.kdfSaltLen.length); offset += this.kdfSaltLen.length; + System.arraycopy(this.kdfSalt, 0, result, offset, this.kdfSalt.length); offset += this.kdfSalt.length; + System.arraycopy(this.blobEncIvSize, 0, result, offset, this.blobEncIvSize.length); offset += this.blobEncIvSize.length; + System.arraycopy(this.blobEncIv, 0, result, offset, this.blobEncIv.length); offset += this.blobEncIv.length; + System.arraycopy(this.blobEncKeyBits, 0, result, offset, this.blobEncKeyBits.length); offset += this.blobEncKeyBits.length; + System.arraycopy(this.blobEncAlgorithm, 0, result, offset, this.blobEncAlgorithm.length); offset += this.blobEncAlgorithm.length; + System.arraycopy(this.blobEncPadding, 0, result, offset, this.blobEncPadding.length); offset += this.blobEncPadding.length; + System.arraycopy(this.blobEncMode, 0, result, offset, this.blobEncMode.length); offset += this.blobEncMode.length; + System.arraycopy(this.encryptedKeyblobSize, 0, result, offset, this.encryptedKeyblobSize.length); offset += this.encryptedKeyblobSize.length; + System.arraycopy(this.encryptedKeyblob, 0, result, offset, this.encryptedKeyblob.length); offset += this.encryptedKeyblob.length; + return result; + } + +} diff --git a/src/org/catacombae/dmg/encrypted/V2Header.struct b/src/org/catacombae/dmg/encrypted/V2Header.struct new file mode 100644 index 0000000..317c03a --- /dev/null +++ b/src/org/catacombae/dmg/encrypted/V2Header.struct @@ -0,0 +1,23 @@ +struct V2Header { + uint8_t signature[8]; Header signature (ASCII: 'encrcdsa'). + uint8_t unknown1[44]; Unknown data. + uint32_t blockSize; Block size of the encrypted block data. + uint8_t unknown2[12]; Unknown data. + uint32_t offsetToDataStart; Offset to the start of the encrypted block data. + uint8_t unknown3[12]; Unknown data. + uint32_t possiblePointerToKdfAlgorithm; Could be a pointer to where kdf_algorithm is located. + uint8_t unknown4[8]; Unknown data. + uint32_t kdfAlgorithm; Algorithm of the key derivation function. + uint32_t kdfPrngAlgorithm; ? + uint32_t kdfIterationCount; Iteration count (normally 1000). + uint32_t kdfSaltLen; Length of kdfSalt (in bytes). + uint8_t kdfSalt[32]; Salt value for key derivation. + uint32_t blobEncIvSize; Size of blobEncIv. + uint8_t blobEncIv[32]; Initialization Vector for encryption-key unwrapping. + uint32_t blobEncKeyBits; Number of bits in the keyblob's encryption key. + uint32_t blobEncAlgorithm; Encryption algorithm used to encrypt the key blob. + uint32_t blobEncPadding; Padding. (?) + uint32_t blobEncMode; Encryption mode for the algorithm. + uint32_t encryptedKeyblobSize; Size of encryptedKeyBlob. + uint8_t encryptedKeyblob[48]; The encrypted key blob, containing all keys. +}; \ No newline at end of file diff --git a/src/org/catacombae/dmg/sparsebundle/Band.java b/src/org/catacombae/dmg/sparsebundle/Band.java new file mode 100644 index 0000000..e4850d6 --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/Band.java @@ -0,0 +1,78 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileLock; +import java.util.Arrays; + +/** + * + * @author erik + */ +class Band extends BundleMember { + private final long bandActualSize; + private final long bandVirtualSize; + + public Band(RandomAccessFile tokenFile, FileLock tokenFileLock, + long bandSize) throws IOException { + super(tokenFile, tokenFileLock); + + this.bandVirtualSize = bandSize; + try { + this.bandActualSize = tokenFile.length(); + } catch(IOException ex) { + super.close(); + throw ex; + } + } + + public int read(long offset, byte[] dest, int destOffset, int destLength) + throws IOException { + if(offset < 0) + throw new IllegalArgumentException("negative offset."); + if(dest == null) + throw new IllegalArgumentException("dest is null."); + if(destOffset < 0 || destOffset > dest.length) + throw new IllegalArgumentException("destOffset out of range."); + if(destLength < 0 || destLength > (dest.length - destOffset)) + throw new IllegalArgumentException("destLength out of range."); + + if(offset >= bandVirtualSize) + return -1; + + final int readLength; + if(destLength > bandVirtualSize - offset) + readLength = (int) (bandVirtualSize - offset); + else + readLength = destLength; + + final int actualLength; + if(offset >= bandActualSize) + actualLength = 0; + else { + long remainingActualBytes = bandActualSize - offset; + if(readLength > remainingActualBytes) + actualLength = (int) remainingActualBytes; + else + actualLength = readLength; + } + + this.file.seek(offset); + int bytesRead = this.file.read(dest, destOffset, actualLength); + if(bytesRead != actualLength) + return bytesRead; + else { + if(actualLength != readLength) { + Arrays.fill(dest, destOffset + actualLength, + destOffset + readLength, (byte) 0); + } + + return readLength; + } + } +} diff --git a/src/org/catacombae/dmg/sparsebundle/BundleMember.java b/src/org/catacombae/dmg/sparsebundle/BundleMember.java new file mode 100644 index 0000000..1163d76 --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/BundleMember.java @@ -0,0 +1,41 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileLock; + +/** + * + * @author erik + */ +abstract class BundleMember { + + /* Backing store. */ + protected RandomAccessFile file; + protected FileLock fileLock; + + public BundleMember(RandomAccessFile file, FileLock fileLock) { + this.file = file; + this.fileLock = fileLock; + } + + public void close() { + try { + fileLock.release(); + } catch(IOException ioe) { + ioe.printStackTrace(); + } + + try { + file.close(); + } catch(IOException ioe) { + ioe.printStackTrace(); + } + } + +} diff --git a/src/org/catacombae/dmg/sparsebundle/Dump.java b/src/org/catacombae/dmg/sparsebundle/Dump.java new file mode 100644 index 0000000..8a50d3b --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/Dump.java @@ -0,0 +1,34 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.File; + +/** + * + * @author erik + */ +public class Dump { + public static void main(String[] args) { + ReadableSparseBundleStream stream = + new ReadableSparseBundleStream(new File(args[0])); + byte[] buf = new byte[512*1024]; + long bytesRead = 0; + + while(true) { + int curBytesRead = stream.read(buf); + if(curBytesRead == -1) + break; + else if(curBytesRead < 0) + throw new RuntimeException("Wtf... curBytesRead=" + + curBytesRead); + + bytesRead += curBytesRead; + + System.out.write(buf, 0, curBytesRead); + } + } +} diff --git a/src/org/catacombae/dmg/sparsebundle/Info.java b/src/org/catacombae/dmg/sparsebundle/Info.java new file mode 100644 index 0000000..dca2534 --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/Info.java @@ -0,0 +1,141 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.Reader; +import java.nio.channels.FileLock; +import org.catacombae.plist.PlistNode; +import org.catacombae.plist.XmlPlist; +import org.catacombae.util.Util; + +/** + * + * @author erik + */ +class Info extends BundleMember { + private long bandSize; + private long size; + + public Info(RandomAccessFile plistFile, FileLock plistFileLock) + throws IOException { + super(plistFile, plistFileLock); + refresh(); + } + + public long getBandSize() { return bandSize; } + public long getSize() { return size; } + + /** + * Re-reads the contents of the .plist file and updates the cached data. + * + * @throws IOException if there is an I/O error, or the data in the plist is + * invalid. + */ + protected void refresh() throws IOException { + long fileLength = file.length(); + if(fileLength > Integer.MAX_VALUE) + throw new ArrayIndexOutOfBoundsException("Info.plist is " + + "unreasonably large and doesn't fit in memory."); + + byte[] plistData = new byte[(int) fileLength]; + int bytesRead = file.read(plistData); + if(bytesRead != fileLength) + throw new IOException("Failed to read entire file. Read " + + bytesRead + "/" + fileLength + " bytes."); + + XmlPlist plist = new XmlPlist(plistData, true); + PlistNode dictNode = plist.getRootNode().cd("dict"); + if(dictNode == null) { + throw new IOException("Malformed Info.plist file: No 'dict' " + + "element at root."); + } + + final String cfBundleInfoDictionaryVersionKey = + "CFBundleInfoDictionaryVersion"; + final String bandSizeKey = + "band-size"; + final String bundleBackingstoreVersionKey = + "bundle-backingstore-version"; + final String diskImageBundleTypeKey = + "diskimage-bundle-type"; + final String sizeKey = + "size"; + + Reader cfBundleInfoDictionaryVersionReader = + dictNode.getKeyValue(cfBundleInfoDictionaryVersionKey); + Reader bandSizeReader = + dictNode.getKeyValue(bandSizeKey); + Reader bundleBackingstoreVersionReader = + dictNode.getKeyValue(bundleBackingstoreVersionKey); + Reader diskImageBundleTypeReader = + dictNode.getKeyValue(diskImageBundleTypeKey); + Reader sizeReader = + dictNode.getKeyValue(sizeKey); + + if(cfBundleInfoDictionaryVersionReader == null) + throw new IOException("Could not find '" + + cfBundleInfoDictionaryVersionKey + "' key in Info.plist " + + "file."); + if(bandSizeReader == null) + throw new IOException("Could not find '" + bandSizeKey + "' key " + + "in Info.plist file."); + if(bundleBackingstoreVersionReader == null) + throw new IOException("Could not find '" + + bundleBackingstoreVersionKey + "' key in Info.plist file."); + if(diskImageBundleTypeReader == null) + throw new IOException("Could not find '" + diskImageBundleTypeKey + + "' key in Info.plist file."); + if(sizeReader == null) + throw new IOException("Could not find '" + sizeKey + "' key in " + + "Info.plist file."); + + // We ignore the value of the dictionary version. + //String cfBundleInfoDictionaryVersionString = + // Util.readFully(cfBundleInfoDictionaryVersionReader); + String bandSizeString = + Util.readFully(bandSizeReader); + String bundleBackingstoreVersionString = + Util.readFully(bundleBackingstoreVersionReader); + String diskImageBundleTypeString = + Util.readFully(diskImageBundleTypeReader); + String sizeString = + Util.readFully(sizeReader); + + if(!diskImageBundleTypeString.equals( + "com.apple.diskimage.sparsebundle")) { + throw new IOException("Unexpected value for '" + + diskImageBundleTypeKey + "': " + diskImageBundleTypeString); + + } + + if(!bundleBackingstoreVersionString.equals("1")) { + throw new IOException("Unknown backing store version: " + + bundleBackingstoreVersionString); + + } + + final long bandSizeLong; + try { + bandSizeLong = Long.parseLong(bandSizeString); + } catch(NumberFormatException nfe) { + throw new IOException("Illegal numeric value for " + bandSizeKey + + ": " + bandSizeString); + } + + final long sizeLong; + try { + sizeLong = Long.parseLong(sizeString); + } catch(NumberFormatException nfe) { + throw new IOException("Illegal numeric value for " + sizeKey + + ": " + sizeString); + } + + this.bandSize = bandSizeLong; + this.size = sizeLong; + } +} diff --git a/src/org/catacombae/dmg/sparsebundle/ReadableSparseBundleStream.java b/src/org/catacombae/dmg/sparsebundle/ReadableSparseBundleStream.java new file mode 100644 index 0000000..68497d8 --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/ReadableSparseBundleStream.java @@ -0,0 +1,132 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import org.catacombae.io.BasicReadableRandomAccessStream; +import org.catacombae.io.RuntimeIOException; + +/** + * + * @author erik + */ +public class ReadableSparseBundleStream extends BasicReadableRandomAccessStream +{ + private final SparseBundle bundle; + private long pos = 0; + + /* Band state variables. */ + private long bandNumber = 0; + private Band band = null; + + public ReadableSparseBundleStream(final File sparseBundleDir) { + this(new SparseBundle(sparseBundleDir)); + } + + ReadableSparseBundleStream(final SparseBundle bundle) { + this.bundle = bundle; + } + + @Override + public void close() throws RuntimeIOException { + if(this.band != null) + this.band.close(); + this.bundle.close(); + } + + @Override + public void seek(long pos) throws RuntimeIOException { + this.pos = pos; + } + + @Override + public long length() throws RuntimeIOException { + return bundle.getSize(); + } + + @Override + public long getFilePointer() throws RuntimeIOException { + return pos; + } + + @Override + public int read(final byte[] data, final int off, final int len) + throws RuntimeIOException { + if(data == null) + throw new IllegalArgumentException("data is null."); + if(off < 0 || off > data.length) + throw new IllegalArgumentException("pos out of range."); + if(len < 0 || len > (data.length - off)) + throw new IllegalArgumentException("len out of range."); + + final long bundleSize = bundle.getSize(); + if(pos >= bundleSize) + return -1; + + final long bytesRemainingInStream = bundleSize - pos; + final int readSize; + if(len > bytesRemainingInStream) + readSize = (int) bytesRemainingInStream; + else + readSize = len; + + final long bandSize = bundle.getBandSize(); + int curOff = off; + int remainingSize = readSize; + while(remainingSize > 0) { + final long curBandNumber = pos / bandSize; + final long posInBand = pos % bandSize; + + if(band == null) + band = bundle.lookupBand(curBandNumber); + else if(curBandNumber != bandNumber) { + band.close(); + band = bundle.lookupBand(curBandNumber); + } + + bandNumber = curBandNumber; + + final long remainingInBand = bandSize - posInBand; + final int bytesToRead; + if(remainingSize > remainingInBand) + bytesToRead = (int) remainingInBand; + else + bytesToRead = remainingSize; + + final int bytesRead; + if(band == null) { + Arrays.fill(data, curOff, curOff+bytesToRead, (byte) 0); + bytesRead = bytesToRead; + } + else { + try { + bytesRead = band.read(posInBand, data, curOff, bytesToRead); + } catch(IOException ex) { + throw new RuntimeIOException("Exception while reading " + + "from band " + bandNumber + ".", ex); + } + + if(bytesRead < 0) { + if(bytesRead != -1) + throw new RuntimeException("Unexpected return value " + + "from Band.read: " + bytesRead); + break; + } + } + + curOff += bytesRead; + pos += bytesRead; + remainingSize -= bytesRead; + + if(bytesRead != bytesToRead) + break; + } + + return readSize - remainingSize; + } +} diff --git a/src/org/catacombae/dmg/sparsebundle/SparseBundle.java b/src/org/catacombae/dmg/sparsebundle/SparseBundle.java new file mode 100644 index 0000000..82bdfb7 --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/SparseBundle.java @@ -0,0 +1,250 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileLock; +import org.catacombae.io.RuntimeIOException; + +/** + * + * @author Erik Larsson + */ +class SparseBundle { + private static final String mainInfoFilename = "Info.plist"; + private static final String backupInfoFilename = "Info.bckup"; + private static final String tokenFilename = "token"; + private static final String bandsDirname = "bands"; + + private final Info mainInfo; + private final Info backupInfo; + private final Token token; + private final File bandsDir; + + private long size; + private long bandSize; + private long bandCount; + + public SparseBundle(final File sparseBundleDir) throws RuntimeIOException { + File[] files = sparseBundleDir.listFiles(); + File mainInfoFile = null; + File backupInfoFile = null; + File tokenFile = null; + File bandsDir = null; + + for(File f : files) { + if(f.getName().equals(mainInfoFilename)) + mainInfoFile = f; + else if(f.getName().equals(backupInfoFilename)) + backupInfoFile = f; + else if(f.getName().equals(tokenFilename)) + tokenFile = f; + else if(f.getName().equals(bandsDirname)) + bandsDir = f; + else + System.err.println("Warning: Encountered unknown file \"" + + f.getName() + " in sparse bundle base dir (\"" + + sparseBundleDir.getAbsolutePath() + "\")."); + } + + if(mainInfoFile == null || backupInfoFile == null || + tokenFile == null || bandsDir == null) { + throw new RuntimeIOException("Some files are missing from the " + + "sparse bundle directory (\"" + + sparseBundleDir.getAbsolutePath() + "\"."); + } + else if(!bandsDir.exists() || !bandsDir.isDirectory()) + throw new RuntimeIOException("Invalid '" + bandsDirname + "' " + + "directory."); + + final RandomAccessFile mainInfoRaf; + final RandomAccessFile backupInfoRaf; + final RandomAccessFile tokenRaf; + + try { + mainInfoRaf = new RandomAccessFile(mainInfoFile, "r"); + } catch(FileNotFoundException ex) { + throw new RuntimeIOException("Failed to open '" + mainInfoFilename + + "' for reading.", ex); + } + + try { + backupInfoRaf = new RandomAccessFile(backupInfoFile, "r"); + } catch(FileNotFoundException ex) { + throw new RuntimeIOException("Failed to open '" + + backupInfoFilename + "' for reading.", ex); + } + + try { + tokenRaf = new RandomAccessFile(tokenFile, "r"); + } catch(FileNotFoundException ex) { + throw new RuntimeIOException("Failed to open '" + tokenFilename + + "' for reading.", ex); + } + + final FileLock mainInfoLock; + final FileLock backupInfoLock; + final FileLock tokenLock; + + try { + mainInfoLock = mainInfoRaf.getChannel().lock(0L, Long.MAX_VALUE, + true); + } catch (IOException ex) { + throw new RuntimeIOException("Failed to aquire a shared lock on " + + "'" + mainInfoFilename + "'.", ex); + } + + try { + backupInfoLock = backupInfoRaf.getChannel().lock(0L, Long.MAX_VALUE, + true); + } catch (IOException ex) { + throw new RuntimeIOException("Failed to aquire a shared lock on " + + "'" + backupInfoFilename + "'.", ex); + } + + try { + tokenLock = tokenRaf.getChannel().lock(0L, Long.MAX_VALUE, + true); + } catch (IOException ex) { + throw new RuntimeIOException("Failed to aquire a shared lock on " + + "'" + tokenFilename + "'.", ex); + } + + try { this.mainInfo = new Info(mainInfoRaf, mainInfoLock); } + catch(IOException ioe) { + throw new RuntimeIOException("Exception while parsing '" + + mainInfoFilename + "'.", ioe); + } + + try { this.backupInfo = new Info(backupInfoRaf, backupInfoLock); } + catch(IOException ioe) { + throw new RuntimeIOException("Exception while parsing '" + + backupInfoFilename + "'.", ioe); + } + + this.token = new Token(tokenRaf, tokenLock); + + this.bandsDir = bandsDir; + + /* Check the 'bands' directory + + /* Cached variables. */ + this.size = mainInfo.getSize(); + this.bandSize = mainInfo.getBandSize(); + this.bandCount = (mainInfo.getSize() + mainInfo.getBandSize() - 1) / + mainInfo.getBandSize(); + + checkBandsDir(); + } + + private void checkBandsDir() throws RuntimeIOException { + for(File f : bandsDir.listFiles()) { + if(!f.isFile()) + throw new RuntimeIOException("Encountered non-file content " + + "inside bands directory."); + + final String curName = f.getName(); + final long bandNumber; + try { + bandNumber = Long.parseLong(curName, 16); + } catch(NumberFormatException nfe) { + throw new RuntimeIOException("Encountered non-parseable " + + "filename in bands directory: \"" + curName + "\""); + } + + //System.err.println("Found band number: " + bandNumber + " " + + // "(filename: \"" + curName + "\")"); + + if(bandNumber < 0 || bandNumber > bandCount - 1) + throw new RuntimeException("Invalid band number: " + + bandNumber); + } + } + + /** + * Returns the size of the virtual device. + * + * @return the size of the virtual device. + */ + public long getSize() { + return size; + } + + /** + * Returns the size of each band in the sparse bundle. + * + * @return the size of each band in the sparse bundle. + */ + public long getBandSize() { + return bandSize; + } + + /** + * Returns the number of bands that are part of this sparse bundle. + * + * @return the number of bands that are part of this sparse bundle. + */ + public long getBandCount() { + return bandCount; + } + + /** + * Looks up and returns the {@link Band} with the specified band number. + * + * The caller is responsible for closing the {@link Band} when it is done + * with it. + * + * @return the {@link Band} with the specified band number. + */ + Band lookupBand(long bandNumber) throws RuntimeIOException { + final String bandFilename = Long.toHexString(bandNumber); + final File bandFile = new File(bandsDir, bandFilename); + if(!bandFile.exists()) + return null; + + final RandomAccessFile bandRaf; + try { + bandRaf = new RandomAccessFile(bandFile, "r"); + } catch(FileNotFoundException ex) { + throw new RuntimeIOException("Failed to open '" + bandFilename + + "' for reading.", ex); + } + + final FileLock bandLock; + try { + bandLock = bandRaf.getChannel().lock(0L, Long.MAX_VALUE, true); + } catch (IOException ex) { + throw new RuntimeIOException("Failed to aquire a shared lock on " + + "'" + bandFilename + "'.", ex); + } + + final long curBandSize; + try { curBandSize = bandRaf.length(); } + catch(IOException ioe) { + throw new RuntimeIOException("Exception while querying band file " + + "length.", ioe); + } + + if(curBandSize > bandSize) + throw new RuntimeIOException("Invalid band: Size (" + curBandSize + + ") is larger than bandSize (" + bandSize + ")."); + + try { return new Band(bandRaf, bandLock, bandSize); } + catch(IOException ioe) { + throw new RuntimeIOException("Exception while creating Band " + + "instance.", ioe); + } + } + + void close() { + mainInfo.close(); + backupInfo.close(); + token.close(); + } +} diff --git a/src/org/catacombae/dmg/sparsebundle/Test.java b/src/org/catacombae/dmg/sparsebundle/Test.java new file mode 100644 index 0000000..83bf41f --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/Test.java @@ -0,0 +1,52 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.File; + +/** + * + * @author erik + */ +public class Test { + public static void main(String[] args) { + SparseBundle sb = new SparseBundle(new File(args[0])); + System.out.println("image size: " + sb.getSize() + " bytes"); + System.out.println("band size: " + sb.getBandSize() + " bytes"); + System.out.println("band count: " + sb.getBandCount() + " bands"); + + ReadableSparseBundleStream stream = new ReadableSparseBundleStream(sb); + byte[] buf = new byte[91673]; + long bytesRead = 0; + final long startTime = System.currentTimeMillis(); + long lastTime = startTime; + + while(true) { + int curBytesRead = stream.read(buf); + if(curBytesRead == -1) + break; + else if(curBytesRead < 0) + throw new RuntimeException("Wtf... curBytesRead=" + + curBytesRead); + + bytesRead += curBytesRead; + + final long curTime = System.currentTimeMillis(); + if(curTime - lastTime > 1000) { + System.err.println("Transferred " + bytesRead + " bytes in " + + (curTime-startTime)/((double) 1000.0) + " seconds."); + lastTime = curTime; + } + } + + System.err.println("Transfer complete."); + + final long curTime = System.currentTimeMillis(); + System.err.println("Transferred " + bytesRead + " bytes in " + + (curTime-startTime)/((double) 1000.0) + " seconds."); + + } +} diff --git a/src/org/catacombae/dmg/sparsebundle/Token.java b/src/org/catacombae/dmg/sparsebundle/Token.java new file mode 100644 index 0000000..4ae1a04 --- /dev/null +++ b/src/org/catacombae/dmg/sparsebundle/Token.java @@ -0,0 +1,20 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.catacombae.dmg.sparsebundle; + +import java.io.RandomAccessFile; +import java.nio.channels.FileLock; + +/** + * + * @author erik + */ +class Token extends BundleMember { + + public Token(RandomAccessFile tokenFile, FileLock tokenFileLock) { + super(tokenFile, tokenFileLock); + } +} diff --git a/src/org/catacombae/dmg/udif/Debug.java b/src/org/catacombae/dmg/udif/Debug.java new file mode 100644 index 0000000..822b3ca --- /dev/null +++ b/src/org/catacombae/dmg/udif/Debug.java @@ -0,0 +1,32 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +public class Debug { + public static boolean debug = false; + + public static void warning(String message) { + if(debug) + System.err.println(message); + } + + public static void notification(String message) { + if(debug) + System.out.println("------->NOTE: " + message); + } +} diff --git a/src/org/catacombae/dmg/udif/Koly.java b/src/org/catacombae/dmg/udif/Koly.java new file mode 100644 index 0000000..7de28bf --- /dev/null +++ b/src/org/catacombae/dmg/udif/Koly.java @@ -0,0 +1,166 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import org.catacombae.dmgextractor.Util; +import java.io.PrintStream; + +/** This class was generated by CStructToJavaClass. */ +public class Koly { + private static final int KOLY_FOURCC = 0x6B6F6C79; // ASCII: 'koly' + /* + * struct Koly + * size: 512 bytes + * description: + * + * BP Size Type Identifier Description + * -------------------------------------------------------- + * 0 4 UInt32 fourCC + * 4 1*28 byte[28] unknown1 + * 32 8 UInt64 plistBegin1 + * 40 8 UInt64 plistEndSometimes + * 48 8 UInt64 unknown2 + * 56 8 UInt64 unknown3 + * 64 8 UInt64 unknown4 + * 72 8 UInt64 unknown5 + * 80 8 UInt64 possibleChecksumType + * 88 4 UInt32 unknown6 + * 92 4 UInt32 possibleUnitSize + * 96 1*120 byte[120] unknown7 + * 216 8 UInt64 plistBegin2 + * 224 8 UInt64 plistSize + * 232 1*120 byte[120] unknown8 + * 352 4 UInt32 checksumAlgorithm + * 356 4 UInt32 checksumSize + * 360 1*152 byte[152] checksumData + */ + + private final byte[] fourCC = new byte[4]; + private final byte[] unknown1 = new byte[1*28]; + private final byte[] plistBegin1 = new byte[8]; + private final byte[] plistEndSometimes = new byte[8]; + private final byte[] unknown2 = new byte[8]; + private final byte[] unknown3 = new byte[8]; + private final byte[] unknown4 = new byte[8]; + private final byte[] unknown5 = new byte[8]; + private final byte[] possibleChecksumType = new byte[8]; + private final byte[] unknown6 = new byte[4]; + private final byte[] possibleUnitSize = new byte[4]; + private final byte[] unknown7 = new byte[1*120]; + private final byte[] plistBegin2 = new byte[8]; + private final byte[] plistSize = new byte[8]; + private final byte[] unknown8 = new byte[1*120]; + private final byte[] checksumAlgorithm = new byte[4]; + private final byte[] checksumSize = new byte[4]; + private final byte[] checksumData = new byte[1*152]; + + public Koly(byte[] data, int offset) { + System.arraycopy(data, offset+0, fourCC, 0, 4); + System.arraycopy(data, offset+4, unknown1, 0, 1*28); + System.arraycopy(data, offset+32, plistBegin1, 0, 8); + System.arraycopy(data, offset+40, plistEndSometimes, 0, 8); + System.arraycopy(data, offset+48, unknown2, 0, 8); + System.arraycopy(data, offset+56, unknown3, 0, 8); + System.arraycopy(data, offset+64, unknown4, 0, 8); + System.arraycopy(data, offset+72, unknown5, 0, 8); + System.arraycopy(data, offset+80, possibleChecksumType, 0, 8); + System.arraycopy(data, offset+88, unknown6, 0, 4); + System.arraycopy(data, offset+92, possibleUnitSize, 0, 4); + System.arraycopy(data, offset+96, unknown7, 0, 1*120); + System.arraycopy(data, offset+216, plistBegin2, 0, 8); + System.arraycopy(data, offset+224, plistSize, 0, 8); + System.arraycopy(data, offset+232, unknown8, 0, 1*120); + System.arraycopy(data, offset+352, checksumAlgorithm, 0, 4); + System.arraycopy(data, offset+356, checksumSize, 0, 4); + System.arraycopy(data, offset+360, checksumData, 0, 1*152); + } + + public static int length() { return 512; } + + public int getFourCC() { return Util.readIntBE(fourCC); } + public byte[] getUnknown1() { return Util.createCopy(unknown1); } + public long getPlistBegin1() { return Util.readLongBE(plistBegin1); } + public long getPlistEndSometimes() { return Util.readLongBE(plistEndSometimes); } + public long getUnknown2() { return Util.readLongBE(unknown2); } + public long getUnknown3() { return Util.readLongBE(unknown3); } + public long getUnknown4() { return Util.readLongBE(unknown4); } + public long getUnknown5() { return Util.readLongBE(unknown5); } + public long getPossibleChecksumType() { return Util.readLongBE(possibleChecksumType); } + public int getUnknown6() { return Util.readIntBE(unknown6); } + public int getPossibleUnitSize() { return Util.readIntBE(possibleUnitSize); } + public byte[] getUnknown7() { return Util.createCopy(unknown7); } + public long getPlistBegin2() { return Util.readLongBE(plistBegin2); } + public long getPlistSize() { return Util.readLongBE(plistSize); } + public byte[] getUnknown8() { return Util.createCopy(unknown8); } + public int getChecksumAlgorithm() { return Util.readIntBE(checksumAlgorithm); } + public int getChecksumSize() { return Util.readIntBE(checksumSize); } + public byte[] getChecksumData() { return Util.createCopy(checksumData); } + + public boolean isValid() { + return getFourCC() == KOLY_FOURCC; + } + + public void printFields(PrintStream ps, String prefix) { + ps.println(prefix + " fourCC: \"" + Util.toASCIIString(getFourCC()) + "\""); + ps.println(prefix + " unknown1: 0x" + Util.byteArrayToHexString(getUnknown1())); + ps.println(prefix + " plistBegin1: " + getPlistBegin1()); + ps.println(prefix + " plistEndSometimes: " + getPlistEndSometimes()); + ps.println(prefix + " unknown2: " + getUnknown2()); + ps.println(prefix + " unknown3: " + getUnknown3()); + ps.println(prefix + " unknown4: " + getUnknown4()); + ps.println(prefix + " unknown5: " + getUnknown5()); + ps.println(prefix + " possibleChecksumType: " + getPossibleChecksumType()); + ps.println(prefix + " unknown6: " + getUnknown6()); + ps.println(prefix + " possibleUnitSize: " + getPossibleUnitSize()); + ps.println(prefix + " unknown7: 0x" + Util.byteArrayToHexString(getUnknown7())); + ps.println(prefix + " plistBegin2: " + getPlistBegin2()); + ps.println(prefix + " plistSize: " + getPlistSize()); + ps.println(prefix + " unknown8: 0x" + Util.byteArrayToHexString(getUnknown8())); + ps.println(prefix + " checksumAlgorithm: " + getChecksumAlgorithm()); + int checksumSize = getChecksumSize()/8; + byte[] checksumData = getChecksumData(); + ps.println(prefix + " checksumSize: " + checksumSize); + ps.println(prefix + " checksumData: 0x" + Util.byteArrayToHexString(checksumData, 0, checksumSize)); // checksumSize is in bits + int i; + for(i = 0; i+4 <= (checksumData.length-checksumSize); i += 4) + ps.println(prefix + " trailing data[" + i + "]: 0x " + Util.byteArrayToHexString(checksumData, checksumSize+i, 4)); + int bytesLeft = i+4 - (checksumData.length-checksumSize); + if(bytesLeft > 0 && bytesLeft < 4) { + System.err.println("bytes left: " + bytesLeft); + ps.println(prefix + " trailing data[" + i + "]: 0x " + Util.byteArrayToHexString(checksumData, checksumSize+i, (i+4)-(checksumData.length-checksumSize))); + } + } + + public void print(PrintStream ps, String prefix) { + ps.println(prefix + "Koly:"); + printFields(ps, prefix); + } + + /** Test main. Reads the last 512 bytes from the input file (args[0]), creates a + Koly object and calls its print method to display the data. */ + public static void main(String[] args) throws java.io.IOException { + byte[] kolyData = new byte[512]; + java.io.RandomAccessFile raf = new java.io.RandomAccessFile(args[0], "r"); + raf.seek(raf.length()-512); + if(raf.read(kolyData) != kolyData.length) + throw new RuntimeException("Could not read entire koly..."); + raf.close(); + Koly k = new Koly(kolyData, 0); + k.print(System.out, ""); + } +} diff --git a/src/org/catacombae/dmg/udif/Koly.struct b/src/org/catacombae/dmg/udif/Koly.struct new file mode 100644 index 0000000..0c2c44e --- /dev/null +++ b/src/org/catacombae/dmg/udif/Koly.struct @@ -0,0 +1,20 @@ +struct Koly { + UInt32 fourCC; + byte unknown1[28]; + UInt64 plistBegin1; + UInt64 plistEndSometimes; + UInt64 unknown2; + UInt64 unknown3; + UInt64 unknown4; + UInt64 unknown5; + UInt64 possibleChecksumType; + UInt32 unknown6; + UInt32 possibleUnitSize; + byte unknown7[120]; + UInt64 plistBegin2; + UInt64 plistSize; + byte unknown8[120]; + UInt32 checksumAlgorithm; + UInt32 checksumSize; + byte checksumData[152]; +}; diff --git a/src/org/catacombae/dmg/udif/Plist.java b/src/org/catacombae/dmg/udif/Plist.java new file mode 100644 index 0000000..a4573a7 --- /dev/null +++ b/src/org/catacombae/dmg/udif/Plist.java @@ -0,0 +1,114 @@ +/*- + * Copyright (C) 2006-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.LinkedList; +import net.iharder.base64.Base64; +import org.catacombae.dmgextractor.Util; +import org.catacombae.dmgextractor.io.*; +import org.catacombae.plist.PlistNode; +import org.catacombae.plist.XmlPlist; + +public class Plist extends XmlPlist { + + public Plist(byte[] data) { + this(data, 0, data.length); + } + public Plist(byte[] data, boolean useSAXParser) { + this(data, 0, data.length, useSAXParser); + } + public Plist(byte[] data, int offset, int length) { + this(data, offset, length, false); + } + public Plist(byte[] data, int offset, int length, boolean useSAXParser) { + super(data, offset, length, useSAXParser); + } + + //public byte[] getData() { return Util.createCopy(plistData); } + + public PlistPartition[] getPartitions() throws IOException { + LinkedList partitionList = new LinkedList(); + PlistNode current = getRootNode(); + current = current.cd("dict"); + current = current.cdkey("resource-fork"); + current = current.cdkey("blkx"); + + // Variables to keep track of the pointers of the previous partition + long previousOutOffset = 0; + long previousInOffset = 0; + + // Iterate over the partitions and gather data + for(PlistNode pn : current.getChildren()) { + String partitionName = Util.readFully(pn.getKeyValue("Name")); + String partitionID = Util.readFully(pn.getKeyValue("ID")); + String partitionAttributes = Util.readFully(pn.getKeyValue("Attributes")); + //System.err.println("Retrieving data..."); + //(new BufferedReader(new InputStreamReader(System.in))).readLine(); + Reader base64Data = pn.getKeyValue("Data"); + //System.gc(); + //System.err.println("Converting data to binary form... free memory: " + Runtime.getRuntime().freeMemory() + " total memory: " + Runtime.getRuntime().totalMemory()); + //byte[] data = Base64.decode(base64Data); + +// try { +// InputStream yo = new Base64.InputStream(new ReaderInputStream(base64Data, Charset.forName("US-ASCII"))); +// String filename1 = "dump_plist_java-" + System.currentTimeMillis() + ".datadpp"; +// System.err.println("Dumping output from ReaderInputStream to file \"" + filename1 + "\""); +// FileOutputStream fos = new FileOutputStream(filename1); +// if(false) { // Standard way +// byte[] buffer = new byte[4096]; +// int curBytesRead = yo.read(buffer); +// while(curBytesRead == buffer.length) { +// fos.write(buffer, 0, curBytesRead); +// curBytesRead = yo.read(buffer); +// } +// if(curBytesRead > 0) +// fos.write(buffer, 0, curBytesRead); +// } +// else { // Simulating PlistPartition constructor +// byte[] buf1 = new byte[0xCC]; +// byte[] buf2 = new byte[0x28]; +// int curBytesRead = (int)yo.skip(0xCC); // SKIP OPERATION FUCKS UP!one +// fos.write(buf1, 0, curBytesRead); +// curBytesRead = yo.read(buf2); +// while(curBytesRead == buf2.length) { +// fos.write(buf2, 0, curBytesRead); +// curBytesRead = yo.read(buf2); +// } +// if(curBytesRead > 0) +// fos.write(buf2, 0, curBytesRead); + +// } +// fos.close(); +// } catch(Exception e) { e.printStackTrace(); } + + InputStream base64DataInputStream = new Base64.InputStream(new ReaderInputStream(base64Data, Charset.forName("US-ASCII"))); + + //System.err.println("Creating PlistPartition."); + //System.out.println("Block list for partition " + i++ + ":"); + PlistPartition dpp = new PlistPartition(partitionName, partitionID, partitionAttributes, + base64DataInputStream, previousOutOffset, previousInOffset); + previousOutOffset = dpp.getFinalOutOffset(); + previousInOffset = dpp.getFinalInOffset(); + partitionList.addLast(dpp); + } + + return partitionList.toArray(new PlistPartition[partitionList.size()]); + } +} diff --git a/src/org/catacombae/dmg/udif/PlistPartition.java b/src/org/catacombae/dmg/udif/PlistPartition.java new file mode 100644 index 0000000..04850d5 --- /dev/null +++ b/src/org/catacombae/dmg/udif/PlistPartition.java @@ -0,0 +1,213 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.LinkedList; + +public class PlistPartition { + private String name; + private String id; + private String attributes; + private UDIFBlock[] blockList; + private long partitionSize; + + // Incoming variables + private final long previousOutOffset; + private final long previousInOffset; + + // Outgoing variables + private long finalOutOffset = -1; + private long finalInOffset = -1; + + public PlistPartition(String name, String id, String attributes, byte[] data, + long previousOutOffset, long previousInOffset) throws IOException { + this(name, id, attributes, new ByteArrayInputStream(data), + previousOutOffset, previousInOffset); + } + + public PlistPartition(String name, String id, String attributes, InputStream data, + long previousOutOffset, long previousInOffset) throws IOException { + this.name = name; + this.id = id; + this.attributes = attributes; + this.previousOutOffset = previousOutOffset; + this.previousInOffset = previousInOffset; + + this.blockList = parseBlocks(data); + this.partitionSize = calculatePartitionSize(blockList); + } + + public String getName() { + return name; + } + + public String getID() { + return id; + } + + public String getAttributes() { + return attributes; + } + + public long getPartitionSize() { + return partitionSize; + } + + /** Copies all blocks to a newly allocated array. Might waste some memory. */ + public UDIFBlock[] getBlocks() { + UDIFBlock[] res = new UDIFBlock[blockList.length]; + for(int i = 0; i < res.length; ++i) + res[i] = blockList[i]; + return res; + } + + /** Returns an iterator over all the UDIFBlocks that describe the contents of this partition. */ + public Iterator getBlockIterator() { + return new BlockIterator(blockList); + } + + public int getBlockCount() { + return blockList.length; + } + + public long getFinalOutOffset() { + if(finalOutOffset < 0) + throw new RuntimeException("parseBlocks has not yet been called!"); + return finalOutOffset; + } + + public long getFinalInOffset() { + if(finalInOffset < 0) + throw new RuntimeException("parseBlocks has not yet been called!"); + return finalInOffset; + } + + private UDIFBlock[] parseBlocks(InputStream is) throws IOException { + long bytesSkipped = is.read(new byte[0xCC]); + + if(bytesSkipped != 0xCC) + throw new RuntimeException("Could not skip the desired amount of bytes..."); + + int blockNumber = 0; // Increments by one for each block we read (each iteration in the while loop below) + + /* These two variables are part of the "hack" described below. */ + long lastByteReadInBlock = -1; + boolean addInOffset = false; + + byte[] blockData = new byte[UDIFBlock.structSize()]; + + LinkedList blocks = new LinkedList(); + + int bytesRead = is.read(blockData); + while(bytesRead > 0) { //offset <= data.length-UDIFBlock) { + //System.err.println("Looping (read " + bytesRead + " bytes)"); + if(bytesRead != blockData.length) + throw new RuntimeException("Could not read the desired amount of bytes... (desired: " + blockData.length + " read: " + bytesRead + ")"); + + long inOffset = UDIFBlock.peekInOffset(blockData, 0); + long inSize = UDIFBlock.peekInSize(blockData, 0); + + // Set compensation to the end of the output data of the previous partition to get true offset in outfile. + long outOffsetCompensation = previousOutOffset; + + // Update pointer to the last byte read in the last block + if(lastByteReadInBlock == -1) + lastByteReadInBlock = inOffset; + lastByteReadInBlock += inSize; + + /* + * The lines below are a "hack" that I had to do to make dmgx work with + * certain dmg-files. I don't understand the issue at all, which is why + * this hack is here, but sometimes inOffset == 0 means that it is 0 + * relative to the previous partition's last inOffset. And sometimes it + * doesn't (meaning the actual position 0 in the dmg file). + */ + if(inOffset == 0 && blockNumber == 0) { + Debug.notification("Detected inOffset == 0, setting addInOffset flag."); + addInOffset = true; + } + long inOffsetCompensation = 0; + if(addInOffset) { + Debug.notification("addInOffset mode: inOffset tranformation " + inOffset + "->" + + (inOffset + previousInOffset)); + inOffsetCompensation = previousInOffset; + } + + UDIFBlock currentBlock = new UDIFBlock(blockData, 0, outOffsetCompensation, inOffsetCompensation); + blocks.add(currentBlock); + ++blockNumber; + + //System.out.println(" " + currentBlock.toString()); + + // Return if we have reached the end, and update + if(currentBlock.getBlockType() == UDIFBlock.BT_END) { + finalOutOffset = currentBlock.getTrueOutOffset(); + finalInOffset = previousInOffset + lastByteReadInBlock; + + if(is.read() != -1) + Debug.warning("Encountered additional data in blkx blob."); + return blocks.toArray(new UDIFBlock[blocks.size()]); + } + + bytesRead = is.read(blockData); + } + + throw new RuntimeException("No BT_END block found!"); + } + + public static long calculatePartitionSize(UDIFBlock[] data) throws IOException { + long partitionSize = 0; + + for(UDIFBlock db : data) + partitionSize += db.getOutSize(); + + return partitionSize; + } + + private class BlockIterator implements Iterator { + + private UDIFBlock[] blocks; + private int pointer, endOffset; + + public BlockIterator(UDIFBlock[] blocks) { + this(blocks, 0, blocks.length); + } + + public BlockIterator(UDIFBlock[] blocks, int offset, int length) { + this.blocks = blocks; + this.pointer = offset; + this.endOffset = offset + length; + } + + public boolean hasNext() { + return pointer < endOffset; + } + + public UDIFBlock next() { + return blocks[pointer++]; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/org/catacombae/dmg/udif/UDIFBlock.java b/src/org/catacombae/dmg/udif/UDIFBlock.java new file mode 100644 index 0000000..06e707d --- /dev/null +++ b/src/org/catacombae/dmg/udif/UDIFBlock.java @@ -0,0 +1,208 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import org.catacombae.dmgextractor.Util; + +public class UDIFBlock implements Comparable{ + /** This blocktype means the data is compressed using some "ADC" algorithm that I have no idea how to decompress... */ + public static final int BT_ADC = 0x80000004; + + /** This blocktype means the data is compressed with zlib. */ + public static final int BT_ZLIB = 0x80000005; + + /** This blocktype means the data is compressed with the bzip2 compression algorithm. These blocktypes are unsupported, + as I haven't found a GPL-compatible bzip2 decompressor written in Java yet. */ + public static final int BT_BZIP2 = 0x80000006; + + /** This blocktype means the data is uncompressed and can simply be copied. */ + public static final int BT_COPY = 0x00000001; + + /** This blocktype represents a fill of zeroes. */ + public static final int BT_ZERO = 0x00000002; + + /** This blocktype represents a fill of zeroes (the difference between this blocktype and BT_ZERO is not documented, + and parsing of these blocks is experimental). */ + public static final int BT_ZERO2 = 0x00000000; + + /** This blocktype indicates the end of the partition. */ + public static final int BT_END = 0xffffffff; + + /** This blocktype has been observed, but its purpose is currently unknown. In all the observed cases the outSize was + equal to 0, so it's probably some marker, like BT_END. */ + public static final int BT_UNKNOWN = 0x7ffffffe; + + + private static final String BT_ADC_STRING = "BT_ADC"; + private static final String BT_ZLIB_STRING = "BT_ZLIB"; + private static final String BT_BZIP2_STRING = "BT_BZIP2"; + private static final String BT_COPY_STRING = "BT_COPY"; + private static final String BT_ZERO_STRING = "BT_ZERO"; + private static final String BT_ZERO2_STRING = "BT_ZERO2"; + private static final String BT_END_STRING = "BT_END"; + private static final String BT_UNKNOWN_STRING = "BT_UNKNOWN"; + + /* + * BP Name Size + * ------------------- + * 0 blockType 4 + * 4 skipped 4 + * 8 outOffset 8 + * 16 outSize 8 + * 24 inOffset 8 + * 32 inSize 8 + * ------------------- + * 40 bytes / 0x28 bytes + */ + private final int blockType; + private final int reserved; + private final long outOffset; + private final long outSize; + private final long inOffset; + private final long inSize; + private final long outOffsetComp; + private final long inOffsetComp; + + //private boolean immutable = false; + + public UDIFBlock(byte[] data, int offset, long outOffsetComp, long inOffsetComp) { + this(Util.readIntBE(data, offset + 0), + Util.readIntBE(data, offset + 4), + Util.readLongBE(data, offset + 8) * 0x200, + Util.readLongBE(data, offset + 16) * 0x200, + Util.readLongBE(data, offset + 24), + Util.readLongBE(data, offset + 32), + outOffsetComp, + inOffsetComp); + } + + public UDIFBlock(int blockType, int reserved, long outOffset, long outSize, long inOffset, long inSize, + long outOffsetComp, long inOffsetComp) { + this.blockType = blockType; + this.reserved = reserved; + this.outOffset = outOffset; + this.outSize = outSize; + this.inOffset = inOffset; + this.inSize = inSize; + this.outOffsetComp = outOffsetComp; + this.inOffsetComp = inOffsetComp; + } + + public static int structSize() { return 40; } + + public int getBlockType() { return blockType; } + public int getReserved() { return reserved; } + public long getOutOffset() { return outOffset; } + public long getOutSize() { return outSize; } + public long getInOffset() { return inOffset; } + public long getInSize() { return inSize; } + + public String getBlockTypeAsString() { + switch(blockType) { + case BT_ADC: + return BT_ADC_STRING; + case BT_ZLIB: + return BT_ZLIB_STRING; + case BT_BZIP2: + return BT_BZIP2_STRING; + case BT_COPY: + return BT_COPY_STRING; + case BT_ZERO: + return BT_ZERO_STRING; + case BT_ZERO2: + return BT_ZERO2_STRING; + case BT_END: + return BT_END_STRING; + case BT_UNKNOWN: + return BT_UNKNOWN_STRING; + default: + return "[Unknown block type! ID=0x" + Integer.toHexString(blockType) + "]"; + } + } + + /** + * This field is not part of the structure itself. It is metadata used to + * determine the actual byte position of the out offset. + */ + public long getOutOffsetCompensation() { return outOffsetComp; } + + /** + * This field is not part of the structure itself. It is metadata used to + * determine the actual byte position of the in offset. + */ + public long getInOffsetCompensation() { return inOffsetComp; } + + //public void setOutOffsetCompensation(long offset) { + // if(immutable) + // throw new RuntimeException("This block has been toggled immutable!"); + // outOffsetComp = offset; + //} + //public void setInOffsetCompensation(long offset) { + // if(immutable) + // throw new RuntimeException("This block has been toggled immutable!"); + // inOffsetComp = offset; + //} + + /** Convenience method for determining the actual compensated out offset. This is what you should use. */ + public long getTrueOutOffset() { + return outOffset + outOffsetComp; + } + + /** Convenience method for determining the actual compensated in offset. This is what you should use. */ + public long getTrueInOffset() { + return inOffset + inOffsetComp; + } + + //public void markImmutable() { + // immutable = true; + //} + + @Override + public String toString() { + return getBlockTypeAsString() + + "(reserved=0x" + Integer.toHexString(reserved) + ",outOffset=" + outOffset + + ",outSize=" + outSize + ",inOffset=" + inOffset + ",inSize=" + inSize + ",outOffsetComp=" + outOffsetComp + ",inOffsetComp=" + inOffsetComp + ")"; + } + + /** + * Reads the inOffset field from data at offset + * which is supposed to be a valid raw UDIF block structure at 40 bytes. + */ + public static long peekInOffset(byte[] data, int offset) { + return Util.readLongBE(data, offset + 24); + } + + /** + * Reads the inSize field from data at offset + * which is supposed to be a valid raw UDIF block structure at 40 bytes. + */ + public static long peekInSize(byte[] data, int offset) { + return Util.readLongBE(data, offset + 32); + } + + /** Orders blocks according to the "true" InOffset. */ + public int compareTo(UDIFBlock db) { + long res = getTrueInOffset() - db.getTrueInOffset(); + if(res > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + else if(res < Integer.MIN_VALUE) + return Integer.MIN_VALUE; + else + return (int) res; + } +} diff --git a/src/org/catacombae/dmg/udif/UDIFBlockInputStream.java b/src/org/catacombae/dmg/udif/UDIFBlockInputStream.java new file mode 100644 index 0000000..6afa5b7 --- /dev/null +++ b/src/org/catacombae/dmg/udif/UDIFBlockInputStream.java @@ -0,0 +1,460 @@ +/*- + * Copyright (C) 2006-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; +import org.apache.tools.bzip2.CBZip2InputStream; +import org.catacombae.dmgextractor.Util; +import org.catacombae.dmgextractor.DmgException; +import org.catacombae.dmgextractor.io.RandomAccessInputStream; +import org.catacombae.dmgextractor.io.SynchronizedRandomAccessStream; +import org.catacombae.io.ReadableRandomAccessStream; +import org.catacombae.io.RuntimeIOException; + +public abstract class UDIFBlockInputStream extends InputStream { + protected ReadableRandomAccessStream raf; + protected UDIFBlock block; + protected final int addInOffset; + private long globalBytesRead; + // 16 KiB buffer... is it reasonable? + protected final byte[] buffer = new byte[16384]; + protected int bufferPos = 0; + // Initializing this to zero will make read call fillBuffer at first call + protected int bufferDataLength = 0; + private final byte[] skipBuffer = new byte[4096]; + + /** + * Subclasses use this variable to report how many bytes were read into the + * buffer. + */ + protected int fillSize; + + /** + * Creates a new UDIFBlockInputStream. + * + * @param raf the RandomAccessFile representing the UDIF file + * @param block the block that we should read (usually obtained via + * {@link PlistPartition#getBlocks()}) + * @param addInOffset the number to add to the block's inOffset to find the + * data. + */ + protected UDIFBlockInputStream(ReadableRandomAccessStream raf, + UDIFBlock block, int addInOffset) { + + this.raf = raf; + this.block = block; + this.addInOffset = addInOffset; + //fillBuffer(); + //bufferPos = buffer.length; + } + + /** + * This method WILL throw a RuntimeException if block has a + * type that there is no handler for. + */ + public static UDIFBlockInputStream getStream(ReadableRandomAccessStream raf, + UDIFBlock block) throws IOException, RuntimeIOException { + + switch(block.getBlockType()) { + case UDIFBlock.BT_ZLIB: + return new ZlibBlockInputStream(raf, block, 0); + case UDIFBlock.BT_BZIP2: + return new Bzip2BlockInputStream(raf, block, 0); + case UDIFBlock.BT_COPY: + return new CopyBlockInputStream(raf, block, 0); + case UDIFBlock.BT_ZERO: + case UDIFBlock.BT_ZERO2: + return new ZeroBlockInputStream(raf, block, 0); + case UDIFBlock.BT_END: + case UDIFBlock.BT_UNKNOWN: + throw new RuntimeException("Block type is a marker and " + + "contains no data."); + case UDIFBlock.BT_ADC: + default: + throw new RuntimeException("No handler for block type " + + block.getBlockTypeAsString()); + } + } + + /** + * In case the available amount of bytes is larger than Integer.MAX_INT, + * Integer.MAX_INT is returned. + */ + @Override + public int available() throws IOException { + long available = block.getOutSize() - globalBytesRead; + if(available > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + else + return (int)available; + } + + /** + * This method does NOT close the underlying RandomAccessFile. It can be + * reused afterwards. + */ + @Override + public void close() throws IOException {} + + /** Not supported. */ + @Override + public void mark(int readlimit) {} + + /** Returns false, because it isn't supported. */ + @Override + public boolean markSupported() { + return false; + } + + /** @see java.io.InputStream */ + @Override + public int read() throws IOException { + byte[] b = new byte[1]; + return read(b, 0, 1); + } + + /** @see java.io.InputStream */ + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** @see java.io.InputStream */ + @Override + public int read(byte[] b, int off, int len) throws IOException { +// System.out.println("UDIFBlockInputStream.read(b, " + off + ", " + len + ") {"); + + final int bytesToRead = len; + + int bytesRead = 0; + int outPos = off; + while(bytesRead < bytesToRead) { + int bytesRemainingInBuffer = bufferDataLength - bufferPos; + if(bytesRemainingInBuffer == 0) { +// System.out.println(" first call to fillBuffer"); + fillBuffer(); +// System.out.println(" bufferDataLength=" + bufferDataLength + ",bufferPos=" + bufferPos); + bytesRemainingInBuffer = bufferDataLength - bufferPos; + if(bytesRemainingInBuffer == 0) { // We apparently have no more data. + if(bytesRead == 0) { + //System.out.println("return: -1 }"); + return -1; + } + else + break; + } + } +// System.out.println(" bytesRemainingInBuffer=" + +// bytesRemainingInBuffer + ",bufferPos=" + bufferPos + +// ",bufferDataLength=" + bufferDataLength); + int bytesToReadFromBuffer = + Math.min(bytesToRead - bytesRead, bytesRemainingInBuffer); +// System.out.println(" bytesToReadFromBuffer=" + +// bytesToReadFromBuffer); +// System.out.println(" System.arraycopy(buffer, " + bufferPos + +// ", b, " + outPos + ", " + bytesToReadFromBuffer + ");"); + System.arraycopy(buffer, bufferPos, b, outPos, bytesToReadFromBuffer); + + outPos += bytesToReadFromBuffer; + bufferPos += bytesToReadFromBuffer; + + bytesRead += bytesToReadFromBuffer; + } + + globalBytesRead += bytesRead; + +// System.out.println("return: " + bytesRead + " }"); + return bytesRead; + } + + /** Does nothing. Not supported. */ + @Override + public void reset() throws IOException {} + + /** + * Skips as many bytes as possible. If end of file is reached, the number + * of bytes skipped is returned. + */ + @Override + public long skip(long n) throws IOException { + long bytesSkipped = 0; + while(bytesSkipped < n) { + int curSkip = (int) Math.min(n - bytesSkipped, skipBuffer.length); + int res = read(skipBuffer, 0, curSkip); + if(res > 0) + bytesSkipped += res; + else + break; + } + return bytesSkipped; + } + + protected abstract void fillBuffer() throws IOException; + + public static class ZlibBlockInputStream extends UDIFBlockInputStream { + + private final Inflater inflater; + private final byte[] inBuffer; + private long inPos; + + public ZlibBlockInputStream(ReadableRandomAccessStream raf, + UDIFBlock block, int addInOffset) throws IOException { + super(raf, block, addInOffset); + inflater = new Inflater(true); + inBuffer = new byte[4096]; + inPos = 0; + feedInflater(); + } + + private void feedInflater() throws IOException { + //System.err.println("ZlibBlockInputStream.feedInflater() {"); + long seekPos = addInOffset + inPos + block.getTrueInOffset(); + //System.out.println(" seeking to " + seekPos + " (file length: " + + // raf.length() + ")"); + raf.seek(seekPos); + long bytesLeftToRead = block.getInSize() - inPos; + int bytesToFeed = (int) Math.min(inBuffer.length, bytesLeftToRead); + //System.out.println(" bytesToFeed=" + bytesToFeed); + + byte[] buf; + int bytesToWrite = bytesToFeed; + if(bytesLeftToRead <= inBuffer.length) { + buf = new byte[inBuffer.length + 1]; + bytesToWrite++; + } + else + buf = inBuffer; + + int curBytesRead = raf.read(buf, 0, bytesToFeed); + inPos += curBytesRead; + inflater.setInput(inBuffer, 0, curBytesRead); + //System.out.println(" curBytesRead=" + curBytesRead); + //System.out.println("}"); + } + + protected void fillBuffer() throws RuntimeIOException, IOException { + //System.err.println("ZlibBlockInputStream.fillBuffer() {"); + //if(inflater == null) + // System.err.println("INFLATER IS NULL"); + //if(inBuffer == null) + // System.err.println("INBUFFER IS NULL"); + if(inflater.finished()) { + //System.out.println("inflater claims to be finished..."); + bufferPos = 0; + bufferDataLength = 0; + } + try { + int bytesInflated = 0; + while(bytesInflated < buffer.length && !inflater.finished()) { + if(inflater.needsInput()) + feedInflater(); + int res = inflater.inflate(buffer, bytesInflated, + buffer.length - bytesInflated); + if(res >= 0) + bytesInflated += res; + else + throw new DmgException("Negative return value when " + + "inflating"); + } + + // The fillBuffer method is responsible for updating bufferPos + // and bufferDataLength + bufferPos = 0; + bufferDataLength = bytesInflated; + } catch(DataFormatException e) { + DmgException re = new DmgException("Invalid zlib data!"); + re.initCause(e); + throw re; + } + //System.out.println("}"); + } + } + + public static class CopyBlockInputStream extends UDIFBlockInputStream { + + private long inPos = 0; + + public CopyBlockInputStream(ReadableRandomAccessStream raf, + UDIFBlock block, int addInOffset) throws RuntimeIOException { + super(raf, block, addInOffset); + } + + protected void fillBuffer() throws IOException { + raf.seek(addInOffset + inPos + block.getTrueInOffset()); + + final int bytesToRead = (int) Math.min(block.getInSize() - inPos, + buffer.length); + int totalBytesRead = 0; + while(totalBytesRead < bytesToRead) { + int bytesRead = raf.read(buffer, totalBytesRead, + bytesToRead - totalBytesRead); + if(bytesRead < 0) + break; + else { + totalBytesRead += bytesRead; + inPos += bytesRead; + } + } + + // The fillBuffer method is responsible for updating bufferPos and + // bufferDataLength + bufferPos = 0; + bufferDataLength = totalBytesRead; + } + + /** Extremely more efficient skip method! */ + @Override + public long skip(long n) throws IOException { + final int bytesToSkip = + (int) Math.min(block.getInSize() - inPos, n); + inPos += bytesToSkip; + + // make read() refill buffer at next call.. + bufferPos = 0; + bufferDataLength = 0; + + return bytesToSkip; + } + } + + public static class ZeroBlockInputStream extends UDIFBlockInputStream { + + private long outPos = 0; + + public ZeroBlockInputStream(ReadableRandomAccessStream raf, + UDIFBlock block, int addInOffset) throws RuntimeIOException { + super(raf, block, addInOffset); + } + + protected void fillBuffer() throws IOException { + final int bytesToWrite = + (int) Math.min(block.getOutSize() - outPos, buffer.length); + Util.zero(buffer, 0, bytesToWrite); + outPos += bytesToWrite; + + // The fillBuffer method is responsible for updating bufferPos and + // bufferDataLength + bufferPos = 0; + bufferDataLength = bytesToWrite; + } + + /** Extremely more efficient skip method! */ + @Override + public long skip(long n) throws IOException { + final int bytesToSkip = + (int) Math.min(block.getOutSize() - outPos, n); + outPos += bytesToSkip; + + // make read() refill buffer at next call.. + bufferPos = 0; + bufferDataLength = 0; + + return bytesToSkip; + } + } + + public static class Bzip2BlockInputStream extends UDIFBlockInputStream { + + private final byte[] BZIP2_SIGNATURE = { 0x42, 0x5A }; // 'BZ' + private InputStream bzip2DataStream; + private CBZip2InputStream decompressingStream; + private long outPos = 0; + + public Bzip2BlockInputStream(ReadableRandomAccessStream raf, + UDIFBlock block, int addInOffset) + throws IOException, RuntimeIOException { + + super(raf, block, addInOffset); + + if(false) { + byte[] inBuffer = new byte[4096]; + String basename = System.nanoTime() + ""; + File outFile = new File(basename + "_bz2.bin"); + int i = 1; + while(outFile.exists()) + outFile = new File(basename + "_" + i++ + "_bz2.bin"); + System.err.println("Creating a new Bzip2BlockInputStream. " + + "Dumping bzip2 block data to file \"" + outFile + "\""); + FileOutputStream outStream = new FileOutputStream(outFile); + raf.seek(block.getTrueInOffset()); + long bytesWritten = 0; + long bytesToWrite = block.getInSize(); + while(bytesWritten < bytesToWrite) { + int curBytesRead = raf.read(inBuffer, 0, + (int) Math.min(bytesToWrite - bytesWritten, + inBuffer.length)); + if(curBytesRead <= 0) + throw new RuntimeException("Unable to read bzip2 " + + "block fully."); + outStream.write(inBuffer, 0, curBytesRead); + bytesWritten += curBytesRead; + } + outStream.close(); + } + + bzip2DataStream = new RandomAccessInputStream( + new SynchronizedRandomAccessStream(raf), + block.getTrueInOffset(), block.getInSize()); + + byte[] signature = new byte[2]; + if(bzip2DataStream.read(signature) != signature.length) + throw new RuntimeException("Read error!"); + if(!Util.arraysEqual(signature, BZIP2_SIGNATURE)) + throw new RuntimeException("Invalid bzip2 block!"); + + /* Buffering needed because of implementation issues in + * CBZip2InputStream. */ + decompressingStream = new CBZip2InputStream( + new BufferedInputStream(bzip2DataStream)); + } + + protected void fillBuffer() throws IOException { + + final int bytesToRead = (int) Math.min(block.getOutSize() - outPos, + buffer.length); + int totalBytesRead = 0; + while(totalBytesRead < bytesToRead) { + int bytesRead = decompressingStream.read(buffer, totalBytesRead, + bytesToRead - totalBytesRead); + if(bytesRead < 0) + break; + else { + totalBytesRead += bytesRead; + outPos += bytesRead; + } + } + + // The fillBuffer method is responsible for updating bufferPos and + // bufferDataLength + bufferPos = 0; + bufferDataLength = totalBytesRead; + } + + @Override + public void close() throws IOException { + decompressingStream.close(); + bzip2DataStream.close(); + } + } +} diff --git a/src/org/catacombae/dmg/udif/UDIFDetector.java b/src/org/catacombae/dmg/udif/UDIFDetector.java new file mode 100644 index 0000000..e5fcffd --- /dev/null +++ b/src/org/catacombae/dmg/udif/UDIFDetector.java @@ -0,0 +1,58 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import java.io.RandomAccessFile; +import org.catacombae.io.ReadableFileStream; +import org.catacombae.io.ReadableRandomAccessStream; +import org.catacombae.io.RuntimeIOException; + +/** + * Contains a few static utility methods for easily detecting an UDIF encoded + * disk image. + * + * @author Erik Larsson + */ +public class UDIFDetector { + /** + * Convenience method. Equivalent to + * isUDIFEncoded(new ReadableFileStream(raf));. + */ + public static boolean isUDIFEncoded(RandomAccessFile raf) throws RuntimeIOException { + return isUDIFEncoded(new ReadableFileStream(raf)); + } + + /** + * Searches through the supplied RandomAccessStream for signature data that validates + * the data as UDIF encoded. + * @throws RuntimeIOException on I/O error + */ + public static boolean isUDIFEncoded(ReadableRandomAccessStream ras) throws RuntimeIOException { + if(ras.length() < 512) + return false; + + byte[] kolyData = new byte[Koly.length()]; + ras.seek(ras.length() - 512); + if(ras.read(kolyData) != kolyData.length) + throw new RuntimeException("Could not read all koly data..."); + Koly koly = new Koly(kolyData, 0); + return koly.isValid() && + koly.getPlistBegin1() >= 0 && koly.getPlistSize() > 0 && + (koly.getPlistBegin1() + koly.getPlistSize()) <= (ras.length() - 512); + } +} diff --git a/src/org/catacombae/dmg/udif/UDIFFile.java b/src/org/catacombae/dmg/udif/UDIFFile.java new file mode 100644 index 0000000..1971206 --- /dev/null +++ b/src/org/catacombae/dmg/udif/UDIFFile.java @@ -0,0 +1,38 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import org.catacombae.io.ReadableRandomAccessStream; + +public class UDIFFile { + private ReadableRandomAccessStream stream; + private UDIFFileView dmgView; + + public UDIFFile(ReadableRandomAccessStream stream) { + this.stream = stream; + this.dmgView = new UDIFFileView(stream); + } + + public UDIFFileView getView() { + return dmgView; + } + + public ReadableRandomAccessStream getStream() { + return stream; + } +} diff --git a/src/org/catacombae/dmg/udif/UDIFFileView.java b/src/org/catacombae/dmg/udif/UDIFFileView.java new file mode 100644 index 0000000..ccd2c8e --- /dev/null +++ b/src/org/catacombae/dmg/udif/UDIFFileView.java @@ -0,0 +1,67 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import org.catacombae.io.ReadableFileStream; +import org.catacombae.io.ReadableRandomAccessStream; +import org.catacombae.io.RuntimeIOException; + +public class UDIFFileView { + private ReadableRandomAccessStream dmgRaf; + + public UDIFFileView(File file) { + try { + //this.file = file; + this.dmgRaf = new ReadableFileStream(new RandomAccessFile(file, "r")); + } catch(IOException ioe) { + throw new RuntimeIOException(ioe); + } + } + public UDIFFileView(ReadableRandomAccessStream dmgRaf) { + this.dmgRaf = dmgRaf; + } + + public byte[] getPlistData() throws RuntimeIOException { + Koly koly = getKoly(); + byte[] plistData = new byte[(int)koly.getPlistSize()]; // Let's hope the plistsize is within int range... (though memory will run out long before that) + + dmgRaf.seek(koly.getPlistBegin1()); + if(dmgRaf.read(plistData) == plistData.length) + return plistData; + else + throw new RuntimeException("Could not read the entire region of data containing the Plist"); + } + + public Plist getPlist() throws RuntimeIOException { + return new Plist(getPlistData()); + } + + public Koly getKoly() throws RuntimeIOException { + dmgRaf.seek(dmgRaf.length()-512); + byte[] kolyData = new byte[512]; + dmgRaf.read(kolyData); + return new Koly(kolyData, 0); + } + + public void close() throws RuntimeIOException { + dmgRaf.close(); + } +} diff --git a/src/org/catacombae/dmg/udif/UDIFInputStream.java b/src/org/catacombae/dmg/udif/UDIFInputStream.java new file mode 100644 index 0000000..324c66a --- /dev/null +++ b/src/org/catacombae/dmg/udif/UDIFInputStream.java @@ -0,0 +1,149 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import java.io.*; + +/** + * An InputStream for reading the "block device" contents of a UDIF disk image file, usually + * using the extension .dmg. Note that all files with the extension .dmg are not UDIF + * encoded. Some are raw disk images, with no wrapper, and can be burnt or restored directly + * to disk/optical disc.
+ * Make sure that no other stream concurrently uses the input file. This class isn't thread + * safe, so use external synchronization if needed. + */ +public class UDIFInputStream extends InputStream { + private UDIFRandomAccessStream wrapped; + private long filePointer; + + /** Constructs a new UDIF input stream from a RandomAccessFile. This is a convenience method, and + equal to new UDI*/ + public UDIFInputStream(RandomAccessFile raf) throws IOException { + this(new UDIFRandomAccessStream(raf)); + } + public UDIFInputStream(UDIFRandomAccessStream dras) throws IOException { + this.wrapped = dras; + } + + /** Returns the number of available bytes, or Integer.MAX_VALUE if the value is too large for int. */ + @Override + public int available() throws IOException { + long len = wrapped.length()-filePointer; + if(len > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + else if(len < 0) + throw new IOException("Internal error! filePointer > wrapped.length! filePointer:" + filePointer + " wrapped.length():" + wrapped.length()); + else + return (int)len; + } + /** Does nothing. You will have to close the underlying RandomAccessFile or RandomAccessStream manually. */ + @Override + public void close() throws IOException {} + + /** Not supported. */ + @Override + public void mark(int readlimit) {} + + /** Mark is not supported. This method always returns false. */ + @Override + public boolean markSupported() { return false; } + + /** See the general contract of the read method for java.io.InputStream. */ + @Override + public int read() throws IOException { + byte[] b = new byte[1]; + if(read(b, 0, 1) != 1) + return -1; + else + return b[0] & 0xFF; + } + + /** See the general contract of the read method for java.io.InputStream. */ + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** See the general contract of the read method for java.io.InputStream. */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if(wrapped.getFilePointer() != filePointer) + wrapped.seek(filePointer); + int bytesRead = wrapped.read(b, off, len); + if(bytesRead > 0) + filePointer += bytesRead; + return bytesRead; + } + + /** Not supported. */ + @Override + public void reset() throws IOException {} + + /** See the general contract of the skip method for java.io.InputStream. */ + @Override + public long skip(long n) throws IOException { + if(n < 0) throw new IllegalArgumentException("n must be positive"); + long newPos = filePointer+n; + if(newPos > wrapped.length()) + newPos = wrapped.length(); + wrapped.seek(newPos); + long result = newPos - filePointer; + filePointer = newPos; + return result; + } + + /** Test code. */ + public static void main(String[] args) throws IOException { + if(args.length != 2) + System.out.println("usage: java org.catacombae.udif.UDIFInputStream "); + File inFile = new File(args[0]); + File outFile = new File(args[1]); + + RandomAccessFile inRaf = null; + FileOutputStream outFos = null; + + if(inFile.canRead()) + inRaf = new RandomAccessFile(inFile, "r"); + else { + System.out.println("Can't read from input file!"); + System.exit(0); + } + + if(!outFile.exists()) + outFos = new FileOutputStream(outFile); + else { + System.out.println("Output file already exists!"); + System.exit(0); + } + + UDIFInputStream dis = new UDIFInputStream(inRaf); + byte[] buffer = new byte[8192]; + long bytesExtracted = 0; + int bytesRead = dis.read(buffer); + while(bytesRead > 0) { + bytesExtracted += bytesRead; + outFos.write(buffer, 0, bytesRead); + bytesRead = dis.read(buffer); + } + dis.close(); + inRaf.close(); + outFos.close(); + + System.out.println("Extracted " + bytesExtracted + " bytes to \"" + outFile + "\"."); + } +} diff --git a/src/org/catacombae/dmg/udif/UDIFRandomAccessStream.java b/src/org/catacombae/dmg/udif/UDIFRandomAccessStream.java new file mode 100644 index 0000000..b3404c0 --- /dev/null +++ b/src/org/catacombae/dmg/udif/UDIFRandomAccessStream.java @@ -0,0 +1,233 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmg.udif; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import org.catacombae.io.BasicReadableRandomAccessStream; +import org.catacombae.io.ReadableFileStream; +import org.catacombae.io.ReadableRandomAccessStream; +import org.catacombae.io.RuntimeIOException; + + +public class UDIFRandomAccessStream extends BasicReadableRandomAccessStream { + /* + We have a string of data divided into blocks. Different algorithms must be applied to + different types of blocks in order to extract the data. + */ + private UDIFFile dmgFile; + private UDIFBlock[] allBlocks; + private UDIFBlock currentBlock; + private UDIFBlockInputStream currentBlockStream; + + private long length; + /** This is the pointer to the current position in the virtual file provided by this stream. */ + private long logicalFilePointer = 0; + private boolean seekCalled = false; + + private static void dbg(String s) { System.err.println(s); } + + public UDIFRandomAccessStream(RandomAccessFile raf) throws RuntimeIOException { + this(new ReadableFileStream(raf)); + } + + public UDIFRandomAccessStream(ReadableRandomAccessStream stream) throws RuntimeIOException { + this(new UDIFFile(stream)); + } + + public UDIFRandomAccessStream(UDIFFile dmgFile) throws RuntimeIOException { + this.dmgFile = dmgFile; + //dbg("dmgFile.getView().getPlist(); free memory: " + Runtime.getRuntime().freeMemory() + " total memory: " + Runtime.getRuntime().totalMemory()); + Plist plist = dmgFile.getView().getPlist(); + //dbg("before gc(): free memory: " + Runtime.getRuntime().freeMemory() + " total memory: " + Runtime.getRuntime().totalMemory()); + //Runtime.getRuntime().gc(); + //dbg("plist.getPartitions(); free memory: " + Runtime.getRuntime().freeMemory() + " total memory: " + Runtime.getRuntime().totalMemory()); + try { + PlistPartition[] partitions = plist.getPartitions(); + + int totalBlockCount = 0; + for(PlistPartition pp : partitions) { + totalBlockCount += pp.getBlockCount(); + //dbg("totalBlockCount = " + totalBlockCount); + } + allBlocks = new UDIFBlock[totalBlockCount]; + int pos = 0; + //dbg("looping for each of " + partitions.length + " partitions..."); + for(PlistPartition pp : partitions) { + UDIFBlock[] blocks = pp.getBlocks(); + //dbg("Blocks in partition: " + blocks.length); + System.arraycopy(blocks, 0, allBlocks, pos, blocks.length); + pos += blocks.length; + length += pp.getPartitionSize(); + } + if(totalBlockCount > 0) { + currentBlock = allBlocks[0]; + //dbg("Repositioning stream"); + repositionStream(); + //dbg("repositioning done."); + } + else { + throw new RuntimeException("Could not find any blocks in the DMG file..."); + } + } catch(IOException ex) { + throw new RuntimeIOException(ex); + } + } + + /** @see java.io.RandomAccessFile */ + public void close() throws RuntimeIOException {} + + /** @see java.io.RandomAccessFile */ + public long getFilePointer() throws RuntimeIOException { return logicalFilePointer; } + + /** @see java.io.RandomAccessFile */ + public long length() throws RuntimeIOException { return length; } + + /** @see java.io.RandomAccessFile */ + public int read() throws RuntimeIOException { + byte[] b = new byte[1]; + if(read(b, 0, 1) != 1) + return -1; + else + return b[0] & 0xFF; + } + + /** @see java.io.RandomAccessFile */ + public int read(byte[] b) throws RuntimeIOException { return read(b, 0, b.length); } + + /** @see java.io.RandomAccessFile */ + public int read(byte[] b, int off, int len) throws RuntimeIOException { + try { + //System.out.println("UDIFRandomAccessStream.read(b.length=" + b.length + ", " + off + ", " + len + ") {"); + if(seekCalled) { + seekCalled = false; + //System.out.print(" Repositioning stream after seek (logical file pointer: " + logicalFilePointer + ")..."); + try { repositionStream(); } + catch(RuntimeException re) { +// System.out.println("return: -1 }"); + return -1; + } + //System.out.println("done."); + } + int bytesRead = 0; + while(bytesRead < len) { + + int curBytesRead = currentBlockStream.read(b, off+bytesRead, len-bytesRead); + if(curBytesRead < 0) { + //System.out.print(" Repositioning stream..."); + try { repositionStream(); } + catch(RuntimeException re) { + if(bytesRead == 0) + bytesRead = -1; // If no bytes could be read, we must indicate that the stream has no more data + break; + } + //System.out.println("done."); + curBytesRead = currentBlockStream.read(b, off+bytesRead, len-bytesRead); + if(curBytesRead < 0) { + throw new RuntimeException("No bytes could be read, and no exception was thrown! Program error..."); +// if(bytesRead == 0) +// bytesRead = -1; // If no bytes could be read, we must indicate that the stream has no more data +// break; + } + } + +// if(curBytesRead >= 0) + bytesRead += curBytesRead; + logicalFilePointer += curBytesRead; + } + + + +// System.out.println("return: " + bytesRead + " }"); + return bytesRead; + } catch(IOException ex) { + throw new RuntimeIOException(ex); + } + } + + /** @see java.io.RandomAccessFile */ + public void seek(long pos) throws RuntimeIOException { + if(logicalFilePointer != pos) { + seekCalled = true; + logicalFilePointer = pos; + } + } + + private void repositionStream() throws RuntimeIOException { +// System.out.println(""); + try { + // if the global file pointer is not within the bounds of the current block, then find the accurate block + if(!(currentBlock.getTrueOutOffset() <= logicalFilePointer && + (currentBlock.getTrueOutOffset()+currentBlock.getOutSize()) > logicalFilePointer)) { + UDIFBlock soughtBlock = null; + for(UDIFBlock dblk : allBlocks) { + long startPos = dblk.getTrueOutOffset(); + long endPos = startPos + dblk.getOutSize(); + if(startPos <= logicalFilePointer && endPos > logicalFilePointer) { + soughtBlock = dblk; + break; + } + } + if(soughtBlock != null) { + //System.out.println("REPOSITION " + currentBlock.getBlockTypeAsString() + "(" + currentBlock.getTrueOutOffset() + "," + currentBlock.getOutSize() + ") -> " + soughtBlock.getBlockTypeAsString() + "(" + soughtBlock.getTrueOutOffset() + "," + soughtBlock.getOutSize() + ")"); +// if(soughtBlock.getTrueOutOffset() == currentBlock.getTrueOutOffset()+currentBlock.getOutSize()) +// System.out.println(" Continuous! :)"); +// else +// System.out.println(" FUCKADSFOA!!!1one"); + currentBlock = soughtBlock; + } + else + throw new RuntimeException("Trying to seek outside bounds."); + } + + currentBlockStream = UDIFBlockInputStream.getStream(dmgFile.getStream(), currentBlock); + long bytesToSkip = logicalFilePointer - currentBlock.getTrueOutOffset(); +// System.out.print(" skipping " + bytesToSkip + " bytes..."); + currentBlockStream.skip(bytesToSkip); +// System.out.println("done."); + } catch(IOException ex) { + throw new RuntimeIOException(ex); + } +// System.out.println(""); + } + + public static void main(String[] args) throws IOException { + System.out.println("UDIFRandomAccessStream simple test program"); + System.out.println("(Simply extracts the contents of a DMG file to a designated output file)"); + if(args.length != 2) + System.out.println(" ERROR: You must supply exactly two arguments: 1. the DMG, 2. the output file"); + else { + byte[] buffer = new byte[4096]; + UDIFRandomAccessStream dras = + new UDIFRandomAccessStream(new UDIFFile(new ReadableFileStream(new RandomAccessFile(args[0], "r")))); + FileOutputStream fos = new FileOutputStream(args[1]); + + long totalBytesRead = 0; + + int bytesRead = dras.read(buffer); + while(bytesRead > 0) { + totalBytesRead += bytesRead; + fos.write(buffer, 0, bytesRead); + bytesRead = dras.read(buffer); + } + System.out.println("Done! Extracted " + totalBytesRead + " bytes."); + System.out.println("Length: " + dras.length() + " bytes"); + } + } +} diff --git a/src/org/catacombae/dmgextractor/BasicUI.java b/src/org/catacombae/dmgextractor/BasicUI.java new file mode 100644 index 0000000..ad08163 --- /dev/null +++ b/src/org/catacombae/dmgextractor/BasicUI.java @@ -0,0 +1,52 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +/** + * + * @author erik + */ +abstract class BasicUI implements UserInterface { + + protected long totalProgressLength = 0; + protected long currentProgress = 0; + /** Used to prevent unneccessary updates of the progress meter. */ + protected long previousPercentage = -1; + protected final boolean verbose; + + public BasicUI(boolean verbose) { + this.verbose = verbose; + } + + /** {@inheritDoc} */ + public void setTotalProgressLength(long len) { + totalProgressLength = len; + } + + /** {@inheritDoc} */ + public void addProgressRaw(long value) { + currentProgress += value; + reportProgress((int) (currentProgress * 100 / totalProgressLength)); + } + + /** {@inheritDoc} */ + public void displayMessageVerbose(String... messageLines) { + if(verbose) + displayMessage(messageLines); + } +} diff --git a/src/BuildNumber.java b/src/org/catacombae/dmgextractor/BuildNumber.java similarity index 54% rename from src/BuildNumber.java rename to src/org/catacombae/dmgextractor/BuildNumber.java index 446a4ad..e3b9029 100644 --- a/src/BuildNumber.java +++ b/src/org/catacombae/dmgextractor/BuildNumber.java @@ -1,26 +1,25 @@ /*- * Copyright (C) 2006 Erik Larsson * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * along with this program. If not, see . */ +package org.catacombae.dmgextractor; + public class BuildNumber { //[BuildEnumerator:Opening] WARNING: The following lines are managed by an external program. Do NOT change. - public static final long BUILD_NUMBER = 130L; + public static final long BUILD_NUMBER = 438L; //[BuildEnumerator:Closing] The lines managed by an external program end here. } diff --git a/src/org/catacombae/dmgextractor/ConcatenatedIterator.java b/src/org/catacombae/dmgextractor/ConcatenatedIterator.java new file mode 100644 index 0000000..5fec069 --- /dev/null +++ b/src/org/catacombae/dmgextractor/ConcatenatedIterator.java @@ -0,0 +1,51 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +import java.util.*; + +public class ConcatenatedIterator implements Iterator { + private ArrayList> sequence = new ArrayList>(); + private int index = 0; + public void add(Iterator next) { sequence.add(next); } + + public boolean hasNext() { + if(index < sequence.size()) { + Iterator curIt = sequence.get(index); + while(!curIt.hasNext() && (index+1) < sequence.size()) + curIt = sequence.get(++index); + return curIt.hasNext(); + } + else + return false; + } + public E next() { + if(index < sequence.size()) { + Iterator curIt = sequence.get(index); + while(!curIt.hasNext() && (index+1) < sequence.size()) + curIt = sequence.get(++index); + return curIt.next(); + } + else + throw new NoSuchElementException(); + + } + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/org/catacombae/dmgextractor/DMGBlockHandlers.java b/src/org/catacombae/dmgextractor/DMGBlockHandlers.java new file mode 100644 index 0000000..49cf141 --- /dev/null +++ b/src/org/catacombae/dmgextractor/DMGBlockHandlers.java @@ -0,0 +1,73 @@ +/*- + * Copyright (C) 2006-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +import java.io.IOException; +import org.catacombae.dmg.udif.UDIFBlockInputStream; +import org.catacombae.dmg.udif.UDIFBlock; +import org.catacombae.io.RandomAccessStream; +import org.catacombae.io.ReadableRandomAccessStream; + +/** + * Please don't try to use this code with concurrent threads... :) Use external + * synchronization to protect the shared data in this class. + */ +class DMGBlockHandlers { + + private static byte[] inBuffer = new byte[0x40000]; + + /** + * Extracts an UDIFBlock describing a region of the file dmgRaf + * to the file isoRaf. If the testOnly flag is + * set, nothing is written to isoRaf (in fact, it can be null + * in this case). ui may not be null. If you do not want user + * interaction, use {@link UserInterface.NullUI}. + */ + static long processBlock(UDIFBlock block, ReadableRandomAccessStream dmgRaf, + RandomAccessStream isoRaf, boolean testOnly, UserInterface ui) + throws IOException { + + UDIFBlockInputStream is = UDIFBlockInputStream.getStream(dmgRaf, block); + long res = processStream(is, isoRaf, testOnly, ui); + is.close(); + if(res != block.getOutSize()) { + System.err.println("WARNING: Could not extract entire block! " + + "Extracted " + res + " of " + block.getOutSize() + + " bytes"); + } + return res; + } + + private static long processStream(UDIFBlockInputStream is, + RandomAccessStream isoRaf, boolean testOnly, UserInterface ui) + throws IOException { + + long totalBytesRead = 0; + int bytesRead = is.read(inBuffer); + while(bytesRead > 0) { + totalBytesRead += bytesRead; + //ui.reportProgress((int)(dmgRaf.getFilePointer()*100/dmgRaf.length())); + ui.addProgressRaw(bytesRead); + if(!testOnly) { + isoRaf.write(inBuffer, 0, bytesRead); + } + bytesRead = is.read(inBuffer); + } + return totalBytesRead; + } +} diff --git a/src/org/catacombae/dmgextractor/DMGExtractor.java b/src/org/catacombae/dmgextractor/DMGExtractor.java new file mode 100644 index 0000000..7602e85 --- /dev/null +++ b/src/org/catacombae/dmgextractor/DMGExtractor.java @@ -0,0 +1,722 @@ +/*- + * Copyright (C) 2006-2008 Erik Larsson + * (C) 2004 vu1tur (not the actual java code, but the C-code which + * has been used for reference) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +import org.catacombae.dmg.udif.Debug; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.io.RandomAccessFile; +import java.util.LinkedList; +import java.util.Iterator; +import java.util.Collections; +import javax.swing.JOptionPane; +import org.catacombae.dmg.encrypted.ReadableCEncryptedEncodingStream; +import org.catacombae.io.FileStream; +import org.catacombae.io.ReadableFileStream; +import org.catacombae.io.ReadableRandomAccessStream; +import org.catacombae.io.TruncatableRandomAccessStream; +import org.catacombae.dmg.udif.Koly; +import org.catacombae.dmg.udif.Plist; +import org.catacombae.dmg.udif.PlistPartition; +import org.catacombae.dmg.udif.UDIFBlock; +import org.catacombae.dmg.udif.UDIFDetector; +import org.xml.sax.XMLReader; + +public class DMGExtractor { + public static final String BASE_APP_NAME = "DMGExtractor"; + public static final String VERSION = "0.70"; + + public static final String APPNAME = BASE_APP_NAME + " " + VERSION; + public static final String BUILDSTRING = "(Build #" + BuildNumber.BUILD_NUMBER + ")"; + public static final String[] COPYRIGHT_MESSAGE = new String[] { + APPNAME + " " + BUILDSTRING, + "Copyright \u00A9 2006-2008 Erik Larsson ", + " based on dmg2iso, Copyright \u00A9 2004 vu1tur ", + " using the libraries:", + " iHarder Base64 Encoder/Decoder ", + " released into the public domain", + " Apache Ant bzip2 library ", + " released under The Apache Software License Version 2.0", + "", + "This program is distributed under the GNU General Public License version 3 or", + "later. See for the details.", + "" + }; + + /** + * Contains settings variables for a DMGExtractor session. + */ + private static class Session { + public String parseArgsErrorMessage = null; + public boolean useSaxParser = false; + public boolean verbose = false; + public boolean debug = false; + public boolean graphical = false; + public String startupCommand = "java DMGExtractor"; + public File dmgFile = null; + public File isoFile = null; + } + + public static void main(String[] args) throws Exception { + Session ses = null; + try { + ses = parseArgs(args); + + if(ses.debug) + ses.verbose = true; + + final UserInterface ui; + if(ses.graphical) + ui = new SwingUI(ses.verbose); + else + ui = new TextModeUI(ses.verbose); + + ui.displayMessage(COPYRIGHT_MESSAGE); + if(ses.parseArgsErrorMessage == null) { + if(ses.graphical) { + if(ses.dmgFile == null) { + ses.dmgFile = ui.getInputFileFromUser(); + } + if(ses.dmgFile != null && ses.isoFile == null) { + if(ui.getOutputConfirmationFromUser()) { + ses.isoFile = ui.getOutputFileFromUser(ses.dmgFile); + if(ses.isoFile == null) + System.exit(0); + } + } + } + + if(ses.dmgFile != null) { + String dmgFilename = null; + String isoFilename = null; + + if(ses.dmgFile != null) + dmgFilename = ses.dmgFile.getName(); + if(ses.isoFile != null) + isoFilename = ses.isoFile.getName(); + + ui.setProgressFilenames(dmgFilename, isoFilename); + extractProcedure(ses, ui); + } + else if(!ses.graphical) + printUsageInstructions(ui, ses.startupCommand, + "Error: No input file specified."); + } + else + printUsageInstructions(ui, ses.startupCommand, ses.parseArgsErrorMessage); + + } catch(Exception e) { + if(ses != null && ses.graphical) { + String stackTrace = e.toString() + "\n"; + for(StackTraceElement ste : e.getStackTrace()) + stackTrace += " " + ste.toString() + "\n"; + JOptionPane.showMessageDialog(null, "The program encountered an uncaught exception:\n" + stackTrace + + "\nCan not recover. Exiting...", "Error", JOptionPane.ERROR_MESSAGE); + } + throw e; + } + } + + private static void extractProcedure(Session ses, UserInterface ui) throws Exception { + + ui.displayMessageVerbose("Processing: \"" + ses.dmgFile + "\""); + + ReadableRandomAccessStream dmgRaf = new ReadableFileStream(new RandomAccessFile(ses.dmgFile, "r")); + + final boolean encrypted; + if(ReadableCEncryptedEncodingStream.isCEncryptedEncoding(dmgRaf)) { + encrypted = true; + char[] password; + while(true) { + password = ui.getPasswordFromUser(); + if(password == null) { + ui.displayMessage("No password specified. Can not continue..."); + System.exit(0); + } + try { + ReadableCEncryptedEncodingStream encryptionFilter = + new ReadableCEncryptedEncodingStream(dmgRaf, password); + dmgRaf = encryptionFilter; + break; + } catch(Exception e) { + ui.displayMessage("Incorrect password!"); + } + } + } + else + encrypted = false; + + TruncatableRandomAccessStream isoRaf = null; + final boolean testOnly; + if(ses.isoFile != null) { + testOnly = false; + isoRaf = new FileStream(ses.isoFile); + isoRaf.setLength(0); + ui.displayMessageVerbose("Extracting to: \"" + ses.isoFile + "\""); + } + else { + testOnly = true; + ui.displayMessageVerbose("Simulating extraction..."); + } + + if(!UDIFDetector.isUDIFEncoded(dmgRaf)) { + if(!encrypted) { + if(!ui.warning("The image you selected does not seem to be UDIF encoded or encrypted.", + "Its contents will be copied unchanged to the destination.")) { + System.exit(1); + } + } + copyData(dmgRaf, isoRaf, ui); + System.exit(0); + } + + Koly koly; + { + dmgRaf.seek(dmgRaf.length() - Koly.length()); + byte[] kolyData = new byte[512]; + int kolyDataRead = dmgRaf.read(kolyData); + if(kolyDataRead != kolyData.length) + throw new RuntimeException("Could not read koly completely. Read " + kolyDataRead + "/" + + kolyData.length + " bytes."); + else + koly = new Koly(kolyData, 0); + } + + if(ses.debug) { + ui.displayMessage("plist addresses:", + " " + koly.getPlistBegin1(), + " " + koly.getPlistBegin2()); + } + if(koly.getPlistBegin1() != koly.getPlistBegin2()) { + ui.displayMessage("WARNING: Addresses not equal! Assumption false.", + koly.getPlistBegin1() + " != " + koly.getPlistBegin2()); + //System.exit(0); + } + // if(false && plistSize != (plistEnd-plistBegin1)) { // This assumption is proven false. plistEnd means something else + // println("NOTE: plistSize field does not match plistEnd marker. Assumption false.", + // "plistSize=" + plistSize + " plistBegin1=" + plistBegin1 + " plistEnd=" + plistEnd + " plistEnd-plistBegin1=" + (plistEnd-plistBegin1)); + // } + ui.displayMessageVerbose("Jumping to address..."); + dmgRaf.seek(koly.getPlistBegin1()); + final long plistSize = koly.getPlistSize(); + if(plistSize > Integer.MAX_VALUE) + throw new RuntimeException("getPlistSize() way too large (" + plistSize + ")!"); + else if(plistSize < 0) + throw new RuntimeException("getPlistSize() way too small (" + plistSize + ")!"); + byte[] buffer = new byte[(int) koly.getPlistSize()]; + dmgRaf.read(buffer); + + Plist plist = new Plist(buffer, ses.useSaxParser); + PlistPartition[] partitions = plist.getPartitions(); + + long totalOutSize = 0; + for(PlistPartition p : partitions) { + Iterator blockIt = p.getBlockIterator(); + while(blockIt.hasNext()) + totalOutSize += blockIt.next().getOutSize(); + } + ui.displayMessageVerbose("Target size: " + totalOutSize + " bytes"); + ui.setTotalProgressLength(totalOutSize); + + byte[] zeroblock = new byte[4096]; + Util.zero(zeroblock); + + int partitionNumber = 0; + int errorsReported = 0; + int warningsReported = 0; + long totalSize = 0; + ui.reportProgress(0); + for(PlistPartition dpp : partitions) { + long partitionSize = dpp.getPartitionSize(); + totalSize += partitionSize; + + ui.displayMessageVerbose(" " + dpp.getName(), + " ID: " + dpp.getID(), + " Attributes: " + dpp.getAttributes(), + " Partition map block count: " + dpp.getBlockCount(), + " Partition size: " + partitionSize + " bytes"); + + int blockCount = 0; + Iterator blockIterator = dpp.getBlockIterator(); + while(blockIterator.hasNext()) { + if(ui.cancelSignaled()) + System.exit(0); + UDIFBlock currentBlock = blockIterator.next(); + + /* Offset of the input data for the current block in the input file */ + final int blockType = currentBlock.getBlockType(); + /* Offset of the input data for the current block in the input file */ + final long inOffset = currentBlock.getTrueInOffset(); + /* Size of the input data for the current block */ + final long inSize = currentBlock.getInSize(); + /* Offset of the output data for the current block in the output file */ + final long outOffset = currentBlock.getTrueOutOffset(); + /* Size of the output data (possibly larger than inSize because of + decompression, zero expansion...) */ + final long outSize = currentBlock.getOutSize(); + + final long trueOutOffset = currentBlock.getTrueOutOffset(); + final long trueInOffset = currentBlock.getTrueInOffset(); + final String blockTypeString = currentBlock.getBlockTypeAsString(); + + /* + String[] variableStatus = {"outOffset=" + outOffset + " outSize=" + outSize, + "inOffset=" + inOffset + " inSize=" + inSize, + "trueOutOffset=" + trueOutOffset + " trueInOffset=" + trueInOffset}; + */ + + if(ses.debug) { + ui.displayMessage( + " " + partitionNumber + ":" + blockCount + ". " + blockTypeString + " processing...", + " outOffset=" + outOffset + " outSize=" + outSize, + " inOffset=" + inOffset + " inSize=" + inSize, + " trueOutOffset=" + trueOutOffset + " trueInOffset=" + trueInOffset); + } + else + ui.displayMessageVerbose(" Processing " + blockTypeString + + " block. In: " + inSize + + " bytes. Out: " + outSize + " bytes."); + + if(!testOnly && isoRaf.getFilePointer() != trueOutOffset) { + ++warningsReported; + boolean proceed = ui.warning(blockTypeString + + " FP != trueOutOffset (" + isoRaf.getFilePointer() + + " != " + trueOutOffset + ")"); + + if(!proceed) + System.exit(0); + } + + + if(blockType == UDIFBlock.BT_ADC) { + ++errorsReported; + + if(extractionError(ui, testOnly, "BT_ADC not supported.")) + break; + else + System.exit(0); + } + else if(blockType == UDIFBlock.BT_ZLIB) { + try { + DMGBlockHandlers.processBlock(currentBlock, dmgRaf, isoRaf, testOnly, ui); + } catch(DmgException de) { + de.printStackTrace(); + String[] message = { "BT_ZLIB Could not decode..." }; + + ++errorsReported; + if(!ses.debug) { + String[] appended = { "outOffset=" + outOffset + " outSize=" + outSize, + "inOffset=" + inOffset + " inSize=" + inSize, + "trueOutOffset=" + trueOutOffset + " trueInOffset=" + trueInOffset }; + message = Util.concatenate(message, appended); + } + + if(extractionError(ui, testOnly, message)) + break; + else + System.exit(0); + } + } + else if(blockType == UDIFBlock.BT_BZIP2) { + DMGBlockHandlers.processBlock(currentBlock, dmgRaf, isoRaf, testOnly, ui); + } + else if(blockType == UDIFBlock.BT_COPY) { + DMGBlockHandlers.processBlock(currentBlock, dmgRaf, isoRaf, testOnly, ui); + } + else if(blockType == UDIFBlock.BT_ZERO) { + DMGBlockHandlers.processBlock(currentBlock, dmgRaf, isoRaf, testOnly, ui); + } + else if(blockType == UDIFBlock.BT_ZERO2) { + DMGBlockHandlers.processBlock(currentBlock, dmgRaf, isoRaf, testOnly, ui); + } + else if(blockType == UDIFBlock.BT_UNKNOWN) { + /* I have no idea what this blocktype is... but it's common, and usually + doesn't appear more than 2-3 times in a dmg. As long as its input and + output sizes are 0, there's no reason to complain... is there? */ + if(!(inSize == 0 && outSize == 0)) { + String[] message = { "Blocktype BT_UNKNOWN had non-zero sizes...", + " inSize=" + inSize + ", outSize=" + outSize, + " Please contact the author of the program to report this bug!" }; + + ++errorsReported; + if(!ses.debug) { + String[] appended = { "outOffset=" + outOffset + " outSize=" + outSize, + "inOffset=" + inOffset + " inSize=" + inSize, + "trueOutOffset=" + trueOutOffset + " trueInOffset=" + trueInOffset }; + message = Util.concatenate(message, appended); + } + + if(extractionError(ui, testOnly, message)) + break; + else + System.exit(0); + } + } + else if(blockType == UDIFBlock.BT_END) { + // Nothing needs to be done in this pass. + } + else { + if(inSize == 0 && outSize == 0) { + ui.warning("previously unseen blocktype " + blockType + " [0x" + Integer.toHexString(blockType) + "]", + ("outOffset=" + outOffset + " outSize=" + outSize + + " inOffset=" + inOffset + " inSize=" + inSize), + "As inSize and outSize is 0 (block is a marker?), we can try to continue the operation..."); + ++warningsReported; + } + else { + String[] message = { "previously unseen blocktype " + blockType + " [0x" + Integer.toHexString(blockType) + "]", + "outOffset=" + outOffset + " outSize=" + outSize + " inOffset=" + inOffset + " inSize=" + inSize, + "CRITICAL. inSize and/or outSize are not 0!" }; + //errorMessage("previously unseen blocktype " + blockType + " [0x" + Integer.toHexString(blockType) + "]", + // (" outOffset=" + outOffset + " outSize=" + outSize + + // " inOffset=" + inOffset + " inSize=" + inSize), + // " CRITICAL. inSize and/or outSize are not 0!"); + ++errorsReported; + if(!ses.debug) { + String[] appended = { "outOffset=" + outOffset + " outSize=" + outSize, + "inOffset=" + inOffset + " inSize=" + inSize, + "trueOutOffset=" + trueOutOffset + " trueInOffset=" + trueInOffset }; + message = Util.concatenate(message, appended); + } + + if(extractionError(ui, testOnly, message)) + break; + else + System.exit(0); + } + + } + + ++blockCount; + } + ++partitionNumber; + } + + ui.reportProgress(100); + ui.reportFinished(isoRaf == null, errorsReported, warningsReported, totalSize); + + if(!ses.debug) { + if(isoRaf != null) + isoRaf.close(); + dmgRaf.close(); + } + else { + if(isoRaf != null) + isoRaf.close(); + ConcatenatedIterator cit = new ConcatenatedIterator(); + for(PlistPartition dpp : partitions) + cit.add(dpp.getBlockIterator()); + + LinkedList blocks = new LinkedList(); + while(cit.hasNext()) { + UDIFBlock b = cit.next(); + if(b.getInSize() == 0) + continue; // Not relevant to the calculation + else if(b.getInSize() > 0) + blocks.add(b); + else + throw new RuntimeException("Negative inSize! inSize=" + b.getInSize()); + } + Collections.sort(blocks); + + LinkedList merged = mergeBlocks(blocks.iterator()); + + String[] mergedRegions = new String[] { "Merged regions (size: " + merged.size() + "):" }; + for(UDIFBlock b : merged) + Util.concatenate(mergedRegions, " " + b.getTrueInOffset() + " - " + + (b.getTrueInOffset() + b.getInSize())); + Util.concatenate(mergedRegions, "", "Extracting the regions not " + + "containing block data from source file..."); + ui.displayMessage(mergedRegions); + int i = 1; + Iterator mergedIt = merged.iterator(); + UDIFBlock previous = null; + if(merged.size() > 0 && merged.getFirst().getTrueInOffset() == 0) + previous = mergedIt.next(); + + while(mergedIt.hasNext() || previous != null) { + UDIFBlock b = null; + if(mergedIt.hasNext()) + b = mergedIt.next(); + //else + // b = + if(b == null || b.getTrueInOffset() > 0) { + long offset; + int size; + if(previous == null) { + offset = 0; + size = (int) (b.getInOffset()); + if(size == 0) + continue; // First part may be empty, then we just continue + } + else if(b == null) { + offset = previous.getTrueInOffset() + previous.getInSize(); + size = (int) (dmgRaf.length() - offset); + if(size == 0) + break; // Last part may be empty (in theory, though not in practice with true UDIF files) + } + else { + offset = previous.getTrueInOffset() + previous.getInSize(); + size = (int) (b.getInOffset() - offset); + } + + String filename = "[" + ses.dmgFile.getName() + "]-" + i++ + "-[" + offset + "-" + (offset + size) + "].region"; + ui.displayMessage(" " + new File(filename).getCanonicalPath() + " (" + size + " bytes)..."); + FileOutputStream curFos = new FileOutputStream(new File(filename)); + + if(size < 0) { + ui.error("ERROR: Negative array size (" + size + ")...", + " current:", + " " + b.toString(), + " previous:", + " " + previous.toString()); + } + + byte[] data = new byte[size]; + dmgRaf.seek(offset); + dmgRaf.read(data); + curFos.write(data); + curFos.close(); + } + previous = b; + } + dmgRaf.close(); + ui.displayMessage("Done!"); + } + } + + private static void copyData(ReadableRandomAccessStream inStream, + TruncatableRandomAccessStream outStream, UserInterface ui) { + byte[] buffer = new byte[64*1024]; + + ui.setTotalProgressLength(inStream.length()); + long totalBytesCopied = 0; + inStream.seek(0); + int bytesRead = inStream.read(buffer); + while(bytesRead > 0 && !ui.cancelSignaled()) { + if(outStream != null) + outStream.write(buffer, 0, bytesRead); + ui.addProgressRaw(bytesRead); + totalBytesCopied += bytesRead; + + bytesRead = inStream.read(buffer); + } + + ui.reportProgress(100); + ui.reportFinished(outStream == null, 0, 0, totalBytesCopied); + } + + + /** + * + * @param message + * @param ui + * @param testOnly + * @return true if the extraction should proceed, false if it should not. + */ + private static boolean extractionError(UserInterface ui, boolean testOnly, + String... message) { + if(!testOnly) { + ui.error(message); + return false; + } + else { + message = Util.concatenate(message, "The program is " + + "run in testing mode, so we can continue..."); + return ui.warning(message); + } + } + + private static Session parseArgs(String[] args) { + Session ses = new Session(); + try { + /* Take care of the options... */ + int i; + for(i = 0; i < args.length; ++i) { + String cur = args[i]; + //System.err.println("Parsing argument: \"" + cur + "\""); + if(!cur.startsWith("-")) + break; + else if(cur.equals("-gui")) + ses.graphical = true; + else if(cur.equals("-saxparser")) + ses.useSaxParser = true; + else if(cur.equals("-v")) + ses.verbose = true; + else if(cur.equals("-debug")) { + Debug.debug = true; + ses.debug = true; + } + else if(cur.equals("-startupcommand")) { + ses.startupCommand = args[i + 1]; + ++i; + } + else { + ses.parseArgsErrorMessage = "Invalid argument: " + cur; + return ses; + } + } + + /* + * This isn't a very good hack... clearly the invoker is doing + * something wrong when this situation comes up. + */ + int emptyTrailingEntries = 0; + for(int j = i; j < args.length; ++j) { + if(args[i].equals("")) + ++emptyTrailingEntries; + } + //System.err.println("empty: " + emptyTrailingEntries); + + if(i != args.length && (args.length - i) != emptyTrailingEntries) { + ses.dmgFile = new File(args[i++]); + if(ses.dmgFile.exists()) { + + if(i <= args.length - 1 && !args[i].trim().equals("")) + ses.isoFile = new File(args[i++]); + + if(i != args.length) { + if(!args[i].trim().equals("")) + ses.parseArgsErrorMessage = "Invalid argument: " + + args[i]; + } + } + else { + ses.parseArgsErrorMessage = + "Input file \"" + ses.dmgFile + "\" not found!"; + } + } + else if(!ses.graphical) { + ses.parseArgsErrorMessage = "Error: No input file specified."; + } + } catch(Exception e) { + e.printStackTrace(); + ses.parseArgsErrorMessage = "Unhandled exception: " + e.toString() + + " (see console for stacktrace)"; + } + return ses; + } + + private static void printUsageInstructions(UserInterface ui, String startupCommand) { + printUsageInstructions(ui, startupCommand, null); + } + + private static void printUsageInstructions(UserInterface ui, String startupCommand, String errorMessage) { + String[] prefixMessage = new String[0]; + + if(errorMessage != null) + prefixMessage = Util.concatenate(prefixMessage, errorMessage); + + // 80 char ruler: + // <--------------------------------------------------------------------------------> + String[] mainMessage = new String[] { + " usage: " + startupCommand + " [options] []", + "", + " If an output file is not supplied, the program will simulate an extraction", + " (useful for detecting errors in dmg-files).", + "", + " options:", + " -v verbose operation... for finding out what went wrong", + " -saxparser use the standard SAX parser for XML processing instead of", + " the APX parser (will connect to Apple's website for DTD", + " validation)", + " -gui starts the program in graphical mode", + " -debug performs unspecified debug operations (only intended for", + " development use)", + "" + }; + + + ui.displayMessage(Util.concatenate(prefixMessage, mainMessage)); + + } + + private static void printSAXParserInfo(XMLReader saxParser, PrintStream ps, String prefix) throws Exception { + ps.println(prefix + "Features:"); + ps.println(prefix + " external-general-entities: " + saxParser.getFeature("http://xml.org/sax/features/external-general-entities")); + ps.println(prefix + " external-parameter-entities: " + saxParser.getFeature("http://xml.org/sax/features/external-parameter-entities")); + ps.println(prefix + " is-standalone: " + saxParser.getFeature("http://xml.org/sax/features/is-standalone")); + ps.println(prefix + " lexical-handler/parameter-entities: " + saxParser.getFeature("http://xml.org/sax/features/lexical-handler/parameter-entities")); + //ps.println(prefix + " parameter-entities: " + saxParser.getFeature("http://xml.org/sax/features/parameter-entities")); + ps.println(prefix + " namespaces: " + saxParser.getFeature("http://xml.org/sax/features/namespaces")); + ps.println(prefix + " namespace-prefixes: " + saxParser.getFeature("http://xml.org/sax/features/namespace-prefixes")); + ps.println(prefix + " resolve-dtd-uris: " + saxParser.getFeature("http://xml.org/sax/features/resolve-dtd-uris")); + ps.println(prefix + " string-interning: " + saxParser.getFeature("http://xml.org/sax/features/string-interning")); + ps.println(prefix + " unicode-normalization-checking: " + saxParser.getFeature("http://xml.org/sax/features/unicode-normalization-checking")); + ps.println(prefix + " use-attributes2: " + saxParser.getFeature("http://xml.org/sax/features/use-attributes2")); + ps.println(prefix + " use-locator2: " + saxParser.getFeature("http://xml.org/sax/features/use-locator2")); + ps.println(prefix + " use-entity-resolver2: " + saxParser.getFeature("http://xml.org/sax/features/use-entity-resolver2")); + ps.println(prefix + " validation: " + saxParser.getFeature("http://xml.org/sax/features/validation")); + ps.println(prefix + " xmlns-uris: " + saxParser.getFeature("http://xml.org/sax/features/xmlns-uris")); + ps.println(prefix + " xml-1.1: " + saxParser.getFeature("http://xml.org/sax/features/xml-1.1")); + + ps.println("Properties: "); + ps.println(prefix + " declaration-handler: " + saxParser.getProperty("http://xml.org/sax/properties/declaration-handler")); + ps.println(prefix + " document-xml-version: " + saxParser.getProperty("http://xml.org/sax/properties/document-xml-version")); + ps.println(prefix + " dom-node: " + saxParser.getProperty("http://xml.org/sax/properties/dom-node")); + ps.println(prefix + " lexical-handler: " + saxParser.getProperty("http://xml.org/sax/properties/lexical-handler")); + ps.println(prefix + " xml-string: " + saxParser.getProperty("http://xml.org/sax/properties/xml-string")); + + //ps.println("isValidating: " + saxParser.isValidating()); + } + + private static LinkedList mergeBlocks(LinkedList blockList) { + Iterator it = blockList.iterator(); + return mergeBlocks(it); + } + + private static LinkedList mergeBlocks(Iterator it) { + LinkedList result = new LinkedList(); + UDIFBlock previous = it.next(); + while(previous.getInSize() == 0 && it.hasNext()) { + //System.err.println("Skipping: " + previous.toString()); + previous = it.next(); + } + //System.err.println("First block in merge sequence: " + previous.toString()); + + UDIFBlock current; + while(it.hasNext()) { + current = it.next(); + if(current.getInSize() != 0) { + if(current.getTrueInOffset() == previous.getTrueInOffset() + previous.getInSize()) { + UDIFBlock mergedBlock = new UDIFBlock( + previous.getBlockType(), + previous.getReserved(), + previous.getOutOffset(), + previous.getOutSize() + current.getOutSize(), + previous.getInOffset(), + previous.getInSize() + current.getInSize(), + previous.getOutOffsetCompensation(), + previous.getInOffsetCompensation()); + previous = mergedBlock; + } + else { + result.addLast(previous); + previous = current; + } + } + } + result.addLast(previous); + return result; + } + +} + diff --git a/src/DMGExtractorGraphical.java b/src/org/catacombae/dmgextractor/DMGExtractorGraphical.java similarity index 52% rename from src/DMGExtractorGraphical.java rename to src/org/catacombae/dmgextractor/DMGExtractorGraphical.java index 3f37ee7..8e131aa 100644 --- a/src/DMGExtractorGraphical.java +++ b/src/org/catacombae/dmgextractor/DMGExtractorGraphical.java @@ -1,27 +1,27 @@ /*- * Copyright (C) 2006 Erik Larsson * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * along with this program. If not, see . */ +package org.catacombae.dmgextractor; + public class DMGExtractorGraphical { public static void main(String[] args) throws Exception { - String[] newargs = new String[1]; + String[] newargs = new String[args.length+1]; newargs[0] = "-gui"; + System.arraycopy(args, 0, newargs, 1, args.length); DMGExtractor.main(newargs); } } diff --git a/src/org/catacombae/dmgextractor/DmgException.java b/src/org/catacombae/dmgextractor/DmgException.java new file mode 100644 index 0000000..358d767 --- /dev/null +++ b/src/org/catacombae/dmgextractor/DmgException.java @@ -0,0 +1,27 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +public class DmgException extends RuntimeException { + public DmgException() { + super(); + } + public DmgException(String message) { + super(message); + } +} diff --git a/src/SimpleFileFilter.java b/src/org/catacombae/dmgextractor/SimpleFileFilter.java similarity index 74% rename from src/SimpleFileFilter.java rename to src/org/catacombae/dmgextractor/SimpleFileFilter.java index 3cbf88b..f52b056 100644 --- a/src/SimpleFileFilter.java +++ b/src/org/catacombae/dmgextractor/SimpleFileFilter.java @@ -1,23 +1,22 @@ /*- * Copyright (C) 2006 Erik Larsson * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * along with this program. If not, see . */ +package org.catacombae.dmgextractor; + import java.util.*; import java.io.*; diff --git a/src/org/catacombae/dmgextractor/SimplerFileFilter.java b/src/org/catacombae/dmgextractor/SimplerFileFilter.java new file mode 100644 index 0000000..8b33310 --- /dev/null +++ b/src/org/catacombae/dmgextractor/SimplerFileFilter.java @@ -0,0 +1,60 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +import java.io.File; +import javax.swing.filechooser.FileFilter; + + +/** + * Even simpler file filter as it only allows one extension.
+ * Directories are always accepted. + * + * @author Erik Larsson + */ +public class SimplerFileFilter extends FileFilter { + + private String extension; + private String description; + + public SimplerFileFilter(String extension, String description) { + this.extension = extension; + this.description = description; + } + + /** {@inheritDoc} */ + @Override + public boolean accept(File f) { + if(f.isDirectory()) + return true; + else if(f.getName().endsWith(extension)) + return true; + else + return false; + } + + /** {@inheritDoc} */ + @Override + public String getDescription() { return description; } + + /** + * Returns the extension that this file filter matches. + * @return the extension that this file filter matches. + */ + public String getExtension() { return extension; } +} diff --git a/src/org/catacombae/dmgextractor/SwingUI.java b/src/org/catacombae/dmgextractor/SwingUI.java new file mode 100644 index 0000000..2661459 --- /dev/null +++ b/src/org/catacombae/dmgextractor/SwingUI.java @@ -0,0 +1,257 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +import java.io.File; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.ProgressMonitor; +import javax.swing.filechooser.FileFilter; +import org.catacombae.dmgextractor.ui.PasswordDialog; + +/** + * User interface implementation using Java Swing. + * + * @author Erik Larsson + */ +class SwingUI extends BasicUI implements UserInterface { + + private ProgressMonitor progmon = null; + private String inputFilename = null; + private String outputFilename = null; + + public SwingUI(boolean verbose) { + super(verbose); + + //System.setProperty("swing.aatext", "true"); //Antialiased text + try { + javax.swing.UIManager.setLookAndFeel( + javax.swing.UIManager.getSystemLookAndFeelClassName()); + } catch(Exception e) { + // No big deal. Go with default. + } + } + + /** {@inheritDoc} */ + public boolean warning(String... messageLines) { + StringBuilder sb = new StringBuilder(); + + boolean firstLine = true; + for(String s : messageLines) { + if(!firstLine) + sb.append("\n"); + else + firstLine = false; + sb.append(s); + } + + sb.append("\n\nDo you want to continue?"); + + int res = JOptionPane.showConfirmDialog(null, sb.toString(), + DMGExtractor.APPNAME + ": Warning", JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + return res == JOptionPane.YES_OPTION; + } + + /** {@inheritDoc} */ + public void error(String... messageLines) { + StringBuilder sb = new StringBuilder(); + + boolean firstLine = true; + for(String s : messageLines) { + if(!firstLine) + sb.append("\n"); + else + firstLine = false; + sb.append(s); + } + + JOptionPane.showMessageDialog(null, sb.toString(), + DMGExtractor.APPNAME + ": Error", JOptionPane.ERROR_MESSAGE); + } + + /** {@inheritDoc} */ + public void reportProgress(int progressPercentage) { + if(progressPercentage != previousPercentage) { + if(progmon == null) { + final String progmonText; + if(outputFilename != null) { + progmonText = "Extracting \"" + + inputFilename + "\" to\n \"" + outputFilename + "\"..."; + } + else { + progmonText = "Simulating extraction of \"" + + inputFilename + "\"..."; + } + progmon = new ProgressMonitor(null, progmonText, "0%", 0, 100); + progmon.setProgress(0); + progmon.setMillisToPopup(0); + } + progmon.setProgress((int) progressPercentage); + progmon.setNote(progressPercentage + "%"); + } + } + + /** {@inheritDoc} */ + public void reportFinished(boolean simulation, int errorsReported, int warningsReported, long totalExtractedSize) { + StringBuilder message = new StringBuilder(); + if(simulation) + message.append("Simulation"); + else + message.append("Extraction"); + message.append(" complete! "); + if(errorsReported != 0) + message.append(errorsReported).append(" errors reported"); + else + message.append("No errors reported"); + + if(warningsReported != 0) + message.append(" (").append(warningsReported).append(" warnings emitted)"); + + message.append(".\nSize of extracted data: ").append(totalExtractedSize); + message.append(" bytes"); + + progmon.close(); + JOptionPane.showMessageDialog(null, message.toString(), + DMGExtractor.APPNAME, JOptionPane.INFORMATION_MESSAGE); + System.exit(0); // TODO check this + } + + /** {@inheritDoc} */ + public boolean cancelSignaled() { + return progmon != null && progmon.isCanceled(); + } + + /** {@inheritDoc} */ + public void displayMessage(String... messageLines) { + StringBuilder resultString = new StringBuilder(); + boolean firstIteration = true; + for(String s : messageLines) { + if(!firstIteration) + resultString.append("\n"); + else + firstIteration = false; + + resultString.append(s); + } + + JOptionPane.showMessageDialog(null, resultString.toString(), + DMGExtractor.APPNAME, JOptionPane.INFORMATION_MESSAGE); + } + + /** {@inheritDoc} */ + public File getInputFileFromUser() { + SimpleFileFilter sff = new SimpleFileFilter(); + sff.addExtension("dmg"); + sff.setDescription("Mac OS X disk images (*.dmg)"); + JFileChooser jfc = new JFileChooser(); + jfc.setFileFilter(sff); + jfc.setMultiSelectionEnabled(false); + jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); + jfc.setDialogTitle("Choose the .dmg file to read..."); + while(true) { + if(jfc.showDialog(null, "Open") == JFileChooser.APPROVE_OPTION) { + File f = jfc.getSelectedFile(); + if(f.exists()) { + return f; + } + else + JOptionPane.showMessageDialog(null, "The file does not exist! Choose again...", + "Error", JOptionPane.ERROR_MESSAGE); + } + else + return null; + } + } + + /** {@inheritDoc} */ + public boolean getOutputConfirmationFromUser() { + return JOptionPane.showConfirmDialog(null, "Do you want to specify an output file?\n" + + "(Choosing \"No\" means the extraction will only be simulated,\n" + + "which can be useful for detecting errors in .dmg files...)", + "Confirmation", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; + } + + /** {@inheritDoc} */ + public File getOutputFileFromUser(File inputFile) { + final String msgFileExists = "The file already exists. Do you want to overwrite?"; + + JFileChooser jfc = new JFileChooser(); + jfc.setMultiSelectionEnabled(false); + jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); + + SimplerFileFilter defaultFileFilter = new SimplerFileFilter(".iso", "CD/DVD image (*.iso)"); + jfc.addChoosableFileFilter(defaultFileFilter); + jfc.addChoosableFileFilter(new SimplerFileFilter(".img", "Raw image (*.img)")); + jfc.addChoosableFileFilter(new SimplerFileFilter(".bin", "Binary file (*.bin)")); + jfc.addChoosableFileFilter(new SimplerFileFilter(".dmg", "Mac OS X read/write disk image (*.dmg)")); + jfc.setFileFilter(defaultFileFilter); + jfc.setDialogTitle("Select your output file"); + + if(inputFile != null) { + String name = inputFile.getName(); + String defaultOutName = name; + int lastDotIndex = defaultOutName.lastIndexOf("."); + if(lastDotIndex >= 0) { + defaultOutName = defaultOutName.substring(0, lastDotIndex); + } + jfc.setSelectedFile(new File(inputFile.getParentFile(), + defaultOutName)); + } + + while(true) { + if(jfc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { + File selectedFile = jfc.getSelectedFile(); + final File saveFile; + FileFilter selectedFileFilter = jfc.getFileFilter(); + if(selectedFileFilter instanceof SimplerFileFilter) { + SimplerFileFilter sff = (SimplerFileFilter) selectedFileFilter; + if(!selectedFile.getName().endsWith(sff.getExtension())) + saveFile = new File(selectedFile.getParentFile(), selectedFile.getName() + sff.getExtension()); + else + saveFile = selectedFile; + } + else { + saveFile = selectedFile; + } + + if(!saveFile.exists()) + return saveFile; + else if(JOptionPane.showConfirmDialog(null, msgFileExists, + "Confirmation", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + return saveFile; + } + } + else + return null; + } + } + + /** {@inheritDoc} */ + public char[] getPasswordFromUser() { + return PasswordDialog.showDialog(null, "Reading encrypted disk image...", + "You need to enter a password to unlock this disk image:"); + } + + public void setProgressFilenames(String inputFilename, String outputFilename) { + this.inputFilename = inputFilename; + this.outputFilename = outputFilename; + } +} diff --git a/src/org/catacombae/dmgextractor/TextModeUI.java b/src/org/catacombae/dmgextractor/TextModeUI.java new file mode 100644 index 0000000..0924e26 --- /dev/null +++ b/src/org/catacombae/dmgextractor/TextModeUI.java @@ -0,0 +1,199 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; + +/** + * User interface implementation using plain old System.in and System.out for a + * text-based UI. + * + * @author Erik Larsson + */ +class TextModeUI extends BasicUI implements UserInterface { + + /** A string containing 79 backspace characters. */ + public static final String BACKSPACE79 = + "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + + "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + + "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + + "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; + private final PrintStream ps = System.out; + private final BufferedReader stdin = + new BufferedReader(new InputStreamReader(System.in)); + + public TextModeUI(boolean verbose) { + super(verbose); + } + + /** {@inheritDoc} */ + public boolean warning(String... messageLines) { + if(messageLines.length > 0) { + ps.println("WARNING: " + messageLines[0]); + for(int i = 1; i < messageLines.length; ++i) + ps.println(" " + messageLines[i]); + } + return true; + } + + /** {@inheritDoc} */ + public void error(String... messageLines) { + if(messageLines.length > 0) { + ps.println("!------>ERROR: " + messageLines[0]); + for(int i = 1; i < messageLines.length; ++i) + ps.println(" " + messageLines[i]); + } + } + + /** {@inheritDoc} */ + public void reportProgress(int progressPercentage) { + if(progressPercentage != previousPercentage) { + previousPercentage = progressPercentage; + ps.println("--->Progress: " + progressPercentage + "%"); + } + } + + /** {@inheritDoc} */ + public void reportFinished(boolean simulation, int errorsReported, int warningsReported, long totalExtractedSize) { + StringBuilder summary = new StringBuilder(); + if(errorsReported != 0) + summary.append(errorsReported).append(" errors reported"); + else + summary.append("No errors reported"); + + if(warningsReported != 0) + summary.append(" (").append(warningsReported).append(" warnings emitted)"); + + summary.append("."); + + ps.println(); + ps.println(summary.toString()); + if(verbose) + ps.println("Size of extracted data: " + totalExtractedSize + " bytes"); + } + + /** {@inheritDoc} */ + public boolean cancelSignaled() { + return false; + } + + /** {@inheritDoc} */ + public void displayMessage(String... messageLines) { + //ps.print(BACKSPACE79); + for(String s : messageLines) + ps.println(s); + if(messageLines.length < 1) + ps.println(); + } + + /** {@inheritDoc} */ + public File getInputFileFromUser() { + /* + //String s = ""; + while(true) { + printCurrentLine("Please specify the path to the dmg file to extract from: "); + File f = new File(stdin.readLine().trim()); + while(!f.exists()) { + println("File does not exist!"); + printCurrentLine("Please specify the path to the dmg file to extract from: "); + f = new File(stdin.readLine().trim()); + } + return f; + } + */ + + // Text mode operation is not interactive anymore. + return null; + } + + /** {@inheritDoc} */ + public boolean getOutputConfirmationFromUser() { + /* + String s = ""; + while(true) { + printCurrentLine("Do you want to specify an output file (y/n)? "); + s = stdin.readLine().trim(); + if(s.equalsIgnoreCase("y")) + return true; + else if(s.equalsIgnoreCase("n")) + return false; + } + */ + + // Text mode operation is not interactive anymore. + return false; + } + + /** {@inheritDoc} */ + public File getOutputFileFromUser(File inputFile) { + /* + final String msg1 = "Please specify the path of the iso file to extract to: "; + final String msg2 = "The file already exists. Do you want to overwrite?"; + while(true) { + printCurrentLine(msg1); + File f = new File(stdin.readLine().trim()); + while(f.exists()) { + while(true) { + printCurrentLine(msg2 + " (y/n)? "); + String s = stdin.readLine().trim(); + if(s.equalsIgnoreCase("y")) + return f; + else if(s.equalsIgnoreCase("n")) + break; + } + printCurrentLine(msg1); + f = new File(stdin.readLine().trim()); + } + return f; + } + */ + + // Text mode operation is not interactive anymore. + return null; + } + + /** {@inheritDoc} */ + public char[] getPasswordFromUser() { + displayMessage("The disk image you are trying to extract is encrypted."); + try { + String reply = prompt("Please enter password: "); + if(reply != null) + return reply.toCharArray(); + else + return null; + } catch(IOException ioe) { + ioe.printStackTrace(); + return null; + } + } + + private String prompt(String s) throws IOException { + ps.print(s); + return stdin.readLine(); + } + + public void setProgressFilenames(String inputFilename, String outputFilename) { + /* + * We currently don't act on this. + */ + } +} diff --git a/src/org/catacombae/dmgextractor/UserInterface.java b/src/org/catacombae/dmgextractor/UserInterface.java new file mode 100644 index 0000000..829d5ba --- /dev/null +++ b/src/org/catacombae/dmgextractor/UserInterface.java @@ -0,0 +1,161 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +import java.io.File; + +interface UserInterface { + + public boolean cancelSignaled(); + + public void displayMessageVerbose(String... messageLines); + + public File getInputFileFromUser(); + + public boolean getOutputConfirmationFromUser(); + + public File getOutputFileFromUser(File inputFile); + + public char[] getPasswordFromUser(); + + /** + * If outputFilename is null, is would mean that a simulation is in progress. + * @param inputFilename + * @param outputFilename + */ + public void setProgressFilenames(String inputFilename, String outputFilename); + + /** + * Unconditionally displays a message to the user, to inform about certain + * things that happen in a program.
+ * This method may block execution, so use it sparsely. + * + * @param messageLines the message, line by line. + */ + public void displayMessage(String... messageLines); + + /** + * Issues a warning message to the user. Returns true if the process should + * proceed despite the warning, and false if the process should be + * aborted.
+ * This method may block execution, so use it sparsely. + * + * @param messageLines the message, line by line. + * @return true if the process should proceed despite the warning, and + * false if the process should be aborted. + */ + public boolean warning(String... messageLines); + + /** + * Issues an error message to the user.
+ * This method may block execution, so use it sparsely. + * + * @param messageLines the message, line by line. + */ + public void error(String... messageLines); + + /** + * This method should be called to bring up a summary after a finished + * extraction/simulation process. + * + * @param simulation set to true when the extraction was only simulated, + * false for a real extraction. + * @param errorsReported the number of errors encountered during the + * extraction. + * @param warningsReported the number of warnings encountered during the + * extraction. + * @param totalExtractedSize the outgoing data size, i.e. the data that was + * written (or should have been written, in the case of a simulation). + */ + public void reportFinished(boolean simulation, int errorsReported, int warningsReported, long totalExtractedSize); + + /** + * Sets the current progress value to a specified percentage. This method + * should not be used except for when the progress meter is to be reset or + * set forcibly to 100% when the process has completed. + * + * @param progressPercentage the percentage to set the progress to (range + * 0-100). + */ + public void reportProgress(int progressPercentage); + + /** + * Used in conjunction with addProgressRaw(...), and + * denotes the total length of the data on which we monitor progress. + * + * @param len the number of bytes of data that is the maximum value for + * raw progress. + */ + public void setTotalProgressLength(long len); + + /** + * Adds progress as raw bytes, instead of setting it as percentage. The + * percentage will automatically be calculated from the value previously + * set through setTotalProgressLength(). + * + * @param value the byte value to add to the current progress. + */ + public void addProgressRaw(long value); + + static class NullUI extends BasicUI { + + public NullUI() { + super(false); + } + + public void reportProgress(int progressPercentage) { + } + + public void displayMessage(String... messageLines) { + } + + public boolean warning(String... messageLines) { + return true; + } + + public void error(String... messageLines) { + } + + public void reportFinished(boolean simulation, int errorsReported, int warningsReported, long totalExtractedSize) { + } + + public boolean cancelSignaled() { + return false; + } + + public File getInputFileFromUser() { + return null; + } + + public boolean getOutputConfirmationFromUser() { + return false; + } + + public File getOutputFileFromUser(File inputFile) { + return null; + } + + public char[] getPasswordFromUser() { + return null; + } + + public void setProgressFilenames(String inputFilename, String outputFilename) { + throw new UnsupportedOperationException("Not supported yet."); + } + } +} diff --git a/src/org/catacombae/dmgextractor/Util.java b/src/org/catacombae/dmgextractor/Util.java new file mode 100644 index 0000000..6e261b9 --- /dev/null +++ b/src/org/catacombae/dmgextractor/Util.java @@ -0,0 +1,22 @@ +/*- + * Copyright (C) 2006-2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor; + +public class Util extends org.catacombae.util.Util { + //public static int sectorSize = 0x800; +} diff --git a/src/org/catacombae/dmgextractor/io/.cvsignore b/src/org/catacombae/dmgextractor/io/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/org/catacombae/dmgextractor/io/ByteCountInputStream.java b/src/org/catacombae/dmgextractor/io/ByteCountInputStream.java new file mode 100644 index 0000000..edf4d80 --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/ByteCountInputStream.java @@ -0,0 +1,107 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Filter stream that records information about how many bytes have been + * processed (either read or skipped). + */ +public class ByteCountInputStream extends InputStream { + private long bytePos = 0; + private InputStream is; + + /** + * Creates a new ByteCountInputStream wrapping is. + * + * @param is the underlying InputStream. + */ + public ByteCountInputStream(InputStream is) { + this.is = is; + } + + /** + * Returns the number of bytes that have been read since the creation + * of this filter stream. + * + * @return the number of bytes that have been read. + */ + public long getBytesRead() { return bytePos; } + + /** {@inheritDoc} */ + @Override + public int available() throws IOException { return is.available(); } + + /** {@inheritDoc} */ + @Override + public void close() throws IOException { is.close(); } + + /** {@inheritDoc} */ + @Override + public void mark(int readLimit) { throw new UnsupportedOperationException("Mark/reset not supported"); } + + /** {@inheritDoc} */ + @Override + public boolean markSupported() { return false; } + + /** {@inheritDoc} */ + @Override + public int read() throws IOException { + //System.out.println("read();"); + int res = is.read(); + if(res > 0) + ++bytePos; + return res; + } + + /** {@inheritDoc} */ + @Override + public int read(byte[] b) throws IOException { + //System.out.println("read(b.length=" + b.length + ");"); + int res = is.read(b); + if(res > 0) + bytePos += res; + return res; + } + + /** {@inheritDoc} */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + //System.out.println("read(b.length=" + b.length + ", " + off + ", " + len + ");"); + int res = is.read(b, off, len); + if(res > 0) + bytePos += res; + return res; + } + + /** {@inheritDoc} */ + @Override + public void reset() throws IOException { throw new UnsupportedOperationException("Mark/reset not supported"); } + + /** {@inheritDoc} */ + @Override + public long skip(long n) throws IOException { + System.out.println("skip(" + n + ");"); + long res = is.skip(n); + if(res > 0) + bytePos += res; + return res; + } +} diff --git a/src/org/catacombae/dmgextractor/io/CharArrayBuilder.java b/src/org/catacombae/dmgextractor/io/CharArrayBuilder.java new file mode 100644 index 0000000..44750bc --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/CharArrayBuilder.java @@ -0,0 +1,54 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.io; +// /* unused... remnants of old ideas */ +public class CharArrayBuilder { + /* + private int growConstant; + private char[] backingArray; + private int pos; + + public CharArrayBuilder() { this(512); } + public CharArrayBuilder(int capacity) { + growConstant = capacity; + backingArray = new char[capacity]; + pos = 0; + } + + public void put(char c) { + if(pos == backingArray.length) + growArray(); + backingArray[pos++] = c; + } + + public byte[] clearBuffer() { + byte[] result = new byte[pos]; + System.arraycopy(backingArray, 0, result, 0, result.length); + backingArray = new byte[growConstant]; + pos = 0; + return result; + } + + private void growArray() { + byte[] oldArray = backingArray; + byte[] newArray = new byte[backingArray.length+growConstant]; + System.arraycopy(oldArray, 0, newArray, 0, oldArray.length); + backingArray = newArray; + } + */ +} diff --git a/src/org/catacombae/dmgextractor/io/CharByCharReader.java b/src/org/catacombae/dmgextractor/io/CharByCharReader.java new file mode 100644 index 0000000..662ceca --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/CharByCharReader.java @@ -0,0 +1,72 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.io; +import java.io.*; +import java.nio.*; +import java.nio.charset.*; + +public class CharByCharReader extends Reader { + private InputStream is; + private Charset cs; + private CharsetDecoder cdec; + private byte[] tempBuffer; + private int tempBufferPtr = 0; + + //private CharArrayBuilder cab = new CharArrayBuilder(); + + /* The assumption we make here is that a number of bytes define a Unicode character. */ + public CharByCharReader(InputStream is, Charset cs) { + this.is = is; + this.cs = cs; + this.cdec = cs.newDecoder(); + tempBuffer = new byte[(int)Math.ceil(cdec.maxCharsPerByte())]; + } + + public void close() throws IOException {} + public int read(char[] cbuf, int off, int len) throws IOException { + int curByte; + int charsRead = 0; + + while(charsRead < len) { + while(true) { + curByte = is.read(); + if(curByte >= 0) { + tempBuffer[tempBufferPtr++] = (byte)curByte; + ByteBuffer bb = ByteBuffer.wrap(tempBuffer, 0, tempBufferPtr); + CharBuffer out = CharBuffer.allocate(1); + + CoderResult res = cdec.decode(bb, out, true); + if(!res.isError()) { + cbuf[off+charsRead] = out.get(0);//cab.put(out.get(0)); + break; + } + else if(tempBufferPtr == tempBuffer.length) { + System.err.println(res.toString()); + throw new RuntimeException("error while decoding"); + } + } + else + return charsRead; + } + ++charsRead; + tempBufferPtr = 0; + } + + return charsRead; + } +} diff --git a/src/org/catacombae/dmgextractor/io/ConcatenatedReader.java b/src/org/catacombae/dmgextractor/io/ConcatenatedReader.java new file mode 100644 index 0000000..a12fefa --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/ConcatenatedReader.java @@ -0,0 +1,51 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.io; +import java.io.*; + +public class ConcatenatedReader extends Reader { + private final Reader[] sources; + private int currentSource; + private long charPos = 0; + public ConcatenatedReader(Reader[] sources) { + this.sources = sources; + this.currentSource = 0; + } + public void close() throws IOException { + for(Reader r : sources) + r.close(); + } + public int read(char[] cbuf, int off, int len) throws IOException { + int bytesRead = 0; + while(bytesRead < len) { + Reader currentReader = sources[currentSource]; + int currentRead = currentReader.read(cbuf, off+bytesRead, len-bytesRead); + while(currentRead != -1 && bytesRead < len) { + bytesRead += currentRead; + currentRead = currentReader.read(cbuf, off+bytesRead, len-bytesRead); + } + if(currentRead == -1) { + if(currentSource+1 < sources.length) + ++currentSource; + else + break; // There wasn't enough data + } + } + return bytesRead; + } +} diff --git a/src/org/catacombae/dmgextractor/io/RandomAccessInputStream.java b/src/org/catacombae/dmgextractor/io/RandomAccessInputStream.java new file mode 100644 index 0000000..fef1136 --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/RandomAccessInputStream.java @@ -0,0 +1,141 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.io; + +import java.io.*; +import org.catacombae.io.RuntimeIOException; + +/** + * This class subclasses java.io.InputStream to transform a part of a RandomAccessStream + * into an ordinary InputStream. + */ +public class RandomAccessInputStream extends InputStream { + private final SynchronizedRandomAccessStream ras; + private long streamPos; + private final long endPos; + + /** length == -1 means length == ras.length() */ + public RandomAccessInputStream(SynchronizedRandomAccessStream ras, long offset, long length) { + try { + long rasLength = ras.length(); + if(length == -1) + length = rasLength; + if(offset > rasLength || offset < 0) + throw new IllegalArgumentException("offset out of bounds (offset=" + + offset + " length=" + length + ")"); + if(length > rasLength-offset || length < 0) + throw new IllegalArgumentException("length out of bounds (offset=" + + offset + " length=" + length + ")"); + this.ras = ras; + this.streamPos = offset; + this.endPos = offset+length; + } catch(Exception e) { throw new RuntimeException(e); } + } + + /** + * Constructs an InputStream that covers the data contained in the underlying + * RandomAccessStream, from the beginning, to the end. */ + public RandomAccessInputStream(SynchronizedRandomAccessStream ras) { + this(ras, 0, -1); + } + + @Override + public int available() throws IOException { + long remaining = endPos - streamPos; + if(remaining > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + else if(remaining < Integer.MIN_VALUE) + return Integer.MIN_VALUE; + else + return (int)remaining; + } + + /** Does not do anything. The underlying SynchronizedRandomAccessStream might be in use by others. */ + @Override + public void close() throws IOException {} + + /** Not supported, not implemented (not needed). */ + @Override + public void mark(int readlimit) { + throw new UnsupportedOperationException("Not supported"); + } + + /** Not supported, not implemented (not needed). */ + @Override + public boolean markSupported() { + return false; + } + + @Override + public int read() throws IOException { + final byte[] tmp = new byte[1]; + int res = read(tmp, 0, 1); + if(res == 1) + return tmp[0] & 0xFF; + else + return -1; + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + try { + int bytesToRead = (int)((streamPos+len > endPos)?endPos-streamPos:len); + if(bytesToRead == 0) + return -1; + int res = ras.readFrom(streamPos, b, off, bytesToRead); + if(res > 0) streamPos += res; + return res; + } catch(RuntimeIOException ex) { + IOException ioe = ex.getIOCause(); + if(ioe != null) { + ex.printStackTrace(); + throw ioe; + } + else + throw ex; + } + } + + /** Not supported, not implemented (not needed). */ + @Override + public void reset() throws IOException { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public long skip(long n) throws IOException { + try { + long res = ras.skipFrom(streamPos, n); + if(res > 0) streamPos += res; + return res; + } catch(RuntimeIOException ex) { + IOException ioe = ex.getIOCause(); + if(ioe != null) { + ex.printStackTrace(); + throw ioe; + } + else + throw ex; + } + } +} diff --git a/src/org/catacombae/dmgextractor/io/ReaderInputStream.java b/src/org/catacombae/dmgextractor/io/ReaderInputStream.java new file mode 100644 index 0000000..ee8c310 --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/ReaderInputStream.java @@ -0,0 +1,182 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.io; + +import java.io.*; +import java.nio.charset.*; + +public class ReaderInputStream extends InputStream { + private Reader r; + private CharsetEncoder encoder; + private byte[] chardata; + private int remainingChardata = 0; + private LousyByteArrayStream lbas; + private OutputStreamWriter osw; + + private static class LousyByteArrayStream extends OutputStream { + private final byte[] buffer; + private int bufpos = 0; + public LousyByteArrayStream(int buflen) { + //System.err.println("Creating a LousyByteArrayStream with length " + buflen); + buffer = new byte[buflen]; + } + public void write(int b) { + buffer[bufpos++] = (byte)b; + } + public int reset(byte[] chardata) { + int length = bufpos; + System.arraycopy(buffer, 0, chardata, 0, length); + bufpos = 0; + return length; + } + } + + public ReaderInputStream(Reader r, Charset c) { + this.r = r; + this.encoder = c.newEncoder(); + //System.err.println("Creating a ReaderInputStream. encoder.maxBytesPerChar() == " + encoder.maxBytesPerChar()); + this.chardata = new byte[(int)Math.ceil(encoder.maxBytesPerChar())]; + + lbas = new LousyByteArrayStream(chardata.length); + osw = new OutputStreamWriter(lbas, encoder); + + } + + public int read() throws IOException { + byte[] b = new byte[1]; + int res = read(b, 0, 1); + if(res == 1) + return b[0] & 0xFF; + else + return -1; + } + + @Override + public int read(byte[] b) throws IOException { return read(b, 0, b.length); } + + /* Säg då att vi skippar 204 bytes. Vad händer? När vi går in i read(3) är remainingChardata = 0, + och off = 0, len = 204. b.length = 4096. Alltså kommer ingen av de 4 första if-satserna vara + giltiga... + I vilken situation kan vi ha läst in mindre data i b än returvärdet? + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { +// System.err.println("ReaderInputStream.read(b.length=" + b.length + ", " + off + ", " + len + ")"); + if(len < 0) throw new IllegalArgumentException(); + if(len == 0) return 0; + + int originalOffset = off; + int endPos = off+len; + + if(remainingChardata > 0) { +// System.err.println("Remaining chardata! length=" + remainingChardata); + int bytesToCopy = remainingChardata>len?len:remainingChardata; +// System.err.println("bytesToCopy=" + bytesToCopy); + System.arraycopy(chardata, 0, b, off, bytesToCopy); + off += bytesToCopy; + remainingChardata -= bytesToCopy; + } + if(off == endPos) { +// System.err.println("(1)returning with " + (off-originalOffset) + " from ReaderInputStream.read"); + return off-originalOffset; + } + +// int baba = 3; + while(off < endPos) { +// if(baba > 0) { +// System.err.println(" looping... off==" + off + " endPos=" + endPos); +// --baba; +// } +// else +// baba = Integer.MIN_VALUE; + int cur = r.read(); + if(cur < 0) + break; + + if(Character.isHighSurrogate((char)cur)) { + int lowSurrogate = r.read(); // UTF-16 is a crap encoding for a programming language + + if(lowSurrogate < 0) + throw new IOException("Too lazy to handle this error..."); + else if(!Character.isSurrogatePair((char)cur, (char)lowSurrogate)) + throw new IOException("Encountered a high surrogate without a matching low surrogate... oh crap."); + + cur = Character.toCodePoint((char)cur, (char)lowSurrogate); + } + char[] charArray = Character.toChars(cur); + String charString = new String(charArray, 0, charArray.length); + + // Now we need to write + //System.out.println("Writing codepoint: 0x" + Util.toHexStringBE(cur)); + + osw.write(charString); + osw.flush(); + + //System.out.println("Resetting..."); + int chardataLength = lbas.reset(chardata); + int remainingLength = endPos-off; + int bytesToCopy = (chardataLength > remainingLength)?remainingLength:chardataLength; + System.arraycopy(chardata, 0, b, off, bytesToCopy); + off += bytesToCopy; + + if(chardataLength > remainingLength) { + remainingChardata = chardataLength-remainingLength; + System.arraycopy(chardata, bytesToCopy, chardata, 0, remainingChardata); + } +// if(baba >= 0) { +// System.err.println(" chardataLength=" + chardataLength + " remainingLength=" + remainingLength); +// System.err.println(" bytesToCopy=" + bytesToCopy + " off=" + off); +// } + } + int bytesRead = off-originalOffset; + if(off < endPos && bytesRead == 0) { // We have a break due to end of stream + //System.err.println("(3)returning -1 due to end of stream"); + return -1; + } + else { + //System.err.println("(2)returning with " + bytesRead + " from ReaderInputStream.read"); + return bytesRead; + } + } + + @Override + public long skip(long n) throws IOException { + System.err.println("ReaderInputStream.skip(" + n + ")"); + byte[] skipBuffer = new byte[4096]; + long bytesSkipped = 0; + while(bytesSkipped < n) { + //System.out.println(" Looping..."); + long remainingBytes = n-bytesSkipped; + int bytesToSkip = (int)(skipBuffer.length 0) { + //System.out.println(" Actually skipped " + res + " bytes."); + //System.out.println(" This is the data skipped: " + Util.byteArrayToHexString(skipBuffer, 0, res)); + bytesSkipped += res; + } + else { + //System.out.println("encountered EOF!"); + break; // Seems we can't skip all bytes + } + } + //debug + //System.out.println(" bytesSkipped=" + bytesSkipped + " n=" + n); + return bytesSkipped; //super.skip(n); + } +} diff --git a/src/org/catacombae/dmgextractor/io/SynchronizedRandomAccessStream.java b/src/org/catacombae/dmgextractor/io/SynchronizedRandomAccessStream.java new file mode 100644 index 0000000..1c9ad6a --- /dev/null +++ b/src/org/catacombae/dmgextractor/io/SynchronizedRandomAccessStream.java @@ -0,0 +1,100 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.io; + +import java.io.*; +import org.catacombae.io.BasicReadableRandomAccessStream; +import org.catacombae.io.ReadableRandomAccessStream; +import org.catacombae.io.RuntimeIOException; + +/** + * This class adds concurrency safety to a random access stream. It includes a seek+read + * atomic operation. All operations on this object is synchronized on its own monitor. + */ +public class SynchronizedRandomAccessStream extends BasicReadableRandomAccessStream { + /** The underlying stream. */ + private ReadableRandomAccessStream ras; + + public SynchronizedRandomAccessStream(ReadableRandomAccessStream ras) { + this.ras = ras; + } + + /** Atomic seek+read. */ + public synchronized int readFrom(long pos, byte[] b, int off, int len) throws RuntimeIOException { + if(getFilePointer() != pos) + seek(pos); + return read(b, off, len); + } + + /** Atomic seek+skip. */ + public synchronized long skipFrom(final long pos, final long length) throws RuntimeIOException { + long streamLength = length(); + long newPos = pos+length; + + if(newPos > streamLength) { + seek(streamLength); + return streamLength-pos; + } + else { + seek(newPos); + return length; + } + } + + /** Atomic length() - getFilePointer(). */ + public synchronized long remainingLength() throws RuntimeIOException { + return length()-getFilePointer(); + } + + /** @see java.io.RandomAccessFile */ + public synchronized void close() throws RuntimeIOException { + ras.close(); + } + + /** @see java.io.RandomAccessFile */ + public synchronized long getFilePointer() throws RuntimeIOException { + return ras.getFilePointer(); + } + + /** @see java.io.RandomAccessFile */ + public synchronized long length() throws RuntimeIOException { + return ras.length(); + } + + /** @see java.io.RandomAccessFile */ + @Override + public synchronized int read() throws RuntimeIOException { + return ras.read(); + } + + /** @see java.io.RandomAccessFile */ + @Override + public synchronized int read(byte[] b) throws RuntimeIOException { + return ras.read(b); + } + + /** @see java.io.RandomAccessFile */ + public synchronized int read(byte[] b, int off, int len) throws RuntimeIOException { + return ras.read(b, off, len); + } + + /** @see java.io.RandomAccessFile */ + public synchronized void seek(long pos) throws RuntimeIOException { + ras.seek(pos); + } +} diff --git a/src/org/catacombae/dmgextractor/ui/PasswordDialog.java b/src/org/catacombae/dmgextractor/ui/PasswordDialog.java new file mode 100644 index 0000000..005d422 --- /dev/null +++ b/src/org/catacombae/dmgextractor/ui/PasswordDialog.java @@ -0,0 +1,101 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.ui; + +import java.awt.Component; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JDialog; +import javax.swing.JOptionPane; + +/** + * + * @author Erik Larsson + */ +public class PasswordDialog extends JDialog { + private final PasswordPanel passwordPanel; + private char[] password = null; + + private PasswordDialog(Frame owner, boolean modal, String dialogTitle, String messageLine) { + super(owner, dialogTitle, modal); + + ActionListener okButtonListener = new ActionListener() { + + public void actionPerformed(ActionEvent e) { + actionOkButtonClicked(); + } + + }; + ActionListener cancelButtonListener = new ActionListener() { + + public void actionPerformed(ActionEvent e) { + actionCancelButtonClicked(); + } + + }; + this.passwordPanel = new PasswordPanel(messageLine, okButtonListener, cancelButtonListener); + add(passwordPanel); + } + private void actionOkButtonClicked() { + password = passwordPanel.getPassword(); + /* It is important to call dispose() to dispose of AWT resources, allowing the calling + * thread to exit the program at will. Otherwise, an active AWT thread will block. */ + dispose(); + } + + private void actionCancelButtonClicked() { + password = null; + dispose(); + } + private char[] getPassword() { + return password; + } + + /** + * Shows a dialog, modal to owner which requests a password from the user, halts + * execution until the password has been entered, and then returns the entered password, or + * null depending on whether the user clicked the "Ok" or the "Cancel" button. + * + * @param parentComponent this dialog's parent component. + * @param dialogTitle the title of the dialog, printed in the window header. + * @param messageLine the one line message to display above the password field, for example + * "Please enter password:" or anything similar. + * @return the entered password, or null if the user canceled the dialog. + */ + public static char[] showDialog(Component parentComponent, String dialogTitle, String messageLine) { + PasswordDialog pd = new PasswordDialog(JOptionPane.getFrameForComponent(parentComponent), true, dialogTitle, messageLine); + pd.pack(); + pd.setResizable(false); + pd.setLocationRelativeTo(null); + pd.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + pd.setVisible(true); + return pd.getPassword(); + } + + /* + public static void main(String[] args) { + char[] pwd = showDialog(null, "hej", "apa"); + //String pwd = JOptionPane.showInputDialog(null, "apa", "hej"); + if(pwd != null) + System.out.println("Password: \"" + new String(pwd) + "\""); + else + System.out.println("User canceled dialog."); + } + */ +} diff --git a/src/org/catacombae/dmgextractor/ui/PasswordPanel.form b/src/org/catacombae/dmgextractor/ui/PasswordPanel.form new file mode 100644 index 0000000..c987d92 --- /dev/null +++ b/src/org/catacombae/dmgextractor/ui/PasswordPanel.form @@ -0,0 +1,71 @@ + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/org/catacombae/dmgextractor/ui/PasswordPanel.java b/src/org/catacombae/dmgextractor/ui/PasswordPanel.java new file mode 100644 index 0000000..4a1d822 --- /dev/null +++ b/src/org/catacombae/dmgextractor/ui/PasswordPanel.java @@ -0,0 +1,118 @@ +/*- + * Copyright (C) 2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.ui; + +import java.awt.event.ActionListener; +//import javax.swing.JFrame; + +/** + * The UI design component for PasswordDialog. + * + * @author Erik Larsson + */ +class PasswordPanel extends javax.swing.JPanel { + + /** Creates new form PasswordPanel */ + public PasswordPanel(String messageLine, ActionListener okButtonListener, ActionListener cancelButtonListener) { + this(); + instructionsLabel.setText(messageLine); + passwordField.addActionListener(okButtonListener); + okButton.addActionListener(okButtonListener); + cancelButton.addActionListener(cancelButtonListener); + } + + private PasswordPanel() { + initComponents(); + } + + char[] getPassword() { + return passwordField.getPassword(); + } + + /* + public static void main(String[] args) { + JFrame jp = new JFrame(); + PasswordPanel pp = new PasswordPanel(); + pp.instructionsLabel.setText("tada\nYADA\nBADA"); + jp.add(pp); + jp.pack(); + jp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jp.setVisible(true); + } + */ + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + passwordField = new javax.swing.JPasswordField(); + instructionsLabel = new javax.swing.JLabel(); + buttonLayoutHelper = new javax.swing.JPanel(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + + instructionsLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + instructionsLabel.setText("You need to enter a password to unlock this disk image:"); + + buttonLayoutHelper.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 0)); + + okButton.setText("OK"); + buttonLayoutHelper.add(okButton); + + cancelButton.setText("Cancel"); + buttonLayoutHelper.add(cancelButton); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(passwordField, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 306, Short.MAX_VALUE) + .add(instructionsLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 306, Short.MAX_VALUE) + .add(buttonLayoutHelper, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 306, Short.MAX_VALUE)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(instructionsLabel) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(passwordField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED) + .add(buttonLayoutHelper, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel buttonLayoutHelper; + private javax.swing.JButton cancelButton; + private javax.swing.JLabel instructionsLabel; + private javax.swing.JButton okButton; + private javax.swing.JPasswordField passwordField; + // End of variables declaration//GEN-END:variables + } diff --git a/src/org/catacombae/dmgextractor/utils/.cvsignore b/src/org/catacombae/dmgextractor/utils/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/org/catacombae/dmgextractor/utils/DMGInfo.java b/src/org/catacombae/dmgextractor/utils/DMGInfo.java new file mode 100644 index 0000000..92dd240 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/DMGInfo.java @@ -0,0 +1,175 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ +package org.catacombae.dmgextractor.utils; + +import java.io.IOException; +import java.io.RandomAccessFile; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +public class DMGInfo { + + public static void main(String[] args) throws IOException { + RandomAccessFile inRaf = new RandomAccessFile(args[0], "r"); + + // Check opening signature "koly" + inRaf.seek(inRaf.length() - 512); + byte[] koly = new byte[4]; + inRaf.readFully(koly); + String kolySignature = new String(koly, "US-ASCII"); + if(!kolySignature.equals("koly")) + System.out.println("ERROR: Signature incorrect. Found \"" + kolySignature + "\" instead of \"koly\"."); + else + System.out.println("\"koly\" signature OK."); + + // Read partition list start location 1 and end location + inRaf.seek(inRaf.length() - 0x1E0); + + // -0x1E0: address to plist xml structure (8 bytes) + long plistAddress1 = inRaf.readLong(); + System.out.println("Address to plist: 0x" + Long.toHexString(plistAddress1)); + + + // -0x1D8: address to end of plist xml structure (8 bytes) + long plistEndAddress = inRaf.readLong(); + System.out.println("Address to end of plist: 0x" + Long.toHexString(plistEndAddress)); + System.out.println(" Implication: plist size = " + (plistEndAddress - plistAddress1) + " B"); + + + long unknown_0x1D0 = inRaf.readLong(); + + + long unknown_0x1C8 = inRaf.readLong(); + if(unknown_0x1C8 != 0x0000000100000001L) + System.out.println("Assertion failed! unknown_0x1C8 == 0x" + + Long.toHexString(unknown_0x1C8) + " and not 0x0000000100000001"); + + + long unknown_0x1C0 = inRaf.readLong(); + + + long unknown_0x1B8 = inRaf.readLong(); + System.out.println("Some kind of signature? Value: 0x" + Long.toHexString(unknown_0x1B8)); + + + long unknown_0x1B0 = inRaf.readLong(); + if(unknown_0x1B0 != 0x0000000200000020L) + System.out.println("Assertion failed! unknown_0x1B0 == 0x" + + Long.toHexString(unknown_0x1B0) + " and not 0x0000000200000020"); + + + int unknown_0x1A8 = inRaf.readInt(); + + + int unknown_0x1A4 = inRaf.readInt(); + System.out.println("Some kind of unit size? Value: 0x" + + Integer.toHexString(unknown_0x1A4) + " / " + unknown_0x1A4); + + + // Unknown chunk of data (120 bytes) + byte[] unknown_0x1A0 = new byte[120]; + inRaf.readFully(unknown_0x1A0); + + + // -0x128: address to beginning of plist xml structure (second occurrence) (8 bytes) + long plistAddress2 = inRaf.readLong(); + System.out.println("Address to plist (2): 0x" + Long.toHexString(plistAddress2)); + + + // -0x120: size of plist xml structure (8 bytes) + long plistSize = inRaf.readLong(); + System.out.println("plist size: " + plistSize + " B"); + + + // Unknown chunk of data (120 bytes) + byte[] unknown_0x118 = new byte[120]; + inRaf.readFully(unknown_0x118); + + + // -0x0A0: Checksum type identifier (4 bytes) + System.out.print("Checksum type"); + int cs_type = inRaf.readInt(); + if(cs_type == 0x00000002) + System.out.println(": CRC-32"); + else if(cs_type == 0x00000004) + System.out.println(": MD5"); + else + System.out.println(" unknown! Data: 0x" + Integer.toHexString(cs_type)); + + + // -0x09C: Length of checksum in bits (4 bytes) + int cs_length = inRaf.readInt(); + System.out.println("Checksum length: " + cs_length + " bits"); + + + // -0x098: Checksum ((cs_length/8) bytes) + byte[] checksum = new byte[cs_length / 8]; + inRaf.readFully(checksum); + System.out.println("Checksum: 0x" + byteArrayToHexString(checksum).toUpperCase()); + + /* + if(unknown_0x != 0xL) + System.out.println("Assertion failed! unknown_0x == 0x" + Long.toHexString(unknown_0x) + " and not 0xL"); + */ + } + + public static String byteArrayToHexString(byte[] array) { + StringBuilder result = new StringBuilder(); + for(byte b : array) { + String s = Integer.toHexString(b & 0xFF); + if(s.length() == 1) + s = "0" + s; + result.append(s); + } + return result.toString(); + } +} + +class DMGInfoFrame extends JFrame { + + private JTabbedPane mainPane; + + public DMGInfoFrame() { + super("DMGInfo"); + + mainPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); + + StatisticsPanel statisticsPanel; + statisticsPanel = new StatisticsPanel(); + //mainPane.addTab(statisticsPanel, "Statistics"); + } +} + +class StatisticsPanel extends JPanel { + + JPanel blocktypeCountPanel; + + public StatisticsPanel(/*DMGFile dmgFile*/) { + } +} + +/* +class DMGFile extends RandomAccessFile { +public DMGFile(File file, String mode) { +super(file, mode); +} +public DMGFile(String name, String mode) { +super(name, mode); +} +} + */ diff --git a/src/org/catacombae/dmgextractor/utils/DMGInfoWindow.java b/src/org/catacombae/dmgextractor/utils/DMGInfoWindow.java new file mode 100644 index 0000000..7cb97e1 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/DMGInfoWindow.java @@ -0,0 +1,48 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.utils; + +import java.awt.BorderLayout; +import javax.swing.JFrame; +import net.iharder.dnd.FileDrop; +import org.catacombae.dmgextractor.utils.gui.DMGInfoPanel; + +public class DMGInfoWindow extends JFrame { + private DMGInfoPanel infoPanel; + + public DMGInfoWindow() { + infoPanel = new DMGInfoPanel(); + add(infoPanel, BorderLayout.CENTER); + + // Register handler for file drag&drop events + new FileDrop(this, new FileDrop.Listener() { + + public void filesDropped(java.io.File[] files) { + if(files.length > 0);//loadFile(files[0]); + } + }); + + pack(); + setLocationRelativeTo(null); + + } + + public static void main(String[] args) { + new DMGInfoWindow().setVisible(true); + } +} diff --git a/src/org/catacombae/dmgextractor/utils/DMGMetadata.java b/src/org/catacombae/dmgextractor/utils/DMGMetadata.java new file mode 100644 index 0000000..7f610d4 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/DMGMetadata.java @@ -0,0 +1,397 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ +package org.catacombae.dmgextractor.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; + +public class DMGMetadata { + + public static final long PLIST_ADDRESS_1 = 0x1E0; + public static final long PLIST_ADDRESS_2 = 0x128; + public final byte[] rawData; + public final byte[] plistXmlData; + public final byte[] unknown1_256; + public PartitionBlockList[] blockLists; + public final byte[] unknown2_12; + public APMPartition[] partitions; + public final byte[] unknown3_unknown; + public final byte[] koly; + + public DMGMetadata(RandomAccessFile dmgFile) throws IOException { + dmgFile.seek(dmgFile.length() - PLIST_ADDRESS_1); + long plistBegin1 = dmgFile.readLong(); + long plistEnd = dmgFile.readLong(); + dmgFile.seek(dmgFile.length() - PLIST_ADDRESS_2); + long plistBegin2 = dmgFile.readLong(); + long plistSize = dmgFile.readLong(); + + rawData = new byte[(int) (dmgFile.length() - plistBegin1)]; + dmgFile.seek(plistBegin1); + dmgFile.readFully(rawData); + + plistXmlData = new byte[(int) plistSize]; + dmgFile.seek(plistBegin1); + dmgFile.readFully(plistXmlData); + + unknown1_256 = new byte[256]; + dmgFile.readFully(unknown1_256); + + LinkedList blockListList = new LinkedList(); + int length = dmgFile.readInt(); + byte[] fourcc = new byte[4]; + dmgFile.readFully(fourcc); + String fourccString = new String(fourcc, "US-ASCII"); + dmgFile.seek(dmgFile.getFilePointer() - 4); + while(fourccString.equals("mish")) { + blockListList.add(new PartitionBlockList(dmgFile, length)); + length = dmgFile.readInt(); + dmgFile.readFully(fourcc); + fourccString = new String(fourcc, "US-ASCII"); + dmgFile.seek(dmgFile.getFilePointer() - 4); + } + blockLists = blockListList.toArray(new PartitionBlockList[blockListList.size()]); + + unknown2_12 = new byte[12]; + dmgFile.readFully(unknown2_12); + + LinkedList partitionList = new LinkedList(); + byte[] currentPartitionEntry = new byte[0x200]; + dmgFile.readFully(currentPartitionEntry); + byte[] pmSig = new byte[2]; + pmSig[0] = currentPartitionEntry[0]; + pmSig[1] = currentPartitionEntry[1]; + while(new String(pmSig, "US-ASCII").equals("PM")) { + partitionList.addLast(new APMPartition(currentPartitionEntry)); + dmgFile.readFully(currentPartitionEntry); + pmSig[0] = currentPartitionEntry[0]; + pmSig[1] = currentPartitionEntry[1]; + } + while(onlyZeros(currentPartitionEntry)) + dmgFile.readFully(currentPartitionEntry); + partitions = partitionList.toArray(new APMPartition[partitionList.size()]); + + unknown3_unknown = new byte[(int) (dmgFile.length() - dmgFile.getFilePointer() - 512)]; + dmgFile.readFully(unknown3_unknown); + + koly = new byte[512]; + dmgFile.seek(dmgFile.length() - koly.length); + dmgFile.readFully(koly); + + if(dmgFile.getFilePointer() != dmgFile.length()) + System.out.println("MISCALCULATION! FP=" + dmgFile.getFilePointer() + " LENGTH=" + dmgFile.length()); + } + + public void printInfo(PrintStream ps) { + ps.println("block list:"); + for(PartitionBlockList pbl : blockLists) + pbl.printInfo(ps); + ps.println("partitions:"); + for(APMPartition ap : partitions) + ap.printPartitionInfo(ps); + } + + private static boolean onlyZeros(byte[] array) { + for(int i = 0; i < array.length; ++i) { + if(array[i] != 0) + return false; + } + return true; + } + + public static class PartitionBlockList { + + public final byte[] header = new byte[0xCC]; + public final BlockDescriptor[] descriptors; + + public PartitionBlockList(byte[] entryData) throws IOException { + this(new DataInputStream(new ByteArrayInputStream(entryData)), entryData.length); + } + + public PartitionBlockList(DataInput di, int length) throws IOException { + int position = 0; + di.readFully(header); + position += header.length; + LinkedList descs = new LinkedList(); + while(position < length) { + descs.addLast(new BlockDescriptor(di)); + position += 0x28; + } + descriptors = descs.toArray(new BlockDescriptor[descs.size()]); + } + + public void printInfo(PrintStream ps) { + for(BlockDescriptor bd : descriptors) + ps.println(bd.toString()); + } + } + + public static class BlockDescriptor { + // Known block types + + public static final int BT_COPY = 0x00000001; + public static final int BT_ZERO = 0x00000002; + public static final int BT_ZLIB = 0x80000005; + public static final int BT_END = 0xffffffff; + public static final int BT_UNKNOWN1 = 0x7ffffffe; + private static final int[] KNOWN_BLOCK_TYPES = { BT_COPY, + BT_ZERO, + BT_ZLIB, + BT_END, + BT_UNKNOWN1 }; + private static final String[] KNOWN_BLOCK_TYPE_NAMES = { "BT_COPY", + "BT_ZERO", + "BT_ZLIB", + "BT_END", + "BT_UNKNOWN1" }; + private int blockType; + private int unknown; + private long outOffset; + private long outSize; + private long inOffset; + private long inSize; + + public BlockDescriptor() { + } + + public BlockDescriptor(byte[] entryData) throws IOException { + this(new DataInputStream(new ByteArrayInputStream(entryData))); + } + + public BlockDescriptor(DataInput dataIn) throws IOException { + blockType = dataIn.readInt(); + unknown = dataIn.readInt(); + outOffset = dataIn.readLong() * 0x200; + outSize = dataIn.readLong() * 0x200; + inOffset = dataIn.readLong(); + inSize = dataIn.readLong(); + } + + public byte[] toBytes() throws IOException { + ByteArrayOutputStream result = new ByteArrayOutputStream(0x28); + DataOutputStream dataOut = new DataOutputStream(result); + + dataOut.writeInt(blockType); // 4 bytes + dataOut.writeInt(unknown); // 4 bytes + if((outOffset % 0x200) != 0) + throw new RuntimeException("Out offset must be aligned to 0x200 block size!"); + dataOut.writeLong(outOffset / 0x200); // 8 bytes + if((outSize % 0x200) != 0) + throw new RuntimeException("Out size must be aligned to 0x200 block size!"); + dataOut.writeLong(outSize / 0x200); // 8 bytes + dataOut.writeLong(inOffset); // 8 bytes + dataOut.writeLong(inSize); // 8 bytes + // sum = 4 + 4 + 8 + 8 + 8 + 8 = 40 = 0x28 + + dataOut.flush(); + dataOut.close(); + return result.toByteArray(); + } + + public int getBlockType() { + return blockType; + } + + public int getUnknown() { + return unknown; + } + + public String getUnknownAsString() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(4); + DataOutputStream dos = new DataOutputStream(baos); + dos.write(unknown); + dos.close(); + return new String(baos.toByteArray(), "US-ASCII"); + } + + public long getOutOffset() { + return outOffset; + } + + public long getOutSize() { + return outSize; + } + + public long getInOffset() { + return inOffset; + } + + public long getInSize() { + return inSize; + } + + public void setBlockType(int blockType) { + this.blockType = blockType; + } + + public void setUnknown(int unknown) { + this.unknown = unknown; + } + + public void setOutOffset(long outOffset) { + if((outOffset % 0x200) != 0) + throw new RuntimeException("Out offset must be aligned to 0x200 block size!"); + this.outOffset = outOffset; + } + + public void setOutSize(long outSize) { + if((outSize % 0x200) != 0) + throw new RuntimeException("Out size must be aligned to 0x200 block size!"); + this.outSize = outSize; + } + + public void setInOffset(long inOffset) { + this.inOffset = inOffset; + } + + public void setInSize(long inSize) { + this.inSize = inSize; + } + + public boolean hasKnownBlockType() { + for(int current : KNOWN_BLOCK_TYPES) { + if(blockType == current) + return true; + } + return false; + } + + public String getBlockTypeName() { + for(int i = 0; i < KNOWN_BLOCK_TYPES.length; ++i) { + int current = KNOWN_BLOCK_TYPES[i]; + if(blockType == current) + return KNOWN_BLOCK_TYPE_NAMES[i]; + } + return null; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder("[BlockDescriptor"); + + String blockTypeString = "\"" + getBlockTypeName() + "\""; + if(blockTypeString == null) + blockTypeString = "0x" + Integer.toHexString(blockType) + " (unknown type)"; + + result.append(" blockType=" + blockTypeString); + result.append(" unknown=" + Integer.toHexString(unknown)); + result.append(" outOffset=" + outOffset); + result.append(" outSize=" + outSize); + result.append(" inOffset=" + inOffset); + result.append(" inSize=" + inSize); + result.append("]"); + + return result.toString(); + } + } + + // Saxat från HFSExplorer.java + public static class APMPartition { + + public int pmSig; // {partition signature} + public int pmSigPad; // {reserved} + public long pmMapBlkCnt; // {number of blocks in partition map} + public long pmPyPartStart; // {first physical block of partition} + public long pmPartBlkCnt; // {number of blocks in partition} + public final byte[] pmPartName = new byte[32]; // {partition name} + public final byte[] pmParType = new byte[32]; // {partition type} + public long pmLgDataStart; // {first logical block of data area} + public long pmDataCnt; // {number of blocks in data area} + public long pmPartStatus; // {partition status information} + public long pmLgBootStart; // {first logical block of boot code} + public long pmBootSize; // {size of boot code, in bytes} + public long pmBootAddr; // {boot code load address} + public long pmBootAddr2; // {reserved} + public long pmBootEntry; // {boot code entry point} + public long pmBootEntry2; // {reserved} + public long pmBootCksum; // {boot code checksum} + public final byte[] pmProcessor = new byte[16]; // {processor type} + public final int[] pmPad = new int[188]; // {reserved} + + public APMPartition(byte[] entryData) throws IOException { + this(new DataInputStream(new ByteArrayInputStream(entryData))); + } + + public APMPartition(DataInput di) throws IOException { + // 2*2 + 4*3 + 32*2 + 10*4 + 16 + 188*2 = 512 + pmSig = di.readShort() & 0xffff; + pmSigPad = di.readShort() & 0xffff; + pmMapBlkCnt = di.readInt() & 0xffffffffL; + pmPyPartStart = di.readInt() & 0xffffffffL; + pmPartBlkCnt = di.readInt() & 0xffffffffL; + di.readFully(pmPartName); + di.readFully(pmParType); + pmLgDataStart = di.readInt() & 0xffffffffL; + pmDataCnt = di.readInt() & 0xffffffffL; + pmPartStatus = di.readInt() & 0xffffffffL; + pmLgBootStart = di.readInt() & 0xffffffffL; + pmBootSize = di.readInt() & 0xffffffffL; + pmBootAddr = di.readInt() & 0xffffffffL; + pmBootAddr2 = di.readInt() & 0xffffffffL; + pmBootEntry = di.readInt() & 0xffffffffL; + pmBootEntry2 = di.readInt() & 0xffffffffL; + pmBootCksum = di.readInt() & 0xffffffffL; + di.readFully(pmProcessor); + for(int i = 0; i < pmPad.length; ++i) + pmPad[i] = di.readShort() & 0xffff; + } + + public void printPartitionInfo(PrintStream ps) { +// String result = ""; +// result += "Partition name: \"" + new String(pmPartName) + "\"\n"; +// result += "Partition type: \"" + new String(pmParType) + "\"\n"; +// result += "Processor type: \"" + new String(pmProcessor) + "\"\n"; +// return result; + try { + ps.println("pmSig: " + pmSig); + ps.println("pmSigPad: " + pmSigPad); + ps.println("pmMapBlkCnt: " + pmMapBlkCnt); + ps.println("pmPyPartStart: " + pmPyPartStart); + ps.println("pmPartBlkCnt: " + pmPartBlkCnt); + ps.println("pmPartName: \"" + new String(pmPartName, "US-ASCII") + "\""); + ps.println("pmParType: \"" + new String(pmParType, "US-ASCII") + "\""); + ps.println("pmLgDataStart: " + pmLgDataStart); + ps.println("pmDataCnt: " + pmDataCnt); + ps.println("pmPartStatus: " + pmPartStatus); + ps.println("pmLgBootStart: " + pmLgBootStart); + ps.println("pmBootSize: " + pmBootSize); + ps.println("pmBootAddr: " + pmBootAddr); + ps.println("pmBootAddr2: " + pmBootAddr2); + ps.println("pmBootEntry: " + pmBootEntry); + ps.println("pmBootEntry2: " + pmBootEntry2); + ps.println("pmBootCksum: " + pmBootCksum); + ps.println("pmProcessor: \"" + new String(pmProcessor, "US-ASCII") + "\""); + ps.println("pmPad: " + pmPad); + } catch(UnsupportedEncodingException uee) { + uee.printStackTrace(); + } // Will never happen. Ever. Period. + } + } + + public static void main(String[] args) throws IOException { + DMGMetadata meta = new DMGMetadata(new RandomAccessFile(args[0], "r")); + meta.printInfo(System.out); + } +} diff --git a/src/org/catacombae/dmgextractor/utils/ExtractPlist.java b/src/org/catacombae/dmgextractor/utils/ExtractPlist.java new file mode 100644 index 0000000..4d56d26 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/ExtractPlist.java @@ -0,0 +1,49 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.utils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import org.catacombae.io.ReadableFileStream; +import org.catacombae.dmg.udif.UDIFFileView; + +public class ExtractPlist { + + public static void main(String[] args) throws IOException { + RandomAccessFile inFile; + OutputStream outStream; + try { + inFile = new RandomAccessFile(args[0], "r"); + outStream = new FileOutputStream(args[1]); + } catch(ArrayIndexOutOfBoundsException aioobe) { + System.out.println("Usage: ExtractPlist "); + return;//System.exit(0); + } + + UDIFFileView dfw = new UDIFFileView(new ReadableFileStream(inFile)); + byte[] plistData = dfw.getPlistData(); + /*int bytesWritten = */ outStream.write(plistData); +// if(bytesWritten != plistData.length) +// System.out.println("ERROR: Could not write all data to output file. " + bytesWritten + " of " + plistData.length + " bytes written."); + + inFile.close(); + outStream.close(); + } +} diff --git a/src/org/catacombae/dmgextractor/utils/ValidateDmg.java b/src/org/catacombae/dmgextractor/utils/ValidateDmg.java new file mode 100644 index 0000000..fdcbf54 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/ValidateDmg.java @@ -0,0 +1,145 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ +package org.catacombae.dmgextractor.utils; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +import org.catacombae.dmgextractor.Util; +import org.catacombae.dmg.udif.Koly; +import org.catacombae.dmg.udif.UDIFFileView; +import org.catacombae.io.ReadableFileStream; + +/** + * This is a console application that validates a set of dmg files + * according to the currently known rules (implemented in Koly.validate). + */ +public class ValidateDmg { + + public static void main(String[] filenames) throws IOException { + if(filenames.length == 0) + System.out.println("No files to validate."); + for(String fn : filenames) { + System.out.println("Processing \"" + fn + "\"..."); + try { + RandomAccessFile raf = new RandomAccessFile(fn, "r"); + UDIFFileView dfw = new UDIFFileView(new ReadableFileStream(raf)); + Koly koly = dfw.getKoly(); + ValidateResult vr = validateKoly(raf, koly); + String[] errors = vr.getErrors(); + String[] warnings = vr.getWarnings(); + for(int i = 0; i < errors.length; ++i) { + if(i == 0) + System.out.println(" " + errors.length + " errors"); + System.out.println(" " + errors[i].toString()); + } + for(int i = 0; i < warnings.length; ++i) { + if(i == 0) + System.out.println(" " + warnings.length + " warnings:"); + System.out.println(" " + warnings[i].toString()); + } + dfw.getPlist(); // Creates a new Plist, which automatically parses the XML data (and validates it). + } catch(Exception e) { + e.printStackTrace(); + } + System.out.println(); + } + } + + public static ValidateResult validateKoly(RandomAccessFile sourceFile, Koly koly) throws IOException { + /* Validates the data in the koly block, as much as we can validate it + * given what we know about it.. */ + + ValidateResult vr = new ValidateResult(); + + // Check that the fourcc reads "koly" + try { + String fourCCString = new String(Util.toByteArrayBE(koly.getFourCC()), "US-ASCII"); + if(!fourCCString.equals("koly")) + vr.addError("Invalid fourCC: \"" + fourCCString + "\" (should be \"koly\")"); + } catch(UnsupportedEncodingException uee) { + throw new RuntimeException(uee); // This should never happen + } + + // unknown1 has always been a certain byte-sequence in examples. checkit + // 0000 0004 0000 0200 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 + byte[] previouslySeenString = { 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x20, 0x0, + 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0 }; + if(!Util.arraysEqual(previouslySeenString, koly.getUnknown1())) + vr.addWarning("unknown1 deviates from earlier observations: 0x" + Util.byteArrayToHexString(koly.getUnknown1())); + + long plistBegin1 = koly.getPlistBegin1(); + long plistBegin2 = koly.getPlistBegin2(); + long plistSize = koly.getPlistSize(); + + // Plist addresses must be equal + if(plistBegin1 != plistBegin2) + vr.addError("The two plist addresses don't match (" + plistBegin1 + "!=" + plistBegin2 + ")"); + + // Address to start of plist must be within file bounds. + if(plistBegin1 > sourceFile.length()) + vr.addError("plistBegin1 out of bounds (pistBegin1: " + plistBegin1 + " file size: " + sourceFile.length() + ")"); + else { + // There must be a plist at that address + sourceFile.seek(plistBegin1); + byte[] xmlIdentifier = new byte[5]; + sourceFile.read(xmlIdentifier); + try { + String xmlIdentifierString = new String(xmlIdentifier, "US-ASCII"); + if(!xmlIdentifierString.equals(" sourceFile.length()) + vr.addError("plistBegin2 out of bounds (pistBegin1: " + plistBegin2 + " file size: " + sourceFile.length() + ")"); + else { + // There must be a plist at that address + sourceFile.seek(plistBegin2); + byte[] xmlIdentifier = new byte[5]; + sourceFile.read(xmlIdentifier); + try { + String xmlIdentifierString = new String(xmlIdentifier, "US-ASCII"); + if(!xmlIdentifierString.equals(" 0) { + if(plistSize + plistBegin1 > sourceFile.length() - 512) + vr.addError("plist dimensions outside file bounds! (plistSize: " + plistSize + " plistBegin1: " + plistBegin1 + " sourceFile.length()-512: " + (sourceFile.length() - 512)); + if(plistSize + plistBegin2 > sourceFile.length() - 512) + vr.addError("plist dimensions outside file bounds! (plistSize: " + plistSize + " plistBegin2: " + plistBegin2 + " sourceFile.length()-512: " + (sourceFile.length() - 512)); + } + else + vr.addError("plist dimensions outside file bounds! (plistSize: " + plistSize + " sourceFile.length-512: " + (sourceFile.length() - 512)); + return vr; + } +} diff --git a/src/org/catacombae/dmgextractor/utils/ValidateDmgs.java b/src/org/catacombae/dmgextractor/utils/ValidateDmgs.java new file mode 100644 index 0000000..1246fc3 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/ValidateDmgs.java @@ -0,0 +1,49 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedList; + +/** + * Wrapper to allow processing of a list of dmg files. (I was too lazy + * to write a shell script for this..) + */ +public class ValidateDmgs { + + public static void main(String[] args) throws IOException { + LinkedList fileList = new LinkedList(); + for(String currentList : args) { + try { + BufferedReader listIn = new BufferedReader(new InputStreamReader(new FileInputStream(new File(currentList)))); + String currentDmg = listIn.readLine(); + while(currentDmg != null) { + fileList.add(currentDmg); + currentDmg = listIn.readLine(); + } + } catch(IOException ioe) { + ioe.printStackTrace(); + } + } + ValidateDmg.main(fileList.toArray(new String[fileList.size()])); + } +} diff --git a/src/org/catacombae/dmgextractor/utils/ValidateResult.java b/src/org/catacombae/dmgextractor/utils/ValidateResult.java new file mode 100644 index 0000000..9d5f6d4 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/ValidateResult.java @@ -0,0 +1,45 @@ +/*- + * Copyright (C) 2006-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.dmgextractor.utils; + +import java.util.LinkedList; + +public class ValidateResult { + + private final LinkedList errors = new LinkedList(); + private final LinkedList warnings = new LinkedList(); + + public ValidateResult() { + } + + public void addError(String message) { + errors.addLast(message); + } + + public void addWarning(String message) { + warnings.addLast(message); + } + + public String[] getErrors() { + return errors.toArray(new String[errors.size()]); + } + + public String[] getWarnings() { + return warnings.toArray(new String[warnings.size()]); + } +} diff --git a/src/org/catacombae/dmgextractor/utils/gui/.cvsignore b/src/org/catacombae/dmgextractor/utils/gui/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/org/catacombae/dmgextractor/utils/gui/DMGInfoPanel.form b/src/org/catacombae/dmgextractor/utils/gui/DMGInfoPanel.form new file mode 100644 index 0000000..3e5ed5e --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/DMGInfoPanel.form @@ -0,0 +1,71 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/org/catacombae/dmgextractor/utils/gui/DMGInfoPanel.java b/src/org/catacombae/dmgextractor/utils/gui/DMGInfoPanel.java new file mode 100644 index 0000000..e801fc9 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/DMGInfoPanel.java @@ -0,0 +1,133 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +/* + * DMGInfoPanel.java + * + * Created on den 7 november 2006, 09:35 + */ + +package org.catacombae.dmgextractor.utils.gui; + +import java.awt.*; +import javax.swing.*; +import javax.swing.event.*; + +/** + * + * @author erik + */ +public class DMGInfoPanel extends javax.swing.JPanel { + private CardLayout contentsCardLayout; + private String[] contentTags = { + "General info", + "plist", + "Unknown (256 bytes)", + "Block map", + "Unknown (12 bytes)", + "Apple Partition Map", + "Unknown (X bytes)", + "koly"}; + + /** Creates new form DMGInfoPanel */ + public DMGInfoPanel() { + initComponents(); + + ListModel listModel = new javax.swing.AbstractListModel() { + public int getSize() { return contentTags.length; } + public Object getElementAt(int i) { return contentTags[i]; } + }; + + contentsList.setModel(listModel); + + contentsCardLayout = new CardLayout(); + contentsPane.setLayout(contentsCardLayout); + + + // Now, let's add all components to contentsPane + Component[] cmp = new Component[contentTags.length]; + cmp[0] = new GeneralInfoPanel(); + cmp[1] = new PlistPanel(); + cmp[2] = new UnknownDataViewPanel(); + cmp[3] = new JPanel(); // N/I + cmp[4] = new UnknownDataViewPanel(); + cmp[5] = new JPanel(); // N/I + cmp[6] = new UnknownDataViewPanel(); + cmp[7] = new KolyPanel(); + + for(int i = 0; i < contentTags.length; ++i) + contentsPane.add(cmp[i], contentTags[i]); + + contentsList.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent lse) { + //System.out.println(lse); + if(!lse.getValueIsAdjusting()) { + int index = contentsList.getSelectedIndex(); + //System.out.println("Switching to " + index + "..."); + contentsCardLayout.show(contentsPane, contentTags[index]); + } + } + }); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + listContentsSplitter = new javax.swing.JSplitPane(); + contentsListScroller = new javax.swing.JScrollPane(); + contentsList = new javax.swing.JList(); + contentsPane = new javax.swing.JPanel(); + + contentsList.setModel(new javax.swing.AbstractListModel() { + String[] strings = { "General info", "plist", "Unknown (256 bytes)", "Block map", "Unknown (12 bytes)", "Apple Partition Map", "Unknown (X bytes)", "koly" }; + public int getSize() { return strings.length; } + public Object getElementAt(int i) { return strings[i]; } + }); + contentsList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + contentsListScroller.setViewportView(contentsList); + + listContentsSplitter.setLeftComponent(contentsListScroller); + + contentsPane.setLayout(new java.awt.CardLayout()); + + listContentsSplitter.setRightComponent(contentsPane); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(listContentsSplitter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 549, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(listContentsSplitter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 369, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JList contentsList; + private javax.swing.JScrollPane contentsListScroller; + private javax.swing.JPanel contentsPane; + private javax.swing.JSplitPane listContentsSplitter; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/org/catacombae/dmgextractor/utils/gui/GeneralInfoPanel.form b/src/org/catacombae/dmgextractor/utils/gui/GeneralInfoPanel.form new file mode 100644 index 0000000..63665fa --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/GeneralInfoPanel.form @@ -0,0 +1,56 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/org/catacombae/dmgextractor/utils/gui/GeneralInfoPanel.java b/src/org/catacombae/dmgextractor/utils/gui/GeneralInfoPanel.java new file mode 100644 index 0000000..61cbadf --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/GeneralInfoPanel.java @@ -0,0 +1,86 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +/* + * GeneralInfoPanel.java + * + * Created on den 7 november 2006, 09:52 + */ + +package org.catacombae.dmgextractor.utils.gui; + +/** + * + * @author erik + */ +public class GeneralInfoPanel extends javax.swing.JPanel { + + /** Creates new form GeneralInfoPanel */ + public GeneralInfoPanel() { + initComponents(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + + jLabel1.setText("File name:"); + + jLabel2.setText("Size:"); + + jLabel3.setText("Number of partitions:"); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jLabel1) + .add(jLabel2) + .add(jLabel3)) + .addContainerGap(244, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(jLabel1) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jLabel2) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jLabel3) + .addContainerGap(222, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/org/catacombae/dmgextractor/utils/gui/KolyPanel.form b/src/org/catacombae/dmgextractor/utils/gui/KolyPanel.form new file mode 100644 index 0000000..6aaf191 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/KolyPanel.form @@ -0,0 +1,36 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/org/catacombae/dmgextractor/utils/gui/KolyPanel.java b/src/org/catacombae/dmgextractor/utils/gui/KolyPanel.java new file mode 100644 index 0000000..b078214 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/KolyPanel.java @@ -0,0 +1,69 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +/* + * KolyPanel.java + * + * Created on den 7 november 2006, 16:43 + */ + +package org.catacombae.dmgextractor.utils.gui; + +/** + * + * @author erik + */ +public class KolyPanel extends javax.swing.JPanel { + + /** Creates new form KolyPanel */ + public KolyPanel() { + initComponents(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + jLabel1 = new javax.swing.JLabel(); + + jLabel1.setText("Koly:"); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(jLabel1) + .addContainerGap(369, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(jLabel1) + .addContainerGap(284, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/org/catacombae/dmgextractor/utils/gui/PlistPanel.form b/src/org/catacombae/dmgextractor/utils/gui/PlistPanel.form new file mode 100644 index 0000000..6c320e7 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/PlistPanel.form @@ -0,0 +1,123 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/org/catacombae/dmgextractor/utils/gui/PlistPanel.java b/src/org/catacombae/dmgextractor/utils/gui/PlistPanel.java new file mode 100644 index 0000000..f08e332 --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/PlistPanel.java @@ -0,0 +1,109 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +/* + * PlistPanel.java + * + * Created on den 7 november 2006, 09:58 + */ + +package org.catacombae.dmgextractor.utils.gui; + +/** + * + * @author erik + */ +public class PlistPanel extends javax.swing.JPanel { + + /** Creates new form PlistPanel */ + public PlistPanel() { + initComponents(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + plistTabs = new javax.swing.JTabbedPane(); + textViewScroller = new javax.swing.JScrollPane(); + textViewArea = new javax.swing.JTextArea(); + hierarchicalViewPanel = new javax.swing.JPanel(); + hierarchicalViewSplitter = new javax.swing.JSplitPane(); + xmlTreeScroller = new javax.swing.JScrollPane(); + xmlTree = new javax.swing.JTree(); + keyDataScroller = new javax.swing.JScrollPane(); + keyDataView = new javax.swing.JTextArea(); + + textViewArea.setColumns(20); + textViewArea.setRows(5); + textViewArea.setText("yo\n"); + textViewScroller.setViewportView(textViewArea); + + plistTabs.addTab("Text view", textViewScroller); + + hierarchicalViewSplitter.setDividerLocation(120); + xmlTreeScroller.setViewportView(xmlTree); + + hierarchicalViewSplitter.setLeftComponent(xmlTreeScroller); + + keyDataView.setColumns(20); + keyDataView.setRows(5); + keyDataScroller.setViewportView(keyDataView); + + hierarchicalViewSplitter.setRightComponent(keyDataScroller); + + org.jdesktop.layout.GroupLayout hierarchicalViewPanelLayout = new org.jdesktop.layout.GroupLayout(hierarchicalViewPanel); + hierarchicalViewPanel.setLayout(hierarchicalViewPanelLayout); + hierarchicalViewPanelLayout.setHorizontalGroup( + hierarchicalViewPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(hierarchicalViewSplitter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE) + ); + hierarchicalViewPanelLayout.setVerticalGroup( + hierarchicalViewPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(hierarchicalViewSplitter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 330, Short.MAX_VALUE) + ); + plistTabs.addTab("Hierarchical view", hierarchicalViewPanel); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(plistTabs, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(plistTabs, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel hierarchicalViewPanel; + private javax.swing.JSplitPane hierarchicalViewSplitter; + private javax.swing.JScrollPane keyDataScroller; + private javax.swing.JTextArea keyDataView; + private javax.swing.JTabbedPane plistTabs; + private javax.swing.JTextArea textViewArea; + private javax.swing.JScrollPane textViewScroller; + private javax.swing.JTree xmlTree; + private javax.swing.JScrollPane xmlTreeScroller; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/org/catacombae/dmgextractor/utils/gui/UnknownDataViewPanel.form b/src/org/catacombae/dmgextractor/utils/gui/UnknownDataViewPanel.form new file mode 100644 index 0000000..238894a --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/UnknownDataViewPanel.form @@ -0,0 +1,119 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/org/catacombae/dmgextractor/utils/gui/UnknownDataViewPanel.java b/src/org/catacombae/dmgextractor/utils/gui/UnknownDataViewPanel.java new file mode 100644 index 0000000..164cafe --- /dev/null +++ b/src/org/catacombae/dmgextractor/utils/gui/UnknownDataViewPanel.java @@ -0,0 +1,111 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +/* + * UnknownDataViewPanel.java + * + * Created on den 7 november 2006, 11:10 + */ + +package org.catacombae.dmgextractor.utils.gui; + +/** + * + * @author erik + */ +public class UnknownDataViewPanel extends javax.swing.JPanel { + + /** Creates new form UnknownDataViewPanel */ + public UnknownDataViewPanel() { + initComponents(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + jTabbedPane1 = new javax.swing.JTabbedPane(); + jScrollPane2 = new javax.swing.JScrollPane(); + jTextArea2 = new javax.swing.JTextArea(); + jPanel1 = new javax.swing.JPanel(); + jPanel2 = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + jComboBox1 = new javax.swing.JComboBox(); + jScrollPane1 = new javax.swing.JScrollPane(); + jTextArea1 = new javax.swing.JTextArea(); + + jTextArea2.setColumns(20); + jTextArea2.setRows(5); + jScrollPane2.setViewportView(jTextArea2); + + jTabbedPane1.addTab("Hex view", jScrollPane2); + + jLabel1.setText("Encoding:"); + jPanel2.add(jLabel1); + + jComboBox1.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "US-ASCII", "UTF-8", "UTF-16BE" })); + jPanel2.add(jComboBox1); + + jTextArea1.setColumns(20); + jTextArea1.setRows(5); + jScrollPane1.setViewportView(jTextArea1); + + org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE) + .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jPanel1Layout.createSequentialGroup() + .add(jPanel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE)) + ); + jTabbedPane1.addTab("Text view", jPanel1); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jTabbedPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(jTabbedPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JComboBox jComboBox1; + private javax.swing.JLabel jLabel1; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JTabbedPane jTabbedPane1; + private javax.swing.JTextArea jTextArea1; + private javax.swing.JTextArea jTextArea2; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/org/catacombae/dmgx/Attribute.java b/src/org/catacombae/dmgx/Attribute.java deleted file mode 100644 index 5b000bb..0000000 --- a/src/org/catacombae/dmgx/Attribute.java +++ /dev/null @@ -1,36 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -class Attribute { - public final String localName; - public final String qName; - public final String type; - public final String URI; - public final String value; - public Attribute(String localName, String qName, String type, String URI, String value) { - this.localName = localName; - this.qName = qName; - this.type = type; - this.URI = URI; - this.value = value; - } -} diff --git a/src/org/catacombae/dmgx/BuildNumber.java b/src/org/catacombae/dmgx/BuildNumber.java deleted file mode 100644 index 5ba865b..0000000 --- a/src/org/catacombae/dmgx/BuildNumber.java +++ /dev/null @@ -1,28 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -public class BuildNumber { - //[BuildEnumerator:Opening] WARNING: The following lines are managed by an external program. Do NOT change. - public static final long BUILD_NUMBER = 144L; - //[BuildEnumerator:Closing] The lines managed by an external program end here. -} - diff --git a/src/org/catacombae/dmgx/DMGExtractor.java b/src/org/catacombae/dmgx/DMGExtractor.java deleted file mode 100644 index a9cc09e..0000000 --- a/src/org/catacombae/dmgx/DMGExtractor.java +++ /dev/null @@ -1,829 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * (C) 2004 vu1tur (not the actual code but...) - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -import net.iharder.Base64; -import java.io.*; -import java.util.LinkedList; -import java.util.Iterator; -import java.util.zip.Inflater; -import java.util.zip.DataFormatException; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.swing.JOptionPane; -import javax.swing.JFileChooser; -import javax.swing.ProgressMonitor; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -public class DMGExtractor { - public static final String APPNAME = "DMGExtractor 0.51pre"; - public static final String BUILDSTRING = "(Build #" + BuildNumber.BUILD_NUMBER + ")"; - public static final boolean DEBUG = false; - // Constants defining block types in the dmg file - public static final int BT_ADC = 0x80000004; - public static final int BT_ZLIB = 0x80000005; - public static final int BT_BZIP2 = 0x80000006; - public static final int BT_COPY = 0x00000001; - public static final int BT_ZERO = 0x00000002; - public static final int BT_END = 0xffffffff; - public static final int BT_UNKNOWN = 0x7ffffffe; - public static final long PLIST_ADDRESS_1 = 0x1E0; - public static final long PLIST_ADDRESS_2 = 0x128; - public static final String BACKSPACE79 = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; -// public static PrintStream stdout = System.out; -// public static PrintStream stderr = System.err; - public static BufferedReader stdin = - new BufferedReader(new InputStreamReader(System.in)); - - public static boolean verbose = false; - public static boolean graphical = false; - public static String startupCommand = "java DMGExtractor"; - public static File dmgFile = null; - public static File isoFile = null; - - public static ProgressMonitor progmon; - - public static void main(String[] args) throws Exception { - try { - notmain(args); - } catch(Exception e) { - if(graphical) - JOptionPane.showMessageDialog(null, "The program encountered an unexpected error: " + e.toString() + - "\nClosing...", "Error", JOptionPane.ERROR_MESSAGE); - throw e; - } - } - - public static void notmain(String[] args) throws Exception { - System.setProperty("swing.aatext", "true"); //Antialiased text - try { javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName()); } - catch(Exception e) {} - - if(DEBUG) verbose = true; - - parseArgs(args); - - printlnVerbose("Processing: " + dmgFile); - RandomAccessFile dmgRaf = new RandomAccessFile(dmgFile, "r"); - RandomAccessFile isoRaf = null; - boolean testOnly = false; - if(isoFile != null) { - isoRaf = new RandomAccessFile(isoFile, "rw"); - isoRaf.setLength(0); - printlnVerbose("Extracting to: " + isoFile); - } - else { - testOnly = true; - printlnVerbose("Simulating extraction..."); - } - - dmgRaf.seek(dmgRaf.length()-PLIST_ADDRESS_1); - long plistBegin1 = dmgRaf.readLong(); - long plistEnd = dmgRaf.readLong(); - dmgRaf.seek(dmgRaf.length()-PLIST_ADDRESS_2); - long plistBegin2 = dmgRaf.readLong(); - long plistSize = dmgRaf.readLong(); - - if(DEBUG) { - println("Read addresses:", - " " + plistBegin1, - " " + plistBegin2); - } - if(plistBegin1 != plistBegin2) { - println("Addresses not equal! Assumption broken... =/", - plistBegin1 + " != " + plistBegin2); - System.exit(0); - } - if(plistSize != (plistEnd-plistBegin1)) { - println("plistSize field does not match plistEnd marker!", - "plistSize=" + plistSize + " plistBegin1=" + plistBegin1 + " plistEnd=" + plistEnd + " plistEnd-plistBegin1=" + (plistEnd-plistBegin1)); - } - printlnVerbose("Jumping to address..."); - dmgRaf.seek(plistBegin1); - byte[] buffer = new byte[(int)plistSize]; - dmgRaf.read(buffer); - - InputStream is = new ByteArrayInputStream(buffer); - - NodeBuilder handler = new NodeBuilder(); - SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); - try { -// System.out.println("validation: " + saxParser.getProperty("validation")); -// System.out.println("external-general-entities: " + saxParser.getProperty("external-general-entities")); -// System.out.println("external-parameter-entities: " + saxParser.getProperty("external-parameter-entities")); -// System.out.println("is-standalone: " + saxParser.getProperty("is-standalone")); -// System.out.println("lexical-handler: " + saxParser.getProperty("lexical-handler")); -// System.out.println("parameter-entities: " + saxParser.getProperty("parameter-entities")); -// System.out.println("namespaces: " + saxParser.getProperty("namespaces")); -// System.out.println("namespace-prefixes: " + saxParser.getProperty("namespace-prefixes")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println(": " + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); -// System.out.println("" + saxParser.getProperty("")); - System.out.println("isValidating: " + saxParser.isValidating()); - saxParser.parse(is, handler); - } catch(SAXException se) { - se.printStackTrace(); - System.err.println("Could not read the partition list... exiting."); - System.exit(1); - } - - XMLNode[] rootNodes = handler.getRoots(); - if(rootNodes.length != 1) { - println("Could not parse DMG-file!"); - System.exit(0); - } - - /* Ok, now we have a tree built from the XML-document. Let's walk to the right place. */ - /* cd plist - cd dict - cdkey resource-fork (type:dict) - cdkey blkx (type:array) */ - XMLNode current; - XMLElement[] children; - boolean keyFound; - current = rootNodes[0]; //We are at plist... probably (there should be only one root node) - - current = current.cd("dict"); - current = current.cdkey("resource-fork"); - current = current.cdkey("blkx"); - printlnVerbose("Found " + current.getChildren().length + " partitions:"); - - byte[] tmp = new byte[0x40000]; - byte[] otmp = new byte[0x40000]; - - byte[] zeroblock = new byte[4096]; - /* I think java always zeroes its arrays on creation... - but let's play safe. */ - for(int y = 0; y < zeroblock.length; ++y) - zeroblock[y] = 0; - - LinkedList blocks = new LinkedList(); - - //long lastOffs = 0; - long lastOutOffset = 0; - long lastInOffset = 0; - long totalSize = 0; - boolean errorsFound = false; - reportProgress(0); - for(XMLElement xe : current.getChildren()) { - if(progmon != null && progmon.isCanceled()) System.exit(0); - if(xe instanceof XMLNode) { - XMLNode xn = (XMLNode)xe; - byte[] data = Base64.decode(xn.getKeyValue("Data")); - - long partitionSize = calculatePartitionSize(data); - totalSize += partitionSize; - - printlnVerbose(" " + xn.getKeyValue("Name")); - printlnVerbose(" ID: " + xn.getKeyValue("ID")); - printlnVerbose(" Attributes: " + xn.getKeyValue("Attributes")); - printlnVerbose(" Partition map data length: " + data.length + " bytes"); - printlnVerbose(" Partition size: " + partitionSize + " bytes"); - if(verbose) { - printlnVerbose(" Dumping blkx..."); - FileOutputStream fos = new FileOutputStream(xn.getKeyValue("ID") + ".blkx"); - fos.write(data); - fos.close(); - } - - if(DEBUG) { - File dumpFile = new File("data " + xn.getKeyValue("ID") + ".bin"); - println(" Dumping partition map to file: " + dumpFile); - - FileOutputStream dump = new FileOutputStream(dumpFile); - dump.write(data); - dump.close(); - } - - int offset = 0xCC; - int blockType = 0; - - /* Offset of the input data for the current block in the input file */ - long inOffset = 0; - /* Size of the input data for the current block */ - long inSize = 0; - /* Offset of the output data for the current block in the output file */ - long outOffset = 0; - /* Size of the output data (possibly larger than inSize because of - decompression, zero expansion...) */ - long outSize = 0; - - long lastByteReadInBlock = -1; - - boolean addInOffset = false; - - //, lastInOffs = 0; - int blockCount = 0; - long previousPercentage = -1; - while(blockType != BT_END) { - if(progmon != null && progmon.isCanceled()) System.exit(0); - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); - int bytesSkipped = 0; - while(bytesSkipped < offset) - bytesSkipped += dis.skipBytes(offset-bytesSkipped); - - blockType = dis.readInt(); - int skipped = dis.readInt(); //Skip 4 bytes forward - outOffset = dis.readLong()*0x200;//(dis.readInt() & 0xffffffffL)*0x200; //unsigned int -> long - //dis.readInt(); //Skip 4 bytes forward - outSize = dis.readLong()*0x200;//(dis.readInt() & 0xffffffffL)*0x200; //unsigned int -> long - inOffset = dis.readLong();// & 0xffffffffL; //unsigned int -> long - //dis.readInt(); //Skip 4 bytes forward - inSize = dis.readLong();//dis.readInt() & 0xffffffffL; //unsigned int -> long - - blocks.add(new DMGBlock(blockType, skipped, outOffset, outSize, inOffset, inSize)); - - if(lastByteReadInBlock == -1) - lastByteReadInBlock = inOffset; - lastByteReadInBlock += inSize; - - /* The lines below are a "hack" that I had to do to make dmgx work with - certain dmg-files. I don't understand the issue at all, which is why - this hack is here, but sometimes inOffset == 0 means that it is 0 - relative to the previous partition's last inOffset. And sometimes it - doesn't (meaning the actual position 0 in the dmg file). */ - if(addInOffset) - inOffset += lastInOffset; - else if(inOffset == 0) { - addInOffset = true; - inOffset += lastInOffset; - } - outOffset += lastOutOffset; - - if(DEBUG) { - println("outOffset=" + outOffset + " outSize=" + outSize + - " inOffset=" + inOffset + " inSize=" + inSize + - " lastOutOffset=" + lastOutOffset + " lastInOffset=" + lastInOffset - /*+ " lastInOffs=" + lastInOffs + " lastOffs=" + lastOffs*/); - } - - if(blockType == BT_ADC) { - println(" " + blockCount + ". BT_ADC not supported."); - if(!testOnly) - System.exit(0); - } - else if(blockType == BT_ZLIB) { - if(DEBUG) - println(" " + blockCount + ". BT_ZLIB processing..."); - - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_ZLIB FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - dmgRaf.seek(/*lastOffs+*/inOffset); - - if(tmp.length < inSize) - tmp = new byte[(int)inSize]; - - long totalBytesRead = 0; - while(totalBytesRead < inSize) { - totalBytesRead += dmgRaf.read(tmp, (int)totalBytesRead, Math.min((int)(inSize-totalBytesRead), tmp.length)); - } - long progressPercentage = dmgRaf.getFilePointer()*100/dmgRaf.length(); - if(progressPercentage != previousPercentage) { - reportProgress(progressPercentage); - previousPercentage = progressPercentage; - } - - Inflater inflater = new Inflater(); - inflater.setInput(tmp, 0, (int)totalBytesRead); - - if(otmp.length < outSize) - otmp = new byte[(int)outSize]; - - int bytesInflated = 0; - while(true) { - try { - int counter = 0; - while(bytesInflated < outSize && counter++ < 10) { - int old = bytesInflated; - bytesInflated += inflater.inflate(otmp, bytesInflated, (int)(outSize-bytesInflated)); - if(old == bytesInflated) - println("Nothing new! finished()=" + inflater.finished() + " needsInput()=" + inflater.needsInput() + " needsDictionary()=" + inflater.needsDictionary() + " getAdler()=" + inflater.getAdler() + " getBytesRead()=" + inflater.getBytesRead() + " getBytesWritten()=" + inflater.getBytesWritten() + " getRemaining()=" + inflater.getRemaining()); - } - //System.out.println(" Inflated " + bytesInflated + " bytes. Left in buffer: " + inflater.getRemaining() + " bytes inSize="+inSize+" outSize="+outSize); - - if(inflater.getRemaining() == 0) { - //System.out.println(" done!"); - break; - } - else { - println(" " + blockCount + ". BT_ZLIB ERROR: otmp contents lost! (should not happen...)", - " outSize=" + outSize + " inSize=" + inSize + " tmp.length=" + tmp.length + " otmp.length=" + otmp.length + " bytesInflated=" + bytesInflated + " inflater.getRemaining()=" + inflater.getRemaining()); -// if(bytesInflated == 0) - throw new RuntimeException("WTF"); - } - } - catch(DataFormatException dfe) { - println(" " + blockCount + ". BT_ZLIB Could not decode..."); - if(!DEBUG) { - println("outOffset=" + outOffset + " outSize=" + outSize + - " inOffset=" + inOffset + " inSize=" + inSize + - " lastOutOffset=" + lastOutOffset + " lastInOffset=" + lastInOffset); - } - dfe.printStackTrace(); - if(!testOnly) - System.exit(0); - else { - println(" Testing mode, so continuing..."); - //System.exit(0); - errorsFound = true; - break; - } - } - - } - inflater.end(); - - if(!testOnly) - isoRaf.write(otmp, 0, (int)outSize); - - //lastInOffs = inOffset+inSize; - } - else if(blockType == BT_BZIP2) { - println(" " + blockCount + ". BT_BZIP2 not currently supported."); - if(!testOnly) - System.exit(0); - } - else if(blockType == BT_COPY) { - if(DEBUG) - println(" " + blockCount + ". BT_COPY processing..."); - - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_COPY FP != outOffset (" + isoRaf.getFilePointer() + " != " + outOffset + ")"); - dmgRaf.seek(/*lastOffs+*/inOffset); - - int bytesRead = dmgRaf.read(tmp, 0, Math.min((int)inSize, tmp.length)); - long totalBytesRead = bytesRead; - while(bytesRead != -1) { - long progressPercentage = dmgRaf.getFilePointer()*100/dmgRaf.length(); - if(progressPercentage != previousPercentage) { - reportProgress(progressPercentage); - previousPercentage = progressPercentage; - } - - - if(!testOnly) - isoRaf.write(tmp, 0, bytesRead); - if(totalBytesRead >= inSize) - break; - bytesRead = dmgRaf.read(tmp, 0, Math.min((int)(inSize-totalBytesRead), tmp.length)); - if(bytesRead > 0) - totalBytesRead += bytesRead; - } - - //lastInOffs = inOffset+inSize; - } - else if(blockType == BT_ZERO) { - if(DEBUG) - println(" " + blockCount + ". BT_ZERO processing..."); - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_ZERO FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - long progressPercentage = dmgRaf.getFilePointer()*100/dmgRaf.length(); - if(progressPercentage != previousPercentage) { - reportProgress(progressPercentage); - previousPercentage = progressPercentage; - } - - long numberOfZeroBlocks = outSize/zeroblock.length; - int numberOfRemainingBytes = (int)(outSize%zeroblock.length); - for(int j = 0; j < numberOfZeroBlocks; ++j) { - if(!testOnly) - isoRaf.write(zeroblock); - } - if(!testOnly) - isoRaf.write(zeroblock, 0, numberOfRemainingBytes); - - //lastInOffs = inOffset+inSize; - } - else if(blockType == BT_UNKNOWN) { - /* I have no idea what this blocktype is... but it's common, and usually - doesn't appear more than 2-3 times in a dmg. As long as its input and - output sizes are 0, there's no reason to complain... is there? */ - if(DEBUG) - println(" " + blockCount + ". BT_UNKNOWN processing..."); - if(!(inSize == 0 && outSize == 0)) { - println(" " + blockCount + ". WARNING! Blocktype BT_UNKNOWN had non-zero sizes...", - " inSize=" + inSize + ", outSize=" + outSize); - //println(" The author of the program would be pleased if you contacted him about this."); - // ...or would I? - } - } - else if(blockType == BT_END) { - if(DEBUG) - println(" " + blockCount + ". BT_END processing..."); - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". BT_END FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - //lastOffs += lastInOffs; - lastOutOffset = outOffset; - lastInOffset += lastByteReadInBlock; - } - else { - println(" " + blockCount + ". WARNING: previously unseen blocktype " + blockType + " [0x" + Integer.toHexString(blockType) + "]", - " " + blockCount + ". outOffset=" + outOffset + " outSize=" + outSize + " inOffset=" + inOffset + " inSize=" + inSize); - - if(!testOnly && isoRaf.getFilePointer() != outOffset) - println(" " + blockCount + ". unknown blocktype FP != outOffset (" + - isoRaf.getFilePointer() + " != " + outOffset + ")"); - - } - - offset += 0x28; - ++blockCount; - } - } - } - //printlnVerbose("Progress: 100% Done!"); - reportProgress(100); - String errors = errorsFound?"There were errors...":"No errors reported."; - if(!graphical) { - newline(); - println(errors); - printlnVerbose("Total extracted bytes: " + totalSize + " B"); - } - else { - progmon.close(); - JOptionPane.showMessageDialog(null, "Extraction complete! " + errors + "\n" + - "Total extracted bytes: " + totalSize + " B", - "Information", JOptionPane.INFORMATION_MESSAGE); - System.exit(0); - } -// System.out.println("blocks.size()=" + blocks.size()); -// for(DMGBlock b : blocks) -// System.out.println(" " + b.toString()); - LinkedList merged = mergeBlocks(blocks); -// System.out.println("merged.size()=" + merged.size()); -// for(DMGBlock b : merged) -// System.out.println(" " + b.toString()); - System.out.println("Extracting all the parts not containing block data from source file:"); - int i = 1; - DMGBlock previous = null; - for(DMGBlock b : merged) { - if(previous == null && b.inOffset > 0) { - String filename = i++ + ".block"; - System.out.print(" " + filename + "..."); - FileOutputStream curFos = new FileOutputStream(new File(filename)); - dmgRaf.seek(0); - byte[] data = new byte[(int)(b.inOffset)]; - dmgRaf.read(data); - curFos.write(data); - curFos.close(); - } - else if(previous != null) { - String filename = i++ + ".block"; - System.out.print(" " + filename + "..."); - FileOutputStream curFos = new FileOutputStream(new File(filename)); - dmgRaf.seek(previous.inOffset+previous.inSize); - byte[] data = new byte[(int)(b.inOffset-(previous.inOffset+previous.inSize))]; - dmgRaf.read(data); - curFos.write(data); - curFos.close(); - } - previous = b; - } - if(previous.inOffset+previous.inSize != dmgRaf.length()) { - String filename = i++ + ".block"; - System.out.print(" " + filename + "..."); - FileOutputStream curFos = new FileOutputStream(new File(filename)); - dmgRaf.seek(previous.inOffset+previous.inSize); - byte[] data = new byte[(int)(dmgRaf.length()-(previous.inOffset+previous.inSize))]; - dmgRaf.read(data); - curFos.write(data); - curFos.close(); - } - dmgRaf.close(); - System.out.println("done!"); - } - - public static void parseArgs(String[] args) { - boolean parseSuccessful = false; - try { - /* Take care of the options... */ - int i; - for(i = 0; i < args.length; ++i) { - String cur = args[i]; - if(!cur.startsWith("-")) - break; - else if(cur.equals("-gui")) - graphical = true; - else if(cur.equals("-v")) - verbose = true; - else if(cur.equals("-startupcommand")) { - startupCommand = args[i+1]; - ++i; - } - } - - println(APPNAME + " " + BUILDSTRING, - "Copyright (c) 2006 Erik Larsson ", - " written from the source code to the original dmg2iso program", - " Copyright (c) 2004 vu1tur ", - " also using the iharder Base64 Encoder/Decoder ", - "", - "This program is distributed under the GNU General Public License version 2 or", - "later.", - "See for the details.", - ""); - - if(i == args.length) { - dmgFile = getInputFileFromUser(); - if(dmgFile == null) - System.exit(0); - if(getOutputConfirmationFromUser()) { - isoFile = getOutputFileFromUser(); - if(isoFile == null) - System.exit(0); - } - } - else { - dmgFile = new File(args[i++]); - if(!dmgFile.exists()) { - println("File \"" + dmgFile + "\" could not be found!"); - System.exit(0); - } - - if(i == args.length-1) - isoFile = new File(args[i]); - else if(i != args.length) - throw new Exception(); - } - - parseSuccessful = true; - } catch(Exception e) { - println(); - println(" usage: " + startupCommand + " [options] []"); - println(" if an iso-file is not supplied, the program will simulate an extraction"); - println(" (useful for detecting errors in dmg-files)"); - println(); - System.exit(0); - } - } - - public static long calculatePartitionSize(byte[] data) throws IOException { - long partitionSize = 0; - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); - long totalBytesRead; - totalBytesRead = 0; - while(totalBytesRead < 0xCC) - totalBytesRead += dis.skip(0xCC); - - while(totalBytesRead < data.length) { - int bytesRead = 0; - while(bytesRead < 0x10) - bytesRead += dis.skip(0x10-bytesRead); - - partitionSize += dis.readLong()*0x200; - bytesRead += 0x8; - - while(bytesRead < 0x28) - bytesRead += dis.skip(0x28-bytesRead); - totalBytesRead += bytesRead; - } - return partitionSize; - } - - /** Never used. Java is big-endian. */ - public static int swapEndian(int i) { - return - ((i & 0xff000000) >> 24) | - ((i & 0x00ff0000) >> 8 ) | - ((i & 0x0000ff00) << 8 ) | - ((i & 0x000000ff) << 24); - } - - public static void printCurrentLine(String s) { - System.out.print(BACKSPACE79); - System.out.print(s); - } - public static void println() { - System.out.print(BACKSPACE79); - System.out.println(); - } - public static void println(String... lines) { - if(!graphical) { - System.out.print(BACKSPACE79); - for(String s : lines) - System.out.println(s); - } - else { - String resultString = null; - for(String s : lines) { - if(resultString == null) - resultString = s; - else - resultString += "\n" + s; - } - JOptionPane.showMessageDialog(null, resultString, - APPNAME, JOptionPane.INFORMATION_MESSAGE); - } - } - public static void printlnVerbose() { - if(verbose) { - System.out.print(BACKSPACE79); - System.out.println(); - } - } - public static void printlnVerbose(String... lines) { - if(verbose) { - System.out.print(BACKSPACE79); - for(String s : lines) - System.out.println(s); - } - } - - public static void newline() { - System.out.println(); - } - - public static void reportProgress(long progressPercentage) { - if(!graphical) { - printCurrentLine("--->Progress: " + progressPercentage + "%"); - } - else { - if(progmon == null) { - progmon = new ProgressMonitor(null, "Extracting dmg to iso...", "0%", 0, 100); - progmon.setProgress(0); - progmon.setMillisToPopup(0); - } - progmon.setProgress((int)progressPercentage); - progmon.setNote(progressPercentage + "%"); - } - } - - public static File getInputFileFromUser() throws IOException { - if(!graphical) { - //String s = ""; - while(true) { - printCurrentLine("Please specify the path to the dmg file to extract from: "); - File f = new File(stdin.readLine().trim()); - while(!f.exists()) { - println("File does not exist!"); - printCurrentLine("Please specify the path to the dmg file to extract from: "); - f = new File(stdin.readLine().trim()); - } - return f; - } - } - else { - SimpleFileFilter sff = new SimpleFileFilter(); - sff.addExtension("dmg"); - sff.setDescription("DMG disk image files"); - JFileChooser jfc = new JFileChooser(); - jfc.setFileFilter(sff); - jfc.setMultiSelectionEnabled(false); - jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); - jfc.setDialogTitle("Choose the dmg-file to read..."); - while(true) { - if(jfc.showDialog(null, "Open") == JFileChooser.APPROVE_OPTION) { - File f = jfc.getSelectedFile(); - if(f.exists()) - return f; - else - JOptionPane.showMessageDialog(null, "The file does not exist! Choose again...", - "Error", JOptionPane.ERROR_MESSAGE); - } - else - return null; - } - } - } - public static boolean getOutputConfirmationFromUser() throws IOException { - if(!graphical) { - String s = ""; - while(true) { - printCurrentLine("Do you want to specify an output file (y/n)? "); - s = stdin.readLine().trim(); - if(s.equalsIgnoreCase("y")) - return true; - else if(s.equalsIgnoreCase("n")) - return false; - } - } - else { - return JOptionPane.showConfirmDialog(null, "Do you want to specify an output file?", - "Confirmation", JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; - } - } - public static File getOutputFileFromUser() throws IOException { - final String msg1 = "Please specify the path of the iso file to extract to: "; - final String msg2 = "The file already exists. Do you want to overwrite?"; - if(!graphical) { - while(true) { - printCurrentLine(msg1); - File f = new File(stdin.readLine().trim()); - while(f.exists()) { - while(true) { - printCurrentLine(msg2 + " (y/n)? "); - String s = stdin.readLine().trim(); - if(s.equalsIgnoreCase("y")) - return f; - else if(s.equalsIgnoreCase("n")) - break; - } - printCurrentLine(msg1); - f = new File(stdin.readLine().trim()); - } - return f; - } - } - else { - JFileChooser jfc = new JFileChooser(); - jfc.setMultiSelectionEnabled(false); - jfc.setFileSelectionMode(JFileChooser.FILES_ONLY); - jfc.setDialogTitle("Choose the output iso-file..."); - while(true) { - if(jfc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { - File f = jfc.getSelectedFile(); - if(!f.exists()) - return f; - else if(JOptionPane.showConfirmDialog(null, msg2, "Confirmation", JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - return f; - } - } - else - return null; - } - } - } - - public static LinkedList mergeBlocks(LinkedList blockList) { - LinkedList result = new LinkedList(); - Iterator it = blockList.iterator(); - DMGBlock previous = it.next(); - DMGBlock current; - while(it.hasNext()) { - current = it.next(); - if(current.inSize != 0) { - if(current.inOffset == previous.inOffset+previous.inSize) { - DMGBlock mergedBlock = new DMGBlock(previous.blockType, previous.skipped, previous.outOffset, previous.outSize+current.outSize, previous.inOffset, previous.inSize+current.inSize); - previous = mergedBlock; - } - else { - result.addLast(previous); - previous = current; - } - } - } - result.addLast(previous); - return result; - } - - public static class DMGBlock { - public int blockType; - public int skipped; - public long outOffset; - public long outSize; - public long inOffset; - public long inSize; - - public DMGBlock(int blockType, int skipped, long outOffset, long outSize, long inOffset, long inSize) { - this.blockType = blockType; - this.skipped = skipped; - this.outOffset = outOffset; - this.outSize = outSize; - this.inOffset = inOffset; - this.inSize = inSize; - } - - public String toString() { - return "[type: 0x" + Integer.toHexString(blockType) + " skipped: 0x" + Integer.toHexString(skipped) + " outOffset: " + outOffset + " outSize: " + outSize + " inOffset: " + inOffset + " inSize: " + inSize + "]"; - } - } -} - diff --git a/src/org/catacombae/dmgx/DMGExtractorGraphical.java b/src/org/catacombae/dmgx/DMGExtractorGraphical.java deleted file mode 100644 index 7aa80e3..0000000 --- a/src/org/catacombae/dmgx/DMGExtractorGraphical.java +++ /dev/null @@ -1,29 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -public class DMGExtractorGraphical { - public static void main(String[] args) throws Exception { - String[] newargs = new String[1]; - newargs[0] = "-gui"; - DMGExtractor.main(newargs); - } -} diff --git a/src/org/catacombae/dmgx/DMGInfo.java b/src/org/catacombae/dmgx/DMGInfo.java deleted file mode 100644 index 479cd50..0000000 --- a/src/org/catacombae/dmgx/DMGInfo.java +++ /dev/null @@ -1,152 +0,0 @@ -package org.catacombae.dmgx; - -import java.io.*; -import javax.swing.*; - -public class DMGInfo { - public static void main(String[] args) throws IOException { - RandomAccessFile inRaf = new RandomAccessFile(args[0], "r"); - - // Check opening signature "koly" - inRaf.seek(inRaf.length()-512); - byte[] koly = new byte[4]; - inRaf.readFully(koly); - String kolySignature = new String(koly, "US-ASCII"); - if(!kolySignature.equals("koly")) - System.out.println("ERROR: Signature incorrect. Found \"" + kolySignature + "\" instead of \"koly\"."); - else - System.out.println("\"koly\" signature OK."); - - // Read partition list start location 1 and end location - inRaf.seek(inRaf.length()-0x1E0); - - // -0x1E0: address to plist xml structure (8 bytes) - long plistAddress1 = inRaf.readLong(); - System.out.println("Address to plist: 0x" + Long.toHexString(plistAddress1)); - - - // -0x1D8: address to end of plist xml structure (8 bytes) - long plistEndAddress = inRaf.readLong(); - System.out.println("Address to end of plist: 0x" + Long.toHexString(plistEndAddress)); - System.out.println(" Implication: plist size = " + (plistEndAddress-plistAddress1) + " B"); - - - long unknown_0x1D0 = inRaf.readLong(); - - - long unknown_0x1C8 = inRaf.readLong(); - if(unknown_0x1C8 != 0x0000000100000001L) - System.out.println("Assertion failed! unknown_0x1C8 == 0x" + - Long.toHexString(unknown_0x1C8) + " and not 0x0000000100000001"); - - - long unknown_0x1C0 = inRaf.readLong(); - - - long unknown_0x1B8 = inRaf.readLong(); - System.out.println("Some kind of signature? Value: 0x" + Long.toHexString(unknown_0x1B8)); - - - long unknown_0x1B0 = inRaf.readLong(); - if(unknown_0x1B0 != 0x0000000200000020L) - System.out.println("Assertion failed! unknown_0x1B0 == 0x" + - Long.toHexString(unknown_0x1B0) + " and not 0x0000000200000020"); - - - int unknown_0x1A8 = inRaf.readInt(); - - - int unknown_0x1A4 = inRaf.readInt(); - System.out.println("Some kind of unit size? Value: 0x" + - Integer.toHexString(unknown_0x1A4) + " / " + unknown_0x1A4); - - - // Unknown chunk of data (120 bytes) - byte[] unknown_0x1A0 = new byte[120]; - inRaf.readFully(unknown_0x1A0); - - - // -0x128: address to beginning of plist xml structure (second occurrence) (8 bytes) - long plistAddress2 = inRaf.readLong(); - System.out.println("Address to plist (2): 0x" + Long.toHexString(plistAddress2)); - - - // -0x120: size of plist xml structure (8 bytes) - long plistSize = inRaf.readLong(); - System.out.println("plist size: " + plistSize + " B"); - - - // Unknown chunk of data (120 bytes) - byte[] unknown_0x118 = new byte[120]; - inRaf.readFully(unknown_0x118); - - - // -0x0A0: Checksum type identifier (4 bytes) - System.out.print("Checksum type"); - int cs_type = inRaf.readInt(); - if(cs_type == 0x00000002) - System.out.println(": CRC-32"); - else if(cs_type == 0x00000004) - System.out.println(": MD5"); - else - System.out.println(" unknown! Data: 0x" + Integer.toHexString(cs_type)); - - - // -0x09C: Length of checksum in bits (4 bytes) - int cs_length = inRaf.readInt(); - System.out.println("Checksum length: " + cs_length + " bits"); - - - // -0x098: Checksum ((cs_length/8) bytes) - byte[] checksum = new byte[cs_length/8]; - inRaf.readFully(checksum); - System.out.println("Checksum: 0x" + byteArrayToHexString(checksum).toUpperCase()); - - /* - if(unknown_0x != 0xL) - System.out.println("Assertion failed! unknown_0x == 0x" + Long.toHexString(unknown_0x) + " and not 0xL"); - */ - } - - public static String byteArrayToHexString(byte[] array) { - StringBuilder result = new StringBuilder(); - for(byte b : array) { - String s = Integer.toHexString(b & 0xFF); - if(s.length() == 1) - s = "0" + s; - result.append(s); - } - return result.toString(); - } -} - -class DMGInfoFrame extends JFrame { - private JTabbedPane mainPane; - - public DMGInfoFrame() { - super("DMGInfo"); - - mainPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); - - StatisticsPanel statisticsPanel; - statisticsPanel = new StatisticsPanel(); - //mainPane.addTab(statisticsPanel, "Statistics"); - } -} - -class StatisticsPanel extends JPanel { - JPanel blocktypeCountPanel; - - public StatisticsPanel(/*DMGFile dmgFile*/) {} -} - -/* -class DMGFile extends RandomAccessFile { - public DMGFile(File file, String mode) { - super(file, mode); - } - public DMGFile(String name, String mode) { - super(name, mode); - } -} -*/ diff --git a/src/org/catacombae/dmgx/DMGInfoWindow.java b/src/org/catacombae/dmgx/DMGInfoWindow.java deleted file mode 100644 index 03230dd..0000000 --- a/src/org/catacombae/dmgx/DMGInfoWindow.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.catacombae.dmgx; - -import net.iharder.dnd.FileDrop; -import org.catacombae.dmgx.gui.*; -import java.awt.*; -import javax.swing.*; - -public class DMGInfoWindow extends JFrame { - private DMGInfoPanel infoPanel; - - public DMGInfoWindow() { - infoPanel = new DMGInfoPanel(); - add(infoPanel, BorderLayout.CENTER); - - // Register handler for file drag&drop events - new FileDrop(this, new FileDrop.Listener() { - public void filesDropped(java.io.File[] files) { - if(files.length > 0) - ;//loadFile(files[0]); - } - }); - - pack(); - setLocationRelativeTo(null); - - } - public static void main(String[] args) { - new DMGInfoWindow().setVisible(true); - } -} \ No newline at end of file diff --git a/src/org/catacombae/dmgx/DMGMetadata.java b/src/org/catacombae/dmgx/DMGMetadata.java deleted file mode 100644 index 725194c..0000000 --- a/src/org/catacombae/dmgx/DMGMetadata.java +++ /dev/null @@ -1,348 +0,0 @@ -package org.catacombae.dmgx; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.DataOutput; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.RandomAccessFile; -import java.io.UnsupportedEncodingException; -import java.util.LinkedList; - -public class DMGMetadata { - public static final long PLIST_ADDRESS_1 = 0x1E0; - public static final long PLIST_ADDRESS_2 = 0x128; - - public final byte[] rawData; - - public final byte[] plistXmlData; - public final byte[] unknown1_256; - public PartitionBlockList[] blockLists; - public final byte[] unknown2_12; - public APMPartition[] partitions; - public final byte[] unknown3_unknown; - public final byte[] koly; - - public DMGMetadata(RandomAccessFile dmgFile) throws IOException { - dmgFile.seek(dmgFile.length()-PLIST_ADDRESS_1); - long plistBegin1 = dmgFile.readLong(); - long plistEnd = dmgFile.readLong(); - dmgFile.seek(dmgFile.length()-PLIST_ADDRESS_2); - long plistBegin2 = dmgFile.readLong(); - long plistSize = dmgFile.readLong(); - - rawData = new byte[(int)(dmgFile.length()-plistBegin1)]; - dmgFile.seek(plistBegin1); - dmgFile.readFully(rawData); - - plistXmlData = new byte[(int)plistSize]; - dmgFile.seek(plistBegin1); - dmgFile.readFully(plistXmlData); - - unknown1_256 = new byte[256]; - dmgFile.readFully(unknown1_256); - - LinkedList blockListList = new LinkedList(); - int length = dmgFile.readInt(); - byte[] fourcc = new byte[4]; - dmgFile.readFully(fourcc); - String fourccString = new String(fourcc, "US-ASCII"); - dmgFile.seek(dmgFile.getFilePointer()-4); - while(fourccString.equals("mish")) { - blockListList.add(new PartitionBlockList(dmgFile, length)); - length = dmgFile.readInt(); - dmgFile.readFully(fourcc); - fourccString = new String(fourcc, "US-ASCII"); - dmgFile.seek(dmgFile.getFilePointer()-4); - } - blockLists = blockListList.toArray(new PartitionBlockList[blockListList.size()]); - - unknown2_12 = new byte[12]; - dmgFile.readFully(unknown2_12); - - LinkedList partitionList = new LinkedList(); - byte[] currentPartitionEntry = new byte[0x200]; - dmgFile.readFully(currentPartitionEntry); - byte[] pmSig = new byte[2]; - pmSig[0] = currentPartitionEntry[0]; - pmSig[1] = currentPartitionEntry[1]; - while(new String(pmSig, "US-ASCII").equals("PM")) { - partitionList.addLast(new APMPartition(currentPartitionEntry)); - dmgFile.readFully(currentPartitionEntry); - pmSig[0] = currentPartitionEntry[0]; - pmSig[1] = currentPartitionEntry[1]; - } - while(onlyZeros(currentPartitionEntry)) - dmgFile.readFully(currentPartitionEntry); - partitions = partitionList.toArray(new APMPartition[partitionList.size()]); - - unknown3_unknown = new byte[(int)(dmgFile.length()-dmgFile.getFilePointer()-512)]; - dmgFile.readFully(unknown3_unknown); - - koly = new byte[512]; - dmgFile.seek(dmgFile.length()-koly.length); - dmgFile.readFully(koly); - - if(dmgFile.getFilePointer() != dmgFile.length()) - System.out.println("MISCALCULATION! FP=" + dmgFile.getFilePointer() + " LENGTH=" + dmgFile.length()); - } - - public void printInfo(PrintStream ps) { - ps.println("block list:"); - for(PartitionBlockList pbl : blockLists) - pbl.printInfo(ps); - ps.println("partitions:"); - for(APMPartition ap : partitions) - ap.printPartitionInfo(ps); - } - - private static boolean onlyZeros(byte[] array) { - for(int i = 0; i < array.length; ++i) { - if(array[i] != 0) - return false; - } - return true; - } - - public static class PartitionBlockList { - public final byte[] header = new byte[0xCC]; - public final BlockDescriptor[] descriptors; - - public PartitionBlockList(byte[] entryData) throws IOException { - this(new DataInputStream(new ByteArrayInputStream(entryData)), entryData.length); - } - - public PartitionBlockList(DataInput di, int length) throws IOException { - int position = 0; - di.readFully(header); - position += header.length; - LinkedList descs = new LinkedList(); - while(position < length) { - descs.addLast(new BlockDescriptor(di)); - position += 0x28; - } - descriptors = descs.toArray(new BlockDescriptor[descs.size()]); - } - - public void printInfo(PrintStream ps) { - for(BlockDescriptor bd : descriptors) - ps.println(bd.toString()); - } - } - - public static class BlockDescriptor { - // Known block types - public static final int BT_COPY = 0x00000001; - public static final int BT_ZERO = 0x00000002; - public static final int BT_ZLIB = 0x80000005; - public static final int BT_END = 0xffffffff; - public static final int BT_UNKNOWN1 = 0x7ffffffe; - private static final int[] KNOWN_BLOCK_TYPES = { BT_COPY, - BT_ZERO, - BT_ZLIB, - BT_END, - BT_UNKNOWN1 }; - private static final String[] KNOWN_BLOCK_TYPE_NAMES = { "BT_COPY", - "BT_ZERO", - "BT_ZLIB", - "BT_END", - "BT_UNKNOWN1" }; - - private int blockType; - private int unknown; - private long outOffset; - private long outSize; - private long inOffset; - private long inSize; - - public BlockDescriptor() {} - - public BlockDescriptor(byte[] entryData) throws IOException { - this(new DataInputStream(new ByteArrayInputStream(entryData))); - } - - public BlockDescriptor(DataInput dataIn) throws IOException { - blockType = dataIn.readInt(); - unknown = dataIn.readInt(); - outOffset = dataIn.readLong()*0x200; - outSize = dataIn.readLong()*0x200; - inOffset = dataIn.readLong(); - inSize = dataIn.readLong(); - } - - public byte[] toBytes() throws IOException { - ByteArrayOutputStream result = new ByteArrayOutputStream(0x28); - DataOutputStream dataOut = new DataOutputStream(result); - - dataOut.writeInt(blockType); // 4 bytes - dataOut.writeInt(unknown); // 4 bytes - if((outOffset % 0x200) != 0) - throw new RuntimeException("Out offset must be aligned to 0x200 block size!"); - dataOut.writeLong(outOffset/0x200); // 8 bytes - if((outSize % 0x200) != 0) - throw new RuntimeException("Out size must be aligned to 0x200 block size!"); - dataOut.writeLong(outSize/0x200); // 8 bytes - dataOut.writeLong(inOffset); // 8 bytes - dataOut.writeLong(inSize); // 8 bytes - // sum = 4 + 4 + 8 + 8 + 8 + 8 = 40 = 0x28 - - dataOut.flush(); - dataOut.close(); - return result.toByteArray(); - } - - public int getBlockType() { return blockType; } - public int getUnknown() { return unknown; } - public String getUnknownAsString() throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(4); - DataOutputStream dos = new DataOutputStream(baos); - dos.write(unknown); - dos.close(); - return new String(baos.toByteArray(), "US-ASCII"); - } - public long getOutOffset() { return outOffset; } - public long getOutSize() { return outSize; } - public long getInOffset() { return inOffset; } - public long getInSize() { return inSize; } - - public void setBlockType(int blockType) { this.blockType = blockType; } - public void setUnknown(int unknown) { this.unknown = unknown; } - public void setOutOffset(long outOffset) { - if((outOffset % 0x200) != 0) - throw new RuntimeException("Out offset must be aligned to 0x200 block size!"); - this.outOffset = outOffset; - } - public void setOutSize(long outSize) { - if((outSize % 0x200) != 0) - throw new RuntimeException("Out size must be aligned to 0x200 block size!"); - this.outSize = outSize; - } - public void setInOffset(long inOffset) { this.inOffset = inOffset; } - public void setInSize(long inSize) { this.inSize = inSize; } - - public boolean hasKnownBlockType() { - for(int current : KNOWN_BLOCK_TYPES) { - if(blockType == current) - return true; - } - return false; - } - - public String getBlockTypeName() { - for(int i = 0; i < KNOWN_BLOCK_TYPES.length; ++i) { - int current = KNOWN_BLOCK_TYPES[i]; - if(blockType == current) - return KNOWN_BLOCK_TYPE_NAMES[i]; - } - return null; - } - - public String toString() { - StringBuilder result = new StringBuilder("[BlockDescriptor"); - - String blockTypeString = "\"" + getBlockTypeName() + "\""; - if(blockTypeString == null) - blockTypeString = "0x" + Integer.toHexString(blockType) + " (unknown type)"; - - result.append(" blockType=" + blockTypeString); - result.append(" unknown=" + Integer.toHexString(unknown)); - result.append(" outOffset=" + outOffset); - result.append(" outSize=" + outSize); - result.append(" inOffset=" + inOffset); - result.append(" inSize=" + inSize); - result.append("]"); - - return result.toString(); - } - } - - // Saxat från HFSExplorer.java - public static class APMPartition { - public int pmSig; // {partition signature} - public int pmSigPad; // {reserved} - public long pmMapBlkCnt; // {number of blocks in partition map} - public long pmPyPartStart; // {first physical block of partition} - public long pmPartBlkCnt; // {number of blocks in partition} - public final byte[] pmPartName = new byte[32]; // {partition name} - public final byte[] pmParType = new byte[32]; // {partition type} - public long pmLgDataStart; // {first logical block of data area} - public long pmDataCnt; // {number of blocks in data area} - public long pmPartStatus; // {partition status information} - public long pmLgBootStart; // {first logical block of boot code} - public long pmBootSize; // {size of boot code, in bytes} - public long pmBootAddr; // {boot code load address} - public long pmBootAddr2; // {reserved} - public long pmBootEntry; // {boot code entry point} - public long pmBootEntry2; // {reserved} - public long pmBootCksum; // {boot code checksum} - public final byte[] pmProcessor = new byte[16]; // {processor type} - public final int[] pmPad = new int[188]; // {reserved} - - public APMPartition(byte[] entryData) throws IOException { - this(new DataInputStream(new ByteArrayInputStream(entryData))); - } - - public APMPartition(DataInput di) throws IOException { - // 2*2 + 4*3 + 32*2 + 10*4 + 16 + 188*2 = 512 - pmSig = di.readShort() & 0xffff; - pmSigPad = di.readShort() & 0xffff; - pmMapBlkCnt = di.readInt() & 0xffffffffL; - pmPyPartStart = di.readInt() & 0xffffffffL; - pmPartBlkCnt = di.readInt() & 0xffffffffL; - di.readFully(pmPartName); - di.readFully(pmParType); - pmLgDataStart = di.readInt() & 0xffffffffL; - pmDataCnt = di.readInt() & 0xffffffffL; - pmPartStatus = di.readInt() & 0xffffffffL; - pmLgBootStart = di.readInt() & 0xffffffffL; - pmBootSize = di.readInt() & 0xffffffffL; - pmBootAddr = di.readInt() & 0xffffffffL; - pmBootAddr2 = di.readInt() & 0xffffffffL; - pmBootEntry = di.readInt() & 0xffffffffL; - pmBootEntry2 = di.readInt() & 0xffffffffL; - pmBootCksum = di.readInt() & 0xffffffffL; - di.readFully(pmProcessor); - for(int i = 0; i < pmPad.length; ++i) - pmPad[i] = di.readShort() & 0xffff; - } - - public void printPartitionInfo(PrintStream ps) { -// String result = ""; -// result += "Partition name: \"" + new String(pmPartName) + "\"\n"; -// result += "Partition type: \"" + new String(pmParType) + "\"\n"; -// result += "Processor type: \"" + new String(pmProcessor) + "\"\n"; -// return result; - try { - ps.println("pmSig: " + pmSig); - ps.println("pmSigPad: " + pmSigPad); - ps.println("pmMapBlkCnt: " + pmMapBlkCnt); - ps.println("pmPyPartStart: " + pmPyPartStart); - ps.println("pmPartBlkCnt: " + pmPartBlkCnt); - ps.println("pmPartName: \"" + new String(pmPartName, "US-ASCII") + "\""); - ps.println("pmParType: \"" + new String(pmParType, "US-ASCII") + "\""); - ps.println("pmLgDataStart: " + pmLgDataStart); - ps.println("pmDataCnt: " + pmDataCnt); - ps.println("pmPartStatus: " + pmPartStatus); - ps.println("pmLgBootStart: " + pmLgBootStart); - ps.println("pmBootSize: " + pmBootSize); - ps.println("pmBootAddr: " + pmBootAddr); - ps.println("pmBootAddr2: " + pmBootAddr2); - ps.println("pmBootEntry: " + pmBootEntry); - ps.println("pmBootEntry2: " + pmBootEntry2); - ps.println("pmBootCksum: " + pmBootCksum); - ps.println("pmProcessor: \"" + new String(pmProcessor, "US-ASCII") + "\""); - ps.println("pmPad: " + pmPad); - } catch(UnsupportedEncodingException uee) { - uee.printStackTrace(); - } // Will never happen. Ever. Period. - } - } - - public static void main(String[] args) throws IOException { - DMGMetadata meta = new DMGMetadata(new RandomAccessFile(args[0], "r")); - meta.printInfo(System.out); - } -} diff --git a/src/org/catacombae/dmgx/NodeBuilder.java b/src/org/catacombae/dmgx/NodeBuilder.java deleted file mode 100644 index da3c3cc..0000000 --- a/src/org/catacombae/dmgx/NodeBuilder.java +++ /dev/null @@ -1,88 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -import org.xml.sax.helpers.DefaultHandler; -import org.xml.sax.SAXException; -import org.xml.sax.Attributes; - -/** - * Plugs into a SAXParser to build a tree of XMLElements representing the document. - */ -class NodeBuilder extends DefaultHandler { - /** NEVER try to obtain anything from this except its children. */ - private XMLNode artificialRoot; - - private XMLNode currentNode; - - public NodeBuilder() { - artificialRoot = new XMLNode(null, null, null, null, null); - currentNode = artificialRoot; - } - public void startElement(String namespaceURI, String sName, String qName, - Attributes attrs) throws SAXException { - //System.out.println("SE"); - Attribute[] attributes = new Attribute[attrs.getLength()]; - for(int i = 0; i < attributes.length; ++i) { - attributes[i] = new Attribute(attrs.getLocalName(i), attrs.getQName(i), - attrs.getType(i), attrs.getURI(i), attrs.getValue(i)); - } - - XMLNode newNode = new XMLNode(namespaceURI, sName, qName, - attributes, currentNode); - currentNode.addChild(newNode); - currentNode = newNode; - } - public void endElement(String namespaceURI, String sName, - String qName) throws SAXException { - //System.out.println("EE"); - currentNode = currentNode.parent; - } - public void characters(char[] buf, int offset, int len) - throws SAXException { - //System.out.println("CH"); - String s = new String(buf, offset, len).trim(); - if(s.length() != 0) - currentNode.addChild(new XMLText(s)); - } - public void notationDecl(String name, String publicId, - String systemId) throws SAXException { - System.out.println("notationDecl(" + name + ", " + - publicId + ", " + systemId + ");"); - } - public XMLNode[] getRoots() throws RuntimeException { - if(artificialRoot != currentNode) - throw new RuntimeException("Tree was not closed!"); - - int numberOfNodes = 0; - for(XMLElement xe : artificialRoot.getChildren()) { - if(xe instanceof XMLNode) - ++numberOfNodes; - } - XMLNode[] result = new XMLNode[numberOfNodes]; - int i = 0; - for(XMLElement xe : artificialRoot.getChildren()) { - if(xe instanceof XMLNode) - result[i++] = (XMLNode)xe; - } - return result; - } -} diff --git a/src/org/catacombae/dmgx/SimpleFileFilter.java b/src/org/catacombae/dmgx/SimpleFileFilter.java deleted file mode 100644 index 145a5cd..0000000 --- a/src/org/catacombae/dmgx/SimpleFileFilter.java +++ /dev/null @@ -1,54 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -import java.util.*; -import java.io.*; - -public class SimpleFileFilter extends javax.swing.filechooser.FileFilter { - - private Vector extensions; - private String description; - - public SimpleFileFilter() { - extensions = new Vector(); - description = ""; - } - public void addExtension(String extension) { extensions.add(extension); } - public void setDescription(String idescription) { description = idescription; } - public void removeExtension(String iextension) { - for(int i = 0; i < extensions.size(); i++) { - if(extensions.get(i).equals(iextension)) - extensions.remove(i); - } - } - public boolean accept(File f) { - - if(f.isDirectory()) return true; - - for(int i = 0; i < extensions.size(); i++) { - if(f.getName().endsWith(extensions.get(i))) - return true; - } - return false; - } - public String getDescription() { return description; } -} diff --git a/src/org/catacombae/dmgx/XMLElement.java b/src/org/catacombae/dmgx/XMLElement.java deleted file mode 100644 index 64e2433..0000000 --- a/src/org/catacombae/dmgx/XMLElement.java +++ /dev/null @@ -1,27 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -import java.io.PrintStream; - -abstract class XMLElement { - protected abstract void _printTree(PrintStream pw, int level); -} diff --git a/src/org/catacombae/dmgx/XMLNode.java b/src/org/catacombae/dmgx/XMLNode.java deleted file mode 100644 index 8cb227d..0000000 --- a/src/org/catacombae/dmgx/XMLNode.java +++ /dev/null @@ -1,123 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -import java.util.LinkedList; -import java.io.PrintStream; - -class XMLNode extends XMLElement { - public final String namespaceURI; - public final String sName; - public final String qName; - public final Attribute[] attrs; - public final XMLNode parent; - private final LinkedList children; - - public XMLNode(String namespaceURI, String sName, - String qName, Attribute[] attrs, XMLNode parent) { - this.namespaceURI = namespaceURI; - this.sName = sName; - this.qName = qName; - this.attrs = attrs; - this.parent = parent; - this.children = new LinkedList(); - } - - public void addChild(XMLElement x) { - children.addLast(x); - } - - public void printTree(PrintStream pw) { - _printTree(pw, 0); - } - - protected void _printTree(PrintStream pw, int level) { - for(int i = 0; i < level; ++i) - pw.print(" "); - pw.print("<"); - pw.print(qName); - for(Attribute a : attrs) - pw.print(" " + a.qName + "=" + a.value); - pw.println(">"); - for(XMLElement xe : children) - xe._printTree(pw, level+1); - - for(int i = 0; i < level; ++i) - pw.print(" "); - pw.println(""); - } - public XMLElement[] getChildren() { - return children.toArray(new XMLElement[children.size()]); - } - - /** - * The concept of "changing directory" in a tree is perhabs not a - * perfect way to describe things. But this method will look up the - * first subnode of our node that is of the type type - * and return it. - * If you have more than one of the same type, tough luck. You only - * get the first. - */ - public XMLNode cd(String type) { - for(XMLElement xn : getChildren()) { - if(xn instanceof XMLNode && ((XMLNode)xn).qName.equals(type)) - return (XMLNode)xn; - } - return null; - } - /** - * This is different from the cd method in that it - * searches for a node of the type "key", and looks up the - * XMLText within. It then compares the text with the String - * key. If they match, it returns the node coming - * after the key node. Else it continues to search. If no match is - * found, null is returned. - */ - public XMLNode cdkey(String key) { - boolean keyFound = false; - for(XMLElement xn : getChildren()) { - if(xn instanceof XMLNode) { - if(keyFound) - return (XMLNode)xn; - - else if(((XMLNode)xn).qName.equals("key")) { - for(XMLElement xn2 : ((XMLNode)xn).getChildren()) { - if(xn2 instanceof XMLText && ((XMLText)xn2).text.equals(key)) - keyFound = true; - } - } - } - } - return null; - } - public String getKeyValue(String key) { - XMLNode keyNode = cdkey(key); - StringBuilder returnString = new StringBuilder(); - for(XMLElement xe : keyNode.getChildren()) { - if(xe instanceof XMLText) - returnString.append(((XMLText)xe).text); - } - if(returnString.length() == 0) - return null; - else - return returnString.toString(); - } -} diff --git a/src/org/catacombae/dmgx/XMLParse.java b/src/org/catacombae/dmgx/XMLParse.java deleted file mode 100644 index d8d2826..0000000 --- a/src/org/catacombae/dmgx/XMLParse.java +++ /dev/null @@ -1,31 +0,0 @@ -/* -public class XMLParse { - public String encoding = "US-ASCII"; - - public XMLElement parse(InputStream is) throws IOException { - readElement(is); - } - - public void readElement(InputStream is) { - LinkedList buf = new LinkedList(); - int currentByte = is.read(); - if(currentByte != '<') - throw new RuntimeException(); - - while(currentByte != '>' && currentByte != -1) { - buf.add(currentByte); - currentByte = is.read(); - } - if(currentByte == -1) - throw new RuntimeException(); - else { - buf.add(currentByte); - byte[] bytes = new byte[buf.size()]; - int i = 0; - for(int cur : buf) - bytes[i++] = cur; - String s = new String(bytes, encoding); - } - } -} -*/ diff --git a/src/org/catacombae/dmgx/XMLText.java b/src/org/catacombae/dmgx/XMLText.java deleted file mode 100644 index b087598..0000000 --- a/src/org/catacombae/dmgx/XMLText.java +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package org.catacombae.dmgx; - -import java.io.PrintStream; - -class XMLText extends XMLElement { - public final String text; - public XMLText(String text) { - this.text = text; - } - protected void _printTree(PrintStream pw, int level) { - for(int i = 0; i < level; ++i) - pw.print(" "); - pw.println(text); - } - - public static void main(String[] args) { - System.out.println(args[0] + " " + args[1]); - } -} diff --git a/src/org/catacombae/plist/PlistNode.java b/src/org/catacombae/plist/PlistNode.java new file mode 100644 index 0000000..5166c5e --- /dev/null +++ b/src/org/catacombae/plist/PlistNode.java @@ -0,0 +1,31 @@ +/*- + * Copyright (C) 2006-2011 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.plist; + +import java.io.Reader; + +/** + * + * @author erik + */ +public abstract class PlistNode { + public abstract PlistNode[] getChildren(); + public abstract PlistNode cd(String type); + public abstract PlistNode cdkey(String key); + public abstract Reader getKeyValue(String key); +} diff --git a/src/org/catacombae/plist/XmlPlist.java b/src/org/catacombae/plist/XmlPlist.java new file mode 100644 index 0000000..f2c48f3 --- /dev/null +++ b/src/org/catacombae/plist/XmlPlist.java @@ -0,0 +1,178 @@ +/*- + * Copyright (C) 2006-2011 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.plist; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.catacombae.dmgextractor.io.RandomAccessInputStream; +import org.catacombae.dmgextractor.io.SynchronizedRandomAccessStream; +import org.catacombae.io.ReadableByteArrayStream; +import org.catacombae.xml.DebugXMLContentHandler; +import org.catacombae.xml.NodeBuilder; +import org.catacombae.xml.NodeBuilderContentHandler; +import org.catacombae.xml.NullXMLContentHandler; +import org.catacombae.xml.XMLNode; +import org.catacombae.xml.apx.APXParser; +import org.catacombae.xml.apx.ParseException; +import org.xml.sax.SAXException; + +/** + * + * @author erik + */ +public class XmlPlist { + + private final XMLNode rootNode; + + public XmlPlist(byte[] data) { + this(data, 0, data.length); + } + + public XmlPlist(byte[] data, boolean useSAXParser) { + this(data, 0, data.length, useSAXParser); + } + + public XmlPlist(byte[] data, int offset, int length) { + this(data, offset, length, false); + } + + public XmlPlist(byte[] data, int offset, int length, boolean useSAXParser) { + //plistData = new byte[length]; + //System.arraycopy(data, offset, plistData, 0, length); + rootNode = parseXMLData(data, useSAXParser); + } + + public PlistNode getRootNode() { + return new XmlPlistNode(rootNode); + } + + private XMLNode parseXMLData(byte[] plistData, boolean defaultToSAX) { + //InputStream is = new ByteArrayInputStream(plistData); + NodeBuilder handler = new NodeBuilder(); + + if(defaultToSAX) { + parseXMLDataSAX(plistData, handler); + } + else { + /* First try to parse with the internal homebrew parser, and if it + * doesn't succeed, go for the SAX parser. */ + //System.err.println("Trying to parse xml data..."); + try { + parseXMLDataAPX(plistData, handler); + //System.err.println("xml data parsed..."); + } catch(Exception e) { + e.printStackTrace(); + System.err.println("APX parser threw exception... falling back to SAX parser. Report this error!"); + handler = new NodeBuilder(); + parseXMLDataSAX(plistData, handler); + } + } + + XMLNode[] rootNodes = handler.getRoots(); + if(rootNodes.length != 1) + throw new RuntimeException("Could not parse DMG-file!"); + else + return rootNodes[0]; + } + + private void parseXMLDataAPX(byte[] buffer, NodeBuilder handler) { + try { + ReadableByteArrayStream ya = new ReadableByteArrayStream(buffer); + SynchronizedRandomAccessStream bufferStream = + new SynchronizedRandomAccessStream(ya);//new ReadableByteArrayStream(buffer)); + + // First we parse the xml declaration using a US-ASCII charset just to extract the charset description + //System.err.println("parsing encoding"); + InputStream is = new RandomAccessInputStream(bufferStream); + APXParser encodingParser = APXParser.create(new InputStreamReader(is, "US-ASCII"), + new NullXMLContentHandler(Charset.forName("US-ASCII"))); + String encodingName = encodingParser.xmlDecl(); + //System.err.println("encodingName=" + encodingName); + if(encodingName == null) + encodingName = "US-ASCII"; + + Charset encoding = Charset.forName(encodingName); + + // Then we proceed to parse the entire document + is = new RandomAccessInputStream(bufferStream); + Reader usedReader = new BufferedReader(new InputStreamReader(is, encoding)); + //System.err.println("parsing document"); + //try { FileOutputStream dump = new FileOutputStream("dump.xml"); dump.write(buffer); dump.close(); } + //catch(Exception e) { e.printStackTrace(); } + + if(false) { // + APXParser documentParser = APXParser.create(usedReader, new DebugXMLContentHandler(encoding)); + documentParser.xmlDocument(); + System.exit(0); + } + else { + APXParser documentParser = APXParser.create(usedReader, new NodeBuilderContentHandler(handler, bufferStream, encoding)); + documentParser.xmlDocument(); + } + + } catch(ParseException pe) { + //System.err.println("Could not read the partition list..."); + throw new RuntimeException(pe); + } catch(UnsupportedEncodingException uee) { + throw new RuntimeException(uee); + } + } + + private void parseXMLDataSAX(byte[] buffer, NodeBuilder handler) { + try { + InputStream is = new ByteArrayInputStream(buffer); + SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); +// System.out.println("validation: " + saxParser.getProperty("validation")); +// System.out.println("external-general-entities: " + saxParser.getProperty("external-general-entities")); +// System.out.println("external-parameter-entities: " + saxParser.getProperty("external-parameter-entities")); +// System.out.println("is-standalone: " + saxParser.getProperty("is-standalone")); +// System.out.println("lexical-handler: " + saxParser.getProperty("lexical-handler")); +// System.out.println("parameter-entities: " + saxParser.getProperty("parameter-entities")); +// System.out.println("namespaces: " + saxParser.getProperty("namespaces")); +// System.out.println("namespace-prefixes: " + saxParser.getProperty("namespace-prefixes")); +// System.out.println(": " + saxParser.getProperty("")); +// System.out.println(": " + saxParser.getProperty("")); +// System.out.println(": " + saxParser.getProperty("")); +// System.out.println(": " + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); +// System.out.println("" + saxParser.getProperty("")); + + //System.out.println("isValidating: " + saxParser.isValidating()); + saxParser.parse(is, handler); + } catch(SAXException se) { + se.printStackTrace(); + //System.err.println("Could not read the partition list... exiting."); + throw new RuntimeException(se); + } catch(Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/org/catacombae/plist/XmlPlistNode.java b/src/org/catacombae/plist/XmlPlistNode.java new file mode 100644 index 0000000..db81b7c --- /dev/null +++ b/src/org/catacombae/plist/XmlPlistNode.java @@ -0,0 +1,178 @@ +/*- + * Copyright (C) 2006-2011 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.plist; + +import java.io.IOException; +import java.io.Reader; +import java.util.LinkedList; +import org.catacombae.dmgextractor.io.ConcatenatedReader; +import org.catacombae.io.RuntimeIOException; +import org.catacombae.util.Util; +import org.catacombae.xml.XMLElement; +import org.catacombae.xml.XMLNode; +import org.catacombae.xml.XMLText; + +/** + * + * @author erik + */ +public class XmlPlistNode extends PlistNode { + private final XMLNode xmlNode; + + public XmlPlistNode(XMLNode xmlNode) { + this.xmlNode = xmlNode; + } + + private XMLNode getXMLNode() { + return xmlNode; + } + + private String[] getKeys() throws RuntimeIOException { + final LinkedList keyList = new LinkedList(); + + for(XMLElement xe : xmlNode.getChildren()) { + if(xe instanceof XMLNode) { + XMLNode xn = (XMLNode) xe; + if(xn.qName.equals("key")) { + for(XMLElement xeChild : xn.getChildren()) { + if(xeChild instanceof XMLText) { + final XMLText xtChild = (XMLText) xeChild; + String key; + + try { + key = Util.readFully(xtChild.getText()); + } catch(IOException e) { + throw new RuntimeIOException(e); + } + + keyList.addLast(key); + } + } + } + } + } + + return keyList.toArray(new String[keyList.size()]); + } + + public PlistNode[] getChildren() { + final LinkedList children = new LinkedList(); + + for(String key : getKeys()) { + children.add(cdkey(key)); + } + + return children.toArray(new PlistNode[children.size()]); + } + + /** + * The concept of "changing directory" in a tree is perhaps not a + * perfect way to describe things. But this method will look up the + * first subnode of our node that is of the type type + * and return it. + * If you have more than one of the same type, tough luck. You only + * get the first. + */ + public PlistNode cd(String type) { + for(XMLElement xn : xmlNode.getChildren()) { + if(xn instanceof XMLNode && ((XMLNode)xn).qName.equals(type)) + return new XmlPlistNode((XMLNode)xn); + } + return null; + } + + /** + * This is different from the cd method in that it + * searches for a node of the type "key", and looks up the + * XMLText within. It then compares the text with the String + * key. If they match, it returns the node coming + * after the key node. Else it continues to search. If no match is + * found, null is returned. + */ + public PlistNode cdkey(String key) { + return cdkeyXml(key); + } + + private XmlPlistNode cdkeyXml(String key) { + boolean keyFound = false; + for(XMLElement xn : xmlNode.getChildren()) { + if(xn instanceof XMLNode) { + if(keyFound) + return new XmlPlistNode((XMLNode)xn); + + else if(((XMLNode)xn).qName.equals("key")) { + for(XMLElement xn2 : ((XMLNode)xn).getChildren()) { + try { + if(xn2 instanceof XMLText) { + String s = Util.readFully(((XMLText)xn2).getText()); + //System.err.println("cdkey searching: \"" + s + "\""); + if(s.equals(key)) + keyFound = true; + } + } catch(Exception e) { throw new RuntimeException(e); } + } + } + } + } + return null; + } + + public Reader getKeyValue(String key) { + //System.out.println("XMLNode.getKeyValue(\"" + key + "\")"); + XmlPlistNode keyNode = cdkeyXml(key); + if(keyNode == null) + return null; + + XMLElement[] nodeChildren = keyNode.getXMLNode().getChildren(); + if(nodeChildren.length != 1) { + //System.out.println(" nodeChildren.length == " + nodeChildren.length); + + LinkedList collectedReaders = new LinkedList(); + for(XMLElement xe : keyNode.getXMLNode().getChildren()) { + if(xe instanceof XMLText) { + try { + Reader xt = ((XMLText)xe).getText(); + collectedReaders.addLast(xt); + } catch(Exception e) { throw new RuntimeException(e); } + //System.out.print("\""); + //for(int i = 0; i < xt.length(); ++i) System.out.print(xt.charAt(i)); + //System.out.println("\""); + //System.out.println("free memory: " + Runtime.getRuntime().freeMemory() + " total memory: " + Runtime.getRuntime().totalMemory()); + } + } + ConcatenatedReader result; + if(collectedReaders.size() == 0) + result = null; + else { + //System.out.println("doing a toString... free memory: " + Runtime.getRuntime().freeMemory() + " total memory: " + Runtime.getRuntime().totalMemory()); + //result = returnString.toString(); + //System.out.println("done.free memory: " + Runtime.getRuntime().freeMemory() + " total memory: " + Runtime.getRuntime().totalMemory()); + result = new ConcatenatedReader(collectedReaders.toArray(new Reader[collectedReaders.size()])); + } + return result; + } + else if(nodeChildren[0] instanceof XMLText) { + //System.err.println("Special case!"); + try { + return ((XMLText)nodeChildren[0]).getText(); + } catch(Exception e) { throw new RuntimeException(e); } + } + else + return null; + } +} diff --git a/src/org/catacombae/xml/.cvsignore b/src/org/catacombae/xml/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/org/catacombae/xml/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/org/catacombae/xml/Attribute.java b/src/org/catacombae/xml/Attribute.java new file mode 100644 index 0000000..3bde8b8 --- /dev/null +++ b/src/org/catacombae/xml/Attribute.java @@ -0,0 +1,58 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import java.util.List; + +public class Attribute { + public static abstract class ValueComponent { + @Override + public abstract String toString(); + } + public static class StringComponent extends ValueComponent { + public String content; + public StringComponent(String content) { this.content = content; } + public String toString() { return content; } + } + public static class ReferenceComponent extends ValueComponent { + public String content; + public ReferenceComponent(String content) { this.content = content; } + public String toString() { return content; } // should resolve the reference here + } + public static class Value { + public List components; + public Value(List components) { + this.components = components; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + for(ValueComponent vc : components) + result.append(vc.toString()); + return result.toString(); + } + } + public String identifier; + public Value value; + + public Attribute(String identifier, Value value) { + this.identifier = identifier; + this.value = value; + } +} \ No newline at end of file diff --git a/src/Attribute.java b/src/org/catacombae/xml/Attribute2.java similarity index 50% rename from src/Attribute.java rename to src/org/catacombae/xml/Attribute2.java index fa62722..0411939 100644 --- a/src/Attribute.java +++ b/src/org/catacombae/xml/Attribute2.java @@ -1,30 +1,29 @@ /*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. + * Copyright (C) 2006-2008 Erik Larsson * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * along with this program. If not, see . */ -class Attribute { +package org.catacombae.xml; + +public class Attribute2 { public final String localName; public final String qName; public final String type; public final String URI; public final String value; - public Attribute(String localName, String qName, String type, String URI, String value) { + public Attribute2(String localName, String qName, String type, String URI, String value) { this.localName = localName; this.qName = qName; this.type = type; diff --git a/src/org/catacombae/xml/DebugXMLContentHandler.java b/src/org/catacombae/xml/DebugXMLContentHandler.java new file mode 100644 index 0000000..ad970e5 --- /dev/null +++ b/src/org/catacombae/xml/DebugXMLContentHandler.java @@ -0,0 +1,108 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import java.util.List; +import java.nio.charset.Charset; + +public class DebugXMLContentHandler extends XMLContentHandler { + public DebugXMLContentHandler(Charset encoding) { + super(encoding); + } + //public void doctype(String + public void xmlDecl(String version, String encoding, Boolean standalone) { + print("xmlDecl: "); + } + public void pi(String id, String content) { + print("pi: "); + } + + public void comment(String content) { + println("comment: "); + } + + public void doctype(String name, ExternalID eid) { + print("doctype: "); + } + + public void cdata(String cdata) { + println("cdata: "); + } + + public void emptyElement(String name, List attributes) { + print("emptyElement: <" + name); + for(Attribute attr : attributes) + print(" " + attr.identifier + "=\"" + attr.value + "\""); + println("/>"); + } + + public void startElement(String name, List attributes) { + print("startElement: <" + name); + for(Attribute attr : attributes) + print(" " + attr.identifier + "=\"" + attr.value + "\""); + println(">"); + } + + public void endElement(String name) { + println("endElement: "); + } + +// public void chardata(CharSequence data) { +// print("chardata: \""); +// for(int i = 0; i < data.length(); ++i) +// print(data.charAt(i)+""); +// println("\""); + +// } + public void chardata(int beginLine, int beginColumn, int endLine, int endColumn) { + println("chardata: starting at (" + beginLine + "," + beginColumn + ") and ending at (" + endLine + "," + endColumn + ")"); + } + + public void reference(String ref) { + println("reference: \"" + ref + "\""); + } + + private static void print(String s) { + System.out.print(s); + } + private static void println(String s) { + System.out.println(s); + } + +} \ No newline at end of file diff --git a/src/org/catacombae/xml/ExternalID.java b/src/org/catacombae/xml/ExternalID.java new file mode 100644 index 0000000..58078dc --- /dev/null +++ b/src/org/catacombae/xml/ExternalID.java @@ -0,0 +1,36 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +public class ExternalID { + public static final int SYSTEM = 0; + public static final int PUBLIC = 1; + public int type; + public String pubidLiteral; + public String systemLiteral; + + public ExternalID(String pubidLiteral, String systemLiteral) { + this.pubidLiteral = pubidLiteral; + this.systemLiteral = systemLiteral; + this.type = PUBLIC; + } + public ExternalID(String systemLiteral) { + this.systemLiteral = systemLiteral; + this.type = SYSTEM; + } +} \ No newline at end of file diff --git a/src/org/catacombae/xml/MutableInputStreamReader.java b/src/org/catacombae/xml/MutableInputStreamReader.java new file mode 100644 index 0000000..c10b8c9 --- /dev/null +++ b/src/org/catacombae/xml/MutableInputStreamReader.java @@ -0,0 +1,146 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import java.io.*; +import java.nio.*; + +public class MutableInputStreamReader extends Reader { + private static final boolean DEBUG = true; + private static final String PREFIX = "---->MutableInputStreamReader: "; + private static final PrintStream os = System.out; + private InputStream iStream; + private InputStreamReader isReader; + + public MutableInputStreamReader(InputStream iStream, String charset) throws UnsupportedEncodingException { + this.iStream = iStream; + this.isReader = new InputStreamReader(iStream, charset); + } + public void close() throws IOException { + try { + if(DEBUG) os.println(PREFIX + "isReader.close()"); + isReader.close(); + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public void mark(int readAheadLimit) throws IOException { + try { + if(DEBUG) os.println(PREFIX + "isReader.mark(" + readAheadLimit + ")"); + isReader.mark(readAheadLimit); + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public boolean markSupported() { + try { + boolean result = isReader.markSupported(); + if(DEBUG) os.println(PREFIX + "isReader.markSupported() == " + result); + return result; + } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public int read() throws IOException { + try { + int result = isReader.read(); + if(DEBUG) os.println(PREFIX + "isReader.read() == " + result); + return result; + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public int read(char[] cbuf) throws IOException { + try { + int result = isReader.read(cbuf); + if(DEBUG) os.println(PREFIX + "isReader.read(" + cbuf.length + " bytes...) == " + result); + return result; + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + try { + int result = isReader.read(cbuf, off, len); + if(DEBUG) os.println(PREFIX + "isReader.read(" + cbuf.length + " bytes..., " + off + ", " + len + ") == " + result); + return result; + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public int read(CharBuffer target) throws IOException { + try { + int result = isReader.read(target); + if(DEBUG) os.println(PREFIX + "isReader.read(CharBuffer with length " + target.length() + ") == " + result); + return result; + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public boolean ready() throws IOException { + try { + boolean result = isReader.ready(); + if(DEBUG) os.println(PREFIX + "isReader.ready() == " + result); + return result; + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public void reset() throws IOException { + try { + if(DEBUG) os.println(PREFIX + "isReader.reset()"); + isReader.reset(); + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + @Override + public long skip(long n) throws IOException { + try { + long result = isReader.skip(n); + if(DEBUG) os.println(PREFIX + "isReader.skip(" + n + ") == " + result); + return result; + } + catch(IOException ioe) { if(DEBUG) ioe.printStackTrace(); throw ioe; } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } + + public void changeEncoding(String charset) throws UnsupportedEncodingException { + try { + if(DEBUG) os.println(PREFIX + "changeEncoding(\"" + charset + "\")"); + isReader = new InputStreamReader(iStream, charset); + } + catch(RuntimeException e) { if(DEBUG) e.printStackTrace(); throw e; } + } +} \ No newline at end of file diff --git a/src/NodeBuilder.java b/src/org/catacombae/xml/NodeBuilder.java similarity index 61% rename from src/NodeBuilder.java rename to src/org/catacombae/xml/NodeBuilder.java index 722713a..c983faf 100644 --- a/src/NodeBuilder.java +++ b/src/org/catacombae/xml/NodeBuilder.java @@ -1,31 +1,32 @@ /*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. + * Copyright (C) 2006-2008 Erik Larsson * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * along with this program. If not, see . */ +package org.catacombae.xml; + +import org.catacombae.dmgextractor.io.*; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.SAXException; import org.xml.sax.Attributes; +import java.nio.charset.Charset; /** * Plugs into a SAXParser to build a tree of XMLElements representing the document. */ -class NodeBuilder extends DefaultHandler { +public class NodeBuilder extends DefaultHandler { /** NEVER try to obtain anything from this except its children. */ private XMLNode artificialRoot; @@ -35,25 +36,40 @@ public NodeBuilder() { artificialRoot = new XMLNode(null, null, null, null, null); currentNode = artificialRoot; } + + @Override public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException { //System.out.println("SE"); - Attribute[] attributes = new Attribute[attrs.getLength()]; + Attribute2[] attributes = new Attribute2[attrs.getLength()]; for(int i = 0; i < attributes.length; ++i) { - attributes[i] = new Attribute(attrs.getLocalName(i), attrs.getQName(i), - attrs.getType(i), attrs.getURI(i), attrs.getValue(i)); + attributes[i] = new Attribute2(attrs.getLocalName(i), attrs.getQName(i), + attrs.getType(i), attrs.getURI(i), attrs.getValue(i)); } - + startElementInternal(namespaceURI, sName, qName, attributes); + } + + void startElementInternal(String namespaceURI, String sName, String qName, + Attribute2[] attributes) throws SAXException { XMLNode newNode = new XMLNode(namespaceURI, sName, qName, attributes, currentNode); currentNode.addChild(newNode); currentNode = newNode; } + + @Override public void endElement(String namespaceURI, String sName, String qName) throws SAXException { //System.out.println("EE"); currentNode = currentNode.parent; } + + public void characters(SynchronizedRandomAccessStream file, Charset encoding, + int startLine, int startColumn, int endLine, int endColumn) { + currentNode.addChild(new XMLText(file, encoding, startLine, startColumn, endLine, endColumn)); + } + + @Override public void characters(char[] buf, int offset, int len) throws SAXException { //System.out.println("CH"); @@ -61,11 +77,13 @@ public void characters(char[] buf, int offset, int len) if(s.length() != 0) currentNode.addChild(new XMLText(s)); } + + @Override public void notationDecl(String name, String publicId, String systemId) throws SAXException { - System.out.println("notationDecl(" + name + ", " + - publicId + ", " + systemId + ");"); + //System.out.println("notationDecl(" + name + ", " + publicId + ", " + systemId + ");"); } + public XMLNode[] getRoots() throws RuntimeException { if(artificialRoot != currentNode) throw new RuntimeException("Tree was not closed!"); diff --git a/src/org/catacombae/xml/NodeBuilderContentHandler.java b/src/org/catacombae/xml/NodeBuilderContentHandler.java new file mode 100644 index 0000000..7e6981e --- /dev/null +++ b/src/org/catacombae/xml/NodeBuilderContentHandler.java @@ -0,0 +1,99 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +//import org.catacombae.xml.*; +import org.catacombae.dmgextractor.io.*; +import java.util.List; +import java.nio.charset.Charset; + +public class NodeBuilderContentHandler extends XMLContentHandler { + private NodeBuilder nodeBuilder; + private SynchronizedRandomAccessStream sras; + private Charset encoding; + + public NodeBuilderContentHandler(NodeBuilder nodeBuilder, SynchronizedRandomAccessStream sras, Charset encoding) { + super(encoding); + this.nodeBuilder = nodeBuilder; + this.sras = sras; + this.encoding = encoding; + } + public void xmlDecl(String version, String encoding, Boolean standalone) {} + public void pi(String id, String content) {} + public void comment(String comment) {} + public void doctype(String name, ExternalID eid) {} // Needs a DTD description also + public void cdata(String cdata) { + try { + nodeBuilder.characters(cdata.toCharArray(), 0, cdata.length()); + } catch(Exception e) { throw new RuntimeException(e); } + } + public void emptyElement(String name, List attributes) { + try { + startElement(name, attributes); + endElement(name); + } catch(Exception e) { throw new RuntimeException(e); } + } + public void startElement(String name, List attributes) { + try { + Attribute2[] attrs = new Attribute2[attributes.size()]; + //for(int i = 0; i < attributes.length; ++i) { + int i = 0; + for(org.catacombae.xml.Attribute a : attributes) { + attrs[i++] = new Attribute2("", a.identifier, + "CDATA", "", a.value.toString()); + } +// org.xml.sax.ext.Attributes2Impl a2i = new org.xml.sax.ext.Attributes2Impl(); +// for(org.catacombae.xml.Attribute a : attributes) { +// System.err.println("id: " + a.identifier + " value: " + a.value.toString()); +// a2i.addAttribute("", a.identifier, a.identifier, "CDATA", a.value.toString()); +// } + nodeBuilder.startElementInternal(null, null, name, attrs); + } catch(Exception e) { throw new RuntimeException(e); } + } + public void endElement(String name) { + try { + nodeBuilder.endElement(null, null, name); + } catch(Exception e) { throw new RuntimeException(e); } + } + public void chardata(int beginLine, int beginColumn, int endLine, int endColumn) { + nodeBuilder.characters(sras, encoding, beginLine, beginColumn, endLine, endColumn); + } +// public void chardata(CharSequence data) { +// try { +// //char[] ca = data.toCharArray(); +// //nodeBuilder.characters(ca, 0, ca.length); +// nodeBuilder.characters(data); +// } catch(Exception e) { throw new RuntimeException(e); } +// } + public void reference(String ref) { + try { + if(ref.startsWith("&#")) { + // CharRef + int[] codePoints = new int[1]; + if(ref.startsWith("&#x")) + codePoints[0] = Integer.parseInt(ref.substring(3), 16); + else + codePoints[0] = Integer.parseInt(ref.substring(2), 10); + char[] cp_ca = Character.toChars(codePoints[0]); + nodeBuilder.characters(cp_ca, 0, cp_ca.length); + } + else + System.out.println("WARNING: Encountered external references, which cannot be resolved with this version of the parser."); + } catch(Exception e) { throw new RuntimeException(e); } + } +} diff --git a/src/org/catacombae/xml/NullXMLContentHandler.java b/src/org/catacombae/xml/NullXMLContentHandler.java new file mode 100644 index 0000000..40dc955 --- /dev/null +++ b/src/org/catacombae/xml/NullXMLContentHandler.java @@ -0,0 +1,37 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import java.util.List; +import java.nio.charset.Charset; + +public class NullXMLContentHandler extends XMLContentHandler { + public NullXMLContentHandler(Charset encoding) { + super(encoding); + } + public void xmlDecl(String version, String encoding, Boolean standalone) {} + public void pi(String id, String content) {} + public void comment(String comment) {} + public void doctype(String name, ExternalID eid) {} // Needs a DTD description also + public void cdata(String cdata) {} + public void emptyElement(String name, List attributes) {} + public void startElement(String name, List attributes) {} + public void endElement(String name) {} + public void chardata(int beginLine, int beginColumn, int endLine, int endColumn) {} + public void reference(String ref) {} +} \ No newline at end of file diff --git a/src/org/catacombae/xml/UTF16BEInputStream.java b/src/org/catacombae/xml/UTF16BEInputStream.java new file mode 100644 index 0000000..c079edb --- /dev/null +++ b/src/org/catacombae/xml/UTF16BEInputStream.java @@ -0,0 +1,141 @@ +/*- + * Copyright (C) 2007-2008 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import java.io.*; +import java.nio.*; +import java.nio.charset.*; + +/** + * This class always outputs UTF-16BE encoded data. The input encoding + * can be changed on the fly. + */ +public class UTF16BEInputStream extends InputStream { + private InputStream underlyingStream; + private Reader inReader; + private CharsetEncoder utf16beEncoder; + private int overflow = -1; + private char[] tempArray = new char[4096]; + + public UTF16BEInputStream(InputStream underlyingStream, String encodingName) throws UnsupportedEncodingException { + this.underlyingStream = underlyingStream; + this.inReader = new InputStreamReader(underlyingStream, encodingName); + this.utf16beEncoder = Charset.forName("UTF-16BE").newEncoder(); + } + + public void changeEncoding(String encodingName) throws UnsupportedEncodingException { + this.inReader = new InputStreamReader(underlyingStream, encodingName); + } + + public int read() throws IOException { + if(overflow != -1) { + int result = overflow; + overflow = -1; + return result; + } + else { + inReader.read(tempArray, 0, 1); + ByteBuffer bb = utf16beEncoder.encode(CharBuffer.wrap(tempArray, 0, 1)); + int result = bb.get() & 0xFF; + overflow = bb.get() & 0xFF; + return result; + } + } + + @Override + public int read(byte[] ba) throws IOException { return read(ba, 0, ba.length); } + + @Override + public int read(byte[] ba, int offset, int length) throws IOException { + if(length == 0) return 0; + + int curOffset = offset; + int curLength = length; + + // Take care of the overflow, if any + if(overflow != -1) { + ba[curOffset++] = (byte)overflow; + --length; + overflow = -1; + } + + // Do the dance... + int numberOfCharsToRead = length/2 + length%2; + int charsRead = 0; + while(numberOfCharsToRead > charsRead) { + int charsRemaining = numberOfCharsToRead - charsRead; + int curCharsRead = inReader.read(tempArray, 0, (charsRemaining < tempArray.length?charsRemaining:tempArray.length)); + if(curCharsRead == -1) + break; + else { + CharBuffer cb = CharBuffer.wrap(tempArray, 0, curCharsRead); + ByteBuffer bb = utf16beEncoder.encode(cb); + int bytesToWrite = curCharsRead*2 > curLength?curLength:curCharsRead*2; + bb.get(ba, curOffset, bytesToWrite); + curOffset += bytesToWrite; + curLength -= bytesToWrite; + if(bytesToWrite != curCharsRead*2) + overflow = bb.get() & 0xFF; + + charsRead += curCharsRead; + if(bytesToWrite != curCharsRead*2 && numberOfCharsToRead > charsRead) + throw new RuntimeException("Mind meltdown!"); + } + } + + if(numberOfCharsToRead*2 != length) { + if(numberOfCharsToRead*2-1 != length) + throw new RuntimeException("wtf?!"); + if(charsRead*2-1 == length) + return length; + else + return charsRead*2; + } + else + return charsRead*2; + } + + @Override + public long skip(long bytesToSkip) throws IOException { + byte[] garbage = new byte[4096]; + long bytesRead = 0; + while(bytesRead < bytesToSkip) { + int curBytesRead = read(garbage); + if(curBytesRead == -1) + break; + else + bytesRead += curBytesRead; + } + return bytesRead; + } + + @Override + public int available() throws IOException { return 0; } + + @Override + public void close() throws IOException { underlyingStream.close(); } + + @Override + public void mark(int readLimit) {} + + @Override + public void reset() throws IOException { throw new IOException("Not supported"); } + + @Override + public boolean markSupported() { return false; } +} \ No newline at end of file diff --git a/src/org/catacombae/xml/XMLContentHandler.java b/src/org/catacombae/xml/XMLContentHandler.java new file mode 100644 index 0000000..5274e85 --- /dev/null +++ b/src/org/catacombae/xml/XMLContentHandler.java @@ -0,0 +1,39 @@ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import java.util.List; +import java.nio.charset.Charset; + +public abstract class XMLContentHandler { + protected Charset encoding; + public XMLContentHandler(Charset encoding) { + this.encoding = encoding; + } + //public void doctype(String + public abstract void xmlDecl(String version, String encoding, Boolean standalone); + public abstract void pi(String id, String content); + public abstract void comment(String comment); + public abstract void doctype(String name, ExternalID eid); // Needs a DTD description also + public abstract void cdata(String cdata); + public abstract void emptyElement(String name, List attributes); + public abstract void startElement(String name, List attributes); + public abstract void endElement(String name); + public abstract void chardata(int beginLine, int beginColumn, int endLine, int endColumn); + public abstract void reference(String ref); +} \ No newline at end of file diff --git a/src/XMLElement.java b/src/org/catacombae/xml/XMLElement.java similarity index 50% rename from src/XMLElement.java rename to src/org/catacombae/xml/XMLElement.java index 4a8658d..ac347e3 100644 --- a/src/XMLElement.java +++ b/src/org/catacombae/xml/XMLElement.java @@ -1,25 +1,24 @@ /*- * Copyright (C) 2006 Erik Larsson * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * along with this program. If not, see . */ +package org.catacombae.xml; + import java.io.PrintStream; -abstract class XMLElement { +public abstract class XMLElement { protected abstract void _printTree(PrintStream pw, int level); } diff --git a/src/org/catacombae/xml/XMLNode.java b/src/org/catacombae/xml/XMLNode.java new file mode 100644 index 0000000..98bf02d --- /dev/null +++ b/src/org/catacombae/xml/XMLNode.java @@ -0,0 +1,68 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import java.util.LinkedList; +import java.io.*; + +public class XMLNode extends XMLElement { + public final String namespaceURI; + public final String sName; + public final String qName; + public final Attribute2[] attrs; + public final XMLNode parent; + private final LinkedList children; + + public XMLNode(String namespaceURI, String sName, + String qName, Attribute2[] attrs, XMLNode parent) { + this.namespaceURI = namespaceURI; + this.sName = sName; + this.qName = qName; + this.attrs = attrs; + this.parent = parent; + this.children = new LinkedList(); + } + + public void addChild(XMLElement x) { + children.addLast(x); + } + + public void printTree(PrintStream pw) { + _printTree(pw, 0); + } + + protected void _printTree(PrintStream pw, int level) { + for(int i = 0; i < level; ++i) + pw.print(" "); + pw.print("<"); + pw.print(qName); + for(Attribute2 a : attrs) + pw.print(" " + a.qName + "=" + a.value); + pw.println(">"); + for(XMLElement xe : children) + xe._printTree(pw, level+1); + + for(int i = 0; i < level; ++i) + pw.print(" "); + pw.println(""); + } + public XMLElement[] getChildren() { + return children.toArray(new XMLElement[children.size()]); + } + +} diff --git a/src/org/catacombae/xml/XMLText.java b/src/org/catacombae/xml/XMLText.java new file mode 100644 index 0000000..9fcf4b8 --- /dev/null +++ b/src/org/catacombae/xml/XMLText.java @@ -0,0 +1,140 @@ +/*- + * Copyright (C) 2006 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml; + +import org.catacombae.dmgextractor.io.*; +import java.io.*; +import java.nio.charset.Charset; + +public class XMLText extends XMLElement { + private static final char CR = '\r'; + private static final char LF = '\n'; + private static final char TAB = '\t'; + private final String text; + private int beginLine = -1, beginColumn = -1, endLine = -1, endColumn = -1; + private long beginOffset, endOffset; + private final SynchronizedRandomAccessStream xmlFile; + private Charset encoding; + + public XMLText(String text) { + this.text = text; + this.xmlFile = null; + } + public XMLText(SynchronizedRandomAccessStream xmlFile, Charset encoding, + long beginOffset, long endOffset) { + this.text = null; + this.xmlFile = xmlFile; + this.encoding = encoding; + this.beginOffset = beginOffset; + this.endOffset = endOffset; + //SynchronizedRandomAccessStream sras = new SynchronizedRandomAccessStream(new RandomAccessFileStream(raf)); + } + + /** This way of dealing with the issue of lines and columns is very heavy and fragile as it tries to + read through the entire xmlFile to seek forward to the desired lines and columns (there's no + algorithmic way to do this, so we have toseek exhaustively). The calculation will not be performed + in the constructor, but on the first call to getText()... (in order to reduce workload) */ + public XMLText(SynchronizedRandomAccessStream xmlFile, Charset encoding, + int beginLine, int beginColumn, int endLine, int endColumn) { + this(xmlFile, encoding, -1, -1); // we set the -1 fields later, ... + if(endLine < beginLine || (endLine == beginLine && endColumn < beginColumn)) + throw new IllegalArgumentException("negative interval length"); + + this.beginLine = beginLine; + this.beginColumn = beginColumn; + this.endLine = endLine; + this.endColumn = endColumn; + } + + public Reader getText() throws IOException { + if(text == null) { + if(beginOffset == -1 && endOffset == -1) + calculateOffsets(); + return new InputStreamReader(new RandomAccessInputStream(xmlFile, beginOffset, endOffset-beginOffset), encoding); + } + else + return new StringReader(text); + } + + private void calculateOffsets() throws IOException { + ByteCountInputStream bcis = new ByteCountInputStream(new BufferedInputStream(new RandomAccessInputStream(xmlFile))); + Reader lnr = new CharByCharReader(bcis, encoding); + //Vi har xmlFile + //CharsetDecoder decoder = Charset.newDecoder(); + + boolean previousCR = false; + long lineNumber = 1, colNumber = -1; + //int beginOffset = -1, endOffset = -1; + + int currentChar = 0; + while(currentChar >= 0) { + char c = (char)currentChar; + + boolean lfskip = false; + if(c == CR) { + ++lineNumber; + previousCR = true; + } + else if(c == LF) { + if(!previousCR) { + ++lineNumber; + colNumber = 0; + } + else { + previousCR = false; + lfskip = true; // We haven't changed the col or line number in this iteration, as in the other cases + } + } + else if(c == TAB) { + colNumber += 8; + previousCR = false; + } + else { + ++colNumber; + previousCR = false; + } + +// System.err.println("Trying to read... lineNumber=" + lineNumber + " colNumber=" + colNumber); + + if(!lfskip) { + if(lineNumber == beginLine && colNumber == beginColumn) + beginOffset = bcis.getBytesRead()-1; // We have already passed the position. + if(lineNumber == endLine && colNumber == endColumn) { + endOffset = bcis.getBytesRead(); + break; + } + } + currentChar = lnr.read(); + } + + if(beginOffset == -1 || endOffset == -1) + throw new RuntimeException("Could not find the requested interval! (begin: (" + beginLine + "," + beginColumn + ") end: (" + endLine + "," + endColumn + "))"); +// else +// System.out.println("Terminating with beginOffset=" + beginOffset + " endOffset=" + endOffset); + } + + protected void _printTree(PrintStream pw, int level) { + for(int i = 0; i < level; ++i) + pw.print(" "); + pw.println(text.toString()); + } + +// public static void main(String[] args) { +// System.out.println(args[0] + " " + args[1]); +// } +} diff --git a/src/org/catacombae/xml/apx/.cvsignore b/src/org/catacombae/xml/apx/.cvsignore new file mode 100644 index 0000000..cd5dc09 --- /dev/null +++ b/src/org/catacombae/xml/apx/.cvsignore @@ -0,0 +1,5 @@ +*~ +*# +*.class +.DS_Store +Thumbs.db diff --git a/src/org/catacombae/xml/apx/APXParser.java b/src/org/catacombae/xml/apx/APXParser.java new file mode 100644 index 0000000..c8809c9 --- /dev/null +++ b/src/org/catacombae/xml/apx/APXParser.java @@ -0,0 +1,804 @@ +/* Generated By:JavaCC: Do not edit this line. APXParser.java */ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; +import org.catacombae.xml.*; + +import java.util.*; +import java.io.*; +import java.nio.charset.Charset; + +/** Early version of the APX Parser for XML. Currently does not parse DTDs and so doesn't check + for any validity constraints. It just breaks up the syntactic structure of an XML file and + reports it to an org.catacombae.xml.XMLContentHandler. This is sufficient for my needs so it + remains to be seen if the parser will be extended in the future. */ +public class APXParser implements APXParserConstants { + public static final String DEFAULT_ENCODING = "US-ASCII"; + //private Reader usedReader; + private XMLContentHandler contentHandler; + + /** Test main method. The first and only argument denotes a file containing an XML document. */ + public static void main(String[] args) throws Exception { + InputStream is; + if(args.length != 1) + throw new RuntimeException("The only valid argument is the name of the input file!"); + + // First, we read the encoding from the xml declaration (if it exists) + APXParser encodingParser = create(new InputStreamReader(new FileInputStream(args[0]), DEFAULT_ENCODING), + new NullXMLContentHandler(Charset.forName(DEFAULT_ENCODING))); + String encoding = null; + try { encoding = encodingParser.xmlDecl();} catch(ParseException pe) {} + if(encoding == null) encoding = DEFAULT_ENCODING; + + encodingParser = null; // GC candy + + // Then we create a new stream, and parse the entire document using the appropriate encoding + InputStreamReader usedReader; + usedReader = new InputStreamReader(new FileInputStream(args[0]), encoding); + APXParser a = create(usedReader, new DebugXMLContentHandler(Charset.forName(encoding))); //new NullXMLContentHandler(encoding));// + a.xmlDocument(); + } + + /** This is the way to create an APXParser. Don't use the constructor even if it would be possible. + @param misr the reader supplying the input data for the parser. Can not be null. + @param xch the content handler that takes care of the contents of the parsed XML document. Can + not be null. Use NullXMLContentHandler if you are not intrested in the contents of + the document. + @return the parser (parser.xmlDocument() will start the parsing) */ + public static APXParser create(Reader misr, XMLContentHandler xch) { + APXParser a = new APXParser(misr); + //a.usedReader = misr; + a.contentHandler = xch; + return a; + } + + final public void xmlDocument() throws ParseException { + prolog(); + element(); + label_1: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STARTPI: + case STARTCOMMENT: + ; + break; + default: + jj_la1[0] = jj_gen; + break label_1; + } + misc(); + } + jj_consume_token(0); + } + + final public void prolog() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STARTXMLDECL: + xmlDecl(); + break; + default: + jj_la1[1] = jj_gen; + ; + } + label_2: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STARTPI: + case STARTCOMMENT: + ; + break; + default: + jj_la1[2] = jj_gen; + break label_2; + } + misc(); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STARTDOCTYPEDECL: + doctypeDecl(); + label_3: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STARTPI: + case STARTCOMMENT: + ; + break; + default: + jj_la1[3] = jj_gen; + break label_3; + } + misc(); + } + break; + default: + jj_la1[4] = jj_gen; + ; + } + } + +/* For convenience, this method returns the encoding. We need to determine that before anything else. */ + final public String xmlDecl() throws ParseException { + String version, encoding = null; + Boolean standalone = null; + jj_consume_token(STARTXMLDECL); + // Takes us to state WithinXMLDecl + version = versionInfo(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ENCODING: + /*LOOKAHEAD(2)*/ encoding = encodingDecl(); + break; + default: + jj_la1[5] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STANDALONE: + /*LOOKAHEAD(2)*/ standalone = sdDecl(); + break; + default: + jj_la1[6] = jj_gen; + ; + } + jj_consume_token(ENDXMLDECL); + contentHandler.xmlDecl(version, encoding, standalone); {if (true) return encoding;} + throw new Error("Missing return statement in function"); + } + + final public String versionInfo() throws ParseException { + Token versionString; + jj_consume_token(VERSION); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case VERSION_DOPEN: + jj_consume_token(VERSION_DOPEN); + versionString = jj_consume_token(VER); + jj_consume_token(VERSION_DCLOSE); + break; + case VERSION_SOPEN: + jj_consume_token(VERSION_SOPEN); + versionString = jj_consume_token(VER); + jj_consume_token(VERSION_SCLOSE); + break; + default: + jj_la1[7] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return versionString.image;} + throw new Error("Missing return statement in function"); + } + + final public String encodingDecl() throws ParseException { + Token encoding; + jj_consume_token(ENCODING); + jj_consume_token(XMLDECL_EQ); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case XD_SQUOTE_OPEN: + jj_consume_token(XD_SQUOTE_OPEN); + encoding = jj_consume_token(XD_SQUOTE_STRING); + jj_consume_token(XD_SQUOTE_CLOSE); + break; + case XD_DQUOTE_OPEN: + jj_consume_token(XD_DQUOTE_OPEN); + encoding = jj_consume_token(XD_DQUOTE_STRING); + jj_consume_token(XD_DQUOTE_CLOSE); + break; + default: + jj_la1[8] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return encoding.image;} + throw new Error("Missing return statement in function"); + } + + final public boolean sdDecl() throws ParseException { + boolean b; + jj_consume_token(STANDALONE); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STANDALONE_DOPEN: + jj_consume_token(STANDALONE_DOPEN); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STANDALONE_YES: + jj_consume_token(STANDALONE_YES); + b = true; + break; + case STANDALONE_NO: + jj_consume_token(STANDALONE_NO); + b = false; + break; + default: + jj_la1[9] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + jj_consume_token(STANDALONE_DCLOSE); + break; + case STANDALONE_SOPEN: + jj_consume_token(STANDALONE_SOPEN); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STANDALONE_YES: + jj_consume_token(STANDALONE_YES); + b = true; + break; + case STANDALONE_NO: + jj_consume_token(STANDALONE_NO); + b = false; + break; + default: + jj_la1[10] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + jj_consume_token(STANDALONE_SCLOSE); + break; + default: + jj_la1[11] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return b;} + throw new Error("Missing return statement in function"); + } + +// Needs more work to support inline DTD declarations + final public void doctypeDecl() throws ParseException { + Token name; + jj_consume_token(STARTDOCTYPEDECL); + // DEFAULT -> WithinDoctypeDecl + + // Lexical state: WithinDoctypeDecl + /**/ name = jj_consume_token(WDD_NAME); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case EXTERNALID: + jj_consume_token(EXTERNALID); + break; + default: + jj_la1[12] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case BEGIN_INTSUBSET: + jj_consume_token(BEGIN_INTSUBSET); + intSubset(); + break; + default: + jj_la1[13] = jj_gen; + ; + } + jj_consume_token(ENDDOCTYPEDECL); + contentHandler.doctype(name.image, null); + } + + final public void intSubset() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case WHATEVER: + jj_consume_token(WHATEVER); + break; + default: + jj_la1[14] = jj_gen; + ; + } + jj_consume_token(END_INTSUBSET); + } + +//void markupDecl() : +//{} +//{ +// +//} + final public void misc() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STARTCOMMENT: + comment(); + break; + case STARTPI: + pi(); + break; + default: + jj_la1[15] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + final public void comment() throws ParseException { + Token t; + StringBuilder sb = new StringBuilder(); + jj_consume_token(STARTCOMMENT); + label_4: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMENT_CHAR: + ; + break; + default: + jj_la1[16] = jj_gen; + break label_4; + } + t = jj_consume_token(COMMENT_CHAR); + sb.append(t.image); + } + jj_consume_token(ENDCOMMENT); + contentHandler.comment(sb.toString()); + } + + final public void pi() throws ParseException { + Token target; + StringBuilder content = null; + jj_consume_token(STARTPI); + target = jj_consume_token(PITARGET); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case WITHINPI_S: + jj_consume_token(WITHINPI_S); + content = piContent(); + break; + case ENDPI: + jj_consume_token(ENDPI); + break; + default: + jj_la1[17] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + contentHandler.pi(target.image, content.toString()); + } + +// returns the content of the processor instruction, minus the trailing "?>" + final public StringBuilder piContent() throws ParseException { + Token t; + StringBuilder sb = new StringBuilder(); + label_5: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ENDPI: + jj_consume_token(ENDPI); + break; + case PC_CHAR: + t = jj_consume_token(PC_CHAR); + sb.append(t.image); + break; + default: + jj_la1[18] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ENDPI: + case PC_CHAR: + ; + break; + default: + jj_la1[19] = jj_gen; + break label_5; + } + } + {if (true) return sb;} + throw new Error("Missing return statement in function"); + } + + final public void element() throws ParseException { + String name; + Attribute currentAttribute; + LinkedList attributes = new LinkedList(); + jj_consume_token(STARTTAG); + // DEFAULT -> WithinTag + name = elementname(); + label_6: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case WT_NAME: + ; + break; + default: + jj_la1[20] = jj_gen; + break label_6; + } + currentAttribute = attribute(); + attributes.add(currentAttribute); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case EMPTY_ENDTAG: + jj_consume_token(EMPTY_ENDTAG); + contentHandler.emptyElement(name, attributes); + break; + case ENDTAG: + jj_consume_token(ENDTAG); + contentHandler.startElement(name, attributes); + content(); + etag(name); + break; + default: + jj_la1[21] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + final public String elementname() throws ParseException { + Token t; + t = jj_consume_token(WT_NAME); + {if (true) return t.image;} + throw new Error("Missing return statement in function"); + } + + final public Attribute attribute() throws ParseException { + Token t; + String name; + LinkedList value = new LinkedList(); + name = elementname(); + jj_consume_token(WT_EQ); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case WT_DQUOTE: + jj_consume_token(WT_DQUOTE); + label_7: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ATTR_DQUOTE_STRING: + case ATTR_REFERENCE: + ; + break; + default: + jj_la1[22] = jj_gen; + break label_7; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ATTR_DQUOTE_STRING: + t = jj_consume_token(ATTR_DQUOTE_STRING); + value.add(new Attribute.StringComponent(t.image)); + break; + case ATTR_REFERENCE: + t = jj_consume_token(ATTR_REFERENCE); + value.add(new Attribute.ReferenceComponent(t.image)); + break; + default: + jj_la1[23] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(ATTR_DQUOTE); + break; + case WT_SQUOTE: + jj_consume_token(WT_SQUOTE); + label_8: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ATTR_SQUOTE_STRING: + case ATTR_REFERENCE: + ; + break; + default: + jj_la1[24] = jj_gen; + break label_8; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ATTR_SQUOTE_STRING: + t = jj_consume_token(ATTR_SQUOTE_STRING); + value.add(new Attribute.StringComponent(t.image)); + break; + case ATTR_REFERENCE: + t = jj_consume_token(ATTR_REFERENCE); + value.add(new Attribute.ReferenceComponent(t.image)); + break; + default: + jj_la1[25] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(ATTR_SQUOTE); + break; + default: + jj_la1[26] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return new Attribute(name, new Attribute.Value(value));} + throw new Error("Missing return statement in function"); + } + + final public void etag(String startName) throws ParseException { + String name; + jj_consume_token(STARTCLOSINGTAG); + // DEFAULT -> WithinTag + name = elementname(); + jj_consume_token(ENDTAG); + if(startName.equals(name)) + contentHandler.endElement(name); + else + {if (true) throw new ParseException("Expected \"= 0) { + la1tokens[jj_kind] = true; + jj_kind = -1; + } + for (int i = 0; i < 34; i++) { + if (jj_la1[i] == jj_gen) { + for (int j = 0; j < 32; j++) { + if ((jj_la1_0[i] & (1<. + */ + +// Name explanation: APX = APX Parser for XML (at least officially) + +options { + STATIC = false; + UNICODE_INPUT = true; +} + +PARSER_BEGIN(APXParser) +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; +import org.catacombae.xml.*; + +import java.util.*; +import java.io.*; +import java.nio.charset.Charset; + +/** Early version of the APX Parser for XML. Currently does not parse DTDs and so doesn't check + for any validity constraints. It just breaks up the syntactic structure of an XML file and + reports it to an org.catacombae.xml.XMLContentHandler. This is sufficient for my needs so it + remains to be seen if the parser will be extended in the future. */ +public class APXParser { + public static final String DEFAULT_ENCODING = "US-ASCII"; + //private Reader usedReader; + private XMLContentHandler contentHandler; + + /** Test main method. The first and only argument denotes a file containing an XML document. */ + public static void main(String[] args) throws Exception { + InputStream is; + if(args.length != 1) + throw new RuntimeException("The only valid argument is the name of the input file!"); + + // First, we read the encoding from the xml declaration (if it exists) + APXParser encodingParser = create(new InputStreamReader(new FileInputStream(args[0]), DEFAULT_ENCODING), + new NullXMLContentHandler(Charset.forName(DEFAULT_ENCODING))); + String encoding = null; + try { encoding = encodingParser.xmlDecl();} catch(ParseException pe) {} + if(encoding == null) encoding = DEFAULT_ENCODING; + + encodingParser = null; // GC candy + + // Then we create a new stream, and parse the entire document using the appropriate encoding + InputStreamReader usedReader; + usedReader = new InputStreamReader(new FileInputStream(args[0]), encoding); + APXParser a = create(usedReader, new DebugXMLContentHandler(Charset.forName(encoding))); //new NullXMLContentHandler(encoding));// + a.xmlDocument(); + } + + /** This is the way to create an APXParser. Don't use the constructor even if it would be possible. + @param misr the reader supplying the input data for the parser. Can not be null. + @param xch the content handler that takes care of the contents of the parsed XML document. Can + not be null. Use NullXMLContentHandler if you are not intrested in the contents of + the document. + @return the parser (parser.xmlDocument() will start the parsing) */ + public static APXParser create(Reader misr, XMLContentHandler xch) { + APXParser a = new APXParser(misr); + //a.usedReader = misr; + a.contentHandler = xch; + return a; + } +} +PARSER_END(APXParser) + +/* Tokens common to all states. */ +/* PROBLEMS: The class CHAR should be able to match the 10000-10FFFF range, but java's Unicode + escape does not allow it, since you have to convert it to UTF-16BE first. This conversion + might not be easy to do as part of a token, and it's really not urgent, considering this + parser is only used to parse the Plist. This is non-urgent TODO. */ +<*> TOKEN: { + < #SINGLE_S: (" " | "\t" | "\n" | "\r") > + //| < #S: ( )+ > + | < #CHAR: ["\u0009", "\n", "\r", "\u0020"-"\uD7FF", "\uE000"-"\uFFFD"/*, "\u10000"-"\u10FFFF"*/] > + | < #NAME: ( )* > + | < #NAMESTART: ( | "_" | ":" ) > + | < #NAMECHAR: | | "." | "-" | "_" | ":" | | > + | < #LETTER: ( | ) > + | < #BASECHAR: ["\u0041"-"\u005A","\u0061"-"\u007A","\u00C0"-"\u00D6","\u00D8"-"\u00F6", + "\u00F8"-"\u00FF","\u0100"-"\u0131","\u0134"-"\u013E","\u0141"-"\u0148", + "\u014A"-"\u017E","\u0180"-"\u01C3","\u01CD"-"\u01F0","\u01F4"-"\u01F5", + "\u01FA"-"\u0217","\u0250"-"\u02A8","\u02BB"-"\u02C1","\u0386", + "\u0388"-"\u038A","\u038C","\u038E"-"\u03A1","\u03A3"-"\u03CE", + "\u03D0"-"\u03D6","\u03DA","\u03DC","\u03DE","\u03E0","\u03E2"-"\u03F3", + "\u0401"-"\u040C","\u040E"-"\u044F","\u0451"-"\u045C","\u045E"-"\u0481", + "\u0490"-"\u04C4","\u04C7"-"\u04C8","\u04CB"-"\u04CC","\u04D0"-"\u04EB", + "\u04EE"-"\u04F5","\u04F8"-"\u04F9","\u0531"-"\u0556","\u0559", + "\u0561"-"\u0586","\u05D0"-"\u05EA","\u05F0"-"\u05F2","\u0621"-"\u063A", + "\u0641"-"\u064A","\u0671"-"\u06B7","\u06BA"-"\u06BE","\u06C0"-"\u06CE", + "\u06D0"-"\u06D3","\u06D5","\u06E5"-"\u06E6","\u0905"-"\u0939","\u093D", + "\u0958"-"\u0961","\u0985"-"\u098C","\u098F"-"\u0990","\u0993"-"\u09A8", + "\u09AA"-"\u09B0","\u09B2","\u09B6"-"\u09B9","\u09DC"-"\u09DD", + "\u09DF"-"\u09E1","\u09F0"-"\u09F1","\u0A05"-"\u0A0A","\u0A0F"-"\u0A10", + "\u0A13"-"\u0A28","\u0A2A"-"\u0A30","\u0A32"-"\u0A33","\u0A35"-"\u0A36", + "\u0A38"-"\u0A39","\u0A59"-"\u0A5C","\u0A5E","\u0A72"-"\u0A74", + "\u0A85"-"\u0A8B","\u0A8D","\u0A8F"-"\u0A91","\u0A93"-"\u0AA8", + "\u0AAA"-"\u0AB0","\u0AB2"-"\u0AB3","\u0AB5"-"\u0AB9","\u0ABD","\u0AE0", + "\u0B05"-"\u0B0C","\u0B0F"-"\u0B10","\u0B13"-"\u0B28","\u0B2A"-"\u0B30", + "\u0B32"-"\u0B33","\u0B36"-"\u0B39","\u0B3D","\u0B5C"-"\u0B5D", + "\u0B5F"-"\u0B61","\u0B85"-"\u0B8A","\u0B8E"-"\u0B90","\u0B92"-"\u0B95", + "\u0B99"-"\u0B9A","\u0B9C","\u0B9E"-"\u0B9F","\u0BA3"-"\u0BA4", + "\u0BA8"-"\u0BAA","\u0BAE"-"\u0BB5","\u0BB7"-"\u0BB9","\u0C05"-"\u0C0C", + "\u0C0E"-"\u0C10","\u0C12"-"\u0C28","\u0C2A"-"\u0C33","\u0C35"-"\u0C39", + "\u0C60"-"\u0C61","\u0C85"-"\u0C8C","\u0C8E"-"\u0C90","\u0C92"-"\u0CA8", + "\u0CAA"-"\u0CB3","\u0CB5"-"\u0CB9","\u0CDE","\u0CE0"-"\u0CE1", + "\u0D05"-"\u0D0C","\u0D0E"-"\u0D10","\u0D12"-"\u0D28","\u0D2A"-"\u0D39", + "\u0D60"-"\u0D61","\u0E01"-"\u0E2E","\u0E30","\u0E32"-"\u0E33", + "\u0E40"-"\u0E45","\u0E81"-"\u0E82","\u0E84","\u0E87"-"\u0E88","\u0E8A", + "\u0E8D","\u0E94"-"\u0E97","\u0E99"-"\u0E9F","\u0EA1"-"\u0EA3","\u0EA5", + "\u0EA7","\u0EAA"-"\u0EAB","\u0EAD"-"\u0EAE","\u0EB0","\u0EB2"-"\u0EB3", + "\u0EBD","\u0EC0"-"\u0EC4","\u0F40"-"\u0F47","\u0F49"-"\u0F69", + "\u10A0"-"\u10C5","\u10D0"-"\u10F6","\u1100","\u1102"-"\u1103", + "\u1105"-"\u1107","\u1109","\u110B"-"\u110C","\u110E"-"\u1112","\u113C", + "\u113E","\u1140","\u114C","\u114E","\u1150","\u1154"-"\u1155","\u1159", + "\u115F"-"\u1161","\u1163","\u1165","\u1167","\u1169","\u116D"-"\u116E", + "\u1172"-"\u1173","\u1175","\u119E","\u11A8","\u11AB","\u11AE"-"\u11AF", + "\u11B7"-"\u11B8","\u11BA","\u11BC"-"\u11C2","\u11EB","\u11F0","\u11F9", + "\u1E00"-"\u1E9B","\u1EA0"-"\u1EF9","\u1F00"-"\u1F15","\u1F18"-"\u1F1D", + "\u1F20"-"\u1F45","\u1F48"-"\u1F4D","\u1F50"-"\u1F57","\u1F59","\u1F5B", + "\u1F5D","\u1F5F"-"\u1F7D","\u1F80"-"\u1FB4","\u1FB6"-"\u1FBC","\u1FBE", + "\u1FC2"-"\u1FC4","\u1FC6"-"\u1FCC","\u1FD0"-"\u1FD3","\u1FD6"-"\u1FDB", + "\u1FE0"-"\u1FEC","\u1FF2"-"\u1FF4","\u1FF6"-"\u1FFC","\u2126", + "\u212A"-"\u212B","\u212E","\u2180"-"\u2182","\u3041"-"\u3094", + "\u30A1"-"\u30FA","\u3105"-"\u312C","\uAC00"-"\uD7A3"] > + | < #IDEOGRAPHIC: ["\u4E00"-"\u9FA5","\u3007","\u3021"-"\u3029"] > + | < #COMBININGCHAR: ["\u0300"-"\u0345", "\u0360"-"\u0361", "\u0483"-"\u0486", + "\u0591"-"\u05A1", "\u05A3"-"\u05B9", "\u05BB"-"\u05BD", "\u05BF", + "\u05C1"-"\u05C2", "\u05C4", "\u064B"-"\u0652", "\u0670", + "\u06D6"-"\u06DC", "\u06DD"-"\u06DF", "\u06E0"-"\u06E4", + "\u06E7"-"\u06E8", "\u06EA"-"\u06ED", "\u0901"-"\u0903", "\u093C", + "\u093E"-"\u094C", "\u094D", "\u0951"-"\u0954", "\u0962"-"\u0963", + "\u0981"-"\u0983", "\u09BC", "\u09BE", "\u09BF", "\u09C0"-"\u09C4", + "\u09C7"-"\u09C8", "\u09CB"-"\u09CD", "\u09D7", "\u09E2"-"\u09E3", + "\u0A02", "\u0A3C", "\u0A3E", "\u0A3F", "\u0A40"-"\u0A42", + "\u0A47"-"\u0A48", "\u0A4B"-"\u0A4D", "\u0A70"-"\u0A71", + "\u0A81"-"\u0A83", "\u0ABC", "\u0ABE"-"\u0AC5", "\u0AC7"-"\u0AC9", + "\u0ACB"-"\u0ACD", "\u0B01"-"\u0B03", "\u0B3C", "\u0B3E"-"\u0B43", + "\u0B47"-"\u0B48", "\u0B4B"-"\u0B4D", "\u0B56"-"\u0B57", + "\u0B82"-"\u0B83", "\u0BBE"-"\u0BC2", "\u0BC6"-"\u0BC8", + "\u0BCA"-"\u0BCD", "\u0BD7", "\u0C01"-"\u0C03", "\u0C3E"-"\u0C44", + "\u0C46"-"\u0C48", "\u0C4A"-"\u0C4D", "\u0C55"-"\u0C56", + "\u0C82"-"\u0C83", "\u0CBE"-"\u0CC4", "\u0CC6"-"\u0CC8", + "\u0CCA"-"\u0CCD", "\u0CD5"-"\u0CD6", "\u0D02"-"\u0D03", + "\u0D3E"-"\u0D43", "\u0D46"-"\u0D48", "\u0D4A"-"\u0D4D", + "\u0D57", "\u0E31", "\u0E34"-"\u0E3A", "\u0E47"-"\u0E4E", "\u0EB1", + "\u0EB4"-"\u0EB9", "\u0EBB"-"\u0EBC", "\u0EC8"-"\u0ECD", + "\u0F18"-"\u0F19", "\u0F35", "\u0F37", "\u0F39", "\u0F3E", "\u0F3F", + "\u0F71"-"\u0F84", "\u0F86"-"\u0F8B", "\u0F90"-"\u0F95", "\u0F97", + "\u0F99"-"\u0FAD", "\u0FB1"-"\u0FB7", "\u0FB9", "\u20D0"-"\u20DC", + "\u20E1", "\u302A"-"\u302F", "\u3099", "\u309A"] > + | < #DIGIT: ["\u0030"-"\u0039", "\u0660"-"\u0669", "\u06F0"-"\u06F9", "\u0966"-"\u096F", + "\u09E6"-"\u09EF", "\u0A66"-"\u0A6F", "\u0AE6"-"\u0AEF", "\u0B66"-"\u0B6F", + "\u0BE7"-"\u0BEF", "\u0C66"-"\u0C6F", "\u0CE6"-"\u0CEF", "\u0D66"-"\u0D6F", + "\u0E50"-"\u0E59", "\u0ED0"-"\u0ED9", "\u0F20"-"\u0F29"] > + | < #EXTENDER: ["\u00B7", "\u02D0", "\u02D1", "\u0387", "\u0640", "\u0E46", "\u0EC6", + "\u3005", "\u3031"-"\u3035", "\u309D"-"\u309E", "\u30FC"-"\u30FE"] > + | < #REFERENCE: | > + | < #ENTITYREF: "&" ()* ";" > + | < #CHARREF: "&#" (["0"-"9"])+ ";" | "&#x" (["0"-"9", "a"-"f", "A"-"F"])+ ";" > +} + + +SKIP: // Whitespace is skipped in lexical state DEFAULT +{ + < > +} + +TOKEN : { + < STARTTAG: "<" > : WithinTag + | < STARTCLOSINGTAG: " : WithinTag + | < STARTPI: " : WithinPI + | < STARTXMLDECL: " : WithinXMLDecl + | < STARTDOCTYPEDECL: " : WithinDoctypeDecl + | < STARTCOMMENT: "" > : DEFAULT + | < COMMENT_ILLEGAL: "--" > + | < COMMENT_CHAR: > +} +void comment() : +{ + Token t; + StringBuilder sb = new StringBuilder(); +} +{ + // DEFAULT -> Comment + ( t = {sb.append(t.image);} )* + // Comment -> DEFAULT + { contentHandler.comment(sb.toString()); } +} + +/********************************************************* + * Here starts the parsing of the processor instructions * + *********************************************************/ + +/* There are two states within a PI, first the one recognizing the identifier of the target + of the processor instruction, represented by . Then the parser switches to a + state where everything is accepted except for the token. + The token of state DEFAULT takes us to the first PI state, labeled WithinPi, + the token (representing whitespace) takes us to the second PI state (where + everything goes except for ) and the token takes us back to DEFAULT. */ + + + TOKEN: { + < ENDPI: "?>" > : DEFAULT +} + + TOKEN: { + < ILLEGALTARGET: ( "X" | "x") ("M" | "m") ("L" | "l") ()* > + | < PITARGET: > + | < WITHINPI_S: > : WithinPIContent +} +void pi() : +{ + Token target; + StringBuilder content = null; +} +{ + + target = //{ System.out.println("target: " + target.image); } + ( content = piContent() | ) + { contentHandler.pi(target.image, content.toString()); } +} + + TOKEN: { + //< ENDPI: "?>" > : WithinXMLDecl + < PC_CHAR: > + //< NONCONTENT: ()* "?>" ()* > + //< CONTENT: ("?" (">" | ) | > +} + +// returns the content of the processor instruction, minus the trailing "?>" +StringBuilder piContent() : +{ + Token t; + StringBuilder sb = new StringBuilder(); +} +{ + ( + | + t = { sb.append(t.image); } )+ + { return sb; } +} + +/************************************************** + * Here starts the parsing of the actual elements * + **************************************************/ + TOKEN: { + < WT_EQ: "=" > + | < WT_NAME: > + //| < WT_S: > + | < WT_DQUOTE: "\"" > : Attribute + | < WT_SQUOTE: "'" > : Attribute + | < EMPTY_ENDTAG: "/>" > : DEFAULT + | < ENDTAG: ">" > : DEFAULT +} + + SKIP: { + < > +} + +void element() : +{ + String name; + Attribute currentAttribute; + LinkedList attributes = new LinkedList(); +} +{ + // DEFAULT -> WithinTag + name = elementname() //name = + ( currentAttribute = attribute() {attributes.add(currentAttribute);} )* + ( // WithinTag -> DEFAULT + { contentHandler.emptyElement(name, attributes); } + | + // WithinTag -> DEFAULT + { contentHandler.startElement(name, attributes); } + content() + etag(name) ) +} + +String elementname() : +{ + Token t; +} +{ + t = + { return t.image; } +} + + TOKEN: { + < ATTR_DQUOTE: "\"" > : WithinTag + | < ATTR_SQUOTE: "'" > : WithinTag + | < ATTR_DQUOTE_STRING: ~["<", "&", "\""] > + | < ATTR_SQUOTE_STRING: ~["<", "&", "'"] > + | < ATTR_REFERENCE: > +} + +Attribute attribute() : +{ + Token t; + String name; + LinkedList value = new LinkedList(); +} +{ + name = elementname() + ( + ( + // WithinTag -> Attribute + ( t = { value.add(new Attribute.StringComponent(t.image)); } | + t = { value.add(new Attribute.ReferenceComponent(t.image)); } )* + // Attribute -> WithinTag + ) + | + ( + // WithinTag -> Attribute + ( t = { value.add(new Attribute.StringComponent(t.image)); } | + t = { value.add(new Attribute.ReferenceComponent(t.image)); } )* + // Attribute -> WithinTag + ) + ) + { return new Attribute(name, new Attribute.Value(value)); } +} + +void etag(String startName) : +{ + String name; +} +{ + // DEFAULT -> WithinTag + name = elementname() /*()? */ + // WithinTag -> DEFAULT + { + if(startName.equals(name)) + contentHandler.endElement(name); + else + throw new ParseException("Expected \" | cdSect() | pi() | comment() ) ( charData() )? )* + + //( element() | | cdSect() | pi() | comment() | charData() )* +} + +void charData() : +{ + Token t; + int beginLine = -1, beginColumn = -1; + //StringBuilder sb = new StringBuilder(); +} +{ + ( t = + { if(beginLine == -1) { beginLine = t.beginLine; beginColumn = t.beginColumn; } } )+ + { contentHandler.chardata(beginLine, beginColumn, t.endLine, t.endColumn); } + + //( t = { System.out.println("chardata1: \"" + t.image + "\""); })+ +} + + +/****************************************** + * Here starts the parsing CDATA sections * + ******************************************/ + +/* To remove from a string of s we use a recursive method, cdSect_prime() */ + TOKEN: { + < ENDCDATA: "]]>" > : DEFAULT + | < WCD_CHAR: > +} +void cdSect() : +{ + Token t; + StringBuilder cdata = new StringBuilder(); +} +{ + // DEFAULT -> WithinCData + ( // WithinCData -> DEFAULT + | + t = {cdata.append(t.image);} )* + + { contentHandler.cdata(cdata.toString()); } +} + +/* +// Old but interesting code + +void xmlDocument() : +{ + Token tagName, optionName, optionValue; + LinkedList params = new LinkedList(); +} +{ + + tagName = + (optionName = + + ( + optionValue = + | + + optionValue = + ) + { params.add(new Param(optionName, optionValue)); } + )* + + { + System.out.println("Collected the following data:"); + System.out.println("Tag name: \"" + tagName.image + "\""); + System.out.println("Params:"); + for(Param p : params) + System.out.println(" \"" + p.id.image + "\" = \"" + p.string.image + "\""); + } + + + +} +*/ \ No newline at end of file diff --git a/src/org/catacombae/xml/apx/APXParserConstants.java b/src/org/catacombae/xml/apx/APXParserConstants.java new file mode 100644 index 0000000..dfe4b81 --- /dev/null +++ b/src/org/catacombae/xml/apx/APXParserConstants.java @@ -0,0 +1,198 @@ +/* Generated By:JavaCC: Do not edit this line. APXParserConstants.java */ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; + +public interface APXParserConstants { + + int EOF = 0; + int SINGLE_S = 1; + int CHAR = 2; + int NAME = 3; + int NAMESTART = 4; + int NAMECHAR = 5; + int LETTER = 6; + int BASECHAR = 7; + int IDEOGRAPHIC = 8; + int COMBININGCHAR = 9; + int DIGIT = 10; + int EXTENDER = 11; + int REFERENCE = 12; + int ENTITYREF = 13; + int CHARREF = 14; + int STARTTAG = 16; + int STARTCLOSINGTAG = 17; + int STARTPI = 18; + int STARTXMLDECL = 19; + int STARTDOCTYPEDECL = 20; + int STARTCOMMENT = 21; + int STARTCDATA = 22; + int CHARDATA = 23; + int DEFAULT_REFERENCE = 24; + int ENDXMLDECL = 25; + int XMLDECL_EQ = 26; + int XD_DQUOTE_OPEN = 27; + int XD_SQUOTE_OPEN = 28; + int VERSION = 29; + int ENCODING = 30; + int STANDALONE = 31; + int XD_DQUOTE_STRING = 33; + int XD_DQUOTE_CLOSE = 34; + int XD_SQUOTE_STRING = 35; + int XD_SQUOTE_CLOSE = 36; + int VERSION_DOPEN = 37; + int VERSION_DCLOSE = 38; + int VERSION_SOPEN = 39; + int VERSION_SCLOSE = 40; + int VER = 41; + int STANDALONE_DOPEN = 42; + int STANDALONE_DCLOSE = 43; + int STANDALONE_SOPEN = 44; + int STANDALONE_SCLOSE = 45; + int STANDALONE_YES = 46; + int STANDALONE_NO = 47; + int WDD_NAME = 48; + int ENDDOCTYPEDECL = 49; + int EXTERNALID = 50; + int SYSTEMLITERAL = 51; + int PUBIDLITERAL = 52; + int PUBIDCHAR = 53; + int BEGIN_INTSUBSET = 54; + int END_INTSUBSET = 56; + int WHATEVER = 57; + int ENDCOMMENT = 58; + int COMMENT_ILLEGAL = 59; + int COMMENT_CHAR = 60; + int ENDPI = 61; + int ILLEGALTARGET = 62; + int PITARGET = 63; + int WITHINPI_S = 64; + int PC_CHAR = 65; + int WT_EQ = 66; + int WT_NAME = 67; + int WT_DQUOTE = 68; + int WT_SQUOTE = 69; + int EMPTY_ENDTAG = 70; + int ENDTAG = 71; + int ATTR_DQUOTE = 73; + int ATTR_SQUOTE = 74; + int ATTR_DQUOTE_STRING = 75; + int ATTR_SQUOTE_STRING = 76; + int ATTR_REFERENCE = 77; + int ENDCDATA = 78; + int WCD_CHAR = 79; + + int WithinCData = 0; + int Attribute = 1; + int WithinTag = 2; + int WithinPIContent = 3; + int WithinPI = 4; + int Comment = 5; + int WithinIntSubset = 6; + int WithinDoctypeDecl = 7; + int WithinXMLDecl_Standalone = 8; + int WithinXMLDecl_Version = 9; + int WithinXMLDecl_SquoteString = 10; + int WithinXMLDecl_DquoteString = 11; + int WithinXMLDecl = 12; + int DEFAULT = 13; + + String[] tokenImage = { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "\"<\"", + "\"", + "", + "\"?>\"", + "\"=\"", + "\"\\\"\"", + "\"\\\'\"", + "\"version\"", + "\"encoding\"", + "\"standalone\"", + "", + "", + "\"\\\"\"", + "", + "\"\\\'\"", + "\"=\\\"\"", + "\"\\\"\"", + "\"=\\\'\"", + "\"\\\'\"", + "\"1.0\"", + "\"=\\\"\"", + "\"\\\"\"", + "\"=\\\'\"", + "\"\\\'\"", + "\"yes\"", + "\"no\"", + "", + "\">\"", + "", + "", + "", + "", + "\"[\"", + "", + "\"]\"", + "", + "\"-->\"", + "\"--\"", + "", + "\"?>\"", + "", + "", + "", + "", + "\"=\"", + "", + "\"\\\"\"", + "\"\\\'\"", + "\"/>\"", + "\">\"", + "", + "\"\\\"\"", + "\"\\\'\"", + "", + "", + "", + "\"]]>\"", + "", + }; + +} diff --git a/src/org/catacombae/xml/apx/APXParserTokenManager.java b/src/org/catacombae/xml/apx/APXParserTokenManager.java new file mode 100644 index 0000000..9b24fde --- /dev/null +++ b/src/org/catacombae/xml/apx/APXParserTokenManager.java @@ -0,0 +1,3055 @@ +/* Generated By:JavaCC: Do not edit this line. APXParserTokenManager.java */ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; +import org.catacombae.xml.*; +import java.util.*; +import java.io.*; +import java.nio.charset.Charset; + +public class APXParserTokenManager implements APXParserConstants +{ + public java.io.PrintStream debugStream = System.out; + public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } +private final int jjStopStringLiteralDfa_1(int pos, long active0, long active1) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_1(int pos, long active0, long active1) +{ + return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0, active1), pos + 1); +} +private final int jjStopAtPos(int pos, int kind) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; +} +private final int jjStartNfaWithStates_1(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_1(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_1() +{ + switch(curChar) + { + case 34: + return jjStopAtPos(0, 73); + case 39: + return jjStopAtPos(0, 74); + default : + return jjMoveNfa_1(0, 0); + } +} +private final void jjCheckNAdd(int state) +{ + if (jjrounds[state] != jjround) + { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } +} +private final void jjAddStates(int start, int end) +{ + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); +} +private final void jjCheckNAddTwoStates(int state1, int state2) +{ + jjCheckNAdd(state1); + jjCheckNAdd(state2); +} +private final void jjCheckNAddStates(int start, int end) +{ + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); +} +private final void jjCheckNAddStates(int start) +{ + jjCheckNAdd(jjnextStates[start]); + jjCheckNAdd(jjnextStates[start + 1]); +} +static final long[] jjbitVec0 = { + 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec2 = { + 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec3 = { + 0x0L, 0xffffffffffffc000L, 0xfffff0007fffffffL, 0x7fffffL +}; +static final long[] jjbitVec4 = { + 0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL +}; +static final long[] jjbitVec5 = { + 0x7ff3ffffffffffffL, 0x7ffffffffffffdfeL, 0xffffffffffffffffL, 0xfc31ffffffffe00fL +}; +static final long[] jjbitVec6 = { + 0xffffffL, 0xffffffffffff0000L, 0xf80001ffffffffffL, 0x3L +}; +static final long[] jjbitVec7 = { + 0x0L, 0x0L, 0xfffffffbffffd740L, 0xffffd547f7fffL +}; +static final long[] jjbitVec8 = { + 0xffffffffffffdffeL, 0xffffffffdffeffffL, 0xffffffffffff0003L, 0x33fcfffffff199fL +}; +static final long[] jjbitVec9 = { + 0xfffe000000000000L, 0xfffffffe027fffffL, 0x7fL, 0x707ffffff0000L +}; +static final long[] jjbitVec10 = { + 0x7fffffe00000000L, 0xfffe0000000007feL, 0x7cffffffffffffffL, 0x60002f7fffL +}; +static final long[] jjbitVec11 = { + 0x23ffffffffffffe0L, 0x3ff000000L, 0x3c5fdfffff99fe0L, 0x30003b0000000L +}; +static final long[] jjbitVec12 = { + 0x36dfdfffff987e0L, 0x1c00005e000000L, 0x23edfdfffffbafe0L, 0x100000000L +}; +static final long[] jjbitVec13 = { + 0x23cdfdfffff99fe0L, 0x3b0000000L, 0x3bfc718d63dc7e0L, 0x0L +}; +static final long[] jjbitVec14 = { + 0x3effdfffffddfe0L, 0x300000000L, 0x3effdfffffddfe0L, 0x340000000L +}; +static final long[] jjbitVec15 = { + 0x3fffdfffffddfe0L, 0x300000000L, 0x0L, 0x0L +}; +static final long[] jjbitVec16 = { + 0xd7ffffffffffeL, 0x3fL, 0x200d6caefef02596L, 0x1fL +}; +static final long[] jjbitVec17 = { + 0x0L, 0x3fffffffeffL, 0x0L, 0x0L +}; +static final long[] jjbitVec18 = { + 0x0L, 0x0L, 0xffffffff00000000L, 0x7fffffffff003fL +}; +static final long[] jjbitVec19 = { + 0x500000000007daedL, 0x2c62ab82315001L, 0xf580c90040000000L, 0x201080000000007L +}; +static final long[] jjbitVec20 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffff0fffffffL, 0x3ffffffffffffffL +}; +static final long[] jjbitVec21 = { + 0xffffffff3f3fffffL, 0x3fffffffaaff3f3fL, 0x5fdfffffffffffffL, 0x1fdc1fff0fcf1fdcL +}; +static final long[] jjbitVec22 = { + 0x4c4000000000L, 0x0L, 0x7L, 0x0L +}; +static final long[] jjbitVec23 = { + 0x3fe00000080L, 0xfffffffffffffffeL, 0xfffffffe001fffffL, 0x7ffffffffffffffL +}; +static final long[] jjbitVec24 = { + 0x1fffffffffe0L, 0x0L, 0x0L, 0x0L +}; +static final long[] jjbitVec25 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0x3fffffffffL, 0x0L +}; +static final long[] jjbitVec26 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0xfffffffffL, 0x0L +}; +static final long[] jjbitVec27 = { + 0x0L, 0x0L, 0x80000000000000L, 0xff7fffffff7fffffL +}; +static final long[] jjbitVec28 = { + 0xffffffL, 0xffffffffffff0000L, 0xf80001ffffffffffL, 0x30003L +}; +static final long[] jjbitVec29 = { + 0xffffffffffffffffL, 0x30000003fL, 0xfffffffbffffd7c0L, 0xffffd547f7fffL +}; +static final long[] jjbitVec30 = { + 0xffffffffffffdffeL, 0xffffffffdffeffffL, 0xffffffffffff007bL, 0x33fcfffffff199fL +}; +static final long[] jjbitVec31 = { + 0xfffe000000000000L, 0xfffffffe027fffffL, 0xbbfffffbfffe007fL, 0x707ffffff0016L +}; +static final long[] jjbitVec32 = { + 0x7fffffe00000000L, 0xffff03ff0007ffffL, 0x7cffffffffffffffL, 0x3ff3dffffef7fffL +}; +static final long[] jjbitVec33 = { + 0xf3ffffffffffffeeL, 0xffcfff1e3fffL, 0xd3c5fdfffff99feeL, 0x3ffcfb080399fL +}; +static final long[] jjbitVec34 = { + 0xd36dfdfffff987e4L, 0x1fffc05e003987L, 0xf3edfdfffffbafeeL, 0xffc100003bbfL +}; +static final long[] jjbitVec35 = { + 0xf3cdfdfffff99feeL, 0xffc3b0c0398fL, 0xc3bfc718d63dc7ecL, 0xff8000803dc7L +}; +static final long[] jjbitVec36 = { + 0xc3effdfffffddfeeL, 0xffc300603ddfL, 0xc3effdfffffddfecL, 0xffc340603ddfL +}; +static final long[] jjbitVec37 = { + 0xc3fffdfffffddfecL, 0xffc300803dcfL, 0x0L, 0x0L +}; +static final long[] jjbitVec38 = { + 0x7ff7ffffffffffeL, 0x3ff7fffL, 0x3bff6caefef02596L, 0x3ff3f5fL +}; +static final long[] jjbitVec39 = { + 0xc2a003ff03000000L, 0xfffe03fffffffeffL, 0x2fe3ffffebf0fdfL, 0x0L +}; +static final long[] jjbitVec40 = { + 0x0L, 0x0L, 0x0L, 0x21fff0000L +}; +static final long[] jjbitVec41 = { + 0x3efffe000000a0L, 0xfffffffffffffffeL, 0xfffffffe661fffffL, 0x77ffffffffffffffL +}; +private final int jjMoveNfa_1(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 12; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xefffff3fffffffffL & l) != 0L) + { + if (kind > 76) + kind = 76; + } + else if (curChar == 38) + jjAddStates(0, 1); + if ((0xefffffbbffffffffL & l) != 0L) + { + if (kind > 75) + kind = 75; + } + else if (curChar == 38) + jjstateSet[jjnewStateCnt++] = 3; + break; + case 1: + if ((0xefffff3fffffffffL & l) != 0L && kind > 76) + kind = 76; + break; + case 2: + if (curChar == 38) + jjstateSet[jjnewStateCnt++] = 3; + break; + case 3: + if (curChar == 58) + jjCheckNAddTwoStates(4, 5); + break; + case 4: + if ((0x7ff600000000000L & l) != 0L) + jjCheckNAddTwoStates(4, 5); + break; + case 5: + if (curChar == 59 && kind > 77) + kind = 77; + break; + case 6: + if (curChar == 38) + jjAddStates(0, 1); + break; + case 8: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(8, 5); + break; + case 9: + if (curChar == 35) + jjstateSet[jjnewStateCnt++] = 7; + break; + case 10: + if (curChar == 35) + jjCheckNAdd(11); + break; + case 11: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(11, 5); + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (kind > 76) + kind = 76; + if (kind > 75) + kind = 75; + break; + case 1: + if (kind > 76) + kind = 76; + break; + case 3: + case 4: + if ((0x7fffffe87fffffeL & l) != 0L) + jjCheckNAddTwoStates(4, 5); + break; + case 7: + if (curChar == 120) + jjCheckNAdd(8); + break; + case 8: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddTwoStates(8, 5); + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { + if (kind > 75) + kind = 75; + } + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + { + if (kind > 76) + kind = 76; + } + break; + case 1: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 76) + kind = 76; + break; + case 3: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(4, 5); + break; + case 4: + if (jjCanMove_2(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(4, 5); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 12 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_6(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_6(int pos, long active0) +{ + return jjMoveNfa_6(jjStopStringLiteralDfa_6(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_6(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_6(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_6() +{ + switch(curChar) + { + case 93: + return jjStopAtPos(0, 56); + default : + return jjMoveNfa_6(0, 0); + } +} +private final int jjMoveNfa_6(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 57; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xffffffffdfffffffL & l) == 0L) + break; + kind = 57; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (!jjCanMove_0(hiByte, i1, i2, l1, l2)) + break; + if (kind > 57) + kind = 57; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_7(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_7(int pos, long active0) +{ + return jjMoveNfa_7(jjStopStringLiteralDfa_7(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_7(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_7(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_7() +{ + switch(curChar) + { + case 62: + return jjStopAtPos(0, 49); + case 91: + return jjStopAtPos(0, 54); + default : + return jjMoveNfa_7(0, 0); + } +} +private final int jjMoveNfa_7(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 34; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x100002600L & l) != 0L) + { + if (kind > 55) + kind = 55; + } + else if (curChar == 58) + { + if (kind > 48) + kind = 48; + jjCheckNAdd(1); + } + break; + case 1: + if ((0x7ff600000000000L & l) == 0L) + break; + if (kind > 48) + kind = 48; + jjCheckNAdd(1); + break; + case 3: + if ((0x100002600L & l) != 0L) + jjAddStates(2, 4); + break; + case 4: + if (curChar == 34) + jjCheckNAddTwoStates(5, 6); + break; + case 5: + if ((0xfffffffbffffffffL & l) != 0L) + jjCheckNAddTwoStates(5, 6); + break; + case 6: + if (curChar == 34 && kind > 50) + kind = 50; + break; + case 7: + if (curChar == 39) + jjCheckNAddTwoStates(8, 9); + break; + case 8: + if ((0xffffff7fffffffffL & l) != 0L) + jjCheckNAddTwoStates(8, 9); + break; + case 9: + if (curChar == 39 && kind > 50) + kind = 50; + break; + case 16: + if ((0x100002600L & l) != 0L) + jjAddStates(5, 7); + break; + case 17: + if (curChar == 34) + jjCheckNAddTwoStates(18, 19); + break; + case 18: + if ((0xafffffbb00002400L & l) != 0L) + jjCheckNAddTwoStates(18, 19); + break; + case 19: + if (curChar == 34) + jjCheckNAdd(20); + break; + case 20: + if ((0x100002600L & l) != 0L) + jjCheckNAddStates(8, 10); + break; + case 21: + if (curChar == 34) + jjCheckNAddTwoStates(22, 6); + break; + case 22: + if ((0xfffffffbffffffffL & l) != 0L) + jjCheckNAddTwoStates(22, 6); + break; + case 23: + if (curChar == 39) + jjCheckNAddTwoStates(24, 9); + break; + case 24: + if ((0xffffff7fffffffffL & l) != 0L) + jjCheckNAddTwoStates(24, 9); + break; + case 25: + if (curChar == 39) + jjCheckNAddTwoStates(26, 27); + break; + case 26: + if ((0xafffff3b00002400L & l) != 0L) + jjCheckNAddTwoStates(26, 27); + break; + case 27: + if (curChar == 39) + jjCheckNAdd(20); + break; + case 33: + if ((0x100002600L & l) != 0L && kind > 55) + kind = 55; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 48) + kind = 48; + jjCheckNAdd(1); + } + if (curChar == 80) + jjstateSet[jjnewStateCnt++] = 31; + else if (curChar == 83) + jjstateSet[jjnewStateCnt++] = 13; + break; + case 1: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 48) + kind = 48; + jjCheckNAdd(1); + break; + case 2: + if (curChar == 77) + jjstateSet[jjnewStateCnt++] = 3; + break; + case 5: + jjCheckNAddTwoStates(5, 6); + break; + case 8: + jjCheckNAddTwoStates(8, 9); + break; + case 10: + if (curChar == 69) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 11: + if (curChar == 84) + jjstateSet[jjnewStateCnt++] = 10; + break; + case 12: + if (curChar == 83) + jjstateSet[jjnewStateCnt++] = 11; + break; + case 13: + if (curChar == 89) + jjstateSet[jjnewStateCnt++] = 12; + break; + case 14: + if (curChar == 83) + jjstateSet[jjnewStateCnt++] = 13; + break; + case 15: + if (curChar == 67) + jjstateSet[jjnewStateCnt++] = 16; + break; + case 18: + if ((0x7fffffe87ffffffL & l) != 0L) + jjAddStates(11, 12); + break; + case 22: + jjCheckNAddTwoStates(22, 6); + break; + case 24: + jjCheckNAddTwoStates(24, 9); + break; + case 26: + if ((0x7fffffe87ffffffL & l) != 0L) + jjAddStates(13, 14); + break; + case 28: + if (curChar == 73) + jjstateSet[jjnewStateCnt++] = 15; + break; + case 29: + if (curChar == 76) + jjstateSet[jjnewStateCnt++] = 28; + break; + case 30: + if (curChar == 66) + jjstateSet[jjnewStateCnt++] = 29; + break; + case 31: + if (curChar == 85) + jjstateSet[jjnewStateCnt++] = 30; + break; + case 32: + if (curChar == 80) + jjstateSet[jjnewStateCnt++] = 31; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 48) + kind = 48; + jjCheckNAdd(1); + break; + case 1: + if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) + break; + if (kind > 48) + kind = 48; + jjCheckNAdd(1); + break; + case 5: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(5, 6); + break; + case 8: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(8, 9); + break; + case 22: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(22, 6); + break; + case 24: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(24, 9); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 34 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_11(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_11(int pos, long active0) +{ + return jjMoveNfa_11(jjStopStringLiteralDfa_11(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_11(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_11(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_11() +{ + switch(curChar) + { + case 34: + return jjStopAtPos(0, 34); + default : + return jjMoveNfa_11(0, 0); + } +} +private final int jjMoveNfa_11(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xfffffffbffffffffL & l) == 0L) + break; + kind = 33; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 33; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (!jjCanMove_0(hiByte, i1, i2, l1, l2)) + break; + if (kind > 33) + kind = 33; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_5(int pos, long active0) +{ + switch (pos) + { + case 0: + if ((active0 & 0xc00000000000000L) != 0L) + { + jjmatchedKind = 60; + return -1; + } + return -1; + case 1: + if ((active0 & 0xc00000000000000L) != 0L) + { + if (jjmatchedPos == 0) + { + jjmatchedKind = 60; + jjmatchedPos = 0; + } + return -1; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_5(int pos, long active0) +{ + return jjMoveNfa_5(jjStopStringLiteralDfa_5(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_5(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_5(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_5() +{ + switch(curChar) + { + case 45: + return jjMoveStringLiteralDfa1_5(0xc00000000000000L); + default : + return jjMoveNfa_5(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_5(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_5(0, active0); + return 1; + } + switch(curChar) + { + case 45: + if ((active0 & 0x800000000000000L) != 0L) + { + jjmatchedKind = 59; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_5(active0, 0x400000000000000L); + default : + break; + } + return jjStartNfa_5(0, active0); +} +private final int jjMoveStringLiteralDfa2_5(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_5(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_5(1, active0); + return 2; + } + switch(curChar) + { + case 62: + if ((active0 & 0x400000000000000L) != 0L) + return jjStopAtPos(2, 58); + break; + default : + break; + } + return jjStartNfa_5(1, active0); +} +static final long[] jjbitVec42 = { + 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0x7fffffff00ffffffL +}; +static final long[] jjbitVec43 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0x3fffffffffffffffL +}; +private final int jjMoveNfa_5(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xffffffff00002600L & l) != 0L) + kind = 60; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 60; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (jjCanMove_3(hiByte, i1, i2, l1, l2) && kind > 60) + kind = 60; + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_12(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_12(int pos, long active0) +{ + return jjMoveNfa_12(jjStopStringLiteralDfa_12(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_12(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_12(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_12() +{ + switch(curChar) + { + case 34: + return jjStopAtPos(0, 27); + case 39: + return jjStopAtPos(0, 28); + case 61: + return jjStopAtPos(0, 26); + case 63: + return jjMoveStringLiteralDfa1_12(0x2000000L); + case 101: + return jjMoveStringLiteralDfa1_12(0x40000000L); + case 115: + return jjMoveStringLiteralDfa1_12(0x80000000L); + case 118: + return jjMoveStringLiteralDfa1_12(0x20000000L); + default : + return jjMoveNfa_12(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_12(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(0, active0); + return 1; + } + switch(curChar) + { + case 62: + if ((active0 & 0x2000000L) != 0L) + return jjStopAtPos(1, 25); + break; + case 101: + return jjMoveStringLiteralDfa2_12(active0, 0x20000000L); + case 110: + return jjMoveStringLiteralDfa2_12(active0, 0x40000000L); + case 116: + return jjMoveStringLiteralDfa2_12(active0, 0x80000000L); + default : + break; + } + return jjStartNfa_12(0, active0); +} +private final int jjMoveStringLiteralDfa2_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(1, active0); + return 2; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa3_12(active0, 0x80000000L); + case 99: + return jjMoveStringLiteralDfa3_12(active0, 0x40000000L); + case 114: + return jjMoveStringLiteralDfa3_12(active0, 0x20000000L); + default : + break; + } + return jjStartNfa_12(1, active0); +} +private final int jjMoveStringLiteralDfa3_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(1, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(2, active0); + return 3; + } + switch(curChar) + { + case 110: + return jjMoveStringLiteralDfa4_12(active0, 0x80000000L); + case 111: + return jjMoveStringLiteralDfa4_12(active0, 0x40000000L); + case 115: + return jjMoveStringLiteralDfa4_12(active0, 0x20000000L); + default : + break; + } + return jjStartNfa_12(2, active0); +} +private final int jjMoveStringLiteralDfa4_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(2, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(3, active0); + return 4; + } + switch(curChar) + { + case 100: + return jjMoveStringLiteralDfa5_12(active0, 0xc0000000L); + case 105: + return jjMoveStringLiteralDfa5_12(active0, 0x20000000L); + default : + break; + } + return jjStartNfa_12(3, active0); +} +private final int jjMoveStringLiteralDfa5_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(3, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(4, active0); + return 5; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa6_12(active0, 0x80000000L); + case 105: + return jjMoveStringLiteralDfa6_12(active0, 0x40000000L); + case 111: + return jjMoveStringLiteralDfa6_12(active0, 0x20000000L); + default : + break; + } + return jjStartNfa_12(4, active0); +} +private final int jjMoveStringLiteralDfa6_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(4, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(5, active0); + return 6; + } + switch(curChar) + { + case 108: + return jjMoveStringLiteralDfa7_12(active0, 0x80000000L); + case 110: + if ((active0 & 0x20000000L) != 0L) + return jjStopAtPos(6, 29); + return jjMoveStringLiteralDfa7_12(active0, 0x40000000L); + default : + break; + } + return jjStartNfa_12(5, active0); +} +private final int jjMoveStringLiteralDfa7_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(5, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(6, active0); + return 7; + } + switch(curChar) + { + case 103: + if ((active0 & 0x40000000L) != 0L) + return jjStopAtPos(7, 30); + break; + case 111: + return jjMoveStringLiteralDfa8_12(active0, 0x80000000L); + default : + break; + } + return jjStartNfa_12(6, active0); +} +private final int jjMoveStringLiteralDfa8_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(6, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(7, active0); + return 8; + } + switch(curChar) + { + case 110: + return jjMoveStringLiteralDfa9_12(active0, 0x80000000L); + default : + break; + } + return jjStartNfa_12(7, active0); +} +private final int jjMoveStringLiteralDfa9_12(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_12(7, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_12(8, active0); + return 9; + } + switch(curChar) + { + case 101: + if ((active0 & 0x80000000L) != 0L) + return jjStopAtPos(9, 31); + break; + default : + break; + } + return jjStartNfa_12(8, active0); +} +private final int jjMoveNfa_12(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x100002600L & l) != 0L) + kind = 32; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_4(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_4(int pos, long active0) +{ + return jjMoveNfa_4(jjStopStringLiteralDfa_4(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_4(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_4(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_4() +{ + switch(curChar) + { + case 63: + return jjMoveStringLiteralDfa1_4(0x2000000000000000L); + default : + return jjMoveNfa_4(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_4(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_4(0, active0); + return 1; + } + switch(curChar) + { + case 62: + if ((active0 & 0x2000000000000000L) != 0L) + return jjStopAtPos(1, 61); + break; + default : + break; + } + return jjStartNfa_4(0, active0); +} +private final int jjMoveNfa_4(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 8; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x100002600L & l) != 0L) + { + if (kind > 64) + kind = 64; + } + else if (curChar == 58) + { + if (kind > 63) + kind = 63; + jjCheckNAdd(6); + } + break; + case 3: + if (curChar != 58) + break; + if (kind > 62) + kind = 62; + jjCheckNAddTwoStates(3, 4); + break; + case 4: + if ((0x7ff600000000000L & l) == 0L) + break; + if (kind > 62) + kind = 62; + jjCheckNAddTwoStates(3, 4); + break; + case 5: + if (curChar != 58) + break; + if (kind > 63) + kind = 63; + jjCheckNAdd(6); + break; + case 6: + if ((0x7ff600000000000L & l) == 0L) + break; + if (kind > 63) + kind = 63; + jjCheckNAdd(6); + break; + case 7: + if ((0x100002600L & l) != 0L) + kind = 64; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 63) + kind = 63; + jjCheckNAdd(6); + } + if ((0x100000001000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 1; + break; + case 1: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 2: + if ((0x100000001000L & l) == 0L) + break; + if (kind > 62) + kind = 62; + jjCheckNAdd(3); + break; + case 3: + case 4: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 62) + kind = 62; + jjCheckNAddTwoStates(3, 4); + break; + case 5: + case 6: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 63) + kind = 63; + jjCheckNAdd(6); + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 63) + kind = 63; + jjCheckNAdd(6); + break; + case 3: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 62) + kind = 62; + jjCheckNAddTwoStates(3, 4); + break; + case 4: + if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) + break; + if (kind > 62) + kind = 62; + jjCheckNAddTwoStates(3, 4); + break; + case 6: + if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) + break; + if (kind > 63) + kind = 63; + jjCheckNAdd(6); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 8 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_13(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_13(int pos, long active0) +{ + return jjMoveNfa_13(jjStopStringLiteralDfa_13(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_13(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_13(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_13() +{ + switch(curChar) + { + case 60: + jjmatchedKind = 16; + return jjMoveStringLiteralDfa1_13(0x7e0000L); + default : + return jjMoveNfa_13(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_13(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(0, active0); + return 1; + } + switch(curChar) + { + case 33: + return jjMoveStringLiteralDfa2_13(active0, 0x700000L); + case 47: + if ((active0 & 0x20000L) != 0L) + return jjStopAtPos(1, 17); + break; + case 63: + if ((active0 & 0x40000L) != 0L) + { + jjmatchedKind = 18; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_13(active0, 0x80000L); + default : + break; + } + return jjStartNfa_13(0, active0); +} +private final int jjMoveStringLiteralDfa2_13(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_13(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(1, active0); + return 2; + } + switch(curChar) + { + case 45: + return jjMoveStringLiteralDfa3_13(active0, 0x200000L); + case 68: + return jjMoveStringLiteralDfa3_13(active0, 0x100000L); + case 91: + return jjMoveStringLiteralDfa3_13(active0, 0x400000L); + case 120: + return jjMoveStringLiteralDfa3_13(active0, 0x80000L); + default : + break; + } + return jjStartNfa_13(1, active0); +} +private final int jjMoveStringLiteralDfa3_13(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_13(1, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(2, active0); + return 3; + } + switch(curChar) + { + case 45: + if ((active0 & 0x200000L) != 0L) + return jjStopAtPos(3, 21); + break; + case 67: + return jjMoveStringLiteralDfa4_13(active0, 0x400000L); + case 79: + return jjMoveStringLiteralDfa4_13(active0, 0x100000L); + case 109: + return jjMoveStringLiteralDfa4_13(active0, 0x80000L); + default : + break; + } + return jjStartNfa_13(2, active0); +} +private final int jjMoveStringLiteralDfa4_13(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_13(2, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(3, active0); + return 4; + } + switch(curChar) + { + case 67: + return jjMoveStringLiteralDfa5_13(active0, 0x100000L); + case 68: + return jjMoveStringLiteralDfa5_13(active0, 0x400000L); + case 108: + if ((active0 & 0x80000L) != 0L) + return jjStopAtPos(4, 19); + break; + default : + break; + } + return jjStartNfa_13(3, active0); +} +private final int jjMoveStringLiteralDfa5_13(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_13(3, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(4, active0); + return 5; + } + switch(curChar) + { + case 65: + return jjMoveStringLiteralDfa6_13(active0, 0x400000L); + case 84: + return jjMoveStringLiteralDfa6_13(active0, 0x100000L); + default : + break; + } + return jjStartNfa_13(4, active0); +} +private final int jjMoveStringLiteralDfa6_13(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_13(4, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(5, active0); + return 6; + } + switch(curChar) + { + case 84: + return jjMoveStringLiteralDfa7_13(active0, 0x400000L); + case 89: + return jjMoveStringLiteralDfa7_13(active0, 0x100000L); + default : + break; + } + return jjStartNfa_13(5, active0); +} +private final int jjMoveStringLiteralDfa7_13(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_13(5, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(6, active0); + return 7; + } + switch(curChar) + { + case 65: + return jjMoveStringLiteralDfa8_13(active0, 0x400000L); + case 80: + return jjMoveStringLiteralDfa8_13(active0, 0x100000L); + default : + break; + } + return jjStartNfa_13(6, active0); +} +private final int jjMoveStringLiteralDfa8_13(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_13(6, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_13(7, active0); + return 8; + } + switch(curChar) + { + case 69: + if ((active0 & 0x100000L) != 0L) + return jjStopAtPos(8, 20); + break; + case 91: + if ((active0 & 0x400000L) != 0L) + return jjStopAtPos(8, 22); + break; + default : + break; + } + return jjStartNfa_13(7, active0); +} +private final int jjMoveNfa_13(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 16; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xefffffbfffffffffL & l) != 0L) + { + if (kind > 23) + kind = 23; + } + else if (curChar == 38) + jjAddStates(15, 16); + if ((0x100002600L & l) != 0L) + { + if (kind > 15) + kind = 15; + } + else if (curChar == 38) + jjstateSet[jjnewStateCnt++] = 7; + break; + case 1: + if ((0xefffffbfffffffffL & l) != 0L && kind > 23) + kind = 23; + break; + case 3: + if ((0xafffffbfffffffffL & l) != 0L && kind > 23) + kind = 23; + break; + case 6: + if (curChar == 38) + jjstateSet[jjnewStateCnt++] = 7; + break; + case 7: + if (curChar == 58) + jjCheckNAddTwoStates(8, 9); + break; + case 8: + if ((0x7ff600000000000L & l) != 0L) + jjCheckNAddTwoStates(8, 9); + break; + case 9: + if (curChar == 59 && kind > 24) + kind = 24; + break; + case 10: + if (curChar == 38) + jjAddStates(15, 16); + break; + case 12: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(12, 9); + break; + case 13: + if (curChar == 35) + jjstateSet[jjnewStateCnt++] = 11; + break; + case 14: + if (curChar == 35) + jjCheckNAdd(15); + break; + case 15: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(15, 9); + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xffffffffdfffffffL & l) != 0L) + { + if (kind > 23) + kind = 23; + } + else if (curChar == 93) + jjstateSet[jjnewStateCnt++] = 1; + if (curChar == 93) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 1: + if ((0xffffffffdfffffffL & l) != 0L && kind > 23) + kind = 23; + break; + case 2: + if (curChar == 93) + jjstateSet[jjnewStateCnt++] = 3; + break; + case 3: + if (kind > 23) + kind = 23; + break; + case 4: + if (curChar == 93) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 5: + if (curChar == 93) + jjstateSet[jjnewStateCnt++] = 1; + break; + case 7: + case 8: + if ((0x7fffffe87fffffeL & l) != 0L) + jjCheckNAddTwoStates(8, 9); + break; + case 11: + if (curChar == 120) + jjCheckNAdd(12); + break; + case 12: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddTwoStates(12, 9); + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + case 1: + case 3: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 23) + kind = 23; + break; + case 7: + if (jjCanMove_1(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(8, 9); + break; + case 8: + if (jjCanMove_2(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(8, 9); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 16 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjMoveStringLiteralDfa0_8() +{ + switch(curChar) + { + case 34: + return jjStopAtPos(0, 43); + case 39: + return jjStopAtPos(0, 45); + case 61: + return jjMoveStringLiteralDfa1_8(0x140000000000L); + case 110: + return jjMoveStringLiteralDfa1_8(0x800000000000L); + case 121: + return jjMoveStringLiteralDfa1_8(0x400000000000L); + default : + return 1; + } +} +private final int jjMoveStringLiteralDfa1_8(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return 1; + } + switch(curChar) + { + case 34: + if ((active0 & 0x40000000000L) != 0L) + return jjStopAtPos(1, 42); + break; + case 39: + if ((active0 & 0x100000000000L) != 0L) + return jjStopAtPos(1, 44); + break; + case 101: + return jjMoveStringLiteralDfa2_8(active0, 0x400000000000L); + case 111: + if ((active0 & 0x800000000000L) != 0L) + return jjStopAtPos(1, 47); + break; + default : + return 2; + } + return 2; +} +private final int jjMoveStringLiteralDfa2_8(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return 2; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return 2; + } + switch(curChar) + { + case 115: + if ((active0 & 0x400000000000L) != 0L) + return jjStopAtPos(2, 46); + break; + default : + return 3; + } + return 3; +} +private final int jjStopStringLiteralDfa_10(int pos, long active0) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_10(int pos, long active0) +{ + return jjMoveNfa_10(jjStopStringLiteralDfa_10(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_10(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_10(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_10() +{ + switch(curChar) + { + case 39: + return jjStopAtPos(0, 36); + default : + return jjMoveNfa_10(0, 0); + } +} +private final int jjMoveNfa_10(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xffffff7fffffffffL & l) == 0L) + break; + kind = 35; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 35; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (!jjCanMove_0(hiByte, i1, i2, l1, l2)) + break; + if (kind > 35) + kind = 35; + jjstateSet[jjnewStateCnt++] = 0; + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_3(int pos, long active0) +{ + switch (pos) + { + case 0: + if ((active0 & 0x2000000000000000L) != 0L) + { + jjmatchedKind = 65; + return -1; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_3(int pos, long active0) +{ + return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0), pos + 1); +} +private final int jjStartNfaWithStates_3(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_3(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_3() +{ + switch(curChar) + { + case 63: + return jjMoveStringLiteralDfa1_3(0x2000000000000000L); + default : + return jjMoveNfa_3(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_3(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_3(0, active0); + return 1; + } + switch(curChar) + { + case 62: + if ((active0 & 0x2000000000000000L) != 0L) + return jjStopAtPos(1, 61); + break; + default : + break; + } + return jjStartNfa_3(0, active0); +} +private final int jjMoveNfa_3(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xffffffff00002600L & l) != 0L) + kind = 65; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 65; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (jjCanMove_3(hiByte, i1, i2, l1, l2) && kind > 65) + kind = 65; + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1) +{ + switch (pos) + { + case 0: + if ((active1 & 0x4000L) != 0L) + { + jjmatchedKind = 79; + return -1; + } + return -1; + case 1: + if ((active1 & 0x4000L) != 0L) + { + if (jjmatchedPos == 0) + { + jjmatchedKind = 79; + jjmatchedPos = 0; + } + return -1; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_0(int pos, long active0, long active1) +{ + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1); +} +private final int jjStartNfaWithStates_0(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_0(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_0() +{ + switch(curChar) + { + case 93: + return jjMoveStringLiteralDfa1_0(0x4000L); + default : + return jjMoveNfa_0(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_0(long active1) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(0, 0L, active1); + return 1; + } + switch(curChar) + { + case 93: + return jjMoveStringLiteralDfa2_0(active1, 0x4000L); + default : + break; + } + return jjStartNfa_0(0, 0L, active1); +} +private final int jjMoveStringLiteralDfa2_0(long old1, long active1) +{ + if (((active1 &= old1)) == 0L) + return jjStartNfa_0(0, 0L, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(1, 0L, active1); + return 2; + } + switch(curChar) + { + case 62: + if ((active1 & 0x4000L) != 0L) + return jjStopAtPos(2, 78); + break; + default : + break; + } + return jjStartNfa_0(1, 0L, active1); +} +private final int jjMoveNfa_0(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 1; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xffffffff00002600L & l) != 0L) + kind = 79; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 79; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (jjCanMove_3(hiByte, i1, i2, l1, l2) && kind > 79) + kind = 79; + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 1 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_2(int pos, long active0, long active1) +{ + switch (pos) + { + default : + return -1; + } +} +private final int jjStartNfa_2(int pos, long active0, long active1) +{ + return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0, active1), pos + 1); +} +private final int jjStartNfaWithStates_2(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_2(state, pos + 1); +} +private final int jjMoveStringLiteralDfa0_2() +{ + switch(curChar) + { + case 34: + return jjStopAtPos(0, 68); + case 39: + return jjStopAtPos(0, 69); + case 47: + return jjMoveStringLiteralDfa1_2(0x40L); + case 61: + return jjStopAtPos(0, 66); + case 62: + return jjStopAtPos(0, 71); + default : + return jjMoveNfa_2(0, 0); + } +} +private final int jjMoveStringLiteralDfa1_2(long active1) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(0, 0L, active1); + return 1; + } + switch(curChar) + { + case 62: + if ((active1 & 0x40L) != 0L) + return jjStopAtPos(1, 70); + break; + default : + break; + } + return jjStartNfa_2(0, 0L, active1); +} +private final int jjMoveNfa_2(int startState, int curPos) +{ + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 3; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x100002600L & l) != 0L) + { + if (kind > 72) + kind = 72; + } + else if (curChar == 58) + { + if (kind > 67) + kind = 67; + jjCheckNAdd(1); + } + break; + case 1: + if ((0x7ff600000000000L & l) == 0L) + break; + if (kind > 67) + kind = 67; + jjCheckNAdd(1); + break; + case 2: + if ((0x100002600L & l) != 0L) + kind = 72; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + case 1: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 67) + kind = 67; + jjCheckNAdd(1); + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 0: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 67) + kind = 67; + jjCheckNAdd(1); + break; + case 1: + if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) + break; + if (kind > 67) + kind = 67; + jjCheckNAdd(1); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 3 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjMoveStringLiteralDfa0_9() +{ + switch(curChar) + { + case 34: + return jjStopAtPos(0, 38); + case 39: + return jjStopAtPos(0, 40); + case 49: + return jjMoveStringLiteralDfa1_9(0x20000000000L); + case 61: + return jjMoveStringLiteralDfa1_9(0xa000000000L); + default : + return 1; + } +} +private final int jjMoveStringLiteralDfa1_9(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return 1; + } + switch(curChar) + { + case 34: + if ((active0 & 0x2000000000L) != 0L) + return jjStopAtPos(1, 37); + break; + case 39: + if ((active0 & 0x8000000000L) != 0L) + return jjStopAtPos(1, 39); + break; + case 46: + return jjMoveStringLiteralDfa2_9(active0, 0x20000000000L); + default : + return 2; + } + return 2; +} +private final int jjMoveStringLiteralDfa2_9(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return 2; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return 2; + } + switch(curChar) + { + case 48: + if ((active0 & 0x20000000000L) != 0L) + return jjStopAtPos(2, 41); + break; + default : + return 3; + } + return 3; +} +static final int[] jjnextStates = { + 9, 10, 3, 4, 7, 16, 17, 25, 20, 21, 23, 18, 19, 26, 27, 13, + 14, +}; +private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + default : + if ((jjbitVec0[i1] & l1) != 0L) + return true; + return false; + } +} +private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec4[i2] & l2) != 0L); + case 1: + return ((jjbitVec5[i2] & l2) != 0L); + case 2: + return ((jjbitVec6[i2] & l2) != 0L); + case 3: + return ((jjbitVec7[i2] & l2) != 0L); + case 4: + return ((jjbitVec8[i2] & l2) != 0L); + case 5: + return ((jjbitVec9[i2] & l2) != 0L); + case 6: + return ((jjbitVec10[i2] & l2) != 0L); + case 9: + return ((jjbitVec11[i2] & l2) != 0L); + case 10: + return ((jjbitVec12[i2] & l2) != 0L); + case 11: + return ((jjbitVec13[i2] & l2) != 0L); + case 12: + return ((jjbitVec14[i2] & l2) != 0L); + case 13: + return ((jjbitVec15[i2] & l2) != 0L); + case 14: + return ((jjbitVec16[i2] & l2) != 0L); + case 15: + return ((jjbitVec17[i2] & l2) != 0L); + case 16: + return ((jjbitVec18[i2] & l2) != 0L); + case 17: + return ((jjbitVec19[i2] & l2) != 0L); + case 30: + return ((jjbitVec20[i2] & l2) != 0L); + case 31: + return ((jjbitVec21[i2] & l2) != 0L); + case 33: + return ((jjbitVec22[i2] & l2) != 0L); + case 48: + return ((jjbitVec23[i2] & l2) != 0L); + case 49: + return ((jjbitVec24[i2] & l2) != 0L); + case 159: + return ((jjbitVec25[i2] & l2) != 0L); + case 215: + return ((jjbitVec26[i2] & l2) != 0L); + default : + if ((jjbitVec3[i1] & l1) != 0L) + return true; + return false; + } +} +private static final boolean jjCanMove_2(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec27[i2] & l2) != 0L); + case 1: + return ((jjbitVec5[i2] & l2) != 0L); + case 2: + return ((jjbitVec28[i2] & l2) != 0L); + case 3: + return ((jjbitVec29[i2] & l2) != 0L); + case 4: + return ((jjbitVec30[i2] & l2) != 0L); + case 5: + return ((jjbitVec31[i2] & l2) != 0L); + case 6: + return ((jjbitVec32[i2] & l2) != 0L); + case 9: + return ((jjbitVec33[i2] & l2) != 0L); + case 10: + return ((jjbitVec34[i2] & l2) != 0L); + case 11: + return ((jjbitVec35[i2] & l2) != 0L); + case 12: + return ((jjbitVec36[i2] & l2) != 0L); + case 13: + return ((jjbitVec37[i2] & l2) != 0L); + case 14: + return ((jjbitVec38[i2] & l2) != 0L); + case 15: + return ((jjbitVec39[i2] & l2) != 0L); + case 16: + return ((jjbitVec18[i2] & l2) != 0L); + case 17: + return ((jjbitVec19[i2] & l2) != 0L); + case 30: + return ((jjbitVec20[i2] & l2) != 0L); + case 31: + return ((jjbitVec21[i2] & l2) != 0L); + case 32: + return ((jjbitVec40[i2] & l2) != 0L); + case 33: + return ((jjbitVec22[i2] & l2) != 0L); + case 48: + return ((jjbitVec41[i2] & l2) != 0L); + case 49: + return ((jjbitVec24[i2] & l2) != 0L); + case 159: + return ((jjbitVec25[i2] & l2) != 0L); + case 215: + return ((jjbitVec26[i2] & l2) != 0L); + default : + if ((jjbitVec3[i1] & l1) != 0L) + return true; + return false; + } +} +private static final boolean jjCanMove_3(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + case 255: + return ((jjbitVec43[i2] & l2) != 0L); + default : + if ((jjbitVec42[i1] & l1) != 0L) + return true; + return false; + } +} +public static final String[] jjstrLiteralImages = { +"", null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, "\74", "\74\57", "\74\77", "\74\77\170\155\154", +"\74\41\104\117\103\124\131\120\105", "\74\41\55\55", "\74\41\133\103\104\101\124\101\133", null, null, "\77\76", +"\75", "\42", "\47", "\166\145\162\163\151\157\156", +"\145\156\143\157\144\151\156\147", "\163\164\141\156\144\141\154\157\156\145", null, null, "\42", null, "\47", +"\75\42", "\42", "\75\47", "\47", "\61\56\60", "\75\42", "\42", "\75\47", "\47", +"\171\145\163", "\156\157", null, "\76", null, null, null, null, "\133", null, "\135", null, +"\55\55\76", "\55\55", null, "\77\76", null, null, null, null, "\75", null, "\42", "\47", +"\57\76", "\76", null, "\42", "\47", null, null, null, "\135\135\76", null, }; +public static final String[] lexStateNames = { + "WithinCData", + "Attribute", + "WithinTag", + "WithinPIContent", + "WithinPI", + "Comment", + "WithinIntSubset", + "WithinDoctypeDecl", + "WithinXMLDecl_Standalone", + "WithinXMLDecl_Version", + "WithinXMLDecl_SquoteString", + "WithinXMLDecl_DquoteString", + "WithinXMLDecl", + "DEFAULT", +}; +public static final int[] jjnewLexState = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 4, 12, 7, 5, 0, -1, -1, + 13, -1, 11, 10, 9, -1, 8, -1, -1, 12, -1, 12, -1, 12, -1, 12, -1, -1, 12, -1, 12, -1, -1, -1, 13, + -1, -1, -1, -1, 6, -1, 7, -1, 13, -1, -1, 13, -1, -1, 3, -1, -1, -1, 1, 1, 13, 13, -1, 2, 2, + -1, -1, -1, 13, -1, +}; +static final long[] jjtoToken = { + 0xff47fffeffff0001L, 0xfeffL, +}; +static final long[] jjtoSkip = { + 0x80000100008000L, 0x100L, +}; +protected SimpleCharStream input_stream; +private final int[] jjrounds = new int[34]; +private final int[] jjstateSet = new int[68]; +protected char curChar; +public APXParserTokenManager(SimpleCharStream stream){ + if (SimpleCharStream.staticFlag) + throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); + input_stream = stream; +} +public APXParserTokenManager(SimpleCharStream stream, int lexState){ + this(stream); + SwitchTo(lexState); +} +public void ReInit(SimpleCharStream stream) +{ + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); +} +private final void ReInitRounds() +{ + int i; + jjround = 0x80000001; + for (i = 34; i-- > 0;) + jjrounds[i] = 0x80000000; +} +public void ReInit(SimpleCharStream stream, int lexState) +{ + ReInit(stream); + SwitchTo(lexState); +} +public void SwitchTo(int lexState) +{ + if (lexState >= 14 || lexState < 0) + throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); + else + curLexState = lexState; +} + +protected Token jjFillToken() +{ + Token t = Token.newToken(jjmatchedKind); + t.kind = jjmatchedKind; + String im = jjstrLiteralImages[jjmatchedKind]; + t.image = (im == null) ? input_stream.GetImage() : im; + t.beginLine = input_stream.getBeginLine(); + t.beginColumn = input_stream.getBeginColumn(); + t.endLine = input_stream.getEndLine(); + t.endColumn = input_stream.getEndColumn(); + return t; +} + +int curLexState = 13; +int defaultLexState = 13; +int jjnewStateCnt; +int jjround; +int jjmatchedPos; +int jjmatchedKind; + +public Token getNextToken() +{ + int kind; + Token specialToken = null; + Token matchedToken; + int curPos = 0; + + EOFLoop : + for (;;) + { + try + { + curChar = input_stream.BeginToken(); + } + catch(java.io.IOException e) + { + jjmatchedKind = 0; + matchedToken = jjFillToken(); + return matchedToken; + } + + switch(curLexState) + { + case 0: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + break; + case 1: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_1(); + break; + case 2: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_2(); + break; + case 3: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_3(); + break; + case 4: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_4(); + break; + case 5: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_5(); + break; + case 6: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_6(); + break; + case 7: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_7(); + break; + case 8: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_8(); + break; + case 9: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_9(); + break; + case 10: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_10(); + break; + case 11: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_11(); + break; + case 12: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_12(); + break; + case 13: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_13(); + break; + } + if (jjmatchedKind != 0x7fffffff) + { + if (jjmatchedPos + 1 < curPos) + input_stream.backup(curPos - jjmatchedPos - 1); + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + return matchedToken; + } + else + { + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + continue EOFLoop; + } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { input_stream.readChar(); input_stream.backup(1); } + catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } + else + error_column++; + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + } + throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); + } +} + +} diff --git a/src/org/catacombae/xml/apx/ParseException.java b/src/org/catacombae/xml/apx/ParseException.java new file mode 100644 index 0000000..44faf6d --- /dev/null +++ b/src/org/catacombae/xml/apx/ParseException.java @@ -0,0 +1,209 @@ +/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; + +/** + * This exception is thrown when parse errors are encountered. + * You can explicitly create objects of this exception type by + * calling the method generateParseException in the generated + * parser. + * + * You can modify this class to customize your error reporting + * mechanisms so long as you retain the public fields. + */ +public class ParseException extends Exception { + + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. The boolean + * flag "specialConstructor" is also set to true to indicate that + * this constructor was used to create this object. + * This constructor calls its super class with the empty string + * to force the "toString" method of parent class "Throwable" to + * print the error message in the form: + * ParseException: + */ + public ParseException(Token currentTokenVal, + int[][] expectedTokenSequencesVal, + String[] tokenImageVal + ) + { + super(""); + specialConstructor = true; + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + } + + /** + * The following constructors are for use by you for whatever + * purpose you can think of. Constructing the exception in this + * manner makes the exception behave in the normal way - i.e., as + * documented in the class "Throwable". The fields "errorToken", + * "expectedTokenSequences", and "tokenImage" do not contain + * relevant information. The JavaCC generated code does not use + * these constructors. + */ + + public ParseException() { + super(); + specialConstructor = false; + } + + public ParseException(String message) { + super(message); + specialConstructor = false; + } + + /** + * This variable determines which constructor was used to create + * this object and thereby affects the semantics of the + * "getMessage" method (see below). + */ + protected boolean specialConstructor; + + /** + * This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * followng this token will (therefore) be the first error token. + */ + public Token currentToken; + + /** + * Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * This method has the standard behavior when this object has been + * created using the standard constructors. Otherwise, it uses + * "currentToken" and "expectedTokenSequences" to generate a parse + * error message and returns it. If this object has been created + * due to a parse error, and you do not catch it (it gets thrown + * from the parser), then this method is called during the printing + * of the final stack trace, and hence the correct error message + * gets displayed. + */ + public String getMessage() { + if (!specialConstructor) { + return super.getMessage(); + } + StringBuffer expected = new StringBuffer(); + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + expected.append(tokenImage[expectedTokenSequences[i][j]]).append(" "); + } + if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { + expected.append("..."); + } + expected.append(eol).append(" "); + } + String retval = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) retval += " "; + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += add_escapes(tok.image); + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + retval += "." + eol; + if (expectedTokenSequences.length == 1) { + retval += "Was expecting:" + eol + " "; + } else { + retval += "Was expecting one of:" + eol + " "; + } + retval += expected.toString(); + return retval; + } + + /** + * The end of line string for this machine. + */ + protected String eol = System.getProperty("line.separator", "\n"); + + /** + * Used to convert raw characters to their escaped version + * when these raw version cannot be used as part of an ASCII + * string literal. + */ + protected String add_escapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + +} diff --git a/src/org/catacombae/xml/apx/SimpleCharStream.java b/src/org/catacombae/xml/apx/SimpleCharStream.java new file mode 100644 index 0000000..798cfcc --- /dev/null +++ b/src/org/catacombae/xml/apx/SimpleCharStream.java @@ -0,0 +1,456 @@ +/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; + +/** + * An implementation of interface CharStream, where the stream is assumed to + * contain only ASCII characters (without unicode processing). + */ + +public class SimpleCharStream +{ + public static final boolean staticFlag = false; + int bufsize; + int available; + int tokenBegin; + public int bufpos = -1; + protected int bufline[]; + protected int bufcolumn[]; + + protected int column = 0; + protected int line = 1; + + protected boolean prevCharIsCR = false; + protected boolean prevCharIsLF = false; + + protected java.io.Reader inputStream; + + protected char[] buffer; + protected int maxNextCharInd = 0; + protected int inBuf = 0; + protected int tabSize = 8; + + protected void setTabSize(int i) { tabSize = i; } + protected int getTabSize(int i) { return tabSize; } + + + protected void ExpandBuff(boolean wrapAround) + { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try + { + if (wrapAround) + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, + bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos += (bufsize - tokenBegin)); + } + else + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos -= tokenBegin); + } + } + catch (Throwable t) + { + throw new Error(t.getMessage()); + } + + + bufsize += 2048; + available = bufsize; + tokenBegin = 0; + } + + protected void FillBuff() throws java.io.IOException + { + if (maxNextCharInd == available) + { + if (available == bufsize) + { + if (tokenBegin > 2048) + { + bufpos = maxNextCharInd = 0; + available = tokenBegin; + } + else if (tokenBegin < 0) + bufpos = maxNextCharInd = 0; + else + ExpandBuff(false); + } + else if (available > tokenBegin) + available = bufsize; + else if ((tokenBegin - available) < 2048) + ExpandBuff(true); + else + available = tokenBegin; + } + + int i; + try { + if ((i = inputStream.read(buffer, maxNextCharInd, + available - maxNextCharInd)) == -1) + { + inputStream.close(); + throw new java.io.IOException(); + } + else + maxNextCharInd += i; + return; + } + catch(java.io.IOException e) { + --bufpos; + backup(0); + if (tokenBegin == -1) + tokenBegin = bufpos; + throw e; + } + } + + public char BeginToken() throws java.io.IOException + { + tokenBegin = -1; + char c = readChar(); + tokenBegin = bufpos; + + return c; + } + + protected void UpdateLineColumn(char c) + { + column++; + + if (prevCharIsLF) + { + prevCharIsLF = false; + line += (column = 1); + } + else if (prevCharIsCR) + { + prevCharIsCR = false; + if (c == '\n') + { + prevCharIsLF = true; + } + else + line += (column = 1); + } + + switch (c) + { + case '\r' : + prevCharIsCR = true; + break; + case '\n' : + prevCharIsLF = true; + break; + case '\t' : + column--; + column += (tabSize - (column % tabSize)); + break; + default : + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + + public char readChar() throws java.io.IOException + { + if (inBuf > 0) + { + --inBuf; + + if (++bufpos == bufsize) + bufpos = 0; + + return buffer[bufpos]; + } + + if (++bufpos >= maxNextCharInd) + FillBuff(); + + char c = buffer[bufpos]; + + UpdateLineColumn(c); + return (c); + } + + /** + * @deprecated + * @see #getEndColumn + */ + + public int getColumn() { + return bufcolumn[bufpos]; + } + + /** + * @deprecated + * @see #getEndLine + */ + + public int getLine() { + return bufline[bufpos]; + } + + public int getEndColumn() { + return bufcolumn[bufpos]; + } + + public int getEndLine() { + return bufline[bufpos]; + } + + public int getBeginColumn() { + return bufcolumn[tokenBegin]; + } + + public int getBeginLine() { + return bufline[tokenBegin]; + } + + public void backup(int amount) { + + inBuf += amount; + if ((bufpos -= amount) < 0) + bufpos += bufsize; + } + + public SimpleCharStream(java.io.Reader dstream, int startline, + int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + + public SimpleCharStream(java.io.Reader dstream, int startline, + int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + public SimpleCharStream(java.io.Reader dstream) + { + this(dstream, 1, 1, 4096); + } + public void ReInit(java.io.Reader dstream, int startline, + int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + if (buffer == null || buffersize != buffer.length) + { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + prevCharIsLF = prevCharIsCR = false; + tokenBegin = inBuf = maxNextCharInd = 0; + bufpos = -1; + } + + public void ReInit(java.io.Reader dstream, int startline, + int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + + public void ReInit(java.io.Reader dstream) + { + ReInit(dstream, 1, 1, 4096); + } + public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, + int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException + { + this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); + } + + public SimpleCharStream(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); + } + + public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, + int startcolumn) throws java.io.UnsupportedEncodingException + { + this(dstream, encoding, startline, startcolumn, 4096); + } + + public SimpleCharStream(java.io.InputStream dstream, int startline, + int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException + { + this(dstream, encoding, 1, 1, 4096); + } + + public SimpleCharStream(java.io.InputStream dstream) + { + this(dstream, 1, 1, 4096); + } + + public void ReInit(java.io.InputStream dstream, String encoding, int startline, + int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException + { + ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); + } + + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); + } + + public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException + { + ReInit(dstream, encoding, 1, 1, 4096); + } + + public void ReInit(java.io.InputStream dstream) + { + ReInit(dstream, 1, 1, 4096); + } + public void ReInit(java.io.InputStream dstream, String encoding, int startline, + int startcolumn) throws java.io.UnsupportedEncodingException + { + ReInit(dstream, encoding, startline, startcolumn, 4096); + } + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + public String GetImage() + { + if (bufpos >= tokenBegin) + return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + else + return new String(buffer, tokenBegin, bufsize - tokenBegin) + + new String(buffer, 0, bufpos + 1); + } + + public char[] GetSuffix(int len) + { + char[] ret = new char[len]; + + if ((bufpos + 1) >= len) + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + else + { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, + len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } + + return ret; + } + + public void Done() + { + buffer = null; + bufline = null; + bufcolumn = null; + } + + /** + * Method to adjust line and column numbers for the start of a token. + */ + public void adjustBeginLineColumn(int newLine, int newCol) + { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) + { + len = bufpos - tokenBegin + inBuf + 1; + } + else + { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0, j = 0, k = 0; + int nextColDiff = 0, columnDiff = 0; + + while (i < len && + bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) + { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) + { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) + { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) + bufline[j] = newLine++; + else + bufline[j] = newLine; + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } + +} diff --git a/src/org/catacombae/xml/apx/Token.java b/src/org/catacombae/xml/apx/Token.java new file mode 100644 index 0000000..47d8f48 --- /dev/null +++ b/src/org/catacombae/xml/apx/Token.java @@ -0,0 +1,98 @@ +/* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; + +/** + * Describes the input token stream. + */ + +public class Token { + + /** + * An integer that describes the kind of this token. This numbering + * system is determined by JavaCCParser, and a table of these numbers is + * stored in the file ...Constants.java. + */ + public int kind; + + /** + * beginLine and beginColumn describe the position of the first character + * of this token; endLine and endColumn describe the position of the + * last character of this token. + */ + public int beginLine, beginColumn, endLine, endColumn; + + /** + * The string image of the token. + */ + public String image; + + /** + * A reference to the next regular (non-special) token from the input + * stream. If this is the last token from the input stream, or if the + * token manager has not read tokens beyond this one, this field is + * set to null. This is true only if this token is also a regular + * token. Otherwise, see below for a description of the contents of + * this field. + */ + public Token next; + + /** + * This field is used to access special tokens that occur prior to this + * token, but after the immediately preceding regular (non-special) token. + * If there are no such special tokens, this field is set to null. + * When there are more than one such special token, this field refers + * to the last of these special tokens, which in turn refers to the next + * previous special token through its specialToken field, and so on + * until the first special token (whose specialToken field is null). + * The next fields of special tokens refer to other special tokens that + * immediately follow it (without an intervening regular token). If there + * is no such token, this field is null. + */ + public Token specialToken; + + /** + * Returns the image. + */ + public String toString() + { + return image; + } + + /** + * Returns a new Token object, by default. However, if you want, you + * can create and return subclass objects based on the value of ofKind. + * Simply add the cases to the switch for all those special cases. + * For example, if you have a subclass of Token called IDToken that + * you want to create if ofKind is ID, simlpy add something like : + * + * case MyParserConstants.ID : return new IDToken(); + * + * to the following switch statement. Then you can cast matchedToken + * variable to the appropriate type and use it in your lexical actions. + */ + public static final Token newToken(int ofKind) + { + switch(ofKind) + { + default : return new Token(); + } + } + +} diff --git a/src/org/catacombae/xml/apx/TokenMgrError.java b/src/org/catacombae/xml/apx/TokenMgrError.java new file mode 100644 index 0000000..2cead60 --- /dev/null +++ b/src/org/catacombae/xml/apx/TokenMgrError.java @@ -0,0 +1,150 @@ +/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */ +/*- + * Copyright (C) 2007 Erik Larsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +package org.catacombae.xml.apx; + +public class TokenMgrError extends Error +{ + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + + /** + * Lexical error occured. + */ + static final int LEXICAL_ERROR = 0; + + /** + * An attempt wass made to create a second instance of a static token manager. + */ + static final int STATIC_LEXER_ERROR = 1; + + /** + * Tried to change to an invalid lexical state. + */ + static final int INVALID_LEXICAL_STATE = 2; + + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + static final int LOOP_DETECTED = 3; + + /** + * Indicates the reason why the exception is thrown. It will have + * one of the above 4 values. + */ + int errorCode; + + /** + * Replaces unprintable characters by their espaced (or unicode escaped) + * equivalents in the given string + */ + protected static final String addEscapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + + /** + * Returns a detailed message for the Error when it is thrown by the + * token manager to indicate a lexical error. + * Parameters : + * EOFSeen : indicates if EOF caused the lexicl error + * curLexState : lexical state in which this error occured + * errorLine : line number when the error occured + * errorColumn : column number when the error occured + * errorAfter : prefix that was seen before this error occured + * curchar : the offending character + * Note: You can customize the lexical error message by modifying this method. + */ + protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { + return("Lexical error at line " + + errorLine + ", column " + + errorColumn + ". Encountered: " + + (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") + + "after : \"" + addEscapes(errorAfter) + "\""); + } + + /** + * You can also modify the body of this method to customize your error messages. + * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not + * of end-users concern, so you can return something like : + * + * "Internal Error : Please file a bug report .... " + * + * from this method for such cases in the release version of your parser. + */ + public String getMessage() { + return super.getMessage(); + } + + /* + * Constructors of various flavors follow. + */ + + public TokenMgrError() { + } + + public TokenMgrError(String message, int reason) { + super(message); + errorCode = reason; + } + + public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { + this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + } +} diff --git a/src/seep/StringBuilder.java b/src/seep/StringBuilder.java deleted file mode 100644 index e82be05..0000000 --- a/src/seep/StringBuilder.java +++ /dev/null @@ -1,212 +0,0 @@ -/*- - * Copyright (C) 2006 Erik Larsson - * - * All rights reserved. - * - * This program 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 - * of the License, or (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -package java.lang; - -/** - * Stupid replacement for Java 1.5's StringBuilder class, allowing - * 1.5-applications to be recompiled with gcj. - * String concatenation operations will be a bit slow. - */ - -class StringBuilder implements java.io.Serializable, CharSequence { - private final StringBuffer contents; - - public StringBuilder() { - contents = new StringBuffer(); - } - public StringBuilder(int capacity) { - contents = new StringBuffer(capacity); - } -// public StringBuilder(CharSequence seq) { -// contents = new StringBuffer(seq); -// } - public StringBuilder(String str) { - contents = new StringBuffer(); - } - private StringBuilder(StringBuffer contents) { - this.contents = contents; - } - public StringBuilder append(boolean bool) { - contents.append(bool); - return this; - } - public StringBuilder append(char ch) { - contents.append(ch); - return this; - } - public StringBuilder append(char[] data) { - contents.append(data); - return this; - } - public StringBuilder append(char[] data, int offset, int count) { - contents.append(data, offset, count); - return this; - } - public StringBuilder append(double dnum) { - contents.append(dnum); - return this; - } - public StringBuilder append(float fnum) { - contents.append(fnum); - return this; - } - public StringBuilder append(int inum) { - contents.append(inum); - return this; - } -// public StringBuilder append(CharSequence seq) { -// contents.append(seq); -// return this; -// } -// public StringBuilder append(CharSequence seq, int start, int end) { -// contents.append(seq, start, end); -// return this; -// } - public StringBuilder append(Object obj) { - contents.append(obj); - return this; - } - public StringBuilder append(String str) { - contents.append(str); - return this; - } - public StringBuilder append(StringBuffer stringBuffer) { - contents.append(stringBuffer); - return this; - } - public StringBuilder append(long lnum) { - contents.append(lnum); - return this; - } -// public StringBuilder appendCodePoint(int code) { -// contents.appendCodePoint(code); -// return this; -// } - public int capacity() { - return contents.capacity(); - } - public char charAt(int index) { - return contents.charAt(index); - } - public StringBuilder delete(int start, int end) { - contents.delete(start, end); - return this; - } - public StringBuilder deleteCharAt(int index) { - contents.deleteCharAt(index); - return this; - } - public void ensureCapacity(int minimumCapacity) { - contents.ensureCapacity(minimumCapacity); - } - public void getChars(int srcOffset, int srcEnd, char[] dst, int dstOffset) { - contents.getChars(srcOffset, srcEnd, dst, dstOffset); - } - public int indexOf(String str) { - return contents.indexOf(str); - } - public int indexOf(String str, int fromIndex) { - return contents.indexOf(str, fromIndex); - } - public StringBuilder insert(int offset, boolean bool) { - contents.insert(offset, bool); - return this; - } - public StringBuilder insert(int offset, char ch) { - contents.insert(offset, ch); - return this; - } - public StringBuilder insert(int offset, char[] data) { - contents.insert(offset, data); - return this; - } - public StringBuilder insert(int offset, char[] str, int str_offset, int len) { - contents.insert(offset, str, str_offset, len); - return this; - } - public StringBuilder insert(int offset, double dnum) { - contents.insert(offset, dnum); - return this; - } - public StringBuilder insert(int offset, float fnum) { - contents.insert(offset, fnum); - return this; - } - public StringBuilder insert(int offset, int inum) { - contents.insert(offset, inum); - return this; - } -// public StringBuilder insert(int offset, CharSequence sequence) { -// contents.insert(offset, sequence); -// return this; -// } -// public StringBuilder insert(int offset, CharSequence sequence, int start, int end) { -// contents.insert(offset, sequence, start, end); -// return this; -// } - public StringBuilder insert(int offset, Object obj) { - contents.insert(offset, obj); - return this; - } - public StringBuilder insert(int offset, String str) { - contents.insert(offset, str); - return this; - } - public StringBuilder insert(int offset, long lnum) { - contents.insert(offset, lnum); - return this; - } - public int lastIndexOf(String str) { - return contents.lastIndexOf(str); - } - public int lastIndexOf(String str, int fromIndex) { - return contents.lastIndexOf(str, fromIndex); - } - public int length() { - return contents.length(); - } - public StringBuilder replace(int start, int end, String str) { - contents.replace(start, end, str); - return this; - } - public StringBuilder reverse() { - contents.reverse(); - return this; - } - public void setCharAt(int index, char ch) { - contents.setCharAt(index, ch); - } - public void setLength(int newLength) { - contents.setLength(newLength); - } - public CharSequence subSequence(int beginIndex, int endIndex) { - return contents.subSequence(beginIndex, endIndex); - } - public String substring(int beginIndex) { - return contents.substring(beginIndex); - } - public String substring(int beginIndex, int endIndex) { - return contents.substring(beginIndex, endIndex); - } - public String toString() { - return contents.toString(); - } -} diff --git a/targets/application/LICENSE.txt b/targets/application/LICENSE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/targets/application/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/targets/application/bin/dmgx.bat b/targets/application/bin/dmgx.bat new file mode 100644 index 0000000..0e88a10 --- /dev/null +++ b/targets/application/bin/dmgx.bat @@ -0,0 +1,5 @@ +@echo off +setlocal +set DMGX_CP="%~dp0..\lib\dmgextractor.jar" +java -cp %DMGX_CP% org.catacombae.dmgextractor.DMGExtractor -startupcommand dmgx %1 %2 %3 %4 %5 %6 %7 %8 %9 +endlocal diff --git a/targets/application/bin/dmgx.sh b/targets/application/bin/dmgx.sh new file mode 100644 index 0000000..b4b22c6 --- /dev/null +++ b/targets/application/bin/dmgx.sh @@ -0,0 +1,3 @@ +#!/bin/sh +DMGX_CP=../lib/dmgextractor.jar +java -cp $DMGX_CP org.catacombae.dmgextractor.DMGExtractor -startupcommand "$0" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" diff --git a/targets/application/lib/apache-ant-1.7.0-bzip2.jar b/targets/application/lib/apache-ant-1.7.0-bzip2.jar new file mode 100644 index 0000000..382ee3f Binary files /dev/null and b/targets/application/lib/apache-ant-1.7.0-bzip2.jar differ diff --git a/targets/application/lib/csframework.jar b/targets/application/lib/csframework.jar new file mode 100644 index 0000000..8a708a1 Binary files /dev/null and b/targets/application/lib/csframework.jar differ diff --git a/targets/application/lib/dmgextractor.jar b/targets/application/lib/dmgextractor.jar new file mode 100644 index 0000000..10ba0cb Binary files /dev/null and b/targets/application/lib/dmgextractor.jar differ diff --git a/lib/filedrop.jar b/targets/application/lib/filedrop.jar similarity index 100% rename from lib/filedrop.jar rename to targets/application/lib/filedrop.jar diff --git a/targets/application/lib/iharder-base64.jar b/targets/application/lib/iharder-base64.jar new file mode 100644 index 0000000..674fb59 Binary files /dev/null and b/targets/application/lib/iharder-base64.jar differ diff --git a/targets/application/lib/swing-layout-1.0.3.jar b/targets/application/lib/swing-layout-1.0.3.jar new file mode 100644 index 0000000..5353286 Binary files /dev/null and b/targets/application/lib/swing-layout-1.0.3.jar differ diff --git a/targets/hfsxlib/hfsx_dmglib.jar b/targets/hfsxlib/hfsx_dmglib.jar new file mode 100644 index 0000000..d2e7fe8 Binary files /dev/null and b/targets/hfsxlib/hfsx_dmglib.jar differ diff --git a/targets/standalone/dmgextractor-standalone.jar b/targets/standalone/dmgextractor-standalone.jar new file mode 100644 index 0000000..865c34f Binary files /dev/null and b/targets/standalone/dmgextractor-standalone.jar differ diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..c603e6b --- /dev/null +++ b/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh +BASE=dist +DMGX_CP=$BASE/lib/dmgextractor.jar:$BASE/lib/apache-ant-1.7.0-bzip2.jar:$BASE/lib/iharder-base64.jar +java -cp $DMGX_CP org.catacombae.$1 $2 $3 $4 $5 $6 $7 $8 $9 diff --git a/testencstream.bat b/testencstream.bat new file mode 100644 index 0000000..def500e --- /dev/null +++ b/testencstream.bat @@ -0,0 +1,5 @@ +@echo off + +call "%~dp0definevars.bat" + +java -cp "%DMGX_CLASSPATH%" org.catacombae.dmg.encrypted.ReadableCEncryptedEncodingStream %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/testencstream.sh b/testencstream.sh new file mode 100755 index 0000000..1dc044a --- /dev/null +++ b/testencstream.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +DMGX_CLASSPATH=`./definevars.sh` + +java -cp "$DMGX_CLASSPATH" org.catacombae.dmgextractor.encodings.encrypted.ReadableCEncryptedEncodingStream "$@" diff --git a/wbuildall.bat b/wbuildall.bat index 28fc301..0aa6232 100644 --- a/wbuildall.bat +++ b/wbuildall.bat @@ -1,3 +1,3 @@ -@echo off -call buildall.bat -pause +@echo off +call "%~dp0buildall.bat" +pause