Unpack.java 5.0 KB

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