package org.rsbot.loader;
import org.rsbot.Configuration;
import org.rsbot.loader.asm.ClassReader;
import org.rsbot.loader.script.ModScript;
import org.rsbot.loader.script.ParseException;
import org.rsbot.util.io.HttpClient;
import javax.swing.*;
import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.logging.Logger;
/**
*/
public class ClientLoader {
private final Logger log = Logger.getLogger(ClientLoader.class.getName());
private ModScript script;
private Map<String, byte[]> classes;
private int world = nextWorld();
public void init(final URL script, final File cache) throws IOException, ParseException {
byte[] data = null;
FileInputStream fis = null;
try {
HttpClient.download(script, cache);
} catch (final IOException ioe) {
if (cache.exists()) {
log.warning("Unable to download client patch, attempting to use cached copy");
}
}
try {
fis = new FileInputStream(cache);
data = load(fis);
} catch (final IOException ioe) {
log.severe("Could not load client patch");
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (final IOException ignored) {
}
}
this.script = new ModScript(data);
}
public void load(final File cache, final File version_file) throws IOException {
classes = new HashMap<String, byte[]>();
final int version = script.getVersion();
final String target = script.getAttribute("target");
int cached_version = 0;
if (cache.exists() && version_file.exists()) {
final BufferedReader reader = new BufferedReader(new FileReader(version_file));
cached_version = Integer.parseInt(reader.readLine());
reader.close();
}
if (script.getAttribute("minbotversion") != null) {
int botVersion = Configuration.getVersion();
int minVersion = Integer.parseInt(script.getAttribute("minbotversion"));
if (botVersion < minVersion) {
throw new IOException("Client patch requires newer version (" + botVersion + " < " + minVersion + ")");
}
}
if (version <= cached_version) {
final JarFile jar = new JarFile(cache);
checkVersion(jar.getInputStream(jar.getJarEntry("client.class")));
log.info("Processing client");
final Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
classes.put(name, script.process(name, jar.getInputStream(entry)));
}
}
} else {
log.info("Downloading client: " + target);
final JarFile loader = getJar(target, true);
final JarFile client = getJar(target, false);
final List<String> replace = Arrays.asList(script.getAttribute("replace").split(" "));
Enumeration<JarEntry> entries = client.entries();
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
classes.put(name, load(client.getInputStream(entry)));
}
}
entries = loader.entries();
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
if (replace.contains(name)) {
classes.put(name, load(loader.getInputStream(entry)));
}
}
}
final FileOutputStream stream = new FileOutputStream(cache);
final JarOutputStream out = new JarOutputStream(stream);
for (final Map.Entry<String, byte[]> entry : classes.entrySet()) {
out.putNextEntry(new JarEntry(entry.getKey() + ".class"));
out.write(entry.getValue());
}
out.close();
stream.close();
int client_version = 0;
try {
client_version = checkVersion(new ByteArrayInputStream(classes.get("client")));
} finally {
if (client_version != 0) {
final FileWriter writer = new FileWriter(Configuration.Paths.getVersionCache());
writer.write(Integer.toString(client_version));
writer.close();
}
}
log.info("Processing client");
for (final Map.Entry<String, byte[]> entry : classes.entrySet()) {
entry.setValue(script.process(entry.getKey(), entry.getValue()));
}
}
}
public Map<String, byte[]> getClasses() {
return classes;
}
public String getTargetName() {
return script.getAttribute("target");
}
private int checkVersion(final InputStream in) throws IOException {
final ClassReader reader = new ClassReader(in);
final VersionVisitor vv = new VersionVisitor();
reader.accept(vv, ClassReader.SKIP_FRAMES);
if (vv.getVersion() != script.getVersion()) {
JOptionPane.showMessageDialog(
null,
"The bot is currently oudated, please wait patiently for a new version.",
"Outdated",
JOptionPane.INFORMATION_MESSAGE);
throw new IOException("ModScript #" + script.getVersion() + " != #" + vv.getVersion());
}
return vv.getVersion();
}
private JarFile getJar(final String target, final boolean loader) {
while (true) {
try {
String s = "jar:http://world" + world + "." + target + ".com/";
if (loader) {
s += "loader.jar!/";
} else {
s += target + ".jar!/";
}
final URL url = new URL(s);
final JarURLConnection juc = (JarURLConnection) url.openConnection();
juc.setConnectTimeout(5000);
return juc.getJarFile();
} catch (final Exception ignored) {
world = nextWorld();
}
}
}
private int nextWorld() {
return 1 + new Random().nextInt(169);
}
private byte[] load(final InputStream is) throws IOException {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
final byte[] buffer = new byte[4096];
int n;
while ((n = is.read(buffer)) != -1) {
os.write(buffer, 0, n);
}
return os.toByteArray();
}
}