sx.kenji.sharpserializerjvm.write.BinaryWriter.java Source code

Java tutorial

Introduction

Here is the source code for sx.kenji.sharpserializerjvm.write.BinaryWriter.java

Source

/**
 *  Eternity Keeper, a Pillars of Eternity save game editor.
 *  Copyright (C) 2015 the authors.
 *
 *  Eternity Keeper is free software: you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  Eternity Keeper is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package sx.kenji.sharpserializerjvm.write;

import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.UnsignedInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sx.kenji.sharpserializerjvm.CSharpType;

import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.UUID;

public class BinaryWriter {
    private static final Logger logger = LoggerFactory.getLogger(BinaryWriter.class);
    public DataOutput out;

    public BinaryWriter(DataOutput out) {
        this.out = out;
    }

    public void writeValue(Object value) throws IOException {
        if (value == null) {
            out.writeBoolean(false);
        } else {
            out.writeBoolean(true);
            writeValueCore(value);
        }
    }

    private void writeValueCore(Object value) throws IOException {
        if (value == null) {
            logger.error("Cannot write null value!%n");
            return;
        }

        if (value instanceof byte[] || value instanceof Byte[]) {
            writeNumber(Array.getLength(value));
            out.write(unboxBytes(value));
            return;
        }

        if (value instanceof String) {
            writeString((String) value);
            return;
        }

        if (value instanceof Boolean) {
            out.writeBoolean((boolean) value);
            return;
        }

        if (value instanceof Byte) {
            out.writeByte((byte) value);
            return;
        }

        if (value instanceof Character) {
            out.writeChar((char) value);
            return;
        }

        if (value instanceof UUID) {
            UUID uuid = (UUID) value;
            byte[] guid = new byte[16];
            long mostSig = uuid.getMostSignificantBits();
            long leastSig = uuid.getLeastSignificantBits();
            byte[] ms = Longs.toByteArray(mostSig);
            byte[] ls = Longs.toByteArray(leastSig);
            int a = Integer.reverseBytes(ByteBuffer.wrap(ms).getInt(0));
            System.arraycopy(Ints.toByteArray(a), 0, guid, 0, 4);
            guid[4] = ms[5];
            guid[5] = ms[4];
            guid[6] = ms[7];
            guid[7] = ms[6];
            System.arraycopy(ls, 0, guid, 8, 8);

            out.write(guid);
            return;
        }

        if (value instanceof Double) {
            out.writeDouble((double) value);
            return;
        }

        if (value instanceof Short) {
            out.writeShort((short) value);
            return;
        }

        if (value instanceof Integer) {
            out.writeInt((int) value);
            return;
        }

        if (value instanceof UnsignedInteger) {
            out.writeInt(((UnsignedInteger) value).intValue());
            return;
        }

        if (value instanceof Long) {
            out.writeLong((long) value);
            return;
        }

        if (value instanceof Float) {
            out.writeFloat((float) value);
            return;
        }

        if (value.getClass().isEnum()) {
            int ordinal;
            Class cls = value.getClass();

            try {
                Field nField = cls.getField("n");
                ordinal = nField.getInt(value);
                out.writeInt(ordinal);
                return;
            } catch (NoSuchFieldException | IllegalAccessException ignored) {
            }

            Object[] constants = cls.getEnumConstants();
            for (int i = 0; i < constants.length; i++) {
                if (value == constants[i]) {
                    out.writeInt(i);
                    return;
                }
            }

            logger.error("Supposed enum value couldn't be " + "matched with any enum constants.");

            return;
        }

        if (value instanceof CSharpType) {
            writeString(((CSharpType) value).type);
            return;
        }

        if (value instanceof Class) {
            logger.error("Trying to write Class type unimplemented.");
            return;
        }

        logger.error("Don't know how to write object of type `{}`.", value.getClass().getSimpleName());
    }

    public void writeStringGuarded(String value) throws IOException {
        if (value == null || value.equals("")) {
            out.writeBoolean(false);
        } else {
            out.writeBoolean(true);
            writeString(value);
        }
    }

    private void writeString(String value) throws IOException {
        byte[] bytes = value.getBytes("UTF-8");
        write7BitEncodedInt(bytes.length);
        out.write(bytes);
    }

    private void write7BitEncodedInt(int v) throws IOException {
        while (v >= 0x80) {
            out.writeByte((byte) (v | 0x80));
            v >>= 7;
        }

        out.writeByte((byte) v);
    }

    public void writeNumber(int n) throws IOException {
        byte size = getIntSizeInBytes(n);
        out.writeByte(size);

        if (size > 0) {
            switch (size) {
            case 1:
                out.writeByte((byte) n);
                break;

            case 2:
                out.writeShort((short) n);
                break;

            default:
                out.writeInt(n);
            }
        }
    }

    private byte getIntSizeInBytes(int n) {
        if (n == 0) {
            return 0;
        }

        if (n > Short.MAX_VALUE || n < Short.MIN_VALUE) {
            return 4;
        }

        if (n > 255 || n < 0) {
            return 2;
        }

        return 1;
    }

    private byte[] unboxBytes(Object value) {
        if (value instanceof byte[]) {
            return (byte[]) value;
        }

        Byte[] in = (Byte[]) value;
        byte[] out = new byte[in.length];
        for (int i = 0; i < in.length; i++) {
            out[i] = in[i];
        }

        return out;
    }

    public void writeNumbers(int[] n) throws IOException {
        writeNumber(n.length);
        for (int i : n) {
            writeNumber(i);
        }
    }
}