1 /* 2 =========================================================================== 3 Copyright (C) 1997-2006 Id Software, Inc. 4 5 This file is part of Quake 2 Tools source code. 6 7 Quake 2 Tools source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake 2 Tools source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Quake 2 Tools source code; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 23 /* 24 * Unpack -- a completely non-object oriented utility... 25 * 26 */ 27 28 import java.io.*; 29 30 class Unpack { 31 static final int IDPAKHEADER = (('K'<<24)+('C'<<16)+('A'<<8)+'P'); 32 intSwap(int i)33 static int intSwap(int i) { 34 int a, b, c, d; 35 36 a = i & 255; 37 b = (i >> 8) & 255; 38 c = (i >> 16) & 255; 39 d = (i >> 24) & 255; 40 41 return (a << 24) + (b << 16) + (c << 8) + d; 42 } 43 patternMatch(String pattern, String s)44 static boolean patternMatch (String pattern, String s) { 45 int index; 46 int remaining; 47 48 if (pattern.equals(s)) { 49 return true; 50 } 51 52 // fairly lame single wildcard matching 53 index = pattern.indexOf('*'); 54 if (index == -1) { 55 return false; 56 } 57 if (!pattern.regionMatches(0, s, 0, index)) { 58 return false; 59 } 60 61 index += 1; // skip the * 62 remaining = pattern.length() - index; 63 if (s.length() < remaining) { 64 return false; 65 } 66 67 if (!pattern.regionMatches(index, s, s.length()-remaining, remaining)) { 68 return false; 69 } 70 71 return true; 72 } 73 usage()74 static void usage() { 75 System.out.println ("Usage: unpack <packfile> <match> <basedir>"); 76 System.out.println (" or: unpack -list <packfile>"); 77 System.out.println ("<match> may contain a single * wildcard"); 78 System.exit (1); 79 } 80 main(String[] args)81 public static void main (String[] args) { 82 int ident; 83 int dirofs; 84 int dirlen; 85 int i; 86 int numLumps; 87 byte[] name = new byte[56]; 88 String nameString; 89 int filepos; 90 int filelen; 91 RandomAccessFile readLump; 92 DataInputStream directory; 93 String pakName; 94 String pattern; 95 96 if (args.length == 2) { 97 if (!args[0].equals("-list")) { 98 usage(); 99 } 100 pakName = args[1]; 101 pattern = null; 102 } else if (args.length == 3) { 103 pakName = args[0]; 104 pattern = args[1]; 105 } else { 106 pakName = null; 107 pattern = null; 108 usage (); 109 } 110 111 try { 112 // one stream to read the directory 113 directory = new DataInputStream(new FileInputStream(pakName)); 114 115 // another to read lumps 116 readLump = new RandomAccessFile(pakName, "r"); 117 118 // read the header 119 ident = intSwap(directory.readInt()); 120 dirofs = intSwap(directory.readInt()); 121 dirlen = intSwap(directory.readInt()); 122 123 if (ident != IDPAKHEADER) { 124 System.out.println ( pakName + " is not a pakfile."); 125 System.exit (1); 126 } 127 128 // read the directory 129 directory.skipBytes (dirofs - 12); 130 numLumps = dirlen / 64; 131 132 System.out.println (numLumps + " lumps in " + pakName); 133 134 for (i = 0 ; i < numLumps ; i++) { 135 directory.readFully(name); 136 filepos = intSwap(directory.readInt()); 137 filelen = intSwap(directory.readInt()); 138 139 nameString = new String (name, 0); 140 // chop to the first 0 byte 141 nameString = nameString.substring (0, nameString.indexOf(0)); 142 143 if (pattern == null) { 144 // listing mode 145 System.out.println (nameString + " : " + filelen + "bytes"); 146 } else if (patternMatch (pattern, nameString) ) { 147 File writeFile; 148 DataOutputStream writeLump; 149 byte[] buffer = new byte[filelen]; 150 StringBuffer fixedString; 151 String finalName; 152 int index; 153 154 System.out.println ("Unpaking " + nameString + " " + filelen 155 + " bytes"); 156 157 // load the lump 158 readLump.seek(filepos); 159 readLump.readFully(buffer); 160 161 // quake uses forward slashes, but java requires 162 // they only by the host's seperator, which 163 // varies from win to unix 164 fixedString = new StringBuffer (args[2] + File.separator + nameString); 165 for (index = 0 ; index < fixedString.length() ; index++) { 166 if (fixedString.charAt(index) == '/') { 167 fixedString.setCharAt(index, File.separatorChar); 168 } 169 } 170 finalName = fixedString.toString (); 171 172 index = finalName.lastIndexOf(File.separatorChar); 173 if (index != -1) { 174 String finalPath; 175 File writePath; 176 177 finalPath = finalName.substring(0, index); 178 writePath = new File (finalPath); 179 writePath.mkdirs(); 180 } 181 182 writeFile = new File (finalName); 183 writeLump = new DataOutputStream ( new FileOutputStream(writeFile) ); 184 writeLump.write(buffer); 185 writeLump.close(); 186 187 } 188 } 189 190 readLump.close(); 191 directory.close(); 192 193 } catch (IOException e) { 194 System.out.println ( e.toString() ); 195 } 196 } 197 198 } 199