/* Copyright (C) 2010 Copyright 2010 Google Inc. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.googlecode.gwtquake.tools; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; import java.io.Writer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.Hashtable; import com.googlecode.gwtquake.shared.common.QuakeFileSystem; public class Unpak { public static class pack_t { String filename; RandomAccessFile handle; ByteBuffer backbuffer; int numfiles; Hashtable<String, packfile_t> files; } public static class packfile_t { static final int SIZE = 64; static final int NAME_SIZE = 56; String name; int filepos, filelen; } static class dpackheader_t { int ident; int dirofs; int dirlen; } static { new WALConverter(); new PCXConverter(); new TGAConverter(); new WAVConverter(); } private static final int IDPAKHEADER = (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'); private static final int MAX_FILES_IN_PACK = 4096; private static byte[] tmpText = new byte[packfile_t.NAME_SIZE]; private static File indir, outdir; public static void main(String[] args) throws Throwable { if (args.length != 2) { System.err.println("Usage: Unpak [indir] [outdir]"); System.err .println(" [indir] should be the game's data directory, containing pak0.pak, et al."); System.exit(-1); } indir = new File(args[0]); outdir = new File(args[1]); File imageSizesFile = new File(outdir, "image_sizes.js"); if (outdir.exists() && imageSizesFile.exists()) { System.out.println(outdir + " already exists; no need to unpak"); return; } if (!indir.exists() || !indir.isDirectory()) { System.err.println("Couldn't find directory " + indir); System.exit(-1); } QuakeFileSystem.CreatePath(imageSizesFile.getAbsolutePath()); Writer imageSizes = new FileWriter(imageSizesFile); imageSizes.write("var __imageSizes = {\n"); convertDir(indir, "", imageSizes); imageSizes.write("};\n"); imageSizes.close(); } private static void convertDir(File file, String prefix, Writer imageSizes) throws IOException { for (String child : file.list()) { File childFile = new File(file, child); if (childFile.isDirectory()) { convertDir(childFile, prefix + child + File.separator, imageSizes); } else { // See if it's a .pak file. if (childFile.getName().endsWith(".pak")) { // Unpak it. unpak(childFile.getAbsolutePath(), imageSizes); } else { // Convert the file. RandomAccessFile rafile = new RandomAccessFile(childFile, "r"); FileChannel fc = rafile.getChannel(); convertFile(prefix + childFile.getName(), fc, (int) fc.size(), imageSizes); fc.close(); } } } } private static void convertFile(String filename, FileChannel inChannel, int len, Writer imageSizes) throws IOException { System.out.println(filename); Converter converter = Converter.get(filename); String destName = converter == null ? filename : replaceExtension(filename, converter.getOutExt()); String canonicalPath = new File(outdir, destName).getCanonicalPath(); File outFile = new File(canonicalPath); QuakeFileSystem.CreatePath(outFile.getAbsolutePath()); // outFile.createNewFile(); ByteBuffer buf = ByteBuffer.allocateDirect(len); if (converter != null) { // Convert the file. inChannel.read(buf); buf.flip(); byte[] raw = new byte[len]; buf.get(raw); int[] size = {0, 0}; converter.convert(raw, outFile, size); if (size[0] != 0) { int cut = filename.indexOf("/baseq2/"); String jsName = cut == -1 ? filename : filename.substring(cut+ "/baseq2/".length()); cut = jsName.lastIndexOf('.'); jsName = jsName.substring(0, cut); imageSizes.write("'" + jsName + "':["+size[0] + ","+ size[1] +"],\n"); } } else { // Just copy it directly. FileOutputStream outStream = new FileOutputStream(outFile); FileChannel outChannel = outStream.getChannel(); inChannel.read(buf); buf.flip(); outChannel.write(buf); outStream.close(); } } private static pack_t loadPackFile(String packfile) { dpackheader_t header; Hashtable<String, packfile_t> newfiles; RandomAccessFile file; int numpackfiles = 0; pack_t pack = null; try { file = new RandomAccessFile(packfile, "r"); FileChannel fc = file.getChannel(); ByteBuffer packhandle = fc.map(FileChannel.MapMode.READ_ONLY, 0, file .length()); packhandle.order(ByteOrder.LITTLE_ENDIAN); fc.close(); if (packhandle == null || packhandle.limit() < 1) return null; header = new dpackheader_t(); header.ident = packhandle.getInt(); header.dirofs = packhandle.getInt(); header.dirlen = packhandle.getInt(); if (header.ident != IDPAKHEADER) System.err.println(packfile + " is not a packfile"); numpackfiles = header.dirlen / packfile_t.SIZE; if (numpackfiles > MAX_FILES_IN_PACK) System.err.println(packfile + " has " + numpackfiles + " files"); newfiles = new Hashtable<String, packfile_t>(numpackfiles); packhandle.position(header.dirofs); // parse the directory packfile_t entry = null; for (int i = 0; i < numpackfiles; i++) { packhandle.get(tmpText); entry = new packfile_t(); entry.name = new String(tmpText).trim(); entry.filepos = packhandle.getInt(); entry.filelen = packhandle.getInt(); newfiles.put(entry.name.toLowerCase(), entry); } } catch (IOException e) { System.out.println(e.getMessage() + '\n'); return null; } pack = new pack_t(); pack.filename = new String(packfile); pack.handle = file; pack.numfiles = numpackfiles; pack.files = newfiles; System.out.println("Opened packfile " + packfile + " (" + numpackfiles + " files)\n"); return pack; } private static String replaceExtension(String filename, String outExt) { int idx = filename.lastIndexOf('.'); assert idx != -1; return filename.substring(0, idx) + "." + outExt; } private static void unpak(String pakname, Writer sizes) throws IOException { pack_t pak = loadPackFile(pakname); pak.handle = new RandomAccessFile(pak.filename, "r"); FileChannel inChannel = pak.handle.getChannel(); for (packfile_t entry : pak.files.values()) { pak.handle.seek(entry.filepos); convertFile(entry.name, inChannel, entry.filelen, sizes); } pak.handle.close(); } }