package tools.data; import constants.ServerConstants; import handling.SendPacketOpcode; import java.awt.Point; import java.awt.Rectangle; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import tools.FileoutputUtil; import tools.HexTool; /** * Writes a maplestory-packet little-endian stream of bytes. * * @author Frz */ public class MaplePacketLittleEndianWriter { private final ByteArrayOutputStream baos; private static final Charset ASCII = Charset.forName(ServerConstants.MAPLE_TYPE.getAscii()); // US-ASCII, ISO-8859-1, UTF-8, MS949 /** * Constructor - initializes this stream with a default size. */ public MaplePacketLittleEndianWriter() { this(32); } /** * Constructor - initializes this stream with size <code>size</code>. * * @param size The size of the underlying stream. */ public MaplePacketLittleEndianWriter(final int size) { this.baos = new ByteArrayOutputStream(size); } private void log(byte str) { log(HexTool.toString(str) + " "); } private void log(String str) { if (SendPacketOpcode.record) { FileoutputUtil.logToFile(FileoutputUtil.Packet_Record, str); } } private void baosWrite(byte b) { baos.write(b); log(b); } /** * Gets a <code>MaplePacket</code> instance representing this sequence of * bytes. * * @return A <code>MaplePacket</code> with the bytes in this stream. */ public final byte[] getPacket() { return baos.toByteArray(); } /** * Changes this packet into a human-readable hexadecimal stream of bytes. * * @return This packet as hex digits. */ @Override public final String toString() { return HexTool.toString(baos.toByteArray()); } /** * Write the number of zero bytes * * @param i */ public final void writeZeroBytes(final int i) { for (int x = 0; x < i; x++) { baosWrite((byte) 0); } log("(長度:" + i + ")"); log("\r\n"); } /** * Write an array of bytes to the stream. * * @param b The bytes to write. */ public final void write(final byte[] b) { for (int x = 0; x < b.length; x++) { baosWrite(b[x]); } log("(長度:" + b.length + ")"); log("\r\n"); } /** * Write a byte to the stream. * * @param b The byte to write. */ public final void write(final byte b) { baosWrite(b); log("\r\n"); } public final void write(final int b) { baosWrite((byte) b); log("\r\n"); } public final void write(final boolean b) { baosWrite((byte) (b ? 1 : 0)); log("\r\n"); } /** * Write a short integer to the stream. * * @param i The short integer to write. */ public final void writeShort(final int i) { baosWrite((byte) (i & 0xFF)); baosWrite((byte) ((i >>> 8) & 0xFF)); log("\r\n"); } /** * Writes an integer to the stream. * * @param i The integer to write. */ public final void writeInt(final int i) { baosWrite((byte) (i & 0xFF)); baosWrite((byte) ((i >>> 8) & 0xFF)); baosWrite((byte) ((i >>> 16) & 0xFF)); baosWrite((byte) ((i >>> 24) & 0xFF)); log("\r\n"); } /** * Writes an ASCII string the the stream. * * @param s The ASCII string to write. */ public final void writeAsciiString(final String s) { write(s.getBytes(ASCII)); log("\r\n"); } public final void writeAsciiString(String s, final int max) { if (s.getBytes(ASCII).length > max) { s = s.substring(0, max); } write(s.getBytes(ASCII)); for (int i = s.getBytes(ASCII).length; i < max; i++) { baosWrite((byte) 0); } log("(長度:" + (max - s.getBytes(ASCII).length) + ")"); log("\r\n\r\n"); } /** * Writes a maple-convention ASCII string to the stream. * * @param s The ASCII string to use maple-convention to write. */ public final void writeMapleAsciiString(final String s) { writeShort((short) s.getBytes(ASCII).length); writeAsciiString(s); } public final void writeMapleAsciiString(String s, final int max) { if (s.getBytes(ASCII).length > max) { s = s.substring(0, max); } writeShort((short) s.getBytes(ASCII).length); write(s.getBytes(ASCII)); for (int i = s.getBytes(ASCII).length; i < max; i++) { baosWrite((byte) 0); } log("(長度:" + (max - s.getBytes(ASCII).length) + ")"); log("\r\n\r\n"); } public final void writeNullTerminatedCharString(String s) { byte[] strBytes = s.getBytes(ASCII); for (byte b : strBytes) { baosWrite(b); baosWrite((byte) 0); } log("\r\n"); } public final void writeBoolean(boolean b) { baosWrite((byte) (b ? 1 : 0)); log("\r\n"); } /** * Writes a 2D 4 byte position information * * @param s The Point position to write. */ public final void writePos(final Point s) { writeShort(s.x); writeShort(s.y); log("\r\n"); } public final void writeRect(final Rectangle s) { writeInt(s.x); writeInt(s.y); writeInt(s.x + s.width); writeInt(s.y + s.height); log("\r\n"); } /** * Write a long integer to the stream. * * @param l The long integer to write. */ public final void writeLong(final long l) { baosWrite((byte) (l & 0xFF)); baosWrite((byte) ((l >>> 8) & 0xFF)); baosWrite((byte) ((l >>> 16) & 0xFF)); baosWrite((byte) ((l >>> 24) & 0xFF)); baosWrite((byte) ((l >>> 32) & 0xFF)); baosWrite((byte) ((l >>> 40) & 0xFF)); baosWrite((byte) ((l >>> 48) & 0xFF)); baosWrite((byte) ((l >>> 56) & 0xFF)); log("\r\n"); } public final void writeReversedLong(final long l) { baosWrite((byte) ((l >>> 32) & 0xFF)); baosWrite((byte) ((l >>> 40) & 0xFF)); baosWrite((byte) ((l >>> 48) & 0xFF)); baosWrite((byte) ((l >>> 56) & 0xFF)); baosWrite((byte) (l & 0xFF)); baosWrite((byte) ((l >>> 8) & 0xFF)); baosWrite((byte) ((l >>> 16) & 0xFF)); baosWrite((byte) ((l >>> 24) & 0xFF)); log("\r\n"); } public final void writeFile(final File file) { try { byte[] bytes; InputStream is = new FileInputStream(file); long length = file.length(); if (length > Integer.MAX_VALUE) { System.err.println("檔案太大"); return; } bytes = new byte[(int) length]; int offset = 0; int numRead = 0; while ((offset < bytes.length) && ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) { offset += numRead; } if (offset < bytes.length) { System.err.println("無法完整讀取檔案:" + file.getName()); return; } writeInt(bytes.length); write(bytes); } catch (IOException e) { System.err.println("讀取檔案失敗:" + e); } } }