/*
This file is part of jpcsp.
Jpcsp 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.
Jpcsp 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 Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.util;
import static java.lang.System.arraycopy;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.MemoryMap;
import jpcsp.Allegrex.Common;
import jpcsp.Allegrex.CpuState;
import jpcsp.Allegrex.compiler.RuntimeContext;
import jpcsp.HLE.Modules;
import jpcsp.HLE.VFS.IVirtualFile;
import jpcsp.filesystems.SeekableDataInput;
import jpcsp.filesystems.SeekableRandomFile;
import jpcsp.filesystems.umdiso.UmdIsoFile;
import jpcsp.memory.IMemoryReader;
import jpcsp.memory.IMemoryWriter;
import jpcsp.memory.MemoryReader;
import jpcsp.memory.MemoryWriter;
public class Utilities {
private static final int[] round4 = {0, 3, 2, 1};
public static String formatString(String type, String oldstring) {
int counter = 0;
if (type.equals("byte")) {
counter = 2;
}
if (type.equals("short")) {
counter = 4;
}
if (type.equals("long")) {
counter = 8;
}
int len = oldstring.length();
StringBuilder sb = new StringBuilder();
while (len++ < counter) {
sb.append('0');
}
oldstring = sb.append(oldstring).toString();
return oldstring;
}
public static String integerToBin(int value) {
return Long.toBinaryString(0x0000000100000000L | ((value) & 0x00000000FFFFFFFFL)).substring(1);
}
public static String integerToHex(int value) {
return Integer.toHexString(0x100 | value).substring(1).toUpperCase();
}
public static String integerToHexShort(int value) {
return Integer.toHexString(0x10000 | value).substring(1).toUpperCase();
}
public static long readUWord(SeekableDataInput f) throws IOException {
long l = (f.readUnsignedByte() | (f.readUnsignedByte() << 8) | (f.readUnsignedByte() << 16) | (f.readUnsignedByte() << 24));
return (l & 0xFFFFFFFFL);
}
public static int readUByte(SeekableDataInput f) throws IOException {
return f.readUnsignedByte();
}
public static int readUHalf(SeekableDataInput f) throws IOException {
return f.readUnsignedByte() | (f.readUnsignedByte() << 8);
}
public static int readWord(SeekableDataInput f) throws IOException {
//readByte() isn't more correct? (already exists one readUWord() method to unsign values)
return (f.readUnsignedByte() | (f.readUnsignedByte() << 8) | (f.readUnsignedByte() << 16) | (f.readUnsignedByte() << 24));
}
public static void skipUnknown(ByteBuffer buf, int length) throws IOException {
buf.position(buf.position() + length);
}
public static String readStringZ(ByteBuffer buf) throws IOException {
StringBuilder sb = new StringBuilder();
byte b;
for (; buf.position() < buf.limit();) {
b = (byte) readUByte(buf);
if (b == 0) {
break;
}
sb.append((char) b);
}
return sb.toString();
}
public static String readStringNZ(ByteBuffer buf, int n) throws IOException {
StringBuilder sb = new StringBuilder();
byte b;
for (; n > 0; n--) {
b = (byte) readUByte(buf);
if (b != 0) {
sb.append((char) b);
}
}
return sb.toString();
}
/**
* Read a string from memory. The string ends when the maximal length is
* reached or a '\0' byte is found. The memory bytes are interpreted as
* UTF-8 bytes to form the string.
*
* @param mem the memory
* @param address the address of the first byte of the string
* @param n the maximal string length
* @return the string converted to UTF-8
*/
public static String readStringNZ(Memory mem, int address, int n) {
address &= Memory.addressMask;
if (address + n > MemoryMap.END_RAM) {
n = MemoryMap.END_RAM - address + 1;
if (n < 0) {
n = 0;
}
}
// Allocate a byte array to store the bytes of the string.
// At first, allocate maximum 10000 bytes in case we don't know
// the maximal string length. The array will be extended if required.
byte[] bytes = new byte[Math.min(n, 10000)];
int length = 0;
IMemoryReader memoryReader = MemoryReader.getMemoryReader(address, n, 1);
for (; n > 0; n--) {
int b = memoryReader.readNext();
if (b == 0) {
break;
}
if (length >= bytes.length) {
// Extend the bytes array
bytes = extendArray(bytes, 10000);
}
bytes[length] = (byte) b;
length++;
}
// Convert the bytes to UTF-8
return new String(bytes, 0, length, Constants.charset);
}
public static String readStringZ(Memory mem, int address) {
address &= Memory.addressMask;
return readStringNZ(mem, address, MemoryMap.END_RAM - address + 1);
}
public static String readStringZ(int address) {
return readStringZ(Memory.getInstance(), address);
}
public static String readStringNZ(byte[] buffer, int offset, int n) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < n; i++) {
byte b = buffer[offset + i];
if (b == (byte) 0) {
break;
}
s.append((char) b);
}
return s.toString();
}
public static String readStringZ(byte[] buffer, int offset) {
StringBuilder s = new StringBuilder();
while (offset < buffer.length) {
byte b = buffer[offset++];
if (b == (byte) 0) {
break;
}
s.append((char) b);
}
return s.toString();
}
public static String readStringNZ(int address, int n) {
return readStringNZ(Memory.getInstance(), address, n);
}
public static void writeStringNZ(Memory mem, int address, int n, String s) {
int offset = 0;
IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(address, n, 1);
if (s != null) {
byte[] bytes = s.getBytes(Constants.charset);
while (offset < bytes.length && offset < n) {
memoryWriter.writeNext(bytes[offset]);
offset++;
}
}
while (offset < n) {
memoryWriter.writeNext(0);
offset++;
}
memoryWriter.flush();
}
public static void writeStringNZ(byte[] buffer, int offset, int n, String s) {
if (s != null) {
byte[] bytes = s.getBytes(Constants.charset);
int length = Math.min(n, bytes.length);
System.arraycopy(bytes, 0, buffer, offset, length);
if (length < n) {
Arrays.fill(buffer, offset + length, offset + n, (byte) 0);
}
} else {
Arrays.fill(buffer, offset, offset + n, (byte) 0);
}
}
public static void writeStringZ(Memory mem, int address, String s) {
// add 1 to the length to write the final '\0'
writeStringNZ(mem, address, s.length() + 1, s);
}
public static void writeStringZ(ByteBuffer buf, String s) {
buf.put(s.getBytes());
buf.put((byte) 0);
}
public static int getUnsignedByte(ByteBuffer bb) throws IOException {
return bb.get() & 0xFF;
}
public static void putUnsignedByte(ByteBuffer bb, int value) {
bb.put((byte) (value & 0xFF));
}
public static int readUByte(ByteBuffer buf) throws IOException {
return getUnsignedByte(buf);
}
public static int readUHalf(ByteBuffer buf) throws IOException {
return getUnsignedByte(buf) | (getUnsignedByte(buf) << 8);
}
public static int readUWord(ByteBuffer buf) throws IOException {
// No difference between signed and unsigned word (32-bit value)
return readWord(buf);
}
public static int readWord(ByteBuffer buf) throws IOException {
return getUnsignedByte(buf) | (getUnsignedByte(buf) << 8) | (getUnsignedByte(buf) << 16) | (getUnsignedByte(buf) << 24);
}
public static void writeWord(ByteBuffer buf, int value) {
putUnsignedByte(buf, value >> 0);
putUnsignedByte(buf, value >> 8);
putUnsignedByte(buf, value >> 16);
putUnsignedByte(buf, value >> 24);
}
public static void writeHalf(ByteBuffer buf, int value) {
putUnsignedByte(buf, value >> 0);
putUnsignedByte(buf, value >> 8);
}
public static void writeByte(ByteBuffer buf, int value) {
putUnsignedByte(buf, value);
}
public static int parseAddress(String s) throws NumberFormatException {
int address = 0;
if (s == null) {
return address;
}
s = s.trim();
if (s.startsWith("0x")) {
s = s.substring(2);
}
if (s.length() == 8 && s.charAt(0) >= '8') {
address = (int) Long.parseLong(s, 16);
} else {
address = Integer.parseInt(s, 16);
}
return address;
}
public static int parseInteger(String s) throws NumberFormatException {
int value = 0;
if (s == null) {
return value;
}
s = s.trim();
boolean neg = false;
if (s.startsWith("-")) {
s = s.substring(1);
neg = true;
}
int base = 10;
if (s.startsWith("0x")) {
s = s.substring(2);
base = 16;
}
if (s.length() == 8 && s.charAt(0) >= '8') {
value = (int) Long.parseLong(s, base);
} else {
value = Integer.parseInt(s, base);
}
if (neg) {
value = -value;
}
return value;
}
public static int getRegister(String s) {
for (int i = 0; i < Common.gprNames.length; i++) {
if (Common.gprNames[i].equalsIgnoreCase(s)) {
return i;
}
}
return -1;
}
public static int parseAddressExpression(String s) {
if (s == null) {
return 0;
}
s = s.trim();
// Build a pattern matching all Gpr register names
String regPattern = "";
for (String gprName : Common.gprNames) {
regPattern += "\\" + gprName + "|";
}
Memory mem = Emulator.getMemory();
CpuState cpu = Emulator.getProcessor().cpu;
Pattern p;
Matcher m;
// Parse e.g.: "$a0"
p = Pattern.compile(regPattern);
m = p.matcher(s);
if (m.matches()) {
int reg = getRegister(s);
if (reg >= 0) {
return cpu.getRegister(reg);
}
}
// Parse e.g.: "16($a0)", "0xc($a1)"
p = Pattern.compile("((0x)?\\p{XDigit}+)\\((" + regPattern + ")\\)");
m = p.matcher(s);
if (m.matches()) {
int offset = parseInteger(m.group(1));
int reg = getRegister(m.group(3));
if (reg >= 0) {
return mem.read32(cpu.getRegister(reg) + offset);
}
}
// Parse e.g.: "$a0 + 16", "$a1 - 0xc"
p = Pattern.compile("(" + regPattern + ")\\s*([+\\-])\\s*((0x)?\\p{XDigit}+)");
m = p.matcher(s);
if (m.matches()) {
int reg = getRegister(m.group(1));
int offset = parseInteger(m.group(3));
if (m.group(2).equals("-")) {
offset = -offset;
}
if (reg >= 0) {
return cpu.getRegister(reg) + offset;
}
}
return Utilities.parseAddress(s);
}
/**
* Parse the string as a number and returns its value. If the string starts
* with "0x", the number is parsed in base 16, otherwise base 10.
*
* @param s the string to be parsed
* @return the numeric value represented by the string.
*/
public static long parseLong(String s) {
long value = 0;
if (s == null) {
return value;
}
if (s.startsWith("0x")) {
value = Long.parseLong(s.substring(2), 16);
} else {
value = Long.parseLong(s);
}
return value;
}
/**
* Parse the string as a number and returns its value. The number is always
* parsed in base 16. The string can start as an option with "0x".
*
* @param s the string to be parsed in base 16
* @param ignoreTrailingChars true if trailing (i.e. non-hex characters) have to be ignored
* false if non-hex characters have to raise an exception NumberFormatException
* @return the numeric value represented by the string.
*/
public static long parseHexLong(String s, boolean ignoreTrailingChars) {
long value = 0;
if (s == null) {
return value;
}
if (s.startsWith("0x")) {
s = s.substring(2);
}
if (ignoreTrailingChars && s.length() > 0) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// Is it an hexadecimal character?
if ("0123456789abcdefABCDEF".indexOf(c) < 0) {
// Delete the trailing non-hex characters
s = s.substring(0, i);
break;
}
}
}
value = Long.parseLong(s, 16);
return value;
}
public static int makePow2(int n) {
--n;
n = (n >> 1) | n;
n = (n >> 2) | n;
n = (n >> 4) | n;
n = (n >> 8) | n;
n = (n >> 16) | n;
return ++n;
}
/**
* Check if a value is a power of 2, i.e. a value that be can computed as (1 << x).
*
* @param n value to be checked
* @return true if the value is a power of 2,
* false otherwise.
*/
public static boolean isPower2(int n) {
return (n & (n - 1)) == 0;
}
public static void readFully(SeekableDataInput input, int address, int length) throws IOException {
final int blockSize = 16 * UmdIsoFile.sectorLength; // 32Kb
byte[] buffer = null;
while (length > 0) {
int size = Math.min(length, blockSize);
if (buffer == null || size != buffer.length) {
buffer = new byte[size];
}
input.readFully(buffer);
Memory.getInstance().copyToMemory(address, ByteBuffer.wrap(buffer), size);
address += size;
length -= size;
}
}
public static void write(SeekableRandomFile output, int address, int length) throws IOException {
Buffer buffer = Memory.getInstance().getBuffer(address, length);
if (buffer instanceof ByteBuffer) {
output.getChannel().write((ByteBuffer) buffer);
} else if (length > 0) {
byte[] bytes = new byte[length];
IMemoryReader memoryReader = MemoryReader.getMemoryReader(address, length, 1);
for (int i = 0; i < length; i++) {
bytes[i] = (byte) memoryReader.readNext();
}
output.write(bytes);
}
}
public static void bytePositionBuffer(Buffer buffer, int bytePosition) {
buffer.position(bytePosition / bufferElementSize(buffer));
}
public static int bufferElementSize(Buffer buffer) {
if (buffer instanceof IntBuffer) {
return 4;
}
return 1;
}
public static String stripNL(String s) {
if (s != null && s.endsWith("\n")) {
s = s.substring(0, s.length() - 1);
}
return s;
}
public static void putBuffer(ByteBuffer destination, Buffer source, ByteOrder sourceByteOrder) {
// Set the destination to the desired ByteOrder
ByteOrder order = destination.order();
destination.order(sourceByteOrder);
if (source instanceof IntBuffer) {
destination.asIntBuffer().put((IntBuffer) source);
} else if (source instanceof ShortBuffer) {
destination.asShortBuffer().put((ShortBuffer) source);
} else if (source instanceof ByteBuffer) {
destination.put((ByteBuffer) source);
} else if (source instanceof FloatBuffer) {
destination.asFloatBuffer().put((FloatBuffer) source);
} else {
Modules.log.error("Utilities.putBuffer: Unsupported Buffer type " + source.getClass().getName());
Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_UNIMPLEMENTED);
}
// Reset the original ByteOrder of the destination
destination.order(order);
}
public static void putBuffer(ByteBuffer destination, Buffer source, ByteOrder sourceByteOrder, int lengthInBytes) {
// Set the destination to the desired ByteOrder
ByteOrder order = destination.order();
destination.order(sourceByteOrder);
int srcLimit = source.limit();
if (source instanceof IntBuffer) {
int copyLength = lengthInBytes & ~3;
destination.asIntBuffer().put((IntBuffer) source.limit(source.position() + (copyLength >> 2)));
int restLength = lengthInBytes - copyLength;
if (restLength > 0) {
// 1 to 3 bytes left to copy
source.limit(srcLimit);
int value = ((IntBuffer) source).get();
int position = destination.position() + copyLength;
do {
destination.put(position, (byte) value);
value >>= 8;
restLength--;
position++;
} while (restLength > 0);
}
} else if (source instanceof ByteBuffer) {
destination.put((ByteBuffer) source.limit(source.position() + lengthInBytes));
} else if (source instanceof ShortBuffer) {
int copyLength = lengthInBytes & ~1;
destination.asShortBuffer().put((ShortBuffer) source.limit(source.position() + (copyLength >> 1)));
int restLength = lengthInBytes - copyLength;
if (restLength > 0) {
// 1 byte left to copy
source.limit(srcLimit);
short value = ((ShortBuffer) source).get();
destination.put(destination.position() + copyLength, (byte) value);
}
} else if (source instanceof FloatBuffer) {
int copyLength = lengthInBytes & ~3;
destination.asFloatBuffer().put((FloatBuffer) source.limit(source.position() + (copyLength >> 2)));
int restLength = lengthInBytes - copyLength;
if (restLength > 0) {
// 1 to 3 bytes left to copy
source.limit(srcLimit);
int value = Float.floatToRawIntBits(((FloatBuffer) source).get());
int position = destination.position() + copyLength;
do {
destination.put(position, (byte) value);
value >>= 8;
restLength--;
position++;
} while (restLength > 0);
}
} else {
Emulator.log.error("Utilities.putBuffer: Unsupported Buffer type " + source.getClass().getName());
Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_UNIMPLEMENTED);
}
// Reset the original ByteOrder of the destination
destination.order(order);
// Reset the original limit of the source
source.limit(srcLimit);
}
/**
* Reads inputstream i into a String with the UTF-8 charset until the
* inputstream is finished (don't use with infinite streams).
*
* @param inputStream to read into a string
* @param close if true, close the inputstream
* @return a string
* @throws java.io.IOException if thrown on reading the stream
* @throws java.lang.NullPointerException if the given inputstream is null
*/
public static String toString(InputStream inputStream, boolean close) throws IOException {
if (inputStream == null) {
throw new NullPointerException("null inputstream");
}
String string;
StringBuilder outputBuilder = new StringBuilder();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while (null != (string = reader.readLine())) {
outputBuilder.append(string).append('\n');
}
} finally {
if (close) {
close(inputStream);
}
}
return outputBuilder.toString();
}
/**
* Close closeables. Use this in a finally clause.
*/
public static void close(Closeable... closeables) {
for (Closeable c : closeables) {
if (c != null) {
try {
c.close();
} catch (Exception ex) {
Logger.getLogger(Utilities.class.getName()).log(Level.WARNING, "Couldn't close Closeable", ex);
}
}
}
}
public static int getSizeKb(long sizeByte) {
return (int) ((sizeByte + 1023) / 1024);
}
private static void addAsciiDump(StringBuilder dump, IMemoryReader charReader, int bytesPerLine) {
dump.append(" >");
for (int i = 0; i < bytesPerLine; i++) {
char c = (char) charReader.readNext();
if (c < ' ' || c > '~') {
c = '.';
}
dump.append(c);
}
dump.append("<");
}
private static String getMemoryDump(int address, int length, int step, int bytesPerLine, IMemoryReader memoryReader, IMemoryReader charReader) {
StringBuilder dump = new StringBuilder();
String lineSeparator = System.getProperty("line.separator");
if (length < bytesPerLine) {
bytesPerLine = length;
}
String format = String.format(" %%0%dX", step * 2);
boolean startOfLine = true;
for (int i = 0; i < length; i += step) {
if ((i % bytesPerLine) < step) {
if (i > 0) {
// Add an ASCII representation at the end of the line
addAsciiDump(dump, charReader, bytesPerLine);
}
dump.append(lineSeparator);
startOfLine = true;
}
if (startOfLine) {
dump.append(String.format("0x%08X", address + i));
startOfLine = false;
}
int value = memoryReader.readNext();
if (length - i >= step) {
dump.append(String.format(format, value));
} else {
switch (length - i) {
case 3:
dump.append(String.format(" %06X", value & 0x00FFFFFF));
break;
case 2:
dump.append(String.format(" %04X", value & 0x0000FFFF));
break;
case 1:
dump.append(String.format(" %02X", value & 0x000000FF));
break;
}
}
}
int lengthLastLine = length % bytesPerLine;
if (lengthLastLine > 0) {
for (int i = lengthLastLine; i < bytesPerLine; i++) {
dump.append(" ");
if ((i % step) == 0) {
dump.append(" ");
}
}
addAsciiDump(dump, charReader, lengthLastLine);
} else {
addAsciiDump(dump, charReader, bytesPerLine);
}
return dump.toString();
}
public static String getMemoryDump(int address, int length) {
// Convenience function using default step and bytesPerLine
return getMemoryDump(address, length, 1, 16);
}
public static String getMemoryDump(int address, int length, int step, int bytesPerLine) {
if (!Memory.isAddressGood(address)) {
return String.format("Invalid memory address 0x%08X", address);
}
if (length <= 0 || bytesPerLine <= 0 || step <= 0) {
return "";
}
IMemoryReader memoryReader = MemoryReader.getMemoryReader(address, length, step);
IMemoryReader charReader = MemoryReader.getMemoryReader(address, length, 1);
return getMemoryDump(address, length, step, bytesPerLine, memoryReader, charReader);
}
public static String getMemoryDump(byte[] bytes) {
return getMemoryDump(bytes, 0, bytes == null ? 0 : bytes.length);
}
public static String getMemoryDump(byte[] bytes, int offset, int length) {
// Convenience function using default step and bytesPerLine
return getMemoryDump(bytes, offset, length, 1, 16);
}
public static String getMemoryDump(byte[] bytes, int offset, int length, int step, int bytesPerLine) {
if (bytes == null || length <= 0 || bytesPerLine <= 0 || step <= 0) {
return "";
}
IMemoryReader memoryReader = MemoryReader.getMemoryReader(0, bytes, offset, length, step);
IMemoryReader charReader = MemoryReader.getMemoryReader(0, bytes, offset, length, step);
return getMemoryDump(0, length, step, bytesPerLine, memoryReader, charReader);
}
public static int alignUp(int value, int alignment) {
return alignDown(value + alignment, alignment);
}
public static int alignDown(int value, int alignment) {
return value & ~alignment;
}
public static long alignDown(long value, long alignment) {
return value & ~alignment;
}
public static int endianSwap32(int x) {
return Integer.reverseBytes(x);
}
public static int endianSwap16(int x) {
return ((x >> 8) & 0x00FF) | ((x << 8) & 0xFF00);
}
public static int readUnaligned32(Memory mem, int address) {
switch (address & 3) {
case 0:
return mem.read32(address);
case 2:
return mem.read16(address) | (mem.read16(address + 2) << 16);
default:
return (mem.read8(address + 3) << 24)
| (mem.read8(address + 2) << 16)
| (mem.read8(address + 1) << 8)
| (mem.read8(address));
}
}
public static int readUnaligned16(Memory mem, int address) {
if ((address & 1) == 0) {
return mem.read16(address);
}
return (mem.read8(address + 1) << 8) | mem.read8(address);
}
public static int read8(byte[] buffer, int offset) {
return buffer[offset] & 0xFF;
}
public static int readUnaligned32(byte[] buffer, int offset) {
return (read8(buffer, offset + 3) << 24)
| (read8(buffer, offset + 2) << 16)
| (read8(buffer, offset + 1) << 8)
| (read8(buffer, offset));
}
public static long readUnaligned64(byte[] buffer, int offset) {
return (((long) read8(buffer, offset + 7)) << 56)
| (((long) read8(buffer, offset + 6)) << 48)
| (((long) read8(buffer, offset + 5)) << 40)
| (((long) read8(buffer, offset + 4)) << 32)
| (((long) read8(buffer, offset + 3)) << 24)
| (((long) read8(buffer, offset + 2)) << 16)
| (((long) read8(buffer, offset + 1)) << 8)
| (((long) read8(buffer, offset)));
}
public static int readUnaligned16(byte[] buffer, int offset) {
return (read8(buffer, offset + 1) << 8) | read8(buffer, offset);
}
public static void writeUnaligned32(Memory mem, int address, int data) {
switch (address & 3) {
case 0:
mem.write32(address, data);
break;
case 2:
mem.write16(address, (short) data);
mem.write16(address + 2, (short) (data >> 16));
break;
default:
mem.write8(address, (byte) data);
mem.write8(address + 1, (byte) (data >> 8));
mem.write8(address + 2, (byte) (data >> 16));
mem.write8(address + 3, (byte) (data >> 24));
}
}
public static void writeUnaligned32(byte[] buffer, int offset, int data) {
buffer[offset + 0] = (byte) data;
buffer[offset + 1] = (byte) (data >> 8);
buffer[offset + 2] = (byte) (data >> 16);
buffer[offset + 3] = (byte) (data >> 24);
}
public static void writeUnaligned16(byte[] buffer, int offset, int data) {
buffer[offset + 0] = (byte) data;
buffer[offset + 1] = (byte) (data >> 8);
}
public static void writeUnaligned64(byte[] buffer, int offset, long data) {
buffer[offset + 0] = (byte) data;
buffer[offset + 1] = (byte) (data >> 8);
buffer[offset + 2] = (byte) (data >> 16);
buffer[offset + 3] = (byte) (data >> 24);
buffer[offset + 4] = (byte) (data >> 32);
buffer[offset + 5] = (byte) (data >> 40);
buffer[offset + 6] = (byte) (data >> 48);
buffer[offset + 7] = (byte) (data >> 56);
}
public static int min(int a, int b) {
return Math.min(a, b);
}
public static float min(float a, float b) {
return Math.min(a, b);
}
public static int max(int a, int b) {
return Math.max(a, b);
}
public static float max(float a, float b) {
return Math.max(a, b);
}
/**
* Minimum value rounded down.
*
* @param a first float value
* @param b second float value
* @return the largest int value that is less than or equal to both
* parameters
*/
public static int minInt(float a, float b) {
return floor(min(a, b));
}
/**
* Minimum value rounded down.
*
* @param a first int value
* @param b second float value
* @return the largest int value that is less than or equal to both
* parameters
*/
public static int minInt(int a, float b) {
return min(a, floor(b));
}
/**
* Maximum value rounded up.
*
* @param a first float value
* @param b second float value
* @return the smallest int value that is greater than or equal to both
* parameters
*/
public static int maxInt(float a, float b) {
return ceil(max(a, b));
}
/**
* Maximum value rounded up.
*
* @param a first float value
* @param b second float value
* @return the smallest int value that is greater than or equal to both
* parameters
*/
public static int maxInt(int a, float b) {
return max(a, ceil(b));
}
public static int min(int a, int b, int c) {
return Math.min(a, Math.min(b, c));
}
public static int max(int a, int b, int c) {
return Math.max(a, Math.max(b, c));
}
public static void sleep(int micros) {
sleep(micros / 1000, micros % 1000);
}
public static void sleep(int millis, int micros) {
if (millis < 0) {
return;
}
try {
if (micros <= 0) {
Thread.sleep(millis);
} else {
Thread.sleep(millis, micros * 1000);
}
} catch (InterruptedException e) {
// Ignore exception
}
}
public static void matrixMult(final float[] result, float[] m1, float[] m2) {
// If the result has to be stored into one of the input matrix,
// duplicate the input matrix.
if (result == m1) {
m1 = m1.clone();
}
if (result == m2) {
m2 = m2.clone();
}
int i = 0;
for (int j = 0; j < 16; j += 4) {
for (int x = 0; x < 4; x++) {
result[i] = m1[x] * m2[j]
+ m1[x + 4] * m2[j + 1]
+ m1[x + 8] * m2[j + 2]
+ m1[x + 12] * m2[j + 3];
i++;
}
}
}
public static void vectorMult(final float[] result, final float[] m, final float[] v) {
for (int i = 0; i < result.length; i++) {
float s = v[0] * m[i];
int k = i + 4;
for (int j = 1; j < v.length; j++) {
s += v[j] * m[k];
k += 4;
}
result[i] = s;
}
}
public static void vectorMult33(final float[] result, final float[] m, final float[] v) {
result[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8];
result[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9];
result[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10];
}
public static void vectorMult34(final float[] result, final float[] m, final float[] v) {
result[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
result[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
result[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
}
public static void vectorMult44(final float[] result, final float[] m, final float[] v) {
result[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
result[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
result[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
result[3] = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
}
// This is equivalent to Math.round but faster: Math.round is using StrictMath.
public static int round(float n) {
return (int) (n + .5f);
}
public static int floor(float n) {
return (int) Math.floor(n);
}
public static int ceil(float n) {
return (int) Math.ceil(n);
}
public static int getPower2(int n) {
return Integer.numberOfTrailingZeros(makePow2(n));
}
public static void copy(boolean[] to, boolean[] from) {
arraycopy(from, 0, to, 0, to.length);
}
public static void copy(boolean[][] to, boolean[][] from) {
for (int i = 0; i < to.length; i++) {
copy(to[i], from[i]);
}
}
public static void copy(int[] to, int[] from) {
arraycopy(from, 0, to, 0, to.length);
}
public static void copy(int[][] to, int[][] from) {
for (int i = 0; i < to.length; i++) {
copy(to[i], from[i]);
}
}
public static void copy(int[][][] to, int[][][] from) {
for (int i = 0; i < to.length; i++) {
copy(to[i], from[i]);
}
}
public static void copy(int[][][][] to, int[][][][] from) {
for (int i = 0; i < to.length; i++) {
copy(to[i], from[i]);
}
}
public static void copy(float[] to, float[] from) {
arraycopy(from, 0, to, 0, to.length);
}
public static void copy(float[][] to, float[][] from) {
for (int i = 0; i < to.length; i++) {
copy(to[i], from[i]);
}
}
public static void copy(float[][][] to, float[][][] from) {
for (int i = 0; i < to.length; i++) {
copy(to[i], from[i]);
}
}
public static void copy(float[][][][] to, float[][][][] from) {
for (int i = 0; i < to.length; i++) {
copy(to[i], from[i]);
}
}
public static float dot3(float[] a, float[] b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
public static float dot3(float[] a, float x, float y, float z) {
return a[0] * x + a[1] * y + a[2] * z;
}
public static float length3(float[] a) {
return (float) Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
}
public static float invertedLength3(float[] a) {
float length = length3(a);
if (length == 0.f) {
return 0.f;
}
return 1.f / length;
}
public static void normalize3(float[] result, float[] a) {
float invertedLength = invertedLength3(a);
result[0] = a[0] * invertedLength;
result[1] = a[1] * invertedLength;
result[2] = a[2] * invertedLength;
}
public static float pow(float a, float b) {
return (float) Math.pow(a, b);
}
public static float clamp(float n, float minValue, float maxValue) {
return max(minValue, min(n, maxValue));
}
/**
* Invert a 3x3 matrix.
*
* Based on
* http://en.wikipedia.org/wiki/Invert_matrix#Inversion_of_3.C3.973_matrices
*
* @param result the inverted matrix (stored as a 4x4 matrix, but only 3x3
* is returned)
* @param m the matrix to be inverted (stored as a 4x4 matrix, but only 3x3
* is used)
* @return true if the matrix could be inverted false if the matrix could
* not be inverted
*/
public static boolean invertMatrix3x3(float[] result, float[] m) {
float A = m[5] * m[10] - m[6] * m[9];
float B = m[6] * m[8] - m[4] * m[10];
float C = m[4] * m[9] - m[5] * m[8];
float det = m[0] * A + m[1] * B + m[2] * C;
if (det == 0.f) {
// Matrix could not be inverted
return false;
}
float invertedDet = 1.f / det;
result[0] = A * invertedDet;
result[1] = (m[2] * m[9] - m[1] * m[10]) * invertedDet;
result[2] = (m[1] * m[6] - m[2] * m[5]) * invertedDet;
result[4] = B * invertedDet;
result[5] = (m[0] * m[10] - m[2] * m[8]) * invertedDet;
result[6] = (m[2] * m[4] - m[0] * m[6]) * invertedDet;
result[8] = C * invertedDet;
result[9] = (m[8] * m[1] - m[0] * m[9]) * invertedDet;
result[10] = (m[0] * m[5] - m[1] * m[4]) * invertedDet;
return true;
}
public static void transposeMatrix3x3(float[] result, float[] m) {
for (int i = 0, j = 0; i < 3; i++, j += 4) {
result[i] = m[j];
result[i + 4] = m[j + 1];
result[i + 8] = m[j + 2];
}
}
public static boolean sameColor(float[] c1, float[] c2, float[] c3) {
for (int i = 0; i < 4; i++) {
if (c1[i] != c2[i] || c1[i] != c3[i]) {
return false;
}
}
return true;
}
public static boolean sameColor(float[] c1, float[] c2, float[] c3, float[] c4) {
for (int i = 0; i < 4; i++) {
if (c1[i] != c2[i] || c1[i] != c3[i] || c1[i] != c4[i]) {
return false;
}
}
return true;
}
/**
* Transform a pixel coordinate (floating-point value "u" or "v") into a
* texel coordinate (integer value to access the texture).
*
* The texel coordinate is calculated by truncating the floating point
* value, not by rounding it. Otherwise transition problems occur at the
* borders. E.g. if a texture has a width of 64, valid texel coordinates
* range from 0 to 63. 64 is already outside of the texture and should not
* be generated when approaching the border to the texture.
*
* @param coordinate the pixel coordinate
* @return the texel coordinate
*/
public static final int pixelToTexel(float coordinate) {
return (int) coordinate;
}
/**
* Wrap the value to the range [0..1[ (1 is excluded).
*
* E.g. value == 4.0 -> return 0.0 value == 4.1 -> return 0.1 value == 4.9
* -> return 0.9 value == -4.0 -> return 0.0 value == -4.1 -> return 0.9
* (and not 0.1) value == -4.9 -> return 0.1 (and not 0.9)
*
* @param value the value to be wrapped
* @return the wrapped value in the range [0..1[ (1 is excluded)
*/
public static float wrap(float value) {
if (value >= 0.f) {
// value == 4.0 -> return 0.0
// value == 4.1 -> return 0.1
// value == 4.9 -> return 0.9
return value - (int) value;
}
// value == -4.0 -> return 0.0
// value == -4.1 -> return 0.9
// value == -4.9 -> return 0.1
// value == -1e-8 -> return 0.0
float wrappedValue = value - (float) Math.floor(value);
if (wrappedValue >= 1.f) {
wrappedValue -= 1.f;
}
return wrappedValue;
}
public static int wrap(float value, int valueMask) {
return pixelToTexel(value) & valueMask;
}
public static void readBytes(int address, int length, byte[] bytes, int offset) {
IMemoryReader memoryReader = MemoryReader.getMemoryReader(address, length, 1);
for (int i = 0; i < length; i++) {
bytes[offset + i] = (byte) memoryReader.readNext();
}
}
public static void writeBytes(int address, int length, byte[] bytes, int offset) {
IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(address, length, 1);
for (int i = 0; i < length; i++) {
memoryWriter.writeNext(bytes[i + offset] & 0xFF);
}
memoryWriter.flush();
}
public static int[] readInt32(int address, int length) {
int[] a = new int[length >> 2];
// Optimize the most common case
if (RuntimeContext.hasMemoryInt()) {
System.arraycopy(RuntimeContext.getMemoryInt(), address >> 2, a, 0, a.length);
} else {
IMemoryReader memoryReader = MemoryReader.getMemoryReader(address, length, 4);
for (int i = 0; i < a.length; i++) {
a[i] = memoryReader.readNext();
}
}
return a;
}
public static int round4(int n) {
return n + round4[n & 3];
}
public static int round2(int n) {
return n + (n & 1);
}
public static int[] extendArray(int[] array, int extend) {
if (array == null) {
return new int[extend];
}
int[] newArray = new int[array.length + extend];
System.arraycopy(array, 0, newArray, 0, array.length);
return newArray;
}
public static byte[] extendArray(byte[] array, int extend) {
if (array == null) {
return new byte[extend];
}
byte[] newArray = new byte[array.length + extend];
System.arraycopy(array, 0, newArray, 0, array.length);
return newArray;
}
public static byte[] extendArray(byte[] array, byte[] extend) {
if (extend == null || extend.length == 0) {
return array;
}
if (array == null) {
array = new byte[extend.length];
System.arraycopy(extend, 0, array, 0, extend.length);
return array;
}
byte[] newArray = new byte[array.length + extend.length];
System.arraycopy(array, 0, newArray, 0, array.length);
System.arraycopy(extend, 0, newArray, array.length, extend.length);
return newArray;
}
public static String[] add(String[] array, String s) {
if (s == null) {
return array;
}
if (array == null) {
return new String[] { s };
}
String[] newArray = new String[array.length + 1];
System.arraycopy(array, 0, newArray, 0, array.length);
newArray[array.length] = s;
return newArray;
}
public static File[] add(File[] array, File f) {
if (f == null) {
return array;
}
if (array == null) {
return new File[] { f };
}
File[] newArray = new File[array.length + 1];
System.arraycopy(array, 0, newArray, 0, array.length);
newArray[array.length] = f;
return newArray;
}
public static byte[] readCompleteFile(IVirtualFile vFile) {
if (vFile == null) {
return null;
}
byte[] buffer;
try {
buffer = new byte[(int) (vFile.length() - vFile.getPosition())];
} catch (OutOfMemoryError e) {
Emulator.log.error("Error while reading a complete vFile", e);
return null;
}
int length = 0;
while (length < buffer.length) {
int readLength = vFile.ioRead(buffer, length, buffer.length - length);
if (readLength < 0) {
break;
}
length += readLength;
}
if (length < buffer.length) {
byte[] resizedBuffer;
try {
resizedBuffer = new byte[length];
} catch (OutOfMemoryError e) {
Emulator.log.error("Error while reading a complete vFile", e);
return null;
}
System.arraycopy(buffer, 0, resizedBuffer, 0, length);
buffer = resizedBuffer;
}
return buffer;
}
public static boolean isSystemLibraryExisting(String libraryName) {
String[] extensions = new String[] { ".dll", ".so" };
String path = System.getProperty("java.library.path");
if (path == null) {
path = "";
} else if (!path.endsWith("/")) {
path += "/";
}
for (String extension : extensions) {
File libraryFile = new File(String.format("%s%s%s", path, libraryName, extension));
if (libraryFile.canExecute()) {
return true;
}
}
return false;
}
public static int signExtend(int value, int bits) {
int shift = Integer.SIZE - bits;
return (value << shift) >> shift;
}
public static int clip(int value, int min, int max) {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
public static float clipf(float value, float min, float max) {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
public static void fill(int a[][], int value) {
for (int i = 0; i < a.length; i++) {
Arrays.fill(a[i], value);
}
}
public static void fill(float a[], float value) {
Arrays.fill(a, value);
}
public static void fill(float a[][], float value) {
for (int i = 0; i < a.length; i++) {
Arrays.fill(a[i], value);
}
}
public static void fill(float a[][][], float value) {
for (int i = 0; i < a.length; i++) {
fill(a[i], value);
}
}
public static void fill(float a[][][][], float value) {
for (int i = 0; i < a.length; i++) {
fill(a[i], value);
}
}
public static long getReturnValue64(CpuState cpu) {
long low = cpu._v0;
long high = cpu._v1;
return (low & 0xFFFFFFFFL) | (high << 32);
}
public static int convertABGRtoARGB(int abgr) {
return (abgr & 0xFF00FF00) | ((abgr & 0x00FF0000) >> 16) | ((abgr & 0x000000FF) << 16);
}
public static void disableSslCertificateChecks() {
try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
Emulator.log.error(e);
} catch (KeyManagementException e) {
Emulator.log.error(e);
}
}
public static int getDefaultPortForProtocol(String protocol) {
if ("http".equals(protocol)) {
return 80;
}
if ("https".equals(protocol)) {
return 443;
}
return -1;
}
public static String[] merge(String[] a1, String[] a2) {
if (a1 == null) {
return a2;
}
if (a2 == null) {
return a1;
}
String[] a = new String[a1.length + a2.length];
System.arraycopy(a1, 0, a, 0, a1.length);
System.arraycopy(a2, 0, a, a1.length, a2.length);
return a;
}
public static InetAddress[] merge(InetAddress[] a1, InetAddress[] a2) {
if (a1 == null) {
return a2;
}
if (a2 == null) {
return a1;
}
InetAddress[] a = new InetAddress[a1.length + a2.length];
System.arraycopy(a1, 0, a, 0, a1.length);
System.arraycopy(a2, 0, a, a1.length, a2.length);
return a;
}
}