233 lines
5.9 KiB
Java
233 lines
5.9 KiB
Java
package ayunami2000;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.DataOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.zip.Deflater;
|
|
import java.util.zip.DeflaterOutputStream;
|
|
|
|
public class MapPacketCodec {
|
|
|
|
public interface FragmentHandler {
|
|
void sendFragment(byte[] data, boolean isLastFragment);
|
|
}
|
|
|
|
public enum PixelFormat {
|
|
R5_G6_B5, R8_G8_B8
|
|
}
|
|
|
|
public final int mapId;
|
|
private Deflater deflate = null;
|
|
private PixelFormat pixelFormat = PixelFormat.R5_G6_B5;
|
|
private int[] pixels = null;
|
|
private int[] pallete = null;
|
|
private boolean palleteIsSet = false;
|
|
private boolean palleteIsDirty = false;
|
|
|
|
/**
|
|
* @param mapId the ID of the map item to write to
|
|
*/
|
|
public MapPacketCodec(int mapId) {
|
|
this.mapId = mapId;
|
|
}
|
|
|
|
/**
|
|
* @param enable enables java.util.zip deflate on packets encoded by this class
|
|
*/
|
|
public MapPacketCodec deflate(boolean enable) {
|
|
deflate(enable ? 5 : 0);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @param level sets or disables compression level (0-9)
|
|
*/
|
|
public MapPacketCodec deflate(int level) {
|
|
deflate = level > 0 ? new Deflater(level) : null;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @param pix sets if pixels should be encoded as 16 bits per pixel or 24 bits per pixel
|
|
*/
|
|
public MapPacketCodec pixelFormat(PixelFormat pix) {
|
|
pixelFormat = pix == null ? PixelFormat.R5_G6_B5 : pix;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @param pixels If pallete is disabled, array of 16384 integers each containing the RGB of a pixel. If
|
|
* pallete is enabled, array of 16384 integers each containing an index in the current pallete between 0 and 255
|
|
*/
|
|
public MapPacketCodec setPixels(int[] pixels) {
|
|
if(pixels != null && pixels.length != 16384) {
|
|
throw new IllegalArgumentException("Pixel array must be 16384 pixels long");
|
|
}
|
|
this.pixels = pixels;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @param pixels a 128x128 RGB java.awt.image.BufferedImage, this will disable the pallete
|
|
*/
|
|
public MapPacketCodec setPixels(BufferedImage pixels) {
|
|
if(pixels.getWidth() != 128 || pixels.getHeight() != 128) {
|
|
throw new IllegalArgumentException("BufferedImage must be 128x128 pixels");
|
|
}
|
|
palleteIsSet = false;
|
|
this.pallete = null;
|
|
palleteIsDirty = false;
|
|
int[] pxls = new int[16384];
|
|
pixels.getRGB(0, 0, 128, 128, pxls, 0, 128);
|
|
setPixels(pxls);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* sets and enables the pallete, or disables it if 'pallete' is null
|
|
*
|
|
* @param pallete an array of any size between 1 and 256 containing integers each representing a single RGB color
|
|
*/
|
|
public MapPacketCodec setPallete(int[] pallete) {
|
|
boolean b = pallete != null;
|
|
if(b) {
|
|
palleteIsSet = true;
|
|
this.pallete = pallete;
|
|
palleteIsDirty = true;
|
|
}else {
|
|
if(pixels != null && this.pallete != null) {
|
|
int[] px = pixels;
|
|
pixels = new int[16384];
|
|
for(int i = 0; i < 16384; ++i) {
|
|
int j = px[i];
|
|
pixels[i] = this.pallete[j >= this.pallete.length ? 0 : j];
|
|
}
|
|
}
|
|
this.pallete = null;
|
|
palleteIsSet = false;
|
|
palleteIsDirty = false;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Disables the pallete
|
|
*/
|
|
public MapPacketCodec clearPallete() {
|
|
setPallete(null);
|
|
return this;
|
|
}
|
|
|
|
/*
|
|
* Operations:
|
|
*
|
|
* - 0: disable engine
|
|
* - 1: compressed data
|
|
*
|
|
* - 2: set pixels R8_G8_B8
|
|
* - 3: set pixels R5_G6_B5
|
|
* - 4: set pallete R8_G8_B8
|
|
* - 5: set pallete R5_G6_B5
|
|
* - 6: set pixels via pallete
|
|
* - 7: set pallete and pixels R8_G8_B8
|
|
* - 8: set pallete and pixels R5_G6_B5
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* takes the current pixels array and writes it to a packet and returns it, or returns null if the current pixels array has not changed
|
|
*/
|
|
public byte[] getNextPacket() {
|
|
if(pixels == null) {
|
|
return null;
|
|
}
|
|
try {
|
|
ByteArrayOutputStream o = new ByteArrayOutputStream();
|
|
DataOutputStream s;
|
|
if(deflate != null) {
|
|
o.write(1);
|
|
s = new DataOutputStream(new DeflaterOutputStream(o, deflate));
|
|
}else {
|
|
s = new DataOutputStream(o);
|
|
}
|
|
if(!palleteIsSet || pallete == null) {
|
|
palleteIsSet = false;
|
|
if(pixelFormat == PixelFormat.R5_G6_B5) {
|
|
s.write(3);
|
|
for(int i = 0; i < 16384; ++i) {
|
|
int j = pixels[i];
|
|
int r = (j >> 19) & 0x1F;
|
|
int g = (j >> 10) & 0x3F;
|
|
int b = (j >> 3) & 0x1F;
|
|
s.writeShort((r << 11) | (g << 5) | b);
|
|
}
|
|
}else if(pixelFormat == PixelFormat.R8_G8_B8) {
|
|
s.write(2);
|
|
for(int i = 0; i < 16384; ++i) {
|
|
int j = pixels[i];
|
|
s.write((j >> 16) & 0xFF);
|
|
s.write((j >> 8) & 0xFF);
|
|
s.write(j & 0xFF);
|
|
}
|
|
}else {
|
|
return null; // ?
|
|
}
|
|
}else {
|
|
if(palleteIsDirty) {
|
|
if(pixelFormat == PixelFormat.R5_G6_B5) {
|
|
s.write(8);
|
|
s.write(pallete.length);
|
|
for(int i = 0; i < pallete.length; ++i) {
|
|
int j = pallete[i];
|
|
int r = (j >> 19) & 0x1F;
|
|
int g = (j >> 10) & 0x3F;
|
|
int b = (j >> 3) & 0x1F;
|
|
s.writeShort((r << 11) | (g << 5) | b);
|
|
}
|
|
}else if(pixelFormat == PixelFormat.R8_G8_B8) {
|
|
s.write(7);
|
|
s.write(pallete.length);
|
|
for(int i = 0; i < pallete.length; ++i) {
|
|
int j = pallete[i];
|
|
s.write((j >> 16) & 0xFF);
|
|
s.write((j >> 8) & 0xFF);
|
|
s.write(j & 0xFF);
|
|
}
|
|
}else {
|
|
return null; // ?
|
|
}
|
|
palleteIsDirty = false;
|
|
}else {
|
|
s.write(6);
|
|
}
|
|
for(int i = 0; i < 16384; ++i) {
|
|
s.write(pixels[i]);
|
|
}
|
|
}
|
|
pixels = null;
|
|
s.close();
|
|
return o.toByteArray();
|
|
}catch(IOException e) {
|
|
throw new RuntimeException("Failed to write ayunami map packet");
|
|
}
|
|
}
|
|
|
|
public byte[] getDisablePacket() {
|
|
try {
|
|
palleteIsSet = false;
|
|
palleteIsDirty = false;
|
|
pallete = null;
|
|
pixels = null;
|
|
ByteArrayOutputStream o = new ByteArrayOutputStream();
|
|
DataOutputStream s = new DataOutputStream(o);
|
|
s.writeShort(mapId);
|
|
s.write(0);
|
|
return o.toByteArray();
|
|
}catch(IOException e) {
|
|
throw new RuntimeException("Failed to write ayunami map packet");
|
|
}
|
|
}
|
|
|
|
}
|