mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
860 lines (689 loc) • 18 kB
JavaScript
import {uint8ToInt8, uint8ToInt16, uint8ToInt32, uint8ToUint16, uint8ToUint32, uint8ToFloat32, uint8ToFloat64, int8ToUint8, int16ToUint8, int32ToUint8, uint16ToUint8, uint32ToUint8, float32ToUint8, float64ToUint8} from './typecast';
// Memory for all of the xxxToUint type casts.
let uint8 = new Uint8Array(8);
/**
* A binary stream.
*/
export default class BinaryStream {
/**
* @param {ArrayBuffer|ArrayBufferView} buffer
* @param {number=} byteOffset
* @param {number=} byteLength
*/
constructor(buffer, byteOffset, byteLength) {
// If given a view, use its properties.
if (ArrayBuffer.isView(buffer)) {
buffer = buffer.buffer;
byteOffset = buffer.byteOffset;
byteLength = buffer.byteLength;
}
if (!(buffer instanceof ArrayBuffer)) {
throw new TypeError(`BinaryStream: expected ArrayBuffer or ArrayBufferView, got ${buffer}`);
}
/** @member {ArrayBuffer} */
this.buffer = buffer;
/** @member {Uint8Array} */
this.uint8array = new Uint8Array(buffer, byteOffset, byteLength);
/** @member {number} */
this.index = 0;
/** @member {number} */
this.byteLength = buffer.byteLength;
}
/**
* Create a subreader of this reader, at its position, with the given byte length
*
* @param {number} byteLength
* @return {BinaryStream}
*/
substream(byteLength) {
return new BinaryStream(this.buffer, this.index, byteLength);
}
/**
* Get the remaining bytes
*
* @return {number}
*/
remaining() {
return this.byteLength - this.index;
}
/**
* Skip a number of bytes
*
* @param {number} bytes
*/
skip(bytes) {
this.index += bytes;
}
/**
* Set the reader's index
*
* @param {number} index
*/
seek(index) {
this.index = index;
}
/**
* Get the reader's index
*
* @return {number}
*/
tell() {
return this.index;
}
/**
* Peek a string
*
* @param {number} size
* @param {boolean=} allowNulls
* @return {string}
*/
peek(size, allowNulls) {
let uint8array = this.uint8array;
let index = this.index;
let data = '';
for (let i = 0; i < size; i++) {
let b = uint8array[index + i];
// Avoid \0
if (allowNulls || b > 0) {
data += String.fromCharCode(b);
}
}
return data;
}
/**
* Read a string
*
* @param {number} size
* @param {boolean=} allowNulls
* @return {string}
*/
read(size, allowNulls) {
// If the size isn't specified, default to everything
size = size || this.remaining();
let data = this.peek(size, allowNulls);
this.index += size;
return data;
}
/**
* Peeks a string until finding a null byte
*
* @return {string}
*/
peekUntilNull() {
let uint8array = this.uint8array;
let index = this.index;
let data = '';
let b = uint8array[index];
let i = 0;
while (b !== 0) {
data += String.fromCharCode(b);
i += 1;
b = uint8array[index + i];
}
return data;
}
/**
* Read a string until finding a null byte
*
* @param {number} size
* @return {string}
*/
readUntilNull() {
let data = this.peekUntilNull();
this.index += data.length + 1; // +1 for the \0 itself
return data;
}
/**
* Peek a character array.
*
* @param {number} size
* @return {Array<string>}
*/
peekCharArray(size) {
let uint8array = this.uint8array;
let index = this.index;
let data = [];
for (let i = 0; i < size; i++) {
data[i] = String.fromCharCode(uint8array[index + i]);
}
return data;
}
/**
* Read a character array.
*
* @param {number} size
* @return {Array<string>}
*/
readCharArray(size) {
let data = this.peekCharArray(size);
this.index += size;
return data;
}
/**
* Read a 8 bit signed integer
*
* @return {number}
*/
readInt8() {
let index = this.index;
let uint8array = this.uint8array;
let data = uint8ToInt8(uint8array[index]);
this.index += 1;
return data;
}
/**
* Read a 16 bit signed integer
*
* @return {number}
*/
readInt16() {
let index = this.index;
let uint8array = this.uint8array;
let data = uint8ToInt16(uint8array[index], uint8array[index + 1]);
this.index += 2;
return data;
}
/**
* Read a 32 bit signed integer
*
* @return {number}
*/
readInt32() {
let index = this.index;
let uint8array = this.uint8array;
let data = uint8ToInt32(uint8array[index], uint8array[index + 1], uint8array[index + 2], uint8array[index + 3]);
this.index += 4;
return data;
}
/**
* Read a 8 bit unsigned integer
*
* @return {number}
*/
readUint8() {
let data = this.uint8array[this.index];
this.index += 1;
return data;
}
/**
* Read a 16 bit unsigned integer
*
* @return {number}
*/
readUint16() {
let index = this.index;
let uint8array = this.uint8array;
let data = uint8ToUint16(uint8array[index], uint8array[index + 1]);
this.index += 2;
return data;
}
/**
* Read a 32 bit unsigned integer
*
* @return {number}
*/
readUint32() {
let index = this.index;
let uint8array = this.uint8array;
let data = uint8ToUint32(uint8array[index], uint8array[index + 1], uint8array[index + 2], uint8array[index + 3]);
this.index += 4;
return data;
}
/**
* Read a 32 bit float
*
* @return {number}
*/
readFloat32() {
let index = this.index;
let uint8array = this.uint8array;
let data = uint8ToFloat32(uint8array[index], uint8array[index + 1], uint8array[index + 2], uint8array[index + 3]);
this.index += 4;
return data;
}
/**
* Read a 64 bit float
*
* @return {number}
*/
readFloat64() {
let index = this.index;
let uint8array = this.uint8array;
let data = uint8ToFloat64(uint8array[index], uint8array[index + 1], uint8array[index + 2], uint8array[index + 3], uint8array[index + 4], uint8array[index + 5], uint8array[index + 6], uint8array[index + 7]);
this.index += 8;
return data;
}
/**
* Read an array of 8 bit signed integers into the given view
*
* @param {Int8Array|number} view
* @return {Int8Array}
*/
readInt8Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Int8Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
view[i] = uint8ToInt8(uint8array[index + i]);
}
this.index += view.byteLength;
return view;
}
/**
* Read an array of 16 bit signed integers into the given view
*
* @param {Int16Array|number} view
* @return {Int16Array}
*/
readInt16Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Int16Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 2;
view[i] = uint8ToInt16(uint8array[offset], uint8array[offset + 1]);
}
this.index += view.byteLength;
return view;
}
/**
* Read an array of 32 bit signed integers into the given view
*
* @param {Int32Array|number} view
* @return {Int32Array}
*/
readInt32Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Int32Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 4;
view[i] = uint8ToInt16(uint8array[offset], uint8array[offset + 1], uint8array[offset + 2], uint8array[offset + 3]);
}
this.index += view.byteLength;
return view;
}
/**
* Read an array of 8 bit unsigned integers into the given view
*
* @param {Uint8Array|number} view
* @return {Uint8Array}
*/
readUint8Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Uint8Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
view[i] = uint8array[index + i];
}
this.index += view.byteLength;
return view;
}
/**
* Read an array of 16 bit unsigned integers into the given view
*
* @param {Uint16Array|number} view
* @return {Uint16Array}
*/
readUint16Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Uint16Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 2;
view[i] = uint8ToUint16(uint8array[offset], uint8array[offset + 1]);
}
this.index += view.byteLength;
return view;
}
/**
* Read an array of 32 bit unsigned integers into the given view
*
* @param {Uint32Array|number} view
* @return {Uint32Array}
*/
readUint32Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Uint32Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 4;
view[i] = uint8ToUint32(uint8array[offset], uint8array[offset + 1], uint8array[offset + 2], uint8array[offset + 3]);
}
this.index += view.byteLength;
return view;
}
/**
* Read an array of 32 bit floats into the given view
*
* @param {Float32Array|number} view
* @return {Float32Array}
*/
readFloat32Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Float32Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 4;
view[i] = uint8ToFloat32(uint8array[offset], uint8array[offset + 1], uint8array[offset + 2], uint8array[offset + 3]);
}
this.index += view.byteLength;
return view;
}
/**
* Read an array of 64 bit floats into the given view
*
* @param {Float64Array|number} view
* @return {Float64Array}
*/
readFloat64Array(view) {
if (!ArrayBuffer.isView(view)) {
view = new Float64Array(view);
}
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 8;
view[i] = uint8ToFloat64(uint8array[offset], uint8array[offset + 1], uint8array[offset + 2], uint8array[offset + 3], uint8array[offset + 4], uint8array[offset + 5], uint8array[offset + 6], uint8array[offset + 7]);
}
this.index += view.byteLength;
return view;
}
/**
* Read into a typed array.
*
* @param {ArrayBufferView} view
*/
readTypedArray(view) {
let buffer = new Uint8Array(view.buffer);
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = buffer.length; i < l; i++) {
buffer[i] = uint8array[index + i];
}
this.index += buffer.length;
}
/**
* Write a string
*
* @param {string} value
*/
write(value) {
let index = this.index;
let uint8array = this.uint8array;
let count = value.length;
for (let i = 0; i < count; i++) {
uint8array[index + i] = value.charCodeAt(i);
}
this.index += count;
}
/**
* Write a 8 bit signed integer
*
* @param {number} value
*/
writeInt8(value) {
this.uint8array[this.index] = int8ToUint8(value);
this.index += 1;
}
/**
* Write a 16 bit signed integer
*
* @param {number} value
*/
writeInt16(value) {
let index = this.index;
let uint8array = this.uint8array;
int16ToUint8(uint8, value);
uint8array[index] = uint8[0];
uint8array[index + 1] = uint8[1];
this.index += 2;
}
/**
* Write a 32 bit signed integer
*
* @param {number} value
*/
writeInt32(value) {
let index = this.index;
let uint8array = this.uint8array;
int32ToUint8(uint8, value);
uint8array[index] = uint8[0];
uint8array[index + 1] = uint8[1];
uint8array[index + 2] = uint8[2];
uint8array[index + 3] = uint8[3];
this.index += 4;
}
/**
* Write a 8 bit unsigned integer
*
* @param {number} value
*/
writeUint8(value) {
this.uint8array[this.index] = value;
this.index += 1;
}
/**
* Write a 16 bit unsigned integer
*
* @param {number} value
*/
writeUint16(value) {
let index = this.index;
let uint8array = this.uint8array;
uint16ToUint8(uint8, value);
uint8array[index] = uint8[0];
uint8array[index + 1] = uint8[1];
this.index += 2;
}
/**
* Write a 32 bit unsigned integer
*
* @param {number} value
*/
writeUint32(value) {
let index = this.index;
let uint8array = this.uint8array;
uint32ToUint8(uint8, value);
uint8array[index] = uint8[0];
uint8array[index + 1] = uint8[1];
uint8array[index + 2] = uint8[2];
uint8array[index + 3] = uint8[3];
this.index += 4;
}
/**
* Write a 32 bit float
*
* @param {number} value
*/
writeFloat32(value) {
let index = this.index;
let uint8array = this.uint8array;
float32ToUint8(uint8, value);
uint8array[index] = uint8[0];
uint8array[index + 1] = uint8[1];
uint8array[index + 2] = uint8[2];
uint8array[index + 3] = uint8[3];
this.index += 4;
}
/**
* Write a 64 bit float
*
* @param {number} value
*/
writeFloat64(value) {
let index = this.index;
let uint8array = this.uint8array;
float64ToUint8(uint8, value);
uint8array[index] = uint8[0];
uint8array[index + 1] = uint8[1];
uint8array[index + 2] = uint8[2];
uint8array[index + 3] = uint8[3];
uint8array[index + 4] = uint8[4];
uint8array[index + 5] = uint8[5];
uint8array[index + 6] = uint8[6];
uint8array[index + 7] = uint8[7];
this.index += 8;
}
/**
* Write an array of 8 bit signed integers
*
* @param {Int8Array} view
*/
writeInt8Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
uint8array[index + i] = int8ToUint8(view[i]);
}
this.index += view.byteLength;
}
/**
* Write an array of 16 bit signed integers
*
* @param {Int16Array} view
*/
writeInt16Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 2;
int16ToUint8(uint8, view[i]);
uint8array[offset] = uint8[0];
uint8array[offset + 1] = uint8[1];
}
this.index += view.byteLength;
}
/**
* Write an array of 32 bit signed integers
*
* @param {Int32Array} view
*/
writeInt32Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 4;
int32ToUint8(uint8, view[i]);
uint8array[offset] = uint8[0];
uint8array[offset + 1] = uint8[1];
uint8array[offset + 2] = uint8[2];
uint8array[offset + 3] = uint8[3];
}
this.index += view.byteLength;
}
/**
* Write an array of 8 bit unsigned integers
*
* @param {Uint8Array} view
*/
writeUint8Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
uint8array[index + i] = view[i];
}
this.index += view.byteLength;
}
/**
* Write an array of 16 bit unsigned integers
*
* @param {Uint16Array} view
*/
writeUint16Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 2;
uint16ToUint8(uint8, view[i]);
uint8array[offset] = uint8[0];
uint8array[offset + 1] = uint8[1];
}
this.index += view.byteLength;
}
/**
* Write an array of 32 bit unsigned integers
*
* @param {Uint32Array} view
*/
writeUint32Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 4;
uint32ToUint8(uint8, view[i]);
uint8array[offset] = uint8[0];
uint8array[offset + 1] = uint8[1];
uint8array[offset + 2] = uint8[2];
uint8array[offset + 3] = uint8[3];
}
this.index += view.byteLength;
}
/**
* Write an array of 32 bit floats
*
* @param {Float32Array} view
*/
writeFloat32Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 4;
float32ToUint8(uint8, view[i]);
uint8array[offset] = uint8[0];
uint8array[offset + 1] = uint8[1];
uint8array[offset + 2] = uint8[2];
uint8array[offset + 3] = uint8[3];
}
this.index += view.byteLength;
}
/**
* Write an array of 64 bit floats
*
* @param {Float64Array} view
*/
writeFloat64Array(view) {
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = view.length; i < l; i++) {
let offset = index + i * 8;
float64ToUint8(uint8, view[i]);
uint8array[offset] = uint8[0];
uint8array[offset + 1] = uint8[1];
uint8array[offset + 2] = uint8[2];
uint8array[offset + 3] = uint8[3];
uint8array[offset + 4] = uint8[4];
uint8array[offset + 5] = uint8[5];
uint8array[offset + 6] = uint8[6];
uint8array[offset + 7] = uint8[7];
}
this.index += view.byteLength;
}
/**
* Write a typed array.
*
* @param {ArrayBufferView} view
*/
writeTypedArray(view) {
let buffer = new Uint8Array(view.buffer);
let index = this.index;
let uint8array = this.uint8array;
for (let i = 0, l = buffer.length; i < l; i++) {
uint8array[index + i] = buffer[i];
}
}
}