package mods.mud.gui; import java.awt.Desktop; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.xml.bind.DatatypeConverter; import mods.mud.ModUpdateDetector; import mods.mud.UpdateChecker; import mods.mud.UpdateEntry; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.util.StatCollector; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; public class GuiChangelogDownload extends GuiScreen { private GuiSlotModList modList; private int selected = -1; private UpdateEntry selectedMod; private ArrayList<UpdateEntry> entries; private String[] changelog; int lineStart = 0; private GuiButton disable; private GuiButton download; private GuiButton close1; private GuiButton ok; private GuiButton urlButton; private boolean isDownloading = false; private boolean downloadComplete = false; private boolean downloadFailed = false; private float downloadPercent; private String message; private GuiScreen parent; private char[] bullets = new char[]{0x2219, 0x25E6, 0x2023}; private int[] bulletWidth = new int[3]; private Thread getChangeLogThread; public GuiChangelogDownload(GuiScreen parent){ this.parent = parent; changelog = new String[]{StatCollector.translateToLocal("log.message.loading")}; if(!ModUpdateDetector.hasChecked){ new UpdateChecker(ModUpdateDetector.getAllUpdateEntries()).run(); } this.entries=new ArrayList<UpdateEntry>(ModUpdateDetector.getAllUpdateEntries()); } public GuiChangelogDownload(){ this(null); } @Override public void initGui() { this.buttonList.clear(); this.modList=new GuiSlotModList(this, entries, 100); this.modList.registerScrollButtons(this.buttonList, 1, 2); for(int i= 0; i < bullets.length; i++){ bulletWidth[i] = fontRendererObj.getStringWidth(bullets[i]+" "); } disable = new GuiButton(3, 15, 10, 125, 20, StatCollector.translateToLocal("mud.disable")+": "+Boolean.toString(!ModUpdateDetector.enabled)); download = new GuiButton(4, 15, height-35, 125, 20, StatCollector.translateToLocal("button.download.latest")); download.enabled = false; close1 = new GuiButton(5, width-140, height-35, 125, 20, StatCollector.translateToLocal("gui.done")); ok = new GuiButton(6, (width - 200)/2 + 5, (height - 150)/2+115, 190, 20, StatCollector.translateToLocal("button.ok")); urlButton = new GuiButton(7, (width - 125)/2, height-35, 125, 20, StatCollector.translateToLocal("button.url")); urlButton.enabled = false; ok.visible = isDownloading; ok.enabled = downloadComplete || downloadFailed; buttonList.add(disable); buttonList.add(download); buttonList.add(close1); buttonList.add(ok); buttonList.add(urlButton); } @Override protected void keyTyped(char par1, int par2) { if(!isDownloading){ if (par2 == 1) { this.mc.displayGuiScreen(parent); this.mc.setIngameFocus(); } } } @Override protected void actionPerformed(GuiButton button) { if (button.enabled) { if(!isDownloading || (downloadFailed || downloadComplete) ){ switch (button.id) { case 3: isDownloading = false; close1.enabled = true; ModUpdateDetector.toggleState(); disable.displayString = StatCollector.translateToLocal("mud.disable")+":"+Boolean.toString(!ModUpdateDetector.enabled); return; case 4: ModContainer mc = selectedMod.getMc(); String filename = String.format("[%s] %s - %s.jar", Loader.instance().getMCVersionString().replaceAll("Minecraft", "").trim(), mc.getName(), selectedMod.getLatest().getVersionString()); File newFile = new File(mc.getSource().getParent(), filename); Thread t = new Thread(new Downloader(selectedMod.getLatest().download, newFile, mc.getSource(), selectedMod.getLatest().md5 )); t.start(); isDownloading = true; ok.visible = true; close1.enabled = false; download.enabled = false; urlButton.enabled = false; return; case 5: this.mc.displayGuiScreen(parent); return; case 6: isDownloading = false; downloadComplete = false; downloadFailed = false; ok.visible = false; close1.enabled = true; urlButton.enabled=true; return; case 7: if(selectedMod != null && selectedMod.getLatest() != null && selectedMod.getLatest().url != null){ try { Desktop.getDesktop().browse(new URI(selectedMod.getLatest().url)); } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. }catch (URISyntaxException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } return; } } } super.actionPerformed(button); } @Override public void drawDefaultBackground() { super.drawBackground(0); } @Override public void drawScreen(int p_571_1_, int p_571_2_, float p_571_3_) { if(modList != null) this.modList.drawScreen(p_571_1_, p_571_2_, p_571_3_); if(selectedMod != null){ int start = 32; for(int i = lineStart; i < changelog.length && start < height-52; i++){ start = drawText(changelog[i], start); } } int mouse_x = Mouse.getEventX() * this.width / this.mc.displayWidth; int mouse_y = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; if(mouse_x > 125 && mouse_y > 35 && mouse_y < height-50){ int scroll = Mouse.getDWheel(); if(scroll < 0 && lineStart < changelog.length-15){ lineStart++; } if(scroll > 0 && lineStart > 0){ lineStart--; } } if(isDownloading){ ok.visible = true; int x = (width - 200)/2; int y = (height - 150)/2; drawRect(x-1,y-1,x+201, y+151, 0xFFFFFFFF); drawRect(x,y,x+200, y+150, 0xFF000000); drawCenteredString(fontRendererObj, StatCollector.translateToLocal("gui.downloading"), width/2, y + 15, 0xFFFFFF00); drawRect(x + 24, y + 39, x+176, y+56, 0xFFFFFFFF); drawRect(x + 25, y + 40, (x+25 + 150), y+55, 0xFF000000); drawRect(x + 25, y + 40, (int)(x+25 + 150*downloadPercent), y+55, 0xFFc0c0c0); drawVerticalLine((int)(x+25 + 150*downloadPercent)-1, y + 39, y+55, 0xFF808080); drawHorizontalLine(x + 25, (int)(x+25 + 150*downloadPercent)-1, y + 54, 0xFF808080); if(downloadComplete){ drawCenteredString(fontRendererObj, StatCollector.translateToLocal("gui.download.complete"), width/2, y + 70, 0xFF44FF44); } if(downloadFailed){ drawCenteredString(fontRendererObj, StatCollector.translateToLocal("gui.download.failed"), width/2, y + 70, 0xFFFF0000); } if(message != null){ drawCenteredString(fontRendererObj, message, width/2, y + 85, 0xFFFFFFFF); } ok.drawButton(mc, p_571_1_, p_571_2_); } super.drawScreen(p_571_1_, p_571_2_, p_571_3_); } private int drawText(String line, int start) { int startX = 125; if(line == null){ return 5; } else if(line.startsWith("==") && line.substring(2).contains("==")){ String main = line.substring(2, line.lastIndexOf("==")).trim(); float scale = 1F; GL11.glScalef(1/scale, 1/scale, 1/scale); this.drawString(fontRendererObj, main.replaceAll("=", "").trim(), (int)((startX)*scale), (int)(start*scale), 0xFFFFFF00); GL11.glScalef(scale, scale, scale); if(line.lastIndexOf("==")+2 <= line.length()){ String sub = line.substring(line.lastIndexOf("==")+2, line.length()).trim(); GL11.glScalef(1/scale, 1/scale, 1/scale); this.drawString(fontRendererObj, sub.replaceAll("=", "").trim(), (int)((startX+fontRendererObj.getStringWidth(main+" "))*scale), (int)(start*scale), 0xFF2222FF); GL11.glScalef(scale, scale, scale); } return (int)(1F/scale * 10)+start; }else if(line.trim().startsWith("**") && line.trim().endsWith("**")){ float scale = 1.1F; GL11.glScalef(1/scale, 1/scale, 1/scale); this.drawString(fontRendererObj, line.replaceAll("\\*\\*", "").trim(), (int)((startX)*scale), (int)(start*scale), 0xFFFFFFFF); GL11.glScalef(scale, scale, scale); return (int)(1F/scale * 10)+start; }else{ float scale = 1.2F; GL11.glScalef(1/scale, 1/scale, 1/scale); int bullet = -1; while(line.startsWith("*")){ startX+=10; line = line.substring(1).trim(); bullet++; } if(bullet > 3){ bullet = 3; } List<String> lineList = fontRendererObj.listFormattedStringToWidth(line, width - 10 - startX); Iterator<String> it = lineList.iterator(); for(int i = 0; it.hasNext(); i++){ String subline = it.next().trim(); if(i == 0 && bullet > -1){ subline = bullets[bullet]+" "+subline; }else if (bullet > -1){ startX+=bulletWidth[bullet]; } this.drawString(fontRendererObj, subline, (int) ((startX) * scale), (int) (start * scale), 0xFFFFFFFF); start += (int)(1F/scale * 10); } GL11.glScalef(scale, scale, scale); return start; } } Minecraft getMinecraftInstance() { return mc; } FontRenderer getFontRenderer() { return fontRendererObj; } /** * @param var1 */ public void selectModIndex(int var1) { if(getChangeLogThread == null || !getChangeLogThread.isAlive()){ this.selected=var1; if (var1>=0 && var1<=entries.size()) { this.selectedMod=entries.get(selected); } else { this.selectedMod=null; } urlButton.enabled = selectedMod!=null && selectedMod.getLatest()!=null && selectedMod.getLatest().url!=null; if(selectedMod!=null) { if (selectedMod.getChangelogURL() == null) { changelog = new String[]{StatCollector.translateToLocal("log.message.none")}; } else { getChangeLogThread = new Thread(new ChangelogLoader(selectedMod.getChangelogURL())); getChangeLogThread.start(); changelog = new String[]{StatCollector.translateToLocal("log.message.loading")}; } try { if (!selectedMod.isUpToDate()) { download.enabled = true; download.displayString = StatCollector.translateToLocal("button.download.latest"); } } catch (Exception e) { } } } } public boolean modIndexSelected(int var1) { return var1==selected; } private class ChangelogLoader implements Runnable{ URL changelogURL; private ChangelogLoader(URL changelogURL) { this.changelogURL = changelogURL; } @Override public void run() { try { BufferedReader br = new BufferedReader(new InputStreamReader(changelogURL.openStream())); String line = br.readLine(); ArrayList<String> lines = new ArrayList<String>(100); if(line != null) lines.add(line); while(line != null){ line = br.readLine(); lines.add(line); } changelog = lines.toArray(new String[0]); } catch (IOException e) { e.printStackTrace(); changelog = new String[]{StatCollector.translateToLocal("log.message.fail")}; } } } private class Downloader implements Runnable{ private String downloadUrl = ""; private File file = null; private File orginial; private byte[] expectedMd5; public Downloader(String url, File location, File originalFile, String md5){ this.downloadUrl = url; this.file = location; this.orginial = originalFile; if(md5 != null){ try{ this.expectedMd5 = DatatypeConverter.parseHexBinary(md5.toUpperCase()); }catch (Exception e){ e.printStackTrace(); this.expectedMd5 = null; } } } @Override public void run() { try { isDownloading = true; if(file.exists()) file.delete(); file.createNewFile(); URL url=new URL(downloadUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); int filesize = connection.getContentLength(); float totalDataRead=0; java.io.BufferedInputStream in = new java.io.BufferedInputStream(connection.getInputStream()); java.io.FileOutputStream fos = new java.io.FileOutputStream(file); java.io.BufferedOutputStream bout = new BufferedOutputStream(fos,1024); byte[] data = new byte[1024]; int i; while((i=in.read(data,0,1024))>=0) { totalDataRead=totalDataRead+i; bout.write(data,0,i); downloadPercent=(totalDataRead)/filesize; } bout.close(); in.close(); if(expectedMd5 != null){ DataInputStream dis = null; try { MessageDigest md = MessageDigest.getInstance("MD5"); byte [] fileData = new byte[(int)file.length()]; dis = new DataInputStream((new FileInputStream(file))); dis.readFully(fileData); dis.close(); byte[] md5 = md.digest(fileData); ModUpdateDetector.logger.trace("Expected MD5: "+bytArrayToHex(expectedMd5)); ModUpdateDetector.logger.trace("File MD5: "+bytArrayToHex(md5)); if(Arrays.equals(md5, expectedMd5)){ downloadComplete = true; ok.enabled = true; message = StatCollector.translateToLocal("gui.restart"); }else{ downloadComplete = false; downloadFailed = true; ok.enabled = true; message = StatCollector.translateToLocal("gui.md5.fail"); //file.delete(); } } catch (Exception e) { e.printStackTrace(); if(dis != null){ try{ dis.close(); }catch (Exception ex){} } downloadFailed = true; ok.enabled = true; } }else{ downloadComplete = true; ok.enabled = true; message = StatCollector.translateToLocal("gui.restart"); } if(downloadComplete && orginial.exists() && !orginial.getName().equals(file.getName()) && !orginial.isDirectory() ){ ModUpdateDetector.logger.trace("Deleting: "+orginial.getAbsolutePath()); if(!orginial.delete()){ ModUpdateDetector.logger.trace("Deleting failed, spawning new process to delete"); String cmd = "java -classpath \""+file.getAbsolutePath()+"\" mods.mud.utils.FileDeleter \""+orginial.getAbsolutePath()+"\""; Runtime.getRuntime().exec(cmd); } } } catch (IOException e) { e.printStackTrace(); message = StatCollector.translateToLocal(e.getLocalizedMessage()); downloadFailed = true; ok.enabled = true; } } } String bytArrayToHex(byte[] a) { StringBuilder sb = new StringBuilder(); for(byte b: a) sb.append(String.format("%02x", b&0xff)); return sb.toString(); } }