From f779b83e25a89d040c0ed605d8ec5b61d9128214 Mon Sep 17 00:00:00 2001 From: zhl Date: Tue, 11 Jun 2019 20:01:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84fc2=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fc/js/nes-embed.js | 1 - fc2/index.html | 21 +- fc2/js/bytebuffer.js | 3651 ++++++++++++++ fc2/js/jcmatchvs.js | 1550 ++++++ fc2/js/jsnes.min.js | 2 - fc2/js/long.js | 1220 +++++ fc2/js/netWorkHandle.js | 63 + fc2/js/network.js | 328 ++ fc2/js/protobuf.js | 5246 ++++++++++++++++++++ fc2/{images => resources}/kingsomevs.proto | 0 fc2/{images => resources}/messages.proto | 0 jcfw/logger/httpcli.js | 203 + jcfw/logger/httpclient.js | 50 + jcfw/logger/jcgamelog.js | 967 ++++ jcfw/logger/logger.js | 31 + jcfw/logger/urlbuilder.js | 33 + package.json | 10 +- tasks/fc2.js | 17 +- tasks/jcfw.js | 30 + 19 files changed, 13407 insertions(+), 16 deletions(-) create mode 100644 fc2/js/bytebuffer.js create mode 100644 fc2/js/jcmatchvs.js delete mode 100644 fc2/js/jsnes.min.js create mode 100644 fc2/js/long.js create mode 100644 fc2/js/netWorkHandle.js create mode 100644 fc2/js/network.js create mode 100644 fc2/js/protobuf.js rename fc2/{images => resources}/kingsomevs.proto (100%) rename fc2/{images => resources}/messages.proto (100%) create mode 100644 jcfw/logger/httpcli.js create mode 100644 jcfw/logger/httpclient.js create mode 100644 jcfw/logger/jcgamelog.js create mode 100644 jcfw/logger/logger.js create mode 100644 jcfw/logger/urlbuilder.js create mode 100644 tasks/jcfw.js diff --git a/fc/js/nes-embed.js b/fc/js/nes-embed.js index fc12a80..4f64810 100644 --- a/fc/js/nes-embed.js +++ b/fc/js/nes-embed.js @@ -12,7 +12,6 @@ var audio_samples_L = new Float32Array(SAMPLE_COUNT); var audio_samples_R = new Float32Array(SAMPLE_COUNT); var audio_write_cursor = 0, audio_read_cursor = 0; - // require(['network.js'], function (foo) { // network = foo // }); diff --git a/fc2/index.html b/fc2/index.html index 084afde..f9ec85f 100644 --- a/fc2/index.html +++ b/fc2/index.html @@ -7,12 +7,6 @@ - - - - - - @@ -63,5 +57,20 @@ + + + + + diff --git a/fc2/js/bytebuffer.js b/fc2/js/bytebuffer.js new file mode 100644 index 0000000..476756b --- /dev/null +++ b/fc2/js/bytebuffer.js @@ -0,0 +1,3651 @@ +/* + Copyright 2013-2014 Daniel Wirtz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/** + * @license bytebuffer.js (c) 2015 Daniel Wirtz + * Backing buffer: ArrayBuffer, Accessor: Uint8Array + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/bytebuffer.js for details + */ +(function(global, factory) { + + /* AMD */ if (typeof define === 'function' && define["amd"]) + define(["long"], factory); + /* CommonJS */ else if (typeof require === 'function' && typeof module === "object" && module && module["exports"]) + module['exports'] = (function() { + var Long; try { Long = require("long"); } catch (e) {} + return factory(Long); + })(); + /* Global */ else + (global["dcodeIO"] = global["dcodeIO"] || {})["ByteBuffer"] = factory(global["dcodeIO"]["Long"]); + +})(this, function(Long) { + "use strict"; + + /** + * Constructs a new ByteBuffer. + * @class The swiss army knife for binary data in JavaScript. + * @exports ByteBuffer + * @constructor + * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @expose + */ + var ByteBuffer = function(capacity, littleEndian, noAssert) { + if (typeof capacity === 'undefined') + capacity = ByteBuffer.DEFAULT_CAPACITY; + if (typeof littleEndian === 'undefined') + littleEndian = ByteBuffer.DEFAULT_ENDIAN; + if (typeof noAssert === 'undefined') + noAssert = ByteBuffer.DEFAULT_NOASSERT; + if (!noAssert) { + capacity = capacity | 0; + if (capacity < 0) + throw RangeError("Illegal capacity"); + littleEndian = !!littleEndian; + noAssert = !!noAssert; + } + + /** + * Backing ArrayBuffer. + * @type {!ArrayBuffer} + * @expose + */ + this.buffer = capacity === 0 ? EMPTY_BUFFER : new ArrayBuffer(capacity); + + /** + * Uint8Array utilized to manipulate the backing buffer. Becomes `null` if the backing buffer has a capacity of `0`. + * @type {?Uint8Array} + * @expose + */ + this.view = capacity === 0 ? null : new Uint8Array(this.buffer); + + /** + * Absolute read/write offset. + * @type {number} + * @expose + * @see ByteBuffer#flip + * @see ByteBuffer#clear + */ + this.offset = 0; + + /** + * Marked offset. + * @type {number} + * @expose + * @see ByteBuffer#mark + * @see ByteBuffer#reset + */ + this.markedOffset = -1; + + /** + * Absolute limit of the contained data. Set to the backing buffer's capacity upon allocation. + * @type {number} + * @expose + * @see ByteBuffer#flip + * @see ByteBuffer#clear + */ + this.limit = capacity; + + /** + * Whether to use little endian byte order, defaults to `false` for big endian. + * @type {boolean} + * @expose + */ + this.littleEndian = littleEndian; + + /** + * Whether to skip assertions of offsets and values, defaults to `false`. + * @type {boolean} + * @expose + */ + this.noAssert = noAssert; + }; + + /** + * ByteBuffer version. + * @type {string} + * @const + * @expose + */ + ByteBuffer.VERSION = "5.0.0"; + + /** + * Little endian constant that can be used instead of its boolean value. Evaluates to `true`. + * @type {boolean} + * @const + * @expose + */ + ByteBuffer.LITTLE_ENDIAN = true; + + /** + * Big endian constant that can be used instead of its boolean value. Evaluates to `false`. + * @type {boolean} + * @const + * @expose + */ + ByteBuffer.BIG_ENDIAN = false; + + /** + * Default initial capacity of `16`. + * @type {number} + * @expose + */ + ByteBuffer.DEFAULT_CAPACITY = 16; + + /** + * Default endianess of `false` for big endian. + * @type {boolean} + * @expose + */ + ByteBuffer.DEFAULT_ENDIAN = ByteBuffer.BIG_ENDIAN; + + /** + * Default no assertions flag of `false`. + * @type {boolean} + * @expose + */ + ByteBuffer.DEFAULT_NOASSERT = false; + + /** + * A `Long` class for representing a 64-bit two's-complement integer value. May be `null` if Long.js has not been loaded + * and int64 support is not available. + * @type {?Long} + * @const + * @see https://github.com/dcodeIO/long.js + * @expose + */ + ByteBuffer.Long = Long || null; + + /** + * @alias ByteBuffer.prototype + * @inner + */ + var ByteBufferPrototype = ByteBuffer.prototype; + + /** + * An indicator used to reliably determine if an object is a ByteBuffer or not. + * @type {boolean} + * @const + * @expose + * @private + */ + ByteBufferPrototype.__isByteBuffer__; + + Object.defineProperty(ByteBufferPrototype, "__isByteBuffer__", { + value: true, + enumerable: false, + configurable: false + }); + + // helpers + + /** + * @type {!ArrayBuffer} + * @inner + */ + var EMPTY_BUFFER = new ArrayBuffer(0); + + /** + * String.fromCharCode reference for compile-time renaming. + * @type {function(...number):string} + * @inner + */ + var stringFromCharCode = String.fromCharCode; + + /** + * Creates a source function for a string. + * @param {string} s String to read from + * @returns {function():number|null} Source function returning the next char code respectively `null` if there are + * no more characters left. + * @throws {TypeError} If the argument is invalid + * @inner + */ + function stringSource(s) { + var i=0; return function() { + return i < s.length ? s.charCodeAt(i++) : null; + }; + } + + /** + * Creates a destination function for a string. + * @returns {function(number=):undefined|string} Destination function successively called with the next char code. + * Returns the final string when called without arguments. + * @inner + */ + function stringDestination() { + var cs = [], ps = []; return function() { + if (arguments.length === 0) + return ps.join('')+stringFromCharCode.apply(String, cs); + if (cs.length + arguments.length > 1024) + ps.push(stringFromCharCode.apply(String, cs)), + cs.length = 0; + Array.prototype.push.apply(cs, arguments); + }; + } + + /** + * Gets the accessor type. + * @returns {Function} `Buffer` under node.js, `Uint8Array` respectively `DataView` in the browser (classes) + * @expose + */ + ByteBuffer.accessor = function() { + return Uint8Array; + }; + /** + * Allocates a new ByteBuffer backed by a buffer of the specified capacity. + * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} + * @expose + */ + ByteBuffer.allocate = function(capacity, littleEndian, noAssert) { + return new ByteBuffer(capacity, littleEndian, noAssert); + }; + + /** + * Concatenates multiple ByteBuffers into one. + * @param {!Array.} buffers Buffers to concatenate + * @param {(string|boolean)=} encoding String encoding if `buffers` contains a string ("base64", "hex", "binary", + * defaults to "utf8") + * @param {boolean=} littleEndian Whether to use little or big endian byte order for the resulting ByteBuffer. Defaults + * to {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values for the resulting ByteBuffer. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} Concatenated ByteBuffer + * @expose + */ + ByteBuffer.concat = function(buffers, encoding, littleEndian, noAssert) { + if (typeof encoding === 'boolean' || typeof encoding !== 'string') { + noAssert = littleEndian; + littleEndian = encoding; + encoding = undefined; + } + var capacity = 0; + for (var i=0, k=buffers.length, length; i 0) capacity += length; + } + if (capacity === 0) + return new ByteBuffer(0, littleEndian, noAssert); + var bb = new ByteBuffer(capacity, littleEndian, noAssert), + bi; + i=0; while (i} buffer Anything that can be wrapped + * @param {(string|boolean)=} encoding String encoding if `buffer` is a string ("base64", "hex", "binary", defaults to + * "utf8") + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} A ByteBuffer wrapping `buffer` + * @expose + */ + ByteBuffer.wrap = function(buffer, encoding, littleEndian, noAssert) { + if (typeof encoding !== 'string') { + noAssert = littleEndian; + littleEndian = encoding; + encoding = undefined; + } + if (typeof buffer === 'string') { + if (typeof encoding === 'undefined') + encoding = "utf8"; + switch (encoding) { + case "base64": + return ByteBuffer.fromBase64(buffer, littleEndian); + case "hex": + return ByteBuffer.fromHex(buffer, littleEndian); + case "binary": + return ByteBuffer.fromBinary(buffer, littleEndian); + case "utf8": + return ByteBuffer.fromUTF8(buffer, littleEndian); + case "debug": + return ByteBuffer.fromDebug(buffer, littleEndian); + default: + throw Error("Unsupported encoding: "+encoding); + } + } + if (buffer === null || typeof buffer !== 'object') + throw TypeError("Illegal buffer"); + var bb; + if (ByteBuffer.isByteBuffer(buffer)) { + bb = ByteBufferPrototype.clone.call(buffer); + bb.markedOffset = -1; + return bb; + } + if (buffer instanceof Uint8Array) { // Extract ArrayBuffer from Uint8Array + bb = new ByteBuffer(0, littleEndian, noAssert); + if (buffer.length > 0) { // Avoid references to more than one EMPTY_BUFFER + bb.buffer = buffer.buffer; + bb.offset = buffer.byteOffset; + bb.limit = buffer.byteOffset + buffer.byteLength; + bb.view = new Uint8Array(buffer.buffer); + } + } else if (buffer instanceof ArrayBuffer) { // Reuse ArrayBuffer + bb = new ByteBuffer(0, littleEndian, noAssert); + if (buffer.byteLength > 0) { + bb.buffer = buffer; + bb.offset = 0; + bb.limit = buffer.byteLength; + bb.view = buffer.byteLength > 0 ? new Uint8Array(buffer) : null; + } + } else if (Object.prototype.toString.call(buffer) === "[object Array]") { // Create from octets + bb = new ByteBuffer(buffer.length, littleEndian, noAssert); + bb.limit = buffer.length; + for (var i=0; i>>= 0; + if (offset < 0 || offset + length > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.byteLength); + } + var slice = this.slice(offset, offset + length); + if (relative) this.offset += length; + return slice; + }; + + /** + * Writes a payload of bytes. This is an alias of {@link ByteBuffer#append}. + * @function + * @param {!ByteBuffer|!ArrayBuffer|!Uint8Array|string} source Data to write. If `source` is a ByteBuffer, its offsets + * will be modified according to the performed read operation. + * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeBytes = ByteBufferPrototype.append; + + // types/ints/int8 + + /** + * Writes an 8bit signed integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeInt8 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 1; + var capacity0 = this.buffer.byteLength; + if (offset > capacity0) + this.resize((capacity0 *= 2) > offset ? capacity0 : offset); + offset -= 1; + this.view[offset] = value; + if (relative) this.offset += 1; + return this; + }; + + /** + * Writes an 8bit signed integer. This is an alias of {@link ByteBuffer#writeInt8}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeByte = ByteBufferPrototype.writeInt8; + + /** + * Reads an 8bit signed integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readInt8 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var value = this.view[offset]; + if ((value & 0x80) === 0x80) value = -(0xFF - value + 1); // Cast to signed + if (relative) this.offset += 1; + return value; + }; + + /** + * Reads an 8bit signed integer. This is an alias of {@link ByteBuffer#readInt8}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readByte = ByteBufferPrototype.readInt8; + + /** + * Writes an 8bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeUint8 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value >>>= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 1; + var capacity1 = this.buffer.byteLength; + if (offset > capacity1) + this.resize((capacity1 *= 2) > offset ? capacity1 : offset); + offset -= 1; + this.view[offset] = value; + if (relative) this.offset += 1; + return this; + }; + + /** + * Writes an 8bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint8}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeUInt8 = ByteBufferPrototype.writeUint8; + + /** + * Reads an 8bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readUint8 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var value = this.view[offset]; + if (relative) this.offset += 1; + return value; + }; + + /** + * Reads an 8bit unsigned integer. This is an alias of {@link ByteBuffer#readUint8}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readUInt8 = ByteBufferPrototype.readUint8; + + // types/ints/int16 + + /** + * Writes a 16bit signed integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @throws {TypeError} If `offset` or `value` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.writeInt16 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 2; + var capacity2 = this.buffer.byteLength; + if (offset > capacity2) + this.resize((capacity2 *= 2) > offset ? capacity2 : offset); + offset -= 2; + if (this.littleEndian) { + this.view[offset+1] = (value & 0xFF00) >>> 8; + this.view[offset ] = value & 0x00FF; + } else { + this.view[offset] = (value & 0xFF00) >>> 8; + this.view[offset+1] = value & 0x00FF; + } + if (relative) this.offset += 2; + return this; + }; + + /** + * Writes a 16bit signed integer. This is an alias of {@link ByteBuffer#writeInt16}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @throws {TypeError} If `offset` or `value` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.writeShort = ByteBufferPrototype.writeInt16; + + /** + * Reads a 16bit signed integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @returns {number} Value read + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.readInt16 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 2 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength); + } + var value = 0; + if (this.littleEndian) { + value = this.view[offset ]; + value |= this.view[offset+1] << 8; + } else { + value = this.view[offset ] << 8; + value |= this.view[offset+1]; + } + if ((value & 0x8000) === 0x8000) value = -(0xFFFF - value + 1); // Cast to signed + if (relative) this.offset += 2; + return value; + }; + + /** + * Reads a 16bit signed integer. This is an alias of {@link ByteBuffer#readInt16}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @returns {number} Value read + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.readShort = ByteBufferPrototype.readInt16; + + /** + * Writes a 16bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @throws {TypeError} If `offset` or `value` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.writeUint16 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value >>>= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 2; + var capacity3 = this.buffer.byteLength; + if (offset > capacity3) + this.resize((capacity3 *= 2) > offset ? capacity3 : offset); + offset -= 2; + if (this.littleEndian) { + this.view[offset+1] = (value & 0xFF00) >>> 8; + this.view[offset ] = value & 0x00FF; + } else { + this.view[offset] = (value & 0xFF00) >>> 8; + this.view[offset+1] = value & 0x00FF; + } + if (relative) this.offset += 2; + return this; + }; + + /** + * Writes a 16bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint16}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @throws {TypeError} If `offset` or `value` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.writeUInt16 = ByteBufferPrototype.writeUint16; + + /** + * Reads a 16bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @returns {number} Value read + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.readUint16 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 2 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength); + } + var value = 0; + if (this.littleEndian) { + value = this.view[offset ]; + value |= this.view[offset+1] << 8; + } else { + value = this.view[offset ] << 8; + value |= this.view[offset+1]; + } + if (relative) this.offset += 2; + return value; + }; + + /** + * Reads a 16bit unsigned integer. This is an alias of {@link ByteBuffer#readUint16}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. + * @returns {number} Value read + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @expose + */ + ByteBufferPrototype.readUInt16 = ByteBufferPrototype.readUint16; + + // types/ints/int32 + + /** + * Writes a 32bit signed integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @expose + */ + ByteBufferPrototype.writeInt32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 4; + var capacity4 = this.buffer.byteLength; + if (offset > capacity4) + this.resize((capacity4 *= 2) > offset ? capacity4 : offset); + offset -= 4; + if (this.littleEndian) { + this.view[offset+3] = (value >>> 24) & 0xFF; + this.view[offset+2] = (value >>> 16) & 0xFF; + this.view[offset+1] = (value >>> 8) & 0xFF; + this.view[offset ] = value & 0xFF; + } else { + this.view[offset ] = (value >>> 24) & 0xFF; + this.view[offset+1] = (value >>> 16) & 0xFF; + this.view[offset+2] = (value >>> 8) & 0xFF; + this.view[offset+3] = value & 0xFF; + } + if (relative) this.offset += 4; + return this; + }; + + /** + * Writes a 32bit signed integer. This is an alias of {@link ByteBuffer#writeInt32}. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @expose + */ + ByteBufferPrototype.writeInt = ByteBufferPrototype.writeInt32; + + /** + * Reads a 32bit signed integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readInt32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var value = 0; + if (this.littleEndian) { + value = this.view[offset+2] << 16; + value |= this.view[offset+1] << 8; + value |= this.view[offset ]; + value += this.view[offset+3] << 24 >>> 0; + } else { + value = this.view[offset+1] << 16; + value |= this.view[offset+2] << 8; + value |= this.view[offset+3]; + value += this.view[offset ] << 24 >>> 0; + } + value |= 0; // Cast to signed + if (relative) this.offset += 4; + return value; + }; + + /** + * Reads a 32bit signed integer. This is an alias of {@link ByteBuffer#readInt32}. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readInt = ByteBufferPrototype.readInt32; + + /** + * Writes a 32bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @expose + */ + ByteBufferPrototype.writeUint32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value >>>= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 4; + var capacity5 = this.buffer.byteLength; + if (offset > capacity5) + this.resize((capacity5 *= 2) > offset ? capacity5 : offset); + offset -= 4; + if (this.littleEndian) { + this.view[offset+3] = (value >>> 24) & 0xFF; + this.view[offset+2] = (value >>> 16) & 0xFF; + this.view[offset+1] = (value >>> 8) & 0xFF; + this.view[offset ] = value & 0xFF; + } else { + this.view[offset ] = (value >>> 24) & 0xFF; + this.view[offset+1] = (value >>> 16) & 0xFF; + this.view[offset+2] = (value >>> 8) & 0xFF; + this.view[offset+3] = value & 0xFF; + } + if (relative) this.offset += 4; + return this; + }; + + /** + * Writes a 32bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint32}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @expose + */ + ByteBufferPrototype.writeUInt32 = ByteBufferPrototype.writeUint32; + + /** + * Reads a 32bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readUint32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var value = 0; + if (this.littleEndian) { + value = this.view[offset+2] << 16; + value |= this.view[offset+1] << 8; + value |= this.view[offset ]; + value += this.view[offset+3] << 24 >>> 0; + } else { + value = this.view[offset+1] << 16; + value |= this.view[offset+2] << 8; + value |= this.view[offset+3]; + value += this.view[offset ] << 24 >>> 0; + } + if (relative) this.offset += 4; + return value; + }; + + /** + * Reads a 32bit unsigned integer. This is an alias of {@link ByteBuffer#readUint32}. + * @function + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} Value read + * @expose + */ + ByteBufferPrototype.readUInt32 = ByteBufferPrototype.readUint32; + + // types/ints/int64 + + if (Long) { + + /** + * Writes a 64bit signed integer. + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeInt64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (typeof value === 'string') + value = Long.fromString(value); + else if (!(value && value instanceof Long)) + throw TypeError("Illegal value: "+value+" (not an integer or Long)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (typeof value === 'string') + value = Long.fromString(value); + offset += 8; + var capacity6 = this.buffer.byteLength; + if (offset > capacity6) + this.resize((capacity6 *= 2) > offset ? capacity6 : offset); + offset -= 8; + var lo = value.low, + hi = value.high; + if (this.littleEndian) { + this.view[offset+3] = (lo >>> 24) & 0xFF; + this.view[offset+2] = (lo >>> 16) & 0xFF; + this.view[offset+1] = (lo >>> 8) & 0xFF; + this.view[offset ] = lo & 0xFF; + offset += 4; + this.view[offset+3] = (hi >>> 24) & 0xFF; + this.view[offset+2] = (hi >>> 16) & 0xFF; + this.view[offset+1] = (hi >>> 8) & 0xFF; + this.view[offset ] = hi & 0xFF; + } else { + this.view[offset ] = (hi >>> 24) & 0xFF; + this.view[offset+1] = (hi >>> 16) & 0xFF; + this.view[offset+2] = (hi >>> 8) & 0xFF; + this.view[offset+3] = hi & 0xFF; + offset += 4; + this.view[offset ] = (lo >>> 24) & 0xFF; + this.view[offset+1] = (lo >>> 16) & 0xFF; + this.view[offset+2] = (lo >>> 8) & 0xFF; + this.view[offset+3] = lo & 0xFF; + } + if (relative) this.offset += 8; + return this; + }; + + /** + * Writes a 64bit signed integer. This is an alias of {@link ByteBuffer#writeInt64}. + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeLong = ByteBufferPrototype.writeInt64; + + /** + * Reads a 64bit signed integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!Long} + * @expose + */ + ByteBufferPrototype.readInt64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 8 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength); + } + var lo = 0, + hi = 0; + if (this.littleEndian) { + lo = this.view[offset+2] << 16; + lo |= this.view[offset+1] << 8; + lo |= this.view[offset ]; + lo += this.view[offset+3] << 24 >>> 0; + offset += 4; + hi = this.view[offset+2] << 16; + hi |= this.view[offset+1] << 8; + hi |= this.view[offset ]; + hi += this.view[offset+3] << 24 >>> 0; + } else { + hi = this.view[offset+1] << 16; + hi |= this.view[offset+2] << 8; + hi |= this.view[offset+3]; + hi += this.view[offset ] << 24 >>> 0; + offset += 4; + lo = this.view[offset+1] << 16; + lo |= this.view[offset+2] << 8; + lo |= this.view[offset+3]; + lo += this.view[offset ] << 24 >>> 0; + } + var value = new Long(lo, hi, false); + if (relative) this.offset += 8; + return value; + }; + + /** + * Reads a 64bit signed integer. This is an alias of {@link ByteBuffer#readInt64}. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!Long} + * @expose + */ + ByteBufferPrototype.readLong = ByteBufferPrototype.readInt64; + + /** + * Writes a 64bit unsigned integer. + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeUint64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (typeof value === 'string') + value = Long.fromString(value); + else if (!(value && value instanceof Long)) + throw TypeError("Illegal value: "+value+" (not an integer or Long)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (typeof value === 'string') + value = Long.fromString(value); + offset += 8; + var capacity7 = this.buffer.byteLength; + if (offset > capacity7) + this.resize((capacity7 *= 2) > offset ? capacity7 : offset); + offset -= 8; + var lo = value.low, + hi = value.high; + if (this.littleEndian) { + this.view[offset+3] = (lo >>> 24) & 0xFF; + this.view[offset+2] = (lo >>> 16) & 0xFF; + this.view[offset+1] = (lo >>> 8) & 0xFF; + this.view[offset ] = lo & 0xFF; + offset += 4; + this.view[offset+3] = (hi >>> 24) & 0xFF; + this.view[offset+2] = (hi >>> 16) & 0xFF; + this.view[offset+1] = (hi >>> 8) & 0xFF; + this.view[offset ] = hi & 0xFF; + } else { + this.view[offset ] = (hi >>> 24) & 0xFF; + this.view[offset+1] = (hi >>> 16) & 0xFF; + this.view[offset+2] = (hi >>> 8) & 0xFF; + this.view[offset+3] = hi & 0xFF; + offset += 4; + this.view[offset ] = (lo >>> 24) & 0xFF; + this.view[offset+1] = (lo >>> 16) & 0xFF; + this.view[offset+2] = (lo >>> 8) & 0xFF; + this.view[offset+3] = lo & 0xFF; + } + if (relative) this.offset += 8; + return this; + }; + + /** + * Writes a 64bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint64}. + * @function + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeUInt64 = ByteBufferPrototype.writeUint64; + + /** + * Reads a 64bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!Long} + * @expose + */ + ByteBufferPrototype.readUint64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 8 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength); + } + var lo = 0, + hi = 0; + if (this.littleEndian) { + lo = this.view[offset+2] << 16; + lo |= this.view[offset+1] << 8; + lo |= this.view[offset ]; + lo += this.view[offset+3] << 24 >>> 0; + offset += 4; + hi = this.view[offset+2] << 16; + hi |= this.view[offset+1] << 8; + hi |= this.view[offset ]; + hi += this.view[offset+3] << 24 >>> 0; + } else { + hi = this.view[offset+1] << 16; + hi |= this.view[offset+2] << 8; + hi |= this.view[offset+3]; + hi += this.view[offset ] << 24 >>> 0; + offset += 4; + lo = this.view[offset+1] << 16; + lo |= this.view[offset+2] << 8; + lo |= this.view[offset+3]; + lo += this.view[offset ] << 24 >>> 0; + } + var value = new Long(lo, hi, true); + if (relative) this.offset += 8; + return value; + }; + + /** + * Reads a 64bit unsigned integer. This is an alias of {@link ByteBuffer#readUint64}. + * @function + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!Long} + * @expose + */ + ByteBufferPrototype.readUInt64 = ByteBufferPrototype.readUint64; + + } // Long + + + // types/floats/float32 + + /* + ieee754 - https://github.com/feross/ieee754 + + The MIT License (MIT) + + Copyright (c) Feross Aboukhadijeh + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + + /** + * Reads an IEEE754 float from a byte array. + * @param {!Array} buffer + * @param {number} offset + * @param {boolean} isLE + * @param {number} mLen + * @param {number} nBytes + * @returns {number} + * @inner + */ + function ieee754_read(buffer, offset, isLE, mLen, nBytes) { + var e, m, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = isLE ? (nBytes - 1) : 0, + d = isLE ? -1 : 1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); + } + + /** + * Writes an IEEE754 float to a byte array. + * @param {!Array} buffer + * @param {number} value + * @param {number} offset + * @param {boolean} isLE + * @param {number} mLen + * @param {number} nBytes + * @inner + */ + function ieee754_write(buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isLE ? 0 : (nBytes - 1), + d = isLE ? 1 : -1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128; + } + + /** + * Writes a 32bit float. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeFloat32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number') + throw TypeError("Illegal value: "+value+" (not a number)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 4; + var capacity8 = this.buffer.byteLength; + if (offset > capacity8) + this.resize((capacity8 *= 2) > offset ? capacity8 : offset); + offset -= 4; + ieee754_write(this.view, value, offset, this.littleEndian, 23, 4); + if (relative) this.offset += 4; + return this; + }; + + /** + * Writes a 32bit float. This is an alias of {@link ByteBuffer#writeFloat32}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeFloat = ByteBufferPrototype.writeFloat32; + + /** + * Reads a 32bit float. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readFloat32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var value = ieee754_read(this.view, offset, this.littleEndian, 23, 4); + if (relative) this.offset += 4; + return value; + }; + + /** + * Reads a 32bit float. This is an alias of {@link ByteBuffer#readFloat32}. + * @function + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readFloat = ByteBufferPrototype.readFloat32; + + // types/floats/float64 + + /** + * Writes a 64bit float. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeFloat64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number') + throw TypeError("Illegal value: "+value+" (not a number)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + offset += 8; + var capacity9 = this.buffer.byteLength; + if (offset > capacity9) + this.resize((capacity9 *= 2) > offset ? capacity9 : offset); + offset -= 8; + ieee754_write(this.view, value, offset, this.littleEndian, 52, 8); + if (relative) this.offset += 8; + return this; + }; + + /** + * Writes a 64bit float. This is an alias of {@link ByteBuffer#writeFloat64}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.writeDouble = ByteBufferPrototype.writeFloat64; + + /** + * Reads a 64bit float. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readFloat64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 8 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength); + } + var value = ieee754_read(this.view, offset, this.littleEndian, 52, 8); + if (relative) this.offset += 8; + return value; + }; + + /** + * Reads a 64bit float. This is an alias of {@link ByteBuffer#readFloat64}. + * @function + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. + * @returns {number} + * @expose + */ + ByteBufferPrototype.readDouble = ByteBufferPrototype.readFloat64; + + + // types/varints/varint32 + + /** + * Maximum number of bytes required to store a 32bit base 128 variable-length integer. + * @type {number} + * @const + * @expose + */ + ByteBuffer.MAX_VARINT32_BYTES = 5; + + /** + * Calculates the actual number of bytes required to store a 32bit base 128 variable-length integer. + * @param {number} value Value to encode + * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT32_BYTES} + * @expose + */ + ByteBuffer.calculateVarint32 = function(value) { + // ref: src/google/protobuf/io/coded_stream.cc + value = value >>> 0; + if (value < 1 << 7 ) return 1; + else if (value < 1 << 14) return 2; + else if (value < 1 << 21) return 3; + else if (value < 1 << 28) return 4; + else return 5; + }; + + /** + * Zigzag encodes a signed 32bit integer so that it can be effectively used with varint encoding. + * @param {number} n Signed 32bit integer + * @returns {number} Unsigned zigzag encoded 32bit integer + * @expose + */ + ByteBuffer.zigZagEncode32 = function(n) { + return (((n |= 0) << 1) ^ (n >> 31)) >>> 0; // ref: src/google/protobuf/wire_format_lite.h + }; + + /** + * Decodes a zigzag encoded signed 32bit integer. + * @param {number} n Unsigned zigzag encoded 32bit integer + * @returns {number} Signed 32bit integer + * @expose + */ + ByteBuffer.zigZagDecode32 = function(n) { + return ((n >>> 1) ^ -(n & 1)) | 0; // // ref: src/google/protobuf/wire_format_lite.h + }; + + /** + * Writes a 32bit base 128 variable-length integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written + * @expose + */ + ByteBufferPrototype.writeVarint32 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var size = ByteBuffer.calculateVarint32(value), + b; + offset += size; + var capacity10 = this.buffer.byteLength; + if (offset > capacity10) + this.resize((capacity10 *= 2) > offset ? capacity10 : offset); + offset -= size; + value >>>= 0; + while (value >= 0x80) { + b = (value & 0x7f) | 0x80; + this.view[offset++] = b; + value >>>= 7; + } + this.view[offset++] = value; + if (relative) { + this.offset = offset; + return this; + } + return size; + }; + + /** + * Writes a zig-zag encoded (signed) 32bit base 128 variable-length integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written + * @expose + */ + ByteBufferPrototype.writeVarint32ZigZag = function(value, offset) { + return this.writeVarint32(ByteBuffer.zigZagEncode32(value), offset); + }; + + /** + * Reads a 32bit base 128 variable-length integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read + * and the actual number of bytes read. + * @throws {Error} If it's not a valid varint. Has a property `truncated = true` if there is not enough data available + * to fully decode the varint. + * @expose + */ + ByteBufferPrototype.readVarint32 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var c = 0, + value = 0 >>> 0, + b; + do { + if (!this.noAssert && offset > this.limit) { + var err = Error("Truncated"); + err['truncated'] = true; + throw err; + } + b = this.view[offset++]; + if (c < 5) + value |= (b & 0x7f) << (7*c); + ++c; + } while ((b & 0x80) !== 0); + value |= 0; + if (relative) { + this.offset = offset; + return value; + } + return { + "value": value, + "length": c + }; + }; + + /** + * Reads a zig-zag encoded (signed) 32bit base 128 variable-length integer. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read + * and the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBufferPrototype.readVarint32ZigZag = function(offset) { + var val = this.readVarint32(offset); + if (typeof val === 'object') + val["value"] = ByteBuffer.zigZagDecode32(val["value"]); + else + val = ByteBuffer.zigZagDecode32(val); + return val; + }; + + // types/varints/varint64 + + if (Long) { + + /** + * Maximum number of bytes required to store a 64bit base 128 variable-length integer. + * @type {number} + * @const + * @expose + */ + ByteBuffer.MAX_VARINT64_BYTES = 10; + + /** + * Calculates the actual number of bytes required to store a 64bit base 128 variable-length integer. + * @param {number|!Long} value Value to encode + * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT64_BYTES} + * @expose + */ + ByteBuffer.calculateVarint64 = function(value) { + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (typeof value === 'string') + value = Long.fromString(value); + // ref: src/google/protobuf/io/coded_stream.cc + var part0 = value.toInt() >>> 0, + part1 = value.shiftRightUnsigned(28).toInt() >>> 0, + part2 = value.shiftRightUnsigned(56).toInt() >>> 0; + if (part2 == 0) { + if (part1 == 0) { + if (part0 < 1 << 14) + return part0 < 1 << 7 ? 1 : 2; + else + return part0 < 1 << 21 ? 3 : 4; + } else { + if (part1 < 1 << 14) + return part1 < 1 << 7 ? 5 : 6; + else + return part1 < 1 << 21 ? 7 : 8; + } + } else + return part2 < 1 << 7 ? 9 : 10; + }; + + /** + * Zigzag encodes a signed 64bit integer so that it can be effectively used with varint encoding. + * @param {number|!Long} value Signed long + * @returns {!Long} Unsigned zigzag encoded long + * @expose + */ + ByteBuffer.zigZagEncode64 = function(value) { + if (typeof value === 'number') + value = Long.fromNumber(value, false); + else if (typeof value === 'string') + value = Long.fromString(value, false); + else if (value.unsigned !== false) value = value.toSigned(); + // ref: src/google/protobuf/wire_format_lite.h + return value.shiftLeft(1).xor(value.shiftRight(63)).toUnsigned(); + }; + + /** + * Decodes a zigzag encoded signed 64bit integer. + * @param {!Long|number} value Unsigned zigzag encoded long or JavaScript number + * @returns {!Long} Signed long + * @expose + */ + ByteBuffer.zigZagDecode64 = function(value) { + if (typeof value === 'number') + value = Long.fromNumber(value, false); + else if (typeof value === 'string') + value = Long.fromString(value, false); + else if (value.unsigned !== false) value = value.toSigned(); + // ref: src/google/protobuf/wire_format_lite.h + return value.shiftRightUnsigned(1).xor(value.and(Long.ONE).toSigned().negate()).toSigned(); + }; + + /** + * Writes a 64bit base 128 variable-length integer. + * @param {number|Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeVarint64 = function(value, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof value === 'number') + value = Long.fromNumber(value); + else if (typeof value === 'string') + value = Long.fromString(value); + else if (!(value && value instanceof Long)) + throw TypeError("Illegal value: "+value+" (not an integer or Long)"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (typeof value === 'number') + value = Long.fromNumber(value, false); + else if (typeof value === 'string') + value = Long.fromString(value, false); + else if (value.unsigned !== false) value = value.toSigned(); + var size = ByteBuffer.calculateVarint64(value), + part0 = value.toInt() >>> 0, + part1 = value.shiftRightUnsigned(28).toInt() >>> 0, + part2 = value.shiftRightUnsigned(56).toInt() >>> 0; + offset += size; + var capacity11 = this.buffer.byteLength; + if (offset > capacity11) + this.resize((capacity11 *= 2) > offset ? capacity11 : offset); + offset -= size; + switch (size) { + case 10: this.view[offset+9] = (part2 >>> 7) & 0x01; + case 9 : this.view[offset+8] = size !== 9 ? (part2 ) | 0x80 : (part2 ) & 0x7F; + case 8 : this.view[offset+7] = size !== 8 ? (part1 >>> 21) | 0x80 : (part1 >>> 21) & 0x7F; + case 7 : this.view[offset+6] = size !== 7 ? (part1 >>> 14) | 0x80 : (part1 >>> 14) & 0x7F; + case 6 : this.view[offset+5] = size !== 6 ? (part1 >>> 7) | 0x80 : (part1 >>> 7) & 0x7F; + case 5 : this.view[offset+4] = size !== 5 ? (part1 ) | 0x80 : (part1 ) & 0x7F; + case 4 : this.view[offset+3] = size !== 4 ? (part0 >>> 21) | 0x80 : (part0 >>> 21) & 0x7F; + case 3 : this.view[offset+2] = size !== 3 ? (part0 >>> 14) | 0x80 : (part0 >>> 14) & 0x7F; + case 2 : this.view[offset+1] = size !== 2 ? (part0 >>> 7) | 0x80 : (part0 >>> 7) & 0x7F; + case 1 : this.view[offset ] = size !== 1 ? (part0 ) | 0x80 : (part0 ) & 0x7F; + } + if (relative) { + this.offset += size; + return this; + } else { + return size; + } + }; + + /** + * Writes a zig-zag encoded 64bit base 128 variable-length integer. + * @param {number|Long} value Value to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeVarint64ZigZag = function(value, offset) { + return this.writeVarint64(ByteBuffer.zigZagEncode64(value), offset); + }; + + /** + * Reads a 64bit base 128 variable-length integer. Requires Long.js. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and + * the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBufferPrototype.readVarint64 = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + // ref: src/google/protobuf/io/coded_stream.cc + var start = offset, + part0 = 0, + part1 = 0, + part2 = 0, + b = 0; + b = this.view[offset++]; part0 = (b & 0x7F) ; if ( b & 0x80 ) { + b = this.view[offset++]; part0 |= (b & 0x7F) << 7; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part0 |= (b & 0x7F) << 14; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part0 |= (b & 0x7F) << 21; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part1 = (b & 0x7F) ; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part1 |= (b & 0x7F) << 7; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part1 |= (b & 0x7F) << 14; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part1 |= (b & 0x7F) << 21; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part2 = (b & 0x7F) ; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + b = this.view[offset++]; part2 |= (b & 0x7F) << 7; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { + throw Error("Buffer overrun"); }}}}}}}}}} + var value = Long.fromBits(part0 | (part1 << 28), (part1 >>> 4) | (part2) << 24, false); + if (relative) { + this.offset = offset; + return value; + } else { + return { + 'value': value, + 'length': offset-start + }; + } + }; + + /** + * Reads a zig-zag encoded 64bit base 128 variable-length integer. Requires Long.js. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and + * the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBufferPrototype.readVarint64ZigZag = function(offset) { + var val = this.readVarint64(offset); + if (val && val['value'] instanceof Long) + val["value"] = ByteBuffer.zigZagDecode64(val["value"]); + else + val = ByteBuffer.zigZagDecode64(val); + return val; + }; + + } // Long + + + // types/strings/cstring + + /** + * Writes a NULL-terminated UTF8 encoded string. For this to work the specified string must not contain any NULL + * characters itself. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * contained in `str` + 1 if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written + * @expose + */ + ByteBufferPrototype.writeCString = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + var i, + k = str.length; + if (!this.noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + for (i=0; i>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + // UTF8 strings do not contain zero bytes in between except for the zero character, so: + k = utfx.calculateUTF16asUTF8(stringSource(str))[1]; + offset += k+1; + var capacity12 = this.buffer.byteLength; + if (offset > capacity12) + this.resize((capacity12 *= 2) > offset ? capacity12 : offset); + offset -= k+1; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view[offset++] = b; + }.bind(this)); + this.view[offset++] = 0; + if (relative) { + this.offset = offset; + return this; + } + return k; + }; + + /** + * Reads a NULL-terminated UTF8 encoded string. For this to work the string read must not contain any NULL characters + * itself. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + */ + ByteBufferPrototype.readCString = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var start = offset, + temp; + // UTF8 strings do not contain zero bytes in between except for the zero character itself, so: + var sd, b = -1; + utfx.decodeUTF8toUTF16(function() { + if (b === 0) return null; + if (offset >= this.limit) + throw RangeError("Illegal range: Truncated data, "+offset+" < "+this.limit); + b = this.view[offset++]; + return b === 0 ? null : b; + }.bind(this), sd = stringDestination(), true); + if (relative) { + this.offset = offset; + return sd(); + } else { + return { + "string": sd(), + "length": offset - start + }; + } + }; + + // types/strings/istring + + /** + * Writes a length as uint32 prefixed UTF8 encoded string. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written + * @expose + * @see ByteBuffer#writeVarint32 + */ + ByteBufferPrototype.writeIString = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var start = offset, + k; + k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1]; + offset += 4+k; + var capacity13 = this.buffer.byteLength; + if (offset > capacity13) + this.resize((capacity13 *= 2) > offset ? capacity13 : offset); + offset -= 4+k; + if (this.littleEndian) { + this.view[offset+3] = (k >>> 24) & 0xFF; + this.view[offset+2] = (k >>> 16) & 0xFF; + this.view[offset+1] = (k >>> 8) & 0xFF; + this.view[offset ] = k & 0xFF; + } else { + this.view[offset ] = (k >>> 24) & 0xFF; + this.view[offset+1] = (k >>> 16) & 0xFF; + this.view[offset+2] = (k >>> 8) & 0xFF; + this.view[offset+3] = k & 0xFF; + } + offset += 4; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view[offset++] = b; + }.bind(this)); + if (offset !== start + 4 + k) + throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+4+k)); + if (relative) { + this.offset = offset; + return this; + } + return offset - start; + }; + + /** + * Reads a length as uint32 prefixed UTF8 encoded string. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + * @see ByteBuffer#readVarint32 + */ + ByteBufferPrototype.readIString = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 4 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength); + } + var start = offset; + var len = this.readUint32(offset); + var str = this.readUTF8String(len, ByteBuffer.METRICS_BYTES, offset += 4); + offset += str['length']; + if (relative) { + this.offset = offset; + return str['string']; + } else { + return { + 'string': str['string'], + 'length': offset - start + }; + } + }; + + // types/strings/utf8string + + /** + * Metrics representing number of UTF8 characters. Evaluates to `c`. + * @type {string} + * @const + * @expose + */ + ByteBuffer.METRICS_CHARS = 'c'; + + /** + * Metrics representing number of bytes. Evaluates to `b`. + * @type {string} + * @const + * @expose + */ + ByteBuffer.METRICS_BYTES = 'b'; + + /** + * Writes an UTF8 encoded string. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeUTF8String = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var k; + var start = offset; + k = utfx.calculateUTF16asUTF8(stringSource(str))[1]; + offset += k; + var capacity14 = this.buffer.byteLength; + if (offset > capacity14) + this.resize((capacity14 *= 2) > offset ? capacity14 : offset); + offset -= k; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view[offset++] = b; + }.bind(this)); + if (relative) { + this.offset = offset; + return this; + } + return offset - start; + }; + + /** + * Writes an UTF8 encoded string. This is an alias of {@link ByteBuffer#writeUTF8String}. + * @function + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBufferPrototype.writeString = ByteBufferPrototype.writeUTF8String; + + /** + * Calculates the number of UTF8 characters of a string. JavaScript itself uses UTF-16, so that a string's + * `length` property does not reflect its actual UTF8 size if it contains code points larger than 0xFFFF. + * @param {string} str String to calculate + * @returns {number} Number of UTF8 characters + * @expose + */ + ByteBuffer.calculateUTF8Chars = function(str) { + return utfx.calculateUTF16asUTF8(stringSource(str))[0]; + }; + + /** + * Calculates the number of UTF8 bytes of a string. + * @param {string} str String to calculate + * @returns {number} Number of UTF8 bytes + * @expose + */ + ByteBuffer.calculateUTF8Bytes = function(str) { + return utfx.calculateUTF16asUTF8(stringSource(str))[1]; + }; + + /** + * Calculates the number of UTF8 bytes of a string. This is an alias of {@link ByteBuffer.calculateUTF8Bytes}. + * @function + * @param {string} str String to calculate + * @returns {number} Number of UTF8 bytes + * @expose + */ + ByteBuffer.calculateString = ByteBuffer.calculateUTF8Bytes; + + /** + * Reads an UTF8 encoded string. + * @param {number} length Number of characters or bytes to read. + * @param {string=} metrics Metrics specifying what `length` is meant to count. Defaults to + * {@link ByteBuffer.METRICS_CHARS}. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + */ + ByteBufferPrototype.readUTF8String = function(length, metrics, offset) { + if (typeof metrics === 'number') { + offset = metrics; + metrics = undefined; + } + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (typeof metrics === 'undefined') metrics = ByteBuffer.METRICS_CHARS; + if (!this.noAssert) { + if (typeof length !== 'number' || length % 1 !== 0) + throw TypeError("Illegal length: "+length+" (not an integer)"); + length |= 0; + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var i = 0, + start = offset, + sd; + if (metrics === ByteBuffer.METRICS_CHARS) { // The same for node and the browser + sd = stringDestination(); + utfx.decodeUTF8(function() { + return i < length && offset < this.limit ? this.view[offset++] : null; + }.bind(this), function(cp) { + ++i; utfx.UTF8toUTF16(cp, sd); + }); + if (i !== length) + throw RangeError("Illegal range: Truncated data, "+i+" == "+length); + if (relative) { + this.offset = offset; + return sd(); + } else { + return { + "string": sd(), + "length": offset - start + }; + } + } else if (metrics === ByteBuffer.METRICS_BYTES) { + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + length > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.byteLength); + } + var k = offset + length; + utfx.decodeUTF8toUTF16(function() { + return offset < k ? this.view[offset++] : null; + }.bind(this), sd = stringDestination(), this.noAssert); + if (offset !== k) + throw RangeError("Illegal range: Truncated data, "+offset+" == "+k); + if (relative) { + this.offset = offset; + return sd(); + } else { + return { + 'string': sd(), + 'length': offset - start + }; + } + } else + throw TypeError("Unsupported metrics: "+metrics); + }; + + /** + * Reads an UTF8 encoded string. This is an alias of {@link ByteBuffer#readUTF8String}. + * @function + * @param {number} length Number of characters or bytes to read + * @param {number=} metrics Metrics specifying what `n` is meant to count. Defaults to + * {@link ByteBuffer.METRICS_CHARS}. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + */ + ByteBufferPrototype.readString = ByteBufferPrototype.readUTF8String; + + // types/strings/vstring + + /** + * Writes a length as varint32 prefixed UTF8 encoded string. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written + * @expose + * @see ByteBuffer#writeVarint32 + */ + ByteBufferPrototype.writeVString = function(str, offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + var start = offset, + k, l; + k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1]; + l = ByteBuffer.calculateVarint32(k); + offset += l+k; + var capacity15 = this.buffer.byteLength; + if (offset > capacity15) + this.resize((capacity15 *= 2) > offset ? capacity15 : offset); + offset -= l+k; + offset += this.writeVarint32(k, offset); + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + this.view[offset++] = b; + }.bind(this)); + if (offset !== start+k+l) + throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+k+l)); + if (relative) { + this.offset = offset; + return this; + } + return offset - start; + }; + + /** + * Reads a length as varint32 prefixed UTF8 encoded string. + * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @expose + * @see ByteBuffer#readVarint32 + */ + ByteBufferPrototype.readVString = function(offset) { + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 1 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength); + } + var start = offset; + var len = this.readVarint32(offset); + var str = this.readUTF8String(len['value'], ByteBuffer.METRICS_BYTES, offset += len['length']); + offset += str['length']; + if (relative) { + this.offset = offset; + return str['string']; + } else { + return { + 'string': str['string'], + 'length': offset - start + }; + } + }; + + + /** + * Appends some data to this ByteBuffer. This will overwrite any contents behind the specified offset up to the appended + * data's length. + * @param {!ByteBuffer|!ArrayBuffer|!Uint8Array|string} source Data to append. If `source` is a ByteBuffer, its offsets + * will be modified according to the performed read operation. + * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") + * @param {number=} offset Offset to append at. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. + * @returns {!ByteBuffer} this + * @expose + * @example A relative `<01 02>03.append(<04 05>)` will result in `<01 02 04 05>, 04 05|` + * @example An absolute `<01 02>03.append(04 05>, 1)` will result in `<01 04>05, 04 05|` + */ + ByteBufferPrototype.append = function(source, encoding, offset) { + if (typeof encoding === 'number' || typeof encoding !== 'string') { + offset = encoding; + encoding = undefined; + } + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (!(source instanceof ByteBuffer)) + source = ByteBuffer.wrap(source, encoding); + var length = source.limit - source.offset; + if (length <= 0) return this; // Nothing to append + offset += length; + var capacity16 = this.buffer.byteLength; + if (offset > capacity16) + this.resize((capacity16 *= 2) > offset ? capacity16 : offset); + offset -= length; + this.view.set(source.view.subarray(source.offset, source.limit), offset); + source.offset += length; + if (relative) this.offset += length; + return this; + }; + + /** + * Appends this ByteBuffer's contents to another ByteBuffer. This will overwrite any contents at and after the + specified offset up to the length of this ByteBuffer's data. + * @param {!ByteBuffer} target Target ByteBuffer + * @param {number=} offset Offset to append to. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * read if omitted. + * @returns {!ByteBuffer} this + * @expose + * @see ByteBuffer#append + */ + ByteBufferPrototype.appendTo = function(target, offset) { + target.append(this, offset); + return this; + }; + + /** + * Enables or disables assertions of argument types and offsets. Assertions are enabled by default but you can opt to + * disable them if your code already makes sure that everything is valid. + * @param {boolean} assert `true` to enable assertions, otherwise `false` + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.assert = function(assert) { + this.noAssert = !assert; + return this; + }; + + /** + * Gets the capacity of this ByteBuffer's backing buffer. + * @returns {number} Capacity of the backing buffer + * @expose + */ + ByteBufferPrototype.capacity = function() { + return this.buffer.byteLength; + }; + /** + * Clears this ByteBuffer's offsets by setting {@link ByteBuffer#offset} to `0` and {@link ByteBuffer#limit} to the + * backing buffer's capacity. Discards {@link ByteBuffer#markedOffset}. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.clear = function() { + this.offset = 0; + this.limit = this.buffer.byteLength; + this.markedOffset = -1; + return this; + }; + + /** + * Creates a cloned instance of this ByteBuffer, preset with this ByteBuffer's values for {@link ByteBuffer#offset}, + * {@link ByteBuffer#markedOffset} and {@link ByteBuffer#limit}. + * @param {boolean=} copy Whether to copy the backing buffer or to return another view on the same, defaults to `false` + * @returns {!ByteBuffer} Cloned instance + * @expose + */ + ByteBufferPrototype.clone = function(copy) { + var bb = new ByteBuffer(0, this.littleEndian, this.noAssert); + if (copy) { + bb.buffer = new ArrayBuffer(this.buffer.byteLength); + bb.view = new Uint8Array(bb.buffer); + } else { + bb.buffer = this.buffer; + bb.view = this.view; + } + bb.offset = this.offset; + bb.markedOffset = this.markedOffset; + bb.limit = this.limit; + return bb; + }; + + /** + * Compacts this ByteBuffer to be backed by a {@link ByteBuffer#buffer} of its contents' length. Contents are the bytes + * between {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will set `offset = 0` and `limit = capacity` and + * adapt {@link ByteBuffer#markedOffset} to the same relative position if set. + * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset} + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.compact = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin === 0 && end === this.buffer.byteLength) + return this; // Already compacted + var len = end - begin; + if (len === 0) { + this.buffer = EMPTY_BUFFER; + this.view = null; + if (this.markedOffset >= 0) this.markedOffset -= begin; + this.offset = 0; + this.limit = 0; + return this; + } + var buffer = new ArrayBuffer(len); + var view = new Uint8Array(buffer); + view.set(this.view.subarray(begin, end)); + this.buffer = buffer; + this.view = view; + if (this.markedOffset >= 0) this.markedOffset -= begin; + this.offset = 0; + this.limit = len; + return this; + }; + + /** + * Creates a copy of this ByteBuffer's contents. Contents are the bytes between {@link ByteBuffer#offset} and + * {@link ByteBuffer#limit}. + * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}. + * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. + * @returns {!ByteBuffer} Copy + * @expose + */ + ByteBufferPrototype.copy = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin === end) + return new ByteBuffer(0, this.littleEndian, this.noAssert); + var capacity = end - begin, + bb = new ByteBuffer(capacity, this.littleEndian, this.noAssert); + bb.offset = 0; + bb.limit = capacity; + if (bb.markedOffset >= 0) bb.markedOffset -= begin; + this.copyTo(bb, 0, begin, end); + return bb; + }; + + /** + * Copies this ByteBuffer's contents to another ByteBuffer. Contents are the bytes between {@link ByteBuffer#offset} and + * {@link ByteBuffer#limit}. + * @param {!ByteBuffer} target Target ByteBuffer + * @param {number=} targetOffset Offset to copy to. Will use and increase the target's {@link ByteBuffer#offset} + * by the number of bytes copied if omitted. + * @param {number=} sourceOffset Offset to start copying from. Will use and increase {@link ByteBuffer#offset} by the + * number of bytes copied if omitted. + * @param {number=} sourceLimit Offset to end copying from, defaults to {@link ByteBuffer#limit} + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.copyTo = function(target, targetOffset, sourceOffset, sourceLimit) { + var relative, + targetRelative; + if (!this.noAssert) { + if (!ByteBuffer.isByteBuffer(target)) + throw TypeError("Illegal target: Not a ByteBuffer"); + } + targetOffset = (targetRelative = typeof targetOffset === 'undefined') ? target.offset : targetOffset | 0; + sourceOffset = (relative = typeof sourceOffset === 'undefined') ? this.offset : sourceOffset | 0; + sourceLimit = typeof sourceLimit === 'undefined' ? this.limit : sourceLimit | 0; + + if (targetOffset < 0 || targetOffset > target.buffer.byteLength) + throw RangeError("Illegal target range: 0 <= "+targetOffset+" <= "+target.buffer.byteLength); + if (sourceOffset < 0 || sourceLimit > this.buffer.byteLength) + throw RangeError("Illegal source range: 0 <= "+sourceOffset+" <= "+this.buffer.byteLength); + + var len = sourceLimit - sourceOffset; + if (len === 0) + return target; // Nothing to copy + + target.ensureCapacity(targetOffset + len); + + target.view.set(this.view.subarray(sourceOffset, sourceLimit), targetOffset); + + if (relative) this.offset += len; + if (targetRelative) target.offset += len; + + return this; + }; + + /** + * Makes sure that this ByteBuffer is backed by a {@link ByteBuffer#buffer} of at least the specified capacity. If the + * current capacity is exceeded, it will be doubled. If double the current capacity is less than the required capacity, + * the required capacity will be used instead. + * @param {number} capacity Required capacity + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.ensureCapacity = function(capacity) { + var current = this.buffer.byteLength; + if (current < capacity) + return this.resize((current *= 2) > capacity ? current : capacity); + return this; + }; + + /** + * Overwrites this ByteBuffer's contents with the specified value. Contents are the bytes between + * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. + * @param {number|string} value Byte value to fill with. If given as a string, the first character is used. + * @param {number=} begin Begin offset. Will use and increase {@link ByteBuffer#offset} by the number of bytes + * written if omitted. defaults to {@link ByteBuffer#offset}. + * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. + * @returns {!ByteBuffer} this + * @expose + * @example `someByteBuffer.clear().fill(0)` fills the entire backing buffer with zeroes + */ + ByteBufferPrototype.fill = function(value, begin, end) { + var relative = typeof begin === 'undefined'; + if (relative) begin = this.offset; + if (typeof value === 'string' && value.length > 0) + value = value.charCodeAt(0); + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof value !== 'number' || value % 1 !== 0) + throw TypeError("Illegal value: "+value+" (not an integer)"); + value |= 0; + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin >= end) + return this; // Nothing to fill + while (begin < end) this.view[begin++] = value; + if (relative) this.offset = begin; + return this; + }; + + /** + * Makes this ByteBuffer ready for a new sequence of write or relative read operations. Sets `limit = offset` and + * `offset = 0`. Make sure always to flip a ByteBuffer when all relative read or write operations are complete. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.flip = function() { + this.limit = this.offset; + this.offset = 0; + return this; + }; + /** + * Marks an offset on this ByteBuffer to be used later. + * @param {number=} offset Offset to mark. Defaults to {@link ByteBuffer#offset}. + * @returns {!ByteBuffer} this + * @throws {TypeError} If `offset` is not a valid number + * @throws {RangeError} If `offset` is out of bounds + * @see ByteBuffer#reset + * @expose + */ + ByteBufferPrototype.mark = function(offset) { + offset = typeof offset === 'undefined' ? this.offset : offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + this.markedOffset = offset; + return this; + }; + /** + * Sets the byte order. + * @param {boolean} littleEndian `true` for little endian byte order, `false` for big endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.order = function(littleEndian) { + if (!this.noAssert) { + if (typeof littleEndian !== 'boolean') + throw TypeError("Illegal littleEndian: Not a boolean"); + } + this.littleEndian = !!littleEndian; + return this; + }; + + /** + * Switches (to) little endian byte order. + * @param {boolean=} littleEndian Defaults to `true`, otherwise uses big endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.LE = function(littleEndian) { + this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : true; + return this; + }; + + /** + * Switches (to) big endian byte order. + * @param {boolean=} bigEndian Defaults to `true`, otherwise uses little endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.BE = function(bigEndian) { + this.littleEndian = typeof bigEndian !== 'undefined' ? !bigEndian : false; + return this; + }; + /** + * Prepends some data to this ByteBuffer. This will overwrite any contents before the specified offset up to the + * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer + * will be resized and its contents moved accordingly. + * @param {!ByteBuffer|string|!ArrayBuffer} source Data to prepend. If `source` is a ByteBuffer, its offset will be + * modified according to the performed read operation. + * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") + * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes + * prepended if omitted. + * @returns {!ByteBuffer} this + * @expose + * @example A relative `00<01 02 03>.prepend(<04 05>)` results in `<04 05 01 02 03>, 04 05|` + * @example An absolute `00<01 02 03>.prepend(<04 05>, 2)` results in `04<05 02 03>, 04 05|` + */ + ByteBufferPrototype.prepend = function(source, encoding, offset) { + if (typeof encoding === 'number' || typeof encoding !== 'string') { + offset = encoding; + encoding = undefined; + } + var relative = typeof offset === 'undefined'; + if (relative) offset = this.offset; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: "+offset+" (not an integer)"); + offset >>>= 0; + if (offset < 0 || offset + 0 > this.buffer.byteLength) + throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength); + } + if (!(source instanceof ByteBuffer)) + source = ByteBuffer.wrap(source, encoding); + var len = source.limit - source.offset; + if (len <= 0) return this; // Nothing to prepend + var diff = len - offset; + if (diff > 0) { // Not enough space before offset, so resize + move + var buffer = new ArrayBuffer(this.buffer.byteLength + diff); + var view = new Uint8Array(buffer); + view.set(this.view.subarray(offset, this.buffer.byteLength), len); + this.buffer = buffer; + this.view = view; + this.offset += diff; + if (this.markedOffset >= 0) this.markedOffset += diff; + this.limit += diff; + offset += diff; + } else { + var arrayView = new Uint8Array(this.buffer); + } + this.view.set(source.view.subarray(source.offset, source.limit), offset - len); + + source.offset = source.limit; + if (relative) + this.offset -= len; + return this; + }; + + /** + * Prepends this ByteBuffer to another ByteBuffer. This will overwrite any contents before the specified offset up to the + * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer + * will be resized and its contents moved accordingly. + * @param {!ByteBuffer} target Target ByteBuffer + * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes + * prepended if omitted. + * @returns {!ByteBuffer} this + * @expose + * @see ByteBuffer#prepend + */ + ByteBufferPrototype.prependTo = function(target, offset) { + target.prepend(this, offset); + return this; + }; + /** + * Prints debug information about this ByteBuffer's contents. + * @param {function(string)=} out Output function to call, defaults to console.log + * @expose + */ + ByteBufferPrototype.printDebug = function(out) { + if (typeof out !== 'function') out = console.log.bind(console); + out( + this.toString()+"\n"+ + "-------------------------------------------------------------------\n"+ + this.toDebug(/* columns */ true) + ); + }; + + /** + * Gets the number of remaining readable bytes. Contents are the bytes between {@link ByteBuffer#offset} and + * {@link ByteBuffer#limit}, so this returns `limit - offset`. + * @returns {number} Remaining readable bytes. May be negative if `offset > limit`. + * @expose + */ + ByteBufferPrototype.remaining = function() { + return this.limit - this.offset; + }; + /** + * Resets this ByteBuffer's {@link ByteBuffer#offset}. If an offset has been marked through {@link ByteBuffer#mark} + * before, `offset` will be set to {@link ByteBuffer#markedOffset}, which will then be discarded. If no offset has been + * marked, sets `offset = 0`. + * @returns {!ByteBuffer} this + * @see ByteBuffer#mark + * @expose + */ + ByteBufferPrototype.reset = function() { + if (this.markedOffset >= 0) { + this.offset = this.markedOffset; + this.markedOffset = -1; + } else { + this.offset = 0; + } + return this; + }; + /** + * Resizes this ByteBuffer to be backed by a buffer of at least the given capacity. Will do nothing if already that + * large or larger. + * @param {number} capacity Capacity required + * @returns {!ByteBuffer} this + * @throws {TypeError} If `capacity` is not a number + * @throws {RangeError} If `capacity < 0` + * @expose + */ + ByteBufferPrototype.resize = function(capacity) { + if (!this.noAssert) { + if (typeof capacity !== 'number' || capacity % 1 !== 0) + throw TypeError("Illegal capacity: "+capacity+" (not an integer)"); + capacity |= 0; + if (capacity < 0) + throw RangeError("Illegal capacity: 0 <= "+capacity); + } + if (this.buffer.byteLength < capacity) { + var buffer = new ArrayBuffer(capacity); + var view = new Uint8Array(buffer); + view.set(this.view); + this.buffer = buffer; + this.view = view; + } + return this; + }; + /** + * Reverses this ByteBuffer's contents. + * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset} + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.reverse = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + if (begin === end) + return this; // Nothing to reverse + Array.prototype.reverse.call(this.view.subarray(begin, end)); + return this; + }; + /** + * Skips the next `length` bytes. This will just advance + * @param {number} length Number of bytes to skip. May also be negative to move the offset back. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBufferPrototype.skip = function(length) { + if (!this.noAssert) { + if (typeof length !== 'number' || length % 1 !== 0) + throw TypeError("Illegal length: "+length+" (not an integer)"); + length |= 0; + } + var offset = this.offset + length; + if (!this.noAssert) { + if (offset < 0 || offset > this.buffer.byteLength) + throw RangeError("Illegal length: 0 <= "+this.offset+" + "+length+" <= "+this.buffer.byteLength); + } + this.offset = offset; + return this; + }; + + /** + * Slices this ByteBuffer by creating a cloned instance with `offset = begin` and `limit = end`. + * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}. + * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. + * @returns {!ByteBuffer} Clone of this ByteBuffer with slicing applied, backed by the same {@link ByteBuffer#buffer} + * @expose + */ + ByteBufferPrototype.slice = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + var bb = this.clone(); + bb.offset = begin; + bb.limit = end; + return bb; + }; + /** + * Returns a copy of the backing buffer that contains this ByteBuffer's contents. Contents are the bytes between + * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. + * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory if + * possible. Defaults to `false` + * @returns {!ArrayBuffer} Contents as an ArrayBuffer + * @expose + */ + ByteBufferPrototype.toBuffer = function(forceCopy) { + var offset = this.offset, + limit = this.limit; + if (!this.noAssert) { + if (typeof offset !== 'number' || offset % 1 !== 0) + throw TypeError("Illegal offset: Not an integer"); + offset >>>= 0; + if (typeof limit !== 'number' || limit % 1 !== 0) + throw TypeError("Illegal limit: Not an integer"); + limit >>>= 0; + if (offset < 0 || offset > limit || limit > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+offset+" <= "+limit+" <= "+this.buffer.byteLength); + } + // NOTE: It's not possible to have another ArrayBuffer reference the same memory as the backing buffer. This is + // possible with Uint8Array#subarray only, but we have to return an ArrayBuffer by contract. So: + if (!forceCopy && offset === 0 && limit === this.buffer.byteLength) + return this.buffer; + if (offset === limit) + return EMPTY_BUFFER; + var buffer = new ArrayBuffer(limit - offset); + new Uint8Array(buffer).set(new Uint8Array(this.buffer).subarray(offset, limit), 0); + return buffer; + }; + + /** + * Returns a raw buffer compacted to contain this ByteBuffer's contents. Contents are the bytes between + * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. This is an alias of {@link ByteBuffer#toBuffer}. + * @function + * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory. + * Defaults to `false` + * @returns {!ArrayBuffer} Contents as an ArrayBuffer + * @expose + */ + ByteBufferPrototype.toArrayBuffer = ByteBufferPrototype.toBuffer; + + /** + * Converts the ByteBuffer's contents to a string. + * @param {string=} encoding Output encoding. Returns an informative string representation if omitted but also allows + * direct conversion to "utf8", "hex", "base64" and "binary" encoding. "debug" returns a hex representation with + * highlighted offsets. + * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset} + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} + * @returns {string} String representation + * @throws {Error} If `encoding` is invalid + * @expose + */ + ByteBufferPrototype.toString = function(encoding, begin, end) { + if (typeof encoding === 'undefined') + return "ByteBufferAB(offset="+this.offset+",markedOffset="+this.markedOffset+",limit="+this.limit+",capacity="+this.capacity()+")"; + if (typeof encoding === 'number') + encoding = "utf8", + begin = encoding, + end = begin; + switch (encoding) { + case "utf8": + return this.toUTF8(begin, end); + case "base64": + return this.toBase64(begin, end); + case "hex": + return this.toHex(begin, end); + case "binary": + return this.toBinary(begin, end); + case "debug": + return this.toDebug(); + case "columns": + return this.toColumns(); + default: + throw Error("Unsupported encoding: "+encoding); + } + }; + + // lxiv-embeddable + + /** + * lxiv-embeddable (c) 2014 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/lxiv for details + */ + var lxiv = function() { + "use strict"; + + /** + * lxiv namespace. + * @type {!Object.} + * @exports lxiv + */ + var lxiv = {}; + + /** + * Character codes for output. + * @type {!Array.} + * @inner + */ + var aout = [ + 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, 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, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47 + ]; + + /** + * Character codes for input. + * @type {!Array.} + * @inner + */ + var ain = []; + for (var i=0, k=aout.length; i>2)&0x3f]); + t = (b&0x3)<<4; + if ((b = src()) !== null) { + t |= (b>>4)&0xf; + dst(aout[(t|((b>>4)&0xf))&0x3f]); + t = (b&0xf)<<2; + if ((b = src()) !== null) + dst(aout[(t|((b>>6)&0x3))&0x3f]), + dst(aout[b&0x3f]); + else + dst(aout[t&0x3f]), + dst(61); + } else + dst(aout[t&0x3f]), + dst(61), + dst(61); + } + }; + + /** + * Decodes base64 char codes to bytes. + * @param {!function():number|null} src Characters source as a function returning the next char code respectively + * `null` if there are no more characters left. + * @param {!function(number)} dst Bytes destination as a function successively called with the next byte. + * @throws {Error} If a character code is invalid + */ + lxiv.decode = function(src, dst) { + var c, t1, t2; + function fail(c) { + throw Error("Illegal character code: "+c); + } + while ((c = src()) !== null) { + t1 = ain[c]; + if (typeof t1 === 'undefined') fail(c); + if ((c = src()) !== null) { + t2 = ain[c]; + if (typeof t2 === 'undefined') fail(c); + dst((t1<<2)>>>0|(t2&0x30)>>4); + if ((c = src()) !== null) { + t1 = ain[c]; + if (typeof t1 === 'undefined') + if (c === 61) break; else fail(c); + dst(((t2&0xf)<<4)>>>0|(t1&0x3c)>>2); + if ((c = src()) !== null) { + t2 = ain[c]; + if (typeof t2 === 'undefined') + if (c === 61) break; else fail(c); + dst(((t1&0x3)<<6)>>>0|t2); + } + } + } + } + }; + + /** + * Tests if a string is valid base64. + * @param {string} str String to test + * @returns {boolean} `true` if valid, otherwise `false` + */ + lxiv.test = function(str) { + return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(str); + }; + + return lxiv; + }(); + + // encodings/base64 + + /** + * Encodes this ByteBuffer's contents to a base64 encoded string. + * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset}. + * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}. + * @returns {string} Base64 encoded string + * @throws {RangeError} If `begin` or `end` is out of bounds + * @expose + */ + ByteBufferPrototype.toBase64 = function(begin, end) { + if (typeof begin === 'undefined') + begin = this.offset; + if (typeof end === 'undefined') + end = this.limit; + begin = begin | 0; end = end | 0; + if (begin < 0 || end > this.capacity || begin > end) + throw RangeError("begin, end"); + var sd; lxiv.encode(function() { + return begin < end ? this.view[begin++] : null; + }.bind(this), sd = stringDestination()); + return sd(); + }; + + /** + * Decodes a base64 encoded string to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromBase64 = function(str, littleEndian) { + if (typeof str !== 'string') + throw TypeError("str"); + var bb = new ByteBuffer(str.length/4*3, littleEndian), + i = 0; + lxiv.decode(stringSource(str), function(b) { + bb.view[i++] = b; + }); + bb.limit = i; + return bb; + }; + + /** + * Encodes a binary string to base64 like `window.btoa` does. + * @param {string} str Binary string + * @returns {string} Base64 encoded string + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.btoa + * @expose + */ + ByteBuffer.btoa = function(str) { + return ByteBuffer.fromBinary(str).toBase64(); + }; + + /** + * Decodes a base64 encoded string to binary like `window.atob` does. + * @param {string} b64 Base64 encoded string + * @returns {string} Binary string + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.atob + * @expose + */ + ByteBuffer.atob = function(b64) { + return ByteBuffer.fromBase64(b64).toBinary(); + }; + + // encodings/binary + + /** + * Encodes this ByteBuffer to a binary encoded string, that is using only characters 0x00-0xFF as bytes. + * @param {number=} begin Offset to begin at. Defaults to {@link ByteBuffer#offset}. + * @param {number=} end Offset to end at. Defaults to {@link ByteBuffer#limit}. + * @returns {string} Binary encoded string + * @throws {RangeError} If `offset > limit` + * @expose + */ + ByteBufferPrototype.toBinary = function(begin, end) { + if (typeof begin === 'undefined') + begin = this.offset; + if (typeof end === 'undefined') + end = this.limit; + begin |= 0; end |= 0; + if (begin < 0 || end > this.capacity() || begin > end) + throw RangeError("begin, end"); + if (begin === end) + return ""; + var chars = [], + parts = []; + while (begin < end) { + chars.push(this.view[begin++]); + if (chars.length >= 1024) + parts.push(String.fromCharCode.apply(String, chars)), + chars = []; + } + return parts.join('') + String.fromCharCode.apply(String, chars); + }; + + /** + * Decodes a binary encoded string, that is using only characters 0x00-0xFF as bytes, to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromBinary = function(str, littleEndian) { + if (typeof str !== 'string') + throw TypeError("str"); + var i = 0, + k = str.length, + charCode, + bb = new ByteBuffer(k, littleEndian); + while (i 0xff) + throw RangeError("illegal char code: "+charCode); + bb.view[i++] = charCode; + } + bb.limit = k; + return bb; + }; + + // encodings/debug + + /** + * Encodes this ByteBuffer to a hex encoded string with marked offsets. Offset symbols are: + * * `<` : offset, + * * `'` : markedOffset, + * * `>` : limit, + * * `|` : offset and limit, + * * `[` : offset and markedOffset, + * * `]` : markedOffset and limit, + * * `!` : offset, markedOffset and limit + * @param {boolean=} columns If `true` returns two columns hex + ascii, defaults to `false` + * @returns {string|!Array.} Debug string or array of lines if `asArray = true` + * @expose + * @example `>00'01 02<03` contains four bytes with `limit=0, markedOffset=1, offset=3` + * @example `00[01 02 03>` contains four bytes with `offset=markedOffset=1, limit=4` + * @example `00|01 02 03` contains four bytes with `offset=limit=1, markedOffset=-1` + * @example `|` contains zero bytes with `offset=limit=0, markedOffset=-1` + */ + ByteBufferPrototype.toDebug = function(columns) { + var i = -1, + k = this.buffer.byteLength, + b, + hex = "", + asc = "", + out = ""; + while (i 32 && b < 127 ? String.fromCharCode(b) : '.'; + } + ++i; + if (columns) { + if (i > 0 && i % 16 === 0 && i !== k) { + while (hex.length < 3*16+3) hex += " "; + out += hex+asc+"\n"; + hex = asc = ""; + } + } + if (i === this.offset && i === this.limit) + hex += i === this.markedOffset ? "!" : "|"; + else if (i === this.offset) + hex += i === this.markedOffset ? "[" : "<"; + else if (i === this.limit) + hex += i === this.markedOffset ? "]" : ">"; + else + hex += i === this.markedOffset ? "'" : (columns || (i !== 0 && i !== k) ? " " : ""); + } + if (columns && hex !== " ") { + while (hex.length < 3*16+3) + hex += " "; + out += hex + asc + "\n"; + } + return columns ? out : hex; + }; + + /** + * Decodes a hex encoded string with marked offsets to a ByteBuffer. + * @param {string} str Debug string to decode (not be generated with `columns = true`) + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + * @see ByteBuffer#toDebug + */ + ByteBuffer.fromDebug = function(str, littleEndian, noAssert) { + var k = str.length, + bb = new ByteBuffer(((k+1)/3)|0, littleEndian, noAssert); + var i = 0, j = 0, ch, b, + rs = false, // Require symbol next + ho = false, hm = false, hl = false, // Already has offset (ho), markedOffset (hm), limit (hl)? + fail = false; + while (i': + if (!noAssert) { + if (hl) { + fail = true; + break; + } + hl = true; + } + bb.limit = j; + rs = false; + break; + case "'": + if (!noAssert) { + if (hm) { + fail = true; + break; + } + hm = true; + } + bb.markedOffset = j; + rs = false; + break; + case ' ': + rs = false; + break; + default: + if (!noAssert) { + if (rs) { + fail = true; + break; + } + } + b = parseInt(ch+str.charAt(i++), 16); + if (!noAssert) { + if (isNaN(b) || b < 0 || b > 255) + throw TypeError("Illegal str: Not a debug encoded string"); + } + bb.view[j++] = b; + rs = true; + } + if (fail) + throw TypeError("Illegal str: Invalid symbol at "+i); + } + if (!noAssert) { + if (!ho || !hl) + throw TypeError("Illegal str: Missing offset or limit"); + if (j>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + var out = new Array(end - begin), + b; + while (begin < end) { + b = this.view[begin++]; + if (b < 0x10) + out.push("0", b.toString(16)); + else out.push(b.toString(16)); + } + return out.join(''); + }; + + /** + * Decodes a hex encoded string to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromHex = function(str, littleEndian, noAssert) { + if (!noAssert) { + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + if (str.length % 2 !== 0) + throw TypeError("Illegal str: Length not a multiple of 2"); + } + var k = str.length, + bb = new ByteBuffer((k / 2) | 0, littleEndian), + b; + for (var i=0, j=0; i 255) + throw TypeError("Illegal str: Contains non-hex characters"); + bb.view[j++] = b; + } + bb.limit = j; + return bb; + }; + + // utfx-embeddable + + /** + * utfx-embeddable (c) 2014 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/utfx for details + */ + var utfx = function() { + "use strict"; + + /** + * utfx namespace. + * @inner + * @type {!Object.} + */ + var utfx = {}; + + /** + * Maximum valid code point. + * @type {number} + * @const + */ + utfx.MAX_CODEPOINT = 0x10FFFF; + + /** + * Encodes UTF8 code points to UTF8 bytes. + * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point + * respectively `null` if there are no more code points left or a single numeric code point. + * @param {!function(number)} dst Bytes destination as a function successively called with the next byte + */ + utfx.encodeUTF8 = function(src, dst) { + var cp = null; + if (typeof src === 'number') + cp = src, + src = function() { return null; }; + while (cp !== null || (cp = src()) !== null) { + if (cp < 0x80) + dst(cp&0x7F); + else if (cp < 0x800) + dst(((cp>>6)&0x1F)|0xC0), + dst((cp&0x3F)|0x80); + else if (cp < 0x10000) + dst(((cp>>12)&0x0F)|0xE0), + dst(((cp>>6)&0x3F)|0x80), + dst((cp&0x3F)|0x80); + else + dst(((cp>>18)&0x07)|0xF0), + dst(((cp>>12)&0x3F)|0x80), + dst(((cp>>6)&0x3F)|0x80), + dst((cp&0x3F)|0x80); + cp = null; + } + }; + + /** + * Decodes UTF8 bytes to UTF8 code points. + * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there + * are no more bytes left. + * @param {!function(number)} dst Code points destination as a function successively called with each decoded code point. + * @throws {RangeError} If a starting byte is invalid in UTF8 + * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the + * remaining bytes. + */ + utfx.decodeUTF8 = function(src, dst) { + var a, b, c, d, fail = function(b) { + b = b.slice(0, b.indexOf(null)); + var err = Error(b.toString()); + err.name = "TruncatedError"; + err['bytes'] = b; + throw err; + }; + while ((a = src()) !== null) { + if ((a&0x80) === 0) + dst(a); + else if ((a&0xE0) === 0xC0) + ((b = src()) === null) && fail([a, b]), + dst(((a&0x1F)<<6) | (b&0x3F)); + else if ((a&0xF0) === 0xE0) + ((b=src()) === null || (c=src()) === null) && fail([a, b, c]), + dst(((a&0x0F)<<12) | ((b&0x3F)<<6) | (c&0x3F)); + else if ((a&0xF8) === 0xF0) + ((b=src()) === null || (c=src()) === null || (d=src()) === null) && fail([a, b, c ,d]), + dst(((a&0x07)<<18) | ((b&0x3F)<<12) | ((c&0x3F)<<6) | (d&0x3F)); + else throw RangeError("Illegal starting byte: "+a); + } + }; + + /** + * Converts UTF16 characters to UTF8 code points. + * @param {!function():number|null} src Characters source as a function returning the next char code respectively + * `null` if there are no more characters left. + * @param {!function(number)} dst Code points destination as a function successively called with each converted code + * point. + */ + utfx.UTF16toUTF8 = function(src, dst) { + var c1, c2 = null; + while (true) { + if ((c1 = c2 !== null ? c2 : src()) === null) + break; + if (c1 >= 0xD800 && c1 <= 0xDFFF) { + if ((c2 = src()) !== null) { + if (c2 >= 0xDC00 && c2 <= 0xDFFF) { + dst((c1-0xD800)*0x400+c2-0xDC00+0x10000); + c2 = null; continue; + } + } + } + dst(c1); + } + if (c2 !== null) dst(c2); + }; + + /** + * Converts UTF8 code points to UTF16 characters. + * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point + * respectively `null` if there are no more code points left or a single numeric code point. + * @param {!function(number)} dst Characters destination as a function successively called with each converted char code. + * @throws {RangeError} If a code point is out of range + */ + utfx.UTF8toUTF16 = function(src, dst) { + var cp = null; + if (typeof src === 'number') + cp = src, src = function() { return null; }; + while (cp !== null || (cp = src()) !== null) { + if (cp <= 0xFFFF) + dst(cp); + else + cp -= 0x10000, + dst((cp>>10)+0xD800), + dst((cp%0x400)+0xDC00); + cp = null; + } + }; + + /** + * Converts and encodes UTF16 characters to UTF8 bytes. + * @param {!function():number|null} src Characters source as a function returning the next char code respectively `null` + * if there are no more characters left. + * @param {!function(number)} dst Bytes destination as a function successively called with the next byte. + */ + utfx.encodeUTF16toUTF8 = function(src, dst) { + utfx.UTF16toUTF8(src, function(cp) { + utfx.encodeUTF8(cp, dst); + }); + }; + + /** + * Decodes and converts UTF8 bytes to UTF16 characters. + * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there + * are no more bytes left. + * @param {!function(number)} dst Characters destination as a function successively called with each converted char code. + * @throws {RangeError} If a starting byte is invalid in UTF8 + * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the remaining bytes. + */ + utfx.decodeUTF8toUTF16 = function(src, dst) { + utfx.decodeUTF8(src, function(cp) { + utfx.UTF8toUTF16(cp, dst); + }); + }; + + /** + * Calculates the byte length of an UTF8 code point. + * @param {number} cp UTF8 code point + * @returns {number} Byte length + */ + utfx.calculateCodePoint = function(cp) { + return (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4; + }; + + /** + * Calculates the number of UTF8 bytes required to store UTF8 code points. + * @param {(!function():number|null)} src Code points source as a function returning the next code point respectively + * `null` if there are no more code points left. + * @returns {number} The number of UTF8 bytes required + */ + utfx.calculateUTF8 = function(src) { + var cp, l=0; + while ((cp = src()) !== null) + l += (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4; + return l; + }; + + /** + * Calculates the number of UTF8 code points respectively UTF8 bytes required to store UTF16 char codes. + * @param {(!function():number|null)} src Characters source as a function returning the next char code respectively + * `null` if there are no more characters left. + * @returns {!Array.} The number of UTF8 code points at index 0 and the number of UTF8 bytes required at index 1. + */ + utfx.calculateUTF16asUTF8 = function(src) { + var n=0, l=0; + utfx.UTF16toUTF8(src, function(cp) { + ++n; l += (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4; + }); + return [n,l]; + }; + + return utfx; + }(); + + // encodings/utf8 + + /** + * Encodes this ByteBuffer's contents between {@link ByteBuffer#offset} and {@link ByteBuffer#limit} to an UTF8 encoded + * string. + * @returns {string} Hex encoded string + * @throws {RangeError} If `offset > limit` + * @expose + */ + ByteBufferPrototype.toUTF8 = function(begin, end) { + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.limit; + if (!this.noAssert) { + if (typeof begin !== 'number' || begin % 1 !== 0) + throw TypeError("Illegal begin: Not an integer"); + begin >>>= 0; + if (typeof end !== 'number' || end % 1 !== 0) + throw TypeError("Illegal end: Not an integer"); + end >>>= 0; + if (begin < 0 || begin > end || end > this.buffer.byteLength) + throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength); + } + var sd; try { + utfx.decodeUTF8toUTF16(function() { + return begin < end ? this.view[begin++] : null; + }.bind(this), sd = stringDestination()); + } catch (e) { + if (begin !== end) + throw RangeError("Illegal range: Truncated data, "+begin+" != "+end); + } + return sd(); + }; + + /** + * Decodes an UTF8 encoded string to a ByteBuffer. + * @param {string} str String to decode + * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to + * {@link ByteBuffer.DEFAULT_ENDIAN}. + * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to + * {@link ByteBuffer.DEFAULT_NOASSERT}. + * @returns {!ByteBuffer} ByteBuffer + * @expose + */ + ByteBuffer.fromUTF8 = function(str, littleEndian, noAssert) { + if (!noAssert) + if (typeof str !== 'string') + throw TypeError("Illegal str: Not a string"); + var bb = new ByteBuffer(utfx.calculateUTF16asUTF8(stringSource(str), true)[1], littleEndian, noAssert), + i = 0; + utfx.encodeUTF16toUTF8(stringSource(str), function(b) { + bb.view[i++] = b; + }); + bb.limit = i; + return bb; + }; + + return ByteBuffer; +}); diff --git a/fc2/js/jcmatchvs.js b/fc2/js/jcmatchvs.js new file mode 100644 index 0000000..754e18b --- /dev/null +++ b/fc2/js/jcmatchvs.js @@ -0,0 +1,1550 @@ + +JCMsgHandler = function(){ + + + this.init=function(handler){ + this._handobj = handler; + this.msglst = []; + this.deltatime = 0; + this.delaytime = 500; + this.rate = 60; + } + + + this.run=function (dt){ + var self = this; + this.msglst.forEach(element => { + element.delaycount--; + if(element.delaycount <= 0){ + self._handobj && self._handobj.handleData && self._handobj.handleData(element); + element.handled = true; + } + }); + + var i = this.msglst.length; + while(i--){ + if(this.msglst[i].handled){ + this.msglst.splice(i,1); + } + } + } + + this.pushMsg =function(obj){ + var msg = this.msgclone(obj); + var subobj = JSON.parse(msg.content); + var tm = subobj.timestamp + this.deltatime; + var d = new Date(); + msg.delaycount = Math.ceil((d.getTime() - tm) / this.rate); + msg.handled = false; + this.msglst.push(msg); + } + + this.msgclone=function(obj) { + // Handle the 3 simple types, and null or undefined + if (null == obj || "object" != typeof obj) return obj; + + // Handle Date + if (obj instanceof Date) { + var copy = new Date(); + copy.setTime(obj.getTime()); + return copy; + } + + // Handle Array + if (obj instanceof Array) { + var copy = []; + var len = obj.length; + for (var i = 0; i < len; ++i) { + copy[i] = this.msgclone(obj[i]); + } + return copy; + } + + // Handle Object + if (obj instanceof Object) { + var copy = {}; + for (var attr in obj) { + if (obj.hasOwnProperty(attr)) copy[attr] = this.msgclone(obj[attr]); + } + return copy; + } + + throw new Error("Unable to copy obj! Its type isn't supported."); + } + + this.setDeltaTime=function(dt){ + this.deltatime = dt; + } + + this.setFramerate=function(rt){ + this.rate = rt; + } +}; + + + + + +MsgEnum = function(){ + + this.c2sMsg = function(){ + return this.msg.CMMessageId_e; + } + + this.s2cMsg = function(){ + return this.msg.SMMessageId_e; + } + this.downloadFile=function(fileName, registrationHandler) { + var ajax = new XMLHttpRequest(); + ajax.onload = registrationHandler; + ajax.open("GET", "./" + fileName, true); + ajax.responseType = "text"; + ajax.overrideMimeType("text/plain; charset=x-user-defined"); + ajax.send(null); + } + + this.init=function(cb){ + this.msg = null; + var protoFile = "./resources/messages.proto"; + this.downloadFile(protoFile, function ( res){ + if(res.target.status===200){ + var Builder = ProtoBuf.protoFromString(res.target.response); + this.msg = Builder.build("kingsomevs"); + cb && cb(); + } + + }.bind(this)); + } +}; + + +MsgObj =function(arrbuf, len){ + + var handbuf = new Uint8Array(arrbuf); + this._id = handbuf[2] | ((handbuf[3] << 8) & 0xFF); + var msgbuf = handbuf.subarray(8, 8+len); + this.u8buf = new Uint8Array(len); + this.u8buf.set(msgbuf); + + this.id=function(){ + return this._id; + } + + this.data=function(){ + return this.u8buf.buffer; + } + +}; + + +const BUFF_SIZE = 1024; + + + +MsgBuffer =function(){ + + + this.init=function(){ + this.BUFF_SZ = BUFF_SIZE; + this.buffer = new ArrayBuffer(BUFF_SIZE); + this.handrecvbuf = new Uint8Array(this.buffer); + this.usedlen = 0; + } + + this.pushData=function(buf){ + if(!buf || !buf.byteLength || buf.byteLength <= 0){ + return; + } + + var len = buf.byteLength; + while (this.usedlen + len >= this.BUFF_SZ){ + console.log("msgbuffer enlarge *2!" + this.BUFF_SZ + "|" + this.usedlen); + this.BUFF_SZ *= 2; + + var newBuffer = new ArrayBuffer(this.BUFF_SZ); + if(this.usedlen>0){ + var oldbuf = new Uint8Array(this.buffer); + var newbuf = new Uint8Array(newBuffer); + var subold = oldbuf.subarray(this.usedlen); + newbuf.set(subold); + } + + this.buffer = newBuffer; + this.handrecvbuf = new Uint8Array(this.buffer); + } + + var subbuf = this.handrecvbuf.subarray(this.usedlen); + var hbuf = new Uint8Array(buf); + subbuf.set(hbuf); + + this.usedlen += len; + } + + this.hasPackMsg=function(){ + if(this.usedlen < 8){//len(2byte) + id(2byte) + code(4byte) + return false; + } + var handbuf = this.handrecvbuf; + + var num = handbuf[0] | ((handbuf[1] << 8)); + if(this.usedlen < num + 8){ + return false; + } + return true; + } + + this.popPackMsg=function(){ + var handbuf = this.handrecvbuf; + + var num = handbuf[0] | ((handbuf[1] << 8)); + var id = handbuf[2] | ((handbuf[3] << 8)); + var msg = new MsgObj(handbuf.buffer, num); + // console.log("[popPackMsg]:"+num+","+id); + //start update param + var n = num + 8; + if(this.usedlen > n){ + this.buffer.copyWithin(0, n, this.usedlen); + } + this.usedlen -= n; + return msg; + } + + this.buildPackMsg=function(msgid, arrbuf){ + var len = arrbuf? arrbuf.byteLength: 0; + var num = len + 8; + var bytebuf = new Uint8Array(num); + bytebuf[0] = len > 0? len & 0xFF: 0; + bytebuf[1] = len > 0? (len >> 8) & 0xFF: 0; + bytebuf[2] = msgid & 0xFF; + bytebuf[3] = (msgid >> 8) & 0xFF; + + bytebuf[4] = 0xAA; + bytebuf[5] = 0xCC; + bytebuf[6] = 0xBB; + bytebuf[7] = 0xAA; + + //var handbuf = new Uint16Array(bytebuf.buffer); + //handbuf[0] = num; + //handbuf[1] = msgid; + //var dwordbuf = new Uint32Array(handbuf.buffer); + //dwordbuf[1] = 0xFFCCAABB; + if(arrbuf){ + var oldbuf = new Uint8Array(arrbuf); + bytebuf.set(oldbuf, 8); + } + + return bytebuf.buffer; + } +} +MessageVo = function(){ + this.update= function (mId, proto, cb) { + this.msgId = mId; + this.protoData = proto; + this.callBack = cb; + return this; + } +}; + + + +Socket = function(callback, hosturl){ + //this._super(); + this._sock = null; + this._cb = callback; + this._host = hosturl; + this._sendqueue = []; + this._isclosed = false; + var self = this; + if(typeof(WebSocket) != 'undefined'){ + //cocos creator websocket + this._sock = new WebSocket(hosturl); + this._sock.binaryType = 'arraybuffer'; + this._sock.onmessage = function (event) { + //console.log("socket on message"); + self._cb.onMessage && self._cb.onMessage(event.data); + }; + this._sock.onopen = function (event) { + console.log("Create the socket is success :"+self._host ); + while (self._sendqueue.length > 0) { + self._sock.send(self._sendqueue.pop()); + } + self._cb.onOpen && self._cb.onOpen(self._host); + }; + this._sock.onclose = function (e) { + self._isclosed = true; + self._cb.onClose && self._cb.onClose(self._host,e); + console.log("socket on closed ,code:"+e.code+"(1000:NORMAL,1005:CLOSE_NO_STATUS,1006:RESET,1009:CLOSE_TOO_LARGE) msg:"+e.reason); + }; + this._sock.onerror = function (event) { + console.log("socket on error ,event:"+JSON.stringify(event)); + self._cb.onError && self._cb.onError(self._host,event); + if(!self._isclosed){ + self.close(); + } + }; + + } + this.send=function(msg){ + var n = this._sock.readyState; + //console.log("sock readyState:" + n); + if(n == WebSocket.CONNECTING){ + this._sendqueue.push(msg); + }else if(n == WebSocket.OPEN){ + this._sock.send(msg); + }else{ + this._sock.onclose({code:-n}); + } + } + + this.close=function(){ + //todo: + this._sock && this._sock.close(); + } +} + + + + +WS = function(){ + this.url ="" + this.sock = null; + + + + this.setCallback=function(cb){ + this.cb = cb; + } + // LIFE-CYCLE CALLBACKS: + + // onLoad () {}, + + this.start=function () { + + } + + // update (dt) {}, + + this.setHost=function(url){ + this.url = url; + } + + this.connect=function(){ + if(this.sock){ + this.sock = null; + } + this.sock = new Socket(this, this.url); + } + + this.send=function(msg){ + this.sock && this.sock.send(msg); + } + + this.disconnect=function(){ + this.sock && this.sock.close(); + } + + this.onOpen=function(url){ + this.cb && this.cb.onConnect(); + } + + this.onClose=function(url, errevent){ + this.cb && this.cb.onClose(errevent); + } + + this.onError=function(url, errevent){ + this.cb && this.cb.onError(errevent); + } + + this.onMessage=function(msg){ + this.cb && this.cb.onRecv(msg); + } +} + +// var MessageBuffer = require('msgbuffer'); +// var MessageVo = require('messageVo'); +// var WS = require('webclient'); +//var Base64 = require('base64'); + +MsgManager = function(){ + + this.init=function() { + this._sendMsgQueue = {}; + this._recvMsgQueue = {}; + this._ws = new WS(); + this._ws.setCallback(this); + this._buf = new MsgBuffer(); + this._buf.init(); + this._isconnected = false; + this._netcb = null; + } + this.initSendMsg=function(msgId, proto) { + var vo = new MessageVo(); + vo.update(msgId, proto, null); + this._sendMsgQueue[msgId] = vo; + } + this.initRecvMsg=function(msgId, proto, callBack) { + var vo = new MessageVo(); + vo.update(msgId, proto, callBack); + this._recvMsgQueue[msgId] = vo; + } + this.sendMsg=function(msgId, msg) { + + var msgVo = this._sendMsgQueue[msgId]; + //console.log(JSON.stringify(msgVo)); + var data = msg? msgVo.protoData.encode(msg).toArrayBuffer():null; + + var pkgdata = this._buf.buildPackMsg(msgId, data); + if(pkgdata && this._ws){ + //console.log(Base64.arrayBufferToBase64(pkgdata)); + //console.log("[sendmsg]:"+msgId + ":" + msg); + this._ws.send(pkgdata); + } + + } + this.parseMsg=function(msgId, msgByte) { + // console.log("信息回调id:" + msgId+ ":"+msgByte); + var msgVo = this._recvMsgQueue[msgId]; + if(msgVo == undefined){ + console.log("[undefined msg]"+msgId); + return; + } + var msg = msgVo.protoData.decode(msgByte); + if(msgVo.callBack) { + + //统一处理errorcode + if(msg && msg.errorcode){ + //TODO 统一处理errorcode + //1:根据errorcode获取对应的文本信息 + //2:提示给用户 + + } + + msgVo.callBack(msg); + } + } + + this.isConnected=function(){ + return this._isconnected; + } + + this.connectNet= function(host, cb){ + console.log("[connectNet]"); + this._netcb = cb; + //this._ws.setHost(host); + this._ws.url = host; + this._ws.connect(); + } + + this.closeNet= function(){ + this._ws.disconnect(); + } + + this.onConnect=function(){ + this._isconnected = true; + if(this._netcb){ + this._netcb("connect", 0); + } + } + + this.onClose=function(err){ + this._isconnected = false; + if(this._netcb){ + var n = err.code? err.code: 0; + this._netcb("close", n); + } + } + + this.onError=function(err){ + this._isconnected = false; + if(this._netcb){ + var n = err.code? err.code: 0; + this._netcb("error", n); + } + } + + this.onRecv=function(msg){ + // console.log("onrecv"); + this._buf.pushData(msg); + while(this._buf.hasPackMsg()){ + var msg = this._buf.popPackMsg(); + this.parseMsg(msg.id(), msg.data()); + } + } +} + + + + +// var MsgMan = require('./net/msgmanager'); +// var MsgIDEnum = require('./net/msgenum'); +// var ProtoBuf = require("./net/proto/protobuf"); + +// var MsgHandler = require("./jcmsghandler"); + +const state_enum ={ + none: 0, + initing: 1, + inited: 2, + logining: 3, + logined: 4, + loginouting: 5, + loginouted: 6, + joining: 7, + joined: 8, + leaving: 9, + leaved: 10, +}; + +UserInfo = function(){ + + this.channelID="" + this.platForm="" + this.gameID="" + this.deviceID="" + this.userID="" + this.token="" + this.roomID="" + this.init_state=state_enum.none + this.login_state= state_enum.none + this.room_state= state_enum.none + +}; + +const url_offical = "wss://matchvs-al.kingsome.cn/websocket"; + +const url_test = "wss://matchvs-al.kingsome.cn/websocket";//"ws://118.31.73.76:81/websocket"; + +JCMatchVS = function(){ + + + // LIFE-CYCLE CALLBACKS: + + this.onLoad =function() { + this.userinfo = {}; + this.rsp = null; + this.userinfo = new UserInfo(); + this.reconnectcount = 0; + this.reconnectmax = 5; + this.msgctrl = new JCMsgHandler(); + } + + + + this.run=function(dt){ + this.msgctrl.run(dt); + } + + // update (dt) {}, + + this.__classCreate=function(){ + this.onLoad(); + if(!this.msgManager){ + this.msgManager = new MsgManager(); + this.msgManager.init(); + } + if(!this.msgIDs){ + this.msgIDs = new MsgEnum(); + } + } + + this.init=function(response, channel, platform, gameid, deviceid) { + this.rsp = response; + this.userinfo.channelID = channel; + this.userinfo.platForm = platform; + this.userinfo.gameID = gameid; + this.userinfo.deviceID = deviceid; + this.userinfo.init_state = state_enum.initing; + this.msgIDs.init(function(err){ + this.initMsgHandler(this.msgIDs.c2sMsg(), this.msgIDs.s2cMsg()); + }.bind(this)); + this.msgctrl.init(response); + } + this.downloadFile=function(fileName, registrationHandler) { + var ajax = new XMLHttpRequest(); + ajax.onload = registrationHandler; + ajax.open("GET", "./" + fileName, true); + ajax.responseType = "text"; + ajax.overrideMimeType("text/plain; charset=x-user-defined"); + ajax.send(null); + } + this.initMsgHandler=function(c2sMsg, s2cMsg){ + var protoFile = "./resources/kingsomevs.proto"; + this.downloadFile(protoFile, function ( res){ + + + var Builder = ProtoBuf.protoFromString(res.target.response); + + var msg = Builder.build("kingsomevs"); + + this.msgManager.initSendMsg(c2sMsg._CMLogin, msg.CMLogin); + this.msgManager.initRecvMsg(s2cMsg._SMLogin, msg.SMLogin, this.onLoginRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMCreateRoom, msg.CMCreateRoom); + this.msgManager.initRecvMsg(s2cMsg._SMCreateRoom, msg.SMCreateRoom, this.onCreateRoomRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMJoinRandomRoom, msg.CMJoinRandomRoom); + this.msgManager.initRecvMsg(s2cMsg._SMJoinRandomRoom, msg.SMJoinRandomRoom, this.onJoinRoomRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMJoinRoom, msg.CMJoinRoom); + this.msgManager.initRecvMsg(s2cMsg._SMJoinRoom, msg.SMJoinRoom, this.onJoinRoomRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMKickPlayer, msg.CMKickPlayer); + this.msgManager.initRecvMsg(s2cMsg._SMKickPlayer, msg.SMKickPlayer, this.onKickPlayerRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMJoinOver, msg.CMJoinOver); + this.msgManager.initRecvMsg(s2cMsg._SMJoinOver, msg.SMJoinOver, this.onJoinOverRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMJoinOpen, msg.CMJoinOpen); + this.msgManager.initRecvMsg(s2cMsg._SMJoinOpen, msg.SMJoinOpen, this.onJoinOpenRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMGameStart, msg.CMGameStart); + this.msgManager.initRecvMsg(s2cMsg._SMGameStart, msg.SMGameStart, this.onGameStartRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMSendRoomEvent, msg.CMSendRoomEvent); + this.msgManager.initRecvMsg(s2cMsg._SMSendRoomEvent, msg.SMSendRoomEvent, this.onSendRoomEvtRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMLeaveRoom, msg.CMLeaveRoom); + this.msgManager.initRecvMsg(s2cMsg._SMLeaveRoom, msg.SMLeaveRoom, this.onLeaveRoomRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMSetFrameSync, msg.CMSetFrameSync); + this.msgManager.initRecvMsg(s2cMsg.SMSetFrameSync, msg.SMSetFrameSync, this.onSetFrameSyncRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMPing, msg.CMPing); + this.msgManager.initRecvMsg(s2cMsg._SMPing, msg.SMPing, this.onPing.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMSendFrameEvent, msg.CMSendFrameEvent); + this.msgManager.initRecvMsg(s2cMsg._SMSendFrameEvent, msg.SMSendFrameEvent, this.onSendFrameEvtRsp.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMReConnect, msg.CMReConnect); + this.msgManager.initRecvMsg(s2cMsg._SMReConnect, msg.SMReConnect, this.onReConnectRsp.bind(this)); + + this.msgManager.initRecvMsg(s2cMsg._SMRoomPeerJoinNotify, msg.SMRoomPeerJoinNotify, this.onJoinRoomNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMRoomKickPlayerNotify, msg.SMRoomKickPlayerNotify, this.onKickNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMRoomEventNotify, msg.SMRoomEventNotify, this.onRoomEvtNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMRoomPeerLeaveNotify, msg.SMRoomPeerLeaveNotify, this.onLeaveRoomNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMFrameEventUpdateNotify, msg.SMFrameEventUpdateNotify, this.onFrameUpdateNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMRoomDisbandNotify, msg.SMRoomDisbandNotify, this.onRoomDestoryNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMJoinOverNotify, msg.SMJoinOverNotify, this.onJoinOverNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMJoinOpenNotify, msg.SMJoinOpenNotify, this.onJoinOpenNotify.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMNetWorkStateNotify, msg.SMNetWorkStateNotify, this.onNetWorkStateNotify.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMSetRoomParam, msg.CMSetRoomParam); + this.msgManager.initRecvMsg(s2cMsg._SMSetRoomParam, msg.SMSetRoomParam, this.onSMSetRoomParam.bind(this)); + this.msgManager.initRecvMsg(s2cMsg._SMRoomMergeNotify, msg.SMRoomMergeNotify, this.onSMRoomMergeNotify.bind(this)); + + this.msgManager.initSendMsg(c2sMsg._CMResetRoom, msg.CMResetRoom); + this.msgManager.initRecvMsg(s2cMsg._SMResetRoom, msg.SMResetRoom, this.onSMResetRoom.bind(this)); + + + this.userinfo.init_state = state_enum.inited; + + this.rsp && this.rsp.initResponse({ + status: 0 + }); + }.bind(this)); + } + + this.setUserId=function(userid){ + this.userinfo.userID = userid; + } + + this.setToken=function(token){ + this.userinfo.token = token; + } + + this. _checkInitState=function(){ + if(this.userinfo.init_state != state_enum.inited){ + if(this.userinfo.init_state == state_enum.none) + return -2; + if(this.userinfo.init_state == state_enum.initing) + return -3; + } + return 0; + } + + this._checkLoginState=function(currstate){ + if(this.userinfo.login_state == state_enum.none && currstate != state_enum.logining){ + return -4; + } + if(this.userinfo.login_state == state_enum.logined && currstate == state_enum.logining){ + return -5; + } + if(this.userinfo.login_state == state_enum.logining && this.userinfo.login_state != currstate){ + return -6; + } + if(this.userinfo.login_state == state_enum.loginouting && this.userinfo.login_state != currstate){ + return -6; + } + return 0; + } + + this._checkRoomState=function(currstate){ + if(this.userinfo.room_state == state_enum.none && currstate != state_enum.joining){ + return -9; + } + if(this.userinfo.room_state == state_enum.joined && currstate == state_enum.joining){ + return -8; + } + if(this.userinfo.room_state == state_enum.joining && this.userinfo.room_state != currstate){ + return -7; + } + if(this.userinfo.room_state != state_enum.joined && currstate == state_enum.leaving){ + return -6; + } + return 0; + } + + this._netCallback=function(msg, err){ + if(msg == "connect"){ + this.reconnectcount = 0; + var res = { + status: 0, + eventtype: msg + }; + this.rsp && this.rsp.netResponse(res); + if(this.userinfo.roomID != ""){ + this.reconnect(); + }else{ + this.login(); + } + //this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMLogin, param); + }else{ + if(msg == "close"){ + if(this.reconnectcount > this.reconnectmax){ + var res = { + status: err, + eventtype: msg + }; + this.rsp && this.rsp.netResponse(res); + this._cleanState(); + }else{ + var self = this; + this.scheduleOnce(function(dt){ + self.reconnectcount++; + self.reconnectNet(); + var res = { + status: -1, + eventtype: "reconnect" + }; + this.rsp && this.rsp.netResponse(res); + }, 2); + } + + } + } + } + + this._cleanState=function(){ + this.userinfo.login_state = state_enum.none; + this.userinfo.room_state = state_enum.none; + this.userinfo.roomID = ""; + } + + this._handleDeltaTime=function(createtime, passtime){ + var ts = createtime * 1000 + passtime; + var d = new Date(); + var ct = d.getTime() - ts; + this.msgctrl.setDeltaTime(ct); + } + + this.isInited=function() { + return this.userinfo.init_state == state_enum.inited; + }, + + this.isLogined=function() { + return this.userinfo.login_state == state_enum.logined; + } + + this.isInRoom=function (){ + return this.userinfo.room_state == state_enum.joined; + } + + this.isConnected=function (){ + return this.msgManager.isConnected(); + } + + this.reconnectNet=function(){ + //this._cleanState(); + var url = this.userinfo.platForm == "alpha"? url_test: url_offical; + this.msgManager.connectNet(url, this._netCallback.bind(this)); + } + + this.disconnect=function(){ + // if(this.msgManager.isConnected()){ + // this.msgManager.closeNet(); + // } + this.msgManager.closeNet(); + } + + this.login=function(){ + var st = state_enum.logining; + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(st); + if(rs != 0){ + return rs; + } + this.userinfo.login_state = st; + + var param = { + account_id: this.userinfo.userID, + session_id: this.userinfo.token, + game_id: this.userinfo.gameID, + device_id: this.userinfo.deviceID + }; + + if(!this.isConnected()){ + this.reconnectNet(); + }else{ + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMLogin, param); + } + this._log && this._log.logVS_login(); + return 0; + } + + this.loginOut=function(){ + this.userinfo.login_state = state_enum.none; + var param = { + status: 0 + }; + this.rsp && this.rsp.logoutResponse(param); + } + + this.createRoom=function(roomname, maxplayer, userdesc){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.joining; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + this.userinfo.room_state = st; + var param = { + create_room_info:{ + room_name: roomname, + max_player: maxplayer, + allow_merge: 0, + auto_exit_room : 1, + }, + user_profile: userdesc + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMCreateRoom, param); + return 0; + } + + this.joinRoom=function(roomid, userdesc){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.joining; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + this.userinfo.room_state = st; + var param = { + room_id: roomid, + user_profile: userdesc + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMJoinRoom, param); + window.log && window.log.logVS_joinRoom(roomid, userdesc, false); + return 0; + } + + this.joinRandomRoom=function(maxplayer, userdesc){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.joining; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + this.userinfo.room_state = st; + var param = { + max_player: maxplayer, + user_profile: userdesc + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMJoinRandomRoom, param); + window.log && window.log.logVS_joinRoom('', userdesc, true); + return 0; + } + + this.leaveRoom=function(content){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.leaving; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + this.userinfo.room_state = st; + var param = { + custom_data: content + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMLeaveRoom, param); + window.log && window.log.logVS_leaveRoom(this.userinfo.roomID, content); + return 0; + } + + this.joinOver=function(content){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + custom_data: content + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMJoinOver, param); + window.log && window.log.logVS_gameReady(this.userinfo.roomID, content, true); + return 0; + } + + this.joinOpen=function(content){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + custom_data: content + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMJoinOpen, param); + window.log && window.log.logVS_gameReady(this.userinfo.roomID, content, false); + return 0; + } + + this.gameStart=function(content){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + custom_data: content + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMGameStart, param); + window.log && window.log.logVS_gameStart(this.userinfo.roomID, content); + return 0; + } + + this.kickPlayer=function(userid, content){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + account_id: userid, + custom_data: content + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMKickPlayer, param); + return 0; + } + + this.sendRoomMsg=function(msg){ + var d = new Date(); + msg.timestamp = d.getTime() - this.msgctrl.deltatime + this.msgctrl.delaytime; + this.sendRoomEvent(JSON.stringify(msg)); + } + + this.sendRoomEvent=function(msg){ + console.log("[sendRoomEvent]"+msg); + var rs = this._checkInitState(); + console.log("rs"+rs); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + custom_data: msg + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMSendRoomEvent, param); + return 0; + } + + this.sendFrameEvent=function(msg){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + custom_data: msg + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMSendFrameEvent, param); + return 0; + } + + this.setFrameSync=function(framerate){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + frame_rate: framerate + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMSetFrameSync, param); + return 0; + } + + this.heart=function(){ + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMPing, null); + return 0; + } + + this.reconnect=function(){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + + var param = { + room_id: this.userinfo.roomID, + account_id: this.userinfo.userID, + session_id: this.userinfo.token + }; + + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMReConnect, param); + return 0; + } + + this.onLoginRsp=function(msg){ + console.log("[onLoginRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result == null || msg.result.error_code == 0){ + this.userinfo.login_state = state_enum.logined; + this.userinfo.roomID = msg.room_id? msg.room_id: ""; + }else{ + err = msg.result.error_code; + errstr = msg.result.error_msg; + this.userinfo.login_state = state_enum.none; + this.userinfo.roomID = ""; + } + var param = { + status: err, + statusmsg: errstr, + roomID: this.userinfo.roomID + }; + this.rsp && this.rsp.loginResponse(param); + } + + this.onLoginOutRsp=function(msg){ + console.log("[onLoginOutRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result == null || msg.result.error_code == 0){ + this.userinfo.login_state = state_enum.none; + }else{ + err = msg.result.error_code; + errstr = msg.result.error_msg; + this.userinfo.login_state = state_enum.logined; + } + var param = { + status: err, + statusmsg: errstr, + }; + this.rsp && this.rsp.logoutResponse(param); + } + + this.onCreateRoomRsp=function(msg){ + console.log("[onCreateRoomRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result == null || msg.result.error_code == 0){ + this.userinfo.room_state = state_enum.joined; + this.userinfo.roomID = msg.room_id? msg.room_id: ""; + }else{ + err = msg.result.error_code; + errstr = msg.result.error_msg; + this.userinfo.room_state = state_enum.none; + window.log && window.log.logButtonClick('matchvs', msg.result, 'onCreateRoomRsp'); + } + var param = { + status: err, + statusmsg: errstr, + roomID: msg.room_id? msg.room_id: "", + owner: "", + create_time: msg.create_time, + room_info:msg.room_info, + }; + + if(param.status == 0){ + this._handleDeltaTime(param.create_time, 0); + } + + this.rsp && this.rsp.createRoomResponse(param); + } + + this.onJoinRoomRsp=function(msg){ + console.log("[onJoinRoomRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result == null ||msg.result.error_code == 0||msg.result.error_code == -3){ + this.userinfo.room_state = state_enum.joined; + this.userinfo.roomID = msg.room_info? msg.room_info.room_id: ""; + + }else{ + err = msg.result.error_code; + errstr = msg.result.error_msg; + this.userinfo.room_state = state_enum.none; + window.log && window.log.logButtonClick('matchvs', msg.result, 'onJoinRoomRsp'); + } + + var lst = []; + if(msg.room_user_info_list){ + msg.room_user_info_list.forEach(element => { + var obj = { + userId: element.account_id, + userProfile: element.user_profile + }; + lst.push(obj); + }); + } + var param = { + status: err, + statusmsg: errstr, + roomInfo:{ + roomID: msg.room_info? msg.room_info.room_id: "", + roomProperty: msg.room_info? msg.room_info.room_property: "", + ownerId: msg.room_info? msg.room_info.owner_id: "", + create_time: msg.room_info? msg.room_info.create_time: 0, + _timestamp: msg.room_info? msg.room_info._timestamp: 0, + robot_list:msg.room_info? msg.room_info.robot_list: 0 + }, + roomUserInfoList: lst + }; + + if(param.status == 0||param.status == -3){ + this._handleDeltaTime(param.roomInfo.create_time, param.roomInfo._timestamp); + } + + this.rsp && this.rsp.joinRoomResponse(param); + } + + this.onJoinOverRsp=function(msg){ + console.log("[onJoinOverRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null && msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + window.log && window.log.logButtonClick('matchvs', msg.result, 'onJoinOverRsp'); + } + var param = { + status: err, + statusmsg: errstr, + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.joinOverResponse(param); + } + + this.onJoinOpenRsp=function(msg){ + console.log("[onJoinOpenRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null && msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + window.log && window.log.logButtonClick('matchvs', msg.result, 'onJoinOpenRsp'); + } + var param = { + status: err, + statusmsg: errstr, + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.joinOpenResponse(param); + } + + this.onGameStartRsp=function(msg){ + console.log("[onGameStartRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null && msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + window.log && window.log.logButtonClick('matchvs', msg.result, 'onGameStartRsp'); + } + var param = { + status: err, + statusmsg: errstr, + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.gameStartResponse(param); + } + + this.onLeaveRoomRsp=function(msg){ + console.log("[onLeaveRoomRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result == null || msg.result.error_code == 0 || msg.result.error_code == -1){ + this.userinfo.room_state = state_enum.none; + this.userinfo.roomID = ""; + }else{ + err = msg.result.error_code; + errstr = msg.result.error_msg; + this.userinfo.room_state = state_enum.joined; + window.log && window.log.logButtonClick('matchvs', msg.result, 'onLeaveRoomRsp'); + } + var param = { + status: err, + statusmsg: errstr, + roomID: msg.room_id? msg.room_id: "", + userId: msg.account_id? msg.account_id: "", + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.leaveRoomResponse(param); + } + + this.onKickPlayerRsp=function(msg){ + console.log("[onKickPlayerRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null && msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + } + var param = { + status: err, + statusmsg: errstr, + owner: "", + userId: msg.account_id? msg.account_id: "", + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.kickPlayerResponse(param); + } + + this.onSendRoomEvtRsp=function(msg){ + console.log("[onSendRoomEvtRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null && msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + } + var param = { + status: err, + statusmsg: errstr, + eventid: msg.event_id? msg.event_id: "", + content: msg.custom_data? msg.custom_data: "", + _timestamp: msg._timestamp? msg._timestamp: 0 + }; + this.rsp && this.rsp.sendEventResponse(param); + } + + this.onSetFrameSyncRsp=function(msg){ + console.log("[onSetFrameSyncRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null && msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + } + var param = { + status: err, + statusmsg: errstr, + framerate: msg.frame_rate? msg.frame_rate: 0 + }; + this.rsp && this.rsp.setFrameSyncResponse(param); + } + + this.onSendFrameEvtRsp=function(msg){ + // console.log("[onSendFrameEvtRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null && msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + } + var param = { + status: err, + statusmsg: errstr, + eventid: msg.event_id? msg.event_id: "" + }; + this.rsp && this.rsp.sendFrameEventResponse(param); + } + + this.onReConnectRsp=function(msg){ + console.log("[onReConnectRsp]"+JSON.stringify(msg)); + var err = 0; + var errstr = ""; + if(msg.result != null){ + if(msg.result.error_code != 0){ + err = msg.result.error_code; + errstr = msg.result.error_msg; + this._cleanState(); + window.log && window.log.logButtonClick('matchvs', msg.result, 'onReConnectRsp'); + }else{ + this.userinfo.roomID = msg.room_info? msg.room_info.room_id: ""; + } + } + + var lst = []; + if(msg.room_user_info_list){ + msg.room_user_info_list.forEach(element => { + var obj = { + userId: element.account_id, + userProfile: element.user_profile + }; + lst.push(obj); + }); + } + var param = { + status: err, + statusmsg: errstr, + roomInfo:{ + roomID: msg.room_info? msg.room_info.room_id: "", + roomProperty: msg.room_info? msg.room_info.room_property: "", + ownerId: msg.room_info? msg.room_info.owner_id: "", + create_time: msg.room_info? msg.room_info.create_time: 0, + _timestamp: msg.room_info? msg.room_info._timestamp: 0 + }, + roomUserInfoList: lst + }; + + if(param.status == 0){ + this._handleDeltaTime(param.roomInfo.create_time, param.roomInfo._timestamp); + } + + this.rsp && this.rsp.reconnectResponse(param); + } + + this.onRoomEvtNotify=function(msg){ + console.log("[onRoomEvtNotify]"+JSON.stringify(msg)); + var param = { + srcUserId: msg.src_account_id? msg.src_account_id: "", + eventid: msg.event_id? msg.event_id: "", + content: msg.custom_data? msg.custom_data: "" + }; + this.msgctrl.pushMsg(param); + this.rsp && this.rsp.sendRoomEvtNotify(param); + } + + this.onKickNotify=function(msg){ + console.log("[onKickNotify]"+JSON.stringify(msg)); + var param = { + srcUserId: "", + userId: msg.account_id? msg.account_id: "", + owner: "", + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.kickPlayerNotify(param); + } + + this.onJoinRoomNotify=function(msg){ + console.log("[onJoinRoomNotify]"+JSON.stringify(msg)); + var param = { + userId: msg.room_user_info? msg.room_user_info.account_id: "", + userProfile: msg.room_user_info? msg.room_user_info.user_profile: "" + }; + this.rsp && this.rsp.joinRoomNotify(param); + } + + this.onLeaveRoomNotify=function(msg){ + console.log("[onLeaveRoomNotify]"+JSON.stringify(msg)); + var param = { + userId: msg.room_user_info? msg.room_user_info.account_id: "", + userProfile: msg.room_user_info? msg.room_user_info.user_profile: "", + roomID: msg.room_id? msg.room_id: "", + owner: "", + content: "" + }; + this.rsp && this.rsp.leaveRoomNotify(param); + } + + this.onFrameUpdateNotify=function(msg){ +// console.log("[onFrameUpdateNotify]"+JSON.stringify(msg)); + var lst = []; + if(msg.frame_info && msg.frame_info.frame_items){ + msg.frame_info.frame_items.forEach(element =>{ + var obj = { + srcUserID: element.src_account_id, + content: element.custom_data, + timestamp: element.timestamp + }; + lst.push(obj); + }); + } + + var param = { + frameIndex: msg.frame_info? msg.frame_info.frame_index: 0, + frameItems: lst, + frameWaitCount: msg.frame_info? msg.frame_info.frame_wait_count: 0 + }; + this.rsp && this.rsp.frameUpdate(param); + } + + this.onRoomDestoryNotify=function(msg){ + console.log("[onFrameUpdateNotify]"+JSON.stringify(msg)); + var param = { + roomID: msg.room_id? msg.room_id: "" + }; + this.userinfo.room_state = state_enum.none; + this.userinfo.roomID = ""; + this.rsp && this.rsp.destroyRoomNotify(param); + } + + this.onJoinOverNotify=function(msg){ + console.log("[onJoinOverNotify]"+JSON.stringify(msg)); + var param = { + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.joinOverRoomNotify(param); + } + + this.onJoinOpenNotify=function(msg){ + console.log("[onJoinOpenNotify]"+JSON.stringify(msg)); + var param = { + content: msg.custom_data? msg.custom_data: "" + }; + this.rsp && this.rsp.joinOpenRoomNotify(param); + } + + this.onNetWorkStateNotify=function(msg){ + console.log("[onNetWorkStateNotify]"+JSON.stringify(msg)); + var param = { + roomID: msg.room_id? msg.room_id: "", + userID: msg.account_id? msg.account_id: "", + state: msg.state? msg.state: -100, + owner: msg.owner? msg.owner: "" + }; + this.rsp && this.rsp.netWorkStateNotify(param); + } + this.resetRoom=function(msg){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + join_over: msg, + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMResetRoom, param); + } + this.setRoomParam=function(msg){ + var rs = this._checkInitState(); + if(rs != 0){ + return rs; + } + rs = this._checkLoginState(this.userinfo.login_state); + if(rs != 0){ + return rs; + } + var st = state_enum.none; + rs = this._checkRoomState(st); + if(rs != 0){ + return rs; + } + var param = { + allow_merge: msg, + }; + this.msgManager.sendMsg(this.msgIDs.c2sMsg()._CMSetRoomParam, param); + } + this.onSMSetRoomParam=function(msg){ + console.log("[onSMSetRoomParam]"); + this.rsp && this.rsp.onSMSetRoomParam(msg); + } + this.onSMResetRoom=function(msg){ + console.log("[onSMResetRoom]"); + this.rsp && this.rsp.onSMResetRoom(msg); + } + this.onSMRoomMergeNotify=function(msg){ + console.log("[onSMRoomMergeNotify]"); + this.rsp && this.rsp.onSMRoomMergeNotify(msg); + } + this.onPing=function(msg){ + + } +} + diff --git a/fc2/js/jsnes.min.js b/fc2/js/jsnes.min.js deleted file mode 100644 index 59bfadc..0000000 --- a/fc2/js/jsnes.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(t,s){"object"==typeof exports&&"object"==typeof module?module.exports=s():"function"==typeof define&&define.amd?define("jsnes",[],s):"object"==typeof exports?exports.jsnes=s():t.jsnes=s()}("undefined"!=typeof self?self:this,function(){return function(t){function s(e){if(i[e])return i[e].exports;var h=i[e]={i:e,l:!1,exports:{}};return t[e].call(h.exports,h,h.exports,s),h.l=!0,h.exports}var i={};return s.m=t,s.c=i,s.d=function(t,i,e){s.o(t,i)||Object.defineProperty(t,i,{configurable:!1,enumerable:!0,get:e})},s.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(i,"a",i),i},s.o=function(t,s){return Object.prototype.hasOwnProperty.call(t,s)},s.p="",s(s.s=3)}([function(t,s){t.exports={copyArrayElements:function(t,s,i,e,h){for(var r=0;r>7-this.x&1)+((i>>7-this.x&1)<<1),0===this.pix[this.tIndex+this.x]&&(this.opaque[t]=!1)},render:function(t,s,i,e,h,r,n,a,o,l,p,u,m){if(!(r<-7||r>=256||n<-7||n>=240))if(this.w=e-s,this.h=h-i,r<0&&(s-=r),r+e>=256&&(e=256-r),n<0&&(i-=n),n+h>=240&&(h=240-n),l||p)if(l&&!p)for(this.fbIndex=(n<<8)+r,this.tIndex=7,this.y=0;this.y<8;this.y++){for(this.x=0;this.x<8;this.x++)this.x>=s&&this.x=i&&this.y=s&&this.x=i&&this.y=s&&this.x=i&&this.y=s&&this.x=i&&this.y8?(t=24,s&&h.clockFrameCounter(8),i.cyclesToHalt-=8):(t=3*i.cyclesToHalt,s&&h.clockFrameCounter(i.cyclesToHalt),i.cyclesToHalt=0);t>0;t--){if(e.curX===e.spr0HitX&&1===e.f_spVisibility&&e.scanline-21===e.spr0HitY&&e.setStatusFlag(e.STATUS_SPRITE0HIT,!0),e.requestEndFrame&&0===--e.nmiCounter){e.requestEndFrame=!1,e.startVBlank();break t}e.curX++,341===e.curX&&(e.curX=0,e.endScanline())}this.fpsFrameCount++},buttonDown:function(t,s){this.controllers[t].buttonDown(s)},buttonUp:function(t,s){this.controllers[t].buttonUp(s)},zapperMove:function(t,s){this.mmap&&(this.mmap.zapperX=t,this.mmap.zapperY=s)},zapperFireDown:function(){this.mmap&&(this.mmap.zapperFired=!0)},zapperFireUp:function(){this.mmap&&(this.mmap.zapperFired=!1)},getFPS:function(){var t=+new Date,s=null;return this.lastFpsTime&&(s=this.fpsFrameCount/((t-this.lastFpsTime)/1e3)),this.fpsFrameCount=0,this.lastFpsTime=t,s},reloadROM:function(){null!==this.romData&&this.loadROM(this.romData)},loadROM:function(t){this.rom=new a(this),this.rom.load(t),this.reset(),this.mmap=this.rom.createMapper(),this.mmap.loadROM(),this.ppu.setMirroring(this.rom.getMirroringType()),this.romData=t},setFramerate:function(t){this.opts.preferredFrameRate=t,this.frameTime=1e3/t,this.papu.setSampleRate(this.opts.sampleRate,!1)},toJSON:function(){return{romData:this.romData,cpu:this.cpu.toJSON(),mmap:this.mmap.toJSON(),ppu:this.ppu.toJSON()}},fromJSON:function(t){this.loadROM(t.romData),this.cpu.fromJSON(t.cpu),this.mmap.fromJSON(t.mmap),this.ppu.fromJSON(t.ppu)}},t.exports=o},function(t,s,i){var e=i(0),h=function(t){this.nes=t,this.mem=null,this.REG_ACC=null,this.REG_X=null,this.REG_Y=null,this.REG_SP=null,this.REG_PC=null,this.REG_PC_NEW=null,this.REG_STATUS=null,this.F_CARRY=null,this.F_DECIMAL=null,this.F_INTERRUPT=null,this.F_INTERRUPT_NEW=null,this.F_OVERFLOW=null,this.F_SIGN=null,this.F_ZERO=null,this.F_NOTUSED=null,this.F_NOTUSED_NEW=null,this.F_BRK=null,this.F_BRK_NEW=null,this.opdata=null,this.cyclesToHalt=null,this.crash=null,this.irqRequested=null,this.irqType=null,this.reset()};h.prototype={IRQ_NORMAL:0,IRQ_NMI:1,IRQ_RESET:2,reset:function(){this.mem=new Array(65536);for(var t=0;t<8192;t++)this.mem[t]=255;for(var s=0;s<4;s++){var i=2048*s;this.mem[i+8]=247,this.mem[i+9]=239,this.mem[i+10]=223,this.mem[i+15]=191}for(var e=8193;e>24,h=0,r=i>>8&255,n=this.REG_PC;this.REG_PC+=i>>16&255;var a=0;switch(r){case 0:a=this.load(n+2);break;case 1:a=this.load(n+2),a+=a<128?this.REG_PC:this.REG_PC-256;break;case 2:break;case 3:a=this.load16bit(n+2);break;case 4:a=this.REG_ACC;break;case 5:a=this.REG_PC;break;case 6:a=this.load(n+2)+this.REG_X&255;break;case 7:a=this.load(n+2)+this.REG_Y&255;break;case 8:a=this.load16bit(n+2),(65280&a)!=(a+this.REG_X&65280)&&(h=1),a+=this.REG_X;break;case 9:a=this.load16bit(n+2),(65280&a)!=(a+this.REG_Y&65280)&&(h=1),a+=this.REG_Y;break;case 10:a=this.load(n+2),(65280&a)!=(a+this.REG_X&65280)&&(h=1),a+=this.REG_X,a&=255,a=this.load16bit(a);break;case 11:a=this.load16bit(this.load(n+2)),(65280&a)!=(a+this.REG_Y&65280)&&(h=1),a+=this.REG_Y;break;case 12:a=this.load16bit(n+2),a=a<8191?this.mem[a]+(this.mem[65280&a|1+(255&a)&255]<<8):this.nes.mmap.load(a)+(this.nes.mmap.load(65280&a|1+(255&a)&255)<<8)}switch(a&=65535,255&i){case 0:t=this.REG_ACC+this.load(a)+this.F_CARRY,0==(128&(this.REG_ACC^this.load(a)))&&0!=(128&(this.REG_ACC^t))?this.F_OVERFLOW=1:this.F_OVERFLOW=0,this.F_CARRY=t>255?1:0,this.F_SIGN=t>>7&1,this.F_ZERO=255&t,this.REG_ACC=255&t,e+=h;break;case 1:this.REG_ACC=this.REG_ACC&this.load(a),this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC,11!==r&&(e+=h);break;case 2:4===r?(this.F_CARRY=this.REG_ACC>>7&1,this.REG_ACC=this.REG_ACC<<1&255,this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC):(t=this.load(a),this.F_CARRY=t>>7&1,t=t<<1&255,this.F_SIGN=t>>7&1,this.F_ZERO=t,this.write(a,t));break;case 3:0===this.F_CARRY&&(e+=(65280&n)!=(65280&a)?2:1,this.REG_PC=a);break;case 4:1===this.F_CARRY&&(e+=(65280&n)!=(65280&a)?2:1,this.REG_PC=a);break;case 5:0===this.F_ZERO&&(e+=(65280&n)!=(65280&a)?2:1,this.REG_PC=a);break;case 6:t=this.load(a),this.F_SIGN=t>>7&1,this.F_OVERFLOW=t>>6&1,t&=this.REG_ACC,this.F_ZERO=t;break;case 7:1===this.F_SIGN&&(e++,this.REG_PC=a);break;case 8:0!==this.F_ZERO&&(e+=(65280&n)!=(65280&a)?2:1,this.REG_PC=a);break;case 9:0===this.F_SIGN&&(e+=(65280&n)!=(65280&a)?2:1,this.REG_PC=a);break;case 10:this.REG_PC+=2,this.push(this.REG_PC>>8&255),this.push(255&this.REG_PC),this.F_BRK=1,this.push(this.F_CARRY|(0===this.F_ZERO?1:0)<<1|this.F_INTERRUPT<<2|this.F_DECIMAL<<3|this.F_BRK<<4|this.F_NOTUSED<<5|this.F_OVERFLOW<<6|this.F_SIGN<<7),this.F_INTERRUPT=1,this.REG_PC=this.load16bit(65534),this.REG_PC--;break;case 11:0===this.F_OVERFLOW&&(e+=(65280&n)!=(65280&a)?2:1,this.REG_PC=a);break;case 12:1===this.F_OVERFLOW&&(e+=(65280&n)!=(65280&a)?2:1,this.REG_PC=a);break;case 13:this.F_CARRY=0;break;case 14:this.F_DECIMAL=0;break;case 15:this.F_INTERRUPT=0;break;case 16:this.F_OVERFLOW=0;break;case 17:t=this.REG_ACC-this.load(a),this.F_CARRY=t>=0?1:0,this.F_SIGN=t>>7&1,this.F_ZERO=255&t,e+=h;break;case 18:t=this.REG_X-this.load(a),this.F_CARRY=t>=0?1:0,this.F_SIGN=t>>7&1,this.F_ZERO=255&t;break;case 19:t=this.REG_Y-this.load(a),this.F_CARRY=t>=0?1:0,this.F_SIGN=t>>7&1,this.F_ZERO=255&t;break;case 20:t=this.load(a)-1&255,this.F_SIGN=t>>7&1,this.F_ZERO=t,this.write(a,t);break;case 21:this.REG_X=this.REG_X-1&255,this.F_SIGN=this.REG_X>>7&1,this.F_ZERO=this.REG_X;break;case 22:this.REG_Y=this.REG_Y-1&255,this.F_SIGN=this.REG_Y>>7&1,this.F_ZERO=this.REG_Y;break;case 23:this.REG_ACC=255&(this.load(a)^this.REG_ACC),this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC,e+=h;break;case 24:t=this.load(a)+1&255,this.F_SIGN=t>>7&1,this.F_ZERO=t,this.write(a,255&t);break;case 25:this.REG_X=this.REG_X+1&255,this.F_SIGN=this.REG_X>>7&1,this.F_ZERO=this.REG_X;break;case 26:this.REG_Y++,this.REG_Y&=255,this.F_SIGN=this.REG_Y>>7&1,this.F_ZERO=this.REG_Y;break;case 27:this.REG_PC=a-1;break;case 28:this.push(this.REG_PC>>8&255),this.push(255&this.REG_PC),this.REG_PC=a-1;break;case 29:this.REG_ACC=this.load(a),this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC,e+=h;break;case 30:this.REG_X=this.load(a),this.F_SIGN=this.REG_X>>7&1,this.F_ZERO=this.REG_X,e+=h;break;case 31:this.REG_Y=this.load(a),this.F_SIGN=this.REG_Y>>7&1,this.F_ZERO=this.REG_Y,e+=h;break;case 32:4===r?(t=255&this.REG_ACC,this.F_CARRY=1&t,t>>=1,this.REG_ACC=t):(t=255&this.load(a),this.F_CARRY=1&t,t>>=1,this.write(a,t)),this.F_SIGN=0,this.F_ZERO=t;break;case 33:break;case 34:t=255&(this.load(a)|this.REG_ACC),this.F_SIGN=t>>7&1,this.F_ZERO=t,this.REG_ACC=t,11!==r&&(e+=h);break;case 35:this.push(this.REG_ACC);break;case 36:this.F_BRK=1,this.push(this.F_CARRY|(0===this.F_ZERO?1:0)<<1|this.F_INTERRUPT<<2|this.F_DECIMAL<<3|this.F_BRK<<4|this.F_NOTUSED<<5|this.F_OVERFLOW<<6|this.F_SIGN<<7);break;case 37:this.REG_ACC=this.pull(),this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC;break;case 38:t=this.pull(),this.F_CARRY=1&t,this.F_ZERO=1==(t>>1&1)?0:1,this.F_INTERRUPT=t>>2&1,this.F_DECIMAL=t>>3&1,this.F_BRK=t>>4&1,this.F_NOTUSED=t>>5&1,this.F_OVERFLOW=t>>6&1,this.F_SIGN=t>>7&1,this.F_NOTUSED=1;break;case 39:4===r?(t=this.REG_ACC,s=this.F_CARRY,this.F_CARRY=t>>7&1,t=(t<<1&255)+s,this.REG_ACC=t):(t=this.load(a),s=this.F_CARRY,this.F_CARRY=t>>7&1,t=(t<<1&255)+s,this.write(a,t)),this.F_SIGN=t>>7&1,this.F_ZERO=t;break;case 40:4===r?(s=this.F_CARRY<<7,this.F_CARRY=1&this.REG_ACC,t=(this.REG_ACC>>1)+s,this.REG_ACC=t):(t=this.load(a),s=this.F_CARRY<<7,this.F_CARRY=1&t,t=(t>>1)+s,this.write(a,t)),this.F_SIGN=t>>7&1,this.F_ZERO=t;break;case 41:if(t=this.pull(),this.F_CARRY=1&t,this.F_ZERO=0==(t>>1&1)?1:0,this.F_INTERRUPT=t>>2&1,this.F_DECIMAL=t>>3&1,this.F_BRK=t>>4&1,this.F_NOTUSED=t>>5&1,this.F_OVERFLOW=t>>6&1,this.F_SIGN=t>>7&1,this.REG_PC=this.pull(),this.REG_PC+=this.pull()<<8,65535===this.REG_PC)return;this.REG_PC--,this.F_NOTUSED=1;break;case 42:if(this.REG_PC=this.pull(),this.REG_PC+=this.pull()<<8,65535===this.REG_PC)return;break;case 43:t=this.REG_ACC-this.load(a)-(1-this.F_CARRY),this.F_SIGN=t>>7&1,this.F_ZERO=255&t,0!=(128&(this.REG_ACC^t))&&0!=(128&(this.REG_ACC^this.load(a)))?this.F_OVERFLOW=1:this.F_OVERFLOW=0,this.F_CARRY=t<0?0:1,this.REG_ACC=255&t,11!==r&&(e+=h);break;case 44:this.F_CARRY=1;break;case 45:this.F_DECIMAL=1;break;case 46:this.F_INTERRUPT=1;break;case 47:this.write(a,this.REG_ACC);break;case 48:this.write(a,this.REG_X);break;case 49:this.write(a,this.REG_Y);break;case 50:this.REG_X=this.REG_ACC,this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC;break;case 51:this.REG_Y=this.REG_ACC,this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC;break;case 52:this.REG_X=this.REG_SP-256,this.F_SIGN=this.REG_SP>>7&1,this.F_ZERO=this.REG_X;break;case 53:this.REG_ACC=this.REG_X,this.F_SIGN=this.REG_X>>7&1,this.F_ZERO=this.REG_X;break;case 54:this.REG_SP=this.REG_X+256,this.stackWrap();break;case 55:this.REG_ACC=this.REG_Y,this.F_SIGN=this.REG_Y>>7&1,this.F_ZERO=this.REG_Y;break;case 56:t=this.REG_ACC&this.load(a),this.F_CARRY=1&t,this.REG_ACC=this.F_ZERO=t>>1,this.F_SIGN=0;break;case 57:this.REG_ACC=this.F_ZERO=this.REG_ACC&this.load(a),this.F_CARRY=this.F_SIGN=this.REG_ACC>>7&1;break;case 58:t=this.REG_ACC&this.load(a),this.REG_ACC=this.F_ZERO=(t>>1)+(this.F_CARRY<<7),this.F_SIGN=this.F_CARRY,this.F_CARRY=t>>7&1,this.F_OVERFLOW=1&(t>>7^t>>6);break;case 59:t=(this.REG_X&this.REG_ACC)-this.load(a),this.F_SIGN=t>>7&1,this.F_ZERO=255&t,0!=(128&(this.REG_X^t))&&0!=(128&(this.REG_X^this.load(a)))?this.F_OVERFLOW=1:this.F_OVERFLOW=0,this.F_CARRY=t<0?0:1,this.REG_X=255&t;break;case 60:this.REG_ACC=this.REG_X=this.F_ZERO=this.load(a),this.F_SIGN=this.REG_ACC>>7&1,e+=h;break;case 61:this.write(a,this.REG_ACC&this.REG_X);break;case 62:t=this.load(a)-1&255,this.write(a,t),t=this.REG_ACC-t,this.F_CARRY=t>=0?1:0,this.F_SIGN=t>>7&1,this.F_ZERO=255&t,11!==r&&(e+=h);break;case 63:t=this.load(a)+1&255,this.write(a,t),t=this.REG_ACC-t-(1-this.F_CARRY),this.F_SIGN=t>>7&1,this.F_ZERO=255&t,0!=(128&(this.REG_ACC^t))&&0!=(128&(this.REG_ACC^this.load(a)))?this.F_OVERFLOW=1:this.F_OVERFLOW=0,this.F_CARRY=t<0?0:1,this.REG_ACC=255&t,11!==r&&(e+=h);break;case 64:t=this.load(a),s=this.F_CARRY,this.F_CARRY=t>>7&1,t=(t<<1&255)+s,this.write(a,t),this.REG_ACC=this.REG_ACC&t,this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC,11!==r&&(e+=h);break;case 65:t=this.load(a),s=this.F_CARRY<<7,this.F_CARRY=1&t,t=(t>>1)+s,this.write(a,t),t=this.REG_ACC+this.load(a)+this.F_CARRY,0==(128&(this.REG_ACC^this.load(a)))&&0!=(128&(this.REG_ACC^t))?this.F_OVERFLOW=1:this.F_OVERFLOW=0,this.F_CARRY=t>255?1:0,this.F_SIGN=t>>7&1,this.F_ZERO=255&t,this.REG_ACC=255&t,11!==r&&(e+=h);break;case 66:t=this.load(a),this.F_CARRY=t>>7&1,t=t<<1&255,this.write(a,t),this.REG_ACC=this.REG_ACC|t,this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC,11!==r&&(e+=h);break;case 67:t=255&this.load(a),this.F_CARRY=1&t,t>>=1,this.write(a,t),this.REG_ACC=this.REG_ACC^t,this.F_SIGN=this.REG_ACC>>7&1,this.F_ZERO=this.REG_ACC,11!==r&&(e+=h);break;case 68:break;case 69:this.load(a),11!==r&&(e+=h);break;default:this.nes.stop(),this.nes.crashMessage="Game crashed, invalid opcode at address $"+n.toString(16)}return e},load:function(t){return t<8192?this.mem[2047&t]:this.nes.mmap.load(t)},load16bit:function(t){return t<8191?this.mem[2047&t]|this.mem[t+1&2047]<<8:this.nes.mmap.load(t)|this.nes.mmap.load(t+1)<<8},write:function(t,s){t<8192?this.mem[2047&t]=s:this.nes.mmap.write(t,s)},requestIrq:function(t){this.irqRequested&&t===this.IRQ_NORMAL||(this.irqRequested=!0,this.irqType=t)},push:function(t){this.nes.mmap.write(this.REG_SP,t),this.REG_SP--,this.REG_SP=256|255&this.REG_SP},stackWrap:function(){this.REG_SP=256|255&this.REG_SP},pull:function(){return this.REG_SP++,this.REG_SP=256|255&this.REG_SP,this.nes.mmap.load(this.REG_SP)},pageCrossed:function(t,s){return(65280&t)!=(65280&s)},haltCycles:function(t){this.cyclesToHalt+=t},doNonMaskableInterrupt:function(t){0!=(128&this.nes.mmap.load(8192))&&(this.REG_PC_NEW++,this.push(this.REG_PC_NEW>>8&255),this.push(255&this.REG_PC_NEW),this.push(t),this.REG_PC_NEW=this.nes.mmap.load(65530)|this.nes.mmap.load(65531)<<8,this.REG_PC_NEW--)},doResetInterrupt:function(){this.REG_PC_NEW=this.nes.mmap.load(65532)|this.nes.mmap.load(65533)<<8,this.REG_PC_NEW--},doIrq:function(t){this.REG_PC_NEW++,this.push(this.REG_PC_NEW>>8&255),this.push(255&this.REG_PC_NEW),this.push(t),this.F_INTERRUPT_NEW=1,this.F_BRK_NEW=0,this.REG_PC_NEW=this.nes.mmap.load(65534)|this.nes.mmap.load(65535)<<8,this.REG_PC_NEW--},getStatus:function(){return this.F_CARRY|this.F_ZERO<<1|this.F_INTERRUPT<<2|this.F_DECIMAL<<3|this.F_BRK<<4|this.F_NOTUSED<<5|this.F_OVERFLOW<<6|this.F_SIGN<<7},setStatus:function(t){this.F_CARRY=1&t,this.F_ZERO=t>>1&1,this.F_INTERRUPT=t>>2&1,this.F_DECIMAL=t>>3&1,this.F_BRK=t>>4&1,this.F_NOTUSED=t>>5&1,this.F_OVERFLOW=t>>6&1,this.F_SIGN=t>>7&1},JSON_PROPERTIES:["mem","cyclesToHalt","irqRequested","irqType","REG_ACC","REG_X","REG_Y","REG_SP","REG_PC","REG_PC_NEW","REG_STATUS","F_CARRY","F_DECIMAL","F_INTERRUPT","F_INTERRUPT_NEW","F_OVERFLOW","F_SIGN","F_ZERO","F_NOTUSED","F_NOTUSED_NEW","F_BRK","F_BRK_NEW"],toJSON:function(){return e.toJSON(this)},fromJSON:function(t){e.fromJSON(this,t)}};var r=function(){this.opdata=new Array(256);for(var t=0;t<256;t++)this.opdata[t]=255;this.setOp(this.INS_ADC,105,this.ADDR_IMM,2,2),this.setOp(this.INS_ADC,101,this.ADDR_ZP,2,3),this.setOp(this.INS_ADC,117,this.ADDR_ZPX,2,4),this.setOp(this.INS_ADC,109,this.ADDR_ABS,3,4),this.setOp(this.INS_ADC,125,this.ADDR_ABSX,3,4),this.setOp(this.INS_ADC,121,this.ADDR_ABSY,3,4),this.setOp(this.INS_ADC,97,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_ADC,113,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_AND,41,this.ADDR_IMM,2,2),this.setOp(this.INS_AND,37,this.ADDR_ZP,2,3),this.setOp(this.INS_AND,53,this.ADDR_ZPX,2,4),this.setOp(this.INS_AND,45,this.ADDR_ABS,3,4),this.setOp(this.INS_AND,61,this.ADDR_ABSX,3,4),this.setOp(this.INS_AND,57,this.ADDR_ABSY,3,4),this.setOp(this.INS_AND,33,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_AND,49,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_ASL,10,this.ADDR_ACC,1,2),this.setOp(this.INS_ASL,6,this.ADDR_ZP,2,5),this.setOp(this.INS_ASL,22,this.ADDR_ZPX,2,6),this.setOp(this.INS_ASL,14,this.ADDR_ABS,3,6),this.setOp(this.INS_ASL,30,this.ADDR_ABSX,3,7),this.setOp(this.INS_BCC,144,this.ADDR_REL,2,2),this.setOp(this.INS_BCS,176,this.ADDR_REL,2,2),this.setOp(this.INS_BEQ,240,this.ADDR_REL,2,2),this.setOp(this.INS_BIT,36,this.ADDR_ZP,2,3),this.setOp(this.INS_BIT,44,this.ADDR_ABS,3,4),this.setOp(this.INS_BMI,48,this.ADDR_REL,2,2),this.setOp(this.INS_BNE,208,this.ADDR_REL,2,2),this.setOp(this.INS_BPL,16,this.ADDR_REL,2,2),this.setOp(this.INS_BRK,0,this.ADDR_IMP,1,7),this.setOp(this.INS_BVC,80,this.ADDR_REL,2,2),this.setOp(this.INS_BVS,112,this.ADDR_REL,2,2),this.setOp(this.INS_CLC,24,this.ADDR_IMP,1,2),this.setOp(this.INS_CLD,216,this.ADDR_IMP,1,2),this.setOp(this.INS_CLI,88,this.ADDR_IMP,1,2),this.setOp(this.INS_CLV,184,this.ADDR_IMP,1,2),this.setOp(this.INS_CMP,201,this.ADDR_IMM,2,2),this.setOp(this.INS_CMP,197,this.ADDR_ZP,2,3),this.setOp(this.INS_CMP,213,this.ADDR_ZPX,2,4),this.setOp(this.INS_CMP,205,this.ADDR_ABS,3,4),this.setOp(this.INS_CMP,221,this.ADDR_ABSX,3,4),this.setOp(this.INS_CMP,217,this.ADDR_ABSY,3,4),this.setOp(this.INS_CMP,193,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_CMP,209,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_CPX,224,this.ADDR_IMM,2,2),this.setOp(this.INS_CPX,228,this.ADDR_ZP,2,3),this.setOp(this.INS_CPX,236,this.ADDR_ABS,3,4),this.setOp(this.INS_CPY,192,this.ADDR_IMM,2,2),this.setOp(this.INS_CPY,196,this.ADDR_ZP,2,3),this.setOp(this.INS_CPY,204,this.ADDR_ABS,3,4),this.setOp(this.INS_DEC,198,this.ADDR_ZP,2,5),this.setOp(this.INS_DEC,214,this.ADDR_ZPX,2,6),this.setOp(this.INS_DEC,206,this.ADDR_ABS,3,6),this.setOp(this.INS_DEC,222,this.ADDR_ABSX,3,7),this.setOp(this.INS_DEX,202,this.ADDR_IMP,1,2),this.setOp(this.INS_DEY,136,this.ADDR_IMP,1,2),this.setOp(this.INS_EOR,73,this.ADDR_IMM,2,2),this.setOp(this.INS_EOR,69,this.ADDR_ZP,2,3),this.setOp(this.INS_EOR,85,this.ADDR_ZPX,2,4),this.setOp(this.INS_EOR,77,this.ADDR_ABS,3,4),this.setOp(this.INS_EOR,93,this.ADDR_ABSX,3,4),this.setOp(this.INS_EOR,89,this.ADDR_ABSY,3,4),this.setOp(this.INS_EOR,65,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_EOR,81,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_INC,230,this.ADDR_ZP,2,5),this.setOp(this.INS_INC,246,this.ADDR_ZPX,2,6),this.setOp(this.INS_INC,238,this.ADDR_ABS,3,6),this.setOp(this.INS_INC,254,this.ADDR_ABSX,3,7),this.setOp(this.INS_INX,232,this.ADDR_IMP,1,2),this.setOp(this.INS_INY,200,this.ADDR_IMP,1,2),this.setOp(this.INS_JMP,76,this.ADDR_ABS,3,3),this.setOp(this.INS_JMP,108,this.ADDR_INDABS,3,5),this.setOp(this.INS_JSR,32,this.ADDR_ABS,3,6),this.setOp(this.INS_LDA,169,this.ADDR_IMM,2,2),this.setOp(this.INS_LDA,165,this.ADDR_ZP,2,3),this.setOp(this.INS_LDA,181,this.ADDR_ZPX,2,4),this.setOp(this.INS_LDA,173,this.ADDR_ABS,3,4),this.setOp(this.INS_LDA,189,this.ADDR_ABSX,3,4),this.setOp(this.INS_LDA,185,this.ADDR_ABSY,3,4),this.setOp(this.INS_LDA,161,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_LDA,177,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_LDX,162,this.ADDR_IMM,2,2),this.setOp(this.INS_LDX,166,this.ADDR_ZP,2,3),this.setOp(this.INS_LDX,182,this.ADDR_ZPY,2,4),this.setOp(this.INS_LDX,174,this.ADDR_ABS,3,4),this.setOp(this.INS_LDX,190,this.ADDR_ABSY,3,4),this.setOp(this.INS_LDY,160,this.ADDR_IMM,2,2),this.setOp(this.INS_LDY,164,this.ADDR_ZP,2,3),this.setOp(this.INS_LDY,180,this.ADDR_ZPX,2,4),this.setOp(this.INS_LDY,172,this.ADDR_ABS,3,4),this.setOp(this.INS_LDY,188,this.ADDR_ABSX,3,4),this.setOp(this.INS_LSR,74,this.ADDR_ACC,1,2),this.setOp(this.INS_LSR,70,this.ADDR_ZP,2,5),this.setOp(this.INS_LSR,86,this.ADDR_ZPX,2,6),this.setOp(this.INS_LSR,78,this.ADDR_ABS,3,6),this.setOp(this.INS_LSR,94,this.ADDR_ABSX,3,7),this.setOp(this.INS_NOP,26,this.ADDR_IMP,1,2),this.setOp(this.INS_NOP,58,this.ADDR_IMP,1,2),this.setOp(this.INS_NOP,90,this.ADDR_IMP,1,2),this.setOp(this.INS_NOP,122,this.ADDR_IMP,1,2),this.setOp(this.INS_NOP,218,this.ADDR_IMP,1,2),this.setOp(this.INS_NOP,234,this.ADDR_IMP,1,2),this.setOp(this.INS_NOP,250,this.ADDR_IMP,1,2),this.setOp(this.INS_ORA,9,this.ADDR_IMM,2,2),this.setOp(this.INS_ORA,5,this.ADDR_ZP,2,3),this.setOp(this.INS_ORA,21,this.ADDR_ZPX,2,4),this.setOp(this.INS_ORA,13,this.ADDR_ABS,3,4),this.setOp(this.INS_ORA,29,this.ADDR_ABSX,3,4),this.setOp(this.INS_ORA,25,this.ADDR_ABSY,3,4),this.setOp(this.INS_ORA,1,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_ORA,17,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_PHA,72,this.ADDR_IMP,1,3),this.setOp(this.INS_PHP,8,this.ADDR_IMP,1,3),this.setOp(this.INS_PLA,104,this.ADDR_IMP,1,4),this.setOp(this.INS_PLP,40,this.ADDR_IMP,1,4),this.setOp(this.INS_ROL,42,this.ADDR_ACC,1,2),this.setOp(this.INS_ROL,38,this.ADDR_ZP,2,5),this.setOp(this.INS_ROL,54,this.ADDR_ZPX,2,6),this.setOp(this.INS_ROL,46,this.ADDR_ABS,3,6),this.setOp(this.INS_ROL,62,this.ADDR_ABSX,3,7),this.setOp(this.INS_ROR,106,this.ADDR_ACC,1,2),this.setOp(this.INS_ROR,102,this.ADDR_ZP,2,5),this.setOp(this.INS_ROR,118,this.ADDR_ZPX,2,6),this.setOp(this.INS_ROR,110,this.ADDR_ABS,3,6),this.setOp(this.INS_ROR,126,this.ADDR_ABSX,3,7),this.setOp(this.INS_RTI,64,this.ADDR_IMP,1,6),this.setOp(this.INS_RTS,96,this.ADDR_IMP,1,6),this.setOp(this.INS_SBC,233,this.ADDR_IMM,2,2),this.setOp(this.INS_SBC,229,this.ADDR_ZP,2,3),this.setOp(this.INS_SBC,245,this.ADDR_ZPX,2,4),this.setOp(this.INS_SBC,237,this.ADDR_ABS,3,4),this.setOp(this.INS_SBC,253,this.ADDR_ABSX,3,4),this.setOp(this.INS_SBC,249,this.ADDR_ABSY,3,4),this.setOp(this.INS_SBC,225,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_SBC,241,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_SEC,56,this.ADDR_IMP,1,2),this.setOp(this.INS_SED,248,this.ADDR_IMP,1,2),this.setOp(this.INS_SEI,120,this.ADDR_IMP,1,2),this.setOp(this.INS_STA,133,this.ADDR_ZP,2,3),this.setOp(this.INS_STA,149,this.ADDR_ZPX,2,4),this.setOp(this.INS_STA,141,this.ADDR_ABS,3,4),this.setOp(this.INS_STA,157,this.ADDR_ABSX,3,5),this.setOp(this.INS_STA,153,this.ADDR_ABSY,3,5),this.setOp(this.INS_STA,129,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_STA,145,this.ADDR_POSTIDXIND,2,6),this.setOp(this.INS_STX,134,this.ADDR_ZP,2,3),this.setOp(this.INS_STX,150,this.ADDR_ZPY,2,4),this.setOp(this.INS_STX,142,this.ADDR_ABS,3,4),this.setOp(this.INS_STY,132,this.ADDR_ZP,2,3),this.setOp(this.INS_STY,148,this.ADDR_ZPX,2,4),this.setOp(this.INS_STY,140,this.ADDR_ABS,3,4),this.setOp(this.INS_TAX,170,this.ADDR_IMP,1,2),this.setOp(this.INS_TAY,168,this.ADDR_IMP,1,2),this.setOp(this.INS_TSX,186,this.ADDR_IMP,1,2),this.setOp(this.INS_TXA,138,this.ADDR_IMP,1,2),this.setOp(this.INS_TXS,154,this.ADDR_IMP,1,2),this.setOp(this.INS_TYA,152,this.ADDR_IMP,1,2),this.setOp(this.INS_ALR,75,this.ADDR_IMM,2,2),this.setOp(this.INS_ANC,11,this.ADDR_IMM,2,2),this.setOp(this.INS_ANC,43,this.ADDR_IMM,2,2),this.setOp(this.INS_ARR,107,this.ADDR_IMM,2,2),this.setOp(this.INS_AXS,203,this.ADDR_IMM,2,2),this.setOp(this.INS_LAX,163,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_LAX,167,this.ADDR_ZP,2,3),this.setOp(this.INS_LAX,175,this.ADDR_ABS,3,4),this.setOp(this.INS_LAX,179,this.ADDR_POSTIDXIND,2,5),this.setOp(this.INS_LAX,183,this.ADDR_ZPY,2,4),this.setOp(this.INS_LAX,191,this.ADDR_ABSY,3,4),this.setOp(this.INS_SAX,131,this.ADDR_PREIDXIND,2,6),this.setOp(this.INS_SAX,135,this.ADDR_ZP,2,3),this.setOp(this.INS_SAX,143,this.ADDR_ABS,3,4),this.setOp(this.INS_SAX,151,this.ADDR_ZPY,2,4),this.setOp(this.INS_DCP,195,this.ADDR_PREIDXIND,2,8),this.setOp(this.INS_DCP,199,this.ADDR_ZP,2,5),this.setOp(this.INS_DCP,207,this.ADDR_ABS,3,6),this.setOp(this.INS_DCP,211,this.ADDR_POSTIDXIND,2,8),this.setOp(this.INS_DCP,215,this.ADDR_ZPX,2,6),this.setOp(this.INS_DCP,219,this.ADDR_ABSY,3,7),this.setOp(this.INS_DCP,223,this.ADDR_ABSX,3,7),this.setOp(this.INS_ISC,227,this.ADDR_PREIDXIND,2,8),this.setOp(this.INS_ISC,231,this.ADDR_ZP,2,5),this.setOp(this.INS_ISC,239,this.ADDR_ABS,3,6),this.setOp(this.INS_ISC,243,this.ADDR_POSTIDXIND,2,8),this.setOp(this.INS_ISC,247,this.ADDR_ZPX,2,6),this.setOp(this.INS_ISC,251,this.ADDR_ABSY,3,7),this.setOp(this.INS_ISC,255,this.ADDR_ABSX,3,7),this.setOp(this.INS_RLA,35,this.ADDR_PREIDXIND,2,8),this.setOp(this.INS_RLA,39,this.ADDR_ZP,2,5),this.setOp(this.INS_RLA,47,this.ADDR_ABS,3,6),this.setOp(this.INS_RLA,51,this.ADDR_POSTIDXIND,2,8),this.setOp(this.INS_RLA,55,this.ADDR_ZPX,2,6),this.setOp(this.INS_RLA,59,this.ADDR_ABSY,3,7),this.setOp(this.INS_RLA,63,this.ADDR_ABSX,3,7),this.setOp(this.INS_RRA,99,this.ADDR_PREIDXIND,2,8),this.setOp(this.INS_RRA,103,this.ADDR_ZP,2,5),this.setOp(this.INS_RRA,111,this.ADDR_ABS,3,6),this.setOp(this.INS_RRA,115,this.ADDR_POSTIDXIND,2,8),this.setOp(this.INS_RRA,119,this.ADDR_ZPX,2,6),this.setOp(this.INS_RRA,123,this.ADDR_ABSY,3,7),this.setOp(this.INS_RRA,127,this.ADDR_ABSX,3,7);this.setOp(this.INS_SLO,3,this.ADDR_PREIDXIND,2,8),this.setOp(this.INS_SLO,7,this.ADDR_ZP,2,5),this.setOp(this.INS_SLO,15,this.ADDR_ABS,3,6),this.setOp(this.INS_SLO,19,this.ADDR_POSTIDXIND,2,8),this.setOp(this.INS_SLO,23,this.ADDR_ZPX,2,6),this.setOp(this.INS_SLO,27,this.ADDR_ABSY,3,7),this.setOp(this.INS_SLO,31,this.ADDR_ABSX,3,7),this.setOp(this.INS_SRE,67,this.ADDR_PREIDXIND,2,8),this.setOp(this.INS_SRE,71,this.ADDR_ZP,2,5),this.setOp(this.INS_SRE,79,this.ADDR_ABS,3,6),this.setOp(this.INS_SRE,83,this.ADDR_POSTIDXIND,2,8),this.setOp(this.INS_SRE,87,this.ADDR_ZPX,2,6),this.setOp(this.INS_SRE,91,this.ADDR_ABSY,3,7),this.setOp(this.INS_SRE,95,this.ADDR_ABSX,3,7),this.setOp(this.INS_SKB,128,this.ADDR_IMM,2,2),this.setOp(this.INS_SKB,130,this.ADDR_IMM,2,2),this.setOp(this.INS_SKB,137,this.ADDR_IMM,2,2),this.setOp(this.INS_SKB,194,this.ADDR_IMM,2,2),this.setOp(this.INS_SKB,226,this.ADDR_IMM,2,2),this.setOp(this.INS_IGN,12,this.ADDR_ABS,3,4),this.setOp(this.INS_IGN,28,this.ADDR_ABSX,3,4),this.setOp(this.INS_IGN,60,this.ADDR_ABSX,3,4),this.setOp(this.INS_IGN,92,this.ADDR_ABSX,3,4),this.setOp(this.INS_IGN,124,this.ADDR_ABSX,3,4),this.setOp(this.INS_IGN,220,this.ADDR_ABSX,3,4),this.setOp(this.INS_IGN,252,this.ADDR_ABSX,3,4),this.setOp(this.INS_IGN,4,this.ADDR_ZP,2,3),this.setOp(this.INS_IGN,68,this.ADDR_ZP,2,3),this.setOp(this.INS_IGN,100,this.ADDR_ZP,2,3),this.setOp(this.INS_IGN,20,this.ADDR_ZPX,2,4),this.setOp(this.INS_IGN,52,this.ADDR_ZPX,2,4),this.setOp(this.INS_IGN,84,this.ADDR_ZPX,2,4),this.setOp(this.INS_IGN,116,this.ADDR_ZPX,2,4),this.setOp(this.INS_IGN,212,this.ADDR_ZPX,2,4),this.setOp(this.INS_IGN,244,this.ADDR_ZPX,2,4),this.cycTable=new Array(7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7),this.instname=new Array(70),this.instname[0]="ADC",this.instname[1]="AND",this.instname[2]="ASL",this.instname[3]="BCC",this.instname[4]="BCS",this.instname[5]="BEQ",this.instname[6]="BIT",this.instname[7]="BMI",this.instname[8]="BNE",this.instname[9]="BPL",this.instname[10]="BRK",this.instname[11]="BVC",this.instname[12]="BVS",this.instname[13]="CLC",this.instname[14]="CLD",this.instname[15]="CLI",this.instname[16]="CLV",this.instname[17]="CMP",this.instname[18]="CPX",this.instname[19]="CPY",this.instname[20]="DEC",this.instname[21]="DEX",this.instname[22]="DEY",this.instname[23]="EOR",this.instname[24]="INC",this.instname[25]="INX",this.instname[26]="INY",this.instname[27]="JMP",this.instname[28]="JSR",this.instname[29]="LDA",this.instname[30]="LDX",this.instname[31]="LDY",this.instname[32]="LSR",this.instname[33]="NOP",this.instname[34]="ORA",this.instname[35]="PHA",this.instname[36]="PHP",this.instname[37]="PLA",this.instname[38]="PLP",this.instname[39]="ROL",this.instname[40]="ROR",this.instname[41]="RTI",this.instname[42]="RTS",this.instname[43]="SBC",this.instname[44]="SEC",this.instname[45]="SED",this.instname[46]="SEI",this.instname[47]="STA",this.instname[48]="STX",this.instname[49]="STY",this.instname[50]="TAX",this.instname[51]="TAY",this.instname[52]="TSX",this.instname[53]="TXA",this.instname[54]="TXS",this.instname[55]="TYA",this.instname[56]="ALR",this.instname[57]="ANC",this.instname[58]="ARR",this.instname[59]="AXS",this.instname[60]="LAX",this.instname[61]="SAX",this.instname[62]="DCP",this.instname[63]="ISC",this.instname[64]="RLA",this.instname[65]="RRA",this.instname[66]="SLO",this.instname[67]="SRE",this.instname[68]="SKB",this.instname[69]="IGN",this.addrDesc=new Array("Zero Page ","Relative ","Implied ","Absolute ","Accumulator ","Immediate ","Zero Page,X ","Zero Page,Y ","Absolute,X ","Absolute,Y ","Preindexed Indirect ","Postindexed Indirect","Indirect Absolute ")};r.prototype={INS_ADC:0,INS_AND:1,INS_ASL:2,INS_BCC:3,INS_BCS:4,INS_BEQ:5,INS_BIT:6,INS_BMI:7,INS_BNE:8,INS_BPL:9,INS_BRK:10,INS_BVC:11,INS_BVS:12,INS_CLC:13,INS_CLD:14,INS_CLI:15,INS_CLV:16,INS_CMP:17,INS_CPX:18,INS_CPY:19,INS_DEC:20,INS_DEX:21,INS_DEY:22,INS_EOR:23,INS_INC:24,INS_INX:25,INS_INY:26,INS_JMP:27,INS_JSR:28,INS_LDA:29,INS_LDX:30,INS_LDY:31,INS_LSR:32,INS_NOP:33,INS_ORA:34,INS_PHA:35,INS_PHP:36,INS_PLA:37,INS_PLP:38,INS_ROL:39,INS_ROR:40,INS_RTI:41,INS_RTS:42,INS_SBC:43,INS_SEC:44,INS_SED:45,INS_SEI:46,INS_STA:47,INS_STX:48,INS_STY:49,INS_TAX:50,INS_TAY:51,INS_TSX:52,INS_TXA:53,INS_TXS:54,INS_TYA:55,INS_ALR:56,INS_ANC:57,INS_ARR:58,INS_AXS:59,INS_LAX:60,INS_SAX:61,INS_DCP:62,INS_ISC:63,INS_RLA:64,INS_RRA:65,INS_SLO:66,INS_SRE:67,INS_SKB:68,INS_IGN:69,INS_DUMMY:70,ADDR_ZP:0,ADDR_REL:1,ADDR_IMP:2,ADDR_ABS:3,ADDR_ACC:4,ADDR_IMM:5,ADDR_ZPX:6,ADDR_ZPY:7,ADDR_ABSX:8,ADDR_ABSY:9,ADDR_PREIDXIND:10,ADDR_POSTIDXIND:11,ADDR_INDABS:12,setOp:function(t,s,i,e,h){this.opdata[s]=255&t|(255&i)<<8|(255&e)<<16|(255&h)<<24}},t.exports=h},function(t,s,i){var e=i(2),h=i(0),r=function(t){this.nes=t,this.vramMem=null,this.spriteMem=null,this.vramAddress=null,this.vramTmpAddress=null,this.vramBufferedReadValue=null,this.firstWrite=null,this.sramAddress=null,this.currentMirroring=null,this.requestEndFrame=null,this.nmiOk=null,this.dummyCycleToggle=null,this.validTileData=null,this.nmiCounter=null,this.scanlineAlreadyRendered=null,this.f_nmiOnVblank=null,this.f_spriteSize=null,this.f_bgPatternTable=null,this.f_spPatternTable=null,this.f_addrInc=null,this.f_nTblAddress=null,this.f_color=null,this.f_spVisibility=null,this.f_bgVisibility=null,this.f_spClipping=null,this.f_bgClipping=null,this.f_dispType=null,this.cntFV=null,this.cntV=null,this.cntH=null,this.cntVT=null,this.cntHT=null,this.regFV=null,this.regV=null,this.regH=null,this.regVT=null,this.regHT=null,this.regFH=null,this.regS=null,this.curNt=null,this.attrib=null,this.buffer=null,this.bgbuffer=null,this.pixrendered=null,this.validTileData=null,this.scantile=null,this.scanline=null,this.lastRenderedScanline=null,this.curX=null,this.sprX=null,this.sprY=null,this.sprTile=null,this.sprCol=null,this.vertFlip=null,this.horiFlip=null,this.bgPriority=null,this.spr0HitX=null,this.spr0HitY=null,this.hitSpr0=null,this.sprPalette=null,this.imgPalette=null,this.ptTile=null,this.ntable1=null,this.currentMirroring=null,this.nameTable=null,this.vramMirrorTable=null,this.palTable=null,this.showSpr0Hit=!1,this.clipToTvSize=!0,this.reset()};r.prototype={STATUS_VRAMWRITE:4,STATUS_SLSPRITECOUNT:5,STATUS_SPRITE0HIT:6,STATUS_VBLANK:7,reset:function(){var t;for(this.vramMem=new Array(32768),this.spriteMem=new Array(256),t=0;t=21&&this.scanline<=260&&(1===this.f_bgVisibility&&(this.scanlineAlreadyRendered||(this.cntHT=this.regHT,this.cntH=this.regH,this.renderBgScanline(!0,this.scanline+1-21)),this.scanlineAlreadyRendered=!1,this.hitSpr0||1!==this.f_spVisibility||this.sprX[0]>=-7&&this.sprX[0]<256&&this.sprY[0]+1<=this.scanline-20&&this.sprY[0]+1+(0===this.f_spriteSize?8:16)>=this.scanline-20&&this.checkSprite0(this.scanline-20)&&(this.hitSpr0=!0)),1!==this.f_bgVisibility&&1!==this.f_spVisibility||this.nes.mmap.clockIrqCounter())}this.scanline++,this.regsToAddress(),this.cntsToAddress()},startFrame:function(){var t=0;if(0===this.f_dispType)t=this.imgPalette[0];else switch(this.f_color){case 0:t=0;break;case 1:t=65280;break;case 2:t=16711680;break;case 3:t=0;break;case 4:t=255;break;default:t=0}var s,i=this.buffer;for(s=0;s<61440;s++)i[s]=t;var e=this.pixrendered;for(s=0;s=0&&this.sprX[0]<256&&this.sprY[0]>=0&&this.sprY[0]<240){for(t=0;t<256;t++)e[(this.sprY[0]<<8)+t]=16733525;for(t=0;t<240;t++)e[(t<<8)+this.sprX[0]]=16733525}if(this.spr0HitX>=0&&this.spr0HitX<256&&this.spr0HitY>=0&&this.spr0HitY<240){for(t=0;t<256;t++)e[(this.spr0HitY<<8)+t]=5635925;for(t=0;t<240;t++)e[(t<<8)+this.spr0HitX]=5635925}}if(this.clipToTvSize||0===this.f_bgClipping||0===this.f_spClipping)for(i=0;i<240;i++)for(s=0;s<8;s++)e[(i<<8)+s]=0;if(this.clipToTvSize)for(i=0;i<240;i++)for(s=0;s<8;s++)e[255+(i<<8)-s]=0;if(this.clipToTvSize)for(i=0;i<8;i++)for(s=0;s<256;s++)e[(i<<8)+s]=0,e[(239-i<<8)+s]=0;this.nes.ui.writeFrame(e)},updateControlReg1:function(t){this.triggerRendering(),this.f_nmiOnVblank=t>>7&1,this.f_spriteSize=t>>5&1,this.f_bgPatternTable=t>>4&1,this.f_spPatternTable=t>>3&1,this.f_addrInc=t>>2&1,this.f_nTblAddress=3&t,this.regV=t>>1&1,this.regH=1&t,this.regS=t>>4&1},updateControlReg2:function(t){this.triggerRendering(),this.f_color=t>>5&7,this.f_spVisibility=t>>4&1,this.f_bgVisibility=t>>3&1,this.f_spClipping=t>>2&1,this.f_bgClipping=t>>1&1,this.f_dispType=1&t,0===this.f_dispType&&this.palTable.setEmphasis(this.f_color),this.updatePalettes()},setStatusFlag:function(t,s){var i=1<>3&31,this.regFH=7&t):(this.regFV=7&t,this.regVT=t>>3&31),this.firstWrite=!this.firstWrite},writeVRAMAddress:function(t){this.firstWrite?(this.regFV=t>>4&3,this.regV=t>>3&1,this.regH=t>>2&1,this.regVT=7&this.regVT|(3&t)<<3):(this.triggerRendering(),this.regVT=24&this.regVT|t>>5&7,this.regHT=31&t,this.cntFV=this.regFV,this.cntV=this.regV,this.cntH=this.regH,this.cntVT=this.regVT,this.cntHT=this.regHT,this.checkSprite0(this.scanline-20)),this.firstWrite=!this.firstWrite,this.cntsToAddress(),this.vramAddress<8192&&this.nes.mmap.latchAccess(this.vramAddress)},vramLoad:function(){var t;return this.cntsToAddress(),this.regsToAddress(),this.vramAddress<=16127?(t=this.vramBufferedReadValue,this.vramAddress<8192?this.vramBufferedReadValue=this.vramMem[this.vramAddress]:this.vramBufferedReadValue=this.mirroredLoad(this.vramAddress),this.vramAddress<8192&&this.nes.mmap.latchAccess(this.vramAddress),this.vramAddress+=1===this.f_addrInc?32:1,this.cntsFromAddress(),this.regsFromAddress(),t):(t=this.mirroredLoad(this.vramAddress),this.vramAddress+=1===this.f_addrInc?32:1,this.cntsFromAddress(),this.regsFromAddress(),t)},vramWrite:function(t){this.triggerRendering(),this.cntsToAddress(),this.regsToAddress(),this.vramAddress>=8192?this.mirroredWrite(this.vramAddress,t):(this.writeMem(this.vramAddress,t),this.nes.mmap.latchAccess(this.vramAddress)),this.vramAddress+=1===this.f_addrInc?32:1,this.regsFromAddress(),this.cntsFromAddress()},sramDMA:function(t){for(var s,i=256*t,e=this.sramAddress;e<256;e++)s=this.nes.cpu.mem[i+e],this.spriteMem[e]=s,this.spriteRamWriteUpdate(e,s);this.nes.cpu.haltCycles(513)},regsFromAddress:function(){var t=this.vramTmpAddress>>8&255;this.regFV=t>>4&7,this.regV=t>>3&1,this.regH=t>>2&1,this.regVT=7&this.regVT|(3&t)<<3,t=255&this.vramTmpAddress,this.regVT=24&this.regVT|t>>5&7,this.regHT=31&t},cntsFromAddress:function(){var t=this.vramAddress>>8&255;this.cntFV=t>>4&3,this.cntV=t>>3&1,this.cntH=t>>2&1,this.cntVT=7&this.cntVT|(3&t)<<3,t=255&this.vramAddress,this.cntVT=24&this.cntVT|t>>5&7,this.cntHT=31&t},regsToAddress:function(){var t=(7&this.regFV)<<4;t|=(1&this.regV)<<3,t|=(1&this.regH)<<2,t|=this.regVT>>3&3;var s=(7&this.regVT)<<5;s|=31&this.regHT,this.vramTmpAddress=32767&(t<<8|s)},cntsToAddress:function(){var t=(7&this.cntFV)<<4;t|=(1&this.cntV)<<3,t|=(1&this.cntH)<<2,t|=this.cntVT>>3&3;var s=(7&this.cntVT)<<5;s|=31&this.cntHT,this.vramAddress=32767&(t<<8|s)},incTileCounter:function(t){for(var s=t;0!==s;s--)32===++this.cntHT&&(this.cntHT=0,++this.cntVT>=30&&2===++this.cntH&&(this.cntH=0,2===++this.cntV&&(this.cntV=0,this.cntFV++,this.cntFV&=7)))},mirroredLoad:function(t){return this.vramMem[this.vramMirrorTable[t]]},mirroredWrite:function(t,s){if(t>=16128&&t<16160)16128===t||16144===t?(this.writeMem(16128,s),this.writeMem(16144,s)):16132===t||16148===t?(this.writeMem(16132,s),this.writeMem(16148,s)):16136===t||16152===t?(this.writeMem(16136,s),this.writeMem(16152,s)):16140===t||16156===t?(this.writeMem(16140,s),this.writeMem(16156,s)):this.writeMem(t,s);else{if(!(t=21&&this.scanline<=260&&(this.renderFramePartially(this.lastRenderedScanline+1,this.scanline-21-this.lastRenderedScanline),this.lastRenderedScanline=this.scanline-21)},renderFramePartially:function(t,s){if(1===this.f_spVisibility&&this.renderSpritesPartially(t,s,!0),1===this.f_bgVisibility){var i=t<<8,e=t+s<<8;e>61440&&(e=61440);for(var h=this.buffer,r=this.bgbuffer,n=this.pixrendered,a=i;a255&&(h[a]=r[a])}1===this.f_spVisibility&&this.renderSpritesPartially(t,s,!1),this.validTileData=!1},renderBgScanline:function(t,s){var i=0===this.regS?0:256,e=(s<<8)-this.regFH;if(this.curNt=this.ntable1[this.cntV+this.cntV+this.cntH],this.cntHT=this.regHT,this.cntH=this.regH,this.curNt=this.ntable1[this.cntV+this.cntV+this.cntH],s<240&&s-this.cntFV>=0){for(var h,r,n,a,o=this.cntFV<<3,l=this.scantile,p=this.attrib,u=this.ptTile,m=this.nameTable,R=this.imgPalette,c=this.pixrendered,_=t?this.bgbuffer:this.buffer,d=0;d<32;d++){if(s>=0){if(this.validTileData){if(void 0===(h=l[d]))continue;r=h.pix,n=p[d]}else{if(void 0===(h=u[i+m[this.curNt].getTileIndex(this.cntHT,this.cntVT)]))continue;r=h.pix,n=m[this.curNt].getAttrib(this.cntHT,this.cntVT),l[d]=h,p[d]=n}var S=0,A=(d<<3)-this.regFH;if(A>-8)if(A<0&&(e-=A,S=-A),h.opaque[this.cntFV])for(;S<8;S++)_[e]=R[r[o+S]+n],c[e]|=256,e++;else for(;S<8;S++)a=r[o+S],0!==a&&(_[e]=R[a+n],c[e]|=256),e++}32==++this.cntHT&&(this.cntHT=0,this.cntH++,this.cntH%=2,this.curNt=this.ntable1[(this.cntV<<1)+this.cntH])}this.validTileData=!0}8===++this.cntFV&&(this.cntFV=0,this.cntVT++,30===this.cntVT?(this.cntVT=0,this.cntV++,this.cntV%=2,this.curNt=this.ntable1[(this.cntV<<1)+this.cntH]):32===this.cntVT&&(this.cntVT=0),this.validTileData=!1)},renderSpritesPartially:function(t,s,i){if(1===this.f_spVisibility)for(var e=0;e<64;e++)if(this.bgPriority[e]===i&&this.sprX[e]>=0&&this.sprX[e]<256&&this.sprY[e]+8>=t&&this.sprY[e]t+s&&(this.srcy2=t+s-this.sprY[e]+1),0===this.f_spPatternTable?this.ptTile[this.sprTile[e]].render(this.buffer,0,this.srcy1,8,this.srcy2,this.sprX[e],this.sprY[e]+1,this.sprCol[e],this.sprPalette,this.horiFlip[e],this.vertFlip[e],e,this.pixrendered):this.ptTile[this.sprTile[e]+256].render(this.buffer,0,this.srcy1,8,this.srcy2,this.sprX[e],this.sprY[e]+1,this.sprCol[e],this.sprPalette,this.horiFlip[e],this.vertFlip[e],e,this.pixrendered);else{var h=this.sprTile[e];0!=(1&h)&&(h=this.sprTile[e]-1+256);var r=0,n=8;this.sprY[e]t+s&&(n=t+s-this.sprY[e]),this.ptTile[h+(this.vertFlip[e]?1:0)].render(this.buffer,0,r,8,n,this.sprX[e],this.sprY[e]+1,this.sprCol[e],this.sprPalette,this.horiFlip[e],this.vertFlip[e],e,this.pixrendered),r=0,n=8,this.sprY[e]+8t+s&&(n=t+s-(this.sprY[e]+8)),this.ptTile[h+(this.vertFlip[e]?0:1)].render(this.buffer,0,r,8,n,this.sprX[e],this.sprY[e]+1+8,this.sprCol[e],this.sprPalette,this.horiFlip[e],this.vertFlip[e],e,this.pixrendered)}},checkSprite0:function(t){this.spr0HitX=-1,this.spr0HitY=-1;var s,i,e,h,r,n,a=0===this.f_spPatternTable?0:256;if(i=this.sprX[0],e=this.sprY[0]+1,0===this.f_spriteSize){if(e<=t&&e+8>t&&i>=-7&&i<256)if(h=this.ptTile[this.sprTile[0]+a],s=this.vertFlip[0]?7-(t-e):t-e,s*=8,n=256*t+i,this.horiFlip[0])for(r=7;r>=0;r--){if(i>=0&&i<256&&n>=0&&n<61440&&0!==this.pixrendered[n]&&0!==h.pix[s+r])return this.spr0HitX=n%256,this.spr0HitY=t,!0;i++,n++}else for(r=0;r<8;r++){if(i>=0&&i<256&&n>=0&&n<61440&&0!==this.pixrendered[n]&&0!==h.pix[s+r])return this.spr0HitX=n%256,this.spr0HitY=t,!0;i++,n++}}else if(e<=t&&e+16>t&&i>=-7&&i<256)if(s=this.vertFlip[0]?15-(t-e):t-e,s<8?h=this.ptTile[this.sprTile[0]+(this.vertFlip[0]?1:0)+(0!=(1&this.sprTile[0])?255:0)]:(h=this.ptTile[this.sprTile[0]+(this.vertFlip[0]?0:1)+(0!=(1&this.sprTile[0])?255:0)],this.vertFlip[0]?s=15-s:s-=8),s*=8,n=256*t+i,this.horiFlip[0])for(r=7;r>=0;r--){if(i>=0&&i<256&&n>=0&&n<61440&&0!==this.pixrendered[n]&&0!==h.pix[s+r])return this.spr0HitX=n%256,this.spr0HitY=t,!0;i++,n++}else for(r=0;r<8;r++){if(i>=0&&i<256&&n>=0&&n<61440&&0!==this.pixrendered[n]&&0!==h.pix[s+r])return this.spr0HitX=n%256,this.spr0HitY=t,!0;i++,n++}return!1},writeMem:function(t,s){this.vramMem[t]=s,t<8192?(this.vramMem[t]=s,this.patternWrite(t,s)):t>=8192&&t<9152?this.nameTableWrite(this.ntable1[0],t-8192,s):t>=9152&&t<9216?this.attribTableWrite(this.ntable1[0],t-9152,s):t>=9216&&t<10176?this.nameTableWrite(this.ntable1[1],t-9216,s):t>=10176&&t<10240?this.attribTableWrite(this.ntable1[1],t-10176,s):t>=10240&&t<11200?this.nameTableWrite(this.ntable1[2],t-10240,s):t>=11200&&t<11264?this.attribTableWrite(this.ntable1[2],t-11200,s):t>=11264&&t<12224?this.nameTableWrite(this.ntable1[3],t-11264,s):t>=12224&&t<12288?this.attribTableWrite(this.ntable1[3],t-12224,s):t>=16128&&t<16160&&this.updatePalettes()},updatePalettes:function(){var t;for(t=0;t<16;t++)0===this.f_dispType?this.imgPalette[t]=this.palTable.getEntry(63&this.vramMem[16128+t]):this.imgPalette[t]=this.palTable.getEntry(32&this.vramMem[16128+t]);for(t=0;t<16;t++)0===this.f_dispType?this.sprPalette[t]=this.palTable.getEntry(63&this.vramMem[16144+t]):this.sprPalette[t]=this.palTable.getEntry(32&this.vramMem[16144+t])},patternWrite:function(t,s){var i=Math.floor(t/16),e=t%16;e<8?this.ptTile[i].setScanline(e,s,this.vramMem[t+8]):this.ptTile[i].setScanline(e-8,this.vramMem[t-8],s)},nameTableWrite:function(t,s,i){this.nameTable[t].tile[s]=i,this.checkSprite0(this.scanline-20)},attribTableWrite:function(t,s,i){this.nameTable[t].writeAttrib(s,i)},spriteRamWriteUpdate:function(t,s){var i=Math.floor(t/4);0===i&&this.checkSprite0(this.scanline-20),t%4==0?this.sprY[i]=s:t%4==1?this.sprTile[i]=s:t%4==2?(this.vertFlip[i]=0!=(128&s),this.horiFlip[i]=0!=(64&s),this.bgPriority[i]=0!=(32&s),this.sprCol[i]=(3&s)<<2):t%4==3&&(this.sprX[i]=s)},doNMI:function(){this.setStatusFlag(this.STATUS_VBLANK,!0),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NMI)},isPixelWhite:function(t,s){return this.triggerRendering(),16777215===this.nes.ppu.buffer[(s<<8)+t]},JSON_PROPERTIES:["vramMem","spriteMem","cntFV","cntV","cntH","cntVT","cntHT","regFV","regV","regH","regVT","regHT","regFH","regS","vramAddress","vramTmpAddress","f_nmiOnVblank","f_spriteSize","f_bgPatternTable","f_spPatternTable","f_addrInc","f_nTblAddress","f_color","f_spVisibility","f_bgVisibility","f_spClipping","f_bgClipping","f_dispType","vramBufferedReadValue","firstWrite","currentMirroring","vramMirrorTable","ntable1","sramAddress","hitSpr0","sprPalette","imgPalette","curX","scanline","lastRenderedScanline","curNt","scantile","attrib","buffer","bgbuffer","pixrendered","requestEndFrame","nmiOk","dummyCycleToggle","nmiCounter","validTileData","scanlineAlreadyRendered"],toJSON:function(){var t,s=h.toJSON(this);for(s.nameTable=[],t=0;t>2*(2*o+l)&3;for(var p=0;p<2;p++)for(var u=0;u<2;u++)e=n+2*l+u,h=a+2*o+p,r=h*this.width+e,this.attrib[r]=i<<2&12}},toJSON:function(){return{tile:this.tile,attrib:this.attrib}},fromJSON:function(t){this.tile=t.tile,this.attrib=t.attrib}};var a=function(){this.curTable=new Array(64),this.emphTable=new Array(8),this.currentEmph=-1};a.prototype={reset:function(){this.setEmphasis(0)},loadNTSCPalette:function(){this.curTable=[5395026,11796480,10485760,11599933,7602281,91,95,6208,12048,543240,26368,1196544,7153664,0,0,0,12899815,16728064,14421538,16729963,14090399,6818519,6588,21681,27227,35843,43776,2918400,10777088,0,0,0,16316664,16755516,16742785,16735173,16730354,14633471,4681215,46327,57599,58229,259115,7911470,15065624,7895160,0,0,16777215,16773822,16300216,16300248,16758527,16761855,13095423,10148607,8973816,8650717,12122296,16119980,16777136,16308472,0,0],this.makeTables(),this.setEmphasis(0)},loadPALPalette:function(){this.curTable=[5395026,11796480,10485760,11599933,7602281,91,95,6208,12048,543240,26368,1196544,7153664,0,0,0,12899815,16728064,14421538,16729963,14090399,6818519,6588,21681,27227,35843,43776,2918400,10777088,0,0,0,16316664,16755516,16742785,16735173,16730354,14633471,4681215,46327,57599,58229,259115,7911470,15065624,7895160,0,0,16777215,16773822,16300216,16300248,16758527,16761855,13095423,10148607,8973816,8650717,12122296,16119980,16777136,16308472,0,0],this.makeTables(),this.setEmphasis(0)},makeTables:function(){for(var t,s,i,e,h,r,n,a,o=0;o<8;o++)for(r=1,n=1,a=1,0!=(1&o)&&(r=.75,a=.75),0!=(2&o)&&(r=.75,n=.75),0!=(4&o)&&(n=.75,a=.75),this.emphTable[o]=new Array(64),h=0;h<64;h++)e=this.curTable[h],t=Math.floor(this.getRed(e)*r),s=Math.floor(this.getGreen(e)*n),i=Math.floor(this.getBlue(e)*a),this.emphTable[o][h]=this.getRgb(t,s,i)},setEmphasis:function(t){if(t!==this.currentEmph){this.currentEmph=t;for(var s=0;s<64;s++)this.curTable[s]=this.emphTable[t][s]}},getEntry:function(t){return this.curTable[t]},getRed:function(t){return t>>16&255},getGreen:function(t){return t>>8&255},getBlue:function(t){return 255&t},getRgb:function(t,s,i){return t<<16|s<<8|i},loadDefaultPalette:function(){this.curTable[0]=this.getRgb(117,117,117),this.curTable[1]=this.getRgb(39,27,143),this.curTable[2]=this.getRgb(0,0,171),this.curTable[3]=this.getRgb(71,0,159),this.curTable[4]=this.getRgb(143,0,119),this.curTable[5]=this.getRgb(171,0,19),this.curTable[6]=this.getRgb(167,0,0),this.curTable[7]=this.getRgb(127,11,0),this.curTable[8]=this.getRgb(67,47,0),this.curTable[9]=this.getRgb(0,71,0),this.curTable[10]=this.getRgb(0,81,0),this.curTable[11]=this.getRgb(0,63,23),this.curTable[12]=this.getRgb(27,63,95),this.curTable[13]=this.getRgb(0,0,0),this.curTable[14]=this.getRgb(0,0,0),this.curTable[15]=this.getRgb(0,0,0),this.curTable[16]=this.getRgb(188,188,188),this.curTable[17]=this.getRgb(0,115,239),this.curTable[18]=this.getRgb(35,59,239),this.curTable[19]=this.getRgb(131,0,243),this.curTable[20]=this.getRgb(191,0,191),this.curTable[21]=this.getRgb(231,0,91),this.curTable[22]=this.getRgb(219,43,0),this.curTable[23]=this.getRgb(203,79,15),this.curTable[24]=this.getRgb(139,115,0),this.curTable[25]=this.getRgb(0,151,0),this.curTable[26]=this.getRgb(0,171,0),this.curTable[27]=this.getRgb(0,147,59),this.curTable[28]=this.getRgb(0,131,139),this.curTable[29]=this.getRgb(0,0,0),this.curTable[30]=this.getRgb(0,0,0),this.curTable[31]=this.getRgb(0,0,0),this.curTable[32]=this.getRgb(255,255,255),this.curTable[33]=this.getRgb(63,191,255),this.curTable[34]=this.getRgb(95,151,255),this.curTable[35]=this.getRgb(167,139,253),this.curTable[36]=this.getRgb(247,123,255),this.curTable[37]=this.getRgb(255,119,183),this.curTable[38]=this.getRgb(255,119,99),this.curTable[39]=this.getRgb(255,155,59),this.curTable[40]=this.getRgb(243,191,63),this.curTable[41]=this.getRgb(131,211,19),this.curTable[42]=this.getRgb(79,223,75),this.curTable[43]=this.getRgb(88,248,152),this.curTable[44]=this.getRgb(0,235,219),this.curTable[45]=this.getRgb(0,0,0),this.curTable[46]=this.getRgb(0,0,0),this.curTable[47]=this.getRgb(0,0,0),this.curTable[48]=this.getRgb(255,255,255),this.curTable[49]=this.getRgb(171,231,255),this.curTable[50]=this.getRgb(199,215,255),this.curTable[51]=this.getRgb(215,203,255),this.curTable[52]=this.getRgb(255,199,255),this.curTable[53]=this.getRgb(255,199,219),this.curTable[54]=this.getRgb(255,191,179),this.curTable[55]=this.getRgb(255,219,171),this.curTable[56]=this.getRgb(255,231,163),this.curTable[57]=this.getRgb(227,255,163),this.curTable[58]=this.getRgb(171,243,191),this.curTable[59]=this.getRgb(179,255,207),this.curTable[60]=this.getRgb(159,255,243),this.curTable[61]=this.getRgb(0,0,0),this.curTable[62]=this.getRgb(0,0,0),this.curTable[63]=this.getRgb(0,0,0),this.makeTables(),this.setEmphasis(0)}},t.exports=r},function(t,s){var i=function(t){this.nes=t,this.square1=new r(this,!0),this.square2=new r(this,!1),this.triangle=new n(this),this.noise=new h(this),this.dmc=new e(this),this.frameIrqCounter=null,this.frameIrqCounterMax=4,this.initCounter=2048,this.channelEnableValue=null,this.sampleRate=44100,this.lengthLookup=null,this.dmcFreqLookup=null,this.noiseWavelengthLookup=null,this.square_table=null,this.tnd_table=null,this.frameIrqEnabled=!1,this.frameIrqActive=null,this.frameClockNow=null,this.startedPlaying=!1,this.recordOutput=!1,this.initingHardware=!1,this.masterFrameCounter=null,this.derivedFrameCounter=null,this.countSequence=null,this.sampleTimer=null,this.frameTime=null,this.sampleTimerMax=null,this.sampleCount=null,this.triValue=0,this.smpSquare1=null,this.smpSquare2=null,this.smpTriangle=null,this.smpDmc=null,this.accCount=null,this.prevSampleL=0,this.prevSampleR=0,this.smpAccumL=0,this.smpAccumR=0,this.dacRange=0,this.dcValue=0,this.masterVolume=256,this.stereoPosLSquare1=null,this.stereoPosLSquare2=null,this.stereoPosLTriangle=null,this.stereoPosLNoise=null,this.stereoPosLDMC=null,this.stereoPosRSquare1=null,this.stereoPosRSquare2=null,this.stereoPosRTriangle=null,this.stereoPosRNoise=null,this.stereoPosRDMC=null,this.extraCycles=null,this.maxSample=null,this.minSample=null,this.panning=[80,170,100,150,128],this.setPanning(this.panning),this.initLengthLookup(),this.initDmcFrequencyLookup(),this.initNoiseWavelengthLookup(),this.initDACtables();for(var s=0;s<20;s++)16===s?this.writeReg(16400,16):this.writeReg(16384+s,0);this.reset()};i.prototype={reset:function(){this.sampleRate=this.nes.opts.sampleRate,this.sampleTimerMax=Math.floor(1832727040*this.nes.opts.preferredFrameRate/(60*this.sampleRate)),this.frameTime=Math.floor(14915*this.nes.opts.preferredFrameRate/60),this.sampleTimer=0,this.updateChannelEnable(0),this.masterFrameCounter=0,this.derivedFrameCounter=0,this.countSequence=0,this.sampleCount=0,this.initCounter=2048,this.frameIrqEnabled=!1,this.initingHardware=!1,this.resetCounter(),this.square1.reset(),this.square2.reset(),this.triangle.reset(),this.noise.reset(),this.dmc.reset(),this.accCount=0,this.smpSquare1=0,this.smpSquare2=0,this.smpTriangle=0,this.smpDmc=0,this.frameIrqEnabled=!1,this.frameIrqCounterMax=4,this.channelEnableValue=255,this.startedPlaying=!1,this.prevSampleL=0,this.prevSampleR=0,this.smpAccumL=0,this.smpAccumR=0,this.maxSample=-5e5,this.minSample=5e5},readReg:function(t){var s=0;return s|=this.square1.getLengthStatus(),s|=this.square2.getLengthStatus()<<1,s|=this.triangle.getLengthStatus()<<2,s|=this.noise.getLengthStatus()<<3,s|=this.dmc.getLengthStatus()<<4,s|=(this.frameIrqActive&&this.frameIrqEnabled?1:0)<<6,s|=this.dmc.getIrqStatus()<<7,this.frameIrqActive=!1,this.dmc.irqGenerated=!1,65535&s},writeReg:function(t,s){t>=16384&&t<16388?this.square1.writeReg(t,s):t>=16388&&t<16392?this.square2.writeReg(t,s):t>=16392&&t<16396?this.triangle.writeReg(t,s):t>=16396&&t<=16399?this.noise.writeReg(t,s):16400===t?this.dmc.writeReg(t,s):16401===t?this.dmc.writeReg(t,s):16402===t?this.dmc.writeReg(t,s):16403===t?this.dmc.writeReg(t,s):16405===t?(this.updateChannelEnable(s),0!==s&&this.initCounter>0&&(this.initingHardware=!0),this.dmc.writeReg(t,s)):16407===t&&(this.countSequence=s>>7&1,this.masterFrameCounter=0,this.frameIrqActive=!1,this.frameIrqEnabled=0==(s>>6&1),0===this.countSequence?(this.frameIrqCounterMax=4,this.derivedFrameCounter=4):(this.frameIrqCounterMax=5,this.derivedFrameCounter=0,this.frameCounterTick()))},resetCounter:function(){0===this.countSequence?this.derivedFrameCounter=4:this.derivedFrameCounter=0},updateChannelEnable:function(t){this.channelEnableValue=65535&t,this.square1.setEnabled(0!=(1&t)),this.square2.setEnabled(0!=(2&t)),this.triangle.setEnabled(0!=(4&t)),this.noise.setEnabled(0!=(8&t)),this.dmc.setEnabled(0!=(16&t))},clockFrameCounter:function(t){if(this.initCounter>0&&this.initingHardware)return this.initCounter-=t,void(this.initCounter<=0&&(this.initingHardware=!1));t+=this.extraCycles;var s=this.sampleTimerMax-this.sampleTimer;t<<10>s?(this.extraCycles=(t<<10)-s>>10,t-=this.extraCycles):this.extraCycles=0;var i=this.dmc,e=this.triangle,h=this.square1,r=this.square2,n=this.noise;if(i.isEnabled)for(i.shiftCounter-=t<<3;i.shiftCounter<=0&&i.dmaFrequency>0;)i.shiftCounter+=i.dmaFrequency,i.clockDmc();if(e.progTimerMax>0)for(e.progTimerCount-=t;e.progTimerCount<=0;)e.progTimerCount+=e.progTimerMax+1,e.linearCounter>0&&e.lengthCounter>0&&(e.triangleCounter++,e.triangleCounter&=31,e.isEnabled&&(e.triangleCounter>=16?e.sampleValue=15&e.triangleCounter:e.sampleValue=15-(15&e.triangleCounter),e.sampleValue<<=4));h.progTimerCount-=t,h.progTimerCount<=0&&(h.progTimerCount+=h.progTimerMax+1<<1,h.squareCounter++,h.squareCounter&=7,h.updateSampleValue()),r.progTimerCount-=t,r.progTimerCount<=0&&(r.progTimerCount+=r.progTimerMax+1<<1,r.squareCounter++,r.squareCounter&=7,r.updateSampleValue());var a=t;if(n.progTimerCount-a>0)n.progTimerCount-=a,n.accCount+=a,n.accValue+=a*n.sampleValue;else for(;a-- >0;)--n.progTimerCount<=0&&n.progTimerMax>0&&(n.shiftReg<<=1,n.tmp=32768&(n.shiftReg<<(0===n.randomMode?1:6)^n.shiftReg),0!==n.tmp?(n.shiftReg|=1,n.randomBit=0,n.sampleValue=0):(n.randomBit=1,n.isEnabled&&n.lengthCounter>0?n.sampleValue=n.masterVolume:n.sampleValue=0),n.progTimerCount+=n.progTimerMax),n.accValue+=n.sampleValue,n.accCount++;this.frameIrqEnabled&&this.frameIrqActive&&this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NORMAL),this.masterFrameCounter+=t<<1,this.masterFrameCounter>=this.frameTime&&(this.masterFrameCounter-=this.frameTime,this.frameCounterTick()),this.accSample(t),this.sampleTimer+=t<<10,this.sampleTimer>=this.sampleTimerMax&&(this.sample(),this.sampleTimer-=this.sampleTimerMax)},accSample:function(t){this.triangle.sampleCondition&&(this.triValue=Math.floor((this.triangle.progTimerCount<<4)/(this.triangle.progTimerMax+1)),this.triValue>16&&(this.triValue=16),this.triangle.triangleCounter>=16&&(this.triValue=16-this.triValue),this.triValue+=this.triangle.sampleValue),2===t?(this.smpTriangle+=this.triValue<<1,this.smpDmc+=this.dmc.sample<<1,this.smpSquare1+=this.square1.sampleValue<<1,this.smpSquare2+=this.square2.sampleValue<<1,this.accCount+=2):4===t?(this.smpTriangle+=this.triValue<<2,this.smpDmc+=this.dmc.sample<<2,this.smpSquare1+=this.square1.sampleValue<<2,this.smpSquare2+=this.square2.sampleValue<<2,this.accCount+=4):(this.smpTriangle+=t*this.triValue,this.smpDmc+=t*this.dmc.sample,this.smpSquare1+=t*this.square1.sampleValue,this.smpSquare2+=t*this.square2.sampleValue,this.accCount+=t)},frameCounterTick:function(){this.derivedFrameCounter++,this.derivedFrameCounter>=this.frameIrqCounterMax&&(this.derivedFrameCounter=0),1!==this.derivedFrameCounter&&3!==this.derivedFrameCounter||(this.triangle.clockLengthCounter(),this.square1.clockLengthCounter(),this.square2.clockLengthCounter(),this.noise.clockLengthCounter(),this.square1.clockSweep(),this.square2.clockSweep()),this.derivedFrameCounter>=0&&this.derivedFrameCounter<4&&(this.square1.clockEnvDecay(),this.square2.clockEnvDecay(),this.noise.clockEnvDecay(),this.triangle.clockLinearCounter()),3===this.derivedFrameCounter&&0===this.countSequence&&(this.frameIrqActive=!0)},sample:function(){var t,s;this.accCount>0?(this.smpSquare1<<=4,this.smpSquare1=Math.floor(this.smpSquare1/this.accCount),this.smpSquare2<<=4,this.smpSquare2=Math.floor(this.smpSquare2/this.accCount),this.smpTriangle=Math.floor(this.smpTriangle/this.accCount),this.smpDmc<<=4,this.smpDmc=Math.floor(this.smpDmc/this.accCount),this.accCount=0):(this.smpSquare1=this.square1.sampleValue<<4,this.smpSquare2=this.square2.sampleValue<<4,this.smpTriangle=this.triangle.sampleValue,this.smpDmc=this.dmc.sample<<4);var i=Math.floor((this.noise.accValue<<4)/this.noise.accCount);this.noise.accValue=i>>4,this.noise.accCount=1,t=this.smpSquare1*this.stereoPosLSquare1+this.smpSquare2*this.stereoPosLSquare2>>8,s=3*this.smpTriangle*this.stereoPosLTriangle+(i<<1)*this.stereoPosLNoise+this.smpDmc*this.stereoPosLDMC>>8,t>=this.square_table.length&&(t=this.square_table.length-1),s>=this.tnd_table.length&&(s=this.tnd_table.length-1);var e=this.square_table[t]+this.tnd_table[s]-this.dcValue;t=this.smpSquare1*this.stereoPosRSquare1+this.smpSquare2*this.stereoPosRSquare2>>8,s=3*this.smpTriangle*this.stereoPosRTriangle+(i<<1)*this.stereoPosRNoise+this.smpDmc*this.stereoPosRDMC>>8,t>=this.square_table.length&&(t=this.square_table.length-1),s>=this.tnd_table.length&&(s=this.tnd_table.length-1);var h=this.square_table[t]+this.tnd_table[s]-this.dcValue,r=e-this.prevSampleL;this.prevSampleL+=r,this.smpAccumL+=r-(this.smpAccumL>>10),e=this.smpAccumL;var n=h-this.prevSampleR;this.prevSampleR+=n,this.smpAccumR+=n-(this.smpAccumR>>10),h=this.smpAccumR,e>this.maxSample&&(this.maxSample=e),e>3]},getDmcFrequency:function(t){return t>=0&&t<16?this.dmcFreqLookup[t]:0},getNoiseWaveLength:function(t){return t>=0&&t<16?this.noiseWavelengthLookup[t]:0},setPanning:function(t){for(var s=0;s<5;s++)this.panning[s]=t[s];this.updateStereoPos()},setMasterVolume:function(t){t<0&&(t=0),t>256&&(t=256),this.masterVolume=t,this.updateStereoPos()},updateStereoPos:function(){this.stereoPosLSquare1=this.panning[0]*this.masterVolume>>8,this.stereoPosLSquare2=this.panning[1]*this.masterVolume>>8,this.stereoPosLTriangle=this.panning[2]*this.masterVolume>>8,this.stereoPosLNoise=this.panning[3]*this.masterVolume>>8,this.stereoPosLDMC=this.panning[4]*this.masterVolume>>8,this.stereoPosRSquare1=this.masterVolume-this.stereoPosLSquare1,this.stereoPosRSquare2=this.masterVolume-this.stereoPosLSquare2,this.stereoPosRTriangle=this.masterVolume-this.stereoPosLTriangle,this.stereoPosRNoise=this.masterVolume-this.stereoPosLNoise,this.stereoPosRDMC=this.masterVolume-this.stereoPosLDMC},initLengthLookup:function(){this.lengthLookup=[10,254,20,2,40,4,80,6,160,8,60,10,14,12,26,14,12,16,24,18,48,20,96,22,192,24,72,26,16,28,32,30]},initDmcFrequencyLookup:function(){this.dmcFreqLookup=new Array(16),this.dmcFreqLookup[0]=3424,this.dmcFreqLookup[1]=3040,this.dmcFreqLookup[2]=2720,this.dmcFreqLookup[3]=2560,this.dmcFreqLookup[4]=2288,this.dmcFreqLookup[5]=2032,this.dmcFreqLookup[6]=1808,this.dmcFreqLookup[7]=1712,this.dmcFreqLookup[8]=1520,this.dmcFreqLookup[9]=1280,this.dmcFreqLookup[10]=1136,this.dmcFreqLookup[11]=1024,this.dmcFreqLookup[12]=848,this.dmcFreqLookup[13]=672,this.dmcFreqLookup[14]=576,this.dmcFreqLookup[15]=432},initNoiseWavelengthLookup:function(){this.noiseWavelengthLookup=new Array(16),this.noiseWavelengthLookup[0]=4,this.noiseWavelengthLookup[1]=8,this.noiseWavelengthLookup[2]=16,this.noiseWavelengthLookup[3]=32,this.noiseWavelengthLookup[4]=64,this.noiseWavelengthLookup[5]=96,this.noiseWavelengthLookup[6]=128,this.noiseWavelengthLookup[7]=160,this.noiseWavelengthLookup[8]=202,this.noiseWavelengthLookup[9]=254,this.noiseWavelengthLookup[10]=380,this.noiseWavelengthLookup[11]=508,this.noiseWavelengthLookup[12]=762,this.noiseWavelengthLookup[13]=1016,this.noiseWavelengthLookup[14]=2034,this.noiseWavelengthLookup[15]=4068},initDACtables:function(){var t,s,i,e=0,h=0;for(this.square_table=new Array(512),this.tnd_table=new Array(3264),i=0;i<512;i++)t=95.52/(8128/(i/16)+100),t*=.98411,t*=5e4,s=Math.floor(t),this.square_table[i]=s,s>e&&(e=s);for(i=0;i<3264;i++)t=163.67/(24329/(i/16)+100),t*=.98411,t*=5e4,s=Math.floor(t),this.tnd_table[i]=s,s>h&&(h=s);this.dacRange=e+h,this.dcValue=this.dacRange/2}};var e=function(t){this.papu=t,this.MODE_NORMAL=0,this.MODE_LOOP=1,this.MODE_IRQ=2,this.isEnabled=null,this.hasSample=null,this.irqGenerated=!1,this.playMode=null,this.dmaFrequency=null,this.dmaCounter=null,this.deltaCounter=null,this.playStartAddress=null,this.playAddress=null,this.playLength=null,this.playLengthCounter=null,this.shiftCounter=null,this.reg4012=null,this.reg4013=null,this.sample=null,this.dacLsb=null,this.data=null,this.reset()};e.prototype={clockDmc:function(){this.hasSample&&(0==(1&this.data)?this.deltaCounter>0&&this.deltaCounter--:this.deltaCounter<63&&this.deltaCounter++,this.sample=this.isEnabled?(this.deltaCounter<<1)+this.dacLsb:0,this.data>>=1),this.dmaCounter--,this.dmaCounter<=0&&(this.hasSample=!1,this.endOfSample(),this.dmaCounter=8),this.irqGenerated&&this.papu.nes.cpu.requestIrq(this.papu.nes.cpu.IRQ_NORMAL)},endOfSample:function(){0===this.playLengthCounter&&this.playMode===this.MODE_LOOP&&(this.playAddress=this.playStartAddress,this.playLengthCounter=this.playLength),this.playLengthCounter>0&&(this.nextSample(),0===this.playLengthCounter&&this.playMode===this.MODE_IRQ&&(this.irqGenerated=!0))},nextSample:function(){this.data=this.papu.nes.mmap.load(this.playAddress),this.papu.nes.cpu.haltCycles(4),this.playLengthCounter--,this.playAddress++,this.playAddress>65535&&(this.playAddress=32768),this.hasSample=!0},writeReg:function(t,s){16400===t?(s>>6==0?this.playMode=this.MODE_NORMAL:1==(s>>6&1)?this.playMode=this.MODE_LOOP:s>>6==2&&(this.playMode=this.MODE_IRQ),0==(128&s)&&(this.irqGenerated=!1),this.dmaFrequency=this.papu.getDmcFrequency(15&s)):16401===t?(this.deltaCounter=s>>1&63,this.dacLsb=1&s,this.sample=(this.deltaCounter<<1)+this.dacLsb):16402===t?(this.playStartAddress=s<<6|49152,this.playAddress=this.playStartAddress,this.reg4012=s):16403===t?(this.playLength=1+(s<<4),this.playLengthCounter=this.playLength,this.reg4013=s):16405===t&&(0==(s>>4&1)?this.playLengthCounter=0:(this.playAddress=this.playStartAddress,this.playLengthCounter=this.playLength),this.irqGenerated=!1)},setEnabled:function(t){!this.isEnabled&&t&&(this.playLengthCounter=this.playLength),this.isEnabled=t},getLengthStatus:function(){return 0!==this.playLengthCounter&&this.isEnabled?1:0},getIrqStatus:function(){return this.irqGenerated?1:0},reset:function(){this.isEnabled=!1,this.irqGenerated=!1,this.playMode=this.MODE_NORMAL,this.dmaFrequency=0,this.dmaCounter=0,this.deltaCounter=0,this.playStartAddress=0,this.playAddress=0,this.playLength=0,this.playLengthCounter=0,this.sample=0,this.dacLsb=0,this.shiftCounter=0,this.reg4012=0,this.reg4013=0,this.data=0}};var h=function(t){this.papu=t,this.isEnabled=null,this.envDecayDisable=null,this.envDecayLoopEnable=null,this.lengthCounterEnable=null,this.envReset=null,this.shiftNow=null,this.lengthCounter=null,this.progTimerCount=null,this.progTimerMax=null,this.envDecayRate=null,this.envDecayCounter=null,this.envVolume=null,this.masterVolume=null,this.shiftReg=16384,this.randomBit=null,this.randomMode=null,this.sampleValue=null,this.accValue=0,this.accCount=1,this.tmp=null,this.reset()};h.prototype={reset:function(){this.progTimerCount=0,this.progTimerMax=0,this.isEnabled=!1,this.lengthCounter=0,this.lengthCounterEnable=!1,this.envDecayDisable=!1,this.envDecayLoopEnable=!1,this.shiftNow=!1,this.envDecayRate=0,this.envDecayCounter=0,this.envVolume=0,this.masterVolume=0,this.shiftReg=1,this.randomBit=0,this.randomMode=0,this.sampleValue=0,this.tmp=0},clockLengthCounter:function(){this.lengthCounterEnable&&this.lengthCounter>0&&0===--this.lengthCounter&&this.updateSampleValue()},clockEnvDecay:function(){this.envReset?(this.envReset=!1,this.envDecayCounter=this.envDecayRate+1,this.envVolume=15):--this.envDecayCounter<=0&&(this.envDecayCounter=this.envDecayRate+1,this.envVolume>0?this.envVolume--:this.envVolume=this.envDecayLoopEnable?15:0),this.envDecayDisable?this.masterVolume=this.envDecayRate:this.masterVolume=this.envVolume,this.updateSampleValue()},updateSampleValue:function(){this.isEnabled&&this.lengthCounter>0&&(this.sampleValue=this.randomBit*this.masterVolume)},writeReg:function(t,s){16396===t?(this.envDecayDisable=0!=(16&s),this.envDecayRate=15&s,this.envDecayLoopEnable=0!=(32&s),this.lengthCounterEnable=0==(32&s),this.envDecayDisable?this.masterVolume=this.envDecayRate:this.masterVolume=this.envVolume):16398===t?(this.progTimerMax=this.papu.getNoiseWaveLength(15&s),this.randomMode=s>>7):16399===t&&(this.lengthCounter=this.papu.getLengthMax(248&s),this.envReset=!0)},setEnabled:function(t){this.isEnabled=t,t||(this.lengthCounter=0),this.updateSampleValue()},getLengthStatus:function(){return 0!==this.lengthCounter&&this.isEnabled?1:0}};var r=function(t,s){this.papu=t,this.dutyLookup=[0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,1,1,1,1,1],this.impLookup=[1,-1,0,0,0,0,0,0,1,0,-1,0,0,0,0,0,1,0,0,0,-1,0,0,0,-1,0,1,0,0,0,0,0],this.sqr1=s,this.isEnabled=null,this.lengthCounterEnable=null,this.sweepActive=null,this.envDecayDisable=null,this.envDecayLoopEnable=null,this.envReset=null,this.sweepCarry=null,this.updateSweepPeriod=null,this.progTimerCount=null,this.progTimerMax=null,this.lengthCounter=null,this.squareCounter=null,this.sweepCounter=null,this.sweepCounterMax=null,this.sweepMode=null,this.sweepShiftAmount=null,this.envDecayRate=null,this.envDecayCounter=null,this.envVolume=null,this.masterVolume=null,this.dutyMode=null,this.sweepResult=null,this.sampleValue=null,this.vol=null,this.reset()};r.prototype={reset:function(){this.progTimerCount=0,this.progTimerMax=0,this.lengthCounter=0,this.squareCounter=0,this.sweepCounter=0,this.sweepCounterMax=0,this.sweepMode=0,this.sweepShiftAmount=0,this.envDecayRate=0,this.envDecayCounter=0,this.envVolume=0,this.masterVolume=0,this.dutyMode=0,this.vol=0,this.isEnabled=!1,this.lengthCounterEnable=!1,this.sweepActive=!1,this.sweepCarry=!1,this.envDecayDisable=!1,this.envDecayLoopEnable=!1},clockLengthCounter:function(){this.lengthCounterEnable&&this.lengthCounter>0&&0===--this.lengthCounter&&this.updateSampleValue()},clockEnvDecay:function(){this.envReset?(this.envReset=!1,this.envDecayCounter=this.envDecayRate+1,this.envVolume=15):--this.envDecayCounter<=0&&(this.envDecayCounter=this.envDecayRate+1,this.envVolume>0?this.envVolume--:this.envVolume=this.envDecayLoopEnable?15:0),this.envDecayDisable?this.masterVolume=this.envDecayRate:this.masterVolume=this.envVolume,this.updateSampleValue()},clockSweep:function(){--this.sweepCounter<=0&&(this.sweepCounter=this.sweepCounterMax+1,this.sweepActive&&this.sweepShiftAmount>0&&this.progTimerMax>7&&(this.sweepCarry=!1,0===this.sweepMode?(this.progTimerMax+=this.progTimerMax>>this.sweepShiftAmount,this.progTimerMax>4095&&(this.progTimerMax=4095,this.sweepCarry=!0)):this.progTimerMax=this.progTimerMax-((this.progTimerMax>>this.sweepShiftAmount)-(this.sqr1?1:0)))),this.updateSweepPeriod&&(this.updateSweepPeriod=!1,this.sweepCounter=this.sweepCounterMax+1)},updateSampleValue:function(){this.isEnabled&&this.lengthCounter>0&&this.progTimerMax>7?0===this.sweepMode&&this.progTimerMax+(this.progTimerMax>>this.sweepShiftAmount)>4095?this.sampleValue=0:this.sampleValue=this.masterVolume*this.dutyLookup[(this.dutyMode<<3)+this.squareCounter]:this.sampleValue=0},writeReg:function(t,s){var i=this.sqr1?0:4;t===16384+i?(this.envDecayDisable=0!=(16&s),this.envDecayRate=15&s,this.envDecayLoopEnable=0!=(32&s),this.dutyMode=s>>6&3,this.lengthCounterEnable=0==(32&s),this.envDecayDisable?this.masterVolume=this.envDecayRate:this.masterVolume=this.envVolume,this.updateSampleValue()):t===16385+i?(this.sweepActive=0!=(128&s),this.sweepCounterMax=s>>4&7,this.sweepMode=s>>3&1,this.sweepShiftAmount=7&s,this.updateSweepPeriod=!0):t===16386+i?(this.progTimerMax&=1792,this.progTimerMax|=s):t===16387+i&&(this.progTimerMax&=255,this.progTimerMax|=(7&s)<<8,this.isEnabled&&(this.lengthCounter=this.papu.getLengthMax(248&s)),this.envReset=!0)},setEnabled:function(t){this.isEnabled=t,t||(this.lengthCounter=0),this.updateSampleValue()},getLengthStatus:function(){return 0!==this.lengthCounter&&this.isEnabled?1:0}};var n=function(t){this.papu=t,this.isEnabled=null,this.sampleCondition=null,this.lengthCounterEnable=null,this.lcHalt=null,this.lcControl=null,this.progTimerCount=null,this.progTimerMax=null,this.triangleCounter=null,this.lengthCounter=null,this.linearCounter=null,this.lcLoadValue=null,this.sampleValue=null,this.tmp=null,this.reset()};n.prototype={reset:function(){this.progTimerCount=0,this.progTimerMax=0,this.triangleCounter=0,this.isEnabled=!1,this.sampleCondition=!1,this.lengthCounter=0,this.lengthCounterEnable=!1,this.linearCounter=0,this.lcLoadValue=0,this.lcHalt=!0,this.lcControl=!1,this.tmp=0,this.sampleValue=15},clockLengthCounter:function(){this.lengthCounterEnable&&this.lengthCounter>0&&0===--this.lengthCounter&&this.updateSampleCondition()},clockLinearCounter:function(){this.lcHalt?(this.linearCounter=this.lcLoadValue,this.updateSampleCondition()):this.linearCounter>0&&(this.linearCounter--,this.updateSampleCondition()),this.lcControl||(this.lcHalt=!1)},getLengthStatus:function(){return 0!==this.lengthCounter&&this.isEnabled?1:0},readReg:function(t){return 0},writeReg:function(t,s){16392===t?(this.lcControl=0!=(128&s),this.lcLoadValue=127&s,this.lengthCounterEnable=!this.lcControl):16394===t?(this.progTimerMax&=1792,this.progTimerMax|=s):16395===t&&(this.progTimerMax&=255,this.progTimerMax|=(7&s)<<8,this.lengthCounter=this.papu.getLengthMax(248&s),this.lcHalt=!0),this.updateSampleCondition()},clockProgrammableTimer:function(t){if(this.progTimerMax>0)for(this.progTimerCount+=t;this.progTimerMax>0&&this.progTimerCount>=this.progTimerMax;)this.progTimerCount-=this.progTimerMax,this.isEnabled&&this.lengthCounter>0&&this.linearCounter>0&&this.clockTriangleGenerator()},clockTriangleGenerator:function(){this.triangleCounter++,this.triangleCounter&=31},setEnabled:function(t){this.isEnabled=t,t||(this.lengthCounter=0),this.updateSampleCondition()},updateSampleCondition:function(){this.sampleCondition=this.isEnabled&&this.progTimerMax>7&&this.linearCounter>0&&this.lengthCounter>0}},t.exports=i},function(t,s,i){var e=i(9),h=i(2),r=function(t){this.nes=t,this.mapperName=new Array(92);for(var s=0;s<92;s++)this.mapperName[s]="Unknown Mapper";this.mapperName[0]="Direct Access",this.mapperName[1]="Nintendo MMC1",this.mapperName[2]="UNROM",this.mapperName[3]="CNROM",this.mapperName[4]="Nintendo MMC3",this.mapperName[5]="Nintendo MMC5",this.mapperName[6]="FFE F4xxx",this.mapperName[7]="AOROM",this.mapperName[8]="FFE F3xxx",this.mapperName[9]="Nintendo MMC2",this.mapperName[10]="Nintendo MMC4",this.mapperName[11]="Color Dreams Chip",this.mapperName[12]="FFE F6xxx",this.mapperName[15]="100-in-1 switch",this.mapperName[16]="Bandai chip",this.mapperName[17]="FFE F8xxx",this.mapperName[18]="Jaleco SS8806 chip",this.mapperName[19]="Namcot 106 chip",this.mapperName[20]="Famicom Disk System",this.mapperName[21]="Konami VRC4a",this.mapperName[22]="Konami VRC2a",this.mapperName[23]="Konami VRC2a",this.mapperName[24]="Konami VRC6",this.mapperName[25]="Konami VRC4b",this.mapperName[32]="Irem G-101 chip",this.mapperName[33]="Taito TC0190/TC0350",this.mapperName[34]="32kB ROM switch",this.mapperName[64]="Tengen RAMBO-1 chip",this.mapperName[65]="Irem H-3001 chip",this.mapperName[66]="GNROM switch",this.mapperName[67]="SunSoft3 chip",this.mapperName[68]="SunSoft4 chip",this.mapperName[69]="SunSoft5 FME-7 chip",this.mapperName[71]="Camerica chip",this.mapperName[78]="Irem 74HC161/32-based",this.mapperName[91]="Pirate HK-SF3 chip"};r.prototype={VERTICAL_MIRRORING:0,HORIZONTAL_MIRRORING:1,FOURSCREEN_MIRRORING:2,SINGLESCREEN_MIRRORING:3,SINGLESCREEN_MIRRORING2:4,SINGLESCREEN_MIRRORING3:5,SINGLESCREEN_MIRRORING4:6,CHRROM_MIRRORING:7,header:null,rom:null,vrom:null,vromTile:null,romCount:null,vromCount:null,mirroring:null,batteryRam:null,trainer:null,fourScreen:null,mapperType:null,valid:!1,load:function(t){var s,i,e;if(-1===t.indexOf("NES"))throw new Error("Not a valid NES ROM.");for(this.header=new Array(16),s=0;s<16;s++)this.header[s]=255&t.charCodeAt(s);this.romCount=this.header[4],this.vromCount=2*this.header[5],this.mirroring=0!=(1&this.header[6])?1:0,this.batteryRam=0!=(2&this.header[6]),this.trainer=0!=(4&this.header[6]),this.fourScreen=0!=(8&this.header[6]),this.mapperType=this.header[6]>>4|240&this.header[7];var r=!1;for(s=8;s<16;s++)if(0!==this.header[s]){r=!0;break}r&&(this.mapperType&=15),this.rom=new Array(this.romCount);var n=16;for(s=0;s=t.length);i++)this.rom[s][i]=255&t.charCodeAt(n+i);n+=16384}for(this.vrom=new Array(this.vromCount),s=0;s=t.length);i++)this.vrom[s][i]=255&t.charCodeAt(n+i);n+=4096}for(this.vromTile=new Array(this.vromCount),s=0;s>4,o=s%16,o<8?this.vromTile[e][a].setScanline(o,this.vrom[e][s],this.vrom[e][s+8]):this.vromTile[e][a].setScanline(o-8,this.vrom[e][s-8],this.vrom[e][s]);this.valid=!0},getMirroringType:function(){return this.fourScreen?this.FOURSCREEN_MIRRORING:0===this.mirroring?this.HORIZONTAL_MIRRORING:this.VERTICAL_MIRRORING},getMapperName:function(){return this.mapperType>=0&&this.mapperType16407?(this.nes.cpu.mem[t]=s,t>=24576&&t<32768&&this.nes.opts.onBatteryRamWrite(t,s)):t>8199&&t<16384?this.regWrite(8192+(7&t),s):this.regWrite(t,s)},writelow:function(t,s){t<8192?this.nes.cpu.mem[2047&t]=s:t>16407?this.nes.cpu.mem[t]=s:t>8199&&t<16384?this.regWrite(8192+(7&t),s):this.regWrite(t,s)},load:function(t){return t&=65535,t>16407?this.nes.cpu.mem[t]:t>=8192?this.regLoad(t):this.nes.cpu.mem[2047&t]},regLoad:function(t){switch(t>>12){case 0:case 1:break;case 2:case 3:switch(7&t){case 0:return this.nes.cpu.mem[8192];case 1:return this.nes.cpu.mem[8193];case 2:return this.nes.ppu.readStatusRegister();case 3:return 0;case 4:return this.nes.ppu.sramLoad();case 5:case 6:return 0;case 7:return this.nes.ppu.vramLoad()}break;case 4:switch(t-16405){case 0:return this.nes.papu.readReg(t);case 1:return this.joy1Read();case 2:var s;return s=null!==this.zapperX&&null!==this.zapperY&&this.nes.ppu.isPixelWhite(this.zapperX,this.zapperY)?0:8,this.zapperFired&&(s|=16),65535&(this.joy2Read()|s)}}return 0},regWrite:function(t,s){switch(t){case 8192:this.nes.cpu.mem[t]=s,this.nes.ppu.updateControlReg1(s);break;case 8193:this.nes.cpu.mem[t]=s,this.nes.ppu.updateControlReg2(s);break;case 8195:this.nes.ppu.writeSRAMAddress(s);break;case 8196:this.nes.ppu.sramWrite(s);break;case 8197:this.nes.ppu.scrollWrite(s);break;case 8198:this.nes.ppu.writeVRAMAddress(s);break;case 8199:this.nes.ppu.vramWrite(s);break;case 16404:this.nes.ppu.sramDMA(s);break;case 16405:this.nes.papu.writeReg(t,s);break;case 16406:0==(1&s)&&1==(1&this.joypadLastWrite)&&(this.joy1StrobeState=0,this.joy2StrobeState=0),this.joypadLastWrite=s;break;case 16407:this.nes.papu.writeReg(t,s);break;default:t>=16384&&t<=16407&&this.nes.papu.writeReg(t,s)}},joy1Read:function(){var t;switch(this.joy1StrobeState){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:t=this.nes.controllers[1].state[this.joy1StrobeState];break;case 8:case 9:case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:t=0;break;case 19:t=1;break;default:t=0}return this.joy1StrobeState++,24===this.joy1StrobeState&&(this.joy1StrobeState=0),t},joy2Read:function(){var t;switch(this.joy2StrobeState){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:t=this.nes.controllers[2].state[this.joy2StrobeState];break;case 8:case 9:case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:t=0;break;case 19:t=1;break;default:t=0}return this.joy2StrobeState++,24===this.joy2StrobeState&&(this.joy2StrobeState=0),t},loadROM:function(){if(!this.nes.rom.valid||this.nes.rom.romCount<1)throw new Error("NoMapper: Invalid ROM! Unable to load.");this.loadPRGROM(),this.loadCHRROM(),this.loadBatteryRam(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},loadPRGROM:function(){this.nes.rom.romCount>1?(this.loadRomBank(0,32768),this.loadRomBank(1,49152)):(this.loadRomBank(0,32768),this.loadRomBank(0,49152))},loadCHRROM:function(){this.nes.rom.vromCount>0&&(1===this.nes.rom.vromCount?(this.loadVromBank(0,0),this.loadVromBank(0,4096)):(this.loadVromBank(0,0),this.loadVromBank(1,4096)))},loadBatteryRam:function(){if(this.nes.rom.batteryRam){var t=this.nes.rom.batteryRam;null!==t&&8192===t.length&&e.copyArrayElements(t,0,this.nes.cpu.mem,24576,8192)}},loadRomBank:function(t,s){t%=this.nes.rom.romCount,e.copyArrayElements(this.nes.rom.rom[t],0,this.nes.cpu.mem,s,16384)},loadVromBank:function(t,s){if(0!==this.nes.rom.vromCount){this.nes.ppu.triggerRendering(),e.copyArrayElements(this.nes.rom.vrom[t%this.nes.rom.vromCount],0,this.nes.ppu.vramMem,s,4096);var i=this.nes.rom.vromTile[t%this.nes.rom.vromCount];e.copyArrayElements(i,0,this.nes.ppu.ptTile,s>>4,256)}},load32kRomBank:function(t,s){this.loadRomBank(2*t%this.nes.rom.romCount,s),this.loadRomBank((2*t+1)%this.nes.rom.romCount,s+16384)},load8kVromBank:function(t,s){0!==this.nes.rom.vromCount&&(this.nes.ppu.triggerRendering(),this.loadVromBank(t%this.nes.rom.vromCount,s),this.loadVromBank((t+1)%this.nes.rom.vromCount,s+4096))},load1kVromBank:function(t,s){if(0!==this.nes.rom.vromCount){this.nes.ppu.triggerRendering();var i=Math.floor(t/4)%this.nes.rom.vromCount,h=t%4*1024;e.copyArrayElements(this.nes.rom.vrom[i],h,this.nes.ppu.vramMem,s,1024);for(var r=this.nes.rom.vromTile[i],n=s>>4,a=0;a<64;a++)this.nes.ppu.ptTile[n+a]=r[(t%4<<6)+a]}},load2kVromBank:function(t,s){if(0!==this.nes.rom.vromCount){this.nes.ppu.triggerRendering();var i=Math.floor(t/2)%this.nes.rom.vromCount,h=t%2*2048;e.copyArrayElements(this.nes.rom.vrom[i],h,this.nes.ppu.vramMem,s,2048);for(var r=this.nes.rom.vromTile[i],n=s>>4,a=0;a<128;a++)this.nes.ppu.ptTile[n+a]=r[(t%2<<7)+a]}},load8kRomBank:function(t,s){var i=Math.floor(t/2)%this.nes.rom.romCount,h=t%2*8192;e.copyArrayElements(this.nes.rom.rom[i],h,this.nes.cpu.mem,s,8192)},clockIrqCounter:function(){},latchAccess:function(t){},toJSON:function(){return{joy1StrobeState:this.joy1StrobeState,joy2StrobeState:this.joy2StrobeState,joypadLastWrite:this.joypadLastWrite}},fromJSON:function(t){this.joy1StrobeState=t.joy1StrobeState,this.joy2StrobeState=t.joy2StrobeState,this.joypadLastWrite=t.joypadLastWrite}},h[1]=function(t){this.nes=t},h[1].prototype=new h[0],h[1].prototype.reset=function(){h[0].prototype.reset.apply(this),this.regBuffer=0,this.regBufferCounter=0,this.mirroring=0,this.oneScreenMirroring=0,this.prgSwitchingArea=1,this.prgSwitchingSize=1,this.vromSwitchingSize=0,this.romSelectionReg0=0,this.romSelectionReg1=0,this.romBankSelect=0},h[1].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);0!=(128&s)?(this.regBufferCounter=0,this.regBuffer=0,0===this.getRegNumber(t)&&(this.prgSwitchingArea=1,this.prgSwitchingSize=1)):(this.regBuffer=this.regBuffer&255-(1<>2&1,this.prgSwitchingSize=s>>3&1,this.vromSwitchingSize=s>>4&1;break;case 1:this.romSelectionReg0=s>>4&1,this.nes.rom.vromCount>0&&(0===this.vromSwitchingSize?0===this.romSelectionReg0?this.load8kVromBank(15&s,0):this.load8kVromBank(Math.floor(this.nes.rom.vromCount/2)+(15&s),0):0===this.romSelectionReg0?this.loadVromBank(15&s,0):this.loadVromBank(Math.floor(this.nes.rom.vromCount/2)+(15&s),0));break;case 2:this.romSelectionReg1=s>>4&1,this.nes.rom.vromCount>0&&1===this.vromSwitchingSize&&(0===this.romSelectionReg1?this.loadVromBank(15&s,4096):this.loadVromBank(Math.floor(this.nes.rom.vromCount/2)+(15&s),4096));break;default:i=15&s;var e,h=0;this.nes.rom.romCount>=32?0===this.vromSwitchingSize?1===this.romSelectionReg0&&(h=16):h=(this.romSelectionReg0|this.romSelectionReg1<<1)<<3:this.nes.rom.romCount>=16&&1===this.romSelectionReg0&&(h=8),0===this.prgSwitchingSize?(e=h+(15&s),this.load32kRomBank(e,32768)):(e=2*h+(15&s),0===this.prgSwitchingArea?this.loadRomBank(e,49152):this.loadRomBank(e,32768))}},h[1].prototype.getRegNumber=function(t){return t>=32768&&t<=40959?0:t>=40960&&t<=49151?1:t>=49152&&t<=57343?2:3},h[1].prototype.loadROM=function(){if(!this.nes.rom.valid)throw new Error("MMC1: Invalid ROM! Unable to load.");this.loadRomBank(0,32768),this.loadRomBank(this.nes.rom.romCount-1,49152),this.loadCHRROM(),this.loadBatteryRam(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},h[1].prototype.switchLowHighPrgRom=function(t){},h[1].prototype.switch16to32=function(){},h[1].prototype.switch32to16=function(){},h[1].prototype.toJSON=function(){var t=h[0].prototype.toJSON.apply(this);return t.mirroring=this.mirroring,t.oneScreenMirroring=this.oneScreenMirroring,t.prgSwitchingArea=this.prgSwitchingArea,t.prgSwitchingSize=this.prgSwitchingSize,t.vromSwitchingSize=this.vromSwitchingSize,t.romSelectionReg0=this.romSelectionReg0,t.romSelectionReg1=this.romSelectionReg1,t.romBankSelect=this.romBankSelect,t.regBuffer=this.regBuffer,t.regBufferCounter=this.regBufferCounter,t},h[1].prototype.fromJSON=function(t){h[0].prototype.fromJSON.apply(this,arguments),this.mirroring=t.mirroring,this.oneScreenMirroring=t.oneScreenMirroring,this.prgSwitchingArea=t.prgSwitchingArea,this.prgSwitchingSize=t.prgSwitchingSize,this.vromSwitchingSize=t.vromSwitchingSize,this.romSelectionReg0=t.romSelectionReg0,this.romSelectionReg1=t.romSelectionReg1,this.romBankSelect=t.romBankSelect,this.regBuffer=t.regBuffer,this.regBufferCounter=t.regBufferCounter},h[2]=function(t){this.nes=t},h[2].prototype=new h[0],h[2].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);this.loadRomBank(s,32768)},h[2].prototype.loadROM=function(){if(!this.nes.rom.valid)throw new Error("UNROM: Invalid ROM! Unable to load.");this.loadRomBank(0,32768),this.loadRomBank(this.nes.rom.romCount-1,49152),this.loadCHRROM(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},h[3]=function(t){this.nes=t},h[3].prototype=new h[0],h[3].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);var i=s%(this.nes.rom.vromCount/2)*2;this.loadVromBank(i,0),this.loadVromBank(i+1,4096),this.load8kVromBank(2*s,0)},h[4]=function(t){this.nes=t,this.CMD_SEL_2_1K_VROM_0000=0,this.CMD_SEL_2_1K_VROM_0800=1,this.CMD_SEL_1K_VROM_1000=2,this.CMD_SEL_1K_VROM_1400=3,this.CMD_SEL_1K_VROM_1800=4,this.CMD_SEL_1K_VROM_1C00=5,this.CMD_SEL_ROM_PAGE1=6,this.CMD_SEL_ROM_PAGE2=7,this.command=null,this.prgAddressSelect=null,this.chrAddressSelect=null,this.pageNumber=null,this.irqCounter=null,this.irqLatchValue=null,this.irqEnable=null,this.prgAddressChanged=!1},h[4].prototype=new h[0],h[4].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);switch(t){case 32768:this.command=7&s;var i=s>>6&1;i!==this.prgAddressSelect&&(this.prgAddressChanged=!0),this.prgAddressSelect=i,this.chrAddressSelect=s>>7&1;break;case 32769:this.executeCommand(this.command,s);break;case 40960:0!=(1&s)?this.nes.ppu.setMirroring(this.nes.rom.HORIZONTAL_MIRRORING):this.nes.ppu.setMirroring(this.nes.rom.VERTICAL_MIRRORING);break;case 40961:break;case 49152:this.irqCounter=s;break;case 49153:this.irqLatchValue=s;break;case 57344:this.irqEnable=0;break;case 57345:this.irqEnable=1}},h[4].prototype.executeCommand=function(t,s){switch(t){case this.CMD_SEL_2_1K_VROM_0000:0===this.chrAddressSelect?(this.load1kVromBank(s,0),this.load1kVromBank(s+1,1024)):(this.load1kVromBank(s,4096),this.load1kVromBank(s+1,5120));break;case this.CMD_SEL_2_1K_VROM_0800:0===this.chrAddressSelect?(this.load1kVromBank(s,2048),this.load1kVromBank(s+1,3072)):(this.load1kVromBank(s,6144),this.load1kVromBank(s+1,7168));break;case this.CMD_SEL_1K_VROM_1000:0===this.chrAddressSelect?this.load1kVromBank(s,4096):this.load1kVromBank(s,0);break;case this.CMD_SEL_1K_VROM_1400:0===this.chrAddressSelect?this.load1kVromBank(s,5120):this.load1kVromBank(s,1024);break;case this.CMD_SEL_1K_VROM_1800:0===this.chrAddressSelect?this.load1kVromBank(s,6144):this.load1kVromBank(s,2048);break;case this.CMD_SEL_1K_VROM_1C00:0===this.chrAddressSelect?this.load1kVromBank(s,7168):this.load1kVromBank(s,3072);break;case this.CMD_SEL_ROM_PAGE1:this.prgAddressChanged&&(0===this.prgAddressSelect?this.load8kRomBank(2*(this.nes.rom.romCount-1),49152):this.load8kRomBank(2*(this.nes.rom.romCount-1),32768),this.prgAddressChanged=!1),0===this.prgAddressSelect?this.load8kRomBank(s,32768):this.load8kRomBank(s,49152);break;case this.CMD_SEL_ROM_PAGE2:this.load8kRomBank(s,40960),this.prgAddressChanged&&(0===this.prgAddressSelect?this.load8kRomBank(2*(this.nes.rom.romCount-1),49152):this.load8kRomBank(2*(this.nes.rom.romCount-1),32768),this.prgAddressChanged=!1)}},h[4].prototype.loadROM=function(){if(!this.nes.rom.valid)throw new Error("MMC3: Invalid ROM! Unable to load.");this.load8kRomBank(2*(this.nes.rom.romCount-1),49152),this.load8kRomBank(2*(this.nes.rom.romCount-1)+1,57344),this.load8kRomBank(0,32768),this.load8kRomBank(1,40960),this.loadCHRROM(),this.loadBatteryRam(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},h[4].prototype.clockIrqCounter=function(){1===this.irqEnable&&--this.irqCounter<0&&(this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NORMAL),this.irqCounter=this.irqLatchValue)},h[4].prototype.toJSON=function(){var t=h[0].prototype.toJSON.apply(this);return t.command=this.command,t.prgAddressSelect=this.prgAddressSelect,t.chrAddressSelect=this.chrAddressSelect,t.pageNumber=this.pageNumber,t.irqCounter=this.irqCounter,t.irqLatchValue=this.irqLatchValue,t.irqEnable=this.irqEnable,t.prgAddressChanged=this.prgAddressChanged,t},h[4].prototype.fromJSON=function(t){h[0].prototype.fromJSON.apply(this,arguments),this.command=t.command,this.prgAddressSelect=t.prgAddressSelect,this.chrAddressSelect=t.chrAddressSelect,this.pageNumber=t.pageNumber,this.irqCounter=t.irqCounter,this.irqLatchValue=t.irqLatchValue,this.irqEnable=t.irqEnable,this.prgAddressChanged=t.prgAddressChanged},h[5]=function(t){this.nes=t},h[5].prototype=new h[0],h[5].prototype.write=function(t,s){t<32768?h[0].prototype.write.apply(this,arguments):this.load8kVromBank(s,0)},h[5].prototype.write=function(t,s){if(t<20480)return void h[0].prototype.write.apply(this,arguments);switch(t){case 20736:this.prg_size=3&s;break;case 20737:this.chr_size=3&s;break;case 20738:this.sram_we_a=3&s;break;case 20739:this.sram_we_b=3&s;break;case 20740:this.graphic_mode=3&s;break;case 20741:this.nametable_mode=s,this.nametable_type[0]=3&s,this.load1kVromBank(3&s,8192),s>>=2,this.nametable_type[1]=3&s,this.load1kVromBank(3&s,9216),s>>=2,this.nametable_type[2]=3&s,this.load1kVromBank(3&s,10240),s>>=2,this.nametable_type[3]=3&s,this.load1kVromBank(3&s,11264);break;case 20742:this.fill_chr=s;break;case 20743:this.fill_pal=3&s;break;case 20755:this.SetBank_SRAM(3,3&s);break;case 20756:case 20757:case 20758:case 20759:this.SetBank_CPU(t,s);break;case 20768:case 20769:case 20770:case 20771:case 20772:case 20773:case 20774:case 20775:this.chr_mode=0,this.chr_page[0][7&t]=s,this.SetBank_PPU();break;case 20776:case 20777:case 20778:case 20779:this.chr_mode=1,this.chr_page[1][0+(3&t)]=s,this.chr_page[1][4+(3&t)]=s,this.SetBank_PPU();break;case 20992:this.split_control=s;break;case 20993:this.split_scroll=s;break;case 20994:this.split_page=63&s;break;case 20995:this.irq_line=s,this.nes.cpu.ClearIRQ();break;case 20996:this.irq_enable=s,this.nes.cpu.ClearIRQ();break;case 20997:this.mult_a=s;break;case 20998:this.mult_b=s;break;default:t>=20480&&t<=20501?this.nes.papu.exWrite(t,s):t>=23552&&t<=24575?2===this.graphic_mode||3!==this.graphic_mode&&this.irq_status:t>=24576&&t<=32767&&2===this.sram_we_a&&this.sram_we_b}},h[5].prototype.loadROM=function(){if(!this.nes.rom.valid)throw new Error("UNROM: Invalid ROM! Unable to load.");this.load8kRomBank(2*this.nes.rom.romCount-1,32768),this.load8kRomBank(2*this.nes.rom.romCount-1,40960),this.load8kRomBank(2*this.nes.rom.romCount-1,49152),this.load8kRomBank(2*this.nes.rom.romCount-1,57344),this.loadCHRROM(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},h[7]=function(t){this.nes=t},h[7].prototype=new h[0],h[7].prototype.write=function(t,s){t<32768?h[0].prototype.write.apply(this,arguments):(this.load32kRomBank(7&s,32768),16&s?this.nes.ppu.setMirroring(this.nes.rom.SINGLESCREEN_MIRRORING2):this.nes.ppu.setMirroring(this.nes.rom.SINGLESCREEN_MIRRORING))},h[7].prototype.loadROM=function(){if(!this.nes.rom.valid)throw new Error("AOROM: Invalid ROM! Unable to load.");this.loadPRGROM(),this.loadCHRROM(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},h[11]=function(t){this.nes=t},h[11].prototype=new h[0],h[11].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);var i=2*(15&s)%this.nes.rom.romCount,e=(2*(15&s)+1)%this.nes.rom.romCount;if(this.loadRomBank(i,32768),this.loadRomBank(e,49152),this.nes.rom.vromCount>0){var r=2*(s>>4)%this.nes.rom.vromCount;this.loadVromBank(r,0),this.loadVromBank(r+1,4096)}},h[34]=function(t){this.nes=t},h[34].prototype=new h[0],h[34].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);this.load32kRomBank(s,32768)},h[38]=function(t){this.nes=t},h[38].prototype=new h[0],h[38].prototype.write=function(t,s){if(t<28672||t>32767)return void h[0].prototype.write.apply(this,arguments);this.load32kRomBank(3&s,32768),this.load8kVromBank(2*(s>>2&3),0)},h[66]=function(t){this.nes=t},h[66].prototype=new h[0],h[66].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);this.load32kRomBank(s>>4&3,32768),this.load8kVromBank(2*(3&s),0)},h[94]=function(t){this.nes=t},h[94].prototype=new h[0],h[94].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);this.loadRomBank(s>>2,32768)},h[94].prototype.loadROM=function(){if(!this.nes.rom.valid)throw new Error("UN1ROM: Invalid ROM! Unable to load.");this.loadRomBank(0,32768),this.loadRomBank(this.nes.rom.romCount-1,49152),this.loadCHRROM(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},h[140]=function(t){this.nes=t},h[140].prototype=new h[0],h[140].prototype.write=function(t,s){if(t<24576||t>32767)return void h[0].prototype.write.apply(this,arguments);this.load32kRomBank(s>>4&3,32768),this.load8kVromBank(2*(15&s),0)},h[180]=function(t){this.nes=t},h[180].prototype=new h[0],h[180].prototype.write=function(t,s){if(t<32768)return void h[0].prototype.write.apply(this,arguments);this.loadRomBank(s,49152)},h[180].prototype.loadROM=function(){if(!this.nes.rom.valid)throw new Error("Mapper 180: Invalid ROM! Unable to load.");this.loadRomBank(0,32768),this.loadRomBank(this.nes.rom.romCount-1,49152),this.loadCHRROM(),this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET)},t.exports=h}])}); -//# sourceMappingURL=jsnes.min.js.map diff --git a/fc2/js/long.js b/fc2/js/long.js new file mode 100644 index 0000000..a986f59 --- /dev/null +++ b/fc2/js/long.js @@ -0,0 +1,1220 @@ +/* + Copyright 2013 Daniel Wirtz + Copyright 2009 The Closure Library Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS-IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/** + * @license long.js (c) 2013 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/long.js for details + */ +(function(global, factory) { + + /* AMD */ if (typeof define === 'function' && define["amd"]) + define([], factory); + /* CommonJS */ else if (typeof require === 'function' && typeof module === "object" && module && module["exports"]) + module["exports"] = factory(); + /* Global */ else + (global["dcodeIO"] = global["dcodeIO"] || {})["Long"] = factory(); + +})(this, function() { + "use strict"; + + /** + * Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers. + * See the from* functions below for more convenient ways of constructing Longs. + * @exports Long + * @class A Long class for representing a 64 bit two's-complement integer value. + * @param {number} low The low (signed) 32 bits of the long + * @param {number} high The high (signed) 32 bits of the long + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @constructor + */ + function Long(low, high, unsigned) { + + /** + * The low 32 bits as a signed value. + * @type {number} + * @expose + */ + this.low = low | 0; + + /** + * The high 32 bits as a signed value. + * @type {number} + * @expose + */ + this.high = high | 0; + + /** + * Whether unsigned or not. + * @type {boolean} + * @expose + */ + this.unsigned = !!unsigned; + } + + // The internal representation of a long is the two given signed, 32-bit values. + // We use 32-bit pieces because these are the size of integers on which + // Javascript performs bit-operations. For operations like addition and + // multiplication, we split each number into 16 bit pieces, which can easily be + // multiplied within Javascript's floating-point representation without overflow + // or change in sign. + // + // In the algorithms below, we frequently reduce the negative case to the + // positive case by negating the input(s) and then post-processing the result. + // Note that we must ALWAYS check specially whether those values are MIN_VALUE + // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as + // a positive number, it overflows back into a negative). Not handling this + // case would often result in infinite recursion. + // + // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from* + // methods on which they depend. + + /** + * An indicator used to reliably determine if an object is a Long or not. + * @type {boolean} + * @const + * @expose + * @private + */ + Long.__isLong__; + + Object.defineProperty(Long.prototype, "__isLong__", { + value: true, + enumerable: false, + configurable: false + }); + + /** + * @function + * @param {*} obj Object + * @returns {boolean} + * @inner + */ + function isLong(obj) { + return (obj && obj["__isLong__"]) === true; + } + + /** + * Tests if the specified object is a Long. + * @function + * @param {*} obj Object + * @returns {boolean} + * @expose + */ + Long.isLong = isLong; + + /** + * A cache of the Long representations of small integer values. + * @type {!Object} + * @inner + */ + var INT_CACHE = {}; + + /** + * A cache of the Long representations of small unsigned integer values. + * @type {!Object} + * @inner + */ + var UINT_CACHE = {}; + + /** + * Determines if an integer value is cacheable. + * @param {number} value Integer value + * @param {boolean=} unsigned Whether unsigned or not + * @returns {boolean} + * @inner + */ + function cacheable(value, unsigned) { + return unsigned ? 0 <= (value >>>= 0) && value < 256 : -128 <= (value |= 0) && value < 128; + } + + /** + * @param {number} value + * @param {boolean=} unsigned + * @returns {!Long} + * @inner + */ + function fromInt(value, unsigned) { + var obj, cachedObj, cache; + if (unsigned) { + if (cache = cacheable(value >>>= 0, true)) { + cachedObj = UINT_CACHE[value]; + if (cachedObj) + return cachedObj; + } + obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true); + if (cache) + UINT_CACHE[value] = obj; + return obj; + } else { + if (cache = cacheable(value |= 0, false)) { + cachedObj = INT_CACHE[value]; + if (cachedObj) + return cachedObj; + } + obj = fromBits(value, value < 0 ? -1 : 0, false); + if (cache) + INT_CACHE[value] = obj; + return obj; + } + } + + /** + * Returns a Long representing the given 32 bit integer value. + * @function + * @param {number} value The 32 bit integer in question + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromInt = fromInt; + + /** + * @param {number} value + * @param {boolean=} unsigned + * @returns {!Long} + * @inner + */ + function fromNumber(value, unsigned) { + if (isNaN(value) || !isFinite(value)) + return unsigned ? UZERO : ZERO; + if (unsigned) { + if (value < 0) + return UZERO; + if (value >= TWO_PWR_64_DBL) + return MAX_UNSIGNED_VALUE; + } else { + if (value <= -TWO_PWR_63_DBL) + return MIN_VALUE; + if (value + 1 >= TWO_PWR_63_DBL) + return MAX_VALUE; + } + if (value < 0) + return fromNumber(-value, unsigned).neg(); + return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); + } + + /** + * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * @function + * @param {number} value The number in question + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromNumber = fromNumber; + + /** + * @param {number} lowBits + * @param {number} highBits + * @param {boolean=} unsigned + * @returns {!Long} + * @inner + */ + function fromBits(lowBits, highBits, unsigned) { + return new Long(lowBits, highBits, unsigned); + } + + /** + * Returns a Long representing the 64 bit integer that comes by concatenating the given low and high bits. Each is + * assumed to use 32 bits. + * @function + * @param {number} lowBits The low 32 bits + * @param {number} highBits The high 32 bits + * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromBits = fromBits; + + /** + * @function + * @param {number} base + * @param {number} exponent + * @returns {number} + * @inner + */ + var pow_dbl = Math.pow; // Used 4 times (4*8 to 15+4) + + /** + * @param {string} str + * @param {(boolean|number)=} unsigned + * @param {number=} radix + * @returns {!Long} + * @inner + */ + function fromString(str, unsigned, radix) { + if (str.length === 0) + throw Error('empty string'); + if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity") + return ZERO; + if (typeof unsigned === 'number') // For goog.math.long compatibility + radix = unsigned, + unsigned = false; + radix = radix || 10; + if (radix < 2 || 36 < radix) + throw RangeError('radix'); + + var p; + if ((p = str.indexOf('-')) > 0) + throw Error('interior hyphen'); + else if (p === 0) { + return fromString(str.substring(1), unsigned, radix).neg(); + } + + // Do several (8) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = fromNumber(pow_dbl(radix, 8)); + + var result = ZERO; + for (var i = 0; i < str.length; i += 8) { + var size = Math.min(8, str.length - i), + value = parseInt(str.substring(i, i + size), radix); + if (size < 8) { + var power = fromNumber(pow_dbl(radix, size)); + result = result.mul(power).add(fromNumber(value)); + } else { + result = result.mul(radixToPower); + result = result.add(fromNumber(value)); + } + } + result.unsigned = unsigned; + return result; + } + + /** + * Returns a Long representation of the given string, written using the specified radix. + * @function + * @param {string} str The textual representation of the Long + * @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to `false` for signed + * @param {number=} radix The radix in which the text is written (2-36), defaults to 10 + * @returns {!Long} The corresponding Long value + * @expose + */ + Long.fromString = fromString; + + /** + * @function + * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val + * @returns {!Long} + * @inner + */ + function fromValue(val) { + if (val /* is compatible */ instanceof Long) + return val; + if (typeof val === 'number') + return fromNumber(val); + if (typeof val === 'string') + return fromString(val); + // Throws for non-objects, converts non-instanceof Long: + return fromBits(val.low, val.high, val.unsigned); + } + + /** + * Converts the specified value to a Long. + * @function + * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value + * @returns {!Long} + * @expose + */ + Long.fromValue = fromValue; + + // NOTE: the compiler should inline these constant values below and then remove these variables, so there should be + // no runtime penalty for these. + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_16_DBL = 1 << 16; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_24_DBL = 1 << 24; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2; + + /** + * @type {!Long} + * @const + * @inner + */ + var TWO_PWR_24 = fromInt(TWO_PWR_24_DBL); + + /** + * @type {!Long} + * @inner + */ + var ZERO = fromInt(0); + + /** + * Signed zero. + * @type {!Long} + * @expose + */ + Long.ZERO = ZERO; + + /** + * @type {!Long} + * @inner + */ + var UZERO = fromInt(0, true); + + /** + * Unsigned zero. + * @type {!Long} + * @expose + */ + Long.UZERO = UZERO; + + /** + * @type {!Long} + * @inner + */ + var ONE = fromInt(1); + + /** + * Signed one. + * @type {!Long} + * @expose + */ + Long.ONE = ONE; + + /** + * @type {!Long} + * @inner + */ + var UONE = fromInt(1, true); + + /** + * Unsigned one. + * @type {!Long} + * @expose + */ + Long.UONE = UONE; + + /** + * @type {!Long} + * @inner + */ + var NEG_ONE = fromInt(-1); + + /** + * Signed negative one. + * @type {!Long} + * @expose + */ + Long.NEG_ONE = NEG_ONE; + + /** + * @type {!Long} + * @inner + */ + var MAX_VALUE = fromBits(0xFFFFFFFF|0, 0x7FFFFFFF|0, false); + + /** + * Maximum signed value. + * @type {!Long} + * @expose + */ + Long.MAX_VALUE = MAX_VALUE; + + /** + * @type {!Long} + * @inner + */ + var MAX_UNSIGNED_VALUE = fromBits(0xFFFFFFFF|0, 0xFFFFFFFF|0, true); + + /** + * Maximum unsigned value. + * @type {!Long} + * @expose + */ + Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE; + + /** + * @type {!Long} + * @inner + */ + var MIN_VALUE = fromBits(0, 0x80000000|0, false); + + /** + * Minimum signed value. + * @type {!Long} + * @expose + */ + Long.MIN_VALUE = MIN_VALUE; + + /** + * @alias Long.prototype + * @inner + */ + var LongPrototype = Long.prototype; + + /** + * Converts the Long to a 32 bit integer, assuming it is a 32 bit integer. + * @returns {number} + * @expose + */ + LongPrototype.toInt = function toInt() { + return this.unsigned ? this.low >>> 0 : this.low; + }; + + /** + * Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa). + * @returns {number} + * @expose + */ + LongPrototype.toNumber = function toNumber() { + if (this.unsigned) + return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0); + return this.high * TWO_PWR_32_DBL + (this.low >>> 0); + }; + + /** + * Converts the Long to a string written in the specified radix. + * @param {number=} radix Radix (2-36), defaults to 10 + * @returns {string} + * @override + * @throws {RangeError} If `radix` is out of range + * @expose + */ + LongPrototype.toString = function toString(radix) { + radix = radix || 10; + if (radix < 2 || 36 < radix) + throw RangeError('radix'); + if (this.isZero()) + return '0'; + if (this.isNegative()) { // Unsigned Longs are never negative + if (this.eq(MIN_VALUE)) { + // We need to change the Long value before it can be negated, so we remove + // the bottom-most digit in this base and then recurse to do the rest. + var radixLong = fromNumber(radix), + div = this.div(radixLong), + rem1 = div.mul(radixLong).sub(this); + return div.toString(radix) + rem1.toInt().toString(radix); + } else + return '-' + this.neg().toString(radix); + } + + // Do several (6) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned), + rem = this; + var result = ''; + while (true) { + var remDiv = rem.div(radixToPower), + intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0, + digits = intval.toString(radix); + rem = remDiv; + if (rem.isZero()) + return digits + result; + else { + while (digits.length < 6) + digits = '0' + digits; + result = '' + digits + result; + } + } + }; + + /** + * Gets the high 32 bits as a signed integer. + * @returns {number} Signed high bits + * @expose + */ + LongPrototype.getHighBits = function getHighBits() { + return this.high; + }; + + /** + * Gets the high 32 bits as an unsigned integer. + * @returns {number} Unsigned high bits + * @expose + */ + LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() { + return this.high >>> 0; + }; + + /** + * Gets the low 32 bits as a signed integer. + * @returns {number} Signed low bits + * @expose + */ + LongPrototype.getLowBits = function getLowBits() { + return this.low; + }; + + /** + * Gets the low 32 bits as an unsigned integer. + * @returns {number} Unsigned low bits + * @expose + */ + LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() { + return this.low >>> 0; + }; + + /** + * Gets the number of bits needed to represent the absolute value of this Long. + * @returns {number} + * @expose + */ + LongPrototype.getNumBitsAbs = function getNumBitsAbs() { + if (this.isNegative()) // Unsigned Longs are never negative + return this.eq(MIN_VALUE) ? 64 : this.neg().getNumBitsAbs(); + var val = this.high != 0 ? this.high : this.low; + for (var bit = 31; bit > 0; bit--) + if ((val & (1 << bit)) != 0) + break; + return this.high != 0 ? bit + 33 : bit + 1; + }; + + /** + * Tests if this Long's value equals zero. + * @returns {boolean} + * @expose + */ + LongPrototype.isZero = function isZero() { + return this.high === 0 && this.low === 0; + }; + + /** + * Tests if this Long's value is negative. + * @returns {boolean} + * @expose + */ + LongPrototype.isNegative = function isNegative() { + return !this.unsigned && this.high < 0; + }; + + /** + * Tests if this Long's value is positive. + * @returns {boolean} + * @expose + */ + LongPrototype.isPositive = function isPositive() { + return this.unsigned || this.high >= 0; + }; + + /** + * Tests if this Long's value is odd. + * @returns {boolean} + * @expose + */ + LongPrototype.isOdd = function isOdd() { + return (this.low & 1) === 1; + }; + + /** + * Tests if this Long's value is even. + * @returns {boolean} + * @expose + */ + LongPrototype.isEven = function isEven() { + return (this.low & 1) === 0; + }; + + /** + * Tests if this Long's value equals the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.equals = function equals(other) { + if (!isLong(other)) + other = fromValue(other); + if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1) + return false; + return this.high === other.high && this.low === other.low; + }; + + /** + * Tests if this Long's value equals the specified's. This is an alias of {@link Long#equals}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.eq = LongPrototype.equals; + + /** + * Tests if this Long's value differs from the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.notEquals = function notEquals(other) { + return !this.eq(/* validates */ other); + }; + + /** + * Tests if this Long's value differs from the specified's. This is an alias of {@link Long#notEquals}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.neq = LongPrototype.notEquals; + + /** + * Tests if this Long's value is less than the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.lessThan = function lessThan(other) { + return this.comp(/* validates */ other) < 0; + }; + + /** + * Tests if this Long's value is less than the specified's. This is an alias of {@link Long#lessThan}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.lt = LongPrototype.lessThan; + + /** + * Tests if this Long's value is less than or equal the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) { + return this.comp(/* validates */ other) <= 0; + }; + + /** + * Tests if this Long's value is less than or equal the specified's. This is an alias of {@link Long#lessThanOrEqual}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.lte = LongPrototype.lessThanOrEqual; + + /** + * Tests if this Long's value is greater than the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.greaterThan = function greaterThan(other) { + return this.comp(/* validates */ other) > 0; + }; + + /** + * Tests if this Long's value is greater than the specified's. This is an alias of {@link Long#greaterThan}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.gt = LongPrototype.greaterThan; + + /** + * Tests if this Long's value is greater than or equal the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) { + return this.comp(/* validates */ other) >= 0; + }; + + /** + * Tests if this Long's value is greater than or equal the specified's. This is an alias of {@link Long#greaterThanOrEqual}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + * @expose + */ + LongPrototype.gte = LongPrototype.greaterThanOrEqual; + + /** + * Compares this Long's value with the specified's. + * @param {!Long|number|string} other Other value + * @returns {number} 0 if they are the same, 1 if the this is greater and -1 + * if the given one is greater + * @expose + */ + LongPrototype.compare = function compare(other) { + if (!isLong(other)) + other = fromValue(other); + if (this.eq(other)) + return 0; + var thisNeg = this.isNegative(), + otherNeg = other.isNegative(); + if (thisNeg && !otherNeg) + return -1; + if (!thisNeg && otherNeg) + return 1; + // At this point the sign bits are the same + if (!this.unsigned) + return this.sub(other).isNegative() ? -1 : 1; + // Both are positive if at least one is unsigned + return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1; + }; + + /** + * Compares this Long's value with the specified's. This is an alias of {@link Long#compare}. + * @function + * @param {!Long|number|string} other Other value + * @returns {number} 0 if they are the same, 1 if the this is greater and -1 + * if the given one is greater + * @expose + */ + LongPrototype.comp = LongPrototype.compare; + + /** + * Negates this Long's value. + * @returns {!Long} Negated Long + * @expose + */ + LongPrototype.negate = function negate() { + if (!this.unsigned && this.eq(MIN_VALUE)) + return MIN_VALUE; + return this.not().add(ONE); + }; + + /** + * Negates this Long's value. This is an alias of {@link Long#negate}. + * @function + * @returns {!Long} Negated Long + * @expose + */ + LongPrototype.neg = LongPrototype.negate; + + /** + * Returns the sum of this and the specified Long. + * @param {!Long|number|string} addend Addend + * @returns {!Long} Sum + * @expose + */ + LongPrototype.add = function add(addend) { + if (!isLong(addend)) + addend = fromValue(addend); + + // Divide each number into 4 chunks of 16 bits, and then sum the chunks. + + var a48 = this.high >>> 16; + var a32 = this.high & 0xFFFF; + var a16 = this.low >>> 16; + var a00 = this.low & 0xFFFF; + + var b48 = addend.high >>> 16; + var b32 = addend.high & 0xFFFF; + var b16 = addend.low >>> 16; + var b00 = addend.low & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 + b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 + b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 + b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 + b48; + c48 &= 0xFFFF; + return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); + }; + + /** + * Returns the difference of this and the specified Long. + * @param {!Long|number|string} subtrahend Subtrahend + * @returns {!Long} Difference + * @expose + */ + LongPrototype.subtract = function subtract(subtrahend) { + if (!isLong(subtrahend)) + subtrahend = fromValue(subtrahend); + return this.add(subtrahend.neg()); + }; + + /** + * Returns the difference of this and the specified Long. This is an alias of {@link Long#subtract}. + * @function + * @param {!Long|number|string} subtrahend Subtrahend + * @returns {!Long} Difference + * @expose + */ + LongPrototype.sub = LongPrototype.subtract; + + /** + * Returns the product of this and the specified Long. + * @param {!Long|number|string} multiplier Multiplier + * @returns {!Long} Product + * @expose + */ + LongPrototype.multiply = function multiply(multiplier) { + if (this.isZero()) + return ZERO; + if (!isLong(multiplier)) + multiplier = fromValue(multiplier); + if (multiplier.isZero()) + return ZERO; + if (this.eq(MIN_VALUE)) + return multiplier.isOdd() ? MIN_VALUE : ZERO; + if (multiplier.eq(MIN_VALUE)) + return this.isOdd() ? MIN_VALUE : ZERO; + + if (this.isNegative()) { + if (multiplier.isNegative()) + return this.neg().mul(multiplier.neg()); + else + return this.neg().mul(multiplier).neg(); + } else if (multiplier.isNegative()) + return this.mul(multiplier.neg()).neg(); + + // If both longs are small, use float multiplication + if (this.lt(TWO_PWR_24) && multiplier.lt(TWO_PWR_24)) + return fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned); + + // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products. + // We can skip products that would overflow. + + var a48 = this.high >>> 16; + var a32 = this.high & 0xFFFF; + var a16 = this.low >>> 16; + var a00 = this.low & 0xFFFF; + + var b48 = multiplier.high >>> 16; + var b32 = multiplier.high & 0xFFFF; + var b16 = multiplier.low >>> 16; + var b00 = multiplier.low & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 * b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 * b00; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c16 += a00 * b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 * b00; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a16 * b16; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a00 * b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; + c48 &= 0xFFFF; + return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); + }; + + /** + * Returns the product of this and the specified Long. This is an alias of {@link Long#multiply}. + * @function + * @param {!Long|number|string} multiplier Multiplier + * @returns {!Long} Product + * @expose + */ + LongPrototype.mul = LongPrototype.multiply; + + /** + * Returns this Long divided by the specified. + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Quotient + * @expose + */ + LongPrototype.divide = function divide(divisor) { + if (!isLong(divisor)) + divisor = fromValue(divisor); + if (divisor.isZero()) + throw Error('division by zero'); + if (this.isZero()) + return this.unsigned ? UZERO : ZERO; + var approx, rem, res; + if (this.eq(MIN_VALUE)) { + if (divisor.eq(ONE) || divisor.eq(NEG_ONE)) + return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE + else if (divisor.eq(MIN_VALUE)) + return ONE; + else { + // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. + var halfThis = this.shr(1); + approx = halfThis.div(divisor).shl(1); + if (approx.eq(ZERO)) { + return divisor.isNegative() ? ONE : NEG_ONE; + } else { + rem = this.sub(divisor.mul(approx)); + res = approx.add(rem.div(divisor)); + return res; + } + } + } else if (divisor.eq(MIN_VALUE)) + return this.unsigned ? UZERO : ZERO; + if (this.isNegative()) { + if (divisor.isNegative()) + return this.neg().div(divisor.neg()); + return this.neg().div(divisor).neg(); + } else if (divisor.isNegative()) + return this.div(divisor.neg()).neg(); + + // Repeat the following until the remainder is less than other: find a + // floating-point that approximates remainder / other *from below*, add this + // into the result, and subtract it from the remainder. It is critical that + // the approximate value is less than or equal to the real value so that the + // remainder never becomes negative. + res = ZERO; + rem = this; + while (rem.gte(divisor)) { + // Approximate the result of division. This may be a little greater or + // smaller than the actual value. + approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber())); + + // We will tweak the approximate result by changing it in the 48-th digit or + // the smallest non-fractional digit, whichever is larger. + var log2 = Math.ceil(Math.log(approx) / Math.LN2), + delta = (log2 <= 48) ? 1 : pow_dbl(2, log2 - 48), + + // Decrease the approximation until it is smaller than the remainder. Note + // that if it is too large, the product overflows and is negative. + approxRes = fromNumber(approx), + approxRem = approxRes.mul(divisor); + while (approxRem.isNegative() || approxRem.gt(rem)) { + approx -= delta; + approxRes = fromNumber(approx, this.unsigned); + approxRem = approxRes.mul(divisor); + } + + // We know the answer can't be zero... and actually, zero would cause + // infinite recursion since we would make no progress. + if (approxRes.isZero()) + approxRes = ONE; + + res = res.add(approxRes); + rem = rem.sub(approxRem); + } + return res; + }; + + /** + * Returns this Long divided by the specified. This is an alias of {@link Long#divide}. + * @function + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Quotient + * @expose + */ + LongPrototype.div = LongPrototype.divide; + + /** + * Returns this Long modulo the specified. + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Remainder + * @expose + */ + LongPrototype.modulo = function modulo(divisor) { + if (!isLong(divisor)) + divisor = fromValue(divisor); + return this.sub(this.div(divisor).mul(divisor)); + }; + + /** + * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}. + * @function + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Remainder + * @expose + */ + LongPrototype.mod = LongPrototype.modulo; + + /** + * Returns the bitwise NOT of this Long. + * @returns {!Long} + * @expose + */ + LongPrototype.not = function not() { + return fromBits(~this.low, ~this.high, this.unsigned); + }; + + /** + * Returns the bitwise AND of this Long and the specified. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + * @expose + */ + LongPrototype.and = function and(other) { + if (!isLong(other)) + other = fromValue(other); + return fromBits(this.low & other.low, this.high & other.high, this.unsigned); + }; + + /** + * Returns the bitwise OR of this Long and the specified. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + * @expose + */ + LongPrototype.or = function or(other) { + if (!isLong(other)) + other = fromValue(other); + return fromBits(this.low | other.low, this.high | other.high, this.unsigned); + }; + + /** + * Returns the bitwise XOR of this Long and the given one. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + * @expose + */ + LongPrototype.xor = function xor(other) { + if (!isLong(other)) + other = fromValue(other); + return fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned); + }; + + /** + * Returns this Long with bits shifted to the left by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + LongPrototype.shiftLeft = function shiftLeft(numBits) { + if (isLong(numBits)) + numBits = numBits.toInt(); + if ((numBits &= 63) === 0) + return this; + else if (numBits < 32) + return fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned); + else + return fromBits(0, this.low << (numBits - 32), this.unsigned); + }; + + /** + * Returns this Long with bits shifted to the left by the given amount. This is an alias of {@link Long#shiftLeft}. + * @function + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + LongPrototype.shl = LongPrototype.shiftLeft; + + /** + * Returns this Long with bits arithmetically shifted to the right by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + LongPrototype.shiftRight = function shiftRight(numBits) { + if (isLong(numBits)) + numBits = numBits.toInt(); + if ((numBits &= 63) === 0) + return this; + else if (numBits < 32) + return fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned); + else + return fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned); + }; + + /** + * Returns this Long with bits arithmetically shifted to the right by the given amount. This is an alias of {@link Long#shiftRight}. + * @function + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + LongPrototype.shr = LongPrototype.shiftRight; + + /** + * Returns this Long with bits logically shifted to the right by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) { + if (isLong(numBits)) + numBits = numBits.toInt(); + numBits &= 63; + if (numBits === 0) + return this; + else { + var high = this.high; + if (numBits < 32) { + var low = this.low; + return fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned); + } else if (numBits === 32) + return fromBits(high, 0, this.unsigned); + else + return fromBits(high >>> (numBits - 32), 0, this.unsigned); + } + }; + + /** + * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}. + * @function + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + * @expose + */ + LongPrototype.shru = LongPrototype.shiftRightUnsigned; + + /** + * Converts this Long to signed. + * @returns {!Long} Signed long + * @expose + */ + LongPrototype.toSigned = function toSigned() { + if (!this.unsigned) + return this; + return fromBits(this.low, this.high, false); + }; + + /** + * Converts this Long to unsigned. + * @returns {!Long} Unsigned long + * @expose + */ + LongPrototype.toUnsigned = function toUnsigned() { + if (this.unsigned) + return this; + return fromBits(this.low, this.high, true); + }; + + return Long; +}); diff --git a/fc2/js/netWorkHandle.js b/fc2/js/netWorkHandle.js new file mode 100644 index 0000000..f73fd0d --- /dev/null +++ b/fc2/js/netWorkHandle.js @@ -0,0 +1,63 @@ +NetEnum = { + STARTGAME:1, + ENTERGAME:2, + CHANGECLOTH:3, + JUMP:100, + USEITEM:101, + ENDITEM:102, + STARTMATCH:103, + SETROBOT:104, +} + + nwh = function(){ + this.functionarr=[]; + var self = this; + this.process=function(msg){ + var data = JSON.parse(msg.content); + data.uid = msg.srcUserId; + this.functionarr[data.m](data) + }; + this.processFrame =function(msg){ + for(var i=0;i + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/** + * @license protobuf.js (c) 2013 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/protobuf.js for details + */ + ProtoBuf = {}; +(function(global, factory) { + + /* AMD */ if (typeof define === 'function' && define["amd"]) + define(["bytebuffer"], factory); + /* CommonJS */ else if (typeof require === "function" && typeof module === "object" && module && module["exports"]) + module["exports"] = factory(require("bytebuffer"), true); + /* Global */ else + (global["dcodeIO"] = global["dcodeIO"] || {})["ProtoBuf"] = factory(global["dcodeIO"]["ByteBuffer"]); + +})(this, function(ByteBuffer, isCommonJS) { + "use strict"; + + /** + * The ProtoBuf namespace. + * @exports ProtoBuf + * @namespace + * @expose + */ + // ProtoBuf = {}; + + /** + * @type {!function(new: ByteBuffer, ...[*])} + * @expose + */ + ProtoBuf.ByteBuffer = ByteBuffer; + + /** + * @type {?function(new: Long, ...[*])} + * @expose + */ + ProtoBuf.Long = ByteBuffer.Long || null; + + /** + * ProtoBuf.js version. + * @type {string} + * @const + * @expose + */ + ProtoBuf.VERSION = "5.0.1"; + + /** + * Wire types. + * @type {Object.} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES = {}; + + /** + * Varint wire type. + * @type {number} + * @expose + */ + ProtoBuf.WIRE_TYPES.VARINT = 0; + + /** + * Fixed 64 bits wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.BITS64 = 1; + + /** + * Length delimited wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.LDELIM = 2; + + /** + * Start group wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.STARTGROUP = 3; + + /** + * End group wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.ENDGROUP = 4; + + /** + * Fixed 32 bits wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.BITS32 = 5; + + /** + * Packable wire types. + * @type {!Array.} + * @const + * @expose + */ + ProtoBuf.PACKABLE_WIRE_TYPES = [ + ProtoBuf.WIRE_TYPES.VARINT, + ProtoBuf.WIRE_TYPES.BITS64, + ProtoBuf.WIRE_TYPES.BITS32 + ]; + + /** + * Types. + * @dict + * @type {!Object.} + * @const + * @expose + */ + ProtoBuf.TYPES = { + // According to the protobuf spec. + "int32": { + name: "int32", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: 0 + }, + "uint32": { + name: "uint32", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: 0 + }, + "sint32": { + name: "sint32", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: 0 + }, + "int64": { + name: "int64", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: ProtoBuf.Long ? ProtoBuf.Long.ZERO : undefined + }, + "uint64": { + name: "uint64", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: ProtoBuf.Long ? ProtoBuf.Long.UZERO : undefined + }, + "sint64": { + name: "sint64", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: ProtoBuf.Long ? ProtoBuf.Long.ZERO : undefined + }, + "bool": { + name: "bool", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: false + }, + "double": { + name: "double", + wireType: ProtoBuf.WIRE_TYPES.BITS64, + defaultValue: 0 + }, + "string": { + name: "string", + wireType: ProtoBuf.WIRE_TYPES.LDELIM, + defaultValue: "" + }, + "bytes": { + name: "bytes", + wireType: ProtoBuf.WIRE_TYPES.LDELIM, + defaultValue: null // overridden in the code, must be a unique instance + }, + "fixed32": { + name: "fixed32", + wireType: ProtoBuf.WIRE_TYPES.BITS32, + defaultValue: 0 + }, + "sfixed32": { + name: "sfixed32", + wireType: ProtoBuf.WIRE_TYPES.BITS32, + defaultValue: 0 + }, + "fixed64": { + name: "fixed64", + wireType: ProtoBuf.WIRE_TYPES.BITS64, + defaultValue: ProtoBuf.Long ? ProtoBuf.Long.UZERO : undefined + }, + "sfixed64": { + name: "sfixed64", + wireType: ProtoBuf.WIRE_TYPES.BITS64, + defaultValue: ProtoBuf.Long ? ProtoBuf.Long.ZERO : undefined + }, + "float": { + name: "float", + wireType: ProtoBuf.WIRE_TYPES.BITS32, + defaultValue: 0 + }, + "enum": { + name: "enum", + wireType: ProtoBuf.WIRE_TYPES.VARINT, + defaultValue: 0 + }, + "message": { + name: "message", + wireType: ProtoBuf.WIRE_TYPES.LDELIM, + defaultValue: null + }, + "group": { + name: "group", + wireType: ProtoBuf.WIRE_TYPES.STARTGROUP, + defaultValue: null + } + }; + + /** + * Valid map key types. + * @type {!Array.>} + * @const + * @expose + */ + ProtoBuf.MAP_KEY_TYPES = [ + ProtoBuf.TYPES["int32"], + ProtoBuf.TYPES["sint32"], + ProtoBuf.TYPES["sfixed32"], + ProtoBuf.TYPES["uint32"], + ProtoBuf.TYPES["fixed32"], + ProtoBuf.TYPES["int64"], + ProtoBuf.TYPES["sint64"], + ProtoBuf.TYPES["sfixed64"], + ProtoBuf.TYPES["uint64"], + ProtoBuf.TYPES["fixed64"], + ProtoBuf.TYPES["bool"], + ProtoBuf.TYPES["string"], + ProtoBuf.TYPES["bytes"] + ]; + + /** + * Minimum field id. + * @type {number} + * @const + * @expose + */ + ProtoBuf.ID_MIN = 1; + + /** + * Maximum field id. + * @type {number} + * @const + * @expose + */ + ProtoBuf.ID_MAX = 0x1FFFFFFF; + + /** + * If set to `true`, field names will be converted from underscore notation to camel case. Defaults to `false`. + * Must be set prior to parsing. + * @type {boolean} + * @expose + */ + ProtoBuf.convertFieldsToCamelCase = false; + + /** + * By default, messages are populated with (setX, set_x) accessors for each field. This can be disabled by + * setting this to `false` prior to building messages. + * @type {boolean} + * @expose + */ + ProtoBuf.populateAccessors = true; + + /** + * By default, messages are populated with default values if a field is not present on the wire. To disable + * this behavior, set this setting to `false`. + * @type {boolean} + * @expose + */ + ProtoBuf.populateDefaults = true; + + /** + * @alias ProtoBuf.Util + * @expose + */ + ProtoBuf.Util = (function() { + "use strict"; + + /** + * ProtoBuf utilities. + * @exports ProtoBuf.Util + * @namespace + */ + var Util = {}; + + /** + * Flag if running in node or not. + * @type {boolean} + * @const + * @expose + */ + Util.IS_NODE = !!( + typeof process === 'object' && process+'' === '[object process]' && !process['browser'] + ); + + Util.IS_COCOS = false; + + /** + * Constructs a XMLHttpRequest object. + * @return {XMLHttpRequest} + * @throws {Error} If XMLHttpRequest is not supported + * @expose + */ + Util.XHR = function() { + // No dependencies please, ref: http://www.quirksmode.org/js/xmlhttp.html + var XMLHttpFactories = [ + function () {return new XMLHttpRequest()}, + function () {return new ActiveXObject("Msxml2.XMLHTTP")}, + function () {return new ActiveXObject("Msxml3.XMLHTTP")}, + function () {return new ActiveXObject("Microsoft.XMLHTTP")} + ]; + /** @type {?XMLHttpRequest} */ + var xhr = null; + for (var i=0;i} + * @expose + */ + ProtoBuf.Lang = { + + // Characters always ending a statement + DELIM: /[\s\{\}=;:\[\],'"\(\)<>]/g, + + // Field rules + RULE: /^(?:required|optional|repeated|map)$/, + + // Field types + TYPE: /^(?:double|float|int32|uint32|sint32|int64|uint64|sint64|fixed32|sfixed32|fixed64|sfixed64|bool|string|bytes)$/, + + // Names + NAME: /^[a-zA-Z_][a-zA-Z_0-9]*$/, + + // Type definitions + TYPEDEF: /^[a-zA-Z][a-zA-Z_0-9]*$/, + + // Type references + TYPEREF: /^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)+$/, + + // Fully qualified type references + FQTYPEREF: /^(?:\.[a-zA-Z][a-zA-Z_0-9]*)+$/, + + // All numbers + NUMBER: /^-?(?:[1-9][0-9]*|0|0[xX][0-9a-fA-F]+|0[0-7]+|([0-9]*(\.[0-9]*)?([Ee][+-]?[0-9]+)?)|inf|nan)$/, + + // Decimal numbers + NUMBER_DEC: /^(?:[1-9][0-9]*|0)$/, + + // Hexadecimal numbers + NUMBER_HEX: /^0[xX][0-9a-fA-F]+$/, + + // Octal numbers + NUMBER_OCT: /^0[0-7]+$/, + + // Floating point numbers + NUMBER_FLT: /^([0-9]*(\.[0-9]*)?([Ee][+-]?[0-9]+)?|inf|nan)$/, + + // Booleans + BOOL: /^(?:true|false)$/i, + + // Id numbers + ID: /^(?:[1-9][0-9]*|0|0[xX][0-9a-fA-F]+|0[0-7]+)$/, + + // Negative id numbers (enum values) + NEGID: /^\-?(?:[1-9][0-9]*|0|0[xX][0-9a-fA-F]+|0[0-7]+)$/, + + // Whitespaces + WHITESPACE: /\s/, + + // All strings + STRING: /(?:"([^"\\]*(?:\\.[^"\\]*)*)")|(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g, + + // Double quoted strings + STRING_DQ: /(?:"([^"\\]*(?:\\.[^"\\]*)*)")/g, + + // Single quoted strings + STRING_SQ: /(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g + }; + + /** + * @alias ProtoBuf.DotProto + * @expose + */ + ProtoBuf.DotProto = (function(ProtoBuf, Lang) { + "use strict"; + + /** + * Utilities to parse .proto files. + * @exports ProtoBuf.DotProto + * @namespace + */ + var DotProto = {}; + + /** + * Constructs a new Tokenizer. + * @exports ProtoBuf.DotProto.Tokenizer + * @class prototype tokenizer + * @param {string} proto Proto to tokenize + * @constructor + */ + var Tokenizer = function(proto) { + + /** + * Source to parse. + * @type {string} + * @expose + */ + this.source = proto+""; + + /** + * Current index. + * @type {number} + * @expose + */ + this.index = 0; + + /** + * Current line. + * @type {number} + * @expose + */ + this.line = 1; + + /** + * Token stack. + * @type {!Array.} + * @expose + */ + this.stack = []; + + /** + * Opening character of the current string read, if any. + * @type {?string} + * @private + */ + this._stringOpen = null; + }; + + /** + * @alias ProtoBuf.DotProto.Tokenizer.prototype + * @inner + */ + var TokenizerPrototype = Tokenizer.prototype; + + /** + * Reads a string beginning at the current index. + * @return {string} + * @private + */ + TokenizerPrototype._readString = function() { + var re = this._stringOpen === '"' + ? Lang.STRING_DQ + : Lang.STRING_SQ; + re.lastIndex = this.index - 1; // Include the open quote + var match = re.exec(this.source); + if (!match) + throw Error("unterminated string"); + this.index = re.lastIndex; + this.stack.push(this._stringOpen); + this._stringOpen = null; + return match[1]; + }; + + /** + * Gets the next token and advances by one. + * @return {?string} Token or `null` on EOF + * @expose + */ + TokenizerPrototype.next = function() { + if (this.stack.length > 0) + return this.stack.shift(); + if (this.index >= this.source.length) + return null; + if (this._stringOpen !== null) + return this._readString(); + + var repeat, + prev, + next; + do { + repeat = false; + + // Strip white spaces + while (Lang.WHITESPACE.test(next = this.source.charAt(this.index))) { + if (next === '\n') + ++this.line; + if (++this.index === this.source.length) + return null; + } + + // Strip comments + if (this.source.charAt(this.index) === '/') { + ++this.index; + if (this.source.charAt(this.index) === '/') { // Line + while (this.source.charAt(++this.index) !== '\n') + if (this.index == this.source.length) + return null; + ++this.index; + ++this.line; + repeat = true; + } else if ((next = this.source.charAt(this.index)) === '*') { /* Block */ + do { + if (next === '\n') + ++this.line; + if (++this.index === this.source.length) + return null; + prev = next; + next = this.source.charAt(this.index); + } while (prev !== '*' || next !== '/'); + ++this.index; + repeat = true; + } else + return '/'; + } + } while (repeat); + + if (this.index === this.source.length) + return null; + + // Read the next token + var end = this.index; + Lang.DELIM.lastIndex = 0; + var delim = Lang.DELIM.test(this.source.charAt(end++)); + if (!delim) + while(end < this.source.length && !Lang.DELIM.test(this.source.charAt(end))) + ++end; + var token = this.source.substring(this.index, this.index = end); + if (token === '"' || token === "'") + this._stringOpen = token; + return token; + }; + + /** + * Peeks for the next token. + * @return {?string} Token or `null` on EOF + * @expose + */ + TokenizerPrototype.peek = function() { + if (this.stack.length === 0) { + var token = this.next(); + if (token === null) + return null; + this.stack.push(token); + } + return this.stack[0]; + }; + + /** + * Skips a specific token and throws if it differs. + * @param {string} expected Expected token + * @throws {Error} If the actual token differs + */ + TokenizerPrototype.skip = function(expected) { + var actual = this.next(); + if (actual !== expected) + throw Error("illegal '"+actual+"', '"+expected+"' expected"); + }; + + /** + * Omits an optional token. + * @param {string} expected Expected optional token + * @returns {boolean} `true` if the token exists + */ + TokenizerPrototype.omit = function(expected) { + if (this.peek() === expected) { + this.next(); + return true; + } + return false; + }; + + /** + * Returns a string representation of this object. + * @return {string} String representation as of "Tokenizer(index/length)" + * @expose + */ + TokenizerPrototype.toString = function() { + return "Tokenizer ("+this.index+"/"+this.source.length+" at line "+this.line+")"; + }; + + /** + * @alias ProtoBuf.DotProto.Tokenizer + * @expose + */ + DotProto.Tokenizer = Tokenizer; + + /** + * Constructs a new Parser. + * @exports ProtoBuf.DotProto.Parser + * @class prototype parser + * @param {string} source Source + * @constructor + */ + var Parser = function(source) { + + /** + * Tokenizer. + * @type {!ProtoBuf.DotProto.Tokenizer} + * @expose + */ + this.tn = new Tokenizer(source); + + /** + * Whether parsing proto3 or not. + * @type {boolean} + */ + this.proto3 = false; + }; + + /** + * @alias ProtoBuf.DotProto.Parser.prototype + * @inner + */ + var ParserPrototype = Parser.prototype; + + /** + * Parses the source. + * @returns {!Object} + * @throws {Error} If the source cannot be parsed + * @expose + */ + ParserPrototype.parse = function() { + var topLevel = { + "name": "[ROOT]", // temporary + "package": null, + "messages": [], + "enums": [], + "imports": [], + "options": {}, + "services": [] + // "syntax": undefined + }; + var token, + head = true; + try { + while (token = this.tn.next()) { + switch (token) { + case 'package': + if (!head || topLevel["package"] !== null) + throw Error("unexpected 'package'"); + token = this.tn.next(); + if (!Lang.TYPEREF.test(token)) + throw Error("illegal package name: " + token); + this.tn.skip(";"); + topLevel["package"] = token; + break; + case 'import': + if (!head) + throw Error("unexpected 'import'"); + token = this.tn.peek(); + if (token === "public") // ignored + this.tn.next(); + token = this._readString(); + this.tn.skip(";"); + topLevel["imports"].push(token); + break; + case 'syntax': + if (!head) + throw Error("unexpected 'syntax'"); + this.tn.skip("="); + if ((topLevel["syntax"] = this._readString()) === "proto3") + this.proto3 = true; + this.tn.skip(";"); + break; + case 'message': + this._parseMessage(topLevel, null); + head = false; + break; + case 'enum': + this._parseEnum(topLevel); + head = false; + break; + case 'option': + this._parseOption(topLevel); + break; + case 'service': + this._parseService(topLevel); + break; + case 'extend': + this._parseExtend(topLevel); + break; + default: + throw Error("unexpected '" + token + "'"); + } + } + } catch (e) { + e.message = "Parse error at line "+this.tn.line+": " + e.message; + throw e; + } + delete topLevel["name"]; + return topLevel; + }; + + /** + * Parses the specified source. + * @returns {!Object} + * @throws {Error} If the source cannot be parsed + * @expose + */ + Parser.parse = function(source) { + return new Parser(source).parse(); + }; + + // ----- Conversion ------ + + /** + * Converts a numerical string to an id. + * @param {string} value + * @param {boolean=} mayBeNegative + * @returns {number} + * @inner + */ + function mkId(value, mayBeNegative) { + var id = -1, + sign = 1; + if (value.charAt(0) == '-') { + sign = -1; + value = value.substring(1); + } + if (Lang.NUMBER_DEC.test(value)) + id = parseInt(value); + else if (Lang.NUMBER_HEX.test(value)) + id = parseInt(value.substring(2), 16); + else if (Lang.NUMBER_OCT.test(value)) + id = parseInt(value.substring(1), 8); + else + throw Error("illegal id value: " + (sign < 0 ? '-' : '') + value); + id = (sign*id)|0; // Force to 32bit + if (!mayBeNegative && id < 0) + throw Error("illegal id value: " + (sign < 0 ? '-' : '') + value); + return id; + } + + /** + * Converts a numerical string to a number. + * @param {string} val + * @returns {number} + * @inner + */ + function mkNumber(val) { + var sign = 1; + if (val.charAt(0) == '-') { + sign = -1; + val = val.substring(1); + } + if (Lang.NUMBER_DEC.test(val)) + return sign * parseInt(val, 10); + else if (Lang.NUMBER_HEX.test(val)) + return sign * parseInt(val.substring(2), 16); + else if (Lang.NUMBER_OCT.test(val)) + return sign * parseInt(val.substring(1), 8); + else if (val === 'inf') + return sign * Infinity; + else if (val === 'nan') + return NaN; + else if (Lang.NUMBER_FLT.test(val)) + return sign * parseFloat(val); + throw Error("illegal number value: " + (sign < 0 ? '-' : '') + val); + } + + // ----- Reading ------ + + /** + * Reads a string. + * @returns {string} + * @private + */ + ParserPrototype._readString = function() { + var value = "", + token, + delim; + do { + delim = this.tn.next(); + if (delim !== "'" && delim !== '"') + throw Error("illegal string delimiter: "+delim); + value += this.tn.next(); + this.tn.skip(delim); + token = this.tn.peek(); + } while (token === '"' || token === '"'); // multi line? + return value; + }; + + /** + * Reads a value. + * @param {boolean=} mayBeTypeRef + * @returns {number|boolean|string} + * @private + */ + ParserPrototype._readValue = function(mayBeTypeRef) { + var token = this.tn.peek(), + value; + if (token === '"' || token === "'") + return this._readString(); + this.tn.next(); + if (Lang.NUMBER.test(token)) + return mkNumber(token); + if (Lang.BOOL.test(token)) + return (token.toLowerCase() === 'true'); + if (mayBeTypeRef && Lang.TYPEREF.test(token)) + return token; + throw Error("illegal value: "+token); + + }; + + // ----- Parsing constructs ----- + + /** + * Parses a namespace option. + * @param {!Object} parent Parent definition + * @param {boolean=} isList + * @private + */ + ParserPrototype._parseOption = function(parent, isList) { + var token = this.tn.next(), + custom = false; + if (token === '(') { + custom = true; + token = this.tn.next(); + } + if (!Lang.TYPEREF.test(token)) + // we can allow options of the form google.protobuf.* since they will just get ignored anyways + // if (!/google\.protobuf\./.test(token)) // FIXME: Why should that not be a valid typeref? + throw Error("illegal option name: "+token); + var name = token; + if (custom) { // (my_method_option).foo, (my_method_option), some_method_option, (foo.my_option).bar + this.tn.skip(')'); + name = '('+name+')'; + token = this.tn.peek(); + if (Lang.FQTYPEREF.test(token)) { + name += token; + this.tn.next(); + } + } + this.tn.skip('='); + this._parseOptionValue(parent, name); + if (!isList) + this.tn.skip(";"); + }; + + /** + * Sets an option on the specified options object. + * @param {!Object.} options + * @param {string} name + * @param {string|number|boolean} value + * @inner + */ + function setOption(options, name, value) { + if (typeof options[name] === 'undefined') + options[name] = value; + else { + if (!Array.isArray(options[name])) + options[name] = [ options[name] ]; + options[name].push(value); + } + } + + /** + * Parses an option value. + * @param {!Object} parent + * @param {string} name + * @private + */ + ParserPrototype._parseOptionValue = function(parent, name) { + var token = this.tn.peek(); + if (token !== '{') { // Plain value + setOption(parent["options"], name, this._readValue(true)); + } else { // Aggregate options + this.tn.skip("{"); + while ((token = this.tn.next()) !== '}') { + if (!Lang.NAME.test(token)) + throw Error("illegal option name: " + name + "." + token); + if (this.tn.omit(":")) + setOption(parent["options"], name + "." + token, this._readValue(true)); + else + this._parseOptionValue(parent, name + "." + token); + } + } + }; + + /** + * Parses a service definition. + * @param {!Object} parent Parent definition + * @private + */ + ParserPrototype._parseService = function(parent) { + var token = this.tn.next(); + if (!Lang.NAME.test(token)) + throw Error("illegal service name at line "+this.tn.line+": "+token); + var name = token; + var svc = { + "name": name, + "rpc": {}, + "options": {} + }; + this.tn.skip("{"); + while ((token = this.tn.next()) !== '}') { + if (token === "option") + this._parseOption(svc); + else if (token === 'rpc') + this._parseServiceRPC(svc); + else + throw Error("illegal service token: "+token); + } + this.tn.omit(";"); + parent["services"].push(svc); + }; + + /** + * Parses a RPC service definition of the form ['rpc', name, (request), 'returns', (response)]. + * @param {!Object} svc Service definition + * @private + */ + ParserPrototype._parseServiceRPC = function(svc) { + var type = "rpc", + token = this.tn.next(); + if (!Lang.NAME.test(token)) + throw Error("illegal rpc service method name: "+token); + var name = token; + var method = { + "request": null, + "response": null, + "request_stream": false, + "response_stream": false, + "options": {} + }; + this.tn.skip("("); + token = this.tn.next(); + if (token.toLowerCase() === "stream") { + method["request_stream"] = true; + token = this.tn.next(); + } + if (!Lang.TYPEREF.test(token)) + throw Error("illegal rpc service request type: "+token); + method["request"] = token; + this.tn.skip(")"); + token = this.tn.next(); + if (token.toLowerCase() !== "returns") + throw Error("illegal rpc service request type delimiter: "+token); + this.tn.skip("("); + token = this.tn.next(); + if (token.toLowerCase() === "stream") { + method["response_stream"] = true; + token = this.tn.next(); + } + method["response"] = token; + this.tn.skip(")"); + token = this.tn.peek(); + if (token === '{') { + this.tn.next(); + while ((token = this.tn.next()) !== '}') { + if (token === 'option') + this._parseOption(method); + else + throw Error("illegal rpc service token: " + token); + } + this.tn.omit(";"); + } else + this.tn.skip(";"); + if (typeof svc[type] === 'undefined') + svc[type] = {}; + svc[type][name] = method; + }; + + /** + * Parses a message definition. + * @param {!Object} parent Parent definition + * @param {!Object=} fld Field definition if this is a group + * @returns {!Object} + * @private + */ + ParserPrototype._parseMessage = function(parent, fld) { + var isGroup = !!fld, + token = this.tn.next(); + var msg = { + "name": "", + "fields": [], + "enums": [], + "messages": [], + "options": {}, + "services": [], + "oneofs": {} + // "extensions": undefined + }; + if (!Lang.NAME.test(token)) + throw Error("illegal "+(isGroup ? "group" : "message")+" name: "+token); + msg["name"] = token; + if (isGroup) { + this.tn.skip("="); + fld["id"] = mkId(this.tn.next()); + msg["isGroup"] = true; + } + token = this.tn.peek(); + if (token === '[' && fld) + this._parseFieldOptions(fld); + this.tn.skip("{"); + while ((token = this.tn.next()) !== '}') { + if (Lang.RULE.test(token)) + this._parseMessageField(msg, token); + else if (token === "oneof") + this._parseMessageOneOf(msg); + else if (token === "enum") + this._parseEnum(msg); + else if (token === "message") + this._parseMessage(msg); + else if (token === "option") + this._parseOption(msg); + else if (token === "service") + this._parseService(msg); + else if (token === "extensions") + msg["extensions"] = this._parseExtensionRanges(); + else if (token === "reserved") + this._parseIgnored(); // TODO + else if (token === "extend") + this._parseExtend(msg); + else if (Lang.TYPEREF.test(token)) { + if (!this.proto3) + throw Error("illegal field rule: "+token); + this._parseMessageField(msg, "optional", token); + } else + throw Error("illegal message token: "+token); + } + this.tn.omit(";"); + parent["messages"].push(msg); + return msg; + }; + + /** + * Parses an ignored statement. + * @private + */ + ParserPrototype._parseIgnored = function() { + while (this.tn.peek() !== ';') + this.tn.next(); + this.tn.skip(";"); + }; + + /** + * Parses a message field. + * @param {!Object} msg Message definition + * @param {string} rule Field rule + * @param {string=} type Field type if already known (never known for maps) + * @returns {!Object} Field descriptor + * @private + */ + ParserPrototype._parseMessageField = function(msg, rule, type) { + if (!Lang.RULE.test(rule)) + throw Error("illegal message field rule: "+rule); + var fld = { + "rule": rule, + "type": "", + "name": "", + "options": {}, + "id": 0 + }; + var token; + if (rule === "map") { + + if (type) + throw Error("illegal type: " + type); + this.tn.skip('<'); + token = this.tn.next(); + if (!Lang.TYPE.test(token) && !Lang.TYPEREF.test(token)) + throw Error("illegal message field type: " + token); + fld["keytype"] = token; + this.tn.skip(','); + token = this.tn.next(); + if (!Lang.TYPE.test(token) && !Lang.TYPEREF.test(token)) + throw Error("illegal message field: " + token); + fld["type"] = token; + this.tn.skip('>'); + token = this.tn.next(); + if (!Lang.NAME.test(token)) + throw Error("illegal message field name: " + token); + fld["name"] = token; + this.tn.skip("="); + fld["id"] = mkId(this.tn.next()); + token = this.tn.peek(); + if (token === '[') + this._parseFieldOptions(fld); + this.tn.skip(";"); + + } else { + + type = typeof type !== 'undefined' ? type : this.tn.next(); + + if (type === "group") { + + // "A [legacy] group simply combines a nested message type and a field into a single declaration. In your + // code, you can treat this message just as if it had a Result type field called result (the latter name is + // converted to lower-case so that it does not conflict with the former)." + var grp = this._parseMessage(msg, fld); + if (!/^[A-Z]/.test(grp["name"])) + throw Error('illegal group name: '+grp["name"]); + fld["type"] = grp["name"]; + fld["name"] = grp["name"].toLowerCase(); + this.tn.omit(";"); + + } else { + + if (!Lang.TYPE.test(type) && !Lang.TYPEREF.test(type)) + throw Error("illegal message field type: " + type); + fld["type"] = type; + token = this.tn.next(); + if (!Lang.NAME.test(token)) + throw Error("illegal message field name: " + token); + fld["name"] = token; + this.tn.skip("="); + fld["id"] = mkId(this.tn.next()); + token = this.tn.peek(); + if (token === "[") + this._parseFieldOptions(fld); + this.tn.skip(";"); + + } + } + msg["fields"].push(fld); + return fld; + }; + + /** + * Parses a message oneof. + * @param {!Object} msg Message definition + * @private + */ + ParserPrototype._parseMessageOneOf = function(msg) { + var token = this.tn.next(); + if (!Lang.NAME.test(token)) + throw Error("illegal oneof name: "+token); + var name = token, + fld; + var fields = []; + this.tn.skip("{"); + while ((token = this.tn.next()) !== "}") { + fld = this._parseMessageField(msg, "optional", token); + fld["oneof"] = name; + fields.push(fld["id"]); + } + this.tn.omit(";"); + msg["oneofs"][name] = fields; + }; + + /** + * Parses a set of field option definitions. + * @param {!Object} fld Field definition + * @private + */ + ParserPrototype._parseFieldOptions = function(fld) { + this.tn.skip("["); + var token, + first = true; + while ((token = this.tn.peek()) !== ']') { + if (!first) + this.tn.skip(","); + this._parseOption(fld, true); + first = false; + } + this.tn.next(); + }; + + /** + * Parses an enum. + * @param {!Object} msg Message definition + * @private + */ + ParserPrototype._parseEnum = function(msg) { + var enm = { + "name": "", + "values": [], + "options": {} + }; + var token = this.tn.next(); + if (!Lang.NAME.test(token)) + throw Error("illegal name: "+token); + enm["name"] = token; + this.tn.skip("{"); + while ((token = this.tn.next()) !== '}') { + if (token === "option") + this._parseOption(enm); + else { + if (!Lang.NAME.test(token)) + throw Error("illegal name: "+token); + this.tn.skip("="); + var val = { + "name": token, + "id": mkId(this.tn.next(), true) + }; + token = this.tn.peek(); + if (token === "[") + this._parseFieldOptions({ "options": {} }); + this.tn.skip(";"); + enm["values"].push(val); + } + } + this.tn.omit(";"); + msg["enums"].push(enm); + }; + + /** + * Parses extension / reserved ranges. + * @returns {!Array.>} + * @private + */ + ParserPrototype._parseExtensionRanges = function() { + var ranges = []; + var token, + range, + value; + do { + range = []; + while (true) { + token = this.tn.next(); + switch (token) { + case "min": + value = ProtoBuf.ID_MIN; + break; + case "max": + value = ProtoBuf.ID_MAX; + break; + default: + value = mkNumber(token); + break; + } + range.push(value); + if (range.length === 2) + break; + if (this.tn.peek() !== "to") { + range.push(value); + break; + } + this.tn.next(); + } + ranges.push(range); + } while (this.tn.omit(",")); + this.tn.skip(";"); + return ranges; + }; + + /** + * Parses an extend block. + * @param {!Object} parent Parent object + * @private + */ + ParserPrototype._parseExtend = function(parent) { + var token = this.tn.next(); + if (!Lang.TYPEREF.test(token)) + throw Error("illegal extend reference: "+token); + var ext = { + "ref": token, + "fields": [] + }; + this.tn.skip("{"); + while ((token = this.tn.next()) !== '}') { + if (Lang.RULE.test(token)) + this._parseMessageField(ext, token); + else if (Lang.TYPEREF.test(token)) { + if (!this.proto3) + throw Error("illegal field rule: "+token); + this._parseMessageField(ext, "optional", token); + } else + throw Error("illegal extend token: "+token); + } + this.tn.omit(";"); + parent["messages"].push(ext); + return ext; + }; + + // ----- General ----- + + /** + * Returns a string representation of this parser. + * @returns {string} + */ + ParserPrototype.toString = function() { + return "Parser at line "+this.tn.line; + }; + + /** + * @alias ProtoBuf.DotProto.Parser + * @expose + */ + DotProto.Parser = Parser; + + return DotProto; + + })(ProtoBuf, ProtoBuf.Lang); + + /** + * @alias ProtoBuf.Reflect + * @expose + */ + ProtoBuf.Reflect = (function(ProtoBuf) { + "use strict"; + + /** + * Reflection types. + * @exports ProtoBuf.Reflect + * @namespace + */ + var Reflect = {}; + + /** + * Constructs a Reflect base class. + * @exports ProtoBuf.Reflect.T + * @constructor + * @abstract + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {?ProtoBuf.Reflect.T} parent Parent object + * @param {string} name Object name + */ + var T = function(builder, parent, name) { + + /** + * Builder reference. + * @type {!ProtoBuf.Builder} + * @expose + */ + this.builder = builder; + + /** + * Parent object. + * @type {?ProtoBuf.Reflect.T} + * @expose + */ + this.parent = parent; + + /** + * Object name in namespace. + * @type {string} + * @expose + */ + this.name = name; + + /** + * Fully qualified class name + * @type {string} + * @expose + */ + this.className; + }; + + /** + * @alias ProtoBuf.Reflect.T.prototype + * @inner + */ + var TPrototype = T.prototype; + + /** + * Returns the fully qualified name of this object. + * @returns {string} Fully qualified name as of ".PATH.TO.THIS" + * @expose + */ + TPrototype.fqn = function() { + var name = this.name, + ptr = this; + do { + ptr = ptr.parent; + if (ptr == null) + break; + name = ptr.name+"."+name; + } while (true); + return name; + }; + + /** + * Returns a string representation of this Reflect object (its fully qualified name). + * @param {boolean=} includeClass Set to true to include the class name. Defaults to false. + * @return String representation + * @expose + */ + TPrototype.toString = function(includeClass) { + return (includeClass ? this.className + " " : "") + this.fqn(); + }; + + /** + * Builds this type. + * @throws {Error} If this type cannot be built directly + * @expose + */ + TPrototype.build = function() { + throw Error(this.toString(true)+" cannot be built directly"); + }; + + /** + * @alias ProtoBuf.Reflect.T + * @expose + */ + Reflect.T = T; + + /** + * Constructs a new Namespace. + * @exports ProtoBuf.Reflect.Namespace + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {?ProtoBuf.Reflect.Namespace} parent Namespace parent + * @param {string} name Namespace name + * @param {Object.=} options Namespace options + * @param {string?} syntax The syntax level of this definition (e.g., proto3) + * @constructor + * @extends ProtoBuf.Reflect.T + */ + var Namespace = function(builder, parent, name, options, syntax) { + T.call(this, builder, parent, name); + + /** + * @override + */ + this.className = "Namespace"; + + /** + * Children inside the namespace. + * @type {!Array.} + */ + this.children = []; + + /** + * Options. + * @type {!Object.} + */ + this.options = options || {}; + + /** + * Syntax level (e.g., proto2 or proto3). + * @type {!string} + */ + this.syntax = syntax || "proto2"; + }; + + /** + * @alias ProtoBuf.Reflect.Namespace.prototype + * @inner + */ + var NamespacePrototype = Namespace.prototype = Object.create(T.prototype); + + /** + * Returns an array of the namespace's children. + * @param {ProtoBuf.Reflect.T=} type Filter type (returns instances of this type only). Defaults to null (all children). + * @return {Array.} + * @expose + */ + NamespacePrototype.getChildren = function(type) { + type = type || null; + if (type == null) + return this.children.slice(); + var children = []; + for (var i=0, k=this.children.length; i} qn Qualified name to resolve + * @param {boolean=} excludeNonNamespace Excludes non-namespace types, defaults to `false` + * @return {?ProtoBuf.Reflect.Namespace} The resolved type or null if not found + * @expose + */ + NamespacePrototype.resolve = function(qn, excludeNonNamespace) { + var part = typeof qn === 'string' ? qn.split(".") : qn, + ptr = this, + i = 0; + if (part[i] === "") { // Fully qualified name, e.g. ".My.Message' + while (ptr.parent !== null) + ptr = ptr.parent; + i++; + } + var child; + do { + do { + if (!(ptr instanceof Reflect.Namespace)) { + ptr = null; + break; + } + child = ptr.getChild(part[i]); + if (!child || !(child instanceof Reflect.T) || (excludeNonNamespace && !(child instanceof Reflect.Namespace))) { + ptr = null; + break; + } + ptr = child; i++; + } while (i < part.length); + if (ptr != null) + break; // Found + // Else search the parent + if (this.parent !== null) + return this.parent.resolve(qn, excludeNonNamespace); + } while (ptr != null); + return ptr; + }; + + /** + * Determines the shortest qualified name of the specified type, if any, relative to this namespace. + * @param {!ProtoBuf.Reflect.T} t Reflection type + * @returns {string} The shortest qualified name or, if there is none, the fqn + * @expose + */ + NamespacePrototype.qn = function(t) { + var part = [], ptr = t; + do { + part.unshift(ptr.name); + ptr = ptr.parent; + } while (ptr !== null); + for (var len=1; len <= part.length; len++) { + var qn = part.slice(part.length-len); + if (t === this.resolve(qn, t instanceof Reflect.Namespace)) + return qn.join("."); + } + return t.fqn(); + }; + + /** + * Builds the namespace and returns the runtime counterpart. + * @return {Object.} Runtime namespace + * @expose + */ + NamespacePrototype.build = function() { + /** @dict */ + var ns = {}; + var children = this.children; + for (var i=0, k=children.length, child; i} + */ + NamespacePrototype.buildOpt = function() { + var opt = {}, + keys = Object.keys(this.options); + for (var i=0, k=keys.length; i}null} Option value or NULL if there is no such option + */ + NamespacePrototype.getOption = function(name) { + if (typeof name === 'undefined') + return this.options; + return typeof this.options[name] !== 'undefined' ? this.options[name] : null; + }; + + /** + * @alias ProtoBuf.Reflect.Namespace + * @expose + */ + Reflect.Namespace = Namespace; + + /** + * Constructs a new Element implementation that checks and converts values for a + * particular field type, as appropriate. + * + * An Element represents a single value: either the value of a singular field, + * or a value contained in one entry of a repeated field or map field. This + * class does not implement these higher-level concepts; it only encapsulates + * the low-level typechecking and conversion. + * + * @exports ProtoBuf.Reflect.Element + * @param {{name: string, wireType: number}} type Resolved data type + * @param {ProtoBuf.Reflect.T|null} resolvedType Resolved type, if relevant + * (e.g. submessage field). + * @param {boolean} isMapKey Is this element a Map key? The value will be + * converted to string form if so. + * @param {string} syntax Syntax level of defining message type, e.g., + * proto2 or proto3. + * @constructor + */ + var Element = function(type, resolvedType, isMapKey, syntax) { + + /** + * Element type, as a string (e.g., int32). + * @type {{name: string, wireType: number}} + */ + this.type = type; + + /** + * Element type reference to submessage or enum definition, if needed. + * @type {ProtoBuf.Reflect.T|null} + */ + this.resolvedType = resolvedType; + + /** + * Element is a map key. + * @type {boolean} + */ + this.isMapKey = isMapKey; + + /** + * Syntax level of defining message type, e.g., proto2 or proto3. + * @type {string} + */ + this.syntax = syntax; + + if (isMapKey && ProtoBuf.MAP_KEY_TYPES.indexOf(type) < 0) + throw Error("Invalid map key type: " + type.name); + }; + + var ElementPrototype = Element.prototype; + + /** + * Obtains a (new) default value for the specified type. + * @param type {string|{name: string, wireType: number}} Field type + * @returns {*} Default value + * @inner + */ + function mkDefault(type) { + if (typeof type === 'string') + type = ProtoBuf.TYPES[type]; + if (typeof type.defaultValue === 'undefined') + throw Error("default value for type "+type.name+" is not supported"); + if (type == ProtoBuf.TYPES["bytes"]) + return new ByteBuffer(0); + return type.defaultValue; + } + + /** + * Returns the default value for this field in proto3. + * @function + * @param type {string|{name: string, wireType: number}} the field type + * @returns {*} Default value + */ + Element.defaultFieldValue = mkDefault; + + /** + * Makes a Long from a value. + * @param {{low: number, high: number, unsigned: boolean}|string|number} value Value + * @param {boolean=} unsigned Whether unsigned or not, defaults to reuse it from Long-like objects or to signed for + * strings and numbers + * @returns {!Long} + * @throws {Error} If the value cannot be converted to a Long + * @inner + */ + function mkLong(value, unsigned) { + if (value && typeof value.low === 'number' && typeof value.high === 'number' && typeof value.unsigned === 'boolean' + && value.low === value.low && value.high === value.high) + return new ProtoBuf.Long(value.low, value.high, typeof unsigned === 'undefined' ? value.unsigned : unsigned); + if (typeof value === 'string') + return ProtoBuf.Long.fromString(value, unsigned || false, 10); + if (typeof value === 'number') + return ProtoBuf.Long.fromNumber(value, unsigned || false); + throw Error("not convertible to Long"); + } + + /** + * Checks if the given value can be set for an element of this type (singular + * field or one element of a repeated field or map). + * @param {*} value Value to check + * @return {*} Verified, maybe adjusted, value + * @throws {Error} If the value cannot be verified for this element slot + * @expose + */ + ElementPrototype.verifyValue = function(value) { + var fail = function(val, msg) { + throw Error("Illegal value for "+this.toString(true)+" of type "+this.type.name+": "+val+" ("+msg+")"); + }.bind(this); + switch (this.type) { + // Signed 32bit + case ProtoBuf.TYPES["int32"]: + case ProtoBuf.TYPES["sint32"]: + case ProtoBuf.TYPES["sfixed32"]: + // Account for !NaN: value === value + if (typeof value !== 'number' || (value === value && value % 1 !== 0)) + fail(typeof value, "not an integer"); + return value > 4294967295 ? value | 0 : value; + + // Unsigned 32bit + case ProtoBuf.TYPES["uint32"]: + case ProtoBuf.TYPES["fixed32"]: + if (typeof value !== 'number' || (value === value && value % 1 !== 0)) + fail(typeof value, "not an integer"); + return value < 0 ? value >>> 0 : value; + + // Signed 64bit + case ProtoBuf.TYPES["int64"]: + case ProtoBuf.TYPES["sint64"]: + case ProtoBuf.TYPES["sfixed64"]: { + if (ProtoBuf.Long) + try { + return mkLong(value, false); + } catch (e) { + fail(typeof value, e.message); + } + else + fail(typeof value, "requires Long.js"); + } + + // Unsigned 64bit + case ProtoBuf.TYPES["uint64"]: + case ProtoBuf.TYPES["fixed64"]: { + if (ProtoBuf.Long) + try { + return mkLong(value, true); + } catch (e) { + fail(typeof value, e.message); + } + else + fail(typeof value, "requires Long.js"); + } + + // Bool + case ProtoBuf.TYPES["bool"]: + if (typeof value !== 'boolean') + fail(typeof value, "not a boolean"); + return value; + + // Float + case ProtoBuf.TYPES["float"]: + case ProtoBuf.TYPES["double"]: + if (typeof value !== 'number') + fail(typeof value, "not a number"); + return value; + + // Length-delimited string + case ProtoBuf.TYPES["string"]: + if (typeof value !== 'string' && !(value && value instanceof String)) + fail(typeof value, "not a string"); + return ""+value; // Convert String object to string + + // Length-delimited bytes + case ProtoBuf.TYPES["bytes"]: + if (ByteBuffer.isByteBuffer(value)) + return value; + return ByteBuffer.wrap(value, "base64"); + + // Constant enum value + case ProtoBuf.TYPES["enum"]: { + var values = this.resolvedType.getChildren(ProtoBuf.Reflect.Enum.Value); + for (i=0; i 4294967295 || value < 0) + fail(typeof value, "not in range for uint32") + return value; + } else { + // proto2 requires enum values to be valid. + fail(value, "not a valid enum value"); + } + } + // Embedded message + case ProtoBuf.TYPES["group"]: + case ProtoBuf.TYPES["message"]: { + if (!value || typeof value !== 'object') + fail(typeof value, "object expected"); + if (value instanceof this.resolvedType.clazz) + return value; + if (value instanceof ProtoBuf.Builder.Message) { + // Mismatched type: Convert to object (see: https://github.com/dcodeIO/ProtoBuf.js/issues/180) + var obj = {}; + for (var i in value) + if (value.hasOwnProperty(i)) + obj[i] = value[i]; + value = obj; + } + // Else let's try to construct one from a key-value object + return new (this.resolvedType.clazz)(value); // May throw for a hundred of reasons + } + } + + // We should never end here + throw Error("[INTERNAL] Illegal value for "+this.toString(true)+": "+value+" (undefined type "+this.type+")"); + }; + + /** + * Calculates the byte length of an element on the wire. + * @param {number} id Field number + * @param {*} value Field value + * @returns {number} Byte length + * @throws {Error} If the value cannot be calculated + * @expose + */ + ElementPrototype.calculateLength = function(id, value) { + if (value === null) return 0; // Nothing to encode + // Tag has already been written + var n; + switch (this.type) { + case ProtoBuf.TYPES["int32"]: + return value < 0 ? ByteBuffer.calculateVarint64(value) : ByteBuffer.calculateVarint32(value); + case ProtoBuf.TYPES["uint32"]: + return ByteBuffer.calculateVarint32(value); + case ProtoBuf.TYPES["sint32"]: + return ByteBuffer.calculateVarint32(ByteBuffer.zigZagEncode32(value)); + case ProtoBuf.TYPES["fixed32"]: + case ProtoBuf.TYPES["sfixed32"]: + case ProtoBuf.TYPES["float"]: + return 4; + case ProtoBuf.TYPES["int64"]: + case ProtoBuf.TYPES["uint64"]: + return ByteBuffer.calculateVarint64(value); + case ProtoBuf.TYPES["sint64"]: + return ByteBuffer.calculateVarint64(ByteBuffer.zigZagEncode64(value)); + case ProtoBuf.TYPES["fixed64"]: + case ProtoBuf.TYPES["sfixed64"]: + return 8; + case ProtoBuf.TYPES["bool"]: + return 1; + case ProtoBuf.TYPES["enum"]: + return ByteBuffer.calculateVarint32(value); + case ProtoBuf.TYPES["double"]: + return 8; + case ProtoBuf.TYPES["string"]: + n = ByteBuffer.calculateUTF8Bytes(value); + return ByteBuffer.calculateVarint32(n) + n; + case ProtoBuf.TYPES["bytes"]: + if (value.remaining() < 0) + throw Error("Illegal value for "+this.toString(true)+": "+value.remaining()+" bytes remaining"); + return ByteBuffer.calculateVarint32(value.remaining()) + value.remaining(); + case ProtoBuf.TYPES["message"]: + n = this.resolvedType.calculate(value); + return ByteBuffer.calculateVarint32(n) + n; + case ProtoBuf.TYPES["group"]: + n = this.resolvedType.calculate(value); + return n + ByteBuffer.calculateVarint32((id << 3) | ProtoBuf.WIRE_TYPES.ENDGROUP); + } + // We should never end here + throw Error("[INTERNAL] Illegal value to encode in "+this.toString(true)+": "+value+" (unknown type)"); + }; + + /** + * Encodes a value to the specified buffer. Does not encode the key. + * @param {number} id Field number + * @param {*} value Field value + * @param {ByteBuffer} buffer ByteBuffer to encode to + * @return {ByteBuffer} The ByteBuffer for chaining + * @throws {Error} If the value cannot be encoded + * @expose + */ + ElementPrototype.encodeValue = function(id, value, buffer) { + if (value === null) return buffer; // Nothing to encode + // Tag has already been written + + switch (this.type) { + // 32bit signed varint + case ProtoBuf.TYPES["int32"]: + // "If you use int32 or int64 as the type for a negative number, the resulting varint is always ten bytes + // long – it is, effectively, treated like a very large unsigned integer." (see #122) + if (value < 0) + buffer.writeVarint64(value); + else + buffer.writeVarint32(value); + break; + + // 32bit unsigned varint + case ProtoBuf.TYPES["uint32"]: + buffer.writeVarint32(value); + break; + + // 32bit varint zig-zag + case ProtoBuf.TYPES["sint32"]: + buffer.writeVarint32ZigZag(value); + break; + + // Fixed unsigned 32bit + case ProtoBuf.TYPES["fixed32"]: + buffer.writeUint32(value); + break; + + // Fixed signed 32bit + case ProtoBuf.TYPES["sfixed32"]: + buffer.writeInt32(value); + break; + + // 64bit varint as-is + case ProtoBuf.TYPES["int64"]: + case ProtoBuf.TYPES["uint64"]: + buffer.writeVarint64(value); // throws + break; + + // 64bit varint zig-zag + case ProtoBuf.TYPES["sint64"]: + buffer.writeVarint64ZigZag(value); // throws + break; + + // Fixed unsigned 64bit + case ProtoBuf.TYPES["fixed64"]: + buffer.writeUint64(value); // throws + break; + + // Fixed signed 64bit + case ProtoBuf.TYPES["sfixed64"]: + buffer.writeInt64(value); // throws + break; + + // Bool + case ProtoBuf.TYPES["bool"]: + if (typeof value === 'string') + buffer.writeVarint32(value.toLowerCase() === 'false' ? 0 : !!value); + else + buffer.writeVarint32(value ? 1 : 0); + break; + + // Constant enum value + case ProtoBuf.TYPES["enum"]: + buffer.writeVarint32(value); + break; + + // 32bit float + case ProtoBuf.TYPES["float"]: + buffer.writeFloat32(value); + break; + + // 64bit float + case ProtoBuf.TYPES["double"]: + buffer.writeFloat64(value); + break; + + // Length-delimited string + case ProtoBuf.TYPES["string"]: + buffer.writeVString(value); + break; + + // Length-delimited bytes + case ProtoBuf.TYPES["bytes"]: + if (value.remaining() < 0) + throw Error("Illegal value for "+this.toString(true)+": "+value.remaining()+" bytes remaining"); + var prevOffset = value.offset; + buffer.writeVarint32(value.remaining()); + buffer.append(value); + value.offset = prevOffset; + break; + + // Embedded message + case ProtoBuf.TYPES["message"]: + var bb = new ByteBuffer().LE(); + this.resolvedType.encode(value, bb); + buffer.writeVarint32(bb.offset); + buffer.append(bb.flip()); + break; + + // Legacy group + case ProtoBuf.TYPES["group"]: + this.resolvedType.encode(value, buffer); + buffer.writeVarint32((id << 3) | ProtoBuf.WIRE_TYPES.ENDGROUP); + break; + + default: + // We should never end here + throw Error("[INTERNAL] Illegal value to encode in "+this.toString(true)+": "+value+" (unknown type)"); + } + return buffer; + }; + + /** + * Decode one element value from the specified buffer. + * @param {ByteBuffer} buffer ByteBuffer to decode from + * @param {number} wireType The field wire type + * @param {number} id The field number + * @return {*} Decoded value + * @throws {Error} If the field cannot be decoded + * @expose + */ + ElementPrototype.decode = function(buffer, wireType, id) { + if (wireType != this.type.wireType) + throw Error("Unexpected wire type for element"); + + var value, nBytes; + switch (this.type) { + // 32bit signed varint + case ProtoBuf.TYPES["int32"]: + return buffer.readVarint32() | 0; + + // 32bit unsigned varint + case ProtoBuf.TYPES["uint32"]: + return buffer.readVarint32() >>> 0; + + // 32bit signed varint zig-zag + case ProtoBuf.TYPES["sint32"]: + return buffer.readVarint32ZigZag() | 0; + + // Fixed 32bit unsigned + case ProtoBuf.TYPES["fixed32"]: + return buffer.readUint32() >>> 0; + + case ProtoBuf.TYPES["sfixed32"]: + return buffer.readInt32() | 0; + + // 64bit signed varint + case ProtoBuf.TYPES["int64"]: + return buffer.readVarint64(); + + // 64bit unsigned varint + case ProtoBuf.TYPES["uint64"]: + return buffer.readVarint64().toUnsigned(); + + // 64bit signed varint zig-zag + case ProtoBuf.TYPES["sint64"]: + return buffer.readVarint64ZigZag(); + + // Fixed 64bit unsigned + case ProtoBuf.TYPES["fixed64"]: + return buffer.readUint64(); + + // Fixed 64bit signed + case ProtoBuf.TYPES["sfixed64"]: + return buffer.readInt64(); + + // Bool varint + case ProtoBuf.TYPES["bool"]: + return !!buffer.readVarint32(); + + // Constant enum value (varint) + case ProtoBuf.TYPES["enum"]: + // The following Builder.Message#set will already throw + return buffer.readVarint32(); + + // 32bit float + case ProtoBuf.TYPES["float"]: + return buffer.readFloat(); + + // 64bit float + case ProtoBuf.TYPES["double"]: + return buffer.readDouble(); + + // Length-delimited string + case ProtoBuf.TYPES["string"]: + return buffer.readVString(); + + // Length-delimited bytes + case ProtoBuf.TYPES["bytes"]: { + nBytes = buffer.readVarint32(); + if (buffer.remaining() < nBytes) + throw Error("Illegal number of bytes for "+this.toString(true)+": "+nBytes+" required but got only "+buffer.remaining()); + value = buffer.clone(); // Offset already set + value.limit = value.offset+nBytes; + buffer.offset += nBytes; + return value; + } + + // Length-delimited embedded message + case ProtoBuf.TYPES["message"]: { + nBytes = buffer.readVarint32(); + return this.resolvedType.decode(buffer, nBytes); + } + + // Legacy group + case ProtoBuf.TYPES["group"]: + return this.resolvedType.decode(buffer, -1, id); + } + + // We should never end here + throw Error("[INTERNAL] Illegal decode type"); + }; + + /** + * Converts a value from a string to the canonical element type. + * + * Legal only when isMapKey is true. + * + * @param {string} str The string value + * @returns {*} The value + */ + ElementPrototype.valueFromString = function(str) { + if (!this.isMapKey) { + throw Error("valueFromString() called on non-map-key element"); + } + + switch (this.type) { + case ProtoBuf.TYPES["int32"]: + case ProtoBuf.TYPES["sint32"]: + case ProtoBuf.TYPES["sfixed32"]: + case ProtoBuf.TYPES["uint32"]: + case ProtoBuf.TYPES["fixed32"]: + return this.verifyValue(parseInt(str)); + + case ProtoBuf.TYPES["int64"]: + case ProtoBuf.TYPES["sint64"]: + case ProtoBuf.TYPES["sfixed64"]: + case ProtoBuf.TYPES["uint64"]: + case ProtoBuf.TYPES["fixed64"]: + // Long-based fields support conversions from string already. + return this.verifyValue(str); + + case ProtoBuf.TYPES["bool"]: + return str === "true"; + + case ProtoBuf.TYPES["string"]: + return this.verifyValue(str); + + case ProtoBuf.TYPES["bytes"]: + return ByteBuffer.fromBinary(str); + } + }; + + /** + * Converts a value from the canonical element type to a string. + * + * It should be the case that `valueFromString(valueToString(val))` returns + * a value equivalent to `verifyValue(val)` for every legal value of `val` + * according to this element type. + * + * This may be used when the element must be stored or used as a string, + * e.g., as a map key on an Object. + * + * Legal only when isMapKey is true. + * + * @param {*} val The value + * @returns {string} The string form of the value. + */ + ElementPrototype.valueToString = function(value) { + if (!this.isMapKey) { + throw Error("valueToString() called on non-map-key element"); + } + + if (this.type === ProtoBuf.TYPES["bytes"]) { + return value.toString("binary"); + } else { + return value.toString(); + } + }; + + /** + * @alias ProtoBuf.Reflect.Element + * @expose + */ + Reflect.Element = Element; + + /** + * Constructs a new Message. + * @exports ProtoBuf.Reflect.Message + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {!ProtoBuf.Reflect.Namespace} parent Parent message or namespace + * @param {string} name Message name + * @param {Object.=} options Message options + * @param {boolean=} isGroup `true` if this is a legacy group + * @param {string?} syntax The syntax level of this definition (e.g., proto3) + * @constructor + * @extends ProtoBuf.Reflect.Namespace + */ + var Message = function(builder, parent, name, options, isGroup, syntax) { + Namespace.call(this, builder, parent, name, options, syntax); + + /** + * @override + */ + this.className = "Message"; + + /** + * Extensions range. + * @type {!Array.|undefined} + * @expose + */ + this.extensions = undefined; + + /** + * Runtime message class. + * @type {?function(new:ProtoBuf.Builder.Message)} + * @expose + */ + this.clazz = null; + + /** + * Whether this is a legacy group or not. + * @type {boolean} + * @expose + */ + this.isGroup = !!isGroup; + + // The following cached collections are used to efficiently iterate over or look up fields when decoding. + + /** + * Cached fields. + * @type {?Array.} + * @private + */ + this._fields = null; + + /** + * Cached fields by id. + * @type {?Object.} + * @private + */ + this._fieldsById = null; + + /** + * Cached fields by name. + * @type {?Object.} + * @private + */ + this._fieldsByName = null; + }; + + /** + * @alias ProtoBuf.Reflect.Message.prototype + * @inner + */ + var MessagePrototype = Message.prototype = Object.create(Namespace.prototype); + + /** + * Builds the message and returns the runtime counterpart, which is a fully functional class. + * @see ProtoBuf.Builder.Message + * @param {boolean=} rebuild Whether to rebuild or not, defaults to false + * @return {ProtoBuf.Reflect.Message} Message class + * @throws {Error} If the message cannot be built + * @expose + */ + MessagePrototype.build = function(rebuild) { + if (this.clazz && !rebuild) + return this.clazz; + + // Create the runtime Message class in its own scope + var clazz = (function(ProtoBuf, T) { + + var fields = T.getChildren(ProtoBuf.Reflect.Message.Field), + oneofs = T.getChildren(ProtoBuf.Reflect.Message.OneOf); + + /** + * Constructs a new runtime Message. + * @name ProtoBuf.Builder.Message + * @class Barebone of all runtime messages. + * @param {!Object.|string} values Preset values + * @param {...string} var_args + * @constructor + * @throws {Error} If the message cannot be created + */ + var Message = function(values, var_args) { + ProtoBuf.Builder.Message.call(this); + + // Create virtual oneof properties + for (var i=0, k=oneofs.length; i 0) { + var value; + // Set field values from a values object + if (arguments.length === 1 && values !== null && typeof values === 'object' && + /* not _another_ Message */ (typeof values.encode !== 'function' || values instanceof Message) && + /* not a repeated field */ !Array.isArray(values) && + /* not a Map */ !(values instanceof ProtoBuf.Map) && + /* not a ByteBuffer */ !ByteBuffer.isByteBuffer(values) && + /* not an ArrayBuffer */ !(values instanceof ArrayBuffer) && + /* not a Long */ !(ProtoBuf.Long && values instanceof ProtoBuf.Long)) { + this.$set(values); + } else // Set field values from arguments, in declaration order + for (i=0, k=arguments.length; i} keyOrObj String key or plain object holding multiple values + * @param {(*|boolean)=} value Value to set if key is a string, otherwise omitted + * @param {boolean=} noAssert Whether to not assert for an actual field / proper value type, defaults to `false` + * @returns {!ProtoBuf.Builder.Message} this + * @throws {Error} If the value cannot be set + * @expose + */ + MessagePrototype.set = function(keyOrObj, value, noAssert) { + if (keyOrObj && typeof keyOrObj === 'object') { + noAssert = value; + for (var ikey in keyOrObj) + if (keyOrObj.hasOwnProperty(ikey) && typeof (value = keyOrObj[ikey]) !== 'undefined') + this.$set(ikey, value, noAssert); + return this; + } + var field = T._fieldsByName[keyOrObj]; + if (!noAssert) { + if (!field) + throw Error(this+"#"+keyOrObj+" is not a field: undefined"); + if (!(field instanceof ProtoBuf.Reflect.Message.Field)) + throw Error(this+"#"+keyOrObj+" is not a field: "+field.toString(true)); + this[field.name] = (value = field.verifyValue(value)); // May throw + } else + this[keyOrObj] = value; + if (field && field.oneof) { // Field is part of an OneOf (not a virtual OneOf field) + var currentField = this[field.oneof.name]; // Virtual field references currently set field + if (value !== null) { + if (currentField !== null && currentField !== field.name) + this[currentField] = null; // Clear currently set field + this[field.oneof.name] = field.name; // Point virtual field at this field + } else if (/* value === null && */currentField === keyOrObj) + this[field.oneof.name] = null; // Clear virtual field (current field explicitly cleared) + } + return this; + }; + + /** + * Sets a field's value. This is an alias for [@link ProtoBuf.Builder.Message#set}. + * @name ProtoBuf.Builder.Message#$set + * @function + * @param {string|!Object.} keyOrObj String key or plain object holding multiple values + * @param {(*|boolean)=} value Value to set if key is a string, otherwise omitted + * @param {boolean=} noAssert Whether to not assert the value, defaults to `false` + * @throws {Error} If the value cannot be set + * @expose + */ + MessagePrototype.$set = MessagePrototype.set; + + /** + * Gets a field's value. + * @name ProtoBuf.Builder.Message#get + * @function + * @param {string} key Key + * @param {boolean=} noAssert Whether to not assert for an actual field, defaults to `false` + * @return {*} Value + * @throws {Error} If there is no such field + * @expose + */ + MessagePrototype.get = function(key, noAssert) { + if (noAssert) + return this[key]; + var field = T._fieldsByName[key]; + if (!field || !(field instanceof ProtoBuf.Reflect.Message.Field)) + throw Error(this+"#"+key+" is not a field: undefined"); + if (!(field instanceof ProtoBuf.Reflect.Message.Field)) + throw Error(this+"#"+key+" is not a field: "+field.toString(true)); + return this[field.name]; + }; + + /** + * Gets a field's value. This is an alias for {@link ProtoBuf.Builder.Message#$get}. + * @name ProtoBuf.Builder.Message#$get + * @function + * @param {string} key Key + * @return {*} Value + * @throws {Error} If there is no such field + * @expose + */ + MessagePrototype.$get = MessagePrototype.get; + + // Getters and setters + + for (var i=0; i} data Data payload + * @param {(!ByteBuffer|boolean)=} buffer ByteBuffer to encode to. Will create a new one and flip it if omitted. + * @param {boolean=} noVerify Whether to not verify field values, defaults to `false` + * @return {!ByteBuffer} Encoded message as a ByteBuffer + * @expose + */ + Message.encode = function(data, buffer, noVerify) { + return new Message(data).encode(buffer, noVerify); + }; + + /** + * Calculates the byte length of the message. + * @name ProtoBuf.Builder.Message#calculate + * @function + * @returns {number} Byte length + * @throws {Error} If the message cannot be calculated or if required fields are missing. + * @expose + */ + MessagePrototype.calculate = function() { + return T.calculate(this); + }; + + /** + * Encodes the varint32 length-delimited message. + * @name ProtoBuf.Builder.Message#encodeDelimited + * @function + * @param {(!ByteBuffer|boolean)=} buffer ByteBuffer to encode to. Will create a new one and flip it if omitted. + * @return {!ByteBuffer} Encoded message as a ByteBuffer + * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still + * returns the encoded ByteBuffer in the `encoded` property on the error. + * @expose + */ + MessagePrototype.encodeDelimited = function(buffer) { + var isNew = false; + if (!buffer) + buffer = new ByteBuffer(), + isNew = true; + var enc = new ByteBuffer().LE(); + T.encode(this, enc).flip(); + buffer.writeVarint32(enc.remaining()); + buffer.append(enc); + return isNew ? buffer.flip() : buffer; + }; + + /** + * Directly encodes the message to an ArrayBuffer. + * @name ProtoBuf.Builder.Message#encodeAB + * @function + * @return {ArrayBuffer} Encoded message as ArrayBuffer + * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still + * returns the encoded ArrayBuffer in the `encoded` property on the error. + * @expose + */ + MessagePrototype.encodeAB = function() { + try { + return this.encode().toArrayBuffer(); + } catch (e) { + if (e["encoded"]) e["encoded"] = e["encoded"].toArrayBuffer(); + throw(e); + } + }; + + /** + * Returns the message as an ArrayBuffer. This is an alias for {@link ProtoBuf.Builder.Message#encodeAB}. + * @name ProtoBuf.Builder.Message#toArrayBuffer + * @function + * @return {ArrayBuffer} Encoded message as ArrayBuffer + * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still + * returns the encoded ArrayBuffer in the `encoded` property on the error. + * @expose + */ + MessagePrototype.toArrayBuffer = MessagePrototype.encodeAB; + + /** + * Directly encodes the message to a node Buffer. + * @name ProtoBuf.Builder.Message#encodeNB + * @function + * @return {!Buffer} + * @throws {Error} If the message cannot be encoded, not running under node.js or if required fields are + * missing. The later still returns the encoded node Buffer in the `encoded` property on the error. + * @expose + */ + MessagePrototype.encodeNB = function() { + try { + return this.encode().toBuffer(); + } catch (e) { + if (e["encoded"]) e["encoded"] = e["encoded"].toBuffer(); + throw(e); + } + }; + + /** + * Returns the message as a node Buffer. This is an alias for {@link ProtoBuf.Builder.Message#encodeNB}. + * @name ProtoBuf.Builder.Message#toBuffer + * @function + * @return {!Buffer} + * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still + * returns the encoded node Buffer in the `encoded` property on the error. + * @expose + */ + MessagePrototype.toBuffer = MessagePrototype.encodeNB; + + /** + * Directly encodes the message to a base64 encoded string. + * @name ProtoBuf.Builder.Message#encode64 + * @function + * @return {string} Base64 encoded string + * @throws {Error} If the underlying buffer cannot be encoded or if required fields are missing. The later + * still returns the encoded base64 string in the `encoded` property on the error. + * @expose + */ + MessagePrototype.encode64 = function() { + try { + return this.encode().toBase64(); + } catch (e) { + if (e["encoded"]) e["encoded"] = e["encoded"].toBase64(); + throw(e); + } + }; + + /** + * Returns the message as a base64 encoded string. This is an alias for {@link ProtoBuf.Builder.Message#encode64}. + * @name ProtoBuf.Builder.Message#toBase64 + * @function + * @return {string} Base64 encoded string + * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still + * returns the encoded base64 string in the `encoded` property on the error. + * @expose + */ + MessagePrototype.toBase64 = MessagePrototype.encode64; + + /** + * Directly encodes the message to a hex encoded string. + * @name ProtoBuf.Builder.Message#encodeHex + * @function + * @return {string} Hex encoded string + * @throws {Error} If the underlying buffer cannot be encoded or if required fields are missing. The later + * still returns the encoded hex string in the `encoded` property on the error. + * @expose + */ + MessagePrototype.encodeHex = function() { + try { + return this.encode().toHex(); + } catch (e) { + if (e["encoded"]) e["encoded"] = e["encoded"].toHex(); + throw(e); + } + }; + + /** + * Returns the message as a hex encoded string. This is an alias for {@link ProtoBuf.Builder.Message#encodeHex}. + * @name ProtoBuf.Builder.Message#toHex + * @function + * @return {string} Hex encoded string + * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still + * returns the encoded hex string in the `encoded` property on the error. + * @expose + */ + MessagePrototype.toHex = MessagePrototype.encodeHex; + + /** + * Clones a message object or field value to a raw object. + * @param {*} obj Object to clone + * @param {boolean} binaryAsBase64 Whether to include binary data as base64 strings or as a buffer otherwise + * @param {boolean} longsAsStrings Whether to encode longs as strings + * @param {!ProtoBuf.Reflect.T=} resolvedType The resolved field type if a field + * @returns {*} Cloned object + * @inner + */ + function cloneRaw(obj, binaryAsBase64, longsAsStrings, resolvedType) { + if (obj === null || typeof obj !== 'object') { + // Convert enum values to their respective names + if (resolvedType && resolvedType instanceof ProtoBuf.Reflect.Enum) { + var name = ProtoBuf.Reflect.Enum.getName(resolvedType.object, obj); + if (name !== null) + return name; + } + // Pass-through string, number, boolean, null... + return obj; + } + // Convert ByteBuffers to raw buffer or strings + if (ByteBuffer.isByteBuffer(obj)) + return binaryAsBase64 ? obj.toBase64() : obj.toBuffer(); + // Convert Longs to proper objects or strings + if (ProtoBuf.Long.isLong(obj)) + return longsAsStrings ? obj.toString() : ProtoBuf.Long.fromValue(obj); + var clone; + // Clone arrays + if (Array.isArray(obj)) { + clone = []; + obj.forEach(function(v, k) { + clone[k] = cloneRaw(v, binaryAsBase64, longsAsStrings, resolvedType); + }); + return clone; + } + clone = {}; + // Convert maps to objects + if (obj instanceof ProtoBuf.Map) { + var it = obj.entries(); + for (var e = it.next(); !e.done; e = it.next()) + clone[obj.keyElem.valueToString(e.value[0])] = cloneRaw(e.value[1], binaryAsBase64, longsAsStrings, obj.valueElem.resolvedType); + return clone; + } + // Everything else is a non-null object + var type = obj.$type, + field = undefined; + for (var i in obj) + if (obj.hasOwnProperty(i)) { + if (type && (field = type.getChild(i))) + clone[i] = cloneRaw(obj[i], binaryAsBase64, longsAsStrings, field.resolvedType); + else + clone[i] = cloneRaw(obj[i], binaryAsBase64, longsAsStrings); + } + return clone; + } + + /** + * Returns the message's raw payload. + * @param {boolean=} binaryAsBase64 Whether to include binary data as base64 strings instead of Buffers, defaults to `false` + * @param {boolean} longsAsStrings Whether to encode longs as strings + * @returns {Object.} Raw payload + * @expose + */ + MessagePrototype.toRaw = function(binaryAsBase64, longsAsStrings) { + return cloneRaw(this, !!binaryAsBase64, !!longsAsStrings, this.$type); + }; + + /** + * Encodes a message to JSON. + * @returns {string} JSON string + * @expose + */ + MessagePrototype.encodeJSON = function() { + return JSON.stringify( + cloneRaw(this, + /* binary-as-base64 */ true, + /* longs-as-strings */ true, + this.$type + ) + ); + }; + + /** + * Decodes a message from the specified buffer or string. + * @name ProtoBuf.Builder.Message.decode + * @function + * @param {!ByteBuffer|!ArrayBuffer|!Buffer|string} buffer Buffer to decode from + * @param {string=} enc Encoding if buffer is a string: hex, utf8 (not recommended), defaults to base64 + * @return {!ProtoBuf.Builder.Message} Decoded message + * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still + * returns the decoded message with missing fields in the `decoded` property on the error. + * @expose + * @see ProtoBuf.Builder.Message.decode64 + * @see ProtoBuf.Builder.Message.decodeHex + */ + Message.decode = function(buffer, enc) { + if (typeof buffer === 'string') + buffer = ByteBuffer.wrap(buffer, enc ? enc : "base64"); + buffer = ByteBuffer.isByteBuffer(buffer) ? buffer : ByteBuffer.wrap(buffer); // May throw + var le = buffer.littleEndian; + try { + var msg = T.decode(buffer.LE()); + buffer.LE(le); + return msg; + } catch (e) { + buffer.LE(le); + throw(e); + } + }; + + /** + * Decodes a varint32 length-delimited message from the specified buffer or string. + * @name ProtoBuf.Builder.Message.decodeDelimited + * @function + * @param {!ByteBuffer|!ArrayBuffer|!Buffer|string} buffer Buffer to decode from + * @param {string=} enc Encoding if buffer is a string: hex, utf8 (not recommended), defaults to base64 + * @return {ProtoBuf.Builder.Message} Decoded message or `null` if not enough bytes are available yet + * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still + * returns the decoded message with missing fields in the `decoded` property on the error. + * @expose + */ + Message.decodeDelimited = function(buffer, enc) { + if (typeof buffer === 'string') + buffer = ByteBuffer.wrap(buffer, enc ? enc : "base64"); + buffer = ByteBuffer.isByteBuffer(buffer) ? buffer : ByteBuffer.wrap(buffer); // May throw + if (buffer.remaining() < 1) + return null; + var off = buffer.offset, + len = buffer.readVarint32(); + if (buffer.remaining() < len) { + buffer.offset = off; + return null; + } + try { + var msg = T.decode(buffer.slice(buffer.offset, buffer.offset + len).LE()); + buffer.offset += len; + return msg; + } catch (err) { + buffer.offset += len; + throw err; + } + }; + + /** + * Decodes the message from the specified base64 encoded string. + * @name ProtoBuf.Builder.Message.decode64 + * @function + * @param {string} str String to decode from + * @return {!ProtoBuf.Builder.Message} Decoded message + * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still + * returns the decoded message with missing fields in the `decoded` property on the error. + * @expose + */ + Message.decode64 = function(str) { + return Message.decode(str, "base64"); + }; + + /** + * Decodes the message from the specified hex encoded string. + * @name ProtoBuf.Builder.Message.decodeHex + * @function + * @param {string} str String to decode from + * @return {!ProtoBuf.Builder.Message} Decoded message + * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still + * returns the decoded message with missing fields in the `decoded` property on the error. + * @expose + */ + Message.decodeHex = function(str) { + return Message.decode(str, "hex"); + }; + + /** + * Decodes the message from a JSON string. + * @name ProtoBuf.Builder.Message.decodeJSON + * @function + * @param {string} str String to decode from + * @return {!ProtoBuf.Builder.Message} Decoded message + * @throws {Error} If the message cannot be decoded or if required fields are + * missing. + * @expose + */ + Message.decodeJSON = function(str) { + return new Message(JSON.parse(str)); + }; + + // Utility + + /** + * Returns a string representation of this Message. + * @name ProtoBuf.Builder.Message#toString + * @function + * @return {string} String representation as of ".Fully.Qualified.MessageName" + * @expose + */ + MessagePrototype.toString = function() { + return T.toString(); + }; + + // Properties + + /** + * Message options. + * @name ProtoBuf.Builder.Message.$options + * @type {Object.} + * @expose + */ + var $optionsS; // cc needs this + + /** + * Message options. + * @name ProtoBuf.Builder.Message#$options + * @type {Object.} + * @expose + */ + var $options; + + /** + * Reflection type. + * @name ProtoBuf.Builder.Message.$type + * @type {!ProtoBuf.Reflect.Message} + * @expose + */ + var $typeS; + + /** + * Reflection type. + * @name ProtoBuf.Builder.Message#$type + * @type {!ProtoBuf.Reflect.Message} + * @expose + */ + var $type; + + if (Object.defineProperty) + Object.defineProperty(Message, '$options', { "value": T.buildOpt() }), + Object.defineProperty(MessagePrototype, "$options", { "value": Message["$options"] }), + Object.defineProperty(Message, "$type", { "value": T }), + Object.defineProperty(MessagePrototype, "$type", { "value": T }); + + return Message; + + })(ProtoBuf, this); + + // Static enums and prototyped sub-messages / cached collections + this._fields = []; + this._fieldsById = {}; + this._fieldsByName = {}; + for (var i=0, k=this.children.length, child; i>> 3; + switch (wireType) { + case ProtoBuf.WIRE_TYPES.VARINT: + do tag = buf.readUint8(); + while ((tag & 0x80) === 0x80); + break; + case ProtoBuf.WIRE_TYPES.BITS64: + buf.offset += 8; + break; + case ProtoBuf.WIRE_TYPES.LDELIM: + tag = buf.readVarint32(); // reads the varint + buf.offset += tag; // skips n bytes + break; + case ProtoBuf.WIRE_TYPES.STARTGROUP: + skipTillGroupEnd(id, buf); + break; + case ProtoBuf.WIRE_TYPES.ENDGROUP: + if (id === expectedId) + return false; + else + throw Error("Illegal GROUPEND after unknown group: "+id+" ("+expectedId+" expected)"); + case ProtoBuf.WIRE_TYPES.BITS32: + buf.offset += 4; + break; + default: + throw Error("Illegal wire type in unknown group "+expectedId+": "+wireType); + } + return true; + } + + /** + * Decodes an encoded message and returns the decoded message. + * @param {ByteBuffer} buffer ByteBuffer to decode from + * @param {number=} length Message length. Defaults to decode all the available data. + * @param {number=} expectedGroupEndId Expected GROUPEND id if this is a legacy group + * @return {ProtoBuf.Builder.Message} Decoded message + * @throws {Error} If the message cannot be decoded + * @expose + */ + MessagePrototype.decode = function(buffer, length, expectedGroupEndId) { + length = typeof length === 'number' ? length : -1; + var start = buffer.offset, + msg = new (this.clazz)(), + tag, wireType, id, field; + while (buffer.offset < start+length || (length === -1 && buffer.remaining() > 0)) { + tag = buffer.readVarint32(); + wireType = tag & 0x07; + id = tag >>> 3; + if (wireType === ProtoBuf.WIRE_TYPES.ENDGROUP) { + if (id !== expectedGroupEndId) + throw Error("Illegal group end indicator for "+this.toString(true)+": "+id+" ("+(expectedGroupEndId ? expectedGroupEndId+" expected" : "not a group")+")"); + break; + } + if (!(field = this._fieldsById[id])) { + // "messages created by your new code can be parsed by your old code: old binaries simply ignore the new field when parsing." + switch (wireType) { + case ProtoBuf.WIRE_TYPES.VARINT: + buffer.readVarint32(); + break; + case ProtoBuf.WIRE_TYPES.BITS32: + buffer.offset += 4; + break; + case ProtoBuf.WIRE_TYPES.BITS64: + buffer.offset += 8; + break; + case ProtoBuf.WIRE_TYPES.LDELIM: + var len = buffer.readVarint32(); + buffer.offset += len; + break; + case ProtoBuf.WIRE_TYPES.STARTGROUP: + while (skipTillGroupEnd(id, buffer)) {} + break; + default: + throw Error("Illegal wire type for unknown field "+id+" in "+this.toString(true)+"#decode: "+wireType); + } + continue; + } + if (field.repeated && !field.options["packed"]) { + msg[field.name].push(field.decode(wireType, buffer)); + } else if (field.map) { + var keyval = field.decode(wireType, buffer); + msg[field.name].set(keyval[0], keyval[1]); + } else { + msg[field.name] = field.decode(wireType, buffer); + if (field.oneof) { // Field is part of an OneOf (not a virtual OneOf field) + var currentField = msg[field.oneof.name]; // Virtual field references currently set field + if (currentField !== null && currentField !== field.name) + msg[currentField] = null; // Clear currently set field + msg[field.oneof.name] = field.name; // Point virtual field at this field + } + } + } + + // Check if all required fields are present and set default values for optional fields that are not + for (var i=0, k=this._fields.length; i=} options Options + * @param {!ProtoBuf.Reflect.Message.OneOf=} oneof Enclosing OneOf + * @param {string?} syntax The syntax level of this definition (e.g., proto3) + * @constructor + * @extends ProtoBuf.Reflect.T + */ + var Field = function(builder, message, rule, keytype, type, name, id, options, oneof, syntax) { + T.call(this, builder, message, name); + + /** + * @override + */ + this.className = "Message.Field"; + + /** + * Message field required flag. + * @type {boolean} + * @expose + */ + this.required = rule === "required"; + + /** + * Message field repeated flag. + * @type {boolean} + * @expose + */ + this.repeated = rule === "repeated"; + + /** + * Message field map flag. + * @type {boolean} + * @expose + */ + this.map = rule === "map"; + + /** + * Message field key type. Type reference string if unresolved, protobuf + * type if resolved. Valid only if this.map === true, null otherwise. + * @type {string|{name: string, wireType: number}|null} + * @expose + */ + this.keyType = keytype || null; + + /** + * Message field type. Type reference string if unresolved, protobuf type if + * resolved. In a map field, this is the value type. + * @type {string|{name: string, wireType: number}} + * @expose + */ + this.type = type; + + /** + * Resolved type reference inside the global namespace. + * @type {ProtoBuf.Reflect.T|null} + * @expose + */ + this.resolvedType = null; + + /** + * Unique message field id. + * @type {number} + * @expose + */ + this.id = id; + + /** + * Message field options. + * @type {!Object.} + * @dict + * @expose + */ + this.options = options || {}; + + /** + * Default value. + * @type {*} + * @expose + */ + this.defaultValue = null; + + /** + * Enclosing OneOf. + * @type {?ProtoBuf.Reflect.Message.OneOf} + * @expose + */ + this.oneof = oneof || null; + + /** + * Syntax level of this definition (e.g., proto3). + * @type {string} + * @expose + */ + this.syntax = syntax || 'proto2'; + + /** + * Original field name. + * @type {string} + * @expose + */ + this.originalName = this.name; // Used to revert camelcase transformation on naming collisions + + /** + * Element implementation. Created in build() after types are resolved. + * @type {ProtoBuf.Element} + * @expose + */ + this.element = null; + + /** + * Key element implementation, for map fields. Created in build() after + * types are resolved. + * @type {ProtoBuf.Element} + * @expose + */ + this.keyElement = null; + + // Convert field names to camel case notation if the override is set + if (this.builder.options['convertFieldsToCamelCase'] && !(this instanceof Message.ExtensionField)) + this.name = ProtoBuf.Util.toCamelCase(this.name); + }; + + /** + * @alias ProtoBuf.Reflect.Message.Field.prototype + * @inner + */ + var FieldPrototype = Field.prototype = Object.create(T.prototype); + + /** + * Builds the field. + * @override + * @expose + */ + FieldPrototype.build = function() { + this.element = new Element(this.type, this.resolvedType, false, this.syntax); + if (this.map) + this.keyElement = new Element(this.keyType, undefined, true, this.syntax); + + // In proto3, fields do not have field presence, and every field is set to + // its type's default value ("", 0, 0.0, or false). + if (this.syntax === 'proto3' && !this.repeated && !this.map) + this.defaultValue = Element.defaultFieldValue(this.type); + + // Otherwise, default values are present when explicitly specified + else if (typeof this.options['default'] !== 'undefined') + this.defaultValue = this.verifyValue(this.options['default']); + }; + + /** + * Checks if the given value can be set for this field. + * @param {*} value Value to check + * @param {boolean=} skipRepeated Whether to skip the repeated value check or not. Defaults to false. + * @return {*} Verified, maybe adjusted, value + * @throws {Error} If the value cannot be set for this field + * @expose + */ + FieldPrototype.verifyValue = function(value, skipRepeated) { + skipRepeated = skipRepeated || false; + var fail = function(val, msg) { + throw Error("Illegal value for "+this.toString(true)+" of type "+this.type.name+": "+val+" ("+msg+")"); + }.bind(this); + if (value === null) { // NULL values for optional fields + if (this.required) + fail(typeof value, "required"); + if (this.syntax === 'proto3' && this.type !== ProtoBuf.TYPES["message"]) + fail(typeof value, "proto3 field without field presence cannot be null"); + return null; + } + var i; + if (this.repeated && !skipRepeated) { // Repeated values as arrays + if (!Array.isArray(value)) + value = [value]; + var res = []; + for (i=0; i 0; + + case ProtoBuf.TYPES["bytes"]: + return value.remaining() > 0; + + case ProtoBuf.TYPES["enum"]: + return value !== 0; + + case ProtoBuf.TYPES["message"]: + return value !== null; + default: + return true; + } + }; + + /** + * Encodes the specified field value to the specified buffer. + * @param {*} value Verified field value + * @param {ByteBuffer} buffer ByteBuffer to encode to + * @param {!ProtoBuf.Builder.Message} message Runtime message + * @return {ByteBuffer} The ByteBuffer for chaining + * @throws {Error} If the field cannot be encoded + * @expose + */ + FieldPrototype.encode = function(value, buffer, message) { + if (this.type === null || typeof this.type !== 'object') + throw Error("[INTERNAL] Unresolved type in "+this.toString(true)+": "+this.type); + if (value === null || (this.repeated && value.length == 0)) + return buffer; // Optional omitted + try { + if (this.repeated) { + var i; + // "Only repeated fields of primitive numeric types (types which use the varint, 32-bit, or 64-bit wire + // types) can be declared 'packed'." + if (this.options["packed"] && ProtoBuf.PACKABLE_WIRE_TYPES.indexOf(this.type.wireType) >= 0) { + // "All of the elements of the field are packed into a single key-value pair with wire type 2 + // (length-delimited). Each element is encoded the same way it would be normally, except without a + // tag preceding it." + buffer.writeVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM); + buffer.ensureCapacity(buffer.offset += 1); // We do not know the length yet, so let's assume a varint of length 1 + var start = buffer.offset; // Remember where the contents begin + for (i=0; i 1) { // We need to move the contents + var contents = buffer.slice(start, buffer.offset); + start += varintLen-1; + buffer.offset = start; + buffer.append(contents); + } + buffer.writeVarint32(len, start-varintLen); + } else { + // "If your message definition has repeated elements (without the [packed=true] option), the encoded + // message has zero or more key-value pairs with the same tag number" + for (i=0; i= 0) { + n += ByteBuffer.calculateVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM); + ni = 0; + for (i=0; i= 0) { + if (!skipRepeated) { + nBytes = buffer.readVarint32(); + nBytes = buffer.offset + nBytes; // Limit + var values = []; + while (buffer.offset < nBytes) + values.push(this.decode(this.type.wireType, buffer, true)); + return values; + } + // Read the next value otherwise... + } + + // Handle maps. + if (this.map) { + // Read one (key, value) submessage, and return [key, value] + var key = Element.defaultFieldValue(this.keyType); + value = Element.defaultFieldValue(this.type); + + // Read the length + nBytes = buffer.readVarint32(); + if (buffer.remaining() < nBytes) + throw Error("Illegal number of bytes for "+this.toString(true)+": "+nBytes+" required but got only "+buffer.remaining()); + + // Get a sub-buffer of this key/value submessage + var msgbuf = buffer.clone(); + msgbuf.limit = msgbuf.offset + nBytes; + buffer.offset += nBytes; + + while (msgbuf.remaining() > 0) { + var tag = msgbuf.readVarint32(); + wireType = tag & 0x07; + var id = tag >>> 3; + if (id === 1) { + key = this.keyElement.decode(msgbuf, wireType, id); + } else if (id === 2) { + value = this.element.decode(msgbuf, wireType, id); + } else { + throw Error("Unexpected tag in map field key/value submessage"); + } + } + + return [key, value]; + } + + // Handle singular and non-packed repeated field values. + return this.element.decode(buffer, wireType, this.id); + }; + + /** + * @alias ProtoBuf.Reflect.Message.Field + * @expose + */ + Reflect.Message.Field = Field; + + /** + * Constructs a new Message ExtensionField. + * @exports ProtoBuf.Reflect.Message.ExtensionField + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {!ProtoBuf.Reflect.Message} message Message reference + * @param {string} rule Rule, one of requried, optional, repeated + * @param {string} type Data type, e.g. int32 + * @param {string} name Field name + * @param {number} id Unique field id + * @param {!Object.=} options Options + * @constructor + * @extends ProtoBuf.Reflect.Message.Field + */ + var ExtensionField = function(builder, message, rule, type, name, id, options) { + Field.call(this, builder, message, rule, /* keytype = */ null, type, name, id, options); + + /** + * Extension reference. + * @type {!ProtoBuf.Reflect.Extension} + * @expose + */ + this.extension; + }; + + // Extends Field + ExtensionField.prototype = Object.create(Field.prototype); + + /** + * @alias ProtoBuf.Reflect.Message.ExtensionField + * @expose + */ + Reflect.Message.ExtensionField = ExtensionField; + + /** + * Constructs a new Message OneOf. + * @exports ProtoBuf.Reflect.Message.OneOf + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {!ProtoBuf.Reflect.Message} message Message reference + * @param {string} name OneOf name + * @constructor + * @extends ProtoBuf.Reflect.T + */ + var OneOf = function(builder, message, name) { + T.call(this, builder, message, name); + + /** + * Enclosed fields. + * @type {!Array.} + * @expose + */ + this.fields = []; + }; + + /** + * @alias ProtoBuf.Reflect.Message.OneOf + * @expose + */ + Reflect.Message.OneOf = OneOf; + + /** + * Constructs a new Enum. + * @exports ProtoBuf.Reflect.Enum + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {!ProtoBuf.Reflect.T} parent Parent Reflect object + * @param {string} name Enum name + * @param {Object.=} options Enum options + * @param {string?} syntax The syntax level (e.g., proto3) + * @constructor + * @extends ProtoBuf.Reflect.Namespace + */ + var Enum = function(builder, parent, name, options, syntax) { + Namespace.call(this, builder, parent, name, options, syntax); + + /** + * @override + */ + this.className = "Enum"; + + /** + * Runtime enum object. + * @type {Object.|null} + * @expose + */ + this.object = null; + }; + + /** + * Gets the string name of an enum value. + * @param {!ProtoBuf.Builder.Enum} enm Runtime enum + * @param {number} value Enum value + * @returns {?string} Name or `null` if not present + * @expose + */ + Enum.getName = function(enm, value) { + var keys = Object.keys(enm); + for (var i=0, key; i} + * @expose + */ + EnumPrototype.build = function(rebuild) { + if (this.object && !rebuild) + return this.object; + var enm = new ProtoBuf.Builder.Enum(), + values = this.getChildren(Enum.Value); + for (var i=0, k=values.length; i=} options Options + * @constructor + * @extends ProtoBuf.Reflect.Namespace + */ + var Service = function(builder, root, name, options) { + Namespace.call(this, builder, root, name, options); + + /** + * @override + */ + this.className = "Service"; + + /** + * Built runtime service class. + * @type {?function(new:ProtoBuf.Builder.Service)} + */ + this.clazz = null; + }; + + /** + * @alias ProtoBuf.Reflect.Service.prototype + * @inner + */ + var ServicePrototype = Service.prototype = Object.create(Namespace.prototype); + + /** + * Builds the service and returns the runtime counterpart, which is a fully functional class. + * @see ProtoBuf.Builder.Service + * @param {boolean=} rebuild Whether to rebuild or not + * @return {Function} Service class + * @throws {Error} If the message cannot be built + * @expose + */ + ServicePrototype.build = function(rebuild) { + if (this.clazz && !rebuild) + return this.clazz; + + // Create the runtime Service class in its own scope + return this.clazz = (function(ProtoBuf, T) { + + /** + * Constructs a new runtime Service. + * @name ProtoBuf.Builder.Service + * @param {function(string, ProtoBuf.Builder.Message, function(Error, ProtoBuf.Builder.Message=))=} rpcImpl RPC implementation receiving the method name and the message + * @class Barebone of all runtime services. + * @constructor + * @throws {Error} If the service cannot be created + */ + var Service = function(rpcImpl) { + ProtoBuf.Builder.Service.call(this); + + /** + * Service implementation. + * @name ProtoBuf.Builder.Service#rpcImpl + * @type {!function(string, ProtoBuf.Builder.Message, function(Error, ProtoBuf.Builder.Message=))} + * @expose + */ + this.rpcImpl = rpcImpl || function(name, msg, callback) { + // This is what a user has to implement: A function receiving the method name, the actual message to + // send (type checked) and the callback that's either provided with the error as its first + // argument or null and the actual response message. + setTimeout(callback.bind(this, Error("Not implemented, see: https://github.com/dcodeIO/ProtoBuf.js/wiki/Services")), 0); // Must be async! + }; + }; + + /** + * @alias ProtoBuf.Builder.Service.prototype + * @inner + */ + var ServicePrototype = Service.prototype = Object.create(ProtoBuf.Builder.Service.prototype); + + /** + * Asynchronously performs an RPC call using the given RPC implementation. + * @name ProtoBuf.Builder.Service.[Method] + * @function + * @param {!function(string, ProtoBuf.Builder.Message, function(Error, ProtoBuf.Builder.Message=))} rpcImpl RPC implementation + * @param {ProtoBuf.Builder.Message} req Request + * @param {function(Error, (ProtoBuf.Builder.Message|ByteBuffer|Buffer|string)=)} callback Callback receiving + * the error if any and the response either as a pre-parsed message or as its raw bytes + * @abstract + */ + + /** + * Asynchronously performs an RPC call using the instance's RPC implementation. + * @name ProtoBuf.Builder.Service#[Method] + * @function + * @param {ProtoBuf.Builder.Message} req Request + * @param {function(Error, (ProtoBuf.Builder.Message|ByteBuffer|Buffer|string)=)} callback Callback receiving + * the error if any and the response either as a pre-parsed message or as its raw bytes + * @abstract + */ + + var rpc = T.getChildren(ProtoBuf.Reflect.Service.RPCMethod); + for (var i=0; i} + * @expose + */ + var $optionsS; // cc needs this + + /** + * Service options. + * @name ProtoBuf.Builder.Service#$options + * @type {Object.} + * @expose + */ + var $options; + + /** + * Reflection type. + * @name ProtoBuf.Builder.Service.$type + * @type {!ProtoBuf.Reflect.Service} + * @expose + */ + var $typeS; + + /** + * Reflection type. + * @name ProtoBuf.Builder.Service#$type + * @type {!ProtoBuf.Reflect.Service} + * @expose + */ + var $type; + + if (Object.defineProperty) + Object.defineProperty(Service, "$options", { "value": T.buildOpt() }), + Object.defineProperty(ServicePrototype, "$options", { "value": Service["$options"] }), + Object.defineProperty(Service, "$type", { "value": T }), + Object.defineProperty(ServicePrototype, "$type", { "value": T }); + + return Service; + + })(ProtoBuf, this); + }; + + /** + * @alias ProtoBuf.Reflect.Service + * @expose + */ + Reflect.Service = Service; + + /** + * Abstract service method. + * @exports ProtoBuf.Reflect.Service.Method + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {!ProtoBuf.Reflect.Service} svc Service + * @param {string} name Method name + * @param {Object.=} options Options + * @constructor + * @extends ProtoBuf.Reflect.T + */ + var Method = function(builder, svc, name, options) { + T.call(this, builder, svc, name); + + /** + * @override + */ + this.className = "Service.Method"; + + /** + * Options. + * @type {Object.} + * @expose + */ + this.options = options || {}; + }; + + /** + * @alias ProtoBuf.Reflect.Service.Method.prototype + * @inner + */ + var MethodPrototype = Method.prototype = Object.create(T.prototype); + + /** + * Builds the method's '$options' property. + * @name ProtoBuf.Reflect.Service.Method#buildOpt + * @function + * @return {Object.} + */ + MethodPrototype.buildOpt = NamespacePrototype.buildOpt; + + /** + * @alias ProtoBuf.Reflect.Service.Method + * @expose + */ + Reflect.Service.Method = Method; + + /** + * RPC service method. + * @exports ProtoBuf.Reflect.Service.RPCMethod + * @param {!ProtoBuf.Builder} builder Builder reference + * @param {!ProtoBuf.Reflect.Service} svc Service + * @param {string} name Method name + * @param {string} request Request message name + * @param {string} response Response message name + * @param {boolean} request_stream Whether requests are streamed + * @param {boolean} response_stream Whether responses are streamed + * @param {Object.=} options Options + * @constructor + * @extends ProtoBuf.Reflect.Service.Method + */ + var RPCMethod = function(builder, svc, name, request, response, request_stream, response_stream, options) { + Method.call(this, builder, svc, name, options); + + /** + * @override + */ + this.className = "Service.RPCMethod"; + + /** + * Request message name. + * @type {string} + * @expose + */ + this.requestName = request; + + /** + * Response message name. + * @type {string} + * @expose + */ + this.responseName = response; + + /** + * Whether requests are streamed + * @type {bool} + * @expose + */ + this.requestStream = request_stream; + + /** + * Whether responses are streamed + * @type {bool} + * @expose + */ + this.responseStream = response_stream; + + /** + * Resolved request message type. + * @type {ProtoBuf.Reflect.Message} + * @expose + */ + this.resolvedRequestType = null; + + /** + * Resolved response message type. + * @type {ProtoBuf.Reflect.Message} + * @expose + */ + this.resolvedResponseType = null; + }; + + // Extends Method + RPCMethod.prototype = Object.create(Method.prototype); + + /** + * @alias ProtoBuf.Reflect.Service.RPCMethod + * @expose + */ + Reflect.Service.RPCMethod = RPCMethod; + + return Reflect; + + })(ProtoBuf); + + /** + * @alias ProtoBuf.Builder + * @expose + */ + ProtoBuf.Builder = (function(ProtoBuf, Lang, Reflect) { + "use strict"; + + /** + * Constructs a new Builder. + * @exports ProtoBuf.Builder + * @class Provides the functionality to build protocol messages. + * @param {Object.=} options Options + * @constructor + */ + var Builder = function(options) { + + /** + * Namespace. + * @type {ProtoBuf.Reflect.Namespace} + * @expose + */ + this.ns = new Reflect.Namespace(this, null, ""); // Global namespace + + /** + * Namespace pointer. + * @type {ProtoBuf.Reflect.T} + * @expose + */ + this.ptr = this.ns; + + /** + * Resolved flag. + * @type {boolean} + * @expose + */ + this.resolved = false; + + /** + * The current building result. + * @type {Object.|null} + * @expose + */ + this.result = null; + + /** + * Imported files. + * @type {Array.} + * @expose + */ + this.files = {}; + + /** + * Import root override. + * @type {?string} + * @expose + */ + this.importRoot = null; + + /** + * Options. + * @type {!Object.} + * @expose + */ + this.options = options || {}; + }; + + /** + * @alias ProtoBuf.Builder.prototype + * @inner + */ + var BuilderPrototype = Builder.prototype; + + // ----- Definition tests ----- + + /** + * Tests if a definition most likely describes a message. + * @param {!Object} def + * @returns {boolean} + * @expose + */ + Builder.isMessage = function(def) { + // Messages require a string name + if (typeof def["name"] !== 'string') + return false; + // Messages do not contain values (enum) or rpc methods (service) + if (typeof def["values"] !== 'undefined' || typeof def["rpc"] !== 'undefined') + return false; + return true; + }; + + /** + * Tests if a definition most likely describes a message field. + * @param {!Object} def + * @returns {boolean} + * @expose + */ + Builder.isMessageField = function(def) { + // Message fields require a string rule, name and type and an id + if (typeof def["rule"] !== 'string' || typeof def["name"] !== 'string' || typeof def["type"] !== 'string' || typeof def["id"] === 'undefined') + return false; + return true; + }; + + /** + * Tests if a definition most likely describes an enum. + * @param {!Object} def + * @returns {boolean} + * @expose + */ + Builder.isEnum = function(def) { + // Enums require a string name + if (typeof def["name"] !== 'string') + return false; + // Enums require at least one value + if (typeof def["values"] === 'undefined' || !Array.isArray(def["values"]) || def["values"].length === 0) + return false; + return true; + }; + + /** + * Tests if a definition most likely describes a service. + * @param {!Object} def + * @returns {boolean} + * @expose + */ + Builder.isService = function(def) { + // Services require a string name and an rpc object + if (typeof def["name"] !== 'string' || typeof def["rpc"] !== 'object' || !def["rpc"]) + return false; + return true; + }; + + /** + * Tests if a definition most likely describes an extended message + * @param {!Object} def + * @returns {boolean} + * @expose + */ + Builder.isExtend = function(def) { + // Extends rquire a string ref + if (typeof def["ref"] !== 'string') + return false; + return true; + }; + + // ----- Building ----- + + /** + * Resets the pointer to the root namespace. + * @returns {!ProtoBuf.Builder} this + * @expose + */ + BuilderPrototype.reset = function() { + this.ptr = this.ns; + return this; + }; + + /** + * Defines a namespace on top of the current pointer position and places the pointer on it. + * @param {string} namespace + * @return {!ProtoBuf.Builder} this + * @expose + */ + BuilderPrototype.define = function(namespace) { + if (typeof namespace !== 'string' || !Lang.TYPEREF.test(namespace)) + throw Error("illegal namespace: "+namespace); + namespace.split(".").forEach(function(part) { + var ns = this.ptr.getChild(part); + if (ns === null) // Keep existing + this.ptr.addChild(ns = new Reflect.Namespace(this, this.ptr, part)); + this.ptr = ns; + }, this); + return this; + }; + + /** + * Creates the specified definitions at the current pointer position. + * @param {!Array.} defs Messages, enums or services to create + * @returns {!ProtoBuf.Builder} this + * @throws {Error} If a message definition is invalid + * @expose + */ + BuilderPrototype.create = function(defs) { + if (!defs) + return this; // Nothing to create + if (!Array.isArray(defs)) + defs = [defs]; + else { + if (defs.length === 0) + return this; + defs = defs.slice(); + } + + // It's quite hard to keep track of scopes and memory here, so let's do this iteratively. + var stack = [defs]; + while (stack.length > 0) { + defs = stack.pop(); + + if (!Array.isArray(defs)) // Stack always contains entire namespaces + throw Error("not a valid namespace: "+JSON.stringify(defs)); + + while (defs.length > 0) { + var def = defs.shift(); // Namespaces always contain an array of messages, enums and services + + if (Builder.isMessage(def)) { + var obj = new Reflect.Message(this, this.ptr, def["name"], def["options"], def["isGroup"], def["syntax"]); + + // Create OneOfs + var oneofs = {}; + if (def["oneofs"]) + Object.keys(def["oneofs"]).forEach(function(name) { + obj.addChild(oneofs[name] = new Reflect.Message.OneOf(this, obj, name)); + }, this); + + // Create fields + if (def["fields"]) + def["fields"].forEach(function(fld) { + if (obj.getChild(fld["id"]|0) !== null) + throw Error("duplicate or invalid field id in "+obj.name+": "+fld['id']); + if (fld["options"] && typeof fld["options"] !== 'object') + throw Error("illegal field options in "+obj.name+"#"+fld["name"]); + var oneof = null; + if (typeof fld["oneof"] === 'string' && !(oneof = oneofs[fld["oneof"]])) + throw Error("illegal oneof in "+obj.name+"#"+fld["name"]+": "+fld["oneof"]); + fld = new Reflect.Message.Field(this, obj, fld["rule"], fld["keytype"], fld["type"], fld["name"], fld["id"], fld["options"], oneof, def["syntax"]); + if (oneof) + oneof.fields.push(fld); + obj.addChild(fld); + }, this); + + // Push children to stack + var subObj = []; + if (def["enums"]) + def["enums"].forEach(function(enm) { + subObj.push(enm); + }); + if (def["messages"]) + def["messages"].forEach(function(msg) { + subObj.push(msg); + }); + if (def["services"]) + def["services"].forEach(function(svc) { + subObj.push(svc); + }); + + // Set extension ranges + if (def["extensions"]) { + if (typeof def["extensions"][0] === 'number') // pre 5.0.1 + obj.extensions = [ def["extensions"] ]; + else + obj.extensions = def["extensions"]; + } + + // Create on top of current namespace + this.ptr.addChild(obj); + if (subObj.length > 0) { + stack.push(defs); // Push the current level back + defs = subObj; // Continue processing sub level + subObj = null; + this.ptr = obj; // And move the pointer to this namespace + obj = null; + continue; + } + subObj = null; + + } else if (Builder.isEnum(def)) { + + obj = new Reflect.Enum(this, this.ptr, def["name"], def["options"], def["syntax"]); + def["values"].forEach(function(val) { + obj.addChild(new Reflect.Enum.Value(this, obj, val["name"], val["id"])); + }, this); + this.ptr.addChild(obj); + + } else if (Builder.isService(def)) { + + obj = new Reflect.Service(this, this.ptr, def["name"], def["options"]); + Object.keys(def["rpc"]).forEach(function(name) { + var mtd = def["rpc"][name]; + obj.addChild(new Reflect.Service.RPCMethod(this, obj, name, mtd["request"], mtd["response"], !!mtd["request_stream"], !!mtd["response_stream"], mtd["options"])); + }, this); + this.ptr.addChild(obj); + + } else if (Builder.isExtend(def)) { + + obj = this.ptr.resolve(def["ref"], true); + if (obj) { + def["fields"].forEach(function(fld) { + if (obj.getChild(fld['id']|0) !== null) + throw Error("duplicate extended field id in "+obj.name+": "+fld['id']); + // Check if field id is allowed to be extended + if (obj.extensions) { + var valid = false; + obj.extensions.forEach(function(range) { + if (fld["id"] >= range[0] && fld["id"] <= range[1]) + valid = true; + }); + if (!valid) + throw Error("illegal extended field id in "+obj.name+": "+fld['id']+" (not within valid ranges)"); + } + // Convert extension field names to camel case notation if the override is set + var name = fld["name"]; + if (this.options['convertFieldsToCamelCase']) + name = ProtoBuf.Util.toCamelCase(name); + // see #161: Extensions use their fully qualified name as their runtime key and... + var field = new Reflect.Message.ExtensionField(this, obj, fld["rule"], fld["type"], this.ptr.fqn()+'.'+name, fld["id"], fld["options"]); + // ...are added on top of the current namespace as an extension which is used for + // resolving their type later on (the extension always keeps the original name to + // prevent naming collisions) + var ext = new Reflect.Extension(this, this.ptr, fld["name"], field); + field.extension = ext; + this.ptr.addChild(ext); + obj.addChild(field); + }, this); + + } else if (!/\.?google\.protobuf\./.test(def["ref"])) // Silently skip internal extensions + throw Error("extended message "+def["ref"]+" is not defined"); + + } else + throw Error("not a valid definition: "+JSON.stringify(def)); + + def = null; + obj = null; + } + // Break goes here + defs = null; + this.ptr = this.ptr.parent; // Namespace done, continue at parent + } + this.resolved = false; // Require re-resolve + this.result = null; // Require re-build + return this; + }; + + /** + * Propagates syntax to all children. + * @param {!Object} parent + * @inner + */ + function propagateSyntax(parent) { + if (parent['messages']) { + parent['messages'].forEach(function(child) { + child["syntax"] = parent["syntax"]; + propagateSyntax(child); + }); + } + if (parent['enums']) { + parent['enums'].forEach(function(child) { + child["syntax"] = parent["syntax"]; + }); + } + } + + /** + * Imports another definition into this builder. + * @param {Object.} json Parsed import + * @param {(string|{root: string, file: string})=} filename Imported file name + * @returns {!ProtoBuf.Builder} this + * @throws {Error} If the definition or file cannot be imported + * @expose + */ + BuilderPrototype["import"] = function(json, filename) { + var delim = '/'; + + // Make sure to skip duplicate imports + + if (typeof filename === 'string') { + + if (ProtoBuf.Util.IS_NODE) + filename = require("path")['resolve'](filename); + if (this.files[filename] === true) + return this.reset(); + this.files[filename] = true; + + } else if (typeof filename === 'object') { // Object with root, file. + + var root = filename.root; + if (ProtoBuf.Util.IS_NODE) + root = require("path")['resolve'](root); + if (root.indexOf("\\") >= 0 || filename.file.indexOf("\\") >= 0) + delim = '\\'; + var fname = root + delim + filename.file; + if (this.files[fname] === true) + return this.reset(); + this.files[fname] = true; + } + + // Import imports + + if (json['imports'] && json['imports'].length > 0) { + var importRoot, + resetRoot = false; + + if (typeof filename === 'object') { // If an import root is specified, override + + this.importRoot = filename["root"]; resetRoot = true; // ... and reset afterwards + importRoot = this.importRoot; + filename = filename["file"]; + if (importRoot.indexOf("\\") >= 0 || filename.indexOf("\\") >= 0) + delim = '\\'; + + } else if (typeof filename === 'string') { + + if (this.importRoot) // If import root is overridden, use it + importRoot = this.importRoot; + else { // Otherwise compute from filename + if (filename.indexOf("/") >= 0) { // Unix + importRoot = filename.replace(/\/[^\/]*$/, ""); + if (/* /file.proto */ importRoot === "") + importRoot = "/"; + } else if (filename.indexOf("\\") >= 0) { // Windows + importRoot = filename.replace(/\\[^\\]*$/, ""); + delim = '\\'; + } else + importRoot = "."; + } + + } else + importRoot = null; + + for (var i=0; i)=} path Specifies what to return. If omitted, the entire namespace will be returned. + * @returns {!ProtoBuf.Builder.Message|!Object.} + * @throws {Error} If a type could not be resolved + * @expose + */ + BuilderPrototype.build = function(path) { + this.reset(); + if (!this.resolved) + this.resolveAll(), + this.resolved = true, + this.result = null; // Require re-build + if (this.result === null) // (Re-)Build + this.result = this.ns.build(); + if (!path) + return this.result; + var part = typeof path === 'string' ? path.split(".") : path, + ptr = this.result; // Build namespace pointer (no hasChild etc.) + for (var i=0; i=} contents Initial contents + * @constructor + */ + var Map = function(field, contents) { + if (!field.map) + throw Error("field is not a map"); + + /** + * The field corresponding to this map. + * @type {!ProtoBuf.Reflect.Field} + */ + this.field = field; + + /** + * Element instance corresponding to key type. + * @type {!ProtoBuf.Reflect.Element} + */ + this.keyElem = new Reflect.Element(field.keyType, null, true, field.syntax); + + /** + * Element instance corresponding to value type. + * @type {!ProtoBuf.Reflect.Element} + */ + this.valueElem = new Reflect.Element(field.type, field.resolvedType, false, field.syntax); + + /** + * Internal map: stores mapping of (string form of key) -> (key, value) + * pair. + * + * We provide map semantics for arbitrary key types, but we build on top + * of an Object, which has only string keys. In order to avoid the need + * to convert a string key back to its native type in many situations, + * we store the native key value alongside the value. Thus, we only need + * a one-way mapping from a key type to its string form that guarantees + * uniqueness and equality (i.e., str(K1) === str(K2) if and only if K1 + * === K2). + * + * @type {!Object} + */ + this.map = {}; + + /** + * Returns the number of elements in the map. + */ + Object.defineProperty(this, "size", { + get: function() { return Object.keys(this.map).length; } + }); + + // Fill initial contents from a raw object. + if (contents) { + var keys = Object.keys(contents); + for (var i = 0; i < keys.length; i++) { + var key = this.keyElem.valueFromString(keys[i]); + var val = this.valueElem.verifyValue(contents[keys[i]]); + this.map[this.keyElem.valueToString(key)] = + { key: key, value: val }; + } + } + }; + + var MapPrototype = Map.prototype; + + /** + * Helper: return an iterator over an array. + * @param {!Array<*>} arr the array + * @returns {!Object} an iterator + * @inner + */ + function arrayIterator(arr) { + var idx = 0; + return { + next: function() { + if (idx < arr.length) + return { done: false, value: arr[idx++] }; + return { done: true }; + } + } + } + + /** + * Clears the map. + */ + MapPrototype.clear = function() { + this.map = {}; + }; + + /** + * Deletes a particular key from the map. + * @returns {boolean} Whether any entry with this key was deleted. + */ + MapPrototype["delete"] = function(key) { + var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key)); + var hadKey = keyValue in this.map; + delete this.map[keyValue]; + return hadKey; + }; + + /** + * Returns an iterator over [key, value] pairs in the map. + * @returns {Object} The iterator + */ + MapPrototype.entries = function() { + var entries = []; + var strKeys = Object.keys(this.map); + for (var i = 0, entry; i < strKeys.length; i++) + entries.push([(entry=this.map[strKeys[i]]).key, entry.value]); + return arrayIterator(entries); + }; + + /** + * Returns an iterator over keys in the map. + * @returns {Object} The iterator + */ + MapPrototype.keys = function() { + var keys = []; + var strKeys = Object.keys(this.map); + for (var i = 0; i < strKeys.length; i++) + keys.push(this.map[strKeys[i]].key); + return arrayIterator(keys); + }; + + /** + * Returns an iterator over values in the map. + * @returns {!Object} The iterator + */ + MapPrototype.values = function() { + var values = []; + var strKeys = Object.keys(this.map); + for (var i = 0; i < strKeys.length; i++) + values.push(this.map[strKeys[i]].value); + return arrayIterator(values); + }; + + /** + * Iterates over entries in the map, calling a function on each. + * @param {function(this:*, *, *, *)} cb The callback to invoke with value, key, and map arguments. + * @param {Object=} thisArg The `this` value for the callback + */ + MapPrototype.forEach = function(cb, thisArg) { + var strKeys = Object.keys(this.map); + for (var i = 0, entry; i < strKeys.length; i++) + cb.call(thisArg, (entry=this.map[strKeys[i]]).value, entry.key, this); + }; + + /** + * Sets a key in the map to the given value. + * @param {*} key The key + * @param {*} value The value + * @returns {!ProtoBuf.Map} The map instance + */ + MapPrototype.set = function(key, value) { + var keyValue = this.keyElem.verifyValue(key); + var valValue = this.valueElem.verifyValue(value); + this.map[this.keyElem.valueToString(keyValue)] = + { key: keyValue, value: valValue }; + return this; + }; + + /** + * Gets the value corresponding to a key in the map. + * @param {*} key The key + * @returns {*|undefined} The value, or `undefined` if key not present + */ + MapPrototype.get = function(key) { + var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key)); + if (!(keyValue in this.map)) + return undefined; + return this.map[keyValue].value; + }; + + /** + * Determines whether the given key is present in the map. + * @param {*} key The key + * @returns {boolean} `true` if the key is present + */ + MapPrototype.has = function(key) { + var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key)); + return (keyValue in this.map); + }; + + return Map; + })(ProtoBuf, ProtoBuf.Reflect); + + + /** + * Loads a .proto string and returns the Builder. + * @param {string} proto .proto file contents + * @param {(ProtoBuf.Builder|string|{root: string, file: string})=} builder Builder to append to. Will create a new one if omitted. + * @param {(string|{root: string, file: string})=} filename The corresponding file name if known. Must be specified for imports. + * @return {ProtoBuf.Builder} Builder to create new messages + * @throws {Error} If the definition cannot be parsed or built + * @expose + */ + ProtoBuf.loadProto = function(proto, builder, filename) { + if (typeof builder === 'string' || (builder && typeof builder["file"] === 'string' && typeof builder["root"] === 'string')) + filename = builder, + builder = undefined; + return ProtoBuf.loadJson(ProtoBuf.DotProto.Parser.parse(proto), builder, filename); + }; + + /** + * Loads a .proto string and returns the Builder. This is an alias of {@link ProtoBuf.loadProto}. + * @function + * @param {string} proto .proto file contents + * @param {(ProtoBuf.Builder|string)=} builder Builder to append to. Will create a new one if omitted. + * @param {(string|{root: string, file: string})=} filename The corresponding file name if known. Must be specified for imports. + * @return {ProtoBuf.Builder} Builder to create new messages + * @throws {Error} If the definition cannot be parsed or built + * @expose + */ + ProtoBuf.protoFromString = ProtoBuf.loadProto; // Legacy + + /** + * Loads a .proto file and returns the Builder. + * @param {string|{root: string, file: string}} filename Path to proto file or an object specifying 'file' with + * an overridden 'root' path for all imported files. + * @param {function(?Error, !ProtoBuf.Builder=)=} callback Callback that will receive `null` as the first and + * the Builder as its second argument on success, otherwise the error as its first argument. If omitted, the + * file will be read synchronously and this function will return the Builder. + * @param {ProtoBuf.Builder=} builder Builder to append to. Will create a new one if omitted. + * @return {?ProtoBuf.Builder|undefined} The Builder if synchronous (no callback specified, will be NULL if the + * request has failed), else undefined + * @expose + */ + ProtoBuf.loadProtoFile = function(filename, callback, builder) { + if (callback && typeof callback === 'object') + builder = callback, + callback = null; + else if (!callback || typeof callback !== 'function') + callback = null; + if (callback) + return ProtoBuf.Util.fetch(typeof filename === 'string' ? filename : filename["root"]+"/"+filename["file"], function(contents) { + if (contents === null) { + callback(Error("Failed to fetch file")); + return; + } + try { + callback(null, ProtoBuf.loadProto(contents, builder, filename)); + } catch (e) { + callback(e); + } + }); + var contents = ProtoBuf.Util.fetch(typeof filename === 'object' ? filename["root"]+"/"+filename["file"] : filename); + return contents === null ? null : ProtoBuf.loadProto(contents, builder, filename); + }; + + /** + * Loads a .proto file and returns the Builder. This is an alias of {@link ProtoBuf.loadProtoFile}. + * @function + * @param {string|{root: string, file: string}} filename Path to proto file or an object specifying 'file' with + * an overridden 'root' path for all imported files. + * @param {function(?Error, !ProtoBuf.Builder=)=} callback Callback that will receive `null` as the first and + * the Builder as its second argument on success, otherwise the error as its first argument. If omitted, the + * file will be read synchronously and this function will return the Builder. + * @param {ProtoBuf.Builder=} builder Builder to append to. Will create a new one if omitted. + * @return {!ProtoBuf.Builder|undefined} The Builder if synchronous (no callback specified, will be NULL if the + * request has failed), else undefined + * @expose + */ + ProtoBuf.protoFromFile = ProtoBuf.loadProtoFile; // Legacy + + + /** + * Constructs a new empty Builder. + * @param {Object.=} options Builder options, defaults to global options set on ProtoBuf + * @return {!ProtoBuf.Builder} Builder + * @expose + */ + ProtoBuf.newBuilder = function(options) { + options = options || {}; + if (typeof options['convertFieldsToCamelCase'] === 'undefined') + options['convertFieldsToCamelCase'] = ProtoBuf.convertFieldsToCamelCase; + if (typeof options['populateAccessors'] === 'undefined') + options['populateAccessors'] = ProtoBuf.populateAccessors; + return new ProtoBuf.Builder(options); + }; + + /** + * Loads a .json definition and returns the Builder. + * @param {!*|string} json JSON definition + * @param {(ProtoBuf.Builder|string|{root: string, file: string})=} builder Builder to append to. Will create a new one if omitted. + * @param {(string|{root: string, file: string})=} filename The corresponding file name if known. Must be specified for imports. + * @return {ProtoBuf.Builder} Builder to create new messages + * @throws {Error} If the definition cannot be parsed or built + * @expose + */ + ProtoBuf.loadJson = function(json, builder, filename) { + if (typeof builder === 'string' || (builder && typeof builder["file"] === 'string' && typeof builder["root"] === 'string')) + filename = builder, + builder = null; + if (!builder || typeof builder !== 'object') + builder = ProtoBuf.newBuilder(); + if (typeof json === 'string') + json = JSON.parse(json); + builder["import"](json, filename); + builder.resolveAll(); + return builder; + }; + + /** + * Loads a .json file and returns the Builder. + * @param {string|!{root: string, file: string}} filename Path to json file or an object specifying 'file' with + * an overridden 'root' path for all imported files. + * @param {function(?Error, !ProtoBuf.Builder=)=} callback Callback that will receive `null` as the first and + * the Builder as its second argument on success, otherwise the error as its first argument. If omitted, the + * file will be read synchronously and this function will return the Builder. + * @param {ProtoBuf.Builder=} builder Builder to append to. Will create a new one if omitted. + * @return {?ProtoBuf.Builder|undefined} The Builder if synchronous (no callback specified, will be NULL if the + * request has failed), else undefined + * @expose + */ + ProtoBuf.loadJsonFile = function(filename, callback, builder) { + if (callback && typeof callback === 'object') + builder = callback, + callback = null; + else if (!callback || typeof callback !== 'function') + callback = null; + if (callback) + return ProtoBuf.Util.fetch(typeof filename === 'string' ? filename : filename["root"]+"/"+filename["file"], function(contents) { + if (contents === null) { + callback(Error("Failed to fetch file")); + return; + } + try { + //fix by sxh start + //LOG(contents); + //callback(contents); + //fix by sxh end + if(typeof contents === 'string'){ + callback(null,ProtoBuf.loadJson(JSON.parse(contents), builder, filename)); + } + else{ + callback(null,ProtoBuf.loadJson(contents), builder, filename); + } + } catch (e) { + callback(e); + } + }); + var contents = ProtoBuf.Util.fetch(typeof filename === 'object' ? filename["root"]+"/"+filename["file"] : filename); + return contents === null ? null : ProtoBuf.loadJson(JSON.parse(contents), builder, filename); + }; + + return ProtoBuf; +}); diff --git a/fc2/images/kingsomevs.proto b/fc2/resources/kingsomevs.proto similarity index 100% rename from fc2/images/kingsomevs.proto rename to fc2/resources/kingsomevs.proto diff --git a/fc2/images/messages.proto b/fc2/resources/messages.proto similarity index 100% rename from fc2/images/messages.proto rename to fc2/resources/messages.proto diff --git a/jcfw/logger/httpcli.js b/jcfw/logger/httpcli.js new file mode 100644 index 0000000..554e97f --- /dev/null +++ b/jcfw/logger/httpcli.js @@ -0,0 +1,203 @@ + + +var httpcli = function(){ + // LIFE-CYCLE CALLBACKS: + + // onLoad () {}, + + // start () {}, + + // update (dt) {}, + + this.init=function(){ + this.cachemsg = []; + this._needretry = false; + this._retrycount = 3; + this._retryms = 3000; + this._runningcount = 0; + this._maxrunning = 10; + // this._retry_tid = setInterval(this._retry.bind(this), this._retryms); + }; + + this.httpGet=function(url, cbRes, cbErr){ + // var realurl = url; + // if(urldata){ + // if(realurl.indexOf("?") == -1){ + // realurl += "?"; + // }else{ + // realurl += "&"; + // } + // realurl += encodeURIComponent(urldata); + // } + return this.httpsend(url, null, cbRes, cbErr, 'GET'); + }; + + this.httpPost=function(url, postdata, cbRes, cbErr){ + return this.httpsend(url, postdata, cbRes, cbErr, 'POST'); + }; + + this.httpsend=function(url, urldata, cbRes, cbErr, smethod, isretry){ + if(this._runningcount < this._maxrunning){ + return this._httpsend(url, urldata, cbRes, cbErr, smethod, isretry); + }else if(!isretry){ + let obj = { + u: url, + v: urldata, + successcb: cbRes, + errcb: cbErr + }; + this.cachemsg.push(obj); + return null; + } + }; + + this._httpsend=function(url, urldata, cbRes, cbErr, smethod, isretry){ + this._runningcount++; + let self = this; + if(typeof(XMLHttpRequest) != 'undefined'){ + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4){ + self._runningcount--; + if(xhr.status >= 200 && xhr.status < 400) { + var response = xhr.responseText; + cbRes && cbRes(response); + }else{ + if(!isretry && self._needretry){ + let obj = { + u: url, + v: urldata, + successcb: cbRes, + errcb: cbErr + }; + self.cachemsg.push(obj); + }else{ + cbErr && cbErr(xhr.status, xhr.statusText); + } + } + } + }; + // xhr.onabort = function(){ + // self._runningcount--; + // }; + xhr.onerror = function(){ + self._runningcount--; + }; + xhr.open(smethod, url, true); + xhr.send(urldata); + return xhr; + }else if(typeof(wx) != 'undefined'){ + var jsobj = urldata? JSON.parse(urldata): null; + var xhr = wx.request({ + url: url, + data: jsobj, + method: smethod, + success: function(res){ + console.log("[wx]request success!" + res.statusCode); + if(res.statusCode >= 200 && res.statusCode < 400) { + var restext = JSON.stringify(res.data); + cbRes && cbRes(restext); + }else{ + cbErr && cbErr(res.statusCode, res.msg); + } + }, + fail: function(res){ + console.log("[wx]request fail!"+ JSON.stringify(res)); + if(!isretry && self._needretry){ + let obj = { + u: url, + v: urldata, + successcb: cbRes, + errcb: cbErr + }; + self.cachemsg.push(obj); + }else{ + cbErr && cbErr(-1, res.msg); + } + // cbErr && cbErr(-1, res.msg); + }, + complete: function(){ + self._runningcount--; + console.log("[wx]request complete!"); + } + }); + return xhr; + } + return null; + }; + + this._retry=function(){ + if(this.cachemsg.length > 0){ + this.cachemsg.forEach(element => { + let obj = element; + if(obj.retrying){ + return; + } + let bneedretry = false; + if(!obj.retry_count){ + obj.retry_count = 1; + obj.passtime = 0; + } + obj.passtime += this._retryms; + bneedretry = (obj.passtime >= obj.retry_count * this._retryms); + + if(bneedretry && !obj.retrying){ + obj.retrying = true; + let method = obj.v? 'POST': 'GET'; + this.httpsend(obj.u, obj.v, (restext) => { + obj.successcb && obj.successcb(restext); + let idx = this.cachemsg.findIndex((element) => { + return element == obj; + }); + if(idx != -1){ + this.cachemsg.splice(idx, 1); + } + }, (errcode, errmsg) => { + obj.retrying = false; + obj.passtime = 0; + obj.retry_count++; + if(obj.retry_count >= this._retrycount){ + obj.errcb && obj.errcb(errcode, errmsg); + let idx = this.cachemsg.findIndex((element) => { + return element == obj; + }); + if(idx != -1){ + this.cachemsg.splice(idx, 1); + } + } + }, method, true); + } + + }); + } + }; + + this.setRetryCount=function(count){ + this.retrycount = count; + }; + + this.setRetryInterval=function(millsec){ + if(millsec != this._retryms){ + this._retryms = millsec; + if(this._retry_tid){ + clearInterval(this._retry_tid); + this._retry_tid = 0; + } + } + if(!this._retry_tid){ + this._retry_tid = setInterval(this._retry.bind(this), this._retryms); + } + }; + + this.setNeedRetry=function(needretry){ + this._needretry = needretry; + // if(!this._needretry && this._retry_tid){ + // clearInterval(this._retry_tid); + // this._retry_tid = 0; + // }else if(this._needretry && !this._retry_tid){ + // this._retry_tid = setInterval(this._retry.bind(this), this._retryms); + // } + } +}; + +module.exports = httpcli; diff --git a/jcfw/logger/httpclient.js b/jcfw/logger/httpclient.js new file mode 100644 index 0000000..90c4287 --- /dev/null +++ b/jcfw/logger/httpclient.js @@ -0,0 +1,50 @@ +var httpcli = require('./httpcli'); + +module.exports = { + // LIFE-CYCLE CALLBACKS: + + // onLoad () {}, + + // start () {}, + + // update (dt) {}, + getInstance() { + if (!this._instance) { + this._instance = new httpcli(); + this._instance.init(); + this._instance.setRetryInterval(3000); + this._instance.setNeedRetry(true); + } + return this._instance; + }, + + httpGet(url, cbRes, cbErr) { + // var realurl = url; + // if(urldata){ + // if(realurl.indexOf("?") == -1){ + // realurl += "?"; + // }else{ + // realurl += "&"; + // } + // realurl += encodeURIComponent(urldata); + // } + return this.httpsend(url, null, cbRes, cbErr, 'GET'); + }, + + httpPost(url, postdata, cbRes, cbErr) { + return this.httpsend(url, postdata, cbRes, cbErr, 'POST'); + }, + + httpsend(url, urldata, cbRes, cbErr, smethod) { + return this.getInstance().httpsend(url, urldata, cbRes, cbErr, smethod); + }, + + JSON_parse(text) { + try { + return JSON.parse(text); + } catch (err) { + console.log(err); + return null; + } + } +}; diff --git a/jcfw/logger/jcgamelog.js b/jcfw/logger/jcgamelog.js new file mode 100644 index 0000000..5218d23 --- /dev/null +++ b/jcfw/logger/jcgamelog.js @@ -0,0 +1,967 @@ + + +var httpclient = require('./httpclient'); +var urlbuilder = require('./urlbuilder'); + +const JC_LOG_T = { + lauch:{ + key: 1, + subkey: 1, + }, + + entermain:{ + key: 1, + subkey: 2, + }, + + show:{ + key: 1, + subkey: 3 + }, + + hide:{ + key: 1, + subkey: 3 + }, + + jumpapp:{ + key: 1, + subkey: 4 + }, + + loginFailed:{ + key: 1, + subkey: 5 + }, + + launchsysteminfo:{ + key: 1, + subkey: 6 + }, + + logined:{ + key: 11, + subkey: 1 + }, + + authed:{ + key: 11, + subkey: 2 + }, + + authfail:{ + key: 11, + subkey: 3 + }, + + startgame:{ + key: 11, + subkey: 4 + }, + + restartgame:{ + key: 11, + subkey: 4 + }, + + gameover:{ + key: 11, + subkey: 6 + }, + + againgame:{ + key: 11, + subkey: 4 + }, + + productitem:{ + key: 11, + subkey: 8 + }, + + useitem:{ + key: 11, + subkey: 9 + }, + + share:{ + key: 11, + subkey: 10 + }, + + inviter:{ + key: 11, + subkey: 11 + }, + + systeminfo:{ + key: 11, + subkey: 20 + }, + + advinfo:{ + key: 11, + subkey: 21 + }, + + vslogin:{ + key: 11, + subkey: 24 + }, + + vsreconnect:{ + key: 11, + subkey: 25 + }, + + vsroomcreate:{ + key: 11, + subkey: 26 + }, + + vsroomjoin:{ + key: 11, + subkey: 27 + }, + + vsroomleave:{ + key: 11, + subkey: 28 + }, + + vsroomsetstate:{ + key: 11, + subkey: 29 + }, + + business:{ + key: 11, + subkey: 30 + }, + + buttonclick:{ + key: 11, + subkey: 31 + }, + + msgevent:{ + key: 11, + subkey: 32 + } +}; + +const _SHOW_FLAG = 2; +const _HIDE_FLAG = 1; + +const _ONLINE_KEY = 'jc_online_flags'; + +// 说明:带下划线的函数是内部函数,无需关心 + +const jcgamelog = { + // LIFE-CYCLE CALLBACKS: + + // onLoad () {}, + + // start () {}, + + // update (dt) {}, + __uuid(len, radix) { + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + var uuid = [], i; + radix = radix || chars.length; + + if (len) { + // Compact form + for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; + } else { + // rfc4122, version 4 form + var r; + + // rfc4122 requires these characters + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; + uuid[14] = '4'; + + // Fill in random data. At i==19 set the high bits of clock sequence as + // per rfc4122, sec. 4.1.5 + for (i = 0; i < 36; i++) { + if (!uuid[i]) { + r = 0 | Math.random() * 16; + uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; + } + } + } + + return uuid.join(''); + }, + + _buildReportUrl(typeobj){ + this.urlbd.clear(); + this.urlbd.addKV('c', 'GameLog') + .addKV('a', 'reportLog') + .addKV('gameid', this.gameid) + .addKV('channel', this.channelid) + .addKV('account_id', this.accountid) + .addKV('session_id', this.sessionid) + .addKV('access_token', this.token) + .addKV('wid', this.wid) + .addKV('sid', this.sid) + .addKV('ptid', this.ptid) + .addKV('logclass1', typeobj.key) + .addKV('logclass2', typeobj.subkey) + .addKV('localuuid', this.localid) + .addKV('from_appid', this.fromid) + ; + //console.log("[gamelog]"); + //console.log(JSON.stringify(typeobj)); + return this.urlbd.baseurl; + }, + + _buildReportUserUrl(){ + this.urlbd.clear(); + this.urlbd.addKV('c', 'GameLog') + .addKV('a', 'reportUser') + .addKV('gameid', this.gameid) + .addKV('channel', this.channelid) + .addKV('account_id', this.accountid) + .addKV('session_id', this.sessionid) + .addKV('access_token', this.token) + ; + return this.urlbd.baseurl; + }, + + _buildShowHideMsg(showflag){ + let data = { + str1: this.localid, + str2: this.fromid, + str3: this.accountid, + str4: this.nickName, + num1: showflag, + num2: -1 + }; + + let msg = { + u: this._buildReportUrl(showflag == _SHOW_FLAG? JC_LOG_T.show: JC_LOG_T.hide), + v: JSON.stringify(data), + ext: showflag + }; + + return msg; + }, + + _report(typeobj, valueobj, successcb, failcb){ + let url = this._buildReportUrl(typeobj); + let value = JSON.stringify(valueobj); + var self = this; + httpclient.httpPost(url, value, function(restext){ + let obj = httpclient.JSON_parse(restext); + if(obj.errcode == 0){ + //console.log('[_report]success!'+JSON.stringify(obj)); + successcb && successcb(); + }else{ + //console.log('[_report]'+url); + //console.log('[_report]failed!'+obj.errcode+":"+obj.errmsg); + failcb && failcb(0, obj.errcode, obj.errmsg); + } + }, function(errcode, errmsg){ + //console.log('[_report]'+url); + //console.log('[_report]failed!'+errcode+":"+errmsg); + failcb && failcb(errcode, 0, errmsg); + let nflag = -1; + if(typeobj.key == JC_LOG_T.show.key && typeobj.subkey == JC_LOG_T.show.subkey){ + nflag = valueobj.num1; + } + let msg = { + u: url, + v: value, + ext: nflag + } + self.cachemsg.push(msg); + }); + }, + + _reportUser(valueobj, successcb, failcb){ + let url = this._buildReportUserUrl(); + let value = JSON.stringify(valueobj); + var self = this; + httpclient.httpPost(url, value, function(restext){ + let obj = httpclient.JSON_parse(restext); + if(obj.errcode == 0){ + //console.log('[_reportUser]success!'+JSON.stringify(obj)); + successcb && successcb(); + }else{ + //console.log('[_reportUser]failed!'+obj.errcode+":"+obj.errmsg); + failcb && failcb(0, obj.errcode, obj.errmsg); + } + }, function(errcode, errmsg){ + //console.log('[_reportUser]failed!'+errcode+":"+errmsg); + failcb && failcb(errcode, 0, errmsg); + let nflag = -1; + let msg = { + u: url, + v: value, + ext: nflag + } + self.cachemsg.push(msg); + }); + }, + + _retry(){ + if(this.cachemsg.length > 0){ + let obj = this.cachemsg[0]; + var self = this; + httpclient.httpPost(obj.u, obj.v, function(restext){ + if(obj.ext >= 0){ + self.showflag = obj.ext; + self._saveflag(self.showflag); + } + self.cachemsg.shift(); + }, function(errcode, errmsg){ + //console.log('[_retry_report]failed!'+errcode+":"+errmsg); + }); + } + }, + + _saveflag(showflag){ + }, + + _loadflag(){ + }, + + _handlelaunch(res){ + if(res){ + if(res.query && res.query.weixinadinfo){ + let wxaddr = res.query.weixinadinfo.split('.'); + let ad = wxaddr[0]; + if(ad && ad != ''){ + this.fromadvid = ad; + } + } + this.launchparam = res.query; + if(res.referrerInfo && res.referrerInfo.appId){ + this.setFromAppID(res.referrerInfo.appId); + this.launchparam = res.referrerInfo.extraData; + }else if(res.query && res.query.scene){ + this.setFromAppID(res.query.scene); + } + this.scene = res.scene? res.scene: 0; + } + }, + + _handleLoginInfo(res){ + if(res.account_id && res.session_id){ + this.setAccountID(res.account_id, res.session_id); + } + if(res && res.nickname && res.nickname != ''){ + this.setNickName(res.nickname); + } + }, + + _handleAuthInfo(res){ + if(res && res.nickname && res.nickname != ''){ + this.setNickName(res.nickname); + } + }, + + //【通用函数】生成本地匿名ID + generateUUID(){ + return this.__uuid(32, 62); + }, + + // 初始化(上报前必须调用此函数)【使用jcfw.init的话此函数无需手动调用】 + init(channelid, gameid, isoffical, owner, url){ + this.gameid = gameid; + this.channelid = channelid; + this.accountid = this.accountid? this.accountid: ''; + this.sessionid = this.sessionid? this.sessionid: ''; + this.token = this.token? this.token: ''; + this.localid = this.localid? this.localid: ''; + this.nickname = this.nickname? this.nickname: ''; + this.fromid = this.fromid? this.fromid: ''; + this.fromadvid = this.fromadvid? this.fromadvid: ''; + this.scene = this.scene? this.scene: 0; + this._tempuuid = ''; + this._starttime = 0; + this._launchtime = 0; + this.needsubmit = false; + this.urlbd = new urlbuilder(url); + this.showflag = this._loadflag(); + this.cachemsg = []; + if(this.showflag == _SHOW_FLAG){ + this.cachemsg.push(this._buildShowHideMsg(this.showflag)); + } + this.showflag = null; + setInterval(this._retry.bind(this), 5000); + //console.log("[jcgamelog]init:"+gameid + "|" + channelid + "|" + isoffical); + }, + + // 【暂未使用】 + setSubmitFlag(bsubmit){ + this.needsubmit = bsubmit; + }, + + // 登陆成功后调用,传入accountid和sessionid【使用jcfw.login的话此函数无需手动调用】 + setAccountID(accountid, sessionid, exobj){ + if(!this.accountid || this.accountid != accountid){ + this.accountid = accountid; + } + if(!this.sessionid || this.sessionid != sessionid){ + this.sessionid = sessionid; + } + if(exobj){ + this.wid = exobj.wid; + this.sid = exobj.sid; + this.ptid = exobj.ptid; + } + }, + + // 设置昵称【无需手动调用】 + setNickName(nickname){ + if(!this.nickname || this.nickname != nickname){ + this.nickname = nickname; + } + }, + + // 设置本app从哪个appid跳转过来的【使用jcfw.init的话此函数无需手动调用】 + setFromAppID(appid){ + if(!this.fromid || this.fromid != appid){ + this.fromid = appid; + } + }, + + // 设置本地匿名ID(本地生成匿名id后调用)【使用jcfw.init的话此函数无需手动调用】 + setLocalUUID(uuid){ + if(!this.localid || this.localid != uuid){ + this.localid = uuid; + } + }, + + setSystemInfo(info){ + + }, + + // 【暂未使用】 + setToken(token){ + this.token = token; + }, + + handlelaunch(res, cb){ + this._launchtime = new Date().getTime(); + var param = 0; + var uid = ''; + var stype = 0; + var invid = ''; + if(res){ + this._handlelaunch(res); + param = res.query.activity_param? res.query.activity_param: 0; + uid = res.query.localuuid? res.query.localuuid: ''; + stype = res.query.sharetype? res.query.sharetype: 0; + invid = res.query.inviter_id? res.query.inviter_id: ''; + } + this.logLauchDefault(res); + cb && cb(stype, param, uid, invid, this.fromid, this.scene, this.launchparam); + }, + + //////////////////统计函数//////////////////// + // 启动游戏--------游戏启动后上报【使用jcfw.init的话此函数无需手动调用】 + logLaunch(launch_option){ + this._launchtime = new Date().getTime(); + this._handlelaunch(launch_option); + this.logLauchDefault(launch_option); + }, + + //启动游戏(内部调用) + logLauchDefault(launch_option){ + let data = { + str1: this.localid, + str2: this.fromid, + ext: launch_option? JSON.stringify(launch_option): null, + str3: this.fromadvid, + num1: this.scene + } + this._report(JC_LOG_T.lauch, data); + }, + + // 登陆成功--------游戏登陆成功后上报【使用jcfw.login的话此函数无需手动调用】 + logLoginSuccess(res, costtime){ + let nowtime = new Date().getTime(); + let dttime = nowtime - this._launchtime; + this._handleLoginInfo(res); + let data = { + str1: res.nickname, + str2: res.unionid, + str3: res.country, + str4: res.province, + str5: res.city, + str6: this.localid, + num1: costtime? costtime: 0, + num2: dttime, + str7: this.fromadvid + } + //console.log('[launchcost]'+dttime); + this._report(JC_LOG_T.logined, data); + }, + + // 登陆失败---------游戏登陆失败后上报【使用jcfw.login的话此函数无需手动调用】 + logLoginFailed(neterr, logicerr, errmsg){ + let data = { + error_code_net: neterr, + error_code_logic: logicerr, + error_msg: errmsg + }; + this._report(JC_LOG_T.loginFailed, data); + }, + + // 授权成功--------微信获取userinfo后上报【使用jcfw.login的话此函数无需手动调用】 + logAuthSuccess(res){ + this._handleAuthInfo(res); + let data = { + str1: res.nickName, + num1: res.gender, + str3: res.country, + str4: res.province, + str5: res.city, + str6: this.localid, + str7: res.avatarUrl? res.avatarUrl: '', + str8: res.language + } + this._report(JC_LOG_T.authed, data); + }, + + // 授权失败--------微信获取userinfo时,用户拒绝授权后上报【使用jcfw.login的话此函数无需手动调用】 + logAuthFail(){ + let data = { + str1: this.nickName, + str2: this.localid + } + this._report(JC_LOG_T.authfail, data); + }, + + // 分享---------------用户分享时上报【使用jcfw.shareNormal/shareCapture的话此函数无需手动调用】 + logShare(businessid, sharetokenid, param){ + let data = { + str1: this.nickname, + str2: sharetokenid? sharetokenid: '', + num1: businessid, + str3: param? param: '' + } + this._report(JC_LOG_T.share, data); + }, + + // 分享点击-------------当用户点击别人分享的卡片进入游戏时上报【使用jcfw.login的话此函数无需手动调用】 + logShareInvite(inviterid, businessid, sharetokenid, param){ + let data = { + str1: this.nickName, + str2: inviterid, + num1: businessid, + str3: sharetokenid? sharetokenid: sharetokenid, + str4: param? param: '' + } + this._report(JC_LOG_T.inviter, data); + }, + + // 系统信息-------------获取用户系统信息后上报【使用jcfw.init的话此函数无需手动调用】 + logSysInfo(res, typeobj){ + let data = { + str1: res.brand, + str2: res.model, + str3: res.language, + str4: res.version, + str5: res.platform, + str6: res.SDKVersion, + str7: res.system, + num1: res.pixelRatio, + num2: res.screenWidth, + num3: res.screenHeight, + num4: res.windowWidth, + num5: res.windowHeight, + num6: res.benchmarkLevel + // ext: JSON.stringify(res) + }; + this._report(typeobj, data); + }, + + logSystemInfo(res){ + if(!res){ + return; + } + this.logSysInfo(res, JC_LOG_T.systeminfo); + }, + + logLaunchSystemInfo(res){ + if(!res){ + return; + } + this.logSysInfo(res, JC_LOG_T.launchsysteminfo); + }, + + // 开始游戏---------开始一局游戏时上报【使用jcfw.gameStart的话此函数无需手动调用】 + logStartGame(param, startmode){ + this._tempuuid = this.generateUUID(); + this._starttime = new Date().getTime(); + let data = { + str1: this.fromid, + str2: this._tempuuid, + str3: param? param: '', + str4: this.nickname, + str5: this.localid, + num1: startmode? startmode: 0 + } + this._report(JC_LOG_T.startgame, data); + }, + + // 重新开始游戏---------重新开始一局游戏时上报【使用jcfw.gameStart的话此函数无需手动调用】 + logRestartGame(param){ + this._tempuuid = this.generateUUID(); + this._starttime = new Date().getTime(); + let data = { + str1: this.fromid, + str2: this._tempuuid, + str3: param? param: '', + str4: this.nickname, + str5: this.localid, + num1: 1 + } + this._report(JC_LOG_T.restartgame, data); + }, + + // 上报游戏还活着 + logGameAlive(param){ + //this._tempuuid = this.generateUUID(); + this._starttime = new Date().getTime(); + let data = { + str1: this.fromid, + str2: this._tempuuid, + str3: param? param: '', + str4: this.nickname, + str5: this.localid, + num1: 3 + } + this._report(JC_LOG_T.restartgame, data); + }, + + // 游戏结束---------本局游戏结束时上报【使用jcfw.gameOver的话此函数无需手动调用】 + logGameover(param, score, endflag){ + let nowtime = new Date().getTime(); + let data = { + str1: this.fromid, + str2: this._tempuuid, + str3: param? param: '', + num1: score, + str4: this.nickname, + str5: this.localid, + num2: nowtime - this._starttime, + num3: endflag? endflag: 0 + } + this._report(JC_LOG_T.gameover, data); + }, + + // 再来一次----------再来一次时上报【使用jcfw.gameStart的话此函数无需手动调用】 + logTryAgain(param){ + this._tempuuid = this.generateUUID(); + let data = { + str1: this.fromid, + str2: this._tempuuid, + str3: param? param: '', + str4: this.nickname, + str5: this.localid, + num1: 2 + } + this._report(JC_LOG_T.againgame, data); + }, + + // 显示-----------------------app显示时上报(eg:wx.onShow)【使用jcfw.gameShow的话此函数无需手动调用】 + logShow(bIgnoreFlag){ + if(!bIgnoreFlag){ + let bok = !this.showflag || this.showflag == _HIDE_FLAG; + if(!bok){ + return; + } + this.showflag = _SHOW_FLAG; + } + + let nowtime = new Date().getTime(); + let data = { + str1: this.localid, + str2: this.fromid, + str3: this.accountid, + str4: this.nickName, + num1: _SHOW_FLAG, + num2: nowtime - this._launchtime + }; + this._report(JC_LOG_T.show, data, ()=>{ + if(!bIgnoreFlag){ + this._saveflag(this.showflag); + } + }, () => { + }); + }, + + // 隐藏-----------------------app隐藏时上报(eg:wx.onHide)【使用jcfw.gameHide的话此函数无需手动调用】 + logHide(bIgnoreFlag){ + if(!bIgnoreFlag){ + let bok = this.showflag && this.showflag == _SHOW_FLAG; + if(!bok){ + return; + } + this.showflag = _HIDE_FLAG; + } + let nowtime = new Date().getTime(); + let data = { + str1: this.localid, + str2: this.fromid, + str3: this.accountid, + str4: this.nickName, + num1: _HIDE_FLAG, + num2: nowtime - this._launchtime + }; + this._report(JC_LOG_T.hide, data, ()=>{ + if(!bIgnoreFlag){ + this._saveflag(this.showflag); + } + }, () => { + }); + }, + + // 产出道具----------道具产出时上报【使用jcfw.gameGetItem的话此函数无需手动调用】 + logProductItem(itemid, itemcount, reson, resonparam){ + let data = { + str1: this.nickname, + num1: itemid, + num2: itemcount, + num3: reson, + num4: resonparam + } + this._report(JC_LOG_T.productitem, data); + }, + + // 使用道具----------使用道具时上报【使用jcfw.gameUseItem的话此函数无需手动调用】 + logUseItem(itemid, itemcount, reson, resonparam){ + let data = { + str1: this.nickname, + num1: itemid, + num2: itemcount, + num3: reson, + num4: resonparam + } + this._report(JC_LOG_T.useitem, data); + }, + + // 运营活动上报---------------开始运营活动时上报 + logBusiness(businessid, actionid){ + let data = { + str1: this.nickName, + str2: this.localid, + num1: businessid, + num2: actionid + }; + this._report(JC_LOG_T.business, data); + }, + + // 进入游戏主界面-------------主界面显示时上报 + logEnterMainScene(dt){ + let data = { + str1: this.localid, + str2: this.fromid, + num1: dt? dt: 0 + }; + this._report(JC_LOG_T.entermain, data); + }, + + // 跳转到其他app--------------跳转到其他app时上报(eg: wx.navigateMiniProgram) + logJumpApp(appid, appparam, jumpres){ + let data = { + str1: this.localid, + str2: this.fromid, + str3: this.accountid, + str4: this.nickName, + str5: appid, + str6: appparam, + num1: jumpres? jumpres: 0 + }; + this._report(JC_LOG_T.jumpapp, data); + }, + + // 广告相关-----------------处理广告时上报(eg: 激励广告出现时) + logAdvInfo(advid, actiontype, actionparam){ + let data = { + str1: this.nickname, + str2: advid, + num1: actiontype, + str3: actionparam + }; + this._report(JC_LOG_T.advinfo, data); + }, + + // 【对战开房间】登陆【无需主动调用】 + logVS_login(){ + let data = { + str1: this.nickName + }; + this._report(JC_LOG_T.vslogin, data); + }, + + // 【对战开房间】重连【无需主动调用】 + logVS_reconnect(roomid){ + let data = { + str1: this.nickName, + str2: roomid + }; + this._report(JC_LOG_T.vsreconnect, data); + }, + + // 【对战开房间】创建房间【无需主动调用】 + logVS_createRoom(roomname, maxplayer){ + let data = { + str1: this.nickname, + str2: roomname, + num1: maxplayer + }; + this._report(JC_LOG_T.vsroomcreate, data); + }, + + // 【对战开房间】加入房间【无需主动调用】 + logVS_joinRoom(roomid, customdata, israndom){ + let data = { + str1: this.nickname, + str2: roomid? roomid: '', + num1: israndom? 1: 0, + str3: customdata + }; + this._report(JC_LOG_T.vsroomjoin, data); + }, + + // 【对战开房间】离开房间【无需主动调用】 + logVS_leaveRoom(roomid, customdata){ + let data = { + str1: this.nickname, + str2: roomid, + str3: customdata + }; + this._report(JC_LOG_T.vsroomleave, data); + }, + + // 【对战开房间】游戏准备【无需主动调用】 + logVS_gameReady(roomid, customdata, isready){ + let data = { + str1: this.nickname, + str2: roomid, + num1: isready? 1: 0, + str3: customdata + }; + this._report(JC_LOG_T.vsroomsetstate, data); + }, + + // 【对战开房间】游戏开始【无需主动调用】 + logVS_gameStart(roomid, customdata){ + let data = { + str1: this.nickname, + str2: roomid, + num1: 2, + str3: customdata + }; + this._report(JC_LOG_T.vsroomsetstate, data); + }, + + // 按钮事件----------------点击按钮时上报 + logButtonClick(buttonname, param, buttonsubname){ + let data = { + nickname: this.nickname, + button_name: buttonname, + button_param: param? param: '' + }; + if(buttonsubname){ + data.button_subname = buttonsubname; + } + this._report(JC_LOG_T.buttonclick, data); + }, + + logMsg(msgid, msgname, errcode, reqcontent, rspcontent, passtime){ + let data = { + msg_id: msgid, + msg_name: msgname, + msg_error: errcode, + req_content: reqcontent, + rsp_content: rspcontent, + msg_cosumetime: passtime + }; + this._report(JC_LOG_T.msgevent, data); + }, + + // 设置角色属性 + /* 调用示例:角色初始化时 + run(){ + let obj = { + level: 1, + money: 10000, + ... + }; + jcfw.gamelog.userSet(obj); + } + */ + userSet(kvobj){ + let data = { + set: kvobj + }; + this._reportUser(data); + }, + + // 设置角色属性(仅允许设置一次) + /* 调用示例:改名时(如果有此功能) + run(){ + let obj = { + nickname: "abc" + }; + jcfw.gamelog.userSet(obj); + } + */ + userSetOnce(kvobj){ + let data = { + set_once: kvobj + }; + this._reportUser(data); + }, + + // 增加/减少角色数值属性 + /* 调用示例:角色升级时 + run(){ + let obj = { + level: 1 + }; + jcfw.gamelog.userAdd(obj); + } + */ + userAdd(kvobj){ + let data = { + add: kvobj + }; + this._reportUser(data); + }, + + /*可以一次调用set/setonce/add */ + userReport(setobj, setonceobj, addobj){ + let data = { + set: setobj, + set_once: setonceobj, + add: addobj + }; + this._reportUser(data); + }, + + /** + * + * @param {number} externalID 外部调用的ID + * @param {object} logFunc 外部调用的方法 + * @param {string} p1 参数1 + * @param {string} p2 参数2 + * @param {string} p3 参数3 + * @param {string} p4 参数4 + */ + externalLog(externalID, logFunc, p1, p2, p3, p4){ + let tmpid = this.gameid; + this.gameid = externalID; + logFunc && logFunc.call(this,p1,p2,p3,p4); + this.gameid = tmpid; + } +}; + +module.exports = jcgamelog; diff --git a/jcfw/logger/logger.js b/jcfw/logger/logger.js new file mode 100644 index 0000000..84ff032 --- /dev/null +++ b/jcfw/logger/logger.js @@ -0,0 +1,31 @@ + +var log = require('./jcgamelog') + +function getParameter (t) { + var e = window.location.search, + i = new RegExp(t + "=([^&?]*)", "ig"); + return e.match(i) ? e.match(i)[0].substr(t.length + 1) : null +} + +window.log = log; +var env = getParameter('version'); +var isTest = (!env || env==='test') ? '-test' : ''; +var url = 'https://gamelog'+isTest+'.kingsome.cn/webapp/index.php' + +window.log.init(6001, 8004, false, '', url); +var accountId = getParameter('account_id'); +var sessionId = getParameter('session_id') +window.log.setAccountID(accountId, sessionId); + +var id = getParameter('id'); +var netmode = getParameter("roomId")?1:0; + +var logObj = { + gid: id, + m: netmode +} +window.log.logStartGame(logObj); + +setInterval(function () { + window.log.logGameAlive(logObj); +}, 60000) diff --git a/jcfw/logger/urlbuilder.js b/jcfw/logger/urlbuilder.js new file mode 100644 index 0000000..a36d6e1 --- /dev/null +++ b/jcfw/logger/urlbuilder.js @@ -0,0 +1,33 @@ +module.exports = function (url) { + this.orginurl = url; + this.baseurl = url; + this.checked = false; + this.addKV = function (key, value) { + if (typeof (value) == 'undefined') { + return this; + } + this._checkurl(); + var str = encodeURIComponent(key) + '=' + encodeURIComponent(value); + this.baseurl += str; + return this; + }; + + this.clear = function () { + this.baseurl = this.orginurl; + this.checked = false; + }; + + this._checkurl = function () { + if (!this.checked) { + if (this.baseurl.indexOf("?") == -1) { + this.baseurl += "?"; + } else { + this.baseurl += "&"; + } + this.checked = true; + } else { + this.baseurl += "&"; + } + + }; +}; diff --git a/package.json b/package.json index f825997..601bc68 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,11 @@ "description": "", "main": "", "devDependencies": { - "babel-preset-env": "^1.7.0", "eslint": "^5.16.0", "eslint-loader": "^2.1.2", "gulp": "^3.9.1", "gulp-babel": "^8.0.0", + "gulp-browserify": "^0.5.1", "gulp-clean-css": "^4.0.0", "gulp-concat": "^2.6.1", "gulp-javascript-obfuscator": "^1.1.5", @@ -24,8 +24,7 @@ "gulp-uglify-es": "^1.0.4", "gulp-util": "^3.0.8", "jshint": "^2.10.1", - "path": "^0.12.7", - "gulp-browserify": "^0.5.1" + "path": "^0.12.7" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", @@ -36,5 +35,8 @@ "url": "git@git.kingsome.cn:weapp/emulator.git" }, "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "protobufjs": "^6.8.8" + } } diff --git a/tasks/fc2.js b/tasks/fc2.js index 89487a7..430696c 100644 --- a/tasks/fc2.js +++ b/tasks/fc2.js @@ -28,13 +28,24 @@ gulp.task('distfcstatic', function() { return gulp.src(['fc2/images/**/*']) .pipe(gulp.dest('dist/fc2/images')); }) +gulp.task('distfcresource', function() { + return gulp.src(['fc2/resources/**/*']) + .pipe(gulp.dest('dist/fc2/resources')); +}) gulp.task('distfcjs', function() { - return gulp.src(['fc2/js/**/*.js']) + return gulp.src(['fc2/js/**/*.js', '!fc2/js/nes-worker.js']) .pipe(javascriptObfuscator(obfuscatorOptions)) - .pipe(stripDebug()) + .pipe(concat('main.min.js')) .pipe(gulp.dest('dist/fc2/js')); }); +gulp.task('distotherjs', function() { + return gulp.src(['fc2/js/nes-worker.js']) + .pipe(javascriptObfuscator(obfuscatorOptions)) + .pipe(gulp.dest('dist/fc2/js')); +}); + + gulp.task('distlib', function () { return gulp.src('fc2/source/index.js') .pipe(browserify({ @@ -46,7 +57,7 @@ gulp.task('distlib', function () { .pipe(rename('jsnes.min.js')) .pipe(gulp.dest('dist/fc2/js/')); }); -gulp.task('distfc', ['distfcjs', 'distfccss', 'distfcstatic', 'distlib']) +gulp.task('distfc', ['distfcjs', 'distfccss', 'distfcstatic', 'distlib', 'distfcresource', 'distotherjs']) module.exports = { dep: ['distfc'], diff --git a/tasks/jcfw.js b/tasks/jcfw.js new file mode 100644 index 0000000..f09047b --- /dev/null +++ b/tasks/jcfw.js @@ -0,0 +1,30 @@ +const gulp = require('gulp'), + rename = require('gulp-rename'), + browserify = require('gulp-browserify'), + concat = require('gulp-concat'), + javascriptObfuscator = require('gulp-javascript-obfuscator'); + +const obfuscatorOptions = { + compact:true, + sourceMap: false, + debugProtection: false, + disableConsoleOutput: false +}; + + +gulp.task('distlogger', function () { + return gulp.src('jcfw/logger/logger.js') + .pipe(browserify({ + insertGlobals : false, + debug: false, + })) + // .pipe(javascriptObfuscator(obfuscatorOptions)) + .pipe(rename('index.min.js')) + .pipe(gulp.dest('dist/jcfw/logger/')); +}); + + +module.exports = { + dep: ['distlogger'], +}; +