/*
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.VFS;
import static jpcsp.HLE.VFS.AbstractVirtualFileSystem.IO_ERROR;
import jpcsp.HLE.TPointer;
import jpcsp.util.Utilities;
/**
* Provide a IVirtualFile interface by reading from another virtual file
* in blocks on a given size.
* No write access is supported.
*
* @author gid15
*
*/
public class BufferedVirtualFile extends AbstractProxyVirtualFile {
private byte[] buffer;
private int bufferIndex;
private int bufferLength;
protected BufferedVirtualFile() {
}
public BufferedVirtualFile(IVirtualFile vFile, int bufferSize) {
setBufferedVirtualFile(vFile, bufferSize);
}
protected void setBufferedVirtualFile(IVirtualFile vFile, int bufferSize) {
setProxyVirtualFile(vFile);
buffer = new byte[bufferSize];
bufferIndex = 0;
bufferLength = 0;
}
private void copyFromBuffer(int outputAddr, int length) {
if (length <= 0) {
return;
}
Utilities.writeBytes(outputAddr, length, buffer, bufferIndex);
bufferIndex += length;
}
private void copyFromBuffer(byte[] output, int offset, int length) {
if (length <= 0) {
return;
}
System.arraycopy(buffer, bufferIndex, output, offset, length);
bufferIndex += length;
}
private void checkPopulateBuffer() {
if (bufferIndex < bufferLength) {
return;
}
if (bufferLength > 0) {
bufferIndex = 0;
}
bufferLength = vFile.ioRead(buffer, 0, buffer.length);
}
@Override
public int ioRead(TPointer outputPointer, int outputLength) {
if (bufferLength < 0) {
return bufferLength;
}
int readLength = 0;
while (bufferLength >= 0 && readLength < outputLength) {
checkPopulateBuffer();
int length = Math.min(bufferLength - bufferIndex, outputLength - readLength);
copyFromBuffer(outputPointer.getAddress() + readLength, length);
readLength += length;
}
return readLength;
}
@Override
public int ioRead(byte[] outputBuffer, int outputOffset, int outputLength) {
if (bufferLength < 0) {
return bufferLength;
}
int readLength = 0;
while (bufferLength >= 0 && readLength < outputLength) {
checkPopulateBuffer();
int length = Math.min(bufferLength - bufferIndex, outputLength - readLength);
copyFromBuffer(outputBuffer, outputOffset + readLength, length);
readLength += length;
}
return readLength;
}
@Override
public int ioWrite(TPointer inputPointer, int inputLength) {
// Write not supported
return IO_ERROR;
}
@Override
public int ioWrite(byte[] inputBuffer, int inputOffset, int inputLength) {
// Write not supported
return IO_ERROR;
}
@Override
public long ioLseek(long offset) {
long virtualFileOffset = (offset / buffer.length) * buffer.length;
long result = vFile.ioLseek(virtualFileOffset);
if (result == IO_ERROR) {
return result;
}
bufferLength = 0;
bufferIndex = 0;
if (offset > virtualFileOffset) {
bufferIndex = (int) (offset - virtualFileOffset);
}
return offset;
}
@Override
public long getPosition() {
if (bufferLength <= 0) {
return vFile.getPosition();
}
return vFile.getPosition() - bufferLength + bufferIndex;
}
@Override
public IVirtualFile duplicate() {
IVirtualFile vFileDuplicate = vFile.duplicate();
if (vFileDuplicate == null) {
return null;
}
BufferedVirtualFile dup = new BufferedVirtualFile(vFileDuplicate, buffer.length);
dup.ioLseek(getPosition());
return dup;
}
}