-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7de45d7
Showing
7 changed files
with
499 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# MinecraftSkinFixer | ||
|
||
MinecraftSkinFixer is a simple program which allows old versions of Minecraft to download Skins from the new Skin servers. | ||
~~The [Minecraft Forum Thread](https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-tools/2923190-minecraftskinfixer-skins-in-old-minecraft-versions) contains more information.~~ | ||
*The Minecraft Forum Thread dissappeared around December 2018/January 2019, together with my Minecraft Forum Account.* | ||
|
||
**NOTE: Versions 2.2 and 2.3 were removed.** | ||
This is because the mods for 1.6.2, 1.6.4 and 1.7.2 which shipped with them did not comply with Minecraft's EULA. | ||
I hope to eventually release a version 2.5 with new, compliant mods. | ||
*And in case you are wondering: 1.x, 2.0 and 2.1 were never released in the first place.* | ||
|
||
--- | ||
|
||
### Supported Minecraft Versions | ||
|
||
All Minecraft versions since the introduction of custom skins up to release 1.5.2 are supported. | ||
However, if you are using a version between release 1.0 and release 1.2.5, you need to supply the `--flip-bottoms=off` parameter when launching the program to make the skins render correctly. | ||
|
||
--- | ||
|
||
### Usage instructions: | ||
1. Download the MinecraftSkinFixer .jar file from the releases tab | ||
2. Start the Minecraft launcher | ||
3. Start MinecraftSkinFixer | ||
4. If this is your first time using MinecraftSkinFixer, configure your launcher profile as instructed by the program | ||
5. Launch Minecraft | ||
|
||
--- | ||
|
||
The main program has two dependencies: | ||
* [Non-standard JRE HTTP server](https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver/spec/index.html) (included in most JREs) | ||
* [org.json:json:20150729](https://search.maven.org/artifact/org.json/json/20150729/jar) (included in the release jars) | ||
|
||
To be able to use it, you need to have Java 8 or later installed on your computer. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package minecraftskinfixer; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.OutputStream; | ||
import java.net.InetSocketAddress; | ||
import java.net.URL; | ||
import java.net.URLConnection; | ||
|
||
import com.sun.net.httpserver.Headers; | ||
import com.sun.net.httpserver.HttpExchange; | ||
import com.sun.net.httpserver.HttpHandler; | ||
import com.sun.net.httpserver.HttpServer; | ||
|
||
public class HTTPProxyServer implements HttpHandler { | ||
|
||
private static SuperSimpleLogger logger; | ||
private static int connectionIdCounter = 0; | ||
|
||
public static void start(int port, SuperSimpleLogger logger) { | ||
HTTPProxyServer.logger = logger; | ||
HttpServer server = null; | ||
try { | ||
server = HttpServer.create(new InetSocketAddress("127.0.0.1", port), 0); | ||
} catch (IOException e) { | ||
logger.log("[HTTP Proxy] Unable to create HTTP Server: " + e.getClass().getName() + ": " + e.getMessage()); | ||
return; | ||
} | ||
server.createContext("/", new HTTPProxyServer()); | ||
server.start(); | ||
logger.log("[HTTP Proxy] HTTP Server successfully started on port " + port); | ||
} | ||
|
||
@Override | ||
public void handle(HttpExchange he) throws IOException { | ||
String connectionId = "[HTTP #" + connectionIdCounter++ + "] "; | ||
Headers requestHeaders = he.getRequestHeaders(); | ||
String requestedHost = requestHeaders.get("Host").get(0); | ||
String requestedUrl = he.getRequestURI().toString(); | ||
if(requestedUrl.contains(requestedHost)) { | ||
requestedUrl = requestedUrl.substring(requestedUrl.indexOf(requestedHost) + requestedHost.length()); | ||
} | ||
logger.log("[HTTP Proxy] " + connectionId + "Received a HTTP " + he.getRequestMethod() + " request for " + requestedHost + requestedUrl); | ||
if(he.getRequestMethod().equals("GET") && ( | ||
requestedHost.equals("www.minecraft.net") && requestedUrl.startsWith("/skin/") || | ||
requestedHost.equals("s3.amazonaws.com") && requestedUrl.startsWith("/MinecraftSkins/") || | ||
requestedHost.equals("s3.amazonaws.com") && requestedUrl.startsWith("/MinecraftCloaks/") || | ||
requestedHost.equals("skins.minecraft.net") && requestedUrl.startsWith("/MinecraftSkins/") || | ||
requestedHost.equals("skins.minecraft.net") && requestedUrl.startsWith("/MinecraftCloaks/"))) { | ||
logger.log("[HTTP Proxy] " + connectionId + "Identified request as Minecraft skin request - forwarding to Minecraft skin request handler."); | ||
SkinFixer.runSkinFixer(connectionId, he, requestedHost, requestedUrl); | ||
} else if(he.getRequestMethod().equals("GET")) { | ||
logger.log("[HTTP Proxy] " + connectionId + "Request does not look like a Minecraft skin request - being a really bad but at least possibly kind of working proxy server."); | ||
URL url = new URL("http://" + requestedHost + requestedUrl); | ||
URLConnection conn = url.openConnection(); | ||
he.sendResponseHeaders(200, conn.getContentLengthLong()); | ||
InputStream is = conn.getInputStream(); | ||
OutputStream os = he.getResponseBody(); | ||
byte[] buff = new byte[4096]; | ||
int read; | ||
while((read = is.read(buff)) != -1) { | ||
os.write(buff, 0, read); | ||
} | ||
he.close(); | ||
} else { | ||
logger.log("[HTTP Proxy] " + connectionId + "Request is not a GET request and therefore not supported by this program. Responding with 500 Internal Server Error."); | ||
he.sendResponseHeaders(500, 0); | ||
he.close(); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package minecraftskinfixer; | ||
|
||
import java.awt.BorderLayout; | ||
import java.awt.Font; | ||
import java.util.Calendar; | ||
|
||
import javax.swing.JFrame; | ||
import javax.swing.JScrollPane; | ||
import javax.swing.JTextArea; | ||
|
||
@SuppressWarnings("serial") | ||
public class LogWindow extends JFrame implements SuperSimpleLogger { | ||
|
||
private JTextArea text; | ||
private JScrollPane scroll; | ||
|
||
public LogWindow() { | ||
super("Minecraft Skin Fixer " + SkinFixer2Main.VERSION); | ||
text = new JTextArea(); | ||
text.setFont(new Font("Monospaced", 0, 12)); | ||
text.setEditable(false); | ||
scroll = new JScrollPane(text, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); | ||
setLayout(new BorderLayout()); | ||
add(scroll, BorderLayout.CENTER); | ||
setSize(600, 400); | ||
setDefaultCloseOperation(EXIT_ON_CLOSE); | ||
setVisible(true); | ||
revalidate(); | ||
repaint(); | ||
} | ||
|
||
@Override | ||
public void log(String s) { | ||
Calendar c = Calendar.getInstance(); | ||
String timestamp = String.format("[%d-%02d-%02d %02d:%02d:%02d.%03d] ", | ||
c.get(Calendar.YEAR), | ||
c.get(Calendar.MONTH) + 1, | ||
c.get(Calendar.DAY_OF_MONTH), | ||
c.get(Calendar.HOUR_OF_DAY), | ||
c.get(Calendar.MINUTE), | ||
c.get(Calendar.SECOND), | ||
c.get(Calendar.MILLISECOND)); | ||
System.out.println(timestamp + s); | ||
if(text.getText().length() == 0) { | ||
text.setText(timestamp + s); | ||
} else { | ||
text.setText(text.getText() + '\n' + timestamp + s); | ||
} | ||
scroll.getVerticalScrollBar().setValue(scroll.getVerticalScrollBar().getMaximum()); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package minecraftskinfixer; | ||
|
||
import java.awt.Graphics2D; | ||
import java.awt.image.BufferedImage; | ||
|
||
public class SkinConverter { | ||
|
||
public static BufferedImage convert(BufferedImage original, boolean isSlim, boolean flipBottoms, SuperSimpleLogger logger, String connectionId) { | ||
if(original.getHeight() <= 32 && !isSlim && !flipBottoms) { | ||
logger.log("[Skin Converter] " + connectionId + "Skin is compatible - not converting."); | ||
return original; | ||
} | ||
BufferedImage converted = new BufferedImage(64, 32, BufferedImage.TYPE_INT_ARGB); | ||
Graphics2D g = converted.createGraphics(); | ||
if(isSlim) { | ||
logger.log("[Skin Converter] " + connectionId + "Converting from slim to normal model"); | ||
//head and second layer of head | ||
g.drawImage(original.getSubimage(0, 0, 64, 16), 0, 0, null); | ||
//legs, body and | ||
//arm: right, front pt. 1 and top pt. 1 | ||
g.drawImage(original.getSubimage(0, 16, 46, 16), 0, 16, null); | ||
//arm: front pt. 2 and top pt. 2 | ||
g.drawImage(original.getSubimage(45, 16, 2, 16), 46, 16, null); | ||
//arm: left, bottom pt. 1 and back pt. 1 | ||
g.drawImage(original.getSubimage(47, 16, 6, 16), 48, 16, null); | ||
//arm: bottom pt. 2 | ||
g.drawImage(original.getSubimage(48, 16, 2, 4), 50, 16, null); | ||
//arm: back pt. 2 | ||
g.drawImage(original.getSubimage(52, 20, 2, 12), 54, 20, null); | ||
} else { | ||
g.drawImage(original.getSubimage(0, 0, 64, 32), 0, 0, null); | ||
} | ||
if(original.getHeight() >= 64) { | ||
logger.log("[Skin Converter] " + connectionId + "Merging second layer from 1.8+ skin onto first layer"); | ||
if(isSlim) { | ||
//legs, body and | ||
//arm: right, front pt. 1 and top pt. 1 | ||
g.drawImage(original.getSubimage(0, 32, 46, 16), 0, 16, null); | ||
//arm: front pt. 2 and top pt. 2 | ||
g.drawImage(original.getSubimage(45, 32, 2, 16), 46, 16, null); | ||
//arm: left, bottom pt. 1 and back pt. 1 | ||
g.drawImage(original.getSubimage(47, 32, 6, 16), 48, 16, null); | ||
//arm: bottom pt. 2 | ||
g.drawImage(original.getSubimage(48, 32, 2, 4), 50, 16, null); | ||
//arm: back pt. 2 | ||
g.drawImage(original.getSubimage(52, 36, 2, 12), 54, 20, null); | ||
} else { | ||
g.drawImage(original.getSubimage(0, 32, 56, 16), 0, 16, null); | ||
} | ||
} | ||
if(flipBottoms) { | ||
logger.log("[Skin Converter]" + connectionId + "Flipping bottom pieces"); | ||
BufferedImage buffer = converted; | ||
converted = new BufferedImage(64, 32, BufferedImage.TYPE_INT_ARGB); | ||
g = converted.createGraphics(); | ||
//head top | ||
g.drawImage(buffer.getSubimage(8, 0, 8, 8), 8, 0, null); | ||
//head2 top | ||
g.drawImage(buffer.getSubimage(40, 0, 8, 8), 40, 0, null); | ||
//head and head2 right, front, left and back | ||
g.drawImage(buffer.getSubimage(0, 8, 64, 8), 0, 8, null); | ||
//legs top | ||
g.drawImage(buffer.getSubimage(4, 16, 4, 4), 4, 16, null); | ||
//body top | ||
g.drawImage(buffer.getSubimage(20, 16, 8, 4), 20, 16, null); | ||
//arms top | ||
g.drawImage(buffer.getSubimage(44, 16, 4, 4), 44, 16, null); | ||
//legs, body and arms right, front, left and back | ||
g.drawImage(buffer.getSubimage(0, 20, 56, 12), 0, 20, null); | ||
for(int i = 0; i < 8; i++) { | ||
//head bottom | ||
g.drawImage(buffer.getSubimage(16, i, 8, 1), 16, 7 - i, null); | ||
//head2 bottom | ||
g.drawImage(buffer.getSubimage(48, i, 8, 1), 48, 7 - i, null); | ||
} | ||
for(int i = 0; i < 4; i++) { | ||
//legs bottom | ||
g.drawImage(buffer.getSubimage(8, 16 + i, 4, 1), 8, 16 + 3 - i, null); | ||
//body bottom | ||
g.drawImage(buffer.getSubimage(28, 16 + i, 8, 1), 28, 16 + 3 - i, null); | ||
//arms bottom | ||
g.drawImage(buffer.getSubimage(48, 16 + i, 4, 1), 48, 16 + 3 - i, null); | ||
} | ||
} | ||
return converted; | ||
} | ||
|
||
} |
Oops, something went wrong.