001/* 002 * Forge Mod Loader 003 * Copyright (c) 2012-2013 cpw. 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser Public License v2.1 006 * which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 008 * 009 * Contributors: 010 * cpw - implementation 011 */ 012 013package cpw.mods.fml.common.asm.transformers; 014 015import java.io.BufferedInputStream; 016import java.io.BufferedOutputStream; 017import java.io.ByteArrayOutputStream; 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.FileNotFoundException; 021import java.io.FileOutputStream; 022import java.io.IOException; 023import java.net.URL; 024import java.util.List; 025import java.util.zip.ZipEntry; 026import java.util.zip.ZipInputStream; 027import java.util.zip.ZipOutputStream; 028 029import org.objectweb.asm.ClassReader; 030import org.objectweb.asm.ClassWriter; 031import org.objectweb.asm.tree.ClassNode; 032 033import com.google.common.base.Charsets; 034import com.google.common.base.Splitter; 035import com.google.common.collect.ArrayListMultimap; 036import com.google.common.collect.Iterables; 037import com.google.common.collect.ListMultimap; 038import com.google.common.collect.Lists; 039import com.google.common.io.LineProcessor; 040import com.google.common.io.Resources; 041 042import cpw.mods.fml.relauncher.IClassTransformer; 043 044public class MarkerTransformer implements IClassTransformer 045{ 046 private ListMultimap<String, String> markers = ArrayListMultimap.create(); 047 048 public MarkerTransformer() throws IOException 049 { 050 this("fml_marker.cfg"); 051 } 052 protected MarkerTransformer(String rulesFile) throws IOException 053 { 054 readMapFile(rulesFile); 055 } 056 057 private void readMapFile(String rulesFile) throws IOException 058 { 059 File file = new File(rulesFile); 060 URL rulesResource; 061 if (file.exists()) 062 { 063 rulesResource = file.toURI().toURL(); 064 } 065 else 066 { 067 rulesResource = Resources.getResource(rulesFile); 068 } 069 Resources.readLines(rulesResource, Charsets.UTF_8, new LineProcessor<Void>() 070 { 071 @Override 072 public Void getResult() 073 { 074 return null; 075 } 076 077 @Override 078 public boolean processLine(String input) throws IOException 079 { 080 String line = Iterables.getFirst(Splitter.on('#').limit(2).split(input), "").trim(); 081 if (line.length()==0) 082 { 083 return true; 084 } 085 List<String> parts = Lists.newArrayList(Splitter.on(" ").trimResults().split(line)); 086 if (parts.size()!=2) 087 { 088 throw new RuntimeException("Invalid config file line "+ input); 089 } 090 List<String> markerInterfaces = Lists.newArrayList(Splitter.on(",").trimResults().split(parts.get(1))); 091 for (String marker : markerInterfaces) 092 { 093 markers.put(parts.get(0), marker); 094 } 095 return true; 096 } 097 }); 098 } 099 100 @SuppressWarnings("unchecked") 101 @Override 102 public byte[] transform(String name, String transformedName, byte[] bytes) 103 { 104 if (bytes == null) { return null; } 105 if (!markers.containsKey(name)) { return bytes; } 106 107 ClassNode classNode = new ClassNode(); 108 ClassReader classReader = new ClassReader(bytes); 109 classReader.accept(classNode, 0); 110 111 for (String marker : markers.get(name)) 112 { 113 classNode.interfaces.add(marker); 114 } 115 116 ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); 117 classNode.accept(writer); 118 return writer.toByteArray(); 119 } 120 121 public static void main(String[] args) 122 { 123 if (args.length < 2) 124 { 125 System.out.println("Usage: MarkerTransformer <JarPath> <MapFile> [MapFile2]... "); 126 return; 127 } 128 129 boolean hasTransformer = false; 130 MarkerTransformer[] trans = new MarkerTransformer[args.length - 1]; 131 for (int x = 1; x < args.length; x++) 132 { 133 try 134 { 135 trans[x - 1] = new MarkerTransformer(args[x]); 136 hasTransformer = true; 137 } 138 catch (IOException e) 139 { 140 System.out.println("Could not read Transformer Map: " + args[x]); 141 e.printStackTrace(); 142 } 143 } 144 145 if (!hasTransformer) 146 { 147 System.out.println("Culd not find a valid transformer to perform"); 148 return; 149 } 150 151 File orig = new File(args[0]); 152 File temp = new File(args[0] + ".ATBack"); 153 if (!orig.exists() && !temp.exists()) 154 { 155 System.out.println("Could not find target jar: " + orig); 156 return; 157 } 158/* 159 if (temp.exists()) 160 { 161 if (orig.exists() && !orig.renameTo(new File(args[0] + (new SimpleDateFormat(".yyyy.MM.dd.HHmmss")).format(new Date())))) 162 { 163 System.out.println("Could not backup existing file: " + orig); 164 return; 165 } 166 if (!temp.renameTo(orig)) 167 { 168 System.out.println("Could not restore backup from previous run: " + temp); 169 return; 170 } 171 } 172*/ 173 if (!orig.renameTo(temp)) 174 { 175 System.out.println("Could not rename file: " + orig + " -> " + temp); 176 return; 177 } 178 179 try 180 { 181 processJar(temp, orig, trans); 182 } 183 catch (IOException e) 184 { 185 e.printStackTrace(); 186 } 187 188 if (!temp.delete()) 189 { 190 System.out.println("Could not delete temp file: " + temp); 191 } 192 } 193 194 private static void processJar(File inFile, File outFile, MarkerTransformer[] transformers) throws IOException 195 { 196 ZipInputStream inJar = null; 197 ZipOutputStream outJar = null; 198 199 try 200 { 201 try 202 { 203 inJar = new ZipInputStream(new BufferedInputStream(new FileInputStream(inFile))); 204 } 205 catch (FileNotFoundException e) 206 { 207 throw new FileNotFoundException("Could not open input file: " + e.getMessage()); 208 } 209 210 try 211 { 212 outJar = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outFile))); 213 } 214 catch (FileNotFoundException e) 215 { 216 throw new FileNotFoundException("Could not open output file: " + e.getMessage()); 217 } 218 219 ZipEntry entry; 220 while ((entry = inJar.getNextEntry()) != null) 221 { 222 if (entry.isDirectory()) 223 { 224 outJar.putNextEntry(entry); 225 continue; 226 } 227 228 byte[] data = new byte[4096]; 229 ByteArrayOutputStream entryBuffer = new ByteArrayOutputStream(); 230 231 int len; 232 do 233 { 234 len = inJar.read(data); 235 if (len > 0) 236 { 237 entryBuffer.write(data, 0, len); 238 } 239 } 240 while (len != -1); 241 242 byte[] entryData = entryBuffer.toByteArray(); 243 244 String entryName = entry.getName(); 245 246 if (entryName.endsWith(".class") && !entryName.startsWith(".")) 247 { 248 ClassNode cls = new ClassNode(); 249 ClassReader rdr = new ClassReader(entryData); 250 rdr.accept(cls, 0); 251 String name = cls.name.replace('/', '.').replace('\\', '.'); 252 253 for (MarkerTransformer trans : transformers) 254 { 255 entryData = trans.transform(name, name, entryData); 256 } 257 } 258 259 ZipEntry newEntry = new ZipEntry(entryName); 260 outJar.putNextEntry(newEntry); 261 outJar.write(entryData); 262 } 263 } 264 finally 265 { 266 if (outJar != null) 267 { 268 try 269 { 270 outJar.close(); 271 } 272 catch (IOException e) 273 { 274 } 275 } 276 277 if (inJar != null) 278 { 279 try 280 { 281 inJar.close(); 282 } 283 catch (IOException e) 284 { 285 } 286 } 287 } 288 } 289}