mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-03 00:38:21 +00:00
Cache already-decoded Archives.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package org.apollo.cache;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* A class which points to a file in the cache.
|
||||
*
|
||||
@@ -28,6 +30,16 @@ public final class FileDescriptor {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof FileDescriptor) {
|
||||
FileDescriptor other = (FileDescriptor) obj;
|
||||
return type == other.type && file == other.file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file id.
|
||||
*
|
||||
@@ -46,4 +58,9 @@ public final class FileDescriptor {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return file * FileSystemConstants.ARCHIVE_COUNT + type;
|
||||
}
|
||||
|
||||
}
|
||||
+74
-40
@@ -4,13 +4,18 @@ import java.io.Closeable;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apollo.cache.archive.Archive;
|
||||
|
||||
/**
|
||||
* A file system based on top of the operating system's file system. It consists of a data file and index files. Index
|
||||
@@ -20,6 +25,21 @@ import com.google.common.base.Preconditions;
|
||||
*/
|
||||
public final class IndexedFileSystem implements Closeable {
|
||||
|
||||
/**
|
||||
* The Map that caches already-decoded Archives.
|
||||
*/
|
||||
private final Map<FileDescriptor, Archive> cache = new HashMap<>(FileSystemConstants.ARCHIVE_COUNT);
|
||||
|
||||
/**
|
||||
* The index files.
|
||||
*/
|
||||
private final RandomAccessFile[] indices = new RandomAccessFile[256];
|
||||
|
||||
/**
|
||||
* Read only flag.
|
||||
*/
|
||||
private final boolean readOnly;
|
||||
|
||||
/**
|
||||
* The cached CRC table.
|
||||
*/
|
||||
@@ -35,16 +55,6 @@ public final class IndexedFileSystem implements Closeable {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -75,30 +85,26 @@ public final class IndexedFileSystem implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically detect the layout of the specified directory.
|
||||
* Gets the {@link Archive} pointed to by the specified {@link FileDescriptor}.
|
||||
*
|
||||
* @param base The base directory.
|
||||
* @throws FileNotFoundException If the data files could not be found.
|
||||
* @param type The file type.
|
||||
* @param file The file id.
|
||||
* @return The Archive.
|
||||
* @throws IOException If there is an error decoding the Archive.
|
||||
*/
|
||||
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");
|
||||
public Archive getArchive(int type, int file) throws IOException {
|
||||
FileDescriptor descriptor = new FileDescriptor(type, file);
|
||||
Archive cached = cache.get(descriptor);
|
||||
|
||||
if (cached == null) {
|
||||
cached = Archive.decode(getFile(descriptor));
|
||||
|
||||
synchronized (this) {
|
||||
cache.put(descriptor, cached);
|
||||
}
|
||||
}
|
||||
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.");
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +152,7 @@ public final class IndexedFileSystem implements Closeable {
|
||||
return crcTable.duplicate();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot get CRC table from a writable file system.");
|
||||
}
|
||||
|
||||
@@ -244,6 +251,42 @@ public final class IndexedFileSystem implements Closeable {
|
||||
return getFile(new FileDescriptor(type, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this {@link IndexedFileSystem} is read only.
|
||||
*
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 number of files with the specified type.
|
||||
*
|
||||
@@ -251,7 +294,7 @@ public final class IndexedFileSystem implements Closeable {
|
||||
* @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.
|
||||
* indices.
|
||||
*/
|
||||
private int getFileCount(int type) throws IOException {
|
||||
Preconditions.checkElementIndex(type, indices.length, "File type out of bounds.");
|
||||
@@ -269,7 +312,7 @@ public final class IndexedFileSystem implements Closeable {
|
||||
* @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.
|
||||
* of indices.
|
||||
*/
|
||||
private Index getIndex(FileDescriptor descriptor) throws IOException {
|
||||
int index = descriptor.getType();
|
||||
@@ -290,13 +333,4 @@ public final class IndexedFileSystem implements Closeable {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public final class ItemDefinitionDecoder {
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
public ItemDefinition[] decode() throws IOException {
|
||||
Archive config = Archive.decode(fs.getFile(0, 2));
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("obj.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("obj.idx").getBuffer();
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class ItemDefinitionDecoder {
|
||||
* @param buffer The buffer.
|
||||
* @return The {@link ItemDefinition}.
|
||||
*/
|
||||
private static ItemDefinition decode(int id, ByteBuffer buffer) {
|
||||
private ItemDefinition decode(int id, ByteBuffer buffer) {
|
||||
ItemDefinition definition = new ItemDefinition(id);
|
||||
while (true) {
|
||||
int opcode = buffer.get() & 0xFF;
|
||||
|
||||
+56
-56
@@ -17,48 +17,21 @@ import org.apollo.cache.archive.ArchiveEntry;
|
||||
*/
|
||||
public final class MapFileDecoder {
|
||||
|
||||
/**
|
||||
* The width (and length) of a map file, in tiles.
|
||||
*/
|
||||
public static final int MAP_FILE_WIDTH = 64;
|
||||
|
||||
/**
|
||||
* The file id of the versions archive.
|
||||
*/
|
||||
private static final int VERSIONS_ARCHIVE_FILE_ID = 5;
|
||||
|
||||
/**
|
||||
* Decodes {@link MapDefinition}s from the specified {@link IndexedFileSystem}.
|
||||
*
|
||||
* @param fs The IndexedFileSystem.
|
||||
* @return A {@link Map} of packed coordinates to their MapDefinitions.
|
||||
* @throws IOException If there is an error reading or decoding the Archive.
|
||||
*/
|
||||
public static Map<Integer, MapDefinition> decode(IndexedFileSystem fs) throws IOException {
|
||||
Archive archive = Archive.decode(fs.getFile(0, VERSIONS_ARCHIVE_FILE_ID));
|
||||
ArchiveEntry entry = archive.getEntry("map_index");
|
||||
Map<Integer, MapDefinition> definitions = new HashMap<>();
|
||||
|
||||
ByteBuffer buffer = entry.getBuffer();
|
||||
int count = buffer.capacity() / (3 * Short.BYTES + Byte.BYTES);
|
||||
|
||||
for (int times = 0; times < count; times++) {
|
||||
int packed = buffer.getShort() & 0xFFFF;
|
||||
int terrain = buffer.getShort() & 0xFFFF;
|
||||
int objects = buffer.getShort() & 0xFFFF;
|
||||
boolean members = buffer.get() == 1;
|
||||
|
||||
definitions.put(packed, new MapDefinition(packed, terrain, objects, members));
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* A definition for a region.
|
||||
*/
|
||||
public static final class MapDefinition {
|
||||
|
||||
/**
|
||||
* Indicates whether or not this map is members-only.
|
||||
*/
|
||||
private final boolean members;
|
||||
|
||||
/**
|
||||
* The object file id.
|
||||
*/
|
||||
private final int objects;
|
||||
|
||||
/**
|
||||
* The packed coordinates.
|
||||
*/
|
||||
@@ -69,16 +42,6 @@ public final class MapFileDecoder {
|
||||
*/
|
||||
private final int terrain;
|
||||
|
||||
/**
|
||||
* The object file id.
|
||||
*/
|
||||
private final int objects;
|
||||
|
||||
/**
|
||||
* Indicates whether or not this map is members-only.
|
||||
*/
|
||||
private final boolean members;
|
||||
|
||||
/**
|
||||
* Creates the {@link MapDefinition}.
|
||||
*
|
||||
@@ -94,6 +57,15 @@ public final class MapFileDecoder {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of the file containing the object data.
|
||||
*
|
||||
* @return The file id.
|
||||
*/
|
||||
public int getObjectFile() {
|
||||
return objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packed coordinates.
|
||||
*
|
||||
@@ -112,15 +84,6 @@ public final class MapFileDecoder {
|
||||
return terrain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of the file containing the object data.
|
||||
*
|
||||
* @return The file id.
|
||||
*/
|
||||
public int getObjectFile() {
|
||||
return objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this MapDefinition is for a members-only area of the world.
|
||||
*
|
||||
@@ -132,4 +95,41 @@ public final class MapFileDecoder {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The width (and length) of a map file, in tiles.
|
||||
*/
|
||||
public static final int MAP_FILE_WIDTH = 64;
|
||||
|
||||
/**
|
||||
* The file id of the versions archive.
|
||||
*/
|
||||
private static final int VERSIONS_ARCHIVE_FILE_ID = 5;
|
||||
|
||||
/**
|
||||
* Decodes {@link MapDefinition}s from the specified {@link IndexedFileSystem}.
|
||||
*
|
||||
* @param fs The IndexedFileSystem.
|
||||
* @return A {@link Map} of packed coordinates to their MapDefinitions.
|
||||
* @throws IOException If there is an error reading or decoding the Archive.
|
||||
*/
|
||||
public static Map<Integer, MapDefinition> decode(IndexedFileSystem fs) throws IOException {
|
||||
Archive archive = fs.getArchive(0, VERSIONS_ARCHIVE_FILE_ID);
|
||||
ArchiveEntry entry = archive.getEntry("map_index");
|
||||
Map<Integer, MapDefinition> definitions = new HashMap<>();
|
||||
|
||||
ByteBuffer buffer = entry.getBuffer();
|
||||
int count = buffer.capacity() / (3 * Short.BYTES + Byte.BYTES);
|
||||
|
||||
for (int times = 0; times < count; times++) {
|
||||
int packed = buffer.getShort() & 0xFFFF;
|
||||
int terrain = buffer.getShort() & 0xFFFF;
|
||||
int objects = buffer.getShort() & 0xFFFF;
|
||||
boolean members = buffer.get() == 1;
|
||||
|
||||
definitions.put(packed, new MapDefinition(packed, terrain, objects, members));
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public final class NpcDefinitionDecoder {
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
public NpcDefinition[] decode() throws IOException {
|
||||
Archive config = Archive.decode(fs.getFile(0, 2));
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("npc.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("npc.idx").getBuffer();
|
||||
|
||||
@@ -64,7 +64,7 @@ public final class NpcDefinitionDecoder {
|
||||
* @param buffer The buffer.
|
||||
* @return The {@link NpcDefinition}.
|
||||
*/
|
||||
private static NpcDefinition decode(int id, ByteBuffer buffer) {
|
||||
private NpcDefinition decode(int id, ByteBuffer buffer) {
|
||||
NpcDefinition definition = new NpcDefinition(id);
|
||||
|
||||
while (true) {
|
||||
@@ -139,7 +139,7 @@ public final class NpcDefinitionDecoder {
|
||||
* @param value The value.
|
||||
* @return -1 if {@code value} is 65,535, otherwise {@code value}.
|
||||
*/
|
||||
private static int wrap(int value) {
|
||||
private int wrap(int value) {
|
||||
return value == 65_535 ? -1 : value;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,32 +29,6 @@ public final class ObjectDefinitionDecoder {
|
||||
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}.
|
||||
*
|
||||
@@ -62,7 +36,7 @@ public final class ObjectDefinitionDecoder {
|
||||
* @param data The {@link ByteBuffer} containing the data.
|
||||
* @return The object definition.
|
||||
*/
|
||||
public static ObjectDefinition decode(int id, ByteBuffer data) {
|
||||
public ObjectDefinition decode(int id, ByteBuffer data) {
|
||||
ObjectDefinition definition = new ObjectDefinition(id);
|
||||
while (true) {
|
||||
int opcode = data.get() & 0xFF;
|
||||
@@ -137,4 +111,30 @@ public final class ObjectDefinitionDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = fs.getArchive(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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user