/*
* $Id: DefaultStoryFileHeader.java 536 2008-02-19 06:03:27Z weiju $
*
* Created on 2005/09/23
* Copyright 2005-2008 by Wei-ju Wu
* This file is part of The Z-machine Preservation Project (ZMPP).
*
* ZMPP 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.
*
* ZMPP 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 ZMPP. If not, see <http://www.gnu.org/licenses/>.
*/
package org.zmpp.vm;
import org.zmpp.base.Memory;
/**
* This is the default implementation of the StoryFileHeader interface.
*
* @author Wei-ju Wu
* @version 1.0
*/
public class DefaultStoryFileHeader implements StoryFileHeader {
/**
* The memory map.
*/
private Memory memory;
/**
* Constructor.
*
* @param memory a Memory object
*/
public DefaultStoryFileHeader(final Memory memory) {
this.memory = memory;
}
/**
* {@inheritDoc}
*/
public int getVersion() {
return memory.readUnsignedByte(0x00);
}
/**
* {@inheritDoc}
*/
public int getRelease() {
return memory.readUnsignedShort(0x02);
}
/**
* {@inheritDoc}
*/
public int getHighMemAddress() {
return memory.readUnsignedShort(0x04);
}
/**
* {@inheritDoc}
*/
public int getProgramStart() {
return memory.readUnsignedShort(0x06);
}
/**
* {@inheritDoc}
*/
public int getDictionaryAddress() {
return memory.readUnsignedShort(0x08);
}
/**
* {@inheritDoc}
*/
public int getObjectTableAddress() {
return memory.readUnsignedShort(0x0a);
}
/**
* {@inheritDoc}
*/
public int getGlobalsAddress() {
return memory.readUnsignedShort(0x0c);
}
/**
* {@inheritDoc}
*/
public int getStaticsAddress() {
return memory.readUnsignedShort(0x0e);
}
/**
* {@inheritDoc}
*/
public String getSerialNumber() {
return extractAscii(0x12, 6);
}
/**
* {@inheritDoc}
*/
public int getAbbreviationsAddress() {
return memory.readUnsignedShort(0x18);
}
/**
* {@inheritDoc}
*/
public int getFileLength() {
// depending on the story file version we have to multiply the
// file length in the header by a constant
int fileLength = memory.readUnsignedShort(0x1a);
if (getVersion() <= 3) {
fileLength *= 2;
} else if (getVersion() <= 5) {
fileLength *= 4;
} else {
fileLength *= 8;
}
return fileLength;
}
/**
* {@inheritDoc}
*/
public int getChecksum() {
return memory.readUnsignedShort(0x1c);
}
/**
* {@inheritDoc}
*/
public void setInterpreterNumber(final int number) {
memory.writeUnsignedByte(0x1e, (short) number);
}
/**
* {@inheritDoc}
*/
public int getInterpreterNumber() {
return memory.readUnsignedByte(0x1e);
}
/**
* {@inheritDoc}
*/
public void setInterpreterVersion(final int version) {
if (getVersion() == 4 || getVersion() == 5) {
memory.writeUnsignedByte(0x1f,
(short) String.valueOf(version).charAt(0));
} else {
memory.writeUnsignedByte(0x1f, (short) version);
}
}
/**
* {@inheritDoc}
*/
public void setScreenWidth(final int numChars) {
memory.writeUnsignedByte(0x21, (short) numChars);
}
/**
* {@inheritDoc}
*/
public void setScreenWidthUnits(final int units) {
memory.writeUnsignedShort(0x22, units);
}
/**
* {@inheritDoc}
*/
public int getScreenWidthUnits() {
return memory.readUnsignedShort(0x22);
}
/**
* {@inheritDoc}
*/
public int getScreenWidth() {
return memory.readUnsignedByte(0x21);
}
/**
* {@inheritDoc}
*/
public int getScreenHeight() {
return memory.readUnsignedByte(0x20);
}
/**
* {@inheritDoc}
*/
public int getScreenHeightUnits() {
return memory.readUnsignedShort(0x24);
}
/**
* {@inheritDoc}
*/
public void setScreenHeight(final int numLines) {
memory.writeUnsignedByte(0x20, (short) numLines);
}
/**
* {@inheritDoc}
*/
public void setScreenHeightUnits(final int units) {
memory.writeUnsignedShort(0x24, units);
}
/**
* {@inheritDoc}
*/
public int getRoutineOffset() {
return memory.readUnsignedShort(0x28);
}
/**
* {@inheritDoc}
*/
public int getStaticStringOffset() {
return memory.readUnsignedShort(0x2a);
}
/**
* {@inheritDoc}
*/
public int getDefaultBackgroundColor() {
return memory.readUnsignedByte(0x2c);
}
/**
* {@inheritDoc}
*/
public int getDefaultForegroundColor() {
return memory.readUnsignedByte(0x2d);
}
/**
* {@inheritDoc}
*/
public void setDefaultBackgroundColor(final int color) {
memory.writeUnsignedByte(0x2c, (short) color);
}
/**
* {@inheritDoc}
*/
public void setDefaultForegroundColor(final int color) {
memory.writeUnsignedByte(0x2d, (short) color);
}
/**
* {@inheritDoc}
*/
public void setStandardRevision(final int major, final int minor) {
memory.writeUnsignedByte(0x32, (short) major);
memory.writeUnsignedByte(0x33, (short) minor);
}
/**
* {@inheritDoc}
*/
public int getTerminatorsAddress() {
return memory.readUnsignedShort(0x2e);
}
/**
* {@inheritDoc}
*/
public void setFontWidth(final int units) {
if (getVersion() == 6) {
memory.writeUnsignedByte(0x27, (short) units);
} else {
memory.writeUnsignedByte(0x26, (short) units);
}
}
/**
* {@inheritDoc}
*/
public int getFontWidth() {
return (getVersion() == 6) ? memory.readUnsignedByte(0x27)
: memory.readUnsignedByte(0x26);
}
/**
* {@inheritDoc}
*/
public void setFontHeight(final int units) {
if (getVersion() == 6) {
memory.writeUnsignedByte(0x26, (short) units);
} else {
memory.writeUnsignedByte(0x27, (short) units);
}
}
/**
* {@inheritDoc}
*/
public int getFontHeight() {
return (getVersion() == 6) ? memory.readUnsignedByte(0x26)
: memory.readUnsignedByte(0x27);
}
/**
* {@inheritDoc}
*/
public int getCustomAlphabetTable() {
return memory.readUnsignedShort(0x34);
}
/**
* {@inheritDoc}
*/
public void setMouseCoordinates(final int x, final int y) {
// check the extension table
final int extTable = memory.readUnsignedShort(0x36);
if (extTable > 0) {
final int numwords = memory.readUnsignedShort(extTable);
if (numwords >= 1) {
memory.writeUnsignedShort(extTable + 2, x);
}
if (numwords >= 2) {
memory.writeUnsignedShort(extTable + 4, y);
}
}
}
/**
* {@inheritDoc}
*/
public int getCustomAccentTable() {
// check the extension table
int result = 0;
final int extTable = memory.readUnsignedShort(0x36);
if (extTable > 0) {
final int numwords = memory.readUnsignedShort(extTable);
if (numwords >= 3) {
result = memory.readUnsignedShort(extTable + 6);
}
}
return result;
}
// ***********************************************************************
// ****** Attributes
// **********************************
/**
* {@inheritDoc}
*/
public void setEnabled(final Attribute attribute, final boolean flag) {
switch (attribute) {
case DEFAULT_FONT_IS_VARIABLE:
setDefaultFontIsVariablePitch(flag);
break;
case TRANSCRIPTING:
setTranscripting(flag);
break;
case FORCE_FIXED_FONT:
setForceFixedFont(flag);
break;
case SUPPORTS_TIMED_INPUT:
setTimedInputAvailable(flag);
break;
case SUPPORTS_FIXED_FONT:
setFixedFontAvailable(flag);
break;
case SUPPORTS_BOLD:
setBoldFaceAvailable(flag);
break;
case SUPPORTS_ITALIC:
setItalicAvailable(flag);
break;
case SUPPORTS_SCREEN_SPLITTING:
setScreenSplittingAvailable(flag);
break;
case SUPPORTS_STATUSLINE:
setStatusLineAvailable(flag);
break;
case SUPPORTS_COLOURS:
setSupportsColours(flag);
default:
break;
}
}
/**
* {@inheritDoc}
*/
public boolean isEnabled(final Attribute attribute) {
switch (attribute) {
case TRANSCRIPTING:
return isTranscriptingOn();
case FORCE_FIXED_FONT:
return forceFixedFont();
case SCORE_GAME:
return isScoreGame();
case DEFAULT_FONT_IS_VARIABLE:
return defaultFontIsVariablePitch();
case USE_MOUSE:
return useMouse();
default:
return false;
}
}
/**
* {@inheritDoc}
*/
public void setOutputStream3TextWidth(int units) {
memory.writeUnsignedShort(0x30, units);
}
/**
* {@inheritDoc}
*/
public String toString() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 55; i++) {
builder.append(String.format("Addr: %02x Byte: %02x\n", i, memory.readUnsignedByte(i)));
}
return builder.toString();
}
// ************************************************************************
// ****** Private section
// *******************************
/**
* Extract an ASCII string of the specified length starting at the specified
* address.
*
* @param address the start address
* @param length the length of the ASCII string
* @return the ASCII string at the specified position
*/
private String extractAscii(final int address, final int length) {
final StringBuilder builder = new StringBuilder();
for (int i = address; i < address + length; i++) {
builder.append((char) memory.readUnsignedByte(i));
}
return builder.toString();
}
private void setTranscripting(final boolean flag) {
int flags = memory.readUnsignedByte(0x10);
flags = flag ? (flags | 1) : (flags & 0xfe);
memory.writeUnsignedByte(0x10, (short) flags);
}
private boolean isTranscriptingOn() {
return (memory.readUnsignedByte(0x10) & 1) > 0;
}
private boolean forceFixedFont() {
return (memory.readUnsignedByte(0x10) & 2) > 0;
}
private void setForceFixedFont(final boolean flag) {
int flags = memory.readUnsignedByte(0x10);
flags = flag ? (flags | 2) : (flags & 0xfd);
memory.writeUnsignedByte(0x10, (short) flags);
}
private void setTimedInputAvailable(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 128) : (flags & 0x7f);
memory.writeUnsignedByte(0x01, (short) flags);
}
private boolean isScoreGame() {
return (memory.readUnsignedByte(0x01) & 2) == 0;
}
private void setFixedFontAvailable(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 16) : (flags & 0xef);
memory.writeUnsignedByte(0x01, (short) flags);
}
private void setBoldFaceAvailable(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 4) : (flags & 0xfb);
memory.writeUnsignedByte(0x01, (short) flags);
}
private void setItalicAvailable(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 8) : (flags & 0xf7);
memory.writeUnsignedByte(0x01, (short) flags);
}
private void setScreenSplittingAvailable(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 32) : (flags & 0xdf);
memory.writeUnsignedByte(0x01, (short) flags);
}
private void setStatusLineAvailable(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 16) : (flags & 0xef);
memory.writeUnsignedByte(0x01, (short) flags);
}
private void setDefaultFontIsVariablePitch(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 64) : (flags & 0xbf);
memory.writeUnsignedByte(0x01, (short) flags);
}
private boolean defaultFontIsVariablePitch() {
return (memory.readUnsignedByte(0x01) & 64) > 0;
}
private void setSupportsColours(final boolean flag) {
int flags = memory.readUnsignedByte(0x01);
flags = flag ? (flags | 1) : (flags & 0xfe);
memory.writeUnsignedByte(0x01, (short) flags);
}
private boolean useMouse() {
return (memory.readUnsignedByte(0x10) & 32) > 0;
}
}