RuneHive-Game
Loading...
Searching...
No Matches
FileSystem.java
Go to the documentation of this file.
1package com.runehive.fs.cache;
2
3import com.google.common.base.Preconditions;
4import com.runehive.fs.cache.archive.Archive;
5
6import java.io.IOException;
7import java.nio.ByteBuffer;
8import java.nio.channels.SeekableByteChannel;
9import java.nio.file.Files;
10import java.nio.file.Path;
11import java.nio.file.Paths;
12import java.util.Objects;
13import java.util.zip.CRC32;
14
15import static java.nio.file.StandardOpenOption.READ;
16import static java.nio.file.StandardOpenOption.WRITE;
17
18/**
19 * Represents a file system of {@link Cache}s and {@link Archive}s.
20 *
21 * @author Ryley Kimmel <ryley.kimmel@live.com>
22 * @author Artem Batutin <artembatutin@gmail.com>
23 */
24public final class FileSystem {
25
26 /** Represents the id of the configurations cache. */
27 public static final int CONFIG_INDEX = 0;
28
29 /** Represents the id of the model cache. */
30 public static final int MODEL_INDEX = 1;
31
32 /** Represents the id of the animations cache. */
33 public static final int ANIMATION_INDEX = 2;
34
35 /** Represents the id of the sounds and music cache. */
36 public static final int MIDI_INDEX = 3;
37
38 /** Represents the id of the tool.mapviewer and landscape cache. */
39 public static final int MAP_INDEX = 4;
40
41 /** Represents the id of the title screen archive. */
42 public static final int TITLE_ARCHIVE = 1;
43
44 /** Represents the id of the configurations archive. */
45 public static final int CONFIG_ARCHIVE = 2;
46
47 /** Represents the id of the interface archive. */
48 public static final int INTERFACE_ARCHIVE = 3;
49
50 /** Represents the id of the media and sprite archive. */
51 public static final int MEDIA_ARCHIVE = 4;
52
53 /** Represents the id of the manifest archive. */
54 public static final int MANIFEST_ARCHIVE = 5;
55
56 /** Represents the id of the textures archive. */
57 public static final int TEXTURES_ARCHIVE = 6;
58
59 /**
60 * Represents the id of the word archive - user for storing profane or
61 * illegal words not allowed to be spoken in-game.
62 */
63 public static final int WORD_ARCHIVE = 7;
64
65 /** Represents the id of the sound and music archive. */
66 public static final int SOUND_ARCHIVE = 8;
67
68 /** Represents the maximum amount of archives within this file system. */
69 private static final int MAXIMUM_ARCHIVES = 9;
70
71 /** Represents the maximum amount of indices within this file system. */
72 private static final int MAXIMUM_INDICES = 256;
73
74 /** Represents the prefix of this {@link FileSystem}s main cache files. */
75 private static final String DATA_PREFIX = "main_file_cache.dat";
76
77 /** Represents the prefix of this {@link FileSystem}s index files. */
78 private static final String INDEX_PREFIX = "main_file_cache.idx";
79
80 /** All of the {@link Cache}s within this {@link FileSystem}. */
81 private final Cache[] caches;
82
83 /** All of the {@link Archive}s within this {@link FileSystem}. */
84 private final Archive[] archives;
85
86 /** The cached archive hashes. */
87 private ByteBuffer archiveHashes;
88
89 /**
90 * Constructs a new {@link FileSystem} with the specified {@link Cache}s and
91 * {@link Archive}s
92 *
93 * @param caches All of the {@link Cache}s within this {@link
94 * FileSystem}.
95 * @param archives All of the {@link Archive}s within this {@link
96 * FileSystem}.
97 */
99 this.caches = caches;
100 this.archives = archives;
101 }
102
103 /**
104 * Constructs and initializes a {@link FileSystem} from the specified {@code
105 * directory}.
106 *
107 * @param directory The directory of the {@link FileSystem}.
108 * @return The constructed {@link FileSystem} instance.
109 * @throws IOException If some I/O exception occurs.
110 */
111 public static FileSystem create(String directory) throws IOException {
112 Path root = Paths.get(directory);
113 Preconditions.checkArgument(Files.isDirectory(root), "Supplied path must be a directory! " + root);
114
115 Path data = root.resolve(DATA_PREFIX);
116 Preconditions.checkArgument(Files.exists(data), "No data file found in the specified path!");
117
118 SeekableByteChannel dataChannel = Files.newByteChannel(data, READ, WRITE);
119
122
123 for (int index = 0; index < caches.length; index++) {
124 Path path = root.resolve(INDEX_PREFIX + index);
125 if (Files.exists(path)) {
126 SeekableByteChannel indexChannel = Files.newByteChannel(path, READ, WRITE);
127 caches[index] = new Cache(dataChannel, indexChannel, index);
128 }
129 }
130
131 // We don't use index 0
132 for (int id = 1; id < archives.length; id++) {
133 Cache cache = Objects.requireNonNull(caches[CONFIG_INDEX], "Configuration cache is null - unable to decode archives");
134 archives[id] = Archive.decode(cache.get(id));
135 }
136
137 return new FileSystem(caches, archives);
138 }
139
140 /**
141 * Gets an {@link Archive} for the specified {@code id}, this method
142 * fails-fast if no archive can be found.
143 *
144 * @param id The id of the {@link Archive} to fetch.
145 * @return The {@link Archive} for the specified {@code id}.
146 * @throws NullPointerException If the archive cannot be found.
147 */
148 public Archive getArchive(int id) {
149 Preconditions.checkElementIndex(id, archives.length);
150 return Objects.requireNonNull(archives[id]);
151 }
152
153 /**
154 * Gets a {@link Cache} for the specified {@code id}, this method fails-fast
155 * if no cache can be found.
156 *
157 * @param id The id of the {@link Cache} to fetch.
158 * @return The {@link Cache} for the specified {@code id}.
159 * @throws NullPointerException If the cache cannot be found.
160 */
161 public Cache getCache(int id) {
162 Preconditions.checkElementIndex(id, caches.length);
163 return Objects.requireNonNull(caches[id]);
164 }
165
166 /**
167 * Returns a {@link ByteBuffer} of file data for the specified index within
168 * the specified {@link Cache}.
169 *
170 * @param cacheId The id of the cache.
171 * @param indexId The id of the index within the cache.
172 * @return A {@link ByteBuffer} of file data for the specified index.
173 * @throws IOException If some I/O exception occurs.
174 */
175 public ByteBuffer getFile(int cacheId, int indexId) throws IOException {
176 Cache cache = getCache(cacheId);
177 synchronized (cache) {
178 return cache.get(indexId);
179 }
180 }
181
182 /**
183 * Returns the cached {@link #archiveHashes} if they exist, otherwise they
184 * are calculated and cached for future use.
185 *
186 * @return The hashes of each {@link Archive}.
187 * @throws IOException If some I/O exception occurs.
188 */
189 public ByteBuffer getArchiveHashes() throws IOException {
190 synchronized (this) {
191 if (archiveHashes != null) {
192 return archiveHashes.duplicate();
193 }
194 }
195
196 int[] crcs = new int[MAXIMUM_ARCHIVES];
197
198 CRC32 crc32 = new CRC32();
199 for (int file = 1; file < crcs.length; file++) {
200 crc32.reset();
201
202 ByteBuffer buffer = getFile(CONFIG_INDEX, file);
203 crc32.update(buffer);
204
205 crcs[file] = (int) crc32.getValue();
206 }
207
208 ByteBuffer buffer = ByteBuffer.allocate((crcs.length + 1) * Integer.BYTES);
209
210 int hash = 1234;
211 for (int crc : crcs) {
212 hash = (hash << 1) + crc;
213 buffer.putInt(crc);
214 }
215
216 buffer.putInt(hash);
217 buffer.flip();
218
219 synchronized (this) {
220 archiveHashes = buffer.asReadOnlyBuffer();
221 return archiveHashes.duplicate();
222 }
223 }
224
225}
Represents a Sector and Index cache.
Definition Cache.java:14
static final int MEDIA_ARCHIVE
Represents the id of the media and sprite archive.
static final int ANIMATION_INDEX
Represents the id of the animations cache.
static final int CONFIG_INDEX
Represents the id of the configurations cache.
static final int MIDI_INDEX
Represents the id of the sounds and music cache.
ByteBuffer getFile(int cacheId, int indexId)
Returns a ByteBuffer of file data for the specified index within the specified Cache.
Archive getArchive(int id)
Gets an Archive for the specified id, this method fails-fast if no archive can be found.
ByteBuffer archiveHashes
The cached archive hashes.
static final int MAXIMUM_INDICES
Represents the maximum amount of indices within this file system.
static final int MODEL_INDEX
Represents the id of the model cache.
Cache getCache(int id)
Gets a Cache for the specified id, this method fails-fast if no cache can be found.
static final int SOUND_ARCHIVE
Represents the id of the sound and music archive.
static final int WORD_ARCHIVE
Represents the id of the word archive - user for storing profane or illegal words not allowed to be s...
ByteBuffer getArchiveHashes()
Returns the cached archiveHashes if they exist, otherwise they are calculated and cached for future u...
FileSystem(Cache[] caches, Archive[] archives)
Constructs a new FileSystem with the specified Caches and Archives.
static final int MANIFEST_ARCHIVE
Represents the id of the manifest archive.
static final int TITLE_ARCHIVE
Represents the id of the title screen archive.
static final String INDEX_PREFIX
Represents the prefix of this FileSystems index files.
static final int MAXIMUM_ARCHIVES
Represents the maximum amount of archives within this file system.
static final int TEXTURES_ARCHIVE
Represents the id of the textures archive.
final Archive[] archives
All of the Archives within this FileSystem.
static final int CONFIG_ARCHIVE
Represents the id of the configurations archive.
static final int MAP_INDEX
Represents the id of the tool.mapviewer and landscape cache.
static final String DATA_PREFIX
Represents the prefix of this FileSystems main cache files.
final Cache[] caches
All of the Caches within this FileSystem.
static final int INTERFACE_ARCHIVE
Represents the id of the interface archive.
static FileSystem create(String directory)
Constructs and initializes a FileSystem from the specified directory.
Represents an archive within the Cache.
Definition Archive.java:25
static Archive decode(ByteBuffer data)
Decodes the data within this Archive.
Definition Archive.java:48