/*
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.HLE.kernel.types;
import java.nio.charset.Charset;
import jpcsp.Memory;
import jpcsp.HLE.ITPointerBase;
import jpcsp.memory.IMemoryReader;
import jpcsp.memory.IMemoryWriter;
import jpcsp.memory.MemoryReader;
import jpcsp.memory.MemoryWriter;
import jpcsp.util.Utilities;
public abstract class pspAbstractMemoryMappedStructure {
private final static int unknown = 0x11111111;
private final static Charset charset16 = Charset.forName("UTF-16LE");
private int baseAddress;
private int maxSize = Integer.MAX_VALUE;
private int offset;
protected Memory mem;
public abstract int sizeof();
protected abstract void read();
protected abstract void write();
private void start(Memory mem) {
this.mem = mem;
offset = 0;
}
protected void start(Memory mem, int address) {
start(mem);
baseAddress = address;
}
private void start(Memory mem, int address, int maxSize) {
start(mem, address);
this.maxSize = maxSize;
}
public void setMaxSize(int maxSize) {
// maxSize is an unsigned int
if (maxSize < 0) {
maxSize = Integer.MAX_VALUE;
}
this.maxSize = maxSize;
}
public void read(Memory mem, int address) {
start(mem, address);
if (address != 0) {
read();
}
}
public void read(ITPointerBase pointer) {
read(pointer, 0);
}
public void read(ITPointerBase pointer, int offset) {
read(Memory.getInstance(), pointer.getAddress() + offset);
}
public void write(Memory mem, int address) {
start(mem, address);
write();
}
public void write(ITPointerBase pointer) {
write(pointer, 0);
}
public void write(ITPointerBase pointer, int offset) {
write(Memory.getInstance(), pointer.getAddress() + offset);
}
public void write(Memory mem) {
start(mem);
write();
}
protected int read8() {
int value;
if (offset >= maxSize) {
value = 0;
} else {
value = mem.read8(baseAddress + offset);
}
offset += 1;
return value;
}
protected void read8Array(byte[] array) {
for (int i = 0; array != null && i < array.length; i++) {
array[i] = (byte) read8();
}
}
protected void align16() {
offset = (offset + 1) & ~1;
}
protected void align32() {
offset = (offset + 3) & ~3;
}
protected int read16() {
align16();
int value;
if (offset >= maxSize) {
value = 0;
} else {
value = mem.read16(baseAddress + offset);
}
offset += 2;
return value;
}
protected int readUnaligned16() {
int n0 = read8();
int n1 = read8();
return (n1 << 8) | n0;
}
protected int read32() {
align32();
int value;
if (offset >= maxSize) {
value = 0;
} else {
value = mem.read32(baseAddress + offset);
}
offset += 4;
return value;
}
protected int readUnaligned32() {
int n01 = readUnaligned16();
int n23 = readUnaligned16();
return (n23 << 16) | n01;
}
protected long read64() {
align32();
long value;
if (offset >= maxSize) {
value = 0;
} else {
value = mem.read64(baseAddress + offset);
}
offset += 8;
return value;
}
protected void read32Array(int[] array) {
for (int i = 0; array != null && i < array.length; i++) {
array[i] = read32();
}
}
protected boolean readBoolean() {
int value = read8();
return (value != 0);
}
protected void readBooleanArray(boolean[] array) {
for (int i = 0; array != null && i < array.length; i++) {
array[i] = readBoolean();
}
}
protected float readFloat() {
int int32 = read32();
return Float.intBitsToFloat(int32);
}
protected void readFloatArray(float[] array) {
for (int i = 0; array != null && i < array.length; i++) {
array[i] = readFloat();
}
}
protected void readFloatArray(float[][] array) {
for (int i = 0; array != null && i < array.length; i++) {
readFloatArray(array[i]);
}
}
protected void readUnknown(int length) {
offset += length;
}
protected String readStringNZ(int n) {
String s;
if (offset >= maxSize) {
s = null;
} else {
s = Utilities.readStringNZ(mem, baseAddress + offset, n);
}
offset += n;
return s;
}
protected String readStringZ(int addr) {
if (addr == 0) {
return null;
}
return Utilities.readStringZ(mem, addr);
}
protected String readStringUTF16NZ(int n) {
StringBuilder s = new StringBuilder();
while (n > 0) {
int char16 = read16();
n -= 2;
if (char16 == 0) {
break;
}
byte[] bytes = new byte[2];
bytes[0] = (byte) char16;
bytes[1] = (byte) (char16 >> 8);
s.append(new String(bytes, charset16));
}
if (n > 0) {
readUnknown(n);
}
return s.toString();
}
protected int writeStringUTF16NZ(int n, String s) {
byte[] bytes = s.getBytes(charset16);
int length = 0;
if (bytes != null) {
length = bytes.length;
for (int i = 0; i < bytes.length && n > 0; i++, n--) {
write8(bytes[i]);
}
}
// Write trailing '\0\0'
if (n > 0) {
write8((byte) 0);
n--;
}
if (n > 0) {
write8((byte) 0);
n--;
}
if (n > 0) {
offset += n;
}
return length;
}
/**
* Read a string in UTF16, until '\0\0'
* @param addr address of the string
* @return the string
*/
protected String readStringUTF16Z(int addr) {
if (addr == 0) {
return null;
}
IMemoryReader memoryReader = MemoryReader.getMemoryReader(addr, 2);
StringBuilder s = new StringBuilder();
while (true) {
int char16 = memoryReader.readNext();
if (char16 == 0) {
break;
}
byte[] bytes = new byte[2];
bytes[0] = (byte) char16;
bytes[1] = (byte) (char16 >> 8);
s.append(new String(bytes, charset16));
}
return s.toString();
}
/**
* Write a string in UTF16, including a trailing '\0\0'
* @param addr address where to write the string
* @param s the string to write
* @return the number of bytes written (not including the trailing '\0\0')
*/
protected int writeStringUTF16Z(int addr, String s) {
if (addr == 0 || s == null) {
return 0;
}
byte[] bytes = s.getBytes(charset16);
if (bytes == null) {
return 0;
}
IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(addr, bytes.length + 2, 1);
for (int i = 0; i < bytes.length; i++) {
memoryWriter.writeNext(bytes[i] & 0xFF);
}
// Write trailing '\0\0'
memoryWriter.writeNext(0);
memoryWriter.writeNext(0);
memoryWriter.flush();
return bytes.length;
}
protected void read(pspAbstractMemoryMappedStructure object) {
if (object == null) {
return;
}
if (offset < maxSize) {
object.start(mem, baseAddress + offset, maxSize - offset);
object.read();
}
offset += object.sizeof();
}
protected void write8(byte data) {
if (offset < maxSize) {
mem.write8(baseAddress + offset, data);
}
offset += 1;
}
protected void write8Array(byte[] array) {
for (int i = 0; array != null && i < array.length; i++) {
write8(array[i]);
}
}
protected void write16(short data) {
align16();
if (offset < maxSize) {
mem.write16(baseAddress + offset, data);
}
offset += 2;
}
protected void writeUnaligned16(short data) {
write8((byte) data);
write8((byte) (data >>> 8));
}
protected void write32(int data) {
align32();
if (offset < maxSize) {
mem.write32(baseAddress + offset, data);
}
offset += 4;
}
protected void writeUnaligned32(int data) {
writeUnaligned16((short) data);
writeUnaligned16((short) (data >>> 16));
}
protected void write64(long data) {
align32();
if (offset < maxSize) {
mem.write64(baseAddress + offset, data);
}
offset += 8;
}
protected void write32Array(int[] array) {
for (int i = 0; array != null && i < array.length; i++) {
write32(array[i]);
}
}
protected void writeBoolean(boolean data) {
write8(data ? (byte) 1 : (byte) 0);
}
protected void writeBooleanArray(boolean[] array) {
for (int i = 0; array != null && i < array.length; i++) {
writeBoolean(array[i]);
}
}
protected void writeFloat(float data) {
int int32 = Float.floatToIntBits(data);
write32(int32);
}
protected void writeFloatArray(float[] array) {
for (int i = 0; array != null && i < array.length; i++) {
writeFloat(array[i]);
}
}
protected void writeFloatArray(float[][] array) {
for (int i = 0; array != null && i < array.length; i++) {
writeFloatArray(array[i]);
}
}
protected void writeUnknown(int length) {
for (int i = 0; i < length; i++) {
write8((byte) unknown);
}
}
protected void writeSkip(int length) {
offset += length;
}
protected void writeStringN(int n, String s) {
if (offset < maxSize) {
Utilities.writeStringNZ(mem, baseAddress + offset, n, s);
// A NULL-byte has only been written at the end of the string
// when enough space was available.
}
offset += n;
}
protected void writeStringNZ(int n, String s) {
if (offset < maxSize) {
Utilities.writeStringNZ(mem, baseAddress + offset, n - 1, s);
// Write always a NULL-byte at the end of the string.
mem.write8(baseAddress + offset + n - 1, (byte) 0);
}
offset += n;
}
protected void writeStringZ(String s, int addr) {
if (s != null && addr != 0) {
Utilities.writeStringZ(mem, addr, s);
}
}
protected void write(pspAbstractMemoryMappedStructure object) {
if (object == null) {
return;
}
if (offset < maxSize) {
object.start(mem, baseAddress + offset, maxSize - offset);
object.write();
}
offset += object.sizeof();
}
protected int getOffset() {
return offset;
}
public int getBaseAddress() {
return baseAddress;
}
public boolean isNull() {
return baseAddress == 0;
}
public boolean isNotNull() {
return baseAddress != 0;
}
protected int endianSwap16(short data) {
return Short.reverseBytes(data) & 0xFFFF;
}
protected int endianSwap32(int data) {
return Integer.reverseBytes(data);
}
@Override
public String toString() {
return String.format("0x%08X", getBaseAddress());
}
}