-
Notifications
You must be signed in to change notification settings - Fork 4
/
CustomInputStream.java
230 lines (197 loc) · 8.32 KB
/
CustomInputStream.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package bnkeditor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* A wrapper for an <code>InputStream</code>.
* Supports advanced stuff like arbitrary endianness, reading primitive types and <code>String</code>s, and reading until specified offsets.
* Additionally, automatically closes the <code>InputStream</code> once its end has been reached.
* This is not buffered, so if you read something once you can never read it again.
* Uses a <code>FileInputStream</code> internally.
* @author marieismywaifu
*/
public class CustomInputStream {
private final InputStream a;
private final boolean b;
private final long c;
private long d;
/**
* Constructs a new <code>CustomInputStream</code>.
* @param file the file to read from
* @param littleEndian the endianness of the file (you cannot change endianness on the fly)
* @throws FileNotFoundException if the specified file cannot be found
*/
public CustomInputStream(File file, boolean littleEndian) throws FileNotFoundException {
a = new FileInputStream(file);
b = littleEndian;
c = file.length();
d = 0;
}
/**
* Reads from the current position until the end of the file.
* @return all read bytes
* @throws IOException passed from the underlying <code>InputStream</code>
* @throws UnsupportedOperationException if the rest of the file is longer than <code>Integer.MAX_VALUE</code> bytes
*/
public byte[] readRest() throws IOException {
if (c - d > Integer.MAX_VALUE) throw new UnsupportedOperationException("Can't store that many bytes at once!");
return readUntil(c);
}
/**
* Reads from the current position until the specified position.
* @param position until where to read (exclusive)
* @return all read bytes
* @throws IllegalArgumentException if the specicied position has already been passed, if it is past the end of the file, or if the amount of bytes to read is greater than <code>Integer.MAX_VALUE</code>
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public byte[] readUntil(long position) throws IOException {
if (position < d) throw new IllegalArgumentException("That point has already been passed!");
else if (position - d > Integer.MAX_VALUE) throw new IllegalArgumentException("Can't store that many bytes at once!");
return read((int) (position - d));
}
/**
* Reads from the file.
* @param length the amount of bytes to read
* @return all read bytes
* @throws IllegalArgumentException if there are less than <code><b>length</b></code> bytes remaining
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public byte[] read(int length) throws IOException {
byte[] e = new byte[length];
read(e);
return e;
}
/**
* Reads from the file into the specified array.
* The entire array is filled, from start to finish.
* @param bytes an array to read into
* @throws IllegalArgumentException if there are less than <code><b>bytes</b>.length</code> bytes remaining
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public void read(byte[] bytes) throws IOException {
if (c < d + bytes.length) throw new IllegalArgumentException("The file isn't that long!");
long e = d;
while (d < e + bytes.length) d += a.read(bytes, (int) (d - e), (int) (bytes.length - (d - e)));
if (d == c) close();
}
/**
* Reads a four-letter <code>String</code>.
* @return the read <code>String</code>
* @throws IOException passed from the underlying <code>InputStream</code>
* @throws UnsupportedOperationException if there are less than 4 bytes remaining
*/
public String readMagic() throws IOException {
if (c < d + 4) throw new UnsupportedOperationException("The file isn't that long!");
return readString(4);
}
/**
* Reads an ASCII-encoded <code>String</code>.
* @param length the length of the <code>String</code>
* @return the read <code>String</code>
* @throws IllegalArgumentException if there are less than <code><b>length</b></code> bytes remaining
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public String readString(int length) throws IOException {
if (c < d + length) throw new IllegalArgumentException("The file isn't that long!");
StringBuilder e = new StringBuilder();
for (int f = 0; f < length; f++) e.append((char) read());
return e.toString();
}
/**
* Reads a 64-bit integer.
* @return the read <code>long</code>
* @throws IOException passed from the underlying <code>InputStream</code>
* @throws UnsupportedOperationException if there are less than 8 bytes remaining
*/
public long readLong() throws IOException {
if (c < d + 8) throw new UnsupportedOperationException("The file isn't that long!");
if (b) return read() + 0x100 * read() + 0x10000 * read() + 0x1000000 * read() + 0x100000000l * read() + 0x10000000000l * read() + 0x1000000000000l * read() + 0x100000000000000l * read();
return 0x100000000000000l * read() + 0x1000000000000l * read() + 0x10000000000l * read() + 0x100000000l * read() + 0x1000000 * read() + 0x10000 * read() + 0x100 * read() + read();
}
/**
* Reads a 32-bit integer.
* @return the read <code>int</code>
* @throws IOException passed from the underlying <code>InputStream</code>
* @throws UnsupportedOperationException if there are less than 4 bytes remaining
*/
public int readInt() throws IOException {
if (c < d + 4) throw new UnsupportedOperationException("The file isn't that long!");
if (b) return read() + 0x100 * read() + 0x10000 * read() + 0x1000000 * read();
return 0x1000000 * read() + 0x10000 * read() + 0x100 * read() + read();
}
/**
* Reads a 16-bit integer.
* @return the read <code>short</code>
* @throws IOException passed from the underlying <code>InputStream</code>
* @throws UnsupportedOperationException if there are less than 2 bytes remaining
*/
public short readShort() throws IOException {
if (c < d + 2) throw new UnsupportedOperationException("The file isn't that long!");
if (b) return (short) (read() + 0x100 * read());
return (short) (0x100 * read() + read());
}
/**
* Reads a singleton byte.
* @return the unsigned integer equivalent to the read byte
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public int read() throws IOException {
int e = a.read();
d++;
if (d == c) close();
return e;
}
/**
* Skips until the specified position.
* @param position until where to skip (exclusive)
* @throws IllegalArgumentException if the specified position has already been reached or if it is past the end of the file
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public void skipUntil(long position) throws IOException {
if (position < d) throw new IllegalArgumentException("That point has already been passed!");
skip(position - d);
}
/**
* Skips over the specified amount of bytes.
* @param amount how many bytes to skip
* @throws IllegalArgumentException if there are less than <code><b>amount</b></code> bytes remaining
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public void skip(long amount) throws IOException {
if (amount > c + d) throw new IllegalArgumentException("The file isn't that long!");
long e = d;
while (d < e +amount) d += a.skip(amount - (d - e));
if (d == c) close();
}
/**
* Closes the underlying <code>InputStream</code>.
* This is called internally automatically once the end of the file has been reached.
* @throws IOException passed from the underlying <code>InputStream</code>
*/
public void close() throws IOException {
a.close();
}
/**
* Returns the current position in the file.
* @return the current position in the file (the next byte to read)
*/
public long getCurrentPosition() {
return d;
}
/**
* Returns the total length of the file.
* @return the total length of the file in bytes
*/
public long getLength() {
return c;
}
/**
* Returns the amount of bytes remaining.
* @return the amount of bytes remaining
*/
public long getRemaining() {
return c - d;
}
}