mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-06-09 00:33:00 +00:00
feat: replace Minecraft logo with LCRE logo at 80% scale
Replace MenuTitle and MenuTitleSmall bitmaps in skinHDWin.swf and skinWin.swf with the custom LCRE (Legacy Console Edition Revelations) logo, scaled to 80% within the original bitmap canvas to avoid occlusion by the load/join menu. Add ReplaceLogo.java and ExtractFromArc.java tools for SWF bitmap replacement and arc file extraction. Keep original arc as .bak.
This commit is contained in:
parent
b8cfd62cdf
commit
4a8291f6cc
Binary file not shown.
BIN
Minecraft.Client/Common/Media/MediaWindows64.arc.bak
Normal file
BIN
Minecraft.Client/Common/Media/MediaWindows64.arc.bak
Normal file
Binary file not shown.
42
tools/ExtractFromArc.java
Normal file
42
tools/ExtractFromArc.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
|
||||
public class ExtractFromArc {
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 3) {
|
||||
System.out.println("Usage: ExtractFromArc <arc_file> <filename> <output_path>");
|
||||
System.exit(1);
|
||||
}
|
||||
String arcPath = args[0];
|
||||
String target = args[1];
|
||||
String outPath = args[2];
|
||||
|
||||
DataInputStream dis = new DataInputStream(new FileInputStream(arcPath));
|
||||
int numFiles = dis.readInt();
|
||||
int foundOffset = -1, foundSize = -1;
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
String name = dis.readUTF();
|
||||
int offset = dis.readInt();
|
||||
int size = dis.readInt();
|
||||
if (name.startsWith("*")) name = name.substring(1);
|
||||
if (name.equals(target)) {
|
||||
foundOffset = offset;
|
||||
foundSize = size;
|
||||
}
|
||||
}
|
||||
dis.close();
|
||||
|
||||
if (foundOffset == -1) {
|
||||
System.err.println("File not found in archive: " + target);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
RandomAccessFile raf = new RandomAccessFile(arcPath, "r");
|
||||
raf.seek(foundOffset);
|
||||
byte[] data = new byte[foundSize];
|
||||
raf.readFully(data);
|
||||
raf.close();
|
||||
Files.write(Paths.get(outPath), data);
|
||||
System.out.println("Extracted " + target + " (" + foundSize + " bytes) -> " + outPath);
|
||||
}
|
||||
}
|
||||
124
tools/ReplaceLogo.java
Normal file
124
tools/ReplaceLogo.java
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.tags.*;
|
||||
import com.jpexs.decompiler.flash.tags.base.*;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Replaces MenuTitle and MenuTitleSmall bitmaps in a skin SWF
|
||||
* with a custom logo image, then saves the modified SWF.
|
||||
*
|
||||
* Usage: ReplaceLogo <skin.swf> <logo.png> <output.swf> [scale]
|
||||
*
|
||||
* Optional scale parameter (0.0-1.0) shrinks the logo within the bitmap
|
||||
* canvas, adding transparent padding. Default is 1.0 (fill canvas).
|
||||
*
|
||||
* Finds bitmaps by symbol name (MenuTitle, MenuTitleSmall) rather than
|
||||
* hardcoded IDs, so it works with both skinHDWin.swf and skinWin.swf.
|
||||
*/
|
||||
public class ReplaceLogo {
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 3) {
|
||||
System.out.println("Usage: ReplaceLogo <skin.swf> <logo.png> <output.swf>");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
String swfPath = args[0];
|
||||
String logoPath = args[1];
|
||||
String outputPath = args[2];
|
||||
double extraScale = (args.length >= 4) ? Double.parseDouble(args[3]) : 1.0;
|
||||
|
||||
System.out.println("Loading SWF: " + swfPath);
|
||||
SWF swf = new SWF(new FileInputStream(swfPath), false);
|
||||
|
||||
System.out.println("Loading logo: " + logoPath);
|
||||
BufferedImage logoOriginal = ImageIO.read(new File(logoPath));
|
||||
System.out.println("Logo dimensions: " + logoOriginal.getWidth() + "x" + logoOriginal.getHeight());
|
||||
System.out.println("Extra scale: " + extraScale);
|
||||
|
||||
// Build symbol name map from SymbolClass tags
|
||||
Map<Integer, String> symbolNames = new HashMap<>();
|
||||
for (Tag tag : swf.getTags()) {
|
||||
if (tag instanceof SymbolClassTag) {
|
||||
SymbolClassTag sym = (SymbolClassTag) tag;
|
||||
for (int i = 0; i < sym.tags.size(); i++) {
|
||||
symbolNames.put(sym.tags.get(i), sym.names.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find and replace MenuTitle / MenuTitleSmall bitmaps by symbol name
|
||||
Set<String> targetNames = Set.of("MenuTitle", "MenuTitleSmall");
|
||||
int replaced = 0;
|
||||
|
||||
for (Tag tag : swf.getTags()) {
|
||||
if (tag instanceof DefineBitsLossless2Tag) {
|
||||
DefineBitsLossless2Tag bmp = (DefineBitsLossless2Tag) tag;
|
||||
String symName = symbolNames.getOrDefault(bmp.characterID, "");
|
||||
|
||||
if (targetNames.contains(symName)) {
|
||||
int targetW = bmp.bitmapWidth;
|
||||
int targetH = bmp.bitmapHeight;
|
||||
|
||||
System.out.println("Replacing " + symName + " (id=" + bmp.characterID + ", " + targetW + "x" + targetH + ")");
|
||||
|
||||
// Scale logo to fit within target dimensions, maintaining aspect ratio
|
||||
// Then apply extra scale factor to shrink within the canvas
|
||||
int fitW = (int) Math.round(targetW * extraScale);
|
||||
int fitH = (int) Math.round(targetH * extraScale);
|
||||
BufferedImage scaled = scaleToFit(logoOriginal, fitW, fitH);
|
||||
|
||||
// Create target-sized image with transparency, center the scaled logo
|
||||
BufferedImage target = new BufferedImage(targetW, targetH, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = target.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
int xOff = (targetW - scaled.getWidth()) / 2;
|
||||
int yOff = (targetH - scaled.getHeight()) / 2;
|
||||
g.drawImage(scaled, xOff, yOff, null);
|
||||
g.dispose();
|
||||
|
||||
// Convert BufferedImage to PNG bytes for FFDec setImage()
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(target, "PNG", baos);
|
||||
bmp.setImage(baos.toByteArray());
|
||||
|
||||
System.out.println(" Replaced with " + scaled.getWidth() + "x" + scaled.getHeight() + " (centered in " + targetW + "x" + targetH + ")");
|
||||
replaced++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (replaced == 0) {
|
||||
System.err.println("ERROR: No logo bitmaps found to replace!");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.out.println("Saving modified SWF: " + outputPath);
|
||||
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
|
||||
swf.saveTo(fos);
|
||||
}
|
||||
System.out.println("Done! Replaced " + replaced + " bitmaps.");
|
||||
}
|
||||
|
||||
static BufferedImage scaleToFit(BufferedImage src, int maxW, int maxH) {
|
||||
double scaleX = (double) maxW / src.getWidth();
|
||||
double scaleY = (double) maxH / src.getHeight();
|
||||
double scale = Math.min(scaleX, scaleY);
|
||||
|
||||
int newW = (int) Math.round(src.getWidth() * scale);
|
||||
int newH = (int) Math.round(src.getHeight() * scale);
|
||||
|
||||
BufferedImage result = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = result.createGraphics();
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.drawImage(src, 0, 0, newW, newH, null);
|
||||
g.dispose();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue