/*
DiskMemoryEntry.java
(c) 2005-2015 Edward Swartz
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
*/
package v9t9.engine.memory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Arrays;
import org.apache.log4j.Logger;
import v9t9.common.events.IEventNotifier;
import v9t9.common.events.NotifyEvent.Level;
import v9t9.common.files.URIUtils;
import v9t9.common.memory.MemoryEntryInfo;
import v9t9.common.memory.StoredMemoryEntryInfo;
import ejs.base.settings.ISettingSection;
import ejs.base.utils.FileUtils;
/**
* @author ejs
*/
public class DiskMemoryEntry extends MemoryEntry {
private static Logger log = Logger.getLogger(DiskMemoryEntry.class);
/** file path */
private String filename;
/** is the file loaded yet? */
private boolean bLoaded;
/** is the data dirty? */
private boolean bDirty;
private StoredMemoryEntryInfo storedInfo;
private MemoryEntryInfo info;
public DiskMemoryEntry() {
// only to be used when reconstructing
super();
}
DiskMemoryEntry(MemoryEntryInfo info, MemoryArea area, StoredMemoryEntryInfo storedInfo) {
super(info.getName(), info.getDomain(storedInfo.memory), info.getAddress(),
Math.min(Math.abs(info.getSize()), storedInfo.size - storedInfo.fileoffs), area);
this.storedInfo = storedInfo;
this.info = storedInfo.createMemoryEntryInfo();
this.locator = storedInfo.locator;
/* this should be set up already */
if (area == null) {
throw new AssertionError();
}
this.filename = storedInfo.fileName;
this.bDirty = false;
this.area = area;
}
/**
* @return the storedInfo
*/
public StoredMemoryEntryInfo getStoredInfo() {
return storedInfo;
}
/**
* @return the info
*/
public MemoryEntryInfo getInfo() {
return info;
}
/* (non-Javadoc)
* @see v9t9.common.memory.MemoryEntry#isVolatile()
*/
@Override
public boolean isVolatile() {
// don't destroy, ever
return false;
}
/* (non-Javadoc)
* @see v9t9.MemoryEntry#load()
*/
@Override
public void load() throws IOException {
super.load();
if (!bLoaded) {
URI uri = null;
if (storedInfo != null) {
uri = locator.findFile(storedInfo.settings, info);
}
if (uri == null) {
throw new IOException("Failed to locate " + (storedInfo != null ? storedInfo.uri : info.getFilename()) + " for " + this);
}
if (!info.isStored()) {
filename = locator.splitFileName(uri).second;
}
int filesize = locator.getContentLength(uri);
if (filesize == 0) {
if (!locator.exists(uri))
throw new IOException("Can no longer locate " + uri);
}
filesize = fixupFileSize(uri, filesize);
InputStream is = locator.createInputStream(uri);
FileUtils.skipFully(is, storedInfo.fileoffs);
byte[] data = FileUtils.readInputStreamContentsAndClose(is,
filesize);
area.copyFromBytes(data);
bLoaded = true;
// see if it has symbols
String symbolFileName = getSymbolFileName();
URI symfile = locator.findFile(symbolFileName);
if (symfile != null) {
try {
loadSymbolsAndClose(locator.createInputStream(symfile));
} catch (IOException e) {
log.error("failed to load symbols from " + symfile, e);
}
}
}
}
/**
* @param uri
* @param filesize
* @return
* @throws IOException
*/
private int fixupFileSize(URI uri, int filesize) throws IOException {
int size = info.getSize();
try {
try {
filesize = storedInfo.locator.getContentLength(uri);
} catch (IllegalArgumentException e) {
filesize = locator.getContentLength(uri);
}
// for large files selected, e.g., by accident
if (size > 0 && filesize > size) {
filesize = size;
} else if (size < 0 && filesize > -size) {
filesize = -size;
} else if (filesize + info.getAddress() > 0x10000) {
filesize = 0x10000 - info.getAddress();
}
} catch (IOException e) {
if (info.isStored()) {
filesize = size; // not created yet
} else {
throw e;
}
}
return filesize;
}
public void setDirty(boolean dirty) {
bDirty = dirty;
}
/* (non-Javadoc)
* @see v9t9.MemoryEntry#save()
*/
@Override
public void save() throws IOException {
if (info != null && info.isStored() && bDirty) {
byte[] data = new byte[getSize()];
area.copyToBytes(data);
URI uri = locator.getWriteURI(filename);
URI backup = URI.create(uri.toString() + "~");
// only make backup if the current backup differs from the new contents
boolean isNew = true;
byte[] origData = null;
if (locator.exists(uri)) {
if (locator.exists(backup)) {
try {
origData = FileUtils.readInputStreamContentsAndClose(
locator.createInputStream(backup), getSize());
isNew = !Arrays.equals(data, origData);
} catch (IOException e) {
// ignore
}
} else {
try {
origData = FileUtils.readInputStreamContentsAndClose(
locator.createInputStream(uri), getSize());
} catch (IOException e) {
// ignore
}
}
}
if (isNew) {
if (origData != null) {
try {
FileUtils.writeOutputStreamContentsAndClose(
locator.createOutputStream(backup), origData, Math.min(getSize(), origData.length));
} catch (IOException e) {
e.printStackTrace();
// ignore
}
}
}
FileUtils.writeOutputStreamContentsAndClose(
locator.createOutputStream(uri), data, getSize());
bDirty = false;
}
super.save();
}
public void overwrite() throws FileNotFoundException, IOException {
byte[] data = new byte[getSize()];
area.copyToBytes(data);
URI uri = locator.findFile(getFilepath());
FileUtils.writeOutputStreamContentsAndClose(
locator.createOutputStream(uri),data, getSize());
}
@Override
public void unload() {
super.unload();
if (isStorable())
bLoaded = false;
else
bLoaded = area != null && storedInfo != null;
}
public String getFilepath() {
return filename;
}
public String getSymbolFileName() {
if (filename == null)
return null;
int idx = filename.lastIndexOf('.');
if (idx >= 0) {
return filename.substring(0, idx) + ".sym";
} else {
return filename + ".sym";
}
}
@Override
public void saveState(ISettingSection section) {
super.saveState(section);
section.put("FileName", filename);
section.put("FileMD5", storedInfo.md5);
if (storedInfo == null)
return;
section.put("FileOffs", storedInfo.fileoffs);
//section.put("FileSize", storedInfo.filesize);
section.put("Storable", storedInfo.info.isStored());
}
/* (non-Javadoc)
* @see v9t9.engine.memory.MemoryEntry#loadFields(org.eclipse.jface.dialogs.IDialogSettings)
*/
@Override
protected void loadFields(IEventNotifier notifier, ISettingSection section) {
super.loadFields(notifier, section);
if (info == null) {
info = (isWordAccess() ? MemoryEntryInfoBuilder.wordMemoryEntry() : MemoryEntryInfoBuilder.byteMemoryEntry())
.withFilename(section.get("FileName") != null ? section.get("FileName") : section.get("FilePath"))
.withFileMD5(section.get("FileMD5"))
.withFileMD5Algorithm(section.get("FileMD5Algorithm"))
.withOffset(section.getInt("FileOffs"))
.withSize(section.getInt("Size"))
.withAddress(getAddr())
.withDomain(getDomain().getIdentifier())
.storable(section.getBoolean("Storable")).create(getName());
}
try {
storedInfo = memory.getMemoryEntryFactory().resolveMemoryEntry(info);
} catch (IOException e) {
// if failed to load essential ROM/GROM, just fill in from model
for (MemoryEntryInfo rinfo : memory.getModel().getRequiredRomMemoryEntries()) {
if (rinfo.getDomainName().equals(info.getDomainName())
&& rinfo.getAddress() == info.getAddress()) {
try {
storedInfo = memory.getMemoryEntryFactory().resolveMemoryEntry(rinfo);
String message = "Loaded '"
+ URIUtils.splitFileName(storedInfo.uri).second
+ "' instead of missing ROM file '"
+ info.getFilename() + "'";
if (notifier != null) {
notifier.notifyEvent(null, Level.WARNING, message);
} else {
System.err.println(message);
}
info = rinfo;
} catch (IOException e2) {
if (notifier != null) {
notifier.notifyEvent(null, Level.ERROR,
e2 instanceof FileNotFoundException ?
"Failed to load ROM file '" + info.getResolvedFilename(null) +
"' and fallback ROM file '" + rinfo.getFilename() + "'"
: e2.getMessage());
} else {
e2.printStackTrace();
}
}
}
}
}
}
/* (non-Javadoc)
* @see v9t9.engine.memory.MemoryEntry#loadMemory(v9t9.common.events.IEventNotifier, ejs.base.settings.ISettingSection)
*/
@Override
public void loadMemory(IEventNotifier notifier, ISettingSection section) throws IOException {
bLoaded = false;
info = null;
super.loadMemory(notifier, section);
try {
load();
} catch (IOException e) {
log.error("failed to load memory for " + this, e);
notifier.notifyEvent(this, Level.ERROR, e.getMessage());
}
}
/**
* @param section
*/
protected void loadMemoryContents(ISettingSection section) throws IOException {
if (storedInfo != null) {
try {
area = (MemoryArea) memory.getMemoryEntryFactory().createMemoryArea(storedInfo.info);
} catch (IOException e) {
e.printStackTrace();
}
}
bLoaded = false;
load();
}
@Override
public String getUniqueName() {
return filename;
}
/**
* @return the fileoffs
*/
public int getFileOffs() {
return storedInfo.fileoffs;
}
/**
* @return the bLoaded
*/
public boolean isLoaded() {
return bLoaded;
}
/**
* @return the bStorable
*/
public boolean isStorable() {
return storedInfo.info.isStored();
}
/**
* @param b
*/
public void setStorable(boolean b) {
info.getProperties().put(MemoryEntryInfo.STORED, b);
}
}