Skip to content

Instantly share code, notes, and snippets.

@m92o
Created September 7, 2010 11:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m92o/568214 to your computer and use it in GitHub Desktop.
Save m92o/568214 to your computer and use it in GitHub Desktop.
// ipa2epub.scala
// usage: scala ipa2epub.scala src.ipa [dst.epub]
// TODO: error handling
import java.io.{ File, FileInputStream, FileOutputStream, BufferedInputStream }
import java.util.zip.{ ZipFile, ZipEntry, ZipOutputStream, CRC32 }
import scala.util.matching.Regex
val TopDir = "Payload"
def die(message: String) {
println(message)
exit(1)
}
def unzip(path: String): Option[String] = {
var result: Option[String] = None
val regex = new Regex("^" + TopDir + "/.+\\.app/book/")
val zip = new ZipFile(path)
val files = zip.entries
while (files.hasMoreElements) {
val file = files.nextElement
val name = file.getName
val base = regex.findPrefixOf(name)
if (base != None) {
if (file.isDirectory)
new File(name).mkdirs
else {
val in = new BufferedInputStream(zip.getInputStream(file))
val out = new FileOutputStream(name)
val buf = new Array[Byte](1024)
var len = in.read(buf)
while (len != -1) {
out.write(buf, 0, len)
len = in.read(buf)
}
in.close
out.close
}
result = base;
}
}
zip.close
result
}
def zip(path: String, base: String): Option[String] = {
var result: Option[String] = None
val file = new File(path)
val out = new ZipOutputStream(new FileOutputStream(file))
if (archive(out, base, new File(base + "mimetype"), false))
if (archive(out, base, new File(base + "META-INF"), true))
if (archive(out, base, new File(base + "OEBPS"), true))
result = Some(file.getPath)
out.close
result
}
def archive(out: ZipOutputStream, base: String, path: File, deflate: Boolean): Boolean = {
val result = path.exists
if (result) {
if (path.isDirectory)
path.listFiles.foreach(file => archive(out, base, file, deflate))
else {
val len = path.length.toInt
val buf = new Array[Byte](len)
val in = new BufferedInputStream(new FileInputStream(path))
in.read(buf, 0, len)
in.close
val file = new ZipEntry(("^" + base).r.replaceFirstIn(path.getPath, ""))
if (deflate == false) {
// MUST: setCrc before putNextEntry
val crc = new CRC32()
crc.update(buf)
file.setMethod(ZipEntry.STORED)
file.setSize(len)
file.setCrc(crc.getValue)
}
out.putNextEntry(file)
out.write(buf, 0, len)
out.closeEntry
}
}
result
}
def clean(path: File) {
if (path.isDirectory)
path.listFiles.foreach(clean)
path.delete
}
// main
val src = if (args.length > 0) args(0) else null
if (src == null || new File(src).exists == false) die("usage: scala ipa2epub.scala src.ipa [dst.epub]")
val dst = if (args.length > 1) args(1) else "\\w+$".r.replaceFirstIn(new File(src).getName, "epub")
val base = unzip(src)
if (base == None) die("No book found in the archive")
val top = new File(TopDir)
if (zip(dst, base.get) == None) die("No contents found in the archive")
clean(top)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment