Modularise! Also add some unit tests.

This commit is contained in:
Major-
2015-05-26 13:49:27 +01:00
parent 902a203861
commit e4778105f5
658 changed files with 1532 additions and 1004 deletions
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>apollo</groupId>
<artifactId>org.apollo</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>cache</artifactId>
<version>0.0.1</version>
<name>Apollo Cache</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<sourceDirectory>src/main</sourceDirectory>
<testSourceDirectory>src/test</testSourceDirectory>
</build>
<dependencies>
<dependency>
<groupId>apollo</groupId>
<artifactId>util</artifactId>
<version>0.0.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
+49
View File
@@ -0,0 +1,49 @@
package org.apollo.cache;
/**
* A class which points to a file in the cache.
*
* @author Graham
*/
public final class FileDescriptor {
/**
* The file id.
*/
private final int file;
/**
* The file type.
*/
private final int type;
/**
* Creates the file descriptor.
*
* @param type The file type.
* @param file The file id.
*/
public FileDescriptor(int type, int file) {
this.type = type;
this.file = file;
}
/**
* Gets the file id.
*
* @return The file id.
*/
public int getFile() {
return file;
}
/**
* Gets the file type.
*
* @return The file type.
*/
public int getType() {
return type;
}
}
@@ -0,0 +1,42 @@
package org.apollo.cache;
/**
* Holds file system related constants.
*
* @author Graham
*/
public final class FileSystemConstants {
/**
* The number of archives in cache 0.
*/
public static final int ARCHIVE_COUNT = 9;
/**
* The size of a chunk.
*/
public static final int CHUNK_SIZE = 512;
/**
* The size of a header.
*/
public static final int HEADER_SIZE = 8;
/**
* The size of a block.
*/
public static final int BLOCK_SIZE = HEADER_SIZE + CHUNK_SIZE;
/**
* The size of an index.
*/
public static final int INDEX_SIZE = 6;
/**
* Default private constructor to prevent instantiation.
*/
private FileSystemConstants() {
}
}
+67
View File
@@ -0,0 +1,67 @@
package org.apollo.cache;
import com.google.common.base.Preconditions;
/**
* An {@link Index} points to a file in the {@code main_file_cache.dat} file.
*
* @author Graham
*/
public final class Index {
/**
* Decodes a buffer into an index.
*
* @param buffer The buffer.
* @return The decoded {@link Index}.
* @throws IllegalArgumentException If the buffer length is invalid.
*/
public static Index decode(byte[] buffer) {
Preconditions.checkArgument(buffer.length == FileSystemConstants.INDEX_SIZE, "Incorrect buffer length.");
int size = (buffer[0] & 0xFF) << 16 | (buffer[1] & 0xFF) << 8 | buffer[2] & 0xFF;
int block = (buffer[3] & 0xFF) << 16 | (buffer[4] & 0xFF) << 8 | buffer[5] & 0xFF;
return new Index(size, block);
}
/**
* The first block of the file.
*/
private final int block;
/**
* The size of the file.
*/
private final int size;
/**
* Creates the index.
*
* @param size The size of the file.
* @param block The first block of the file.
*/
public Index(int size, int block) {
this.size = size;
this.block = block;
}
/**
* Gets the first block of the file.
*
* @return The first block of the file.
*/
public int getBlock() {
return block;
}
/**
* Gets the size of the file.
*
* @return The size of the file.
*/
public int getSize() {
return size;
}
}
+302
View File
@@ -0,0 +1,302 @@
package org.apollo.cache;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.zip.CRC32;
import com.google.common.base.Preconditions;
/**
* A file system based on top of the operating system's file system. It consists of a data file and index files. Index
* files point to blocks in the data file, which contains the actual data.
*
* @author Graham
*/
public final class IndexedFileSystem implements Closeable {
/**
* The cached CRC table.
*/
private ByteBuffer crcTable;
/**
* The {@link #crcTable} represented as an {@code int} array.
*/
private int[] crcs;
/**
* The data file.
*/
private RandomAccessFile data;
/**
* The index files.
*/
private final RandomAccessFile[] indices = new RandomAccessFile[256];
/**
* Read only flag.
*/
private final boolean readOnly;
/**
* Creates the file system with the specified base directory.
*
* @param base The base directory.
* @param readOnly Indicates whether the file system will be read only or not.
* @throws FileNotFoundException If the data files could not be found.
*/
public IndexedFileSystem(Path base, boolean readOnly) throws FileNotFoundException {
this.readOnly = readOnly;
detectLayout(base);
}
@Override
public void close() throws IOException {
if (data != null) {
synchronized (data) {
data.close();
}
}
for (RandomAccessFile index : indices) {
if (index != null) {
synchronized (index) {
index.close();
}
}
}
}
/**
* Automatically detect the layout of the specified directory.
*
* @param base The base directory.
* @throws FileNotFoundException If the data files could not be found.
*/
private void detectLayout(Path base) throws FileNotFoundException {
int indexCount = 0;
for (int index = 0; index < indices.length; index++) {
Path idx = base.resolve("main_file_cache.idx" + index);
if (Files.exists(idx) && !Files.isDirectory(idx)) {
indexCount++;
indices[index] = new RandomAccessFile(idx.toFile(), readOnly ? "r" : "rw");
}
}
if (indexCount <= 0) {
throw new FileNotFoundException("No index file(s) present.");
}
Path resources = base.resolve("main_file_cache.dat");
if (Files.exists(resources) && !Files.isDirectory(resources)) {
data = new RandomAccessFile(resources.toFile(), readOnly ? "r" : "rw");
} else {
throw new FileNotFoundException("No data file present.");
}
}
/**
* Gets the CRC table.
*
* @return The CRC table.
* @throws IOException If there is an error accessing files to create the table.
* @throws IllegalStateException If this file system is not read-only.
*/
public ByteBuffer getCrcTable() throws IOException {
if (readOnly) {
synchronized (this) {
if (crcTable != null) {
return crcTable.duplicate();
}
}
int archives = getFileCount(0);
int hash = 1234;
int[] crcs = new int[archives];
CRC32 crc32 = new CRC32();
for (int i = 1; i < crcs.length; i++) {
crc32.reset();
ByteBuffer buffer = getFile(0, i);
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes, 0, bytes.length);
crc32.update(bytes, 0, bytes.length);
crcs[i] = (int) crc32.getValue();
}
ByteBuffer buffer = ByteBuffer.allocate((crcs.length + 1) * Integer.BYTES);
for (int crc : crcs) {
hash = (hash << 1) + crc;
buffer.putInt(crc);
}
buffer.putInt(hash);
buffer.flip();
synchronized (this) {
crcTable = buffer.asReadOnlyBuffer();
return crcTable.duplicate();
}
}
throw new IllegalStateException("Cannot get CRC table from a writable file system.");
}
/**
* Gets the CRC table as an {@code int} array.
*
* @return The CRC table as an {@code int} array.
* @throws IOException If there is an error accessing files to create the table.
*/
public int[] getCrcs() throws IOException {
if (crcs != null) {
return crcs;
}
ByteBuffer buffer = getCrcTable();
crcs = new int[(buffer.remaining() / Integer.BYTES) - 1];
Arrays.setAll(crcs, crc -> buffer.getInt());
return crcs;
}
/**
* Gets a file.
*
* @param descriptor The {@link FileDescriptor} pointing to the file.
* @return A {@link ByteBuffer} containing the contents of the file.
* @throws IOException If there is an error decoding the file.
*/
public ByteBuffer getFile(FileDescriptor descriptor) throws IOException {
Index index = getIndex(descriptor);
ByteBuffer buffer = ByteBuffer.allocate(index.getSize());
long position = index.getBlock() * FileSystemConstants.BLOCK_SIZE;
int read = 0;
int size = index.getSize();
int blocks = size / FileSystemConstants.CHUNK_SIZE;
if (size % FileSystemConstants.CHUNK_SIZE != 0) {
blocks++;
}
for (int i = 0; i < blocks; i++) {
byte[] header = new byte[FileSystemConstants.HEADER_SIZE];
synchronized (data) {
data.seek(position);
data.readFully(header);
}
position += FileSystemConstants.HEADER_SIZE;
int nextFile = (header[0] & 0xFF) << 8 | header[1] & 0xFF;
int curChunk = (header[2] & 0xFF) << 8 | header[3] & 0xFF;
int nextBlock = (header[4] & 0xFF) << 16 | (header[5] & 0xFF) << 8 | header[6] & 0xFF;
int nextType = header[7] & 0xFF;
Preconditions.checkArgument(i == curChunk, "Chunk id mismatch.");
int chunkSize = size - read;
if (chunkSize > FileSystemConstants.CHUNK_SIZE) {
chunkSize = FileSystemConstants.CHUNK_SIZE;
}
byte[] chunk = new byte[chunkSize];
synchronized (data) {
data.seek(position);
data.readFully(chunk);
}
buffer.put(chunk);
read += chunkSize;
position = (long) nextBlock * (long) FileSystemConstants.BLOCK_SIZE;
// if we still have more data to read, check the validity of the header
if (size > read) {
if (nextType != descriptor.getType() + 1) {
throw new IOException("File type mismatch.");
}
if (nextFile != descriptor.getFile()) {
throw new IOException("File id mismatch.");
}
}
}
buffer.flip();
return buffer;
}
/**
* Gets a file.
*
* @param type The file type.
* @param file The file id.
* @return A {@link ByteBuffer} which contains the contents of the file.
* @throws IOException If an I/O error occurs.
*/
public ByteBuffer getFile(int type, int file) throws IOException {
return getFile(new FileDescriptor(type, file));
}
/**
* Gets the number of files with the specified type.
*
* @param type The type.
* @return The number of files.
* @throws IOException If there is an error getting the length of the specified index file.
* @throws IndexOutOfBoundsException If {@code type} is less than 0, or greater than or equal to the amount of
* indices.
*/
private int getFileCount(int type) throws IOException {
Preconditions.checkElementIndex(type, indices.length, "File type out of bounds.");
RandomAccessFile indexFile = indices[type];
synchronized (indexFile) {
return (int) (indexFile.length() / FileSystemConstants.INDEX_SIZE);
}
}
/**
* Gets the index of a file.
*
* @param descriptor The {@link FileDescriptor} which points to the file.
* @return The {@link Index}.
* @throws IOException If there is an error reading from the index file.
* @throws IndexOutOfBoundsException If the descriptor type is less than 0, or greater than or equal to the amount
* of indices.
*/
private Index getIndex(FileDescriptor descriptor) throws IOException {
int index = descriptor.getType();
Preconditions.checkElementIndex(index, indices.length, "File descriptor type out of bounds.");
byte[] buffer = new byte[FileSystemConstants.INDEX_SIZE];
RandomAccessFile indexFile = indices[index];
synchronized (indexFile) {
long position = descriptor.getFile() * FileSystemConstants.INDEX_SIZE;
if (position >= 0 && indexFile.length() >= position + FileSystemConstants.INDEX_SIZE) {
indexFile.seek(position);
indexFile.readFully(buffer);
} else {
throw new FileNotFoundException("Could not find find index.");
}
}
return Index.decode(buffer);
}
/**
* Checks if this {@link IndexedFileSystem} is read only.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean isReadOnly() {
return readOnly;
}
}
+110
View File
@@ -0,0 +1,110 @@
package org.apollo.cache.archive;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apollo.util.BufferUtil;
import org.apollo.util.CompressionUtil;
/**
* Represents an archive.
*
* @author Graham
*/
public final class Archive {
/**
* Decodes the archive in the specified buffer.
*
* @param buffer The buffer.
* @return The archive.
* @throws IOException If there is an error decompressing the archive.
*/
public static Archive decode(ByteBuffer buffer) throws IOException {
int extractedSize = BufferUtil.readUnsignedMedium(buffer);
int size = BufferUtil.readUnsignedMedium(buffer);
boolean extracted = false;
if (size != extractedSize) {
byte[] compressed = new byte[size];
byte[] decompressed = new byte[extractedSize];
buffer.get(compressed);
CompressionUtil.debzip2(compressed, decompressed);
buffer = ByteBuffer.wrap(decompressed);
extracted = true;
}
int entryCount = buffer.getShort() & 0xFFFF;
int[] identifiers = new int[entryCount];
int[] extractedSizes = new int[entryCount];
int[] sizes = new int[entryCount];
for (int i = 0; i < entryCount; i++) {
identifiers[i] = buffer.getInt();
extractedSizes[i] = BufferUtil.readUnsignedMedium(buffer);
sizes[i] = BufferUtil.readUnsignedMedium(buffer);
}
ArchiveEntry[] entries = new ArchiveEntry[entryCount];
for (int entry = 0; entry < entryCount; entry++) {
ByteBuffer entryBuffer;
if (!extracted) {
byte[] compressed = new byte[sizes[entry]];
byte[] decompressed = new byte[extractedSizes[entry]];
buffer.get(compressed);
CompressionUtil.debzip2(compressed, decompressed);
entryBuffer = ByteBuffer.wrap(decompressed);
} else {
byte[] buf = new byte[extractedSizes[entry]];
buffer.get(buf);
entryBuffer = ByteBuffer.wrap(buf);
}
entries[entry] = new ArchiveEntry(identifiers[entry], entryBuffer);
}
return new Archive(entries);
}
/**
* The entries in this archive.
*/
private final ArchiveEntry[] entries;
/**
* Creates a new archive.
*
* @param entries The entries in this archive.
*/
public Archive(ArchiveEntry[] entries) {
this.entries = entries;
}
/**
* Gets an {@link ArchiveEntry} by its name.
*
* @param name The name.
* @return The entry.
* @throws FileNotFoundException If the entry could not be found.
*/
public ArchiveEntry getEntry(String name) throws FileNotFoundException {
int hash = hash(name);
for (ArchiveEntry entry : entries) {
if (entry.getIdentifier() == hash) {
return entry;
}
}
throw new FileNotFoundException("Could not find entry: " + name + ".");
}
/**
* Hashes the specified string into an integer used to identify an {@link ArchiveEntry}.
*
* @param name The name of the entry.
* @return The hash.
*/
public static int hash(String name) {
return name.toUpperCase().chars().reduce(0, (hash, character) -> hash * 61 + character - 32);
}
}
@@ -0,0 +1,51 @@
package org.apollo.cache.archive;
import java.nio.ByteBuffer;
/**
* Represents a single entry in an {@link Archive}.
*
* @author Graham
*/
public final class ArchiveEntry {
/**
* The buffer of this entry.
*/
private final ByteBuffer buffer;
/**
* The identifier of this entry.
*/
private final int identifier;
/**
* Creates a new archive entry.
*
* @param identifier The identifier.
* @param buffer The buffer.
*/
public ArchiveEntry(int identifier, ByteBuffer buffer) {
this.identifier = identifier;
this.buffer = buffer.asReadOnlyBuffer();
}
/**
* Gets the buffer of this entry.
*
* @return This buffer of this entry.
*/
public ByteBuffer getBuffer() {
return buffer.duplicate();
}
/**
* Gets the identifier of this entry.
*
* @return The identifier of this entry.
*/
public int getIdentifier() {
return identifier;
}
}
@@ -0,0 +1,4 @@
/**
* Contains classes which deal with archives.
*/
package org.apollo.cache.archive;
@@ -0,0 +1,130 @@
package org.apollo.cache.decoder;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.cache.archive.Archive;
import org.apollo.cache.def.ItemDefinition;
import org.apollo.util.BufferUtil;
/**
* Decodes item data from the {@code obj.dat} file into {@link ItemDefinition}s.
*
* @author Graham
*/
public final class ItemDefinitionDecoder {
/**
* The {@link IndexedFileSystem}.
*/
private final IndexedFileSystem fs;
/**
* Creates the item definition decoder.
*
* @param fs The indexed file system.
*/
public ItemDefinitionDecoder(IndexedFileSystem fs) {
this.fs = fs;
}
/**
* Decodes the item definitions.
*
* @return The item definitions.
* @throws IOException If an I/O error occurs.
*/
public ItemDefinition[] decode() throws IOException {
Archive config = Archive.decode(fs.getFile(0, 2));
ByteBuffer data = config.getEntry("obj.dat").getBuffer();
ByteBuffer idx = config.getEntry("obj.idx").getBuffer();
int count = idx.getShort(), index = 2;
int[] indices = new int[count];
for (int i = 0; i < count; i++) {
indices[i] = index;
index += idx.getShort();
}
ItemDefinition[] defs = new ItemDefinition[count];
for (int i = 0; i < count; i++) {
data.position(indices[i]);
defs[i] = decode(i, data);
}
return defs;
}
/**
* Decodes a single definition.
*
* @param id The item's id.
* @param buffer The buffer.
* @return The {@link ItemDefinition}.
*/
private static ItemDefinition decode(int id, ByteBuffer buffer) {
ItemDefinition definition = new ItemDefinition(id);
while (true) {
int opcode = buffer.get() & 0xFF;
if (opcode == 0) {
return definition;
} else if (opcode == 1) {
buffer.getShort();
} else if (opcode == 2) {
definition.setName(BufferUtil.readString(buffer));
} else if (opcode == 3) {
definition.setDescription(BufferUtil.readString(buffer));
} else if (opcode >= 4 && opcode <= 8 || opcode == 10) {
buffer.getShort();
} else if (opcode == 11) {
definition.setStackable(true);
} else if (opcode == 12) {
definition.setValue(buffer.getInt());
} else if (opcode == 16) {
definition.setMembersOnly(true);
} else if (opcode == 23) {
buffer.getShort();
buffer.get();
} else if (opcode == 24) {
buffer.getShort();
} else if (opcode == 25) {
buffer.getShort();
buffer.get();
} else if (opcode == 26) {
buffer.getShort();
} else if (opcode >= 30 && opcode < 35) {
String str = BufferUtil.readString(buffer);
if (str.equalsIgnoreCase("hidden")) {
str = null;
}
definition.setGroundAction(opcode - 30, str);
} else if (opcode >= 35 && opcode < 40) {
definition.setInventoryAction(opcode - 35, BufferUtil.readString(buffer));
} else if (opcode == 40) {
int colourCount = buffer.get() & 0xFF;
for (int i = 0; i < colourCount; i++) {
buffer.getShort();
buffer.getShort();
}
} else if (opcode == 78 || opcode == 79 || opcode >= 90 || opcode <= 93 || opcode == 95) {
buffer.getShort();
} else if (opcode == 97) {
definition.setNoteInfoId(buffer.getShort() & 0xFFFF);
} else if (opcode == 98) {
definition.setNoteGraphicId(buffer.getShort() & 0xFFFF);
} else if (opcode >= 100 && opcode < 110) {
buffer.getShort();
buffer.getShort();
} else if (opcode >= 110 && opcode <= 112) {
buffer.getShort();
} else if (opcode == 113 || opcode == 114) {
buffer.get();
} else if (opcode == 115) {
definition.setTeam(buffer.get() & 0xFF);
}
}
}
}
@@ -0,0 +1,146 @@
package org.apollo.cache.decoder;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.cache.archive.Archive;
import org.apollo.cache.def.NpcDefinition;
import org.apollo.util.BufferUtil;
/**
* Decodes npc data from the {@code npc.dat} file into {@link NpcDefinition}s.
*
* @author Major
*/
public final class NpcDefinitionDecoder {
/**
* The {@link IndexedFileSystem}.
*/
private final IndexedFileSystem fs;
/**
* Creates the npc definition decoder.
*
* @param fs The indexed file system.
*/
public NpcDefinitionDecoder(IndexedFileSystem fs) {
this.fs = fs;
}
/**
* Decodes the npc definitions.
*
* @return An array of all parsed npc definitions.
* @throws IOException If an I/O error occurs.
*/
public NpcDefinition[] decode() throws IOException {
Archive config = Archive.decode(fs.getFile(0, 2));
ByteBuffer data = config.getEntry("npc.dat").getBuffer();
ByteBuffer idx = config.getEntry("npc.idx").getBuffer();
int count = idx.getShort(), index = 2;
int[] indices = new int[count];
for (int i = 0; i < count; i++) {
indices[i] = index;
index += idx.getShort();
}
NpcDefinition[] defs = new NpcDefinition[count];
for (int i = 0; i < count; i++) {
data.position(indices[i]);
defs[i] = decode(i, data);
}
return defs;
}
/**
* Decodes a single definition.
*
* @param id The npc's id.
* @param buffer The buffer.
* @return The {@link NpcDefinition}.
*/
private static NpcDefinition decode(int id, ByteBuffer buffer) {
NpcDefinition definition = new NpcDefinition(id);
while (true) {
int opcode = buffer.get() & 0xFF;
if (opcode == 0) {
return definition;
} else if (opcode == 1) {
int length = buffer.get() & 0xFF;
int[] models = new int[length];
for (int i = 0; i < length; i++) {
models[i] = buffer.getShort();
}
} else if (opcode == 2) {
definition.setName(BufferUtil.readString(buffer));
} else if (opcode == 3) {
definition.setDescription(BufferUtil.readString(buffer));
} else if (opcode == 12) {
definition.setSize(buffer.get());
} else if (opcode == 13) {
definition.setStandAnimation(buffer.getShort());
} else if (opcode == 14) {
definition.setWalkAnimation(buffer.getShort());
} else if (opcode == 17) {
definition
.setWalkAnimations(buffer.getShort(), buffer.getShort(), buffer.getShort(), buffer.getShort());
} else if (opcode >= 30 && opcode < 40) {
String str = BufferUtil.readString(buffer);
if (str.equals("hidden")) {
str = null;
}
definition.setInteraction(opcode - 30, str);
} else if (opcode == 40) {
int length = buffer.get() & 0xFF;
int[] originalColours = new int[length];
int[] replacementColours = new int[length];
for (int i = 0; i < length; i++) {
originalColours[i] = buffer.getShort();
replacementColours[i] = buffer.getShort();
}
} else if (opcode == 60) {
int length = buffer.get() & 0xFF;
int[] additionalModels = new int[length];
for (int i = 0; i < length; i++) {
additionalModels[i] = buffer.getShort();
}
} else if (opcode >= 90 && opcode <= 92) {
buffer.getShort(); // Dummy
} else if (opcode == 95) {
definition.setCombatLevel(buffer.getShort());
} else if (opcode == 97 || opcode == 98) {
buffer.getShort();
} else if (opcode == 100 || opcode == 101) {
buffer.get();
} else if (opcode == 102 || opcode == 103) {
buffer.getShort();
} else if (opcode == 106) {
wrap(buffer.getShort());
wrap(buffer.getShort());
int count = buffer.get() & 0xFF;
int[] morphisms = new int[count + 1];
Arrays.setAll(morphisms, index -> wrap(buffer.getShort()));
} else if (opcode == 107) {
}
}
}
/**
* Wraps a morphism value around, returning -1 if the specified value is 65,535.
*
* @param value The value.
* @return -1 if {@code value} is 65,535, otherwise {@code value}.
*/
private static int wrap(int value) {
return value == 65_535 ? -1 : value;
}
}
@@ -0,0 +1,140 @@
package org.apollo.cache.decoder;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.cache.archive.Archive;
import org.apollo.cache.def.ObjectDefinition;
import org.apollo.util.BufferUtil;
/**
* Decodes object data from the {@code loc.dat} file into {@link ObjectDefinition}s.
*
* @author Major
*/
public final class ObjectDefinitionDecoder {
/**
* The {@link IndexedFileSystem}.
*/
private final IndexedFileSystem fs;
/**
* Creates the decoder.
*
* @param fs The {@link IndexedFileSystem}.
*/
public ObjectDefinitionDecoder(IndexedFileSystem fs) {
this.fs = fs;
}
/**
* Decodes all of the data into {@link ObjectDefinition}s.
*
* @return The definitions.
* @throws IOException If an error occurs when decoding the archive or finding an entry.
*/
public ObjectDefinition[] decode() throws IOException {
Archive config = Archive.decode(fs.getFile(0, 2));
ByteBuffer data = config.getEntry("loc.dat").getBuffer();
ByteBuffer idx = config.getEntry("loc.idx").getBuffer();
int count = idx.getShort(), index = 2;
int[] indices = new int[count];
for (int i = 0; i < count; i++) {
indices[i] = index;
index += idx.getShort();
}
ObjectDefinition[] defs = new ObjectDefinition[count];
for (int i = 0; i < count; i++) {
data.position(indices[i]);
defs[i] = decode(i, data);
}
return defs;
}
/**
* Decodes data from the cache into an {@link ObjectDefinition}.
*
* @param id The id of the object.
* @param data The {@link ByteBuffer} containing the data.
* @return The object definition.
*/
public static ObjectDefinition decode(int id, ByteBuffer data) {
ObjectDefinition definition = new ObjectDefinition(id);
while (true) {
int opcode = data.get() & 0xFF;
if (opcode == 0) {
return definition;
} else if (opcode == 1) {
int amount = data.get() & 0xFF;
for (int i = 0; i < amount; i++) {
data.getShort();
data.get();
}
} else if (opcode == 2) {
definition.setName(BufferUtil.readString(data));
} else if (opcode == 3) {
definition.setDescription(BufferUtil.readString(data));
} else if (opcode == 5) {
int amount = data.get() & 0xFF;
for (int i = 0; i < amount; i++) {
data.getShort();
}
} else if (opcode == 14) {
definition.setWidth(data.get() & 0xFF);
} else if (opcode == 15) {
definition.setLength(data.get() & 0xFF);
} else if (opcode == 17) {
definition.setSolid(false);
} else if (opcode == 18) {
definition.setImpenetrable(false);
} else if (opcode == 19) {
definition.setInteractive((data.get() & 0xFF) == 1);
} else if (opcode == 24) {
data.getShort();
} else if (opcode == 28 || opcode == 29) {
data.get();
} else if (opcode >= 30 && opcode < 39) {
String[] actions = definition.getMenuActions();
if (actions == null) {
actions = new String[10];
}
String action = BufferUtil.readString(data);
actions[opcode - 30] = action;
definition.setMenuActions(actions);
} else if (opcode == 39) {
data.get();
} else if (opcode == 40) {
int amount = data.get() & 0xFF;
for (int i = 0; i < amount; i++) {
data.getShort();
data.getShort();
}
} else if (opcode == 60 || opcode >= 65 && opcode <= 68) {
data.getShort();
} else if (opcode == 69) {
data.get();
} else if (opcode >= 70 && opcode <= 72) {
data.getShort();
} else if (opcode == 73) {
definition.setObstructive(true);
} else if (opcode == 75) {
data.get();
} else if (opcode == 77) {
data.getShort();
data.getShort();
int count = data.get();
for (int i = 0; i <= count; i++) {
data.getShort();
}
} else {
continue;
}
}
}
}
@@ -0,0 +1,4 @@
/**
* Contains classes which parse files within the game's cache.
*/
package org.apollo.cache.decoder;
@@ -0,0 +1,309 @@
package org.apollo.cache.def;
import java.util.HashMap;
import java.util.Map;
import com.google.common.base.Preconditions;
/**
* Represents a type of Item that may be equipped.
*
* @author Graham
*/
public final class EquipmentDefinition {
/**
* The attack id.
*/
private static final int ATTACK = 0;
/**
* The defence id.
*/
private static final int DEFENCE = 1;
/**
* The strength id.
*/
private static final int STRENGTH = 2;
/**
* The hitpoints id.
*/
private static final int HITPOINTS = 3;
/**
* The ranged id.
*/
private static final int RANGED = 4;
/**
* The prayer id.
*/
private static final int PRAYER = 5;
/**
* The magic id.
*/
private static final int MAGIC = 6;
/**
* The equipment definitions.
*/
private static final Map<Integer, EquipmentDefinition> definitions = new HashMap<>();
/**
* Gets the total number of equipment definitions.
*
* @return The count.
*/
public static int count() {
return definitions.size();
}
/**
* Initialises the equipment definitions.
*
* @param definitions The definitions.
* @throws RuntimeException If there is an id mismatch.
*/
public static void init(EquipmentDefinition[] definitions) {
for (int id = 0; id < definitions.length; id++) {
EquipmentDefinition def = definitions[id];
if (def != null) {
if (def.getId() != id) {
throw new RuntimeException("Equipment definition id mismatch.");
}
EquipmentDefinition.definitions.put(def.getId(), def);
}
}
}
/**
* Gets an equipment definition by its id.
*
* @param id The id.
* @return {@code null} if the item is not equipment, the definition otherwise.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public static EquipmentDefinition lookup(int id) {
Preconditions.checkElementIndex(id, ItemDefinition.count(), "Id out of bounds.");
return definitions.get(id);
}
/**
* The item id.
*/
private final int id;
/**
* The array of skill requirement levels.
*/
private final int[] levels = { 1, 1, 1, 1, 1, 1, 1 };
/**
* The slot this equipment goes into.
*/
private int slot;
/**
* Various flags.
*/
private boolean twoHanded, fullBody, fullHat, fullMask;
/**
* Creates a new equipment definition.
*
* @param id The id.
*/
public EquipmentDefinition(int id) {
this.id = id;
}
/**
* Gets the minimum attack level required to equip this item.
*
* @return The level.
*/
public int getAttackLevel() {
return levels[ATTACK];
}
/**
* Gets the minimum defence level required to equip this item.
*
* @return The level.
*/
public int getDefenceLevel() {
return levels[DEFENCE];
}
/**
* Gets the minimum hitpoints level required to equip this item.
*
* @return The level.
*/
public int getHitpointsLevel() {
return levels[HITPOINTS];
}
/**
* Gets the id.
*
* @return The id.
*/
public int getId() {
return id;
}
/**
* Gets the minimum level required to equip this item for a specific skill.
*
* @param skill The skill id.
* @return The level.
*/
public int getLevel(int skill) {
Preconditions.checkArgument(skill >= ATTACK && skill <= MAGIC, "Skill id out of bounds.");
return levels[skill];
}
/**
* Gets the minimum magic level required to equip this item.
*
* @return The level.
*/
public int getMagicLevel() {
return levels[MAGIC];
}
/**
* Gets the minimum prayer level required to equip this item.
*
* @return The level.
*/
public int getPrayerLevel() {
return levels[PRAYER];
}
/**
* Gets the minimum ranged level required to equip this item.
*
* @return The level.
*/
public int getRangedLevel() {
return levels[RANGED];
}
/**
* Gets the target slot.
*
* @return The target slot.
*/
public int getSlot() {
return slot;
}
/**
* Gets the minimum strength level required to equip this item.
*
* @return The level.
*/
public int getStrengthLevel() {
return levels[STRENGTH];
}
/**
* Checks if this equipment is a full body.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean isFullBody() {
return fullBody;
}
/**
* Checks if this equipment is a full hat.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean isFullHat() {
return fullHat;
}
/**
* Checks if this equipment is a full mask.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean isFullMask() {
return fullMask;
}
/**
* Checks if this equipment is two-handed.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean isTwoHanded() {
return twoHanded;
}
/**
* Sets the flags.
*
* @param twoHanded The two handed flag.
* @param fullBody The full body flag.
* @param fullHat The full hat flag.
* @param fullMask The full mask flag.
*/
public void setFlags(boolean twoHanded, boolean fullBody, boolean fullHat, boolean fullMask) {
this.twoHanded = twoHanded;
this.fullBody = fullBody;
this.fullHat = fullHat;
this.fullMask = fullMask;
}
/**
* Sets the required levels.
*
* @param attack The required attack level.
* @param strength The required strength level.
* @param defence The required defence level.
* @param ranged The required ranged level.
* @param magic The required magic level.
*/
public void setLevels(int attack, int strength, int defence, int ranged, int magic) {
setLevels(attack, strength, defence, 1, ranged, 1, magic);
}
/**
* Sets the required levels.
*
* @param attack The required attack level.
* @param strength The required strength level.
* @param defence The required defence level.
* @param hitpoints The required hitpoints level.
* @param ranged The required ranged level.
* @param prayer The required prayer level.
* @param magic The required magic level.
*/
public void setLevels(int attack, int strength, int defence, int hitpoints, int ranged, int prayer, int magic) {
levels[ATTACK] = attack;
levels[STRENGTH] = strength;
levels[DEFENCE] = defence;
levels[HITPOINTS] = hitpoints;
levels[RANGED] = ranged;
levels[PRAYER] = prayer;
levels[MAGIC] = magic;
}
/**
* Sets the target slot.
*
* @param slot The target slot.
*/
public void setSlot(int slot) {
this.slot = slot;
}
}
+410
View File
@@ -0,0 +1,410 @@
package org.apollo.cache.def;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
/**
* Represents a type of Item.
*
* @author Graham
*/
public final class ItemDefinition {
/**
* The item definitions.
*/
private static ItemDefinition[] definitions;
/**
* A map of item ids to noted ids.
*/
private static final BiMap<Integer, Integer> notes = HashBiMap.create();
/**
* A map of noted ids to item ids.
*/
private static final BiMap<Integer, Integer> notesInverse = notes.inverse();
/**
* Gets the total number of item definitions.
*
* @return The count.
*/
public static int count() {
return definitions.length;
}
/**
* Gets the array of item definitions.
*
* @return The definitions.
*/
public static ItemDefinition[] getDefinitions() {
return definitions;
}
/**
* Initialises the class with the specified set of definitions.
*
* @param definitions The definitions.
* @throws RuntimeException If there is an id mismatch.
*/
public static void init(ItemDefinition[] definitions) {
ItemDefinition.definitions = definitions;
for (int id = 0; id < definitions.length; id++) {
ItemDefinition def = definitions[id];
if (def.getId() != id) {
throw new RuntimeException("Item definition id mismatch.");
}
if (def.isNote()) {
def.toNote();
notes.put(def.getNoteInfoId(), def.getId());
}
}
}
/**
* Converts an item id to a noted id.
*
* @param id The item id.
* @return The noted id.
*/
public static int itemToNote(int id) {
Integer entry = notes.get(id);
if (entry == null) {
return id;
}
return entry;
}
/**
* Gets the item definition for the specified id.
*
* @param id The id.
* @return The definition.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public static ItemDefinition lookup(int id) {
Preconditions.checkElementIndex(id, definitions.length, "Id out of bounds.");
return definitions[id];
}
/**
* Converts a noted id to the normal item id.
*
* @param id The note id.
* @return The item id.
*/
public static int noteToItem(int id) {
Integer entry = notesInverse.get(id);
if (entry == null) {
return id;
}
return entry;
}
/**
* The description of the item.
*/
private String description;
/**
* The ground actions array.
*/
private final String[] groundActions = new String[5];
/**
* The item's id.
*/
private final int id;
/**
* The inventory actions array.
*/
private final String[] inventoryActions = new String[5];
/**
* A flag indicating if this item is members only.
*/
private boolean members = false;
/**
* The name of the item.
*/
private String name;
/**
* The id of the item to copy note graphics from.
*/
private int noteGraphicId = -1;
/**
* The id of the item to copy note info from.
*/
private int noteInfoId = -1;
/**
* A flag indicating if this item is stackable.
*/
private boolean stackable = false;
/**
* This item's team.
*/
private int team;
/**
* The item's floor value.
*/
private int value = 1;
/**
* Creates an item definition with the default values.
*
* @param id The item's id.
*/
public ItemDefinition(int id) {
this.id = id;
}
/**
* Gets the description of this item.
*
* @return The item's description.
*/
public String getDescription() {
return description;
}
/**
* Gets a ground action.
*
* @param id The id.
* @return The action.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public String getGroundAction(int id) {
Preconditions.checkElementIndex(id, groundActions.length, "Ground action id is out of bounds.");
return groundActions[id];
}
/**
* Gets this item's id.
*
* @return The id.
*/
public int getId() {
return id;
}
/**
* Gets an inventory action.
*
* @param id The id.
* @return The action.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public String getInventoryAction(int id) {
Preconditions.checkElementIndex(id, inventoryActions.length, "Inventory action id is out of bounds.");
return inventoryActions[id];
}
/**
* Gets this item's name.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* Gets this item's note graphic id.
*
* @return The note graphic id.
*/
public int getNoteGraphicId() {
return noteGraphicId;
}
/**
* Gets this item's note info id.
*
* @return The note info id.
*/
public int getNoteInfoId() {
return noteInfoId;
}
/**
* Gets this item's team.
*
* @return The team.
*/
public int getTeam() {
return team;
}
/**
* Gets this item's value.
*
* @return The value.
*/
public int getValue() {
return value;
}
/**
* Checks if this item is members only.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean isMembersOnly() {
return members;
}
/**
* Checks if this item is a note.
*
* @return {@code true} if so, {@code false} otherwise.
*/
public boolean isNote() {
return noteGraphicId != -1 && noteInfoId != -1;
}
/**
* Checks if the item specified by this definition is stackable.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean isStackable() {
return stackable;
}
/**
* Sets this item's description.
*
* @param description The description.
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Sets a ground action.
*
* @param id The id.
* @param action The action.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public void setGroundAction(int id, String action) {
Preconditions.checkElementIndex(id, groundActions.length, "Ground action id is out of bounds.");
groundActions[id] = action;
}
/**
* Sets an inventory action.
*
* @param id The id.
* @param action The action.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public void setInventoryAction(int id, String action) {
Preconditions.checkElementIndex(id, inventoryActions.length, "Inventory action id is out of bounds.");
inventoryActions[id] = action;
}
/**
* Sets this item's members only flag.
*
* @param members The flag.
*/
public void setMembersOnly(boolean members) {
this.members = members;
}
/**
* Sets this item's name.
*
* @param name The name.
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets this item's note graphic id.
*
* @param noteGraphicId The note graphic id.
*/
public void setNoteGraphicId(int noteGraphicId) {
this.noteGraphicId = noteGraphicId;
}
/**
* Sets this item's note info id.
*
* @param noteInfoId The note info id.
*/
public void setNoteInfoId(int noteInfoId) {
this.noteInfoId = noteInfoId;
}
/**
* Sets this item's stackable flag.
*
* @param stackable The stackable flag.
*/
public void setStackable(boolean stackable) {
this.stackable = stackable;
}
/**
* Sets this item's team.
*
* @param team The team.
*/
public void setTeam(int team) {
this.team = team;
}
/**
* sets this item's value.
*
* @param value The value.
*/
public void setValue(int value) {
this.value = value;
}
/**
* Converts this item to a note, if possible.
*
* @throws IllegalStateException If {@link ItemDefinition#isNote()} returns {@code false}.
*/
public void toNote() {
if (isNote()) {
if (description != null && description.startsWith("Swap this note at any bank for ")) {
return; // already converted.
}
ItemDefinition infoDef = lookup(noteInfoId);
name = infoDef.name;
members = infoDef.members;
String prefix = "a";
char firstChar = name == null ? 'n' : name.charAt(0);
if (firstChar == 'A' || firstChar == 'E' || firstChar == 'I' || firstChar == 'O' || firstChar == 'U') {
prefix = "an";
}
description = "Swap this note at any bank for " + prefix + " " + name + ".";
stackable = true;
} else {
throw new IllegalStateException("Item cannot be noted.");
}
}
}
+365
View File
@@ -0,0 +1,365 @@
package org.apollo.cache.def;
import com.google.common.base.Preconditions;
/**
* Represents a type of Npc.
*
* @author Chris Fletcher
*/
public final class NpcDefinition {
/**
* The npc definitions.
*/
private static NpcDefinition[] definitions;
/**
* Gets the total number of npc definitions.
*
* @return The count.
*/
public static int count() {
return definitions.length;
}
/**
* Gets the array of npc definitions.
*
* @return The definitions.
*/
public static NpcDefinition[] getDefinitions() {
return definitions;
}
/**
* Initialises the class with the specified set of definitions.
*
* @param definitions The definitions.
* @throws RuntimeException If there is an id mismatch.
*/
public static void init(NpcDefinition[] definitions) {
NpcDefinition.definitions = definitions;
for (int id = 0; id < definitions.length; id++) {
NpcDefinition def = definitions[id];
if (def.getId() != id) {
throw new RuntimeException("Npc definition id mismatch.");
}
}
}
/**
* Gets the npc definition for the specified id.
*
* @param id The id.
* @return The definition.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public static NpcDefinition lookup(int id) {
Preconditions.checkElementIndex(id, definitions.length, "Id out of bounds.");
return definitions[id];
}
/**
* The combat level of the npc.
*/
private int combatLevel = -1;
/**
* The description of the npc.
*/
private String description;
/**
* The npc id.
*/
private final int id;
/**
* An array of interaction options.
*/
private final String[] interactions = new String[5];
/**
* The name of the npc.
*/
private String name;
/**
* The npc's size, in tiles.
*/
private int size = 1;
/**
* The various animation ids.
*/
private int standAnim = -1, walkAnim = -1, walkBackAnim = -1, walkLeftAnim = -1, walkRightAnim = -1;
/**
* Creates a new npc definition.
*
* @param id The npc id.
*/
public NpcDefinition(int id) {
this.id = id;
}
/**
* Gets the npc's combat level.
*
* @return The combat level, or -1 if it doesn't have one.
*/
public int getCombatLevel() {
return combatLevel;
}
/**
* Gets the description of the npc.
*
* @return The description.
*/
public String getDescription() {
return description;
}
/**
* Gets the npc id.
*
* @return The npc id.
*/
public int getId() {
return id;
}
/**
* Gets an interaction option.
*
* @param slot The slot of the option.
* @return The option, or {@code null} if there isn't any at the specified slot.
* @throws IndexOutOfBoundsException If the slot is out of bounds.
*/
public String getInteraction(int slot) {
Preconditions.checkElementIndex(slot, interactions.length, "Npc interaction id is out of bounds.");
return interactions[slot];
}
/**
* Gets the array of interaction options.
*
* @return The interaction options.
*/
public String[] getInteractions() {
return interactions;
}
/**
* Gets the name of the npc.
*
* @return The name of the npc.
*/
public String getName() {
return name;
}
/**
* Gets the npc's size, in tiles.
*
* @return The size.
*/
public int getSize() {
return size;
}
/**
* Gets the id of the npc's standing animation.
*
* @return The stand animation id, or -1 if it doesn't have one.
*/
public int getStandAnimation() {
return standAnim;
}
/**
* Gets the walking animation of the npc.
*
* @return The walking animation.
*/
public int getWalkAnimation() {
return walkAnim;
}
/**
* Gets the walk-back animation of the npc.
*
* @return The walk-back animation.
*/
public int getWalkBackAnimation() {
return walkBackAnim;
}
/**
* Gets the walk-left animation of the npc.
*
* @return The walk-left animation.
*/
public int getWalkLeftAnimation() {
return walkLeftAnim;
}
/**
* Gets the walk-right animation of the npc.
*
* @return The walk-right animation.
*/
public int getWalkRightAnimation() {
return walkRightAnim;
}
/**
* Checks if the npc has a combat level.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasCombatLevel() {
return combatLevel != -1;
}
/**
* Checks if there is an interaction option present.
*
* @param slot The slot to check.
* @return {@code true} if so, {@code false} if not.
* @throws IndexOutOfBoundsException If the slot is out of bounds.
*/
public boolean hasInteraction(int slot) {
Preconditions.checkElementIndex(slot, interactions.length, "Npc interaction id is out of bounds.");
return interactions[slot] != null;
}
/**
* Checks if the npc has a standing animation id.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasStandAnimation() {
return standAnim != -1;
}
/**
* Checks if the npc has a walking animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkAnimation() {
return walkAnim != -1;
}
/**
* Checks if the npc has a walk-back animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkBackAnimation() {
return walkBackAnim != -1;
}
/**
* Checks if the npc has a walk-left animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkLeftAnimation() {
return walkLeftAnim != -1;
}
/**
* Checks if the npc has a walk-right animation.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasWalkRightAnimation() {
return walkRightAnim != -1;
}
/**
* Sets the npc's combat level.
*
* @param combatLevel The combat level.
*/
public void setCombatLevel(int combatLevel) {
this.combatLevel = combatLevel;
}
/**
* Sets the description of the npc.
*
* @param description The description.
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Sets an interaction option.
*
* @param slot The slot of the option.
* @param interaction The interaction options.
* @throws IndexOutOfBoundsException If the slot is out of bounds.
*/
public void setInteraction(int slot, String interaction) {
Preconditions.checkElementIndex(slot, interactions.length, "Npc interaction id is out of bounds.");
interactions[slot] = interaction;
}
/**
* Sets the name of the npc.
*
* @param name The name.
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the size of the npc, in tiles.
*
* @param size The size.
*/
public void setSize(int size) {
this.size = size;
}
/**
* Sets the id of the npc's standing animation.
*
* @param standAnim The stand animation id.
*/
public void setStandAnimation(int standAnim) {
this.standAnim = standAnim;
}
/**
* Sets the walking animation of the npc.
*
* @param walkAnim The walking animation.
*/
public void setWalkAnimation(int walkAnim) {
this.walkAnim = walkAnim;
}
/**
* Sets the various walking animations of the npc.
*
* @param walkAnim The walking animation.
* @param walkBackAnim The walk-back animation.
* @param walkLeftAnim The walk-left animation.
* @param walkRightAnim The walk-right animation.
*/
public void setWalkAnimations(int walkAnim, int walkBackAnim, int walkLeftAnim, int walkRightAnim) {
this.walkAnim = walkAnim;
this.walkBackAnim = walkBackAnim;
this.walkLeftAnim = walkLeftAnim;
this.walkRightAnim = walkRightAnim;
}
}
@@ -0,0 +1,293 @@
package org.apollo.cache.def;
import com.google.common.base.Preconditions;
/**
* Represents a type of GameObject.
*
* @author Major
*/
public final class ObjectDefinition {
/**
* The array of game object definitions.
*/
private static ObjectDefinition[] definitions;
/**
* Gets the total number of object definitions.
*
* @return The count.
*/
public static int count() {
return definitions.length;
}
/**
* Gets the array of object definitions.
*
* @return The definitions.
*/
public static ObjectDefinition[] getDefinitions() {
return definitions;
}
/**
* Initialises the object definitions.
*
* @param definitions The decoded definitions.
* @throws RuntimeException If there is an id mismatch.
*/
public static void init(ObjectDefinition[] definitions) {
ObjectDefinition.definitions = definitions;
for (int id = 0; id < definitions.length; id++) {
ObjectDefinition def = definitions[id];
if (def.getId() != id) {
throw new RuntimeException("Item definition id mismatch.");
}
}
}
/**
* Gets the object definition for the specified id.
*
* @param id The id of the object.
* @return The definition.
* @throws IndexOutOfBoundsException If the id is out of bounds.
*/
public static ObjectDefinition lookup(int id) {
Preconditions.checkElementIndex(id, definitions.length, "Id out of bounds.");
return definitions[id];
}
/**
* The object's description.
*/
private String description;
/**
* The object's id.
*/
private final int id;
/**
* Denotes whether this object is impenetrable or not.
*/
private boolean impenetrable = true;
/**
* Denotes whether this object has actions associated with it or not.
*/
private boolean interactive;
/**
* Denotes whether or not this object obstructs the ground.
*/
private boolean obstructive;
/**
* This object's length.
*/
private int length = 1;
/**
* The object's menu actions.
*/
private String[] menuActions;
/**
* The object's name.
*/
private String name;
/**
* Denotes whether the object can be walked over or not.
*/
private boolean solid = true;
/**
* This object's width.
*/
private int width = 1;
/**
* Creates a new object definition.
*
* @param id The id of the object.
*/
public ObjectDefinition(int id) {
this.id = id;
}
/**
* Gets the description of this object.
*
* @return The description.
*/
public String getDescription() {
return description;
}
/**
* Gets the id of this object.
*
* @return The id.
*/
public int getId() {
return id;
}
/**
* Gets the length of this object.
*
* @return The length.
*/
public int getLength() {
return length;
}
/**
* Gets the menu actions of this object.
*
* @return The menu actions.
*/
public String[] getMenuActions() {
return menuActions;
}
/**
* Gets the name of this object.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* Gets the with of this object.
*
* @return The width.
*/
public int getWidth() {
return width;
}
/**
* Indicates the impenetrability of this object.
*
* @return {@code true} if this object is impenetrable, otherwise {@code false}.
*/
public boolean isImpenetrable() {
return impenetrable;
}
/**
* Indicates the interactivity of this object.
*
* @return {@code true} if the object is interactive, otherwise {@code false}.
*/
public boolean isInteractive() {
return interactive;
}
/**
* Indicates whether or not this object obstructs the ground.
*
* @return {@code true} if the object obstructs the ground otherwise {@code false}.
*/
public boolean isObstructive() {
return obstructive;
}
/**
* Indicates the solidity of this object.
*
* @return {@code true} if this object is solid, otherwise {@code false}.
*/
public boolean isSolid() {
return solid;
}
/**
* Sets the description of this object.
*
* @param description The description.
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Sets the impenetrability of this object.
*
* @param impenetrable The impenetrability.
*/
public void setImpenetrable(boolean impenetrable) {
this.impenetrable = impenetrable;
}
/**
* Sets the interactivity of this object.
*
* @param interactive The interactivity.
*/
public void setInteractive(boolean interactive) {
this.interactive = interactive;
}
/**
* Sets the length of this object.
*
* @param length The length.
*/
public void setLength(int length) {
this.length = length;
}
/**
* Sets the menu actions of this object.
*
* @param menuActions The menu actions.
*/
public void setMenuActions(String[] menuActions) {
this.menuActions = menuActions;
}
/**
* Sets the name of this object.
*
* @param name The name.
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the solidity of this object.
*
* @param solid The solidity.
*/
public void setSolid(boolean solid) {
this.solid = solid;
}
/**
* Sets the width of this object.
*
* @param width The width.
*/
public void setWidth(int width) {
this.width = width;
}
/**
* Sets whether or not this object is obstructive to the ground.
*
* @param obstructive Whether or not this object obstructs the ground.
*/
public void setObstructive(boolean obstructive) {
this.obstructive = obstructive;
}
}
+4
View File
@@ -0,0 +1,4 @@
/**
* Contains definition classes which contain information about types of items, NPCs, etc.
*/
package org.apollo.cache.def;
+4
View File
@@ -0,0 +1,4 @@
/**
* Contains classes which deal with the file system that the client uses to store game data files.
*/
package org.apollo.cache;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
/**
* Contains cache-related tools.
*/
package org.apollo.cache.tools;