/*
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.filesystems.umdiso;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import jpcsp.Emulator;
import org.apache.log4j.Logger;
/**
* Implements a SectorDevice where the sectors are stored in random
* order in another SectorDevice. Not all the sectors need to be available.
* Writing a new sectors is also supported by adding the new sectors at the
* end of the mapped SectorDevice.
*
* @author gid15
*
*/
public class MappingSectorDevice implements ISectorDevice {
protected static Logger log = Emulator.log;
protected static final int freeSectorNumber = -1;
protected ISectorDevice sectorDevice;
protected int[] sectorMapping;
protected File mappingFile;
protected boolean sectorMappingDirty;
public MappingSectorDevice(ISectorDevice sectorDevice, File mappingFile) {
this.sectorDevice = sectorDevice;
this.mappingFile = mappingFile;
// Create a default empty mapping in case the mapping file cannot be read
sectorMapping = new int[0];
sectorMappingDirty = true;
try {
readMappingFile();
} catch (FileNotFoundException e) {
log.debug("Mapping file not found, creating it", e);
} catch (IOException e) {
log.warn("Error reading mapping file", e);
}
}
public void setNumSectors(int numSectors) throws IOException {
int previousNumSectors = getNumSectors();
if (numSectors != previousNumSectors) {
// Shrink or extend the sectorMapping array
sectorMapping = Arrays.copyOf(sectorMapping, numSectors);
if (numSectors > previousNumSectors) {
// Extending the sector mapping with -1 values
Arrays.fill(sectorMapping, previousNumSectors, sectorMapping.length, freeSectorNumber);
}
sectorMappingDirty = true;
}
}
protected void readMappingFile() throws IOException {
InputStream mappingFileReader = new FileInputStream(mappingFile);
int mappingSize = (int) (mappingFile.length() / 4);
sectorMapping = new int[mappingSize];
byte[] buffer = new byte[4];
IntBuffer intBuffer = ByteBuffer.wrap(buffer).asIntBuffer();
for (int i = 0; i < mappingSize; i++) {
mappingFileReader.read(buffer);
sectorMapping[i] = intBuffer.get(0);
}
mappingFileReader.close();
sectorMappingDirty = false;
}
protected void writeMappingFile() throws IOException {
OutputStream mappingFileWriter = new FileOutputStream(mappingFile);
byte[] buffer = new byte[4];
IntBuffer intBuffer = ByteBuffer.wrap(buffer).asIntBuffer();
int numSectors = getNumSectors();
for (int i = 0; i < numSectors; i++) {
intBuffer.put(0, sectorMapping[i]);
mappingFileWriter.write(buffer);
}
mappingFileWriter.close();
sectorMappingDirty = false;
}
protected int mapSector(int sectorNumber) throws IOException {
if (sectorNumber >= 0 && sectorNumber < getNumSectors()) {
return sectorMapping[sectorNumber];
}
return freeSectorNumber;
}
protected int getFreeSectorNumber() throws IOException {
int numSectors = getNumSectors();
for (int i = 0; i < numSectors; i++) {
if (sectorMapping[i] == freeSectorNumber) {
return i;
}
}
return freeSectorNumber;
}
protected void setSectorMapping(int sectorNumber, int mappedSectorNumber) {
sectorMapping[sectorNumber] = mappedSectorNumber;
sectorMappingDirty = true;
}
@Override
public void readSector(int sectorNumber, byte[] buffer, int offset) throws IOException {
int mappedSectorNumber = mapSector(sectorNumber);
if (mappedSectorNumber >= 0) {
sectorDevice.readSector(mappedSectorNumber, buffer, offset);
} else {
Arrays.fill(buffer, offset, offset + sectorLength, (byte) 0);
}
}
@Override
public int readSectors(int sectorNumber, int numberSectors, byte[] buffer, int offset) throws IOException {
for (int i = 0; i < numberSectors; i++) {
readSector(sectorNumber + i, buffer, offset + i * sectorLength);
}
return numberSectors;
}
@Override
public int getNumSectors() throws IOException {
return sectorMapping.length;
}
@Override
public void close() throws IOException {
// Write back the mapping file if it has been changed
if (sectorMappingDirty) {
writeMappingFile();
}
sectorDevice.close();
}
@Override
public void writeSector(int sectorNumber, byte[] buffer, int offset) throws IOException {
int freeSectorNumber = getFreeSectorNumber();
if (freeSectorNumber < 0) {
throw new IOException(String.format("Sector Device '%s' is full", mappingFile));
}
sectorDevice.writeSector(freeSectorNumber, buffer, offset);
setSectorMapping(freeSectorNumber, sectorNumber);
}
@Override
public void writeSectors(int sectorNumber, int numberSectors, byte[] buffer, int offset) throws IOException {
for (int i = 0; i < numberSectors; i++) {
writeSector(sectorNumber + i, buffer, offset + i * sectorLength);
}
}
}