/* * SK's Minecraft Launcher * Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors * Please see LICENSE.txt for license information. */ package com.skcraft.launcher.creator.util; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.io.CharStreams; import com.google.common.io.Closer; import lombok.Data; import lombok.extern.java.Log; import java.io.*; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * Reads the mod information file from a mod .jar, with support for both Forge * and LiteLoader. */ @Log public class ModInfoReader { private static final String FORGE_INFO_FILENAME = "mcmod.info"; private static final String LITELOADER_INFO_FILENAME = "litemod.json"; private final ObjectMapper mapper = new ObjectMapper(); /** * Detect the mods listed in the given .jar * * @param file The file * @return A list of detected mods */ public List<? extends ModInfo> detectMods(File file) { Closer closer = Closer.create(); try { FileInputStream fis = closer.register(new FileInputStream(file)); BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); ZipInputStream zis = closer.register(new ZipInputStream(bis)); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (entry.getName().equalsIgnoreCase(FORGE_INFO_FILENAME)) { List<ForgeModInfo> mods; String content = CharStreams.toString(new InputStreamReader(zis, Charsets.UTF_8)); try { mods = mapper.readValue(content, ForgeModManifest.class).getMods(); } catch (JsonMappingException | JsonParseException e) { mods = mapper.readValue(content, new TypeReference<List<ForgeModInfo>>() {}); } if (mods != null) { // Ignore "examplemod" return Collections.unmodifiableList( mods.stream() .filter(info -> !info.getModId().equals("examplemod")) .collect(Collectors.toList())); } else { return Collections.emptyList(); } } else if (entry.getName().equalsIgnoreCase(LITELOADER_INFO_FILENAME)) { String content = CharStreams.toString(new InputStreamReader(zis, Charsets.UTF_8)); return new ImmutableList.Builder<ModInfo>().add(mapper.readValue(content, LiteLoaderModInfo.class)).build(); } } return Collections.emptyList(); } catch (JsonMappingException e) { log.log(Level.WARNING, "Unknown format " + FORGE_INFO_FILENAME + " file in " + file.getAbsolutePath(), e); return Collections.emptyList(); } catch (JsonParseException e) { log.log(Level.WARNING, "Corrupt " + FORGE_INFO_FILENAME + " file in " + file.getAbsolutePath(), e); return Collections.emptyList(); } catch (IOException e) { log.log(Level.WARNING, "Failed to read " + file.getAbsolutePath(), e); return Collections.emptyList(); } finally { try { closer.close(); } catch (IOException ignored) { } } } public interface ModInfo { String getModId(); String getName(); String getDescription(); String getVersion(); String getGameVersion(); String getUrl(); } @Data @JsonIgnoreProperties(ignoreUnknown = true) private static class ForgeModInfo implements ModInfo { @JsonProperty("modid") private String modId; private String name; private String description; private String version; @JsonProperty("mcversion") private String gameVersion; private String url; private String updateUrl; private List<String> authorList; private String credits; private List<String> dependencies; } @Data @JsonIgnoreProperties(ignoreUnknown = true) private static class ForgeModManifest { @JsonProperty("modListVersion") private int version; @JsonProperty("modList") private List<ForgeModInfo> mods; } @Data @JsonIgnoreProperties(ignoreUnknown = true) private static class LiteLoaderModInfo implements ModInfo { private String name; private String version; @JsonProperty("mcversion") private String gameVersion; private String revision; private String author; private String description; @JsonIgnore @Override public String getModId() { return null; } @Override public String getUrl() { return null; } } }